Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 13 Mar 2014 13:10:30 +0100
changeset 191669 d9b7a56dbffbe6c668c962267fb9022114a58936
parent 191668 dce96be376bd751874fd28d57c8c68d0aaa28788 (current diff)
parent 191580 b01286b4ed370adcb5a26cf462fe055f1a5a826b (diff)
child 191670 53e8cf0a814b9568b3814998a8c4ab3ec200be6b
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -194,17 +194,17 @@ var shell = {
     Services.obs.addObserver(function observer(subject, topic, state) {
       let network = subject.QueryInterface(Ci.nsINetworkInterface);
       if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
           && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
         shell.submitQueuedCrashes();
 
         Services.obs.removeObserver(observer, topic);
       }
-    }, "network-interface-state-changed", false);
+    }, "network-connection-state-changed", false);
   },
 
   get contentBrowser() {
     delete this.contentBrowser;
     return this.contentBrowser = document.getElementById('systemapp');
   },
 
   get homeURL() {
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "remote": "", 
         "branch": "", 
         "revision": ""
     }, 
-    "revision": "57aedcebe10cd4fef4dc111f1b7347ba1ea3286c", 
+    "revision": "3981aa44b3b0c977fbb34d0203594474b48b5b74", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ec28d9dcf57ca8da142f9f2fea33bc288b76ed59"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="b5d0f9b0cd5dce38b79287a9363226b912929270"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="b5151b89ff31e92dc44b466f15ad4909e73db248"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -54,16 +54,17 @@
 
         this._prefs.addObserver("", this, false);
         this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
         this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
         this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
         this.timeout = this._prefs.getIntPref("delay");
         this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled");
         this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
+        this._ignoreNextSelect = false;
 
         this.inputField.controllers.insertControllerAt(0, this._copyCutController);
         this.inputField.addEventListener("mousedown", this, false);
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
         this.inputField.addEventListener("underflow", this, false);
 
@@ -673,16 +674,25 @@
         <body><![CDATA[
           this._numNoActionsKeys = 0;
           this.popup.removeAttribute("noactions");
           let action = this._parseActionUrl(this._value);
           if (action)
             this.setAttribute("actiontype", action.type);
         ]]></body>
       </method>
+
+      <method name="selectTextRange">
+        <parameter name="aStartIndex"/>
+        <parameter name="aEndIndex"/>
+        <body><![CDATA[
+          this._ignoreNextSelect = true;
+          this.inputField.setSelectionRange(aStartIndex, aEndIndex);
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="keydown"><![CDATA[
         if ((event.keyCode === KeyEvent.DOM_VK_ALT ||
              event.keyCode === KeyEvent.DOM_VK_SHIFT) &&
             this.popup.selectedIndex >= 0) {
           this._numNoActionsKeys++;
@@ -734,16 +744,25 @@
       <handler event="focus" phase="capturing"><![CDATA[
         this._hideURLTooltip();
         this._clearFormatting();
       ]]></handler>
 
       <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
       <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
       <handler event="select"><![CDATA[
+        if (this._ignoreNextSelect) {
+          // If this select event is coming from autocomplete's selectTextRange,
+          // then we don't need to adjust what's on the selection keyboard here,
+          // but make sure to reset the flag since this should be a one-time
+          // suppression.
+          this._ignoreNextSelect = false;
+          return;
+        }
+
         if (!Cc["@mozilla.org/widget/clipboard;1"]
                .getService(Ci.nsIClipboard)
                .supportsSelectionClipboard())
           return;
 
         var val = this._getSelectedValueForClipboard();
         if (!val)
           return;
--- a/build/mobile/robocop/Actions.java
+++ b/build/mobile/robocop/Actions.java
@@ -10,16 +10,17 @@ public interface Actions {
     /** Special keys supported by sendSpecialKey() */
     public enum SpecialKey {
         DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
     }
 
     public interface EventExpecter {
         /** Blocks until the event has been received. Subsequent calls will return immediately. */
         public void blockForEvent();
+        public void blockForEvent(long millis, boolean failOnTimeout);
 
         /** Blocks until the event has been received and returns data associated with the event. */
         public String blockForEventData();
 
         /**
          * Blocks until the event has been received, or until the timeout has been exceeded.
          * Returns the data associated with the event, if applicable.
          */
--- a/build/mobile/robocop/FennecNativeActions.java
+++ b/build/mobile/robocop/FennecNativeActions.java
@@ -75,17 +75,17 @@ public class FennecNativeActions impleme
             GeckoAppShell.registerEventListener(mGeckoEvent, mListener);
             mIsRegistered = true;
         }
 
         public void blockForEvent() {
             blockForEvent(MAX_WAIT_MS, true);
         }
 
-        private void blockForEvent(long millis, boolean failOnTimeout) {
+        public void blockForEvent(long millis, boolean failOnTimeout) {
             if (!mIsRegistered) {
                 throw new IllegalStateException("listener not registered");
             }
 
             try {
                 mEventData = mEventDataQueue.poll(millis, TimeUnit.MILLISECONDS);
             } catch (InterruptedException ie) {
                 FennecNativeDriver.log(LogLevel.ERROR, ie);
@@ -224,17 +224,17 @@ public class FennecNativeActions impleme
             mListening = true;
         }
 
         private synchronized void notifyOfEvent() {
             mPaintDone = true;
             this.notifyAll();
         }
 
-        private synchronized void blockForEvent(long millis, boolean failOnTimeout) {
+        public synchronized void blockForEvent(long millis, boolean failOnTimeout) {
             if (!mListening) {
                 throw new IllegalStateException("draw listener not registered");
             }
             long startTime = SystemClock.uptimeMillis();
             long endTime = 0;
             while (!mPaintDone) {
                 try {
                     this.wait(millis);
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -5074,16 +5074,19 @@ HTMLInputElement::SetSelectionRange(int3
       nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
       if (aDirection.WasPassed() && aDirection.Value().EqualsLiteral("backward")) {
         dir = nsITextControlFrame::eBackward;
       }
 
       aRv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
       if (!aRv.Failed()) {
         aRv = textControlFrame->ScrollSelectionIntoView();
+        nsRefPtr<nsAsyncDOMEvent> event =
+          new nsAsyncDOMEvent(this, NS_LITERAL_STRING("select"), true, false);
+        event->PostDOMEvent();
       }
     }
   }
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
                                     int32_t aSelectionEnd,
@@ -5199,21 +5202,16 @@ HTMLInputElement::SetRangeText(const nsA
         aSelectionEnd = newEnd;
       }
     }
     break;
   }
 
   Optional<nsAString> direction;
   SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv);
-  if (!aRv.Failed()) {
-    nsRefPtr<nsAsyncDOMEvent> event =
-      new nsAsyncDOMEvent(this, NS_LITERAL_STRING("select"), true, false);
-    event->PostDOMEvent();
-  }
 }
 
 int32_t
 HTMLInputElement::GetSelectionStart(ErrorResult& aRv)
 {
   int32_t selEnd, selStart;
   aRv = GetSelectionRange(&selStart, &selEnd);
 
--- a/content/html/content/src/HTMLTextAreaElement.cpp
+++ b/content/html/content/src/HTMLTextAreaElement.cpp
@@ -882,16 +882,19 @@ HTMLTextAreaElement::SetSelectionRange(u
       nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
       if (aDirection.WasPassed() && aDirection.Value().EqualsLiteral("backward")) {
         dir = nsITextControlFrame::eBackward;
       }
 
       rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
       if (NS_SUCCEEDED(rv)) {
         rv = textControlFrame->ScrollSelectionIntoView();
+        nsRefPtr<nsAsyncDOMEvent> event =
+          new nsAsyncDOMEvent(this, NS_LITERAL_STRING("select"), true, false);
+        event->PostDOMEvent();
       }
     }
   }
 
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
@@ -988,21 +991,16 @@ HTMLTextAreaElement::SetRangeText(const 
         aSelectionEnd = newEnd;
       }
     }
     break;
   }
 
   Optional<nsAString> direction;
   SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv);
-  if (!aRv.Failed()) {
-    nsRefPtr<nsAsyncDOMEvent> event =
-      new nsAsyncDOMEvent(this, NS_LITERAL_STRING("select"), true, false);
-    event->PostDOMEvent();
-  }
 }
 
 nsresult
 HTMLTextAreaElement::Reset()
 {
   nsresult rv;
 
   // To get the initial spellchecking, reset value to
--- a/content/html/content/test/forms/test_set_range_text.html
+++ b/content/html/content/test/forms/test_set_range_text.html
@@ -67,18 +67,19 @@ https://bugzilla.mozilla.org/show_bug.cg
     //Supported types should not throw
     for (i = 0; i < SupportedTypes.length; ++i) {
       opThrows = false;
       msg = "input_" + SupportedTypes[i];
       elem = document.getElementById(msg);
       elem.focus();
       try {
         elem.setRangeText("abc");
+        expectedNumOfSelectCalls += 1;
       } catch (ex) {
-          opThrows = true;
+        opThrows = true;
       }
       is(opThrows, false, msg + " should not throw NotSupportedError");
 
       elem.addEventListener("select", function (aEvent) {
         ok(true, "select event should be fired for " + aEvent.target.id);
         if (++numOfSelectCalls == expectedNumOfSelectCalls) {
           SimpleTest.finish();
         } else if (numOfSelectCalls > expectedNumOfSelectCalls) {
@@ -94,70 +95,69 @@ https://bugzilla.mozilla.org/show_bug.cg
       elem.value = "0123456789ABCDEF";
       elem.setSelectionRange(1, 6);
       elem.setRangeText("xyz");
       is(elem.value, "0xyz6789ABCDEF", msg + test);
       is(elem.selectionStart, 1, msg + test);
       is(elem.selectionEnd, 4, msg + test);
       elem.setRangeText("mnk");
       is(elem.value, "0mnk6789ABCDEF", msg + test);
-      expectedNumOfSelectCalls += 2;
+      expectedNumOfSelectCalls += 3;
 
       test = " setRange(replacement), expand";
       elem.value = "0123456789ABCDEF";
       elem.setSelectionRange(1, 2);
       elem.setRangeText("xyz");
       is(elem.value, "0xyz23456789ABCDEF", msg + test);
       is(elem.selectionStart, 1, msg + test);
       is(elem.selectionEnd, 4, msg + test);
       elem.setRangeText("mnk");
       is(elem.value, "0mnk23456789ABCDEF", msg + test);
-      expectedNumOfSelectCalls += 2;
+      expectedNumOfSelectCalls += 3;
 
       test = " setRange(replacement) pure insertion at start";
       elem.value = "0123456789ABCDEF";
       elem.setSelectionRange(0, 0);
       elem.setRangeText("xyz");
       is(elem.value, "xyz0123456789ABCDEF", msg + test);
       is(elem.selectionStart, 0, msg + test);
       is(elem.selectionEnd, 0, msg + test);
       elem.setRangeText("mnk");
       is(elem.value, "mnkxyz0123456789ABCDEF", msg + test);
-      expectedNumOfSelectCalls += 2;
+      expectedNumOfSelectCalls += 3;
 
       test = " setRange(replacement) pure insertion in the middle";
       elem.value = "0123456789ABCDEF";
       elem.setSelectionRange(4, 4);
       elem.setRangeText("xyz");
       is(elem.value, "0123xyz456789ABCDEF", msg + test);
       is(elem.selectionStart, 4, msg + test);
       is(elem.selectionEnd, 4, msg + test);
       elem.setRangeText("mnk");
       is(elem.value, "0123mnkxyz456789ABCDEF", msg + test);
-      expectedNumOfSelectCalls += 2;
+      expectedNumOfSelectCalls += 3;
 
       test = " setRange(replacement) pure insertion at the end";
       elem.value = "0123456789ABCDEF";
       elem.setSelectionRange(16, 16);
       elem.setRangeText("xyz");
       is(elem.value, "0123456789ABCDEFxyz", msg + test);
       is(elem.selectionStart, 16, msg + test);
       is(elem.selectionEnd, 16, msg + test);
       elem.setRangeText("mnk");
       is(elem.value, "0123456789ABCDEFmnkxyz", msg + test);
-      expectedNumOfSelectCalls += 2;
+      expectedNumOfSelectCalls += 3;
 
       //test SetRange(replacement, start, end, mode) with start > end
       try {
         elem.setRangeText("abc", 20, 4);
       } catch (ex) {
         opThrows = (ex.name == "IndexSizeError" && ex.code == DOMException.INDEX_SIZE_ERR);
       }
       is(opThrows, true, msg + " should throw IndexSizeError");
-      expectedNumOfSelectCalls += 1;
 
       //test SelectionMode 'select'
       elem.value = "0123456789ABCDEF";
       elem.setRangeText("xyz", 4, 9, "select");
       is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
       is(elem.selectionStart, 4, msg + ".selectionStart == 4, with \"select\"");
       is(elem.selectionEnd, 7, msg + ".selectionEnd == 7, with \"select\"");
       expectedNumOfSelectCalls += 1;
@@ -200,44 +200,44 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       //subcase: selection{Start|End} > end
       elem.value = "0123456789";
       elem.setSelectionRange(6, 9);
       elem.setRangeText("Z", 1, 2, "preserve");
       is(elem.value, "0Z23456789", msg + ".value == \"0Z23456789\"");
       is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"preserve\"");
       is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"preserve\"");
-      expectedNumOfSelectCalls += 1;
+      expectedNumOfSelectCalls += 2;
 
       //subcase: selection{Start|End} < end
       elem.value = "0123456789";
       elem.setSelectionRange(4, 5);
       elem.setRangeText("QRST", 2, 9, "preserve");
       is(elem.value, "01QRST9", msg + ".value == \"01QRST9\"");
       is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"preserve\"");
       is(elem.selectionEnd, 6, msg + ".selectionEnd == 6, with \"preserve\"");
-      expectedNumOfSelectCalls += 1;
+      expectedNumOfSelectCalls += 2;
 
       //subcase: selectionStart > end, selectionEnd < end
       elem.value = "0123456789";
       elem.setSelectionRange(8, 4);
       elem.setRangeText("QRST", 1, 5);
       is(elem.value, "0QRST56789", msg + ".value == \"0QRST56789\"");
       is(elem.selectionStart, 1, msg + ".selectionStart == 1, with \"default\"");
       is(elem.selectionEnd, 5, msg + ".selectionEnd == 5, with \"default\"");
-      expectedNumOfSelectCalls += 1;
+      expectedNumOfSelectCalls += 2;
 
       //subcase: selectionStart < end, selectionEnd > end
       elem.value = "0123456789";
       elem.setSelectionRange(4, 9);
       elem.setRangeText("QRST", 2, 6);
       is(elem.value, "01QRST6789", msg + ".value == \"01QRST6789\"");
       is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"default\"");
       is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"default\"");
-      expectedNumOfSelectCalls += 1;
+      expectedNumOfSelectCalls += 2;
     }
   }
 
   addLoadEvent(TestInputs);
 
 </script>
 </pre>
 </body>
--- a/content/media/webaudio/test/test_oscillatorNode.html
+++ b/content/media/webaudio/test/test_oscillatorNode.html
@@ -44,15 +44,22 @@ addLoadEvent(function() {
   }
 
   // Verify setPeriodicWave()
   var real = new Float32Array([1.0, 0.5, 0.25, 0.125]);
   var imag = new Float32Array([1.0, 0.7, -1.0, 0.5]);
   osc.setPeriodicWave(context.createPeriodicWave(real, imag));
   is(osc.type, "custom", "Failed to set custom waveform");
 
+  expectNoException(function() {
+    osc.start();
+  });
+  expectNoException(function() {
+    osc.stop();
+  });
+
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -7,16 +7,26 @@ function expectException(func, exception
   } catch (ex) {
     threw = true;
     ok(ex instanceof DOMException, "Expect a DOM exception");
     is(ex.code, exceptionCode, "Expect the correct exception code");
   }
   ok(threw, "The exception was thrown");
 }
 
+function expectNoException(func) {
+  var threw = false;
+  try {
+    func();
+  } catch (ex) {
+    threw = true;
+  }
+  ok(!threw, "An exception was not thrown");
+}
+
 function expectTypeError(func) {
   var threw = false;
   try {
     func();
   } catch (ex) {
     threw = true;
     ok(ex instanceof TypeError, "Expect a TypeError");
   }
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -27,22 +27,23 @@
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/Preferences.h"
 
-#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
+#if defined(MOZ_CONTENT_SANDBOX)
+#if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
+#elif defined(XP_LINUX)
+#include "mozilla/Sandbox.h"
 #endif
-#if defined(XP_LINUX)
-#include "mozilla/Sandbox.h"
 #endif
 
 #include "mozilla/unused.h"
 
 #include "nsIConsoleListener.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
@@ -679,33 +680,26 @@ ContentChild::AllocPImageBridgeChild(moz
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
     return BackgroundChild::Alloc(aTransport, aOtherProcess);
 }
 
 bool
-ContentChild::RecvSetProcessPrivileges(const ChildPrivileges& aPrivs)
+ContentChild::RecvSetProcessSandbox()
 {
-  ChildPrivileges privs = (aPrivs == PRIVILEGES_DEFAULT) ?
-                          GeckoChildProcessHost::DefaultChildPrivileges() :
-                          aPrivs;
-#if defined(XP_LINUX)
-  // SetCurrentProcessSandbox includes SetCurrentProcessPrivileges.
-  // But we may want to move the sandbox initialization somewhere else
+  // We may want to move the sandbox initialization somewhere else
   // at some point; see bug 880808.
-  SetCurrentProcessSandbox(privs);
-#else
-  // If this fails, we die.
-  SetCurrentProcessPrivileges(privs);
+#if defined(MOZ_CONTENT_SANDBOX)
+#if defined(XP_LINUX)
+  SetCurrentProcessSandbox();
+#elif defined(XP_WIN)
+  mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
-
-#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_WIN)
-  mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
   return true;
 }
 
 bool
 ContentChild::RecvSpeakerManagerNotify()
 {
 #ifdef MOZ_WIDGET_GONK
@@ -1850,11 +1844,18 @@ AddNewIPCProcess(pid_t aPid, NuwaProtoFd
 
 NS_EXPORT void
 OnNuwaProcessReady()
 {
     mozilla::dom::ContentChild* content =
         mozilla::dom::ContentChild::GetSingleton();
     content->SendNuwaReady();
 }
+
+NS_EXPORT void
+AfterNuwaFork()
+{
+    SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
+}
+
 #endif // MOZ_NUWA_PROCESS
 
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -81,17 +81,17 @@ public:
 
     PCompositorChild*
     AllocPCompositorChild(mozilla::ipc::Transport* aTransport,
                           base::ProcessId aOtherProcess) MOZ_OVERRIDE;
     PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
-    virtual bool RecvSetProcessPrivileges(const ChildPrivileges& aPrivs) MOZ_OVERRIDE;
+    virtual bool RecvSetProcessSandbox() MOZ_OVERRIDE;
 
     PBackgroundChild*
     AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                           MOZ_OVERRIDE;
 
     virtual PBrowserChild* AllocPBrowserChild(const IPCTabContext &aContext,
                                               const uint32_t &chromeFlags);
     virtual bool DeallocPBrowserChild(PBrowserChild*);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -426,60 +426,53 @@ static const char* sObserverTopics[] = {
 /* static */ already_AddRefed<ContentParent>
 ContentParent::RunNuwaProcess()
 {
     MOZ_ASSERT(NS_IsMainThread());
     nsRefPtr<ContentParent> nuwaProcess =
         new ContentParent(/* aApp = */ nullptr,
                           /* aIsForBrowser = */ false,
                           /* aIsForPreallocated = */ true,
-                          // Final privileges are set when we
-                          // transform into our app.
-                          base::PRIVILEGES_INHERIT,
                           PROCESS_PRIORITY_BACKGROUND,
                           /* aIsNuwaProcess = */ true);
     nuwaProcess->Init();
     return nuwaProcess.forget();
 }
 
 // PreallocateAppProcess is called by the PreallocatedProcessManager.
 // ContentParent then takes this process back within
 // MaybeTakePreallocatedAppProcess.
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::PreallocateAppProcess()
 {
     nsRefPtr<ContentParent> process =
         new ContentParent(/* app = */ nullptr,
                           /* isForBrowserElement = */ false,
                           /* isForPreallocated = */ true,
-                          // Final privileges are set when we
-                          // transform into our app.
-                          base::PRIVILEGES_INHERIT,
                           PROCESS_PRIORITY_BACKGROUND);
     process->Init();
     return process.forget();
 }
 
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
-                                               ChildPrivileges aPrivs,
                                                ProcessPriority aInitialPriority)
 {
     nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
     if (!process) {
         return nullptr;
     }
 
-    if (!process->SetPriorityAndCheckIsAlive(aInitialPriority) ||
-        !process->TransformPreallocatedIntoApp(aAppManifestURL, aPrivs)) {
+    if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) {
         // Kill the process just in case it's not actually dead; we don't want
         // to "leak" this process!
         process->KillHard();
         return nullptr;
     }
+    process->TransformPreallocatedIntoApp(aAppManifestURL);
 
     return process.forget();
 }
 
 /*static*/ void
 ContentParent::StartUp()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
@@ -575,51 +568,22 @@ ContentParent::GetNewOrUsed(bool aForBro
         NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContentParents?");
         return p.forget();
     }
 
     nsRefPtr<ContentParent> p =
         new ContentParent(/* app = */ nullptr,
                           aForBrowserElement,
                           /* isForPreallocated = */ false,
-                          base::PRIVILEGES_DEFAULT,
                           PROCESS_PRIORITY_FOREGROUND);
     p->Init();
     sNonAppContentParents->AppendElement(p);
     return p.forget();
 }
 
-namespace {
-struct SpecialPermission {
-    const char* perm;           // an app permission
-    ChildPrivileges privs;      // the OS privilege it requires
-};
-}
-
-static ChildPrivileges
-PrivilegesForApp(mozIApplication* aApp)
-{
-    const SpecialPermission specialPermissions[] = {
-        // FIXME/bug 785592: implement a CameraBridge so we don't have
-        // to hack around with OS permissions
-        { "camera", base::PRIVILEGES_CAMERA }
-    };
-    for (size_t i = 0; i < ArrayLength(specialPermissions); ++i) {
-        const char* const permission = specialPermissions[i].perm;
-        bool hasPermission = false;
-        if (NS_FAILED(aApp->HasPermission(permission, &hasPermission))) {
-            NS_WARNING("Unable to check permissions.  Breakage may follow.");
-            break;
-        } else if (hasPermission) {
-            return specialPermissions[i].privs;
-        }
-    }
-    return base::PRIVILEGES_DEFAULT;
-}
-
 /*static*/ ProcessPriority
 ContentParent::GetInitialProcessPriority(Element* aFrameElement)
 {
     // Frames with mozapptype == critical which are expecting a system message
     // get FOREGROUND_HIGH priority.
 
     if (!aFrameElement) {
         return PROCESS_PRIORITY_FOREGROUND;
@@ -728,33 +692,31 @@ ContentParent::CreateBrowserOrApp(const 
         // Hopefully the process won't die after this point, if this call
         // succeeds.
         if (!p->SetPriorityAndCheckIsAlive(initialPriority)) {
             p = nullptr;
         }
     }
 
     if (!p) {
-        ChildPrivileges privs = PrivilegesForApp(ownApp);
-        p = MaybeTakePreallocatedAppProcess(manifestURL, privs,
+        p = MaybeTakePreallocatedAppProcess(manifestURL,
                                             initialPriority);
         if (!p) {
 #ifdef MOZ_NUWA_PROCESS
             if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled",
                                      false)) {
                 // Returning nullptr from here so the frame loader will retry
                 // later when we have a spare process.
                 return nullptr;
             }
 #endif
             NS_WARNING("Unable to use pre-allocated app process");
             p = new ContentParent(ownApp,
                                   /* isForBrowserElement = */ false,
                                   /* isForPreallocated = */ false,
-                                  privs,
                                   initialPriority);
             p->Init();
         }
         sAppContentParents->Put(manifestURL, p);
     }
 
     uint32_t chromeFlags = 0;
 
@@ -984,25 +946,22 @@ TryGetNameFromManifestURL(const nsAStrin
 
     if (!app) {
         return;
     }
 
     app->GetName(aName);
 }
 
-bool
-ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL,
-                                            ChildPrivileges aPrivs)
+void
+ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL)
 {
     MOZ_ASSERT(IsPreallocated());
     mAppManifestURL = aAppManifestURL;
     TryGetNameFromManifestURL(aAppManifestURL, mAppName);
-
-    return SendSetProcessPrivileges(aPrivs);
 }
 
 void
 ContentParent::ShutDownProcess(bool aCloseWithError)
 {
     const InfallibleTArray<PIndexedDBParent*>& idbParents =
         ManagedPIndexedDBParent();
     for (uint32_t i = 0; i < idbParents.Length(); ++i) {
@@ -1366,21 +1325,19 @@ ContentParent::InitializeMembers()
     mCalledClose = false;
     mCalledCloseWithError = false;
     mCalledKillHard = false;
 }
 
 ContentParent::ContentParent(mozIApplication* aApp,
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
-                             ChildPrivileges aOSPrivileges,
                              ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
                              bool aIsNuwaProcess /* = false */)
-    : mOSPrivileges(aOSPrivileges)
-    , mIsForBrowser(aIsForBrowser)
+    : mIsForBrowser(aIsForBrowser)
     , mIsNuwaProcess(aIsNuwaProcess)
 {
     InitializeMembers();  // Perform common initialization.
 
     // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be
     // true.
     MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
 
@@ -1402,18 +1359,20 @@ ContentParent::ContentParent(mozIApplica
         mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL;
     }
 
     // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
     // PID along with the warning.
     nsDebugImpl::SetMultiprocessMode("Parent");
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content,
-                                            aOSPrivileges);
+    ChildPrivileges privs = aIsNuwaProcess
+        ? base::PRIVILEGES_INHERIT
+        : base::PRIVILEGES_DEFAULT;
+    mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
     mSubprocess->SetSandboxEnabled(ShouldSandboxContentProcesses());
 
     IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
 
     if (!aIsNuwaProcess) {
         // Tell the memory reporter manager that this ContentParent exists.
         nsRefPtr<nsMemoryReporterManager> mgr =
             nsMemoryReporterManager::GetOrCreate();
@@ -1451,20 +1410,18 @@ FindFdProtocolFdMapping(const nsTArray<P
 /**
  * This constructor is used for new content process cloned from a template.
  *
  * For Nuwa.
  */
 ContentParent::ContentParent(ContentParent* aTemplate,
                              const nsAString& aAppManifestURL,
                              base::ProcessHandle aPid,
-                             const nsTArray<ProtocolFdMapping>& aFds,
-                             ChildPrivileges aOSPrivileges)
-    : mOSPrivileges(aOSPrivileges)
-    , mAppManifestURL(aAppManifestURL)
+                             const nsTArray<ProtocolFdMapping>& aFds)
+    : mAppManifestURL(aAppManifestURL)
     , mIsForBrowser(false)
     , mIsNuwaProcess(false)
 {
     InitializeMembers();  // Perform common initialization.
 
     sContentParents->insertBack(this);
 
     // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
@@ -1473,18 +1430,17 @@ ContentParent::ContentParent(ContentPare
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     const FileDescriptor* fd = FindFdProtocolFdMapping(aFds, GetProtocolId());
 
     NS_ASSERTION(fd != nullptr, "IPC Channel for PContent is necessary!");
     mSubprocess = new GeckoExistingProcessHost(GeckoProcessType_Content,
                                                aPid,
-                                               *fd,
-                                               aOSPrivileges);
+                                               *fd);
 
     // Tell the memory reporter manager that this ContentParent exists.
     nsRefPtr<nsMemoryReporterManager> mgr =
         nsMemoryReporterManager::GetOrCreate();
     if (mgr) {
         mgr->IncrementNumChildProcesses();
     }
 
@@ -1618,26 +1574,24 @@ ContentParent::InitInternal(ProcessPrior
         for (uint32_t i = 0; i < authorSheets.Length(); i++) {
             URIParams uri;
             SerializeURI(authorSheets[i]->GetSheetURI(), uri);
             unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
         }
     }
 
 #ifdef MOZ_CONTENT_SANDBOX
-    // Bug 921817.  We enable the sandbox in RecvSetProcessPrivileges,
-    // which is where a preallocated process drops unnecessary privileges,
-    // but a non-preallocated process will already have changed its
-    // uid/gid/etc immediately after forking.  Thus, we send this message,
-    // which is otherwise a no-op, to sandbox it at an appropriate point
-    // during startup.
-    if (mOSPrivileges != base::PRIVILEGES_INHERIT) {
-        if (!SendSetProcessPrivileges(base::PRIVILEGES_INHERIT)) {
-            KillHard();
-        }
+    bool shouldSandbox = true;
+#ifdef MOZ_NUWA_PROCESS
+    if (IsNuwaProcess()) {
+        shouldSandbox = false;
+    }
+#endif
+    if (shouldSandbox && !SendSetProcessSandbox()) {
+        KillHard();
     }
 #endif
 }
 
 bool
 ContentParent::IsAlive()
 {
     return mIsAlive;
@@ -1989,18 +1943,17 @@ ContentParent::RecvAddNewProcess(const u
                       "AddNewProcess(%d)", Pid(), aPid);
         KillHard();
         return false;
     }
     nsRefPtr<ContentParent> content;
     content = new ContentParent(this,
                                 MAGIC_PREALLOCATED_APP_MANIFEST_URL,
                                 aPid,
-                                aFds,
-                                base::PRIVILEGES_INHERIT);
+                                aFds);
     content->Init();
     PreallocatedProcessManager::PublishSpareProcess(content);
     return true;
 #else
     NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");
     return false;
 #endif
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -239,41 +239,38 @@ private:
     static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                       Monitor* aMonitor, bool* aDone);
 
     // Take the preallocated process and transform it into a "real" app process,
     // for the specified manifest URL.  If there is no preallocated process (or
     // if it's dead), this returns false.
     static already_AddRefed<ContentParent>
     MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
-                                    ChildPrivileges aPrivs,
                                     hal::ProcessPriority aInitialPriority);
 
     static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
 
     // Hide the raw constructor methods since we don't want client code
     // using them.
     using PContentParent::SendPBrowserConstructor;
     using PContentParent::SendPTestShellConstructor;
 
     // No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be
     // true.
     ContentParent(mozIApplication* aApp,
                   bool aIsForBrowser,
                   bool aIsForPreallocated,
-                  ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT,
                   hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND,
                   bool aIsNuwaProcess = false);
 
 #ifdef MOZ_NUWA_PROCESS
     ContentParent(ContentParent* aTemplate,
                   const nsAString& aAppManifestURL,
                   base::ProcessHandle aPid,
-                  const nsTArray<ProtocolFdMapping>& aFds,
-                  ChildPrivileges aOSPrivileges = base::PRIVILEGES_DEFAULT);
+                  const nsTArray<ProtocolFdMapping>& aFds);
 #endif
 
     // The common initialization for the constructors.
     void InitializeMembers();
 
     // The common initialization logic shared by all constuctors.
     void InitInternal(ProcessPriority aPriority,
                       bool aSetupOffMainThreadCompositing,
@@ -291,20 +288,18 @@ private:
 
     // Set the child process's priority and then check whether the child is
     // still alive.  Returns true if the process is still alive, and false
     // otherwise.  If you pass a FOREGROUND* priority here, it's (hopefully)
     // unlikely that the process will be killed after this point.
     bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
 
     // Transform a pre-allocated app process into a "real" app
-    // process, for the specified manifest URL.  If this returns false, the
-    // child process has died.
-    bool TransformPreallocatedIntoApp(const nsAString& aAppManifestURL,
-                                      ChildPrivileges aPrivs);
+    // process, for the specified manifest URL.
+    void TransformPreallocatedIntoApp(const nsAString& aAppManifestURL);
 
     /**
      * Mark this ContentParent as dead for the purposes of Get*().
      * This method is idempotent.
      */
     void MarkAsDead();
 
     /**
@@ -534,17 +529,16 @@ private:
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
 
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
-    base::ChildPrivileges mOSPrivileges;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
 
     nsString mAppManifestURL;
 
     /**
      * We cache mAppName instead of looking it up using mAppManifestURL when we
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -262,21 +262,21 @@ both:
     async PBrowser(IPCTabContext context, uint32_t chromeFlags);
 
     async PBlob(BlobConstructorParams params);
 
     async PJavaScript();
 
 child:
     /**
-     * Update OS process privileges to |privs|.  Can usually only be
-     * performed zero or one times.  The child will abnormally exit if
-     * the privilege update fails.
+     * Enable system-level sandboxing features, if available.  Can
+     * usually only be performed zero or one times.  The child may
+     * abnormally exit if this fails; the details are OS-specific.
      */
-    async SetProcessPrivileges(ChildPrivileges privs);
+    async SetProcessSandbox();
 
     PMemoryReportRequest(uint32_t generation, bool minimizeMemoryUsage, nsString DMDDumpIdent);
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -316,26 +316,26 @@ TabChild::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
   mLastRootMetrics.mCompositionBounds = ParentLayerIntRect(
       ParentLayerIntPoint(),
       ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenToParentLayerForRoot));
-  mLastRootMetrics.mZoom = mLastRootMetrics.CalculateIntrinsicScale();
+  mLastRootMetrics.SetZoom(mLastRootMetrics.CalculateIntrinsicScale());
   mLastRootMetrics.mDevPixelsPerCSSPixel = mWidget->GetDefaultScale();
   // We use ScreenToLayerScale(1) below in order to turn the
   // async zoom amount into the gecko zoom amount.
   mLastRootMetrics.mCumulativeResolution =
-    mLastRootMetrics.mZoom / mLastRootMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
+    mLastRootMetrics.GetZoom() / mLastRootMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   mLastRootMetrics.mResolution = mLastRootMetrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1);
-  mLastRootMetrics.mScrollOffset = CSSPoint(0, 0);
+  mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0));
 }
 
 bool
 TabChild::HasValidInnerSize()
 {
   return (mInnerSize.width != 0) && (mInnerSize.height != 0);
 }
 
@@ -604,39 +604,39 @@ TabChild::HandlePossibleViewportChange()
   // 3. screen size remains constant, but CSS viewport changes (meta viewport
   //    tag is added or removed)
   // 4. neither screen size nor CSS viewport changes
   //
   // In all of these cases, we maintain how much actual content is visible
   // within the screen width. Note that "actual content" may be different with
   // respect to CSS pixels because of the CSS viewport size changing.
   float oldIntrinsicScale = oldScreenWidth / oldBrowserWidth;
-  metrics.mZoom.scale *= metrics.CalculateIntrinsicScale().scale / oldIntrinsicScale;
+  metrics.ZoomBy(metrics.CalculateIntrinsicScale().scale / oldIntrinsicScale);
 
   // Changing the zoom when we're not doing a first paint will get ignored
   // by AsyncPanZoomController and causes a blurry flash.
   bool isFirstPaint;
   nsresult rv = utils->GetIsFirstPaint(&isFirstPaint);
   if (NS_FAILED(rv) || isFirstPaint) {
     // FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
     // 0.0 to mean "did not calculate a zoom".  In that case, we default
     // it to the intrinsic scale.
     if (viewportInfo.GetDefaultZoom().scale < 0.01f) {
       viewportInfo.SetDefaultZoom(metrics.CalculateIntrinsicScale());
     }
 
     CSSToScreenScale defaultZoom = viewportInfo.GetDefaultZoom();
     MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom &&
                defaultZoom <= viewportInfo.GetMaxZoom());
-    metrics.mZoom = defaultZoom;
+    metrics.SetZoom(defaultZoom);
 
     metrics.mScrollId = viewId;
   }
 
-  metrics.mCumulativeResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
+  metrics.mCumulativeResolution = metrics.GetZoom() / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   metrics.mResolution = metrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1);
   utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale);
 
   CSSSize scrollPort = CSSSize(metrics.CalculateCompositedRectInCssPixels().Size());
   utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
 
@@ -1521,18 +1521,18 @@ TabChild::ProcessUpdateFrame(const Frame
     FrameMetrics newMetrics = aFrameMetrics;
     APZCCallbackHelper::UpdateRootFrame(utils, newMetrics);
 
     CSSRect cssCompositedRect = CSSRect(newMetrics.CalculateCompositedRectInCssPixels());
     // The BrowserElementScrolling helper must know about these updated metrics
     // for other functions it performs, such as double tap handling.
     // Note, %f must not be used because it is locale specific!
     nsCString data;
-    data.AppendPrintf("{ \"x\" : %d", NS_lround(newMetrics.mScrollOffset.x));
-    data.AppendPrintf(", \"y\" : %d", NS_lround(newMetrics.mScrollOffset.y));
+    data.AppendPrintf("{ \"x\" : %d", NS_lround(newMetrics.GetScrollOffset().x));
+    data.AppendPrintf(", \"y\" : %d", NS_lround(newMetrics.GetScrollOffset().y));
     data.AppendLiteral(", \"viewport\" : ");
         data.AppendLiteral("{ \"width\" : ");
         data.AppendFloat(newMetrics.mViewport.width);
         data.AppendLiteral(", \"height\" : ");
         data.AppendFloat(newMetrics.mViewport.height);
         data.AppendLiteral(" }");
     data.AppendLiteral(", \"cssPageRect\" : ");
         data.AppendLiteral("{ \"x\" : ");
--- a/dom/mobilemessage/src/gonk/MmsService.js
+++ b/dom/mobilemessage/src/gonk/MmsService.js
@@ -34,17 +34,17 @@ const kSmsFailedObserverTopic           
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSmsRetrievingObserverTopic        = "sms-retrieving";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 const kSmsReadSuccessObserverTopic       = "sms-read-success";
 const kSmsReadErrorObserverTopic         = "sms-read-error";
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
-const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
+const kNetworkConnStateChangedTopic      = "network-connection-state-changed";
 const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
 
 const kPrefRilRadioDisabled              = "ril.radio.disabled";
 
 // HTTP status codes:
 // @see http://tools.ietf.org/html/rfc2616#page-39
 const HTTP_STATUS_OK = 200;
 
@@ -228,17 +228,17 @@ MmsConnection.prototype = {
   onDisconnectTimerTimeout: function() {
     if (DEBUG) debug("onDisconnectTimerTimeout: deactivate the MMS data call.");
     if (this.connected) {
       this.radioInterface.deactivateDataCallByType("mms");
     }
   },
 
   init: function() {
-    Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic,
+    Services.obs.addObserver(this, kNetworkConnStateChangedTopic,
                              false);
     Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     this.settings.forEach(function(name) {
       Services.prefs.addObserver(name, this, false);
     }, this);
 
     try {
       this.radioDisabled = Services.prefs.getBoolPref(kPrefRilRadioDisabled);
@@ -401,29 +401,29 @@ MmsConnection.prototype = {
         initWithCallback(this.onDisconnectTimerTimeout.bind(this),
                          PREF_TIME_TO_RELEASE_MMS_CONNECTION,
                          Ci.nsITimer.TYPE_ONE_SHOT);
     }
   },
 
   shutdown: function() {
     Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
-    Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
+    Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
 
     this.connectTimer.cancel();
     this.flushPendingCallbacks(_HTTP_STATUS_RADIO_DISABLED);
     this.disconnectTimer.cancel();
     this.onDisconnectTimerTimeout();
   },
 
   // nsIObserver
 
   observe: function(subject, topic, data) {
     switch (topic) {
-      case kNetworkInterfaceStateChangedTopic: {
+      case kNetworkConnStateChangedTopic: {
         // The network for MMS connection must be nsIRilNetworkInterface.
         if (!(subject instanceof Ci.nsIRilNetworkInterface)) {
           return;
         }
 
         // Check if the network state change belongs to this service.
         let network = subject.QueryInterface(Ci.nsIRilNetworkInterface);
         if (network.serviceId != this.serviceId) {
--- a/dom/mobilemessage/tests/marionette/head.js
+++ b/dom/mobilemessage/tests/marionette/head.js
@@ -340,107 +340,30 @@ function sendTextSmsToEmulator(aFrom, aT
  * @return A deferred promise.
  */
 function sendRawSmsToEmulator(aPdu) {
   let command = "sms pdu " + aPdu;
   return runEmulatorCmdSafe(command);
 }
 
 /**
- * Name space for MobileMessageDB.jsm.  Only initialized after first call to
- * newMobileMessageDB.
- */
-let MMDB;
-
-// Create a new MobileMessageDB instance.
-function newMobileMessageDB() {
-  if (!MMDB) {
-    MMDB = Cu.import("resource://gre/modules/MobileMessageDB.jsm", {});
-    is(typeof MMDB.MobileMessageDB, "function", "MMDB.MobileMessageDB");
-  }
-
-  let mmdb = new MMDB.MobileMessageDB();
-  ok(mmdb, "MobileMessageDB instance");
-  return mmdb;
-}
-
-/**
- * Initialize a MobileMessageDB.  Resolve if initialized with success, reject
- * otherwise.
- *
- * Fulfill params: a MobileMessageDB instance.
- * Reject params: a MobileMessageDB instance.
- *
- * @param aMmdb
- *        A MobileMessageDB instance.
- * @param aDbName
- *        A string name for that database.
- * @param aDbVersion
- *        The version that MobileMessageDB should upgrade to. 0 for the lastest
- *        version.
- *
- * @return A deferred promise.
- */
-function initMobileMessageDB(aMmdb, aDbName, aDbVersion) {
-  let deferred = Promise.defer();
-
-  aMmdb.init(aDbName, aDbVersion, function(aError) {
-    if (aError) {
-      deferred.reject(aMmdb);
-    } else {
-      deferred.resolve(aMmdb);
-    }
-  });
-
-  return deferred.promise;
-}
-
-/**
- * Close a MobileMessageDB.
- *
- * @return The passed MobileMessageDB instance.
- */
-function closeMobileMessageDB(aMmdb) {
-  aMmdb.close();
-  return aMmdb;
-}
-
-/**
  * Create a new array of id attribute of input messages.
  *
  * @param aMessages an array of {Sms,Mms}Message instances.
  *
  * @return an array of numeric values.
  */
 function messagesToIds(aMessages) {
   let ids = [];
   for (let message of aMessages) {
     ids.push(message.id);
   }
   return ids;
 }
 
-// A reference to a nsIUUIDGenerator service.
-let uuidGenerator;
-
-/**
- * Generate a new UUID.
- *
- * @return A UUID string.
- */
-function newUUID() {
-  if (!uuidGenerator) {
-    uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
-                    .getService(Ci.nsIUUIDGenerator);
-    ok(uuidGenerator, "uuidGenerator");
-  }
-
-  return uuidGenerator.generateUUID().toString();
-}
-
 /**
  * Flush permission settings and call |finish()|.
  */
 function cleanUp() {
   waitFor(function() {
     SpecialPowers.flushPermissions(function() {
       // Use ok here so that we have at least one test run.
       ok(true, "permissions flushed");
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/tests/marionette/mmdb_head.js
@@ -0,0 +1,352 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_CONTEXT = "chrome";
+
+let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
+
+/**
+ * Name space for MobileMessageDB.jsm.  Only initialized after first call to
+ * newMobileMessageDB.
+ */
+let MMDB;
+
+/**
+ * Create a new MobileMessageDB instance.
+ *
+ * @return A MobileMessageDB instance.
+ */
+function newMobileMessageDB() {
+  if (!MMDB) {
+    MMDB = Cu.import("resource://gre/modules/MobileMessageDB.jsm", {});
+    is(typeof MMDB.MobileMessageDB, "function", "MMDB.MobileMessageDB");
+  }
+
+  let mmdb = new MMDB.MobileMessageDB();
+  ok(mmdb, "MobileMessageDB instance");
+  return mmdb;
+}
+
+/**
+ * Initialize a MobileMessageDB.  Resolve if initialized with success, reject
+ * otherwise.
+ *
+ * Fulfill params: a MobileMessageDB instance.
+ * Reject params: a MobileMessageDB instance.
+ *
+ * @param aMmdb
+ *        A MobileMessageDB instance.
+ * @param aDbName
+ *        A string name for that database.
+ * @param aDbVersion
+ *        The version that MobileMessageDB should upgrade to. 0 for the lastest
+ *        version.
+ *
+ * @return A deferred promise.
+ */
+function initMobileMessageDB(aMmdb, aDbName, aDbVersion) {
+  let deferred = Promise.defer();
+
+  aMmdb.init(aDbName, aDbVersion, function(aError) {
+    if (aError) {
+      deferred.reject(aMmdb);
+    } else {
+      deferred.resolve(aMmdb);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Close a MobileMessageDB.
+ *
+ * @param aMmdb
+ *        A MobileMessageDB instance.
+ *
+ * @return The passed MobileMessageDB instance.
+ */
+function closeMobileMessageDB(aMmdb) {
+  aMmdb.close();
+  return aMmdb;
+}
+
+/**
+ * Utility function for calling MMDB methods that takes either a
+ * nsIRilMobileMessageDatabaseCallback or a
+ * nsIRilMobileMessageDatabaseRecordCallback.
+ *
+ * Resolve when the target method notifies us with a successful result code;
+ * reject otherwise. In either case, the arguments passed are packed into an
+ * array and propagated to next action.
+ *
+ * Fulfill params: an array whose elements are the arguments of the original
+ *                 callback.
+ * Reject params: same as fulfill params.
+ *
+ * @param aMmdb
+ *        A MobileMessageDB instance.
+ * @param aMethodName
+ *        A string name for that target method.
+ * @param ...
+ *        Extra arguments to pass to that target method. The last callback
+ *        argument should always be excluded.
+ *
+ * @return A deferred promise.
+ */
+function callMmdbMethod(aMmdb, aMethodName) {
+  let deferred = Promise.defer();
+
+  let args = Array.slice(arguments, 2);
+  args.push({
+    notify: function(aRv) {
+      if (!Components.isSuccessCode(aRv)) {
+        ok(true, aMethodName + " returns a unsuccessful code: " + aRv);
+        deferred.reject(Array.slice(arguments));
+      } else {
+        ok(true, aMethodName + " returns a successful code: " + aRv);
+        deferred.resolve(Array.slice(arguments));
+      }
+    }
+  });
+  aMmdb[aMethodName].apply(aMmdb, args);
+
+  return deferred.promise;
+}
+
+/**
+ * A convenient function for calling |mmdb.saveSendingMessage(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function saveSendingMessage(aMmdb, aMessage) {
+  return callMmdbMethod(aMmdb, "saveSendingMessage", aMessage);
+}
+
+/**
+ * A convenient function for calling |mmdb.saveReceivedMessage(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function saveReceivedMessage(aMmdb, aMessage) {
+  return callMmdbMethod(aMmdb, "saveReceivedMessage", aMessage);
+}
+
+/**
+ * A convenient function for calling |mmdb.setMessageDeliveryByMessageId(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function setMessageDeliveryByMessageId(aMmdb, aMessageId, aReceiver, aDelivery,
+                                       aDeliveryStatus, aEnvelopeId) {
+  return callMmdbMethod(aMmdb, "setMessageDeliveryByMessageId", aMessageId,
+                        aReceiver, aDelivery, aDeliveryStatus, aEnvelopeId);
+}
+
+/**
+ * A convenient function for calling
+ * |mmdb.setMessageDeliveryStatusByEnvelopeId(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function setMessageDeliveryStatusByEnvelopeId(aMmdb, aEnvelopeId, aReceiver,
+                                              aDeliveryStatus) {
+  return callMmdbMethod(aMmdb, "setMessageDeliveryStatusByEnvelopeId",
+                        aMmdb, aEnvelopeId, aReceiver, aDeliveryStatus);
+}
+
+/**
+ * A convenient function for calling
+ * |mmdb.setMessageReadStatusByEnvelopeId(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function setMessageReadStatusByEnvelopeId(aMmdb, aEnvelopeId, aReceiver,
+                                          aReadStatus) {
+  return callMmdbMethod(aMmdb, "setMessageReadStatusByEnvelopeId",
+                        aEnvelopeId, aReceiver, aReadStatus);
+}
+
+/**
+ * A convenient function for calling
+ * |mmdb.getMessageRecordByTransactionId(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DB Record>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function getMessageRecordByTransactionId(aMmdb, aTransactionId) {
+  return callMmdbMethod(aMmdb, "getMessageRecordByTransactionId",
+                        aTransactionId);
+}
+
+/**
+ * A convenient function for calling |mmdb.getMessageRecordById(...)|.
+ *
+ * Fulfill params: [<Cr.NS_ERROR_FOO>, <DB Record>, <DOM message>].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function getMessageRecordById(aMmdb, aMessageId) {
+  return callMmdbMethod(aMmdb, "getMessageRecordById", aMessageId);
+}
+
+/**
+ * A convenient function for calling |mmdb.markMessageRead(...)|.
+ *
+ * Fulfill params: Ci.nsIMobileMessageCallback.FOO.
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function markMessageRead(aMmdb, aMessageId, aRead) {
+  let deferred = Promise.defer();
+
+  aMmdb.markMessageRead(aMessageId, aRead, false, {
+    notifyMarkMessageReadFailed: function(aRv) {
+      ok(true, "markMessageRead returns a unsuccessful code: " + aRv);
+      deferred.reject(aRv);
+    },
+
+    notifyMessageMarkedRead: function(aRead) {
+      ok(true, "markMessageRead returns a successful code: " + Cr.NS_OK);
+      deferred.resolve(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/**
+ * Utility function for calling cursor-based MMDB methods.
+ *
+ * Resolve when the target method notifies us with |notifyCursorDone|,
+ * reject otherwise.
+ *
+ * Fulfill params: [<Ci.nsIMobileMessageCallback.FOO>, [<DOM message/thread>]]
+ * Reject params: same as fulfill params.
+ *
+ * @param aMmdb
+ *        A MobileMessageDB instance.
+ * @param aMethodName
+ *        A string name for that target method.
+ * @param ...
+ *        Extra arguments to pass to that target method. The last callback
+ *        argument should always be excluded.
+ *
+ * @return A deferred promise.
+ */
+function createMmdbCursor(aMmdb, aMethodName) {
+  let deferred = Promise.defer();
+
+  let cursor;
+  let results = [];
+  let args = Array.slice(arguments, 2);
+  args.push({
+    notifyCursorError: function(aRv) {
+      ok(true, "notifyCursorError: " + aRv);
+      deferred.reject([aRv, results]);
+    },
+
+    notifyCursorResult: function(aResult) {
+      ok(true, "notifyCursorResult: " + aResult.id);
+      results.push(aResult);
+      cursor.handleContinue();
+    },
+
+    notifyCursorDone: function() {
+      ok(true, "notifyCursorDone");
+      deferred.resolve([Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR, results]);
+    }
+  });
+
+  cursor = aMmdb[aMethodName].apply(aMmdb, args);
+
+  return deferred.promise;
+}
+
+/**
+ * A convenient function for calling |mmdb.createMessageCursor(...)|.
+ *
+ * Fulfill params: [<Ci.nsIMobileMessageCallback.FOO>, [<DOM message>]].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function createMessageCursor(aMmdb, aFilter, aReverse) {
+  return createMmdbCursor(aMmdb, "createMessageCursor", aFilter, aReverse);
+}
+
+/**
+ * A convenient function for calling |mmdb.createThreadCursor(...)|.
+ *
+ * Fulfill params: [<Ci.nsIMobileMessageCallback.FOO>, [<DOM thread>]].
+ * Reject params: same as fulfill params.
+ *
+ * @return A deferred promise.
+ */
+function createThreadCursor(aMmdb) {
+  return createMmdbCursor(aMmdb, "createThreadCursor");
+}
+
+// A reference to a nsIUUIDGenerator service.
+let _uuidGenerator;
+
+/**
+ * Generate a new UUID.
+ *
+ * @return A UUID string.
+ */
+function newUUID() {
+  if (!_uuidGenerator) {
+    _uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
+                     .getService(Ci.nsIUUIDGenerator);
+    ok(_uuidGenerator, "uuidGenerator");
+  }
+
+  return _uuidGenerator.generateUUID().toString();
+}
+
+/**
+ * Flush permission settings and call |finish()|.
+ */
+function cleanUp() {
+  // Use ok here so that we have at least one test run.
+  ok(true, "permissions flushed");
+
+  finish();
+}
+
+/**
+ * Basic test routine helper for mobile message tests.
+ *
+ * This helper does nothing but clean-ups.
+ *
+ * @param aTestCaseMain
+ *        A function that takes no parameter.
+ */
+function startTestBase(aTestCaseMain) {
+  Promise.resolve()
+    .then(aTestCaseMain)
+    .then(null, function() {
+      ok(false, 'promise rejects during test.');
+    })
+    .then(cleanUp);
+}
--- a/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js
+++ b/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js
@@ -1,17 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
-MARIONETTE_HEAD_JS = 'head.js';
+MARIONETTE_HEAD_JS = 'mmdb_head.js';
 
-// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
-// empty object in return.
-let mmdb;
+const DBNAME = "test_mmdb_foreachmatchedmmsdeliveryinfo:" + newUUID();
 
 const PHONE_0 = "+15555215500";
 const PHONE_1 = "+15555215501";
 const PHONE_2 = "+15555215502";
 const PHONE_2_NET = "5555215502";
 const PHONE_3 = "+15555215503";
 const PHONE_3_NET = "5555215503";
 const EMAIL_1 = "foo@bar.com";
@@ -46,69 +44,52 @@ function doTest(aMmdb, aNeedle, aVerifyF
     ++count;
   });
   is(count, aCount, "matched count");
 }
 
 function testNotFound(aMmdb) {
   log("Testing unavailable");
 
-  // TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
-  // empty object in return.
-  aMmdb = mmdb;
-
   doTest(aMmdb, PHONE_0, function(aElement) {
     ok(false, "Should never have a match");
   }, 0);
-
-  return aMmdb;
 }
 
 function testDirectMatch(aMmdb) {
   log("Testing direct matching");
 
-  // TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
-  // empty object in return.
-  aMmdb = mmdb;
-
   for (let needle of [PHONE_1, EMAIL_1]) {
     let count = deliveryInfo.reduce(function(aCount, aElement) {
       return aElement.receiver == needle ? aCount + 1 : aCount;
     }, 0);
     doTest(aMmdb, needle, function(aElement) {
       is(aElement.receiver, needle, "element.receiver");
     }, count);
   }
-
-  return aMmdb;
 }
 
 function testPhoneMatch(aMmdb) {
   log("Testing phone matching");
 
   let verifyFunc = function(aValid, aElement) {
     ok(aValid.indexOf(aElement.receiver) >= 0, "element.receiver");
   };
-  // TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
-  // empty object in return.
-  aMmdb = mmdb;
 
   let matchingGroups = [
     [PHONE_2, PHONE_2_NET],
     [PHONE_3, PHONE_3_NET],
   ];
   for (let group of matchingGroups) {
     for (let item of group) {
       doTest(aMmdb, item, verifyFunc.bind(null, group), group.length);
     }
   }
-
-  return aMmdb;
 }
 
 startTestBase(function testCaseMain() {
-  mmdb = newMobileMessageDB();
-  return initMobileMessageDB(mmdb, "test_mmdb_foreachmatchedmmsdeliveryinfo", 0)
-    .then(testNotFound)
-    .then(testDirectMatch)
-    .then(testPhoneMatch)
-    .then(closeMobileMessageDB.bind(null, mmdb));
+  let mmdb = newMobileMessageDB();
+  return initMobileMessageDB(mmdb, DBNAME, 0)
+    .then(() => testNotFound(mmdb))
+    .then(() => testDirectMatch(mmdb))
+    .then(() => testPhoneMatch(mmdb))
+    .then(() => closeMobileMessageDB(mmdb));
 });
--- a/dom/mobilemessage/tests/marionette/test_mmdb_full_storage.js
+++ b/dom/mobilemessage/tests/marionette/test_mmdb_full_storage.js
@@ -1,89 +1,119 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
-MARIONETTE_HEAD_JS = 'head.js';
+MARIONETTE_HEAD_JS = 'mmdb_head.js';
 
-let gMmdb;
+const DBNAME = "test_mmdb_full_storage:" + newUUID();
 
 let gIsDiskFull = true;
 
-let gMessageToSave = {
-  type: "sms",
-  sender: "+0987654321",
-  receiver: "+1234567890",
-  body: "quick fox jump over the lazy dog",
-  deliveryStatusRequested: false,
-  timestamp: Date.now(),
-  iccId: "1029384756"
-};
+function newSavableMessage() {
+  return {
+    type: "sms",
+    sender: "+0987654321",
+    receiver: "+1234567890",
+    body: "quick fox jump over the lazy dog",
+    deliveryStatusRequested: false,
+    messageClass: "normal",
+    timestamp: Date.now(),
+    iccId: "1029384756"
+  };
+}
 
-function testSaveSendingMessage() {
+function isFileNoDeviceSpaceError(aErrorResult) {
+  is(aErrorResult, Cr.NS_ERROR_FILE_NO_DEVICE_SPACE, "Database error code");
+}
+
+function isCallbackStorageFullError(aErrorCode) {
+  is(aErrorCode, Ci.nsIMobileMessageCallback.STORAGE_FULL_ERROR,
+     "nsIMobileMessageCallback error code");
+}
+
+function testSaveSendingMessage(aMmdb) {
   log("testSaveSendingMessage()");
 
-  let deferred = Promise.defer();
-
-  gMmdb.saveSendingMessage(gMessageToSave,
-                          { notify : function(aRv, aDomMessage) {
-    if (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE) {
-      ok(true, "Forbidden due to storage full.");
-      deferred.resolve(Promise.resolve());
-    } else {
-      ok(false, "Unexpected result: " + aRv);
-      deferred.reject(aRv);
-    }
-  }});
-
-  return deferred.promise;
+  gIsDiskFull = true;
+  return saveSendingMessage(aMmdb, newSavableMessage())
+    // Resolved/rejected results are both [<Cr.NS_ERROR_FOO>, <DOM message>],
+    // and we need only the error code in both cases.
+    .then((aValue) => aValue[0],
+          (aValue) => aValue[0])
+    .then(isFileNoDeviceSpaceError);
 }
 
-function testSaveReceivingMessage() {
-  log("testSaveReceivingMessage()");
+function testSaveReceivedMessage(aMmdb) {
+  log("testSaveReceivedMessage()");
 
-  let deferred = Promise.defer();
+  gIsDiskFull = true;
+  return saveReceivedMessage(aMmdb, newSavableMessage())
+    // Resolved/rejected results are both [<Cr.NS_ERROR_FOO>, <DOM message>],
+    // and we need only the error code in both cases.
+    .then((aValue) => aValue[0],
+          (aValue) => aValue[0])
+    .then(isFileNoDeviceSpaceError);
+}
 
-  gMmdb.saveReceivedMessage(gMessageToSave,
-                            { notify : function(aRv, aDomMessage) {
-    if (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE) {
-      ok(true, "Forbidden due to storage full.");
-      deferred.resolve(Promise.resolve());
-    } else {
-      ok(false, "Unexpected result: " + aRv);
-      deferred.reject(aRv);
-    }
-  }});
+function testGetMessageRecordById(aMmdb) {
+  log("testGetMessageRecordById()");
 
-  return deferred.promise;
+  gIsDiskFull = false;
+  return saveReceivedMessage(aMmdb, newSavableMessage())
+    // Resolved result is [<Cr.NS_ERROR_FOO>, <DOM message>],
+    .then(function(aValue) {
+      let domMessage = aValue[1];
+
+      gIsDiskFull = true;
+      return getMessageRecordById(aMmdb, domMessage.id);
+    });
 }
 
-function testGetMessage() {
-  log("testGetMessage()");
+function testMarkMessageRead(aMmdb) {
+  log("testMarkMessageRead()");
 
-  let deferred = Promise.defer();
+  gIsDiskFull = false;
+  return saveReceivedMessage(aMmdb, newSavableMessage())
+    // Resolved/rejected results are both [<Cr.NS_ERROR_FOO>, <DOM message>].
+    .then(function(aValue) {
+      let domMessage = aValue[1];
 
-  gMmdb.getMessage(1,
-                   { notifyGetMessageFailed : function(aRv) {
-    if (aRv === Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR) {
-      ok(true, "Getting message successfully!");
-      deferred.resolve(Promise.resolve());
-    } else {
-      ok(false, "Unexpected result: " + aRv);
-      deferred.reject(aRv);
-    }
-  }});
+      gIsDiskFull = true;
+      return markMessageRead(aMmdb, domMessage.id, true)
+        .then(null, (aValue) => aValue)
+        .then(isCallbackStorageFullError);
+    });
+}
+
+function testCreateMessageCursor(aMmdb) {
+  log("testCreateMessageCursor()");
 
-  return deferred.promise;
+  gIsDiskFull = true;
+  return createMessageCursor(aMmdb, {}, false);
+}
+
+function testCreateThreadCursor(aMmdb) {
+  log("testCreateThreadCursor()");
+
+  gIsDiskFull = true;
+  return createThreadCursor(aMmdb);
 }
 
 startTestBase(function testCaseMain() {
 
-  gMmdb = newMobileMessageDB();
-  gMmdb.isDiskFull = function() {
-    return gIsDiskFull;
-  };
-  return initMobileMessageDB(gMmdb, "test_gMmdb_full_storage", 0)
-         .then(testSaveSendingMessage)
-         .then(testSaveReceivingMessage)
-         .then(testGetMessage)
-         .then(closeMobileMessageDB.bind(null, gMmdb));
+  let mmdb = newMobileMessageDB();
+  return initMobileMessageDB(mmdb, DBNAME, 0)
+    .then(function() {
+      mmdb.isDiskFull = function() {
+        return gIsDiskFull;
+      };
+    })
+
+    .then(() => testSaveSendingMessage(mmdb))
+    .then(() => testSaveReceivedMessage(mmdb))
+    .then(() => testGetMessageRecordById(mmdb))
+    .then(() => testMarkMessageRead(mmdb))
+    .then(() => testCreateMessageCursor(mmdb))
+    .then(() => testCreateThreadCursor(mmdb))
+
+    .then(() => closeMobileMessageDB(mmdb));
 });
--- a/dom/mobilemessage/tests/marionette/test_mmdb_new.js
+++ b/dom/mobilemessage/tests/marionette/test_mmdb_new.js
@@ -1,36 +1,36 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
-MARIONETTE_HEAD_JS = 'head.js';
+MARIONETTE_HEAD_JS = 'mmdb_head.js';
+
+const DBNAME = "test_mmdb_new:" + newUUID();
+let dbVersion = 0;
+
+function check(aMmdb) {
+  is(aMmdb.dbName, DBNAME, "dbName");
+  if (!dbVersion) {
+    ok(aMmdb.dbVersion, "dbVersion");
+    dbVersion = aMmdb.dbVersion;
+  } else {
+    is(aMmdb.dbVersion, dbVersion, "dbVersion");
+  }
+}
 
 startTestBase(function testCaseMain() {
   log("Test init MobileMessageDB");
 
-  // TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
-  // empty object in return.
   let mmdb = newMobileMessageDB();
-  let dbName = "test_mmdb_new";
-  let dbVersion = 0;
-  let check = function() {
-    is(mmdb.dbName, dbName, "dbName");
-    if (!dbVersion) {
-      ok(mmdb.dbVersion, "dbVersion");
-      dbVersion = mmdb.dbVersion;
-    } else {
-      is(mmdb.dbVersion, dbVersion, "dbVersion");
-    }
-  };
+  return initMobileMessageDB(mmdb, DBNAME, dbVersion)
+    .then(() => check(mmdb))
+    .then(() => closeMobileMessageDB(mmdb))
+    .then(() => check(mmdb))
 
-  return initMobileMessageDB(mmdb, dbName, dbVersion)
-    .then(check)
-    .then(closeMobileMessageDB.bind(null, mmdb))
-    .then(check)
     .then(function() {
       log("Test re-init and close.");
-      return initMobileMessageDB(mmdb, dbName, dbVersion);
+      return initMobileMessageDB(mmdb, DBNAME, dbVersion);
     })
-    .then(check)
-    .then(closeMobileMessageDB.bind(null, mmdb))
-    .then(check);
+    .then(() => check(mmdb))
+    .then(() => closeMobileMessageDB(mmdb))
+    .then(() => check(mmdb));
 });
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -35,16 +35,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 const TOPIC_INTERFACE_STATE_CHANGED  = "network-interface-state-changed";
 const TOPIC_INTERFACE_REGISTERED     = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED   = "network-interface-unregistered";
 const TOPIC_ACTIVE_CHANGED           = "network-active-changed";
 const TOPIC_MOZSETTINGS_CHANGED      = "mozsettings-changed";
 const TOPIC_PREF_CHANGED             = "nsPref:changed";
 const TOPIC_XPCOM_SHUTDOWN           = "xpcom-shutdown";
+const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
 const PREF_MANAGE_OFFLINE_STATUS     = "network.gonk.manage-offline-status";
 
 const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
 const DEFAULT_USB_INTERFACE_NAME  = "rndis0";
 const DEFAULT_3G_INTERFACE_NAME   = "rmnet0";
 const DEFAULT_WIFI_INTERFACE_NAME = "wlan0";
 
 // The kernel's proc entry for network lists.
@@ -271,16 +272,21 @@ NetworkManager.prototype = {
             if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
               for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
                 this.mRil.getRadioInterface(i).updateRILNetworkInterface();
               }
             }
 #endif
             break;
         }
+#ifdef MOZ_B2G_RIL
+        // Notify outer modules like MmsService to start the transaction after
+        // the configuration of the network interface is done.
+        Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED, null);
+#endif
         break;
 #ifdef MOZ_B2G_RIL
       case TOPIC_INTERFACE_REGISTERED:
         let regNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
         // Add extra host route. For example, mms proxy or mmsc.
         this.setExtraHostRoute(regNetwork);
         // Dun type is a special case where we add the default route to a
         // secondary table.
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -64,16 +64,17 @@ const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
 const GSMICCINFO_CID =
   Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
 const CDMAICCINFO_CID =
   Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID      = "xpcom-shutdown";
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
+const kNetworkConnStateChangedTopic      = "network-connection-state-changed";
 const kSmsReceivedObserverTopic          = "sms-received";
 const kSilentSmsReceivedObserverTopic    = "silent-sms-received";
 const kSmsSendingObserverTopic           = "sms-sending";
 const kSmsSentObserverTopic              = "sms-sent";
 const kSmsFailedObserverTopic            = "sms-failed";
 const kSmsDeliverySuccessObserverTopic   = "sms-delivery-success";
 const kSmsDeliveryErrorObserverTopic     = "sms-delivery-error";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
@@ -1896,17 +1897,17 @@ function RadioInterface(aClientId, aWork
   // Read the Cell Broadcast Search List setting, string of integers or integer
   // ranges separated by comma, to set listening channels.
   lock.get(kSettingsCellBroadcastSearchList, this);
 
   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
   Services.obs.addObserver(this, kSysClockChangeObserverTopic, false);
   Services.obs.addObserver(this, kScreenStateChangedTopic, false);
 
-  Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic, false);
+  Services.obs.addObserver(this, kNetworkConnStateChangedTopic, false);
   Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false);
 
   this.portAddressedSmsApps = {};
   this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
 
   this._sntp = new Sntp(this.setClockBySntp.bind(this),
                         Services.prefs.getIntPref("network.sntp.maxRetryCount"),
                         Services.prefs.getIntPref("network.sntp.refreshPeriod"),
@@ -1935,17 +1936,17 @@ RadioInterface.prototype = {
 
   shutdown: function() {
     // Release the CPU wake lock for handling the received SMS.
     this._releaseSmsHandledWakeLock();
 
     Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
     Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
     Services.obs.removeObserver(this, kScreenStateChangedTopic);
-    Services.obs.removeObserver(this, kNetworkInterfaceStateChangedTopic);
+    Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
   },
 
   /**
    * A utility function to copy objects. The srcInfo may contain
    * "rilMessageType", should ignore it.
    */
   updateInfo: function(srcInfo, destInfo) {
     for (let key in srcInfo) {
@@ -3177,17 +3178,17 @@ RadioInterface.prototype = {
         break;
       case kSysClockChangeObserverTopic:
         let offset = parseInt(data, 10);
         if (this._lastNitzMessage) {
           this._lastNitzMessage.receiveTimeInMS += offset;
         }
         this._sntp.updateOffset(offset);
         break;
-      case kNetworkInterfaceStateChangedTopic:
+      case kNetworkConnStateChangedTopic:
         let network = subject.QueryInterface(Ci.nsINetworkInterface);
         if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
           return;
         }
 
         // SNTP can only update when we have mobile or Wifi connections.
         if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI &&
             network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
--- a/dom/system/gonk/tests/marionette/test_data_connection.js
+++ b/dom/system/gonk/tests/marionette/test_data_connection.js
@@ -63,17 +63,17 @@ function setEmulatorAPN() {
   ];
 
   return setSetting(APN_KEY, apn);
 }
 
 function waitNetworkConnected(networkType) {
   log("wait network " + networkType + " connected");
 
-  let interfaceStateChangeTopic = "network-interface-state-changed";
+  let interfaceStateChangeTopic = "network-connection-state-changed";
   let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
   let deferred = Promise.defer();
 
   function observer(subject, topic, data) {
     let network = subject.QueryInterface(Ci.nsINetworkInterface);
     log("Network " + network.type + " state changes to " + network.state);
     if (network.type == networkType &&
         network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
@@ -85,17 +85,17 @@ function waitNetworkConnected(networkTyp
   obs.addObserver(observer, interfaceStateChangeTopic, false);
 
   return deferred.promise;
 }
 
 function waitNetworkDisconnected(networkType) {
   log("wait network " + networkType + " disconnected");
 
-  let interfaceStateChangeTopic = "network-interface-state-changed";
+  let interfaceStateChangeTopic = "network-connection-state-changed";
   let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
   let deferred = Promise.defer();
 
   function observer(subject, topic, data) {
     let network = subject.QueryInterface(Ci.nsINetworkInterface);
     log("Network " + network.type + " state changes to " + network.state);
     // We can not check network.type here cause network.type would return
     // NETWORK_TYPE_MOBILE_SUPL (NETWORK_TYPE_MOBILE_OTHERS) when disconnecting
--- a/dom/webidl/OscillatorNode.webidl
+++ b/dom/webidl/OscillatorNode.webidl
@@ -25,19 +25,19 @@ interface OscillatorNode : AudioNode {
 
     [SetterThrows]
     attribute OscillatorType type;
 
     readonly attribute AudioParam frequency; // in Hertz
     readonly attribute AudioParam detune; // in Cents
 
     [Throws]
-    void start(double when);
+    void start(optional double when = 0);
     [Throws]
-    void stop(double when);
+    void stop(optional double when = 0);
     void setPeriodicWave(PeriodicWave periodicWave);
 
     attribute EventHandler onended;
 
 };
 
 /*
  * The origin of this IDL file is
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -33,16 +33,18 @@ dictionary RTCRTPStreamStats : RTCStats 
   DOMString codecId;
 };
 
 dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
   unsigned long packetsReceived;
   unsigned long long bytesReceived;
   double jitter;
   unsigned long packetsLost;
+  long mozAvSyncDelay;
+  long mozJitterBufferDelay;
 };
 
 dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
   unsigned long packetsSent;
   unsigned long long bytesSent;
 };
 
 dictionary RTCMediaStreamTrackStats : RTCStats {
--- a/gfx/angle/README.mozilla
+++ b/gfx/angle/README.mozilla
@@ -52,16 +52,19 @@ In this order:
     Note that a different version of this patch was upstreamed, so the next time that
     Angle is updated this patch can be discarded.  See:
     https://chromium.googlesource.com/angle/angle/+/0dd3b3ff66cdc50882125d21e60112d5161279b4
     https://chromium.googlesource.com/angle/angle/+/0685fbde65a3e90d8d4d4a6c72f2cc1771617fd0
 
   angle-fix-vc12.patch:
     Fixes angle to build on Visual Studio 2013
 
+  angle-d3dcc47.patch:
+    Tell ANGLE about d3dcompiler_47.dll from WinSDK 8.1.
+
 In addition to these patches, the Makefile.in and moz.build build files are ours,
 they're not present in upsteam ANGLE. Therefore, changes made to the build files
 should not be stored in the local .patch files.
 
 
 == How to do a clean-slate upgrade ==
 1.  Backup our moz-specific files:
       README.mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/angle/angle-d3dcc47.patch
@@ -0,0 +1,69 @@
+From: Jeff Gilbert <jgilbert@mozilla.com>
+Bug 982973 - Tell ANGLE about d3dcompiler_47.dll. - r=vlad
+
+diff --git a/gfx/angle/src/libGLESv2/renderer/Renderer.cpp b/gfx/angle/src/libGLESv2/renderer/Renderer.cpp
+--- a/gfx/angle/src/libGLESv2/renderer/Renderer.cpp
++++ b/gfx/angle/src/libGLESv2/renderer/Renderer.cpp
+@@ -17,16 +17,17 @@
+ 
+ #if !defined(ANGLE_ENABLE_D3D11)
+ // Enables use of the Direct3D 11 API for a default display, when available
+ #define ANGLE_ENABLE_D3D11 0
+ #endif
+ 
+ #define ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES \
+     {                                            \
++        TEXT("d3dcompiler_47.dll"),              \
+         TEXT("d3dcompiler_46.dll"),              \
+         TEXT("d3dcompiler_43.dll")               \
+     }
+ 
+ 
+ namespace rx
+ {
+ 
+@@ -173,23 +174,23 @@ ShaderBlob *Renderer::compileToBinary(gl
+ 
+ extern "C"
+ {
+ 
+ rx::Renderer *glCreateRenderer(egl::Display *display, HDC hDc, EGLNativeDisplayType displayId)
+ {
+     rx::Renderer *renderer = NULL;
+     EGLint status = EGL_BAD_ALLOC;
+-    
++
+     if (ANGLE_ENABLE_D3D11 ||
+         displayId == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ||
+         displayId == EGL_D3D11_ONLY_DISPLAY_ANGLE)
+     {
+         renderer = new rx::Renderer11(display, hDc);
+-    
++
+         if (renderer)
+         {
+             status = renderer->initialize();
+         }
+ 
+         if (status == EGL_SUCCESS)
+         {
+             return renderer;
+@@ -200,17 +201,17 @@ rx::Renderer *glCreateRenderer(egl::Disp
+         }
+ 
+         // Failed to create a D3D11 renderer, try creating a D3D9 renderer
+         delete renderer;
+     }
+ 
+     bool softwareDevice = (displayId == EGL_SOFTWARE_DISPLAY_ANGLE);
+     renderer = new rx::Renderer9(display, hDc, softwareDevice);
+-    
++
+     if (renderer)
+     {
+         status = renderer->initialize();
+     }
+ 
+     if (status == EGL_SUCCESS)
+     {
+         return renderer;
--- a/gfx/angle/src/libGLESv2/renderer/Renderer.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/Renderer.cpp
@@ -17,16 +17,17 @@
 
 #if !defined(ANGLE_ENABLE_D3D11)
 // Enables use of the Direct3D 11 API for a default display, when available
 #define ANGLE_ENABLE_D3D11 0
 #endif
 
 #define ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES \
     {                                            \
+        TEXT("d3dcompiler_47.dll"),              \
         TEXT("d3dcompiler_46.dll"),              \
         TEXT("d3dcompiler_43.dll")               \
     }
 
 
 namespace rx
 {
 
@@ -173,23 +174,23 @@ ShaderBlob *Renderer::compileToBinary(gl
 
 extern "C"
 {
 
 rx::Renderer *glCreateRenderer(egl::Display *display, HDC hDc, EGLNativeDisplayType displayId)
 {
     rx::Renderer *renderer = NULL;
     EGLint status = EGL_BAD_ALLOC;
-    
+
     if (ANGLE_ENABLE_D3D11 ||
         displayId == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ||
         displayId == EGL_D3D11_ONLY_DISPLAY_ANGLE)
     {
         renderer = new rx::Renderer11(display, hDc);
-    
+
         if (renderer)
         {
             status = renderer->initialize();
         }
 
         if (status == EGL_SUCCESS)
         {
             return renderer;
@@ -200,17 +201,17 @@ rx::Renderer *glCreateRenderer(egl::Disp
         }
 
         // Failed to create a D3D11 renderer, try creating a D3D9 renderer
         delete renderer;
     }
 
     bool softwareDevice = (displayId == EGL_SOFTWARE_DISPLAY_ANGLE);
     renderer = new rx::Renderer9(display, hDc, softwareDevice);
-    
+
     if (renderer)
     {
         status = renderer->initialize();
     }
 
     if (status == EGL_SUCCESS)
     {
         return renderer;
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -66,51 +66,51 @@ public:
   static const ViewID START_SCROLL_ID = 2;  // This is the ID that scrolling subframes
                                         // will begin at.
 
   FrameMetrics()
     : mCompositionBounds(0, 0, 0, 0)
     , mDisplayPort(0, 0, 0, 0)
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mViewport(0, 0, 0, 0)
-    , mScrollOffset(0, 0)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollableRect(0, 0, 0, 0)
     , mResolution(1)
     , mCumulativeResolution(1)
-    , mZoom(1)
     , mTransformScale(1)
     , mDevPixelsPerCSSPixel(1)
     , mPresShellId(-1)
     , mMayHaveTouchListeners(false)
     , mIsRoot(false)
     , mHasScrollgrab(false)
+    , mScrollOffset(0, 0)
+    , mZoom(1)
     , mUpdateScrollOffset(false)
     , mScrollGeneration(0)
   {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     // mContentDescription is not compared on purpose as it's only used
     // for debugging.
     return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
            mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
            mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
            mViewport.IsEqualEdges(aOther.mViewport) &&
-           mScrollOffset == aOther.mScrollOffset &&
            mScrollId == aOther.mScrollId &&
            mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
            mResolution == aOther.mResolution &&
            mCumulativeResolution == aOther.mCumulativeResolution &&
            mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
            mMayHaveTouchListeners == aOther.mMayHaveTouchListeners &&
            mPresShellId == aOther.mPresShellId &&
            mIsRoot == aOther.mIsRoot &&
+           mScrollOffset == aOther.mScrollOffset &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
@@ -134,17 +134,17 @@ public:
 
   CSSToLayerScale LayersPixelsPerCSSPixel() const
   {
     return mCumulativeResolution * mDevPixelsPerCSSPixel;
   }
 
   LayerPoint GetScrollOffsetInLayerPixels() const
   {
-    return mScrollOffset * LayersPixelsPerCSSPixel();
+    return GetScrollOffset() * LayersPixelsPerCSSPixel();
   }
 
   LayoutDeviceToParentLayerScale GetParentResolution() const
   {
     return mCumulativeResolution / mResolution;
   }
 
   // Ensure the scrollableRect is at least as big as the compositionBounds
@@ -189,16 +189,26 @@ public:
     return mZoom * mTransformScale;
   }
 
   CSSIntRect CalculateCompositedRectInCssPixels() const
   {
     return gfx::RoundedIn(mCompositionBounds / GetZoomToParent());
   }
 
+  void ScrollBy(const CSSPoint& aPoint)
+  {
+    mScrollOffset += aPoint;
+  }
+
+  void ZoomBy(float aFactor)
+  {
+    mZoom.scale *= aFactor;
+  }
+
   // ---------------------------------------------------------------------------
   // The following metrics are all in widget space/device pixels.
   //
 
   // This is the area within the widget that we're compositing to. It is relative
   // to the layer tree origin.
   //
   // This is useful because, on mobile, the viewport and composition dimensions
@@ -253,33 +263,16 @@ public:
   // method layout uses to scroll content.
   //
   // This is mainly useful on the root layer, however nested iframes can have
   // their own viewport, which will just be the size of the window of the
   // iframe. For layers that don't correspond to a document, this metric is
   // meaningless and invalid.
   CSSRect mViewport;
 
-  // The position of the top-left of the CSS viewport, relative to the document
-  // (or the document relative to the viewport, if that helps understand it).
-  //
-  // Thus it is relative to the document. It is in the same coordinate space as
-  // |mScrollableRect|, but a different coordinate space than |mViewport| and
-  // |mDisplayPort|.
-  //
-  // It is required that the rect:
-  // { x = mScrollOffset.x, y = mScrollOffset.y,
-  //   width = mCompositionBounds.x / mResolution.scale,
-  //   height = mCompositionBounds.y / mResolution.scale }
-  // Be within |mScrollableRect|.
-  //
-  // This is valid for any layer, but is always relative to this frame and
-  // not any parents, regardless of parent transforms.
-  CSSPoint mScrollOffset;
-
   // A unique ID assigned to each scrollable frame.
   ViewID mScrollId;
 
   // The scrollable bounds of a frame. This is determined by reflow.
   // Ordinarily the x and y will be 0 and the width and height will be the
   // size of the element being scrolled. However for RTL pages or elements
   // the x value may be negative.
   //
@@ -300,22 +293,16 @@ public:
   // by Gecko at layout/paint time.
   ParentLayerToLayerScale mResolution;
 
   // The cumulative resolution that the current frame has been painted at.
   // This is the product of our mResolution and the mResolutions of our parent frames.
   // This information is provided by Gecko at layout/paint time.
   LayoutDeviceToLayerScale mCumulativeResolution;
 
-  // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
-  // but will be drawn to the screen at mZoom. In the steady state, the
-  // two will be the same, but during an async zoom action the two may
-  // diverge. This information is initialized in Gecko but updated in the APZC.
-  CSSToScreenScale mZoom;
-
   // The conversion factor between local screen pixels (the coordinate
   // system in which APZCs receive input events) and our parent layer's
   // layer pixels (the coordinate system of mCompositionBounds).
   // This consists of the scale of the local CSS transform and the
   // nontransient async transform.
   // TODO: APZ does not currently work well if there is a CSS transform
   //       on the layer being scrolled that's not just a scale that's
   //       the same in both directions. When we fix this, mTransformScale
@@ -335,16 +322,36 @@ public:
 
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
   // Whether or not this frame is for an element marked 'scrollgrab'.
   bool mHasScrollgrab;
 
 public:
+  void SetScrollOffset(const CSSPoint& aScrollOffset)
+  {
+    mScrollOffset = aScrollOffset;
+  }
+
+  const CSSPoint& GetScrollOffset() const
+  {
+    return mScrollOffset;
+  }
+
+  void SetZoom(const CSSToScreenScale& aZoom)
+  {
+    mZoom = aZoom;
+  }
+
+  CSSToScreenScale GetZoom() const
+  {
+    return mZoom;
+  }
+
   void SetScrollOffsetUpdated(uint32_t aScrollGeneration)
   {
     mUpdateScrollOffset = true;
     mScrollGeneration = aScrollGeneration;
   }
 
   bool GetScrollOffsetUpdated() const
   {
@@ -365,16 +372,39 @@ public:
   {
     mContentDescription = aContentDescription;
   }
 
 private:
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
 
+  // The position of the top-left of the CSS viewport, relative to the document
+  // (or the document relative to the viewport, if that helps understand it).
+  //
+  // Thus it is relative to the document. It is in the same coordinate space as
+  // |mScrollableRect|, but a different coordinate space than |mViewport| and
+  // |mDisplayPort|.
+  //
+  // It is required that the rect:
+  // { x = mScrollOffset.x, y = mScrollOffset.y,
+  //   width = mCompositionBounds.x / mResolution.scale,
+  //   height = mCompositionBounds.y / mResolution.scale }
+  // Be within |mScrollableRect|.
+  //
+  // This is valid for any layer, but is always relative to this frame and
+  // not any parents, regardless of parent transforms.
+  CSSPoint mScrollOffset;
+
+  // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
+  // but will be drawn to the screen at mZoom. In the steady state, the
+  // two will be the same, but during an async zoom action the two may
+  // diverge. This information is initialized in Gecko but updated in the APZC.
+  CSSToScreenScale mZoom;
+
   // Whether mScrollOffset was updated by something other than the APZ code, and
   // if the APZC receiving this metrics should update its local copy.
   bool mUpdateScrollOffset;
   // The scroll generation counter used to acknowledge the scroll offset update.
   uint32_t mScrollGeneration;
 
   // A description of the content element corresponding to this frame.
   // This is empty unless the apz.printtree pref is turned on.
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -700,17 +700,17 @@ SendTextureSource(GLContext* aGLContext,
                   TextureSourceOGL* aSource,
                   bool aFlipY)
 {
     GLenum textureTarget = aSource->GetTextureTarget();
     ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
                                                              aSource->GetFormat());
     int shaderConfig = config.mFeatures;
 
-    aSource->BindTexture(LOCAL_GL_TEXTURE0);
+    aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR);
 
     GLuint textureId = 0;
     // This is horrid hack. It assumes that aGLContext matches the context
     // aSource has bound to.
     if (textureTarget == LOCAL_GL_TEXTURE_2D) {
         aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &textureId);
     } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
         aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &textureId);
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -113,17 +113,17 @@ AppendToString(nsACString& s, const nsIn
 }
 
 nsACString&
 AppendToString(nsACString& s, const FrameMetrics& m,
                const char* pfx, const char* sfx)
 {
   s += pfx;
   AppendToString(s, m.mViewport, "{ viewport=");
-  AppendToString(s, m.mScrollOffset, " viewportScroll=");
+  AppendToString(s, m.GetScrollOffset(), " viewportScroll=");
   AppendToString(s, m.mDisplayPort, " displayport=");
   AppendToString(s, m.mScrollableRect, " scrollableRect=");
   AppendToString(s, m.mScrollId, " scrollId=", " }");
   return s += sfx;
 }
 
 nsACString&
 AppendToString(nsACString& s, const IntSize& size,
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -544,17 +544,17 @@ ClientLayerManager::ProgressiveUpdateCal
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
 
     // This is derived from the code in
     // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
     CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
     const CSSRect& metricsDisplayPort =
       (aDrawingCritical && !metrics.mCriticalDisplayPort.IsEmpty()) ?
         metrics.mCriticalDisplayPort : metrics.mDisplayPort;
-    LayerRect displayPort = (metricsDisplayPort + metrics.mScrollOffset) * paintScale;
+    LayerRect displayPort = (metricsDisplayPort + metrics.GetScrollOffset()) * paintScale;
 
     return AndroidBridge::Bridge()->ProgressiveUpdateCallback(
       aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical,
       aCompositionBounds, aZoom);
   }
 #endif
 
   return false;
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -115,26 +115,26 @@ ClientTiledThebesLayer::BeginPaint()
     LayoutDeviceRect transformedCriticalDisplayPort =
       ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout, criticalDisplayPort);
     mPaintData.mLayoutCriticalDisplayPort =
       LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort));
   }
 
   // Calculate the frame resolution. Because this is Gecko-side, before any
   // async transforms have occurred, we can use mZoom for this.
-  mPaintData.mResolution = metrics.mZoom;
+  mPaintData.mResolution = metrics.GetZoom();
 
   // Calculate the scroll offset since the last transaction, and the
   // composition bounds.
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mScrollOffset.MoveTo(0, 0);
   Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer();
   if (primaryScrollable) {
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
-    mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom;
+    mPaintData.mScrollOffset = metrics.GetScrollOffset() * metrics.GetZoom();
     mPaintData.mCompositionBounds =
       ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout,
                                         ParentLayerRect(metrics.mCompositionBounds));
   }
 }
 
 void
 ClientTiledThebesLayer::EndPaint(bool aFinish)
--- a/gfx/layers/client/SimpleTiledContentClient.cpp
+++ b/gfx/layers/client/SimpleTiledContentClient.cpp
@@ -327,26 +327,26 @@ SimpleClientTiledThebesLayer::BeginPaint
     LayoutDeviceRect transformedCriticalDisplayPort =
       ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout, criticalDisplayPort);
     mPaintData.mLayoutCriticalDisplayPort =
       LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort));
   }
 
   // Calculate the frame resolution. Because this is Gecko-side, before any
   // async transforms have occurred, we can use mZoom for this.
-  mPaintData.mResolution = metrics.mZoom;
+  mPaintData.mResolution = metrics.GetZoom();
 
   // Calculate the scroll offset since the last transaction, and the
   // composition bounds.
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mScrollOffset.MoveTo(0, 0);
   Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer();
   if (primaryScrollable) {
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
-    mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom;
+    mPaintData.mScrollOffset = metrics.GetScrollOffset() * metrics.GetZoom();
     mPaintData.mCompositionBounds =
       ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout,
                                         ParentLayerRect(metrics.mCompositionBounds));
   }
 }
 
 void
 SimpleClientTiledThebesLayer::EndPaint(bool aFinish)
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -173,26 +173,26 @@ SharedFrameMetricsHelper::UpdateFromComp
       return true;
     }
     mProgressiveUpdateWasInDanger = false;
   }
   mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
 
   // Always abort updates if the resolution has changed. There's no use
   // in drawing at the incorrect resolution.
-  if (!FuzzyEquals(compositorMetrics.mZoom.scale, contentMetrics.mZoom.scale)) {
+  if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) {
     return true;
   }
 
   // Never abort drawing if we can't be sure we've sent a more recent
   // display-port. If we abort updating when we shouldn't, we can end up
   // with blank regions on the screen and we open up the risk of entering
   // an endless updating cycle.
-  if (fabsf(contentMetrics.mScrollOffset.x - compositorMetrics.mScrollOffset.x) <= 2 &&
-      fabsf(contentMetrics.mScrollOffset.y - compositorMetrics.mScrollOffset.y) <= 2 &&
+  if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
+      fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
       fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 &&
       fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 &&
       fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 &&
       fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height)) {
     return false;
   }
 
   // When not a low precision pass and the page is in danger of checker boarding
@@ -236,17 +236,17 @@ SharedFrameMetricsHelper::FindFallbackCo
   aZoom = contentMetrics->GetZoomToParent();  // TODO(botond): double-check this
   return;
 }
 
 bool
 SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
                                               const FrameMetrics& aCompositorMetrics)
 {
-  return !aContentMetrics.mDisplayPort.Contains(CSSRect(aCompositorMetrics.CalculateCompositedRectInCssPixels()) - aCompositorMetrics.mScrollOffset);
+  return !aContentMetrics.mDisplayPort.Contains(CSSRect(aCompositorMetrics.CalculateCompositedRectInCssPixels()) - aCompositorMetrics.GetScrollOffset());
 }
 
 ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer,
                                              CompositableClient* aCompositableClient,
                                              ClientLayerManager* aManager,
                                              SharedFrameMetricsHelper* aHelper)
   : mThebesLayer(aThebesLayer)
   , mCompositableClient(aCompositableClient)
@@ -311,18 +311,17 @@ gfxMemorySharedReadLock::GetReadCount()
 gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator)
   : mAllocator(aAllocator)
   , mAllocSuccess(false)
 {
   MOZ_COUNT_CTOR(gfxShmSharedReadLock);
   MOZ_ASSERT(mAllocator);
   if (mAllocator) {
 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
-    if (mAllocator->AllocUnsafeShmem(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)),
-                                     mozilla::ipc::SharedMemory::TYPE_BASIC, &mShmem)) {
+    if (mAllocator->AllocShmemSection(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
       ShmReadLockInfo* info = GetShmReadLockInfoPtr();
       info->readCount = 1;
       mAllocSuccess = true;
     }
   }
 }
 
 gfxShmSharedReadLock::~gfxShmSharedReadLock()
@@ -344,17 +343,17 @@ int32_t
 gfxShmSharedReadLock::ReadUnlock() {
   if (!mAllocSuccess) {
     return 0;
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
   NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
   if (readCount <= 0) {
-    mAllocator->DeallocShmem(mShmem);
+    mAllocator->FreeShmemSection(mShmemSection);
   }
   return readCount;
 }
 
 int32_t
 gfxShmSharedReadLock::GetReadCount() {
   NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock);
   if (!mAllocSuccess) {
@@ -543,20 +542,25 @@ TileClient::GetTileDescriptor()
   }
   MOZ_ASSERT(mFrontLock);
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     // AddRef here and Release when receiving on the host side to make sure the
     // reference count doesn't go to zero before the host receives the message.
     // see TiledLayerBufferComposite::TiledLayerBufferComposite
     mFrontLock->AddRef();
   }
-  return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
-            mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY
-              ? TileLock(uintptr_t(mFrontLock.get()))
-              : TileLock(static_cast<gfxShmSharedReadLock*>(mFrontLock.get())->GetShmem()));
+
+  if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
+    return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+                                  TileLock(uintptr_t(mFrontLock.get())));
+  } else {
+    gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get());
+    return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+                                  TileLock(lock->GetShmemSection()));
+  }
 }
 
 void
 ClientTiledLayerBuffer::ReadUnlock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     if (mRetainedTiles[i].IsPlaceholderTile()) continue;
     mRetainedTiles[i].ReadUnlock();
   }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -103,42 +103,42 @@ public:
   virtual int32_t ReadUnlock() MOZ_OVERRIDE;
 
   virtual int32_t GetReadCount() MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE { return mAllocSuccess; };
 
   virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_SHMEM; }
 
-  mozilla::ipc::Shmem& GetShmem() { return mShmem; }
+  mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
 
   static already_AddRefed<gfxShmSharedReadLock>
-  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
+  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection)
   {
-    nsRefPtr<gfxShmSharedReadLock> readLock = new gfxShmSharedReadLock(aAllocator, aShmem);
+    nsRefPtr<gfxShmSharedReadLock> readLock = new gfxShmSharedReadLock(aAllocator, aShmemSection);
     return readLock.forget();
   }
 
 private:
-  gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
+  gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection)
     : mAllocator(aAllocator)
-    , mShmem(aShmem)
+    , mShmemSection(aShmemSection)
     , mAllocSuccess(true)
   {
     MOZ_COUNT_CTOR(gfxShmSharedReadLock);
   }
 
   ShmReadLockInfo* GetShmReadLockInfoPtr()
   {
     return reinterpret_cast<ShmReadLockInfo*>
-      (mShmem.get<char>() + mShmem.Size<char>() - sizeof(ShmReadLockInfo));
+      (mShmemSection.shmem().get<char>() + mShmemSection.offset());
   }
 
   RefPtr<ISurfaceAllocator> mAllocator;
-  mozilla::ipc::Shmem mShmem;
+  mozilla::layers::ShmemSection mShmemSection;
   bool mAllocSuccess;
 };
 
 /**
  * Represent a single tile in tiled buffer. The buffer keeps tiles,
  * each tile keeps a reference to a texture client and a read-lock. This
  * read-lock is used to help implement a copy-on-write mechanism. The tile
  * should be locked before being sent to the compositor. The compositor should
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -657,17 +657,17 @@ AsyncCompositionManager::TransformScroll
   gfx3DMatrix currentTransform;
   To3DMatrix(aLayer->GetTransform(), currentTransform);
   Matrix4x4 oldTransform = aLayer->GetTransform();
 
   gfx3DMatrix treeTransform;
 
   CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel();
 
-  LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.mScrollOffset * geckoZoom);
+  LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
 
   if (mIsFirstPaint) {
     mContentRect = metrics.mScrollableRect;
     SetFirstPaintViewport(scrollOffsetLayerPixels,
                           geckoZoom,
                           mContentRect);
     mIsFirstPaint = false;
   } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) {
@@ -689,17 +689,17 @@ AsyncCompositionManager::TransformScroll
   ScreenPoint offset(0, 0);
 
   // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics)
   // but this causes a reftest-ipc test to fail (see bug 883646 comment 27). The reason for this
   // appears to be that metrics.mZoom is poorly initialized in some scenarios. In these scenarios,
   // however, we can assume there is no async zooming in progress and so the following statement
   // works fine.
   CSSToScreenScale userZoom(metrics.mDevPixelsPerCSSPixel * metrics.mCumulativeResolution * LayerToScreenScale(1));
-  ScreenPoint userScroll = metrics.mScrollOffset * userZoom;
+  ScreenPoint userScroll = metrics.GetScrollOffset() * userZoom;
   SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated,
                    userScroll, userZoom, fixedLayerMargins,
                    offset);
   mLayersUpdated = false;
 
   // Apply the render offset
   mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
@@ -708,17 +708,17 @@ AsyncCompositionManager::TransformScroll
   // determine the scroll offset used by Gecko from the frame metrics of the
   // primary scrollable layer. We compare this to the user zoom and scroll
   // offset in the view transform we obtained from Java in order to compute the
   // transformation we need to apply.
   LayerToScreenScale zoomAdjust = userZoom / geckoZoom;
 
   LayerPoint geckoScroll(0, 0);
   if (metrics.IsScrollable()) {
-    geckoScroll = metrics.mScrollOffset * geckoZoom;
+    geckoScroll = metrics.GetScrollOffset() * geckoZoom;
   }
 
   LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll;
   treeTransform = gfx3DMatrix(ViewTransform(-translation,
                                             userZoom
                                           / metrics.mDevPixelsPerCSSPixel
                                           / metrics.GetParentResolution()));
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -681,18 +681,18 @@ LayerManagerComposite::ComputeRenderInte
     // AsyncCompositionManager::TransformScrollableLayer
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
     gfx3DMatrix transform;
     gfx::To3DMatrix(primaryScrollable->GetEffectiveTransform(), transform);
     transform.ScalePost(metrics.mResolution.scale, metrics.mResolution.scale, 1);
 
     // Clip the screen rect to the document bounds
     gfxRect documentBounds =
-      transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.mScrollOffset.x,
-                                        metrics.mScrollableRect.y - metrics.mScrollOffset.y,
+      transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.GetScrollOffset().x,
+                                        metrics.mScrollableRect.y - metrics.GetScrollOffset().y,
                                         metrics.mScrollableRect.width,
                                         metrics.mScrollableRect.height));
     documentBounds.RoundOut();
     screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y,
                                                 documentBounds.width, documentBounds.height));
 
     // If the screen rect is empty, the user has scrolled entirely into
     // over-scroll and so we can be considered to have full integrity.
--- a/gfx/layers/composite/ThebesLayerComposite.cpp
+++ b/gfx/layers/composite/ThebesLayerComposite.cpp
@@ -178,17 +178,17 @@ ThebesLayerComposite::CleanupResources()
 }
 
 CSSToScreenScale
 ThebesLayerComposite::GetEffectiveResolution()
 {
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     const FrameMetrics& metrics = parent->GetFrameMetrics();
     if (metrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) {
-      return metrics.mZoom;
+      return metrics.GetZoom();
     }
   }
 
   return CSSToScreenScale(1.0);
 }
 
 nsACString&
 ThebesLayerComposite::PrintInfo(nsACString& aTo, const char* aPrefix)
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -53,18 +53,18 @@ TiledLayerBufferComposite::TiledLayerBuf
   for(size_t i = 0; i < tiles.Length(); i++) {
     RefPtr<TextureHost> texture;
     const TileDescriptor& tileDesc = tiles[i];
     switch (tileDesc.type()) {
       case TileDescriptor::TTexturedTileDescriptor : {
         texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
         const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
         nsRefPtr<gfxSharedReadLock> sharedLock;
-        if (ipcLock.type() == TileLock::TShmem) {
-          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_Shmem());
+        if (ipcLock.type() == TileLock::TShmemSection) {
+          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
         } else {
           sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
           if (sharedLock) {
             // The corresponding AddRef is in TiledClient::GetTileDescriptor
             sharedLock->Release();
           }
         }
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1149,17 +1149,17 @@ void AsyncPanZoomController::AttemptScro
   // down (*positive* direction along y axis) causes the vertical scroll offset
   // to *decrease* as the page follows your finger.
   ScreenPoint displacement = aStartPoint - aEndPoint;
 
   ScreenPoint overscroll;  // will be used outside monitor block
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
-    CSSToScreenScale zoom = mFrameMetrics.mZoom;
+    CSSToScreenScale zoom = mFrameMetrics.GetZoom();
 
     // Inversely scale the offset by the resolution (when you're zoomed further in,
     // the same swipe should move you a shorter distance).
     CSSPoint cssDisplacement = displacement / zoom;
 
     CSSPoint cssOverscroll;
     CSSPoint allowedDisplacement(mX.AdjustDisplacement(cssDisplacement.x,
                                                        cssOverscroll.x),
@@ -1279,22 +1279,22 @@ bool FlingAnimation::Sample(FrameMetrics
   // we save it here. Would be ScreenVector instead of ScreenPoint if we had
   // vector classes.
   ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
 
   ScreenPoint offset = velocity * aDelta.ToMilliseconds();
 
   // Inversely scale the offset by the resolution (when you're zoomed further in,
   // the same swipe should move you a shorter distance).
-  CSSPoint cssOffset = offset / aFrameMetrics.mZoom;
+  CSSPoint cssOffset = offset / aFrameMetrics.GetZoom();
   CSSPoint overscroll;
-  aFrameMetrics.mScrollOffset += CSSPoint(
+  aFrameMetrics.ScrollBy(CSSPoint(
     mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
     mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
-  );
+  ));
 
   // If the fling has caused us to reach the end of our scroll range, hand
   // off the fling to the next APZC in the overscroll handoff chain.
   if (!IsZero(overscroll)) {
     // We may have reached the end of the scroll range along one axis but
     // not the other. In such a case we only want to hand off the relevant
     // component of the fling.
     if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
@@ -1346,27 +1346,27 @@ void AsyncPanZoomController::SetComposit
   mCompositorParent = aCompositorParent;
 }
 
 void AsyncPanZoomController::SetCrossProcessCompositorParent(PCompositorParent* aCrossProcessCompositorParent) {
   mCrossProcessCompositorParent = aCrossProcessCompositorParent;
 }
 
 void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
-  mFrameMetrics.mScrollOffset += aOffset;
+  mFrameMetrics.ScrollBy(aOffset);
 }
 
 void AsyncPanZoomController::ScaleWithFocus(float aScale,
                                             const CSSPoint& aFocus) {
-  mFrameMetrics.mZoom.scale *= aScale;
+  mFrameMetrics.ZoomBy(aScale);
   // We want to adjust the scroll offset such that the CSS point represented by aFocus remains
   // at the same position on the screen before and after the change in zoom. The below code
   // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
   // in-depth explanation of how.
-  mFrameMetrics.mScrollOffset = (mFrameMetrics.mScrollOffset + aFocus) - (aFocus / aScale);
+  mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
 }
 
 /**
  * Enlarges the displayport along both axes based on the velocity.
  */
 static CSSSize
 CalculateDisplayPortSize(const CSSRect& aCompositionBounds,
                          const CSSPoint& aVelocity)
@@ -1409,18 +1409,18 @@ RedistributeDisplayPortExcess(CSSSize& a
 
 /* static */
 const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
   const FrameMetrics& aFrameMetrics,
   const ScreenPoint& aVelocity,
   double aEstimatedPaintDuration)
 {
   CSSRect compositionBounds(aFrameMetrics.CalculateCompositedRectInCssPixels());
-  CSSPoint velocity = aVelocity / aFrameMetrics.mZoom;
-  CSSPoint scrollOffset = aFrameMetrics.mScrollOffset;
+  CSSPoint velocity = aVelocity / aFrameMetrics.GetZoom();
+  CSSPoint scrollOffset = aFrameMetrics.GetScrollOffset();
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
 
   // Calculate the displayport size based on how fast we're moving along each axis.
   CSSSize displayPortSize = CalculateDisplayPortSize(compositionBounds, velocity);
 
   if (gEnlargeDisplayPortWhenClipped) {
     RedistributeDisplayPortExcess(displayPortSize, compositionBounds, scrollableRect);
   }
@@ -1467,29 +1467,29 @@ void AsyncPanZoomController::RequestCont
   aFrameMetrics.mDisplayPort =
     CalculatePendingDisplayPort(aFrameMetrics,
                                 GetVelocityVector(),
                                 mPaintThrottler.AverageDuration().ToSeconds());
 
   // If we're trying to paint what we already think is painted, discard this
   // request since it's a pointless paint.
   CSSRect oldDisplayPort = mLastPaintRequestMetrics.mDisplayPort
-                         + mLastPaintRequestMetrics.mScrollOffset;
+                         + mLastPaintRequestMetrics.GetScrollOffset();
   CSSRect newDisplayPort = aFrameMetrics.mDisplayPort
-                         + aFrameMetrics.mScrollOffset;
+                         + aFrameMetrics.GetScrollOffset();
 
   if (fabsf(oldDisplayPort.x - newDisplayPort.x) < EPSILON &&
       fabsf(oldDisplayPort.y - newDisplayPort.y) < EPSILON &&
       fabsf(oldDisplayPort.width - newDisplayPort.width) < EPSILON &&
       fabsf(oldDisplayPort.height - newDisplayPort.height) < EPSILON &&
-      fabsf(mLastPaintRequestMetrics.mScrollOffset.x -
-            aFrameMetrics.mScrollOffset.x) < EPSILON &&
-      fabsf(mLastPaintRequestMetrics.mScrollOffset.y -
-            aFrameMetrics.mScrollOffset.y) < EPSILON &&
-      aFrameMetrics.mZoom == mLastPaintRequestMetrics.mZoom &&
+      fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
+            aFrameMetrics.GetScrollOffset().x) < EPSILON &&
+      fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
+            aFrameMetrics.GetScrollOffset().y) < EPSILON &&
+      aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
       fabsf(aFrameMetrics.mViewport.width - mLastPaintRequestMetrics.mViewport.width) < EPSILON &&
       fabsf(aFrameMetrics.mViewport.height - mLastPaintRequestMetrics.mViewport.height) < EPSILON) {
     return;
   }
 
   SendAsyncScrollEvent();
   mPaintThrottler.PostTask(
     FROM_HERE,
@@ -1504,17 +1504,17 @@ void AsyncPanZoomController::RequestCont
 
 void
 AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     APZC_LOG_FM(aFrameMetrics, "%p requesting content repaint", this);
 
     LogRendertraceRect(GetGuid(), "requested displayport", "yellow",
-        aFrameMetrics.mDisplayPort + aFrameMetrics.mScrollOffset);
+        aFrameMetrics.mDisplayPort + aFrameMetrics.GetScrollOffset());
 
     controller->RequestContentRepaint(aFrameMetrics);
     mLastDispatchedPaintMetrics = aFrameMetrics;
   }
 }
 
 void
 AsyncPanZoomController::FireAsyncScrollOnTimeout()
@@ -1527,35 +1527,35 @@ AsyncPanZoomController::FireAsyncScrollO
 }
 
 bool ZoomAnimation::Sample(FrameMetrics& aFrameMetrics,
                            const TimeDuration& aDelta) {
   mDuration += aDelta;
   double animPosition = mDuration / ZOOM_TO_DURATION;
 
   if (animPosition >= 1.0) {
-    aFrameMetrics.mZoom = mEndZoom;
-    aFrameMetrics.mScrollOffset = mEndOffset;
+    aFrameMetrics.SetZoom(mEndZoom);
+    aFrameMetrics.SetScrollOffset(mEndOffset);
     return false;
   }
 
   // Sample the zoom at the current time point.  The sampled zoom
   // will affect the final computed resolution.
   double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
 
   // We scale the scrollOffset linearly with sampledPosition, so the zoom
   // needs to scale inversely to match.
-  aFrameMetrics.mZoom = CSSToScreenScale(1 /
+  aFrameMetrics.SetZoom(CSSToScreenScale(1 /
     (sampledPosition / mEndZoom.scale +
-    (1 - sampledPosition) / mStartZoom.scale));
+    (1 - sampledPosition) / mStartZoom.scale)));
 
-  aFrameMetrics.mScrollOffset = CSSPoint::FromUnknownPoint(gfx::Point(
+  aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
     mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
     mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
-  ));
+  )));
 
   return true;
 }
 
 bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime)
 {
   if (mAnimation) {
     if (mAnimation->Sample(mFrameMetrics, aSampleTime - mLastSampleTime)) {
@@ -1586,24 +1586,24 @@ bool AsyncPanZoomController::SampleConte
   // responsibility to schedule a composite.
   bool requestAnimationFrame = false;
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     requestAnimationFrame = UpdateAnimation(aSampleTime);
 
-    aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.mZoom;
+    aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
     *aNewTransform = GetCurrentAsyncTransform();
 
     LogRendertraceRect(GetGuid(), "viewport", "red",
-      CSSRect(mFrameMetrics.mScrollOffset,
+      CSSRect(mFrameMetrics.GetScrollOffset(),
               ParentLayerSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.GetZoomToParent()));
 
-    mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
+    mCurrentAsyncScrollOffset = mFrameMetrics.GetScrollOffset();
   }
 
   // Execute tasks queued up by mAnimation's Sample() (called by
   // UpdateAnimation()) for execution after mMonitor has been released.
   if (mAnimation) {
     mAnimation->ExecuteDeferredTasks();
   }
 
@@ -1637,20 +1637,20 @@ bool AsyncPanZoomController::SampleConte
   return requestAnimationFrame;
 }
 
 ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   CSSPoint lastPaintScrollOffset;
   if (mLastContentPaintMetrics.IsScrollable()) {
-    lastPaintScrollOffset = mLastContentPaintMetrics.mScrollOffset;
+    lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
   }
 
-  CSSPoint currentScrollOffset = mFrameMetrics.mScrollOffset +
+  CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
     mTestAsyncScrollOffset;
 
   // If checkerboarding has been disallowed, clamp the scroll position to stay
   // within rendered content.
   if (!gAllowCheckerboarding &&
       !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
     CSSRect compositedRect(mLastContentPaintMetrics.CalculateCompositedRectInCssPixels());
     CSSPoint maxScrollOffset = lastPaintScrollOffset +
@@ -1665,50 +1665,50 @@ ViewTransform AsyncPanZoomController::Ge
       currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
     }
   }
 
   LayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
                          * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
 
   return ViewTransform(-translation,
-                       mFrameMetrics.mZoom
+                       mFrameMetrics.GetZoom()
                      / mLastContentPaintMetrics.mDevPixelsPerCSSPixel
                      / mFrameMetrics.GetParentResolution());
 }
 
 gfx3DMatrix AsyncPanZoomController::GetNontransientAsyncTransform() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   return gfx3DMatrix::ScalingMatrix(mLastContentPaintMetrics.mResolution.scale,
                                     mLastContentPaintMetrics.mResolution.scale,
                                     1.0f);
 }
 
 gfx3DMatrix AsyncPanZoomController::GetTransformToLastDispatchedPaint() {
   ReentrantMonitorAutoEnter lock(mMonitor);
-  LayerPoint scrollChange = (mLastContentPaintMetrics.mScrollOffset - mLastDispatchedPaintMetrics.mScrollOffset)
+  LayerPoint scrollChange = (mLastContentPaintMetrics.GetScrollOffset() - mLastDispatchedPaintMetrics.GetScrollOffset())
                           * mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
-  float zoomChange = mLastContentPaintMetrics.mZoom.scale / mLastDispatchedPaintMetrics.mZoom.scale;
+  float zoomChange = mLastContentPaintMetrics.GetZoom().scale / mLastDispatchedPaintMetrics.GetZoom().scale;
   return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
          gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   mLastContentPaintMetrics = aLayerMetrics;
   UpdateTransformScale();
 
   bool isDefault = mFrameMetrics.IsDefault();
   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect(GetGuid(), "painted displayport", "green",
-    aLayerMetrics.mDisplayPort + aLayerMetrics.mScrollOffset);
+    aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
 
   mPaintThrottler.TaskComplete(GetFrameTime());
   bool needContentRepaint = false;
   if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
       aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
     // Remote content has sync'd up to the composition geometry
     // change, so we can accept the viewport it's calculated.
     if (mFrameMetrics.mViewport.width != aLayerMetrics.mViewport.width ||
@@ -1735,37 +1735,37 @@ void AsyncPanZoomController::NotifyLayer
     // If we're not taking the aLayerMetrics wholesale we still need to pull
     // in some things into our local mFrameMetrics because these things are
     // determined by Gecko and our copy in mFrameMetrics may be stale.
 
     if (mFrameMetrics.mCompositionBounds.width == aLayerMetrics.mCompositionBounds.width &&
         mFrameMetrics.mDevPixelsPerCSSPixel == aLayerMetrics.mDevPixelsPerCSSPixel) {
       float parentResolutionChange = aLayerMetrics.GetParentResolution().scale
                                    / mFrameMetrics.GetParentResolution().scale;
-      mFrameMetrics.mZoom.scale *= parentResolutionChange;
+      mFrameMetrics.ZoomBy(parentResolutionChange);
     } else {
       // Take the new zoom as either device scale or composition width or both
       // got changed (e.g. due to orientation change).
-      mFrameMetrics.mZoom.scale = aLayerMetrics.mZoom.scale;
+      mFrameMetrics.SetZoom(aLayerMetrics.GetZoom());
       mFrameMetrics.mDevPixelsPerCSSPixel.scale = aLayerMetrics.mDevPixelsPerCSSPixel.scale;
     }
     mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     mFrameMetrics.mResolution = aLayerMetrics.mResolution;
     mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
     mFrameMetrics.mHasScrollgrab = aLayerMetrics.mHasScrollgrab;
 
     // If the layers update was not triggered by our own repaint request, then
     // we want to take the new scroll offset.
     if (aLayerMetrics.GetScrollOffsetUpdated()) {
       APZC_LOG("%p updating scroll offset from (%f, %f) to (%f, %f)\n", this,
         mFrameMetrics.mScrollOffset.x, mFrameMetrics.mScrollOffset.y,
         aLayerMetrics.mScrollOffset.x, aLayerMetrics.mScrollOffset.y);
 
-      mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset;
+      mFrameMetrics.SetScrollOffset(aLayerMetrics.GetScrollOffset());
 
       // Because of the scroll offset update, any inflight paint requests are
       // going to be ignored by layout, and so mLastDispatchedPaintMetrics
       // becomes incorrect for the purposes of calculating the LD transform. To
       // correct this we need to update mLastDispatchedPaintMetrics to be the
       // last thing we know was painted by Gecko.
       mLastDispatchedPaintMetrics = aLayerMetrics;
     }
@@ -1802,17 +1802,17 @@ void AsyncPanZoomController::ZoomToRect(
 
   SetState(ANIMATING_ZOOM);
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     ParentLayerIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
     CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
-    CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
+    CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
     CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoomToParent();
     CSSToParentLayerScale targetZoom;
 
     // The minimum zoom to prevent over-zoom-out.
     // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
     // then the CSS content rect, in layers pixels, will be smaller than the
     // composition bounds. If this happens, we can't fill the target composited
     // area with this frame.
@@ -1846,43 +1846,43 @@ void AsyncPanZoomController::ZoomToRect(
                       newHeight);
       aRect = aRect.Intersect(cssPageRect);
       targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
                                                   compositionBounds.height / aRect.height));
     }
 
     targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
     FrameMetrics endZoomToMetrics = mFrameMetrics;
-    endZoomToMetrics.mZoom = targetZoom / mFrameMetrics.mTransformScale;
+    endZoomToMetrics.SetZoom(targetZoom / mFrameMetrics.mTransformScale);
 
     // Adjust the zoomToRect to a sensible position to prevent overscrolling.
     CSSRect rectAfterZoom = CSSRect(endZoomToMetrics.CalculateCompositedRectInCssPixels());
 
     // If either of these conditions are met, the page will be
     // overscrolled after zoomed
     if (aRect.y + rectAfterZoom.height > cssPageRect.height) {
       aRect.y = cssPageRect.height - rectAfterZoom.height;
       aRect.y = aRect.y > 0 ? aRect.y : 0;
     }
     if (aRect.x + rectAfterZoom.width > cssPageRect.width) {
       aRect.x = cssPageRect.width - rectAfterZoom.width;
       aRect.x = aRect.x > 0 ? aRect.x : 0;
     }
 
-    endZoomToMetrics.mScrollOffset = aRect.TopLeft();
+    endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
     endZoomToMetrics.mDisplayPort =
       CalculatePendingDisplayPort(endZoomToMetrics,
                                   ScreenPoint(0,0),
                                   0);
 
     StartAnimation(new ZoomAnimation(
-        mFrameMetrics.mScrollOffset,
-        mFrameMetrics.mZoom,
-        endZoomToMetrics.mScrollOffset,
-        endZoomToMetrics.mZoom));
+        mFrameMetrics.GetScrollOffset(),
+        mFrameMetrics.GetZoom(),
+        endZoomToMetrics.GetScrollOffset(),
+        endZoomToMetrics.GetZoom()));
 
     // Schedule a repaint now, so the new displayport will be painted before the
     // animation finishes.
     RequestContentRepaint(endZoomToMetrics);
   }
 }
 
 void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -262,17 +262,17 @@ float Axis::GetCompositionEnd() {
   return GetOrigin() + GetCompositionLength();
 }
 
 float Axis::GetPageEnd() {
   return GetPageStart() + GetPageLength();
 }
 
 float Axis::GetOrigin() {
-  CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().mScrollOffset;
+  CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().GetScrollOffset();
   return GetPointOffset(origin);
 }
 
 float Axis::GetCompositionLength() {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
   CSSRect cssCompositedRect = CSSRect(metrics.CalculateCompositedRectInCssPixels());
   return GetRectLength(cssCompositedRect);
 }
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -7,16 +7,17 @@
 
 #include "ISurfaceAllocator.h"
 #include <sys/types.h>                  // for int32_t
 #include "gfx2DGlue.h"                  // for IntSize
 #include "gfxASurface.h"                // for gfxASurface, etc
 #include "gfxPlatform.h"                // for gfxPlatform, gfxImageFormat
 #include "gfxSharedImageSurface.h"      // for gfxSharedImageSurface
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
+#include "mozilla/Atomics.h"            // for PrimitiveIntrinsics
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "ShadowLayerUtils.h"
 #include "mozilla/mozalloc.h"           // for operator delete[], etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, getter_AddRefs, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #ifdef DEBUG
@@ -39,16 +40,24 @@ mozilla::ipc::SharedMemory::SharedMemory
 
 bool
 IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
 {
   return aSurface.type() != SurfaceDescriptor::T__None &&
          aSurface.type() != SurfaceDescriptor::Tnull_t;
 }
 
+ISurfaceAllocator::~ISurfaceAllocator()
+{
+  ShrinkShmemSectionHeap();
+
+  // Check if we're not leaking..
+  MOZ_ASSERT(mUsedShmems.empty());
+}
+
 bool
 ISurfaceAllocator::AllocSharedImageSurface(const gfx::IntSize& aSize,
                                gfxContentType aContent,
                                gfxSharedImageSurface** aBuffer)
 {
   mozilla::ipc::SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
   gfxImageFormat format = gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
 
@@ -172,10 +181,144 @@ ISurfaceAllocator::PlatformAllocSurfaceD
                                                   gfxContentType,
                                                   uint32_t,
                                                   SurfaceDescriptor*)
 {
   return false;
 }
 #endif
 
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+const uint32_t sSupportedBlockSize = 4;
+
+enum AllocationStatus
+{
+  STATUS_ALLOCATED,
+  STATUS_FREED
+};
+
+struct ShmemSectionHeapHeader
+{
+  Atomic<uint32_t> mTotalBlocks;
+  Atomic<uint32_t> mAllocatedBlocks;
+};
+
+struct ShmemSectionHeapAllocation
+{
+  Atomic<uint32_t> mStatus;
+  uint32_t mSize;
+};
+
+bool
+ISurfaceAllocator::AllocShmemSection(size_t aSize, mozilla::layers::ShmemSection* aShmemSection)
+{
+  // For now we only support sizes of 4. If we want to support different sizes
+  // some more complicated bookkeeping should be added.
+  MOZ_ASSERT(aSize == sSupportedBlockSize);
+  MOZ_ASSERT(aShmemSection);
+
+  uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+  for (size_t i = 0; i < mUsedShmems.size(); i++) {
+    ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+    if ((header->mAllocatedBlocks + 1) * allocationSize + sizeof(ShmemSectionHeapHeader) < sShmemPageSize) {
+      aShmemSection->shmem() = mUsedShmems[i];
+      MOZ_ASSERT(mUsedShmems[i].IsWritable());
+      break;
+    }
+  }
+
+  if (!aShmemSection->shmem().IsWritable()) {
+    ipc::Shmem tmp;
+    if (!AllocUnsafeShmem(sShmemPageSize, ipc::SharedMemory::TYPE_BASIC, &tmp)) {
+      return false;
+    }
+
+    ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+    header->mTotalBlocks = 0;
+    header->mAllocatedBlocks = 0;
+
+    mUsedShmems.push_back(tmp);
+    aShmemSection->shmem() = tmp;
+  }
+
+  MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+  ShmemSectionHeapHeader* header = aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+  uint8_t* heap = aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+  ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+  if (header->mTotalBlocks > header->mAllocatedBlocks) {
+    // Search for the first available block.
+    for (size_t i = 0; i < header->mTotalBlocks; i++) {
+      allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+      if (allocHeader->mStatus == STATUS_FREED) {
+        break;
+      }
+      heap += allocationSize;
+    }
+    MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+    MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+  } else {
+    heap += header->mTotalBlocks * allocationSize;
+
+    header->mTotalBlocks++;
+    allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+    allocHeader->mSize = aSize;
+  }
+
+  MOZ_ASSERT(allocHeader);
+  header->mAllocatedBlocks++;
+  allocHeader->mStatus = STATUS_ALLOCATED;
+
+  aShmemSection->size() = aSize;
+  aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get<uint8_t>();
+  ShrinkShmemSectionHeap();
+  return true;
+}
+
+void
+ISurfaceAllocator::FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+  MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+  MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+  ShmemSectionHeapAllocation* allocHeader =
+    reinterpret_cast<ShmemSectionHeapAllocation*>(aShmemSection.shmem().get<char>() +
+                                                  aShmemSection.offset() -
+                                                  sizeof(ShmemSectionHeapAllocation));
+
+  MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+  DebugOnly<bool> success = allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+  // If this fails something really weird is going on.
+  MOZ_ASSERT(success);
+
+  ShmemSectionHeapHeader* header = aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+  header->mAllocatedBlocks--;
+
+  ShrinkShmemSectionHeap();
+}
+
+void
+ISurfaceAllocator::ShrinkShmemSectionHeap()
+{
+  for (size_t i = 0; i < mUsedShmems.size(); i++) {
+    ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+    if (header->mAllocatedBlocks == 0) {
+      DeallocShmem(mUsedShmems[i]);
+
+      // We don't particularly care about order, move the last one in the array
+      // to this position.
+      mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1];
+      mUsedShmems.pop_back();
+      i--;
+      break;
+    }
+  }
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -9,17 +9,19 @@
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t
 #include "gfxTypes.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/RefPtr.h"
 #include "nsIMemoryReporter.h"          // for nsIMemoryReporter
 #include "mozilla/Atomics.h"            // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
 #include "LayersTypes.h"
+#include <vector>
 
 /*
  * FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
  *
  * This #define is actually needed here, because subclasses of ISurfaceAllocator,
  * namely ShadowLayerForwarder, will or will not override AllocGrallocBuffer
  * depending on whether MOZ_HAVE_SURFACEDESCRIPTORGRALLOC is defined.
  */
@@ -101,16 +103,30 @@ public:
 
   /**
    * Allocate shared memory that can be accessed by both processes at the
    * same time. Safety is left for the user of the memory to care about.
    */
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aType,
                                 mozilla::ipc::Shmem* aShmem) = 0;
+
+  /**
+   * Allocate memory in shared memory that can always be accessed by both
+   * processes at a time. Safety is left for the user of the memory to care
+   * about.
+   */
+  bool AllocShmemSection(size_t aSize,
+                         mozilla::layers::ShmemSection* aShmemSection);
+
+  /**
+   * Deallocates a shmem section.
+   */
+  void FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection);
+
   /**
    * Deallocate memory allocated by either AllocShmem or AllocUnsafeShmem.
    */
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
 
   // was AllocBuffer
   virtual bool AllocSharedImageSurface(const gfx::IntSize& aSize,
                                        gfxContentType aContent,
@@ -153,17 +169,22 @@ protected:
   virtual bool IsOnCompositorSide() const = 0;
   static bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
   virtual bool PlatformAllocSurfaceDescriptor(const gfx::IntSize& aSize,
                                               gfxContentType aContent,
                                               uint32_t aCaps,
                                               SurfaceDescriptor* aBuffer);
 
 
-  virtual ~ISurfaceAllocator() {}
+  virtual ~ISurfaceAllocator();
+
+  void ShrinkShmemSectionHeap();
+
+  // This is used to implement an extremely simple & naive heap allocator.
+  std::vector<mozilla::ipc::Shmem> mUsedShmems;
 
   friend class detail::RefCounted<ISurfaceAllocator, detail::AtomicRefCount>;
 };
 
 class GfxMemoryImageReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -250,18 +250,24 @@ struct OpSetRoot          { PLayer root;
 struct OpInsertAfter      { PLayer container; PLayer childLayer; PLayer after; };
 struct OpPrependChild     { PLayer container; PLayer childLayer; };
 struct OpRemoveChild      { PLayer container; PLayer childLayer; };
 struct OpRepositionChild  { PLayer container; PLayer childLayer; PLayer after; };
 struct OpRaiseToTopChild  { PLayer container; PLayer childLayer; };
 
 struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
 
+struct ShmemSection {
+  Shmem shmem;
+  uint32_t offset;
+  size_t size;
+};
+
 union TileLock {
-  Shmem;
+  ShmemSection;
   uintptr_t;
 };
 
 struct TexturedTileDescriptor {
   PTexture texture;
   TileLock sharedLock;
 };
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -65,17 +65,17 @@ static inline IntSize ns2gfxSize(const n
   return IntSize(s.width, s.height);
 }
 
 static void
 BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
                    GLenum aTexUnit, const gfx::Matrix4x4& aTransform)
 {
   MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31);
-  aSourceMask->BindTexture(aTexUnit);
+  aSourceMask->BindTexture(aTexUnit, gfx::Filter::LINEAR);
   aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
   aProgram->SetMaskLayerTransform(aTransform);
 }
 
 // Draw the given quads with the already selected shader. Texture coordinates
 // are supplied if the shader requires them.
 static void
 DrawQuads(GLContext *aGLContext,
@@ -120,25 +120,22 @@ DrawQuads(GLContext *aGLContext,
                                bytes,
                                bytes,
                                aRects.texCoords().Elements());
     aGLContext->fEnableVertexAttribArray(texCoordAttribIndex);
     aGLContext->fVertexAttribPointer(texCoordAttribIndex,
                                      2, LOCAL_GL_FLOAT,
                                      LOCAL_GL_FALSE,
                                      0, BUFFER_OFFSET(bytes));
+  } else {
+    aGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
   }
 
   aGLContext->fDrawArrays(aMode, 0, aRects.elements());
 
-  aGLContext->fDisableVertexAttribArray(vertAttribIndex);
-  if (texCoords) {
-    aGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
-  }
-
   aGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 }
 
 CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
                              int aSurfaceHeight, bool aUseExternalSurfaceSize)
   : mWidget(aWidget)
   , mWidgetSize(-1, -1)
   , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
@@ -689,16 +686,18 @@ CompositorOGL::BeginFrame(const nsIntReg
   mWindowRenderTarget = mCurrentRenderTarget;
 #endif
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE);
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
+  mGLContext->fEnable(LOCAL_GL_SCISSOR_TEST);
+
   if (aClipRectOut && !aClipRectIn) {
     aClipRectOut->SetRect(0, 0, width, height);
   }
 
   // If the Android compositor is being used, this clear will be done in
   // DrawWindowUnderlay. Make sure the bits used here match up with those used
   // in mobile/android/base/gfx/LayerRenderer.java
 #ifndef MOZ_ANDROID_OMTC
@@ -857,87 +856,16 @@ CompositorOGL::GetShaderProgramFor(const
     delete shader;
     return nullptr;
   }
 
   mPrograms[aConfig] = shader;
   return shader;
 }
 
-struct MOZ_STACK_CLASS AutoBindTexture
-  : public ScopedGLWrapper<AutoBindTexture>
-{
-  friend struct ScopedGLWrapper<AutoBindTexture>;
-
-protected:
-  GLenum mTexUnit;
-  GLuint mOldTexId;
-
-public:
-  explicit AutoBindTexture(GLContext* aGL)
-    : ScopedGLWrapper<AutoBindTexture>(aGL)
-    , mTexUnit(0)
-    , mOldTexId(GLuint(-1))
-  { }
-
-  AutoBindTexture(GLContext* aGL, TextureSourceOGL* aTexture,
-                  GLenum aTexUnit = LOCAL_GL_TEXTURE0)
-    : ScopedGLWrapper<AutoBindTexture>(aGL)
-    , mTexUnit(0)
-    , mOldTexId(GLuint(-1))
-  {
-    MOZ_ASSERT(aTexture);
-    MOZ_ASSERT(mOldTexId == GLuint(-1));
-    mTexUnit = aTexUnit;
-
-    ScopedBindTextureUnit autoBindTexUnit(mGL, aTexUnit);
-
-    mGL->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &mOldTexId);
-    aTexture->BindTexture(mTexUnit);
-  }
-
-protected:
-  void UnwrapImpl()
-  {
-    if (mOldTexId == GLuint(-1))
-      return;
-
-    ScopedBindTextureUnit autoBindTexUnit(mGL, mTexUnit);
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mOldTexId);
-  }
-};
-
-struct MOZ_STACK_CLASS AutoSaveTexture
-  : public ScopedGLWrapper<AutoSaveTexture>
-{
-  friend struct ScopedGLWrapper<AutoSaveTexture>;
-
-protected:
-  GLenum mTexUnit;
-  GLuint mOldTexId;
-
-public:
-  AutoSaveTexture(GLContext* aGL, GLenum aTexUnit = LOCAL_GL_TEXTURE0)
-    : ScopedGLWrapper<AutoSaveTexture>(aGL)
-    , mTexUnit(aTexUnit)
-    , mOldTexId(GLuint(-1))
-  {
-    ScopedBindTextureUnit savedTexUnit(mGL, mTexUnit);
-    mGL->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &mOldTexId);
-  }
-
-protected:
-  void UnwrapImpl()
-  {
-    ScopedBindTextureUnit savedTexUnit(mGL, mTexUnit);
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mOldTexId);
-  }
-};
-
-
 void
 CompositorOGL::DrawLines(const std::vector<gfx::Point>& aLines, const gfx::Rect& aClipRect,
                          const gfx::Color& aColor,
                          gfx::Float aOpacity, const gfx::Matrix4x4 &aTransform)
 {
   mGLContext->fLineWidth(2.0);
 
   EffectChain effects;
@@ -947,30 +875,16 @@ CompositorOGL::DrawLines(const std::vect
     const gfx::Point& p1 = aLines[i];
     const gfx::Point& p2 = aLines[i+1];
     DrawQuadInternal(Rect(p1.x, p2.y, p2.x - p1.x, p1.y - p2.y),
                      aClipRect, effects, aOpacity, aTransform,
                      LOCAL_GL_LINE_STRIP);
   }
 }
 
-/**
- * Applies aFilter to the texture currently bound to aTarget.
- */
-void ApplyFilterToBoundTexture(GLContext* aGL,
-                               GraphicsFilter aFilter,
-                               GLuint aTarget = LOCAL_GL_TEXTURE_2D)
-{
-  GLenum filter =
-    (aFilter == GraphicsFilter::FILTER_NEAREST ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR);
-
-    aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, filter);
-    aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, filter);
-}
-
 void
 CompositorOGL::DrawQuadInternal(const Rect& aRect,
                                 const Rect& aClipRect,
                                 const EffectChain &aEffectChain,
                                 Float aOpacity,
                                 const gfx::Matrix4x4 &aTransform,
                                 GLuint aDrawMode)
 {
@@ -979,22 +893,18 @@ CompositorOGL::DrawQuadInternal(const Re
 
   Rect clipRect = aClipRect;
   if (!mTarget) {
     clipRect.MoveBy(mRenderOffset.x, mRenderOffset.y);
   }
   IntRect intClipRect;
   clipRect.ToIntRect(&intClipRect);
 
-  ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
-  ScopedScissorRect autoScissor(mGLContext,
-                                intClipRect.x,
-                                FlipY(intClipRect.y + intClipRect.height),
-                                intClipRect.width,
-                                intClipRect.height);
+  gl()->fScissor(intClipRect.x, FlipY(intClipRect.y + intClipRect.height),
+                 intClipRect.width, intClipRect.height);
 
   LayerScope::SendEffectChain(mGLContext, aEffectChain,
                               aRect.width, aRect.height);
 
   MaskType maskType;
   EffectMask* effectMask;
   TextureSourceOGL* sourceMask = nullptr;
   gfx::Matrix4x4 maskQuadTransform;
@@ -1065,17 +975,16 @@ CompositorOGL::DrawQuadInternal(const Re
     // This is used by IOSurface that use 0,0...w,h coordinate rather then 0,0..1,1.
     program->SetTexCoordMultiplier(source->GetSize().width, source->GetSize().height);
   }
 
   switch (aEffectChain.mPrimaryEffect->mType) {
     case EFFECT_SOLID_COLOR: {
       program->SetRenderColor(color);
 
-      AutoSaveTexture bindMask(mGLContext, LOCAL_GL_TEXTURE0);
       if (maskType != MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
       }
 
       BindAndDrawQuad(program, false, aDrawMode);
     }
     break;
 
@@ -1084,40 +993,36 @@ CompositorOGL::DrawQuadInternal(const Re
           static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
       TextureSource *source = texturedEffect->mTexture;
 
       if (!texturedEffect->mPremultiplied) {
         mGLContext->fBlendFuncSeparate(LOCAL_GL_SRC_ALPHA, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                        LOCAL_GL_ONE, LOCAL_GL_ONE);
       }
 
-      AutoBindTexture bindSource(mGLContext, source->AsSourceOGL(), LOCAL_GL_TEXTURE0);
-
-      GraphicsFilter filter = ThebesFilter(texturedEffect->mFilter);
+      gfx::Filter filter = texturedEffect->mFilter;
       gfx3DMatrix textureTransform;
       gfx::To3DMatrix(source->AsSourceOGL()->GetTextureTransform(), textureTransform);
 
 #ifdef MOZ_WIDGET_ANDROID
       gfxMatrix textureTransform2D;
-      if (filter != GraphicsFilter::FILTER_NEAREST &&
+      if (filter != gfx::Filter::POINT &&
           aTransform.Is2DIntegerTranslation() &&
           textureTransform.Is2D(&textureTransform2D) &&
           textureTransform2D.HasOnlyIntegerTranslation()) {
         // On Android we encounter small resampling errors in what should be
         // pixel-aligned compositing operations. This works around them. This
         // code should not be needed!
-        filter = GraphicsFilter::FILTER_NEAREST;
+        filter = gfx::Filter::POINT;
       }
 #endif
-      ApplyFilterToBoundTexture(mGLContext, filter,
-                                source->AsSourceOGL()->GetTextureTarget());
+      source->AsSourceOGL()->BindTexture(LOCAL_GL_TEXTURE0, filter);
 
       program->SetTextureUnit(0);
 
-      AutoSaveTexture bindMask(mGLContext, LOCAL_GL_TEXTURE1);
       if (maskType != MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
       }
 
       BindAndDrawQuadWithTextureRect(program, textureTransform,
                                      texturedEffect->mTextureCoords, source);
 
       if (!texturedEffect->mPremultiplied) {
@@ -1135,28 +1040,22 @@ CompositorOGL::DrawQuadInternal(const Re
       TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
       TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
 
       if (!sourceY && !sourceCb && !sourceCr) {
         NS_WARNING("Invalid layer texture.");
         return;
       }
 
-      GraphicsFilter filter = ThebesFilter(effectYCbCr->mFilter);
-
-      AutoBindTexture bindY(mGLContext, sourceY, LOCAL_GL_TEXTURE0);
-      ApplyFilterToBoundTexture(mGLContext, filter);
-      AutoBindTexture bindCb(mGLContext, sourceCb, LOCAL_GL_TEXTURE1);
-      ApplyFilterToBoundTexture(mGLContext, filter);
-      AutoBindTexture bindCr(mGLContext, sourceCr, LOCAL_GL_TEXTURE2);
-      ApplyFilterToBoundTexture(mGLContext, filter);
+      sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectYCbCr->mFilter);
+      sourceCb->BindTexture(LOCAL_GL_TEXTURE1, effectYCbCr->mFilter);
+      sourceCr->BindTexture(LOCAL_GL_TEXTURE2, effectYCbCr->mFilter);
 
       program->SetYCbCrTextureUnits(Y, Cb, Cr);
 
-      AutoSaveTexture bindMask(mGLContext, LOCAL_GL_TEXTURE3);
       if (maskType != MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
       }
       BindAndDrawQuadWithTextureRect(program,
                                      gfx3DMatrix(),
                                      effectYCbCr->mTextureCoords,
                                      sourceYCbCr->GetSubSource(Y));
     }
@@ -1167,19 +1066,18 @@ CompositorOGL::DrawQuadInternal(const Re
       RefPtr<CompositingRenderTargetOGL> surface
         = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get());
 
       surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget);
 
       program->SetTextureTransform(Matrix4x4());
       program->SetTextureUnit(0);
 
-      AutoSaveTexture bindMask(mGLContext, LOCAL_GL_TEXTURE1);
       if (maskType != MaskNone) {
-        sourceMask->BindTexture(LOCAL_GL_TEXTURE1);
+        sourceMask->BindTexture(LOCAL_GL_TEXTURE1, gfx::Filter::LINEAR);
         program->SetMaskTextureUnit(1);
         program->SetMaskLayerTransform(maskQuadTransform);
       }
 
       if (config.mFeatures & ENABLE_TEXTURE_RECT) {
         // 2DRect case, get the multiplier right for a sampler2DRect
         program->SetTexCoordMultiplier(aRect.width, aRect.height);
       }
@@ -1198,24 +1096,23 @@ CompositorOGL::DrawQuadInternal(const Re
       TextureSourceOGL* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceOGL();
 
       if (!sourceOnBlack->IsValid() ||
           !sourceOnWhite->IsValid()) {
         NS_WARNING("Invalid layer texture for component alpha");
         return;
       }
 
-      AutoBindTexture bindSourceOnBlack(mGLContext, sourceOnBlack, LOCAL_GL_TEXTURE0);
-      AutoBindTexture bindSourceOnWhite(mGLContext, sourceOnWhite, LOCAL_GL_TEXTURE1);
+      sourceOnBlack->BindTexture(LOCAL_GL_TEXTURE0, effectComponentAlpha->mFilter);
+      sourceOnWhite->BindTexture(LOCAL_GL_TEXTURE1, effectComponentAlpha->mFilter);
 
       program->SetBlackTextureUnit(0);
       program->SetWhiteTextureUnit(1);
       program->SetTextureTransform(gfx::Matrix4x4());
 
-      AutoBindTexture bindMask(mGLContext);
       if (maskType != MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
       }
       // Pass 1.
       gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR,
                                LOCAL_GL_ONE, LOCAL_GL_ONE);
       program->SetTexturePass2(false);
       BindAndDrawQuadWithTextureRect(program,
@@ -1236,17 +1133,16 @@ CompositorOGL::DrawQuadInternal(const Re
                                      LOCAL_GL_ONE, LOCAL_GL_ONE);
     }
     break;
   default:
     MOZ_ASSERT(false, "Unhandled effect type");
     break;
   }
 
-  mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
   // in case rendering has used some other GL context
   MakeCurrent();
 }
 
 void
 CompositorOGL::EndFrame()
 {
   PROFILER_LABEL("CompositorOGL", "EndFrame");
@@ -1281,16 +1177,27 @@ CompositorOGL::EndFrame()
   mCurrentRenderTarget = nullptr;
 
   if (mTexturePool) {
     mTexturePool->EndFrame();
   }
 
   mGLContext->SwapBuffers();
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+  // Unbind all textures
+  mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  mGLContext->fActiveTexture(LOCAL_GL_TEXTURE1);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  mGLContext->fActiveTexture(LOCAL_GL_TEXTURE2);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
 }
 
 void
 CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform)
 {
   // This lets us reftest and screenshot content rendered externally
   if (mTarget) {
     MakeCurrent();
@@ -1471,21 +1378,16 @@ CompositorOGL::BindAndDrawQuad(GLuint aV
   }
 
   mGLContext->fEnableVertexAttribArray(aVertAttribIndex);
   if (aDrawMode == LOCAL_GL_LINE_STRIP) {
     mGLContext->fDrawArrays(aDrawMode, 1, 2);
   } else {
     mGLContext->fDrawArrays(aDrawMode, 0, 4);
   }
-  mGLContext->fDisableVertexAttribArray(aVertAttribIndex);
-
-  if (aTexCoordAttribIndex != GLuint(-1)) {
-    mGLContext->fDisableVertexAttribArray(aTexCoordAttribIndex);
-  }
 }
 
 void
 CompositorOGL::BindAndDrawQuad(ShaderProgramOGL *aProg,
                                bool aFlipped,
                                GLuint aDrawMode)
 {
   NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -99,31 +99,31 @@ GrallocTextureSourceOGL::GrallocTextureS
 
 GrallocTextureSourceOGL::~GrallocTextureSourceOGL()
 {
   DeallocateDeviceData();
   mCompositor = nullptr;
 }
 
 void
-GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit)
+GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   MOZ_ASSERT(gl());
   if (!IsValid()) {
     return;
   }
   gl()->MakeCurrent();
 
   GLuint tex = GetGLTexture();
   GLuint textureTarget = GetTextureTarget();
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(textureTarget, tex);
 
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+  ApplyFilterToBoundTexture(gl(), aFilter, textureTarget);
 }
 
 void GrallocTextureSourceOGL::Lock()
 {
   /*
    * The job of this function is to ensure that the texture is tied to the
    * android::GraphicBuffer, so that texturing will source the GraphicBuffer.
    *
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -25,17 +25,17 @@ public:
   GrallocTextureSourceOGL(CompositorOGL* aCompositor,
                           android::GraphicBuffer* aGraphicBuffer,
                           gfx::SurfaceFormat aFormat);
 
   virtual ~GrallocTextureSourceOGL();
 
   virtual bool IsValid() const MOZ_OVERRIDE;
 
-  virtual void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
 
   virtual GLenum GetTextureTarget() const MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -79,28 +79,28 @@ MacIOSurfaceTextureSourceOGL::GetSize() 
 
 gfx::SurfaceFormat
 MacIOSurfaceTextureSourceOGL::GetFormat() const
 {
   return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8X8;
 }
 
 void
-MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit)
+MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   if (!gl()) {
     NS_WARNING("Trying to bind a texture without a GLContext");
     return;
   }
   GLuint tex = mCompositor->GetTemporaryTexture(aTextureUnit);
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
   mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl())->GetCGLContext());
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+  ApplyFilterToBoundTexture(gl(), aFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 }
 
 void
 MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
 {
   mCompositor = static_cast<CompositorOGL*>(aCompositor);
 }
 
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -24,17 +24,17 @@ class MacIOSurfaceTextureSourceOGL : pub
 {
 public:
   MacIOSurfaceTextureSourceOGL(CompositorOGL* aCompositor,
                                MacIOSurface* aSurface);
   virtual ~MacIOSurfaceTextureSourceOGL();
 
   virtual TextureSourceOGL* AsSourceOGL() { return this; }
 
-  virtual void BindTexture(GLenum activetex) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE { return !!gl(); }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
 
   virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_RECTANGLE_ARB; }
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -241,16 +241,17 @@ TextureImageTextureSourceOGL::Update(gfx
       // See bug 893300 (tracks the implementation of ContentHost for new textures).
       mTexImage = CreateTextureImage(mGL,
                                      size,
                                      gfx::ContentForFormat(aSurface->GetFormat()),
                                      WrapMode(mGL, mFlags & TEXTURE_ALLOW_REPEAT),
                                      FlagsToGLFlags(mFlags),
                                      SurfaceFormatToImageFormat(aSurface->GetFormat()));
     }
+    ClearCachedFilter();
   }
 
   mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset);
 
   if (mTexImage->InUpdate()) {
     mTexImage->EndUpdate();
   }
   return true;
@@ -288,21 +289,22 @@ TextureImageTextureSourceOGL::GetFormat(
 }
 
 nsIntRect TextureImageTextureSourceOGL::GetTileRect()
 {
   return ThebesIntRect(mTexImage->GetTileRect());
 }
 
 void
-TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit)
+TextureImageTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   MOZ_ASSERT(mTexImage,
     "Trying to bind a TextureSource that does not have an underlying GL texture.");
   mTexImage->BindTexture(aTextureUnit);
+  SetFilter(mGL, aFilter);
 }
 
 SharedTextureSourceOGL::SharedTextureSourceOGL(CompositorOGL* aCompositor,
                                                gl::SharedTextureHandle aHandle,
                                                gfx::SurfaceFormat aFormat,
                                                GLenum aTarget,
                                                GLenum aWrapMode,
                                                SharedTextureShareType aShareType,
@@ -312,31 +314,31 @@ SharedTextureSourceOGL::SharedTextureSou
   , mSharedHandle(aHandle)
   , mFormat(aFormat)
   , mShareType(aShareType)
   , mTextureTarget(aTarget)
   , mWrapMode(aWrapMode)
 {}
 
 void
-SharedTextureSourceOGL::BindTexture(GLenum aTextureUnit)
+SharedTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   if (!gl()) {
     NS_WARNING("Trying to bind a texture without a GLContext");
     return;
   }
   GLuint tex = mCompositor->GetTemporaryTexture(aTextureUnit);
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(mTextureTarget, tex);
   if (!AttachSharedHandle(gl(), mShareType, mSharedHandle)) {
     NS_ERROR("Failed to bind shared texture handle");
     return;
   }
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+  ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget);
 }
 
 void
 SharedTextureSourceOGL::DetachSharedHandle()
 {
   if (!gl()) {
     return;
   }
@@ -448,21 +450,22 @@ SharedTextureHostOGL::SetCompositor(Comp
 gfx::SurfaceFormat
 SharedTextureHostOGL::GetFormat() const
 {
   MOZ_ASSERT(mTextureSource);
   return mTextureSource->GetFormat();
 }
 
 void
-StreamTextureSourceOGL::BindTexture(GLenum activetex)
+StreamTextureSourceOGL::BindTexture(GLenum activetex, gfx::Filter aFilter)
 {
   MOZ_ASSERT(gl());
   gl()->fActiveTexture(activetex);
   gl()->fBindTexture(mTextureTarget, mTextureHandle);
+  SetFilter(gl(), aFilter);
 }
 
 bool
 StreamTextureSourceOGL::RetrieveTextureFromStream()
 {
   gl()->MakeCurrent();
 
   SharedSurface* sharedSurf = mStream->SwapConsumer();
@@ -534,16 +537,18 @@ StreamTextureSourceOGL::RetrieveTextureF
   gl()->fBindTexture(mTextureTarget, mTextureHandle);
   gl()->fTexParameteri(mTextureTarget,
                       LOCAL_GL_TEXTURE_WRAP_S,
                       LOCAL_GL_CLAMP_TO_EDGE);
   gl()->fTexParameteri(mTextureTarget,
                       LOCAL_GL_TEXTURE_WRAP_T,
                       LOCAL_GL_CLAMP_TO_EDGE);
 
+  ClearCachedFilter();
+
   return true;
 }
 
 void
 StreamTextureSourceOGL::DeallocateDeviceData()
 {
   if (mUploadTexture) {
     MOZ_ASSERT(gl());
@@ -766,20 +771,21 @@ TextureImageDeprecatedTextureHostOGL::Lo
 }
 
 TiledDeprecatedTextureHostOGL::~TiledDeprecatedTextureHostOGL()
 {
   DeleteTextures();
 }
 
 void
-TiledDeprecatedTextureHostOGL::BindTexture(GLenum aTextureUnit)
+TiledDeprecatedTextureHostOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   mGL->fActiveTexture(aTextureUnit);
   mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
+  SetFilter(mGL, aFilter);
 }
 
 static void
 GetFormatAndTileForImageFormat(gfxImageFormat aFormat,
                                GLenum& aOutFormat,
                                GLenum& aOutType)
 {
   if (aFormat == gfxImageFormat::RGB16_565) {
@@ -831,20 +837,19 @@ TiledDeprecatedTextureHostOGL::Update(gf
   if (!mGL->MakeCurrent()) {
     return;
   }
 
   if (aFlags & TEXTURE_NEW_TILE) {
     SetFlags(aFlags);
     mGL->fGenTextures(1, &mTextureHandle);
     mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
     mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
     mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    ClearCachedFilter();
   } else {
     mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mTextureHandle);
     // We're re-using a texture, but the format may change. Update the memory
     // reporter with a free and alloc (below) using the old and new formats.
     gl::GfxTexturesReporter::UpdateAmount(gl::GfxTexturesReporter::MemoryFreed,
                                           mGLFormat, GetTileType(),
                                           TILEDLAYERBUFFER_TILE_SIZE);
   }
@@ -1048,17 +1053,17 @@ GrallocDeprecatedTextureHostOGL::SwapTex
 }
 
 gl::GLContext*
 GrallocDeprecatedTextureHostOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
-void GrallocDeprecatedTextureHostOGL::BindTexture(GLenum aTextureUnit)
+void GrallocDeprecatedTextureHostOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   PROFILER_LABEL("Gralloc", "BindTexture");
   /*
    * The job of this function is to ensure that the texture is tied to the
    * android::GraphicBuffer, so that texturing will source the GraphicBuffer.
    *
    * To this effect we create an EGLImage wrapping this GraphicBuffer,
    * using CreateEGLImageForNativeBuffer, and then we tie this EGLImage to our
@@ -1071,17 +1076,17 @@ void GrallocDeprecatedTextureHostOGL::Bi
   if (!gl()->MakeCurrent()) {
     return;
   }
 
   GLuint tex = GetGLTexture();
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(mTextureTarget, tex);
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+  ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget);
 }
 
 bool
 GrallocDeprecatedTextureHostOGL::IsValid() const
 {
   return !!gl() && !!mGraphicBuffer.get();
 }
 
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -72,16 +72,27 @@ public:
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
   virtual void ClearData() MOZ_OVERRIDE;
   gl::GLContext* gl() const;
 protected:
   RefPtr<CompositorOGL> mCompositor;
 };
 
+inline void ApplyFilterToBoundTexture(gl::GLContext* aGL,
+                                      gfx::Filter aFilter,
+                                      GLuint aTarget = LOCAL_GL_TEXTURE_2D)
+{
+  GLenum filter =
+    (aFilter == gfx::Filter::POINT ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR);
+
+  aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, filter);
+  aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, filter);
+}
+
 /*
  * TextureHost implementations for the OpenGL backend.
  *
  * Note that it is important to be careful about the ownership model with
  * the OpenGL backend, due to some widget limitation on Linux: before
  * the nsBaseWidget associated with our OpenGL context has been completely
  * deleted, every resource belonging to the OpenGL context MUST have been
  * released. At the moment the teardown sequence happens in the middle of
@@ -94,31 +105,52 @@ protected:
 
 /**
  * TextureSourceOGL provides the necessary API for CompositorOGL to composite
  * a TextureSource.
  */
 class TextureSourceOGL
 {
 public:
+  TextureSourceOGL()
+    : mHasCachedFilter(false)
+  {}
+
   virtual bool IsValid() const = 0;
 
-  virtual void BindTexture(GLenum aTextureUnit) = 0;
+  virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) = 0;
 
   virtual gfx::IntSize GetSize() const = 0;
 
   virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; }
 
   virtual gfx::SurfaceFormat GetFormat() const = 0;
 
   virtual GLenum GetWrapMode() const = 0;
 
   virtual gfx::Matrix4x4 GetTextureTransform() { return gfx::Matrix4x4(); }
 
   virtual TextureImageDeprecatedTextureHostOGL* AsTextureImageDeprecatedTextureHost() { return nullptr; }
+
+  void SetFilter(gl::GLContext* aGL, gfx::Filter aFilter)
+  {
+    if (mHasCachedFilter &&
+        mCachedFilter == aFilter) {
+      return;
+    }
+    mHasCachedFilter = true;
+    mCachedFilter = aFilter;
+    ApplyFilterToBoundTexture(aGL, aFilter, GetTextureTarget());
+  }
+
+  void ClearCachedFilter() { mHasCachedFilter = false; }
+
+private:
+  gfx::Filter mCachedFilter;
+  bool mHasCachedFilter;
 };
 
 /**
  * TextureHostOGL provides the necessary API for platform specific composition.
  */
 class TextureHostOGL
 {
 public:
@@ -171,17 +203,17 @@ public:
   virtual void DeallocateDeviceData() MOZ_OVERRIDE
   {
     mTexImage = nullptr;
     SetUpdateSerial(0);
   }
 
   virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
 
-  virtual void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE { return !!mTexImage; }
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
@@ -244,17 +276,17 @@ public:
                          gfx::SurfaceFormat aFormat,
                          GLenum aTarget,
                          GLenum aWrapMode,
                          SharedTextureShareType aShareType,
                          gfx::IntSize aSize);
 
   virtual TextureSourceOGL* AsSourceOGL() { return this; }
 
-  virtual void BindTexture(GLenum activetex) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual gfx::Matrix4x4 GetTextureTransform() MOZ_OVERRIDE;
@@ -357,17 +389,17 @@ public:
 
   ~StreamTextureSourceOGL()
   {
     MOZ_COUNT_DTOR(StreamTextureSourceOGL);
   }
 
   virtual TextureSourceOGL* AsSourceOGL() { return this; }
 
-  virtual void BindTexture(GLenum activetex) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE { return !!gl(); }
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual GLenum GetTextureTarget() const { return mTextureTarget; }
@@ -490,17 +522,17 @@ public:
     return !!mTexture;
   }
 
   virtual bool Lock() MOZ_OVERRIDE;
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
   // textureSource
-  void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE
+  void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE
   {
     mTexture->BindTexture(aTextureUnit);
   }
 
   gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   GLenum GetWrapMode() const MOZ_OVERRIDE
   {
@@ -580,17 +612,17 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE
   {
     return DeprecatedTextureHost::GetFormat();
   }
 
   virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
   virtual bool IsValid() const MOZ_OVERRIDE { return true; }
   virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; }
-  virtual void BindTexture(GLenum aTextureUnit);
+  virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter);
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE
   {
     return mSize;
   }
 
   virtual void SwapTexturesImpl(const SurfaceDescriptor& aImage,
                                 nsIntRegion* aRegion = nullptr)
   { MOZ_ASSERT(false, "Tiles should not use this path"); }
@@ -662,17 +694,17 @@ public:
   }
 
   bool IsValid() const MOZ_OVERRIDE;
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
   virtual const char* Name() { return "GrallocDeprecatedTextureHostOGL"; }
 
-  void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
+  void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE
   {
     return this;
   }
 
   // only overridden for hacky fix in gecko 23 for bug 862324
   // see bug 865908 about fixing this.
--- a/gfx/layers/opengl/X11TextureSourceOGL.cpp
+++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp
@@ -34,32 +34,32 @@ X11TextureSourceOGL::DeallocateDeviceDat
       gl::sGLXLibrary.ReleaseTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
       gl()->fDeleteTextures(1, &mTexture);
       mTexture = 0;
     }
   }
 }
 
 void
-X11TextureSourceOGL::BindTexture(GLenum aTextureUnit)
+X11TextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
   gl()->fActiveTexture(aTextureUnit);
 
   if (!mTexture) {
     gl()->fGenTextures(1, &mTexture);
 
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
 
     gl::sGLXLibrary.BindTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
   } else {
     gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
     gl::sGLXLibrary.UpdateTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap());
   }
 
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+  ApplyFilterToBoundTexture(gl(), aFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 }
 
 IntSize
 X11TextureSourceOGL::GetSize() const
 {
   return ToIntSize(mSurface->GetSize());
 }
 
--- a/gfx/layers/opengl/X11TextureSourceOGL.h
+++ b/gfx/layers/opengl/X11TextureSourceOGL.h
@@ -21,17 +21,17 @@ class X11TextureSourceOGL
 {
 public:
   X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface);
   ~X11TextureSourceOGL();
 
   virtual X11TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
 
   virtual bool IsValid() const MOZ_OVERRIDE { return !!gl(); } ;
-  virtual void BindTexture(GLenum aTextureUnit) MOZ_OVERRIDE;
+  virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE;
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
   virtual GLenum GetTextureTarget() const MOZ_OVERRIDE {
     return LOCAL_GL_TEXTURE_2D;
   }
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
   virtual GLenum GetWrapMode() const MOZ_OVERRIDE {
      return LOCAL_GL_CLAMP_TO_EDGE;
   }
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -344,61 +344,61 @@ TEST(AsyncPanZoomController, Constructor
 TEST(AsyncPanZoomController, Pinch) {
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 980, 480);
   fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
   fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
-  fm.mScrollOffset = CSSPoint(300, 300);
-  fm.mZoom = CSSToScreenScale(2.0);
+  fm.SetScrollOffset(CSSPoint(300, 300));
+  fm.SetZoom(CSSToScreenScale(2.0));
   apzc->SetFrameMetrics(fm);
   apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
   // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   ApzcPinch(apzc, 250, 300, 1.25);
 
   // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
   fm = apzc->GetFrameMetrics();
-  EXPECT_EQ(fm.mZoom.scale, 2.5f);
-  EXPECT_EQ(fm.mScrollOffset.x, 305);
-  EXPECT_EQ(fm.mScrollOffset.y, 310);
+  EXPECT_EQ(fm.GetZoom().scale, 2.5f);
+  EXPECT_EQ(fm.GetScrollOffset().x, 305);
+  EXPECT_EQ(fm.GetScrollOffset().y, 310);
 
   // part 2 of the test, move to the top-right corner of the page and pinch and
   // make sure we stay in the correct spot
-  fm.mZoom = CSSToScreenScale(2.0);
-  fm.mScrollOffset = CSSPoint(930, 5);
+  fm.SetZoom(CSSToScreenScale(2.0));
+  fm.SetScrollOffset(CSSPoint(930, 5));
   apzc->SetFrameMetrics(fm);
   // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
 
   ApzcPinch(apzc, 250, 300, 0.5);
 
   // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
   fm = apzc->GetFrameMetrics();
-  EXPECT_EQ(fm.mZoom.scale, 1.0f);
-  EXPECT_EQ(fm.mScrollOffset.x, 880);
-  EXPECT_EQ(fm.mScrollOffset.y, 0);
+  EXPECT_EQ(fm.GetZoom().scale, 1.0f);
+  EXPECT_EQ(fm.GetScrollOffset().x, 880);
+  EXPECT_EQ(fm.GetScrollOffset().y, 0);
 
   apzc->Destroy();
 }
 
 TEST(AsyncPanZoomController, PinchWithTouchActionNone) {
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 980, 480);
   fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
   fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
-  fm.mScrollOffset = CSSPoint(300, 300);
-  fm.mZoom = CSSToScreenScale(2.0);
+  fm.SetScrollOffset(CSSPoint(300, 300));
+  fm.SetZoom(CSSToScreenScale(2.0));
   apzc->SetFrameMetrics(fm);
   // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
 
   // Apzc's OnScaleEnd method calls once SendAsyncScrollDOMEvent and RequestContentRepaint methods,
   // therefore we're setting these specific values.
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtMost(1));
 
@@ -408,46 +408,46 @@ TEST(AsyncPanZoomController, PinchWithTo
   apzc->SetTouchActionEnabled(true);
 
   apzc->SetAllowedTouchBehavior(values);
   ApzcPinch(apzc, 250, 300, 1.25);
 
   // The frame metrics should stay the same since touch-action:none makes
   // apzc ignore pinch gestures.
   fm = apzc->GetFrameMetrics();
-  EXPECT_EQ(fm.mZoom.scale, 2.0f);
-  EXPECT_EQ(fm.mScrollOffset.x, 300);
-  EXPECT_EQ(fm.mScrollOffset.y, 300);
+  EXPECT_EQ(fm.GetZoom().scale, 2.0f);
+  EXPECT_EQ(fm.GetScrollOffset().x, 300);
+  EXPECT_EQ(fm.GetScrollOffset().y, 300);
 }
 
 TEST(AsyncPanZoomController, Overzoom) {
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 100, 100);
   fm.mCompositionBounds = ParentLayerIntRect(0, 0, 100, 100);
   fm.mScrollableRect = CSSRect(0, 0, 125, 150);
-  fm.mScrollOffset = CSSPoint(10, 0);
-  fm.mZoom = CSSToScreenScale(1.0);
+  fm.SetScrollOffset(CSSPoint(10, 0));
+  fm.SetZoom(CSSToScreenScale(1.0));
   apzc->SetFrameMetrics(fm);
   apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
   // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   ApzcPinch(apzc, 50, 50, 0.5);
 
   fm = apzc->GetFrameMetrics();
-  EXPECT_EQ(fm.mZoom.scale, 0.8f);
+  EXPECT_EQ(fm.GetZoom().scale, 0.8f);
   // bug 936721 - PGO builds introduce rounding error so
   // use a fuzzy match instead
-  EXPECT_LT(abs(fm.mScrollOffset.x), 1e-5);
-  EXPECT_LT(abs(fm.mScrollOffset.y), 1e-5);
+  EXPECT_LT(abs(fm.GetScrollOffset().x), 1e-5);
+  EXPECT_LT(abs(fm.GetScrollOffset().y), 1e-5);
 }
 
 TEST(AsyncPanZoomController, SimpleTransform) {
   TimeStamp testStartTime = TimeStamp::Now();
   // RefCounted class can't live in the stack
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
   apzc->SetFrameMetrics(TestFrameMetrics());
@@ -500,21 +500,21 @@ TEST(AsyncPanZoomController, ComplexTran
   nsTArray<nsRefPtr<Layer> > layers;
   nsRefPtr<LayerManager> lm;
   nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
 
   FrameMetrics metrics;
   metrics.mCompositionBounds = ParentLayerIntRect(0, 0, 24, 24);
   metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
   metrics.mViewport = CSSRect(0, 0, 4, 4);
-  metrics.mScrollOffset = CSSPoint(10, 10);
+  metrics.SetScrollOffset(CSSPoint(10, 10));
   metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
   metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2);
   metrics.mResolution = ParentLayerToLayerScale(2);
-  metrics.mZoom = CSSToScreenScale(6);
+  metrics.SetZoom(CSSToScreenScale(6));
   metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3);
   metrics.mScrollId = FrameMetrics::START_SCROLL_ID;
 
   FrameMetrics childMetrics = metrics;
   childMetrics.mScrollId = FrameMetrics::START_SCROLL_ID + 1;
 
   layers[0]->AsContainerLayer()->SetFrameMetrics(metrics);
   layers[1]->AsContainerLayer()->SetFrameMetrics(childMetrics);
@@ -534,36 +534,36 @@ TEST(AsyncPanZoomController, ComplexTran
 
   childApzc->SetFrameMetrics(childMetrics);
   childApzc->NotifyLayersUpdated(childMetrics, true);
   childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(), ParentLayerToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(60, 60), pointOut);
 
   // do an async scroll by 5 pixels and check the transform
-  metrics.mScrollOffset += CSSPoint(5, 0);
+  metrics.ScrollBy(CSSPoint(5, 0));
   apzc->SetFrameMetrics(metrics);
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(90, 60), pointOut);
 
-  childMetrics.mScrollOffset += CSSPoint(5, 0);
+  childMetrics.ScrollBy(CSSPoint(5, 0));
   childApzc->SetFrameMetrics(childMetrics);
   childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(90, 60), pointOut);
 
   // do an async zoom of 1.5x and check the transform
-  metrics.mZoom.scale *= 1.5f;
+  metrics.ZoomBy(1.5f);
   apzc->SetFrameMetrics(metrics);
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(135, 90), pointOut);
 
-  childMetrics.mZoom.scale *= 1.5f;
+  childMetrics.ZoomBy(1.5f);
   childApzc->SetFrameMetrics(childMetrics);
   childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), ParentLayerToScreenScale(3)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(135, 90), pointOut);
 }
 
 TEST_F(AsyncPanZoomControllerTester, Pan) {
   DoPanTest(true, false, mozilla::layers::AllowedTouchBehavior::NONE);
@@ -927,17 +927,17 @@ SetScrollableFrameMetrics(Layer* aLayer,
 {
   ContainerLayer* container = aLayer->AsContainerLayer();
   FrameMetrics metrics;
   metrics.mScrollId = aScrollId;
   nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
   metrics.mCompositionBounds = ParentLayerIntRect(layerBound.x, layerBound.y,
                                                   layerBound.width, layerBound.height);
   metrics.mScrollableRect = aScrollableRect;
-  metrics.mScrollOffset = CSSPoint(0, 0);
+  metrics.SetScrollOffset(CSSPoint(0, 0));
   container->SetFrameMetrics(metrics);
 }
 
 static already_AddRefed<AsyncPanZoomController>
 GetTargetAPZC(APZCTreeManager* manager, const ScreenPoint& aPoint,
               gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToGeckoOut)
 {
   nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint);
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -124,17 +124,16 @@ void SetAllFDsToCloseOnExec();
 // given multimap. Only call this function in a child process where you know
 // that there aren't any other threads.
 void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map);
 #endif
 
 enum ChildPrivileges {
   PRIVILEGES_DEFAULT,
   PRIVILEGES_UNPRIVILEGED,
-  PRIVILEGES_CAMERA,
   PRIVILEGES_INHERIT,
   PRIVILEGES_LAST
 };
 
 #if defined(OS_WIN)
 // Runs the given application name with the given command line. Normally, the
 // first command line argument should be the path to the process, and don't
 // forget to quote it.
--- a/ipc/chromium/src/base/process_util_linux.cc
+++ b/ipc/chromium/src/base/process_util_linux.cc
@@ -297,27 +297,16 @@ void SetCurrentProcessPrivileges(ChildPr
     }
     if (!pix_max_ok) {
       DLOG(ERROR) << "Can't safely get unique uid/gid";
       _exit(127);
     }
     gid += getpid();
     uid += getpid();
   }
-  if (privs == PRIVILEGES_CAMERA) {
-#if ANDROID_VERSION < 17
-    gid_t groups[] = { AID_SDCARD_RW };
-#else
-    gid_t groups[] = { AID_SDCARD_R, AID_SDCARD_RW, AID_MEDIA_RW };
-#endif
-    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
-      DLOG(ERROR) << "FAILED TO setgroups() CHILD PROCESS";
-      _exit(127);
-    }
-  }
 #endif
   if (setgid(gid) != 0) {
     DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
     _exit(127);
   }
   if (setuid(uid) != 0) {
     DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
     _exit(127);
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -118,16 +118,17 @@ namespace JS {
 // Data for tracking memory usage of things hanging off objects.
 struct ObjectsExtraSizes
 {
 #define FOR_EACH_SIZE(macro) \
     macro(Objects, NotLiveGCThing, mallocHeapSlots) \
     macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \
     macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \
     macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \
+    macro(Objects, NotLiveGCThing, nonHeapElementsMapped) \
     macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \
     macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \
     macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \
     macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \
     macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \
     macro(Objects, NotLiveGCThing, mallocHeapCtypesData)
 
     ObjectsExtraSizes()
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -102,16 +102,31 @@ size_t
 gc::GetPageFaultCount()
 {
     PROCESS_MEMORY_COUNTERS pmc;
     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
         return 0;
     return pmc.PageFaultCount;
 }
 
+void *
+gc::AllocateMappedObject(int fd, int *new_fd, size_t offset, size_t length,
+                         size_t alignment, size_t header)
+{
+    // TODO: to be implemented.
+    return nullptr;
+}
+
+// Deallocate mapped memory for object.
+void
+gc::DeallocateMappedObject(int fd, void *p, size_t length)
+{
+    // TODO: to be implemented.
+}
+
 #elif defined(SOLARIS)
 
 #include <sys/mman.h>
 #include <unistd.h>
 
 #ifndef MAP_NOSYNC
 # define MAP_NOSYNC 0
 #endif
@@ -160,20 +175,38 @@ gc::MarkPagesInUse(JSRuntime *rt, void *
 }
 
 size_t
 gc::GetPageFaultCount()
 {
     return 0;
 }
 
+void *
+gc::AllocateMappedObject(int fd, int *new_fd, size_t offset, size_t length,
+                         size_t alignment, size_t header)
+{
+    // TODO: to be implemented.
+    return nullptr;
+}
+
+// Deallocate mapped memory for object.
+void
+gc::DeallocateMappedObject(int fd, void *p, size_t length)
+{
+    // TODO: to be implemented.
+}
+
 #elif defined(XP_UNIX)
 
+#include <algorithm>
 #include <sys/mman.h>
 #include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 void
 gc::InitMemorySubsystem(JSRuntime *rt)
 {
     rt->gcSystemPageSize = rt->gcSystemAllocGranularity = size_t(sysconf(_SC_PAGESIZE));
 }
 
@@ -280,11 +313,95 @@ gc::GetPageFaultCount()
 {
     struct rusage usage;
     int err = getrusage(RUSAGE_SELF, &usage);
     if (err)
         return 0;
     return usage.ru_majflt;
 }
 
+void *
+gc::AllocateMappedObject(int fd, int *new_fd, size_t offset, size_t length,
+                         size_t alignment, size_t header)
+{
+#define NEED_PAGE_ALIGNED 0
+    size_t pa_start; // Page aligned starting
+    size_t pa_end; // Page aligned ending
+    size_t pa_size; // Total page aligned size
+    size_t page_size = sysconf(_SC_PAGESIZE); // Page size
+    bool page_for_header = false; // Do we need an additional page for header?
+    struct stat st;
+    uint8_t *buf;
+
+    // Make sure file exists and do sanity check for offset and size.
+    if (fstat(fd, &st) < 0 || offset >= (size_t) st.st_size ||
+        length == 0 || length > (size_t) st.st_size - offset)
+        return nullptr;
+
+    // Check for minimal alignment requirement.
+#if NEED_PAGE_ALIGNED
+    alignment = std::max(alignment, page_size);
+#endif
+    if (offset & (alignment - 1))
+        return nullptr;
+
+    // Page aligned starting of the offset.
+    pa_start = offset & ~(page_size - 1);
+    // Calculate page aligned ending by adding one page to the page aligned
+    // starting of data end position(offset + length - 1).
+    pa_end = ((offset + length - 1) & ~(page_size - 1)) + page_size;
+    pa_size = pa_end - pa_start;
+
+    // Do we need one more page for header?
+    if (offset - pa_start < header) {
+        page_for_header = true;
+        pa_size += page_size;
+    }
+
+    // Ask for a continuous memory location.
+    buf = (uint8_t *) MapMemory(pa_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (buf == MAP_FAILED)
+        return nullptr;
+
+    // Duplicate a new fd for mapping, so each cloned object uses a different fd.
+    *new_fd = dup(fd);
+
+    // If there's an additional page for header, don't map that page to file.
+    if (page_for_header) {
+        buf = (uint8_t *) mmap(buf + page_size, pa_size - page_size,
+                               PROT_READ | PROT_WRITE,
+                               MAP_PRIVATE | MAP_FIXED, *new_fd, pa_start);
+    } else {
+        buf = (uint8_t *) mmap(buf, pa_size, PROT_READ | PROT_WRITE,
+                               MAP_PRIVATE | MAP_FIXED, *new_fd, pa_start);
+    }
+    if (buf == MAP_FAILED) {
+        close(*new_fd);
+        return nullptr;
+    }
+
+    // Reset the data before target file, which we don't need to see.
+    memset(buf, 0, offset - pa_start);
+
+    // Reset the data after target file, which we don't need to see.
+    memset(buf + (offset - pa_start) + length, 0, pa_end - (offset + length));
+
+    return buf + (offset - pa_start) - header;
+}
+
+void
+gc::DeallocateMappedObject(int fd, void *p, size_t length)
+{
+    void *pa_start; // Page aligned starting
+    size_t page_size = sysconf(_SC_PAGESIZE); // Page size
+    size_t total_size; // Total allocated size
+
+    // The fd is not needed anymore.
+    close(fd);
+
+    pa_start = (void *)(uintptr_t(p) & ~(page_size - 1));
+    total_size = ((uintptr_t(p) + length) & ~(page_size - 1)) + page_size - uintptr_t(pa_start);
+    munmap(pa_start, total_size);
+}
+
 #else
 #error "Memory mapping functions are not defined for your OS."
 #endif
--- a/js/src/gc/Memory.h
+++ b/js/src/gc/Memory.h
@@ -36,12 +36,25 @@ MarkPagesUnused(JSRuntime *rt, void *p, 
 // platforms.
 bool
 MarkPagesInUse(JSRuntime *rt, void *p, size_t size);
 
 // Returns #(hard faults) + #(soft faults)
 size_t
 GetPageFaultCount();
 
+// Allocate mapped memory for object from file descriptor, offset and length
+// of the file.
+// The new_fd is duplicated from original fd, for the purpose of cloned object.
+// The offset must be aligned according to alignment requirement.
+// An additional page might be allocated depending on offset and header size given.
+void *
+AllocateMappedObject(int fd, int *new_fd, size_t offset, size_t length,
+                     size_t alignment, size_t header);
+
+// Deallocate mapped memory of the object.
+void
+DeallocateMappedObject(int fd, void *p, size_t length);
+
 } // namespace gc
 } // namespace js
 
 #endif /* gc_Memory_h */
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -40,16 +40,17 @@ UNIFIED_SOURCES += [
     'testHashTableInit.cpp',
     'testIndexToString.cpp',
     'testIntern.cpp',
     'testIntString.cpp',
     'testIntTypesABI.cpp',
     'testJSEvaluateScript.cpp',
     'testLookup.cpp',
     'testLooselyEqual.cpp',
+    'testMappedArrayBuffer.cpp',
     'testNewObject.cpp',
     'testNullRoot.cpp',
     'testObjectEmulatingUndefined.cpp',
     'testOOM.cpp',
     'testOps.cpp',
     'testOriginPrincipals.cpp',
     'testParseJSON.cpp',
     'testPersistentRooted.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testMappedArrayBuffer.cpp
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+
+#ifdef XP_UNIX
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "jsfriendapi.h"
+#include "js/StructuredClone.h"
+#include "jsapi-tests/tests.h"
+#include "vm/ArrayBufferObject.h"
+
+const char test_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+const char test_filename[] = "temp-bug945152_MappedArrayBuffer";
+
+BEGIN_TEST(testMappedArrayBuffer_bug945152)
+{
+    TempFile test_file;
+    FILE *test_stream = test_file.open(test_filename);
+    CHECK(fputs(test_data, test_stream) != EOF);
+    test_file.close();
+
+    // Offset 0.
+    CHECK(TestCreateObject(0, 12));
+
+    // Aligned offset.
+    CHECK(TestCreateObject(8, 12));
+
+    // Unaligned offset.
+    CHECK(CreateNewObject(11, 12) == nullptr);
+
+    // Offset + length greater than file size.
+    CHECK(CreateNewObject(8, sizeof(test_data) - 7) == nullptr);
+
+    // Release the mapped content.
+    CHECK(TestReleaseContents());
+
+#ifdef JSGC_USE_EXACT_ROOTING
+    // Ensure that fd is closed after object been GCed.
+    // Check the fd returned from object created in a function,
+    // then do the GC, in order to guarantee the object is freed when
+    // exact rooting is not on.
+    int fd = GetNewObjectFD();
+    GC(cx);
+    CHECK(!fd_is_valid(fd));
+#endif
+
+    // Neuter mapped array buffer.
+    CHECK(TestNeuterObject());
+
+    // Clone mapped array buffer.
+    CHECK(TestCloneObject());
+
+    test_file.remove();
+
+    return true;
+}
+
+JSObject *CreateNewObject(const int offset, const int length)
+{
+    int fd = open(test_filename, O_RDONLY);
+    void *ptr;
+    int new_fd;
+    if (!JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length, &ptr))
+        return nullptr;
+    JSObject *obj = JS_NewArrayBufferWithContents(cx, ptr);
+    close(fd);
+
+    return obj;
+}
+
+// Return the fd from object created in the stack.
+int GetNewObjectFD()
+{
+    JS::RootedObject obj(cx, CreateNewObject(0, 12));
+    int fd = getFD(obj);
+    CHECK(fd_is_valid(fd));
+
+    return fd;
+}
+
+bool VerifyObject(JS::HandleObject obj, const int offset, const int length)
+{
+    CHECK(obj != nullptr);
+    CHECK(JS_IsArrayBufferObject(obj));
+    CHECK_EQUAL(JS_GetArrayBufferByteLength(obj), length);
+    js::ArrayBufferObject *buf = &obj->as<js::ArrayBufferObject>();
+    CHECK(buf->isMappedArrayBuffer());
+    const char *data = reinterpret_cast<const char *>(JS_GetArrayBufferData(obj));
+    CHECK(data != nullptr);
+    CHECK(memcmp(data, test_data + offset, length) == 0);
+
+    return true;
+}
+
+bool TestCreateObject(const int offset, const int length)
+{
+    JS::RootedObject obj(cx, CreateNewObject(offset, length));
+    CHECK(VerifyObject(obj, offset, length));
+
+    return true;
+}
+
+bool TestReleaseContents()
+{
+    int fd = open(test_filename, O_RDONLY);
+    void *ptr;
+    int new_fd;
+    if (!JS_CreateMappedArrayBufferContents(fd, &new_fd, 0, 12, &ptr))
+        return false;
+    CHECK(fd_is_valid(new_fd));
+    JS_ReleaseMappedArrayBufferContents(new_fd, ptr, 12);
+    CHECK(!fd_is_valid(new_fd));
+    close(fd);
+
+    return true;
+}
+
+bool TestNeuterObject()
+{
+    JS::RootedObject obj(cx, CreateNewObject(8, 12));
+    CHECK(obj != nullptr);
+    int fd = getFD(obj);
+    CHECK(fd_is_valid(fd));
+    JS_NeuterArrayBuffer(cx, obj);
+    CHECK(isNeutered(obj));
+    CHECK(!fd_is_valid(fd));
+
+    return true;
+}
+
+bool TestCloneObject()
+{
+    JS::RootedObject obj1(cx, CreateNewObject(8, 12));
+    CHECK(obj1 != nullptr);
+    JSAutoStructuredCloneBuffer cloned_buffer;
+    JS::RootedValue v1(cx, OBJECT_TO_JSVAL(obj1));
+    const JSStructuredCloneCallbacks *callbacks = js::GetContextStructuredCloneCallbacks(cx);
+    CHECK(cloned_buffer.write(cx, v1, callbacks, nullptr));
+    JS::RootedValue v2(cx);
+    CHECK(cloned_buffer.read(cx, &v2, callbacks, nullptr));
+    JS::RootedObject obj2(cx, JSVAL_TO_OBJECT(v2));
+    CHECK(VerifyObject(obj2, 8, 12));
+
+    return true;
+}
+
+bool isNeutered(JS::HandleObject obj)
+{
+    JS::RootedValue v(cx);
+    return JS_GetProperty(cx, obj, "byteLength", &v) && v.toInt32() == 0;
+}
+
+int getFD(JS::HandleObject obj)
+{
+    CHECK(obj != nullptr);
+    js::ArrayBufferObject *buf = &obj->as<js::ArrayBufferObject>();
+    return buf->getMappingFD();
+}
+
+static bool fd_is_valid(int fd)
+{
+     return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
+}
+
+static void GC(JSContext *cx)
+{
+    JS_GC(JS_GetRuntime(cx));
+    // Trigger another to wait for background finalization to end.
+    JS_GC(JS_GetRuntime(cx));
+}
+
+END_TEST(testMappedArrayBuffer_bug945152)
+#endif
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3156,16 +3156,36 @@ JS_AllocateArrayBufferContents(JSContext
 /*
  * Reallocate memory allocated by JS_AllocateArrayBufferContents, growing or
  * shrinking it as appropriate.  The new data pointer will be returned in data.
  * If *contents is nullptr, behaves like JS_AllocateArrayBufferContents.
  */
 extern JS_PUBLIC_API(bool)
 JS_ReallocateArrayBufferContents(JSContext *cx, uint32_t nbytes, void **contents, uint8_t **data);
 
+/*
+ * Create memory mapped array buffer contents.
+ * For cloning, the fd will not be closed after mapping, and the caller must
+ * take care of closing fd after calling this function.
+ * A new duplicated fd used by the mapping is returned in new_fd.
+ */
+extern JS_PUBLIC_API(bool)
+JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset,
+                                   size_t length, void **contents);
+
+/*
+ * Release the allocated resource of mapped array buffer contents before the
+ * object is created.
+ * If a new object has been created by JS_NewArrayBufferWithContents() with
+ * this content, then JS_NeuterArrayBuffer() should be used instead to release
+ * the resource used by the object.
+ */
+extern JS_PUBLIC_API(void)
+JS_ReleaseMappedArrayBufferContents(int fd, void *contents, size_t length);
+
 extern JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj);
 
 /*
  * Create an object to iterate over enumerable properties of obj, in arbitrary
  * property definition order.  NB: This differs from longstanding for..in loop
  * order, which uses order of property definition in obj.
  */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5903,16 +5903,18 @@ JSObject::addSizeOfExcludingThis(mozilla
         if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer())) {
 #if defined (JS_CPU_X64)
             // On x64, ArrayBufferObject::prepareForAsmJS switches the
             // ArrayBufferObject to use mmap'd storage.
             sizes->nonHeapElementsAsmJS += as<ArrayBufferObject>().byteLength();
 #else
             sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements);
 #endif
+        } else if (MOZ_UNLIKELY(elements->isMappedArrayBuffer())) {
+            sizes->nonHeapElementsMapped += as<ArrayBufferObject>().byteLength();
         } else {
             sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
         }
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
     if (is<JSFunction>() ||
         is<JSObject>() ||
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -583,16 +583,18 @@ inline void
 JSObject::finish(js::FreeOp *fop)
 {
     if (hasDynamicSlots())
         fop->free_(slots);
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
         if (MOZ_UNLIKELY(elements->isAsmJSArrayBuffer()))
             js::ArrayBufferObject::releaseAsmJSArrayBuffer(fop, this);
+        else if (MOZ_UNLIKELY(elements->isMappedArrayBuffer()))
+            js::ArrayBufferObject::releaseMappedArrayBuffer(fop, this);
         else
             fop->free_(elements);
     }
 }
 
 /* static */ inline bool
 JSObject::hasProperty(JSContext *cx, js::HandleObject obj,
                       js::HandleId id, bool *foundp, unsigned flags)
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -25,16 +25,17 @@
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
+#include "gc/Memory.h"
 #include "jit/AsmJS.h"
 #include "jit/AsmJSModule.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/NumericConversions.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 
@@ -473,17 +474,20 @@ ArrayBufferObject::changeContents(JSCont
 }
 
 void
 ArrayBufferObject::neuter(JSContext *cx)
 {
     JS_ASSERT(!isSharedArrayBuffer());
 
     JS_ASSERT(cx);
-    if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
+    if (isMappedArrayBuffer()) {
+        releaseMappedArrayBuffer(nullptr, this);
+        setFixedElements();
+    } else if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
         ObjectElements *oldHeader = getElementsHeader();
         changeContents(cx, ObjectElements::fromElements(fixedElements()));
 
         FreeOp fop(cx->runtime(), false);
         fop.free_(oldHeader);
     }
 
     uint32_t byteLen = 0;
@@ -625,16 +629,43 @@ ArrayBufferObject::neuterAsmJSArrayBuffe
 
     js_ReportOverRecursed(cx);
     return false;
 #else
     return true;
 #endif
 }
 
+void *
+ArrayBufferObject::createMappedArrayBuffer(int fd, int *new_fd, size_t offset, size_t length)
+{
+    void *ptr = AllocateMappedObject(fd, new_fd, offset, length, 8,
+                                     sizeof(MappingInfoHeader) + sizeof(ObjectElements));
+    if (!ptr)
+        return nullptr;
+
+    ptr = reinterpret_cast<void *>(uintptr_t(ptr) + sizeof(MappingInfoHeader));
+    ObjectElements *header = reinterpret_cast<ObjectElements *>(ptr);
+    initMappedElementsHeader(header, *new_fd, offset, length);
+
+    return ptr;
+}
+
+void
+ArrayBufferObject::releaseMappedArrayBuffer(FreeOp *fop, JSObject *obj)
+{
+    ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+    if(!buffer.isMappedArrayBuffer() || buffer.isNeutered())
+        return;
+
+    ObjectElements *header = buffer.getElementsHeader();
+    if (header)
+        DeallocateMappedObject(buffer.getMappingFD(), header, header->initializedLength);
+}
+
 void
 ArrayBufferObject::addView(ArrayBufferViewObject *view)
 {
     // This view should never have been associated with a buffer before
     JS_ASSERT(view->bufferLink() == UNSET_BUFFER_LINK);
 
     // Note that pre-barriers are not needed here because either the list was
     // previously empty, in which case no pointer is being overwritten, or the
@@ -1354,16 +1385,31 @@ JS_StealArrayBufferContents(JSContext *c
 
     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
     if (!ArrayBufferObject::stealContents(cx, buffer, contents, data))
         return false;
 
     return true;
 }
 
+JS_PUBLIC_API(bool)
+JS_CreateMappedArrayBufferContents(int fd, int *new_fd, size_t offset,
+                                   size_t length, void **contents)
+{
+    *contents = ArrayBufferObject::createMappedArrayBuffer(fd, new_fd, offset, length);
+
+    return *contents;
+}
+
+JS_PUBLIC_API(void)
+JS_ReleaseMappedArrayBufferContents(int fd, void *contents, size_t length)
+{
+    DeallocateMappedObject(fd, contents, length);
+}
+
 JS_FRIEND_API(void *)
 JS_GetArrayBufferViewData(JSObject *obj)
 {
     obj = CheckedUnwrap(obj);
     if (!obj)
         return nullptr;
     return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer()
                                      : obj->as<TypedArrayObject>().viewData();
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -13,16 +13,23 @@
 #include "vm/Runtime.h"
 
 typedef struct JSProperty JSProperty;
 
 namespace js {
 
 class ArrayBufferViewObject;
 
+// Header for mapped array buffer
+struct MappingInfoHeader
+{
+    uint32_t fd;
+    uint32_t offset;
+};
+
 // The inheritance hierarchy for the various classes relating to typed arrays
 // is as follows.
 //
 // - JSObject
 //   - ArrayBufferObject
 //     - SharedArrayBufferObject
 //   - ArrayBufferViewObject
 //     - DataViewObject
@@ -145,16 +152,43 @@ class ArrayBufferObject : public JSObjec
         header->capacity = 0;
     }
 
     static void initElementsHeader(js::ObjectElements *header, uint32_t bytes) {
         header->flags = 0;
         updateElementsHeader(header, bytes);
     }
 
+    static void initMappedElementsHeader(js::ObjectElements *header, uint32_t fd,
+                                         uint32_t offset, uint32_t bytes) {
+        initElementsHeader(header, bytes);
+        header->setIsMappedArrayBuffer();
+        MappingInfoHeader *mh = getMappingInfoHeader(header);
+        mh->fd = fd;
+        mh->offset = offset;
+    }
+
+    static MappingInfoHeader *getMappingInfoHeader(js::ObjectElements *header) {
+        MOZ_ASSERT(header->isMappedArrayBuffer());
+        return reinterpret_cast<MappingInfoHeader *>(uintptr_t(header) -
+                                                     sizeof(MappingInfoHeader));
+    }
+
+    uint32_t getMappingFD() {
+        MOZ_ASSERT(getElementsHeader()->isMappedArrayBuffer());
+        MappingInfoHeader *mh = getMappingInfoHeader(getElementsHeader());
+        return mh->fd;
+    }
+
+    uint32_t getMappingOffset() const {
+        MOZ_ASSERT(getElementsHeader()->isMappedArrayBuffer());
+        MappingInfoHeader *mh = getMappingInfoHeader(getElementsHeader());
+        return mh->offset;
+    }
+
     static uint32_t headerInitializedLength(const js::ObjectElements *header) {
         return header->initializedLength;
     }
 
     void addView(ArrayBufferViewObject *view);
 
     void changeContents(JSContext *cx, ObjectElements *newHeader);
 
@@ -197,16 +231,25 @@ class ArrayBufferObject : public JSObjec
         return getElementsHeader()->isAsmJSArrayBuffer();
     }
     bool isNeutered() const {
         return getElementsHeader()->isNeuteredBuffer();
     }
     static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
     static bool neuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
     static void releaseAsmJSArrayBuffer(FreeOp *fop, JSObject *obj);
+
+    bool isMappedArrayBuffer() const {
+        return getElementsHeader()->isMappedArrayBuffer();
+    }
+    void setIsMappedArrayBuffer() {
+        getElementsHeader()->setIsMappedArrayBuffer();
+    }
+    static void *createMappedArrayBuffer(int fd, int *new_fd, size_t offset, size_t length);
+    static void releaseMappedArrayBuffer(FreeOp *fop, JSObject *obj);
 };
 
 /*
  * ArrayBufferViewObject
  *
  * Common definitions shared by all ArrayBufferViews.
  */
 
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -166,20 +166,21 @@ ArraySetLength(typename ExecutionModeTra
 class ObjectElements
 {
   public:
     enum Flags {
         CONVERT_DOUBLE_ELEMENTS     = 0x1,
         ASMJS_ARRAY_BUFFER          = 0x2,
         NEUTERED_BUFFER             = 0x4,
         SHARED_ARRAY_BUFFER         = 0x8,
+        MAPPED_ARRAY_BUFFER         = 0x10,
 
         // Present only if these elements correspond to an array with
         // non-writable length; never present for non-arrays.
-        NONWRITABLE_ARRAY_LENGTH    = 0x10
+        NONWRITABLE_ARRAY_LENGTH    = 0x20,
     };
 
   private:
     friend class ::JSObject;
     friend class ObjectImpl;
     friend class ArrayObject;
     friend class ArrayBufferObject;
     friend class ArrayBufferViewObject;
@@ -244,16 +245,22 @@ class ObjectElements
         flags |= NEUTERED_BUFFER;
     }
     bool isSharedArrayBuffer() const {
         return flags & SHARED_ARRAY_BUFFER;
     }
     void setIsSharedArrayBuffer() {
         flags |= SHARED_ARRAY_BUFFER;
     }
+    bool isMappedArrayBuffer() const {
+        return flags & MAPPED_ARRAY_BUFFER;
+    }
+    void setIsMappedArrayBuffer() {
+        flags |= MAPPED_ARRAY_BUFFER;
+    }
     bool hasNonwritableArrayLength() const {
         return flags & NONWRITABLE_ARRAY_LENGTH;
     }
     void setNonwritableArrayLength() {
         flags |= NONWRITABLE_ARRAY_LENGTH;
     }
 
   public:
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -61,16 +61,17 @@ enum StructuredDataType {
     SCTAG_BOOLEAN,
     SCTAG_INDEX,
     SCTAG_STRING,
     SCTAG_DATE_OBJECT,
     SCTAG_REGEXP_OBJECT,
     SCTAG_ARRAY_OBJECT,
     SCTAG_OBJECT_OBJECT,
     SCTAG_ARRAY_BUFFER_OBJECT,
+    SCTAG_MAPPED_ARRAY_BUFFER_OBJECT,
     SCTAG_BOOLEAN_OBJECT,
     SCTAG_STRING_OBJECT,
     SCTAG_NUMBER_OBJECT,
     SCTAG_BACK_REFERENCE_OBJECT,
     SCTAG_DO_NOT_USE_1,
     SCTAG_DO_NOT_USE_2,
     SCTAG_TYPED_ARRAY_OBJECT,
     SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
@@ -207,16 +208,17 @@ struct JSStructuredCloneReader {
     JSContext *context() { return in.context(); }
 
     bool readTransferMap();
 
     bool checkDouble(double d);
     JSString *readString(uint32_t nchars);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, js::Value *vp, bool v1Read = false);
     bool readArrayBuffer(uint32_t nbytes, js::Value *vp);
+    bool readMappedArrayBuffer(Value *vp, uint32_t fd, uint32_t offset, uint32_t length);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, js::Value *vp);
     bool readId(jsid *idp);
     bool startRead(js::Value *vp);
 
     js::SCInput &in;
 
     // Stack of objects with properties remaining to be read.
     js::AutoValueVector objs;
@@ -826,16 +828,22 @@ JSStructuredCloneWriter::writeTypedArray
 
     return out.write(tarr->byteOffset());
 }
 
 bool
 JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
 {
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
+
+    if (buffer.isMappedArrayBuffer()) {
+        return out.writePair(SCTAG_MAPPED_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
+               out.writePair(buffer.getMappingFD(), buffer.getMappingOffset());
+    }
+
     return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
            out.writeBytes(buffer.dataPointer(), buffer.byteLength());
 }
 
 bool
 JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref)
 {
     /* Handle cycles in the object graph. */
@@ -1223,16 +1231,36 @@ JSStructuredCloneReader::readArrayBuffer
     if (!obj)
         return false;
     vp->setObject(*obj);
     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
     JS_ASSERT(buffer.byteLength() == nbytes);
     return in.readArray(buffer.dataPointer(), nbytes);
 }
 
+bool
+JSStructuredCloneReader::readMappedArrayBuffer(Value *vp, uint32_t fd,
+                                               uint32_t offset, uint32_t length)
+{
+    void *ptr;
+    int new_fd;
+    if(!JS_CreateMappedArrayBufferContents(fd, &new_fd, offset, length, &ptr)) {
+        JS_ReportError(context(), "Failed to create mapped array buffer contents");
+        return false;
+    }
+    JSObject *obj = JS_NewArrayBufferWithContents(context(), ptr);
+    if (!obj) {
+        JS_ReleaseMappedArrayBufferContents(new_fd, ptr, length);
+        return false;
+    }
+    vp->setObject(*obj);
+
+    return true;
+}
+
 static size_t
 bytesPerTypedArrayElement(uint32_t arrayType)
 {
     switch (arrayType) {
       case ScalarTypeDescr::TYPE_INT8:
       case ScalarTypeDescr::TYPE_UINT8:
       case ScalarTypeDescr::TYPE_UINT8_CLAMPED:
         return sizeof(uint8_t);
@@ -1407,16 +1435,24 @@ JSStructuredCloneReader::startRead(Value
                              "invalid input");
         return false;
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
         if (!readArrayBuffer(data, vp))
             return false;
         break;
 
+      case SCTAG_MAPPED_ARRAY_BUFFER_OBJECT:
+        uint32_t fd, offset;
+        if (!in.readPair(&fd, &offset))
+            return false;
+        if (!readMappedArrayBuffer(vp, fd, offset, data))
+            return false;
+        break;
+
       case SCTAG_TYPED_ARRAY_OBJECT:
         // readTypedArray adds the array to allObjs
         uint64_t arrayType;
         if (!in.read(&arrayType))
             return false;
         return readTypedArray(arrayType, data, vp);
         break;
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2134,16 +2134,22 @@ ReportCompartmentStats(const JS::Compart
     }
     if (nonHeapElementsAsmJS > 0) {
         REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
             KIND_NONHEAP, nonHeapElementsAsmJS,
             "asm.js array buffer elements outside both the malloc heap and "
             "the GC heap.");
     }
 
+    if (cStats.objectsExtra.nonHeapElementsMapped > 0) {
+        REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
+            KIND_NONHEAP, cStats.objectsExtra.nonHeapElementsMapped,
+            "Memory-mapped array buffer elements.");
+    }
+
     REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
         KIND_NONHEAP, cStats.objectsExtra.nonHeapCodeAsmJS,
         "AOT-compiled asm.js code.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/asm.js-module-data"),
         cStats.objectsExtra.mallocHeapAsmJSModuleData,
         "asm.js module data.");
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -677,17 +677,17 @@ static void RecordFrameMetrics(nsIFrame*
     }
     if (scrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
       contentBounds.x = scrollPosition.x;
       contentBounds.width = 0;
     }
     contentBounds.width += scrollableFrame->GetScrollPortRect().width;
     contentBounds.height += scrollableFrame->GetScrollPortRect().height;
     metrics.mScrollableRect = CSSRect::FromAppUnits(contentBounds);
-    metrics.mScrollOffset = CSSPoint::FromAppUnits(scrollPosition);
+    metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
 
     // If the frame was scrolled since the last layers update, and by
     // something other than the APZ code, we want to tell the APZ to update
     // its scroll offset.
     nsIAtom* originOfLastScroll = scrollableFrame->OriginOfLastScroll();
     if (originOfLastScroll && originOfLastScroll != nsGkAtoms::apz) {
       metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
     }
@@ -723,18 +723,18 @@ static void RecordFrameMetrics(nsIFrame*
   }
 
   metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(
     (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel);
 
   // Initially, AsyncPanZoomController should render the content to the screen
   // at the painted resolution.
   const LayerToScreenScale layerToScreenScale(1.0f);
-  metrics.mZoom = metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel
-                * layerToScreenScale;
+  metrics.SetZoom(metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel
+                  * layerToScreenScale);
 
   if (presShell) {
     nsIDocument* document = nullptr;
     document = presShell->GetDocument();
     if (document) {
       nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
       if (innerWin) {
         metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners();
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -154,18 +154,18 @@ nsTextControlFrame::CalcIntrinsicSize(ns
   nsRefPtr<nsFontMetrics> fontMet;
   nsresult rv =
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
                                           aFontSizeInflation);
   NS_ENSURE_SUCCESS(rv, rv);
   aRenderingContext->SetFont(fontMet);
 
   lineHeight =
-    nsHTMLReflowState::CalcLineHeight(StyleContext(), NS_AUTOHEIGHT,
-                                      aFontSizeInflation);
+    nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
+                                      NS_AUTOHEIGHT, aFontSizeInflation);
   charWidth = fontMet->AveCharWidth();
   charMaxAdvance = fontMet->MaxAdvance();
 
   // Set the width equal to the width in characters
   int32_t cols = GetCols();
   aIntrinsicSize.width = cols * charWidth;
 
   // To better match IE, take the maximum character width(in twips) and remove
@@ -481,18 +481,18 @@ nsTextControlFrame::Reflow(nsPresContext
                        aReflowState.ComputedPhysicalBorderPadding().LeftRight();
   aDesiredSize.Height() = aReflowState.ComputedHeight() +
                         aReflowState.ComputedPhysicalBorderPadding().TopBottom();
 
   // computation of the ascent wrt the input height
   nscoord lineHeight = aReflowState.ComputedHeight();
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   if (!IsSingleLineTextControl()) {
-    lineHeight = nsHTMLReflowState::CalcLineHeight(StyleContext(), 
-                                                  NS_AUTOHEIGHT, inflation);
+    lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
+                                                   NS_AUTOHEIGHT, inflation);
   }
   nsRefPtr<nsFontMetrics> fontMet;
   nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this, 
                                                       getter_AddRefs(fontMet), 
                                                       inflation);
   NS_ENSURE_SUCCESS(rv, rv);
   // now adjust for our borders and padding
   aDesiredSize.SetTopAscent( 
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -451,19 +451,20 @@ nsBlockFrame::GetCaretBaseline() const
     const nsLineBox* firstLine = line;
     if (firstLine->GetChildCount()) {
       return bp.top + firstLine->mFirstChild->GetCaretBaseline();
     }
   }
   nsRefPtr<nsFontMetrics> fm;
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
-  return nsLayoutUtils::GetCenteredFontBaseline(fm, nsHTMLReflowState::
-      CalcLineHeight(StyleContext(), contentRect.height, inflation)) +
-    bp.top;
+  nscoord lineHeight =
+    nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
+                                      contentRect.height, inflation);
+  return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight) + bp.top;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Child frame enumeration
 
 const nsFrameList&
 nsBlockFrame::GetChildList(ChildListID aListID) const
 {
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -2463,32 +2463,43 @@ ComputeLineHeight(nsStyleContext* aStyle
 
 nscoord
 nsHTMLReflowState::CalcLineHeight() const
 {
   nscoord blockHeight =
     nsLayoutUtils::IsNonWrapperBlock(frame) ? ComputedHeight() :
     (mCBReflowState ? mCBReflowState->ComputedHeight() : NS_AUTOHEIGHT);
 
-  return CalcLineHeight(frame->StyleContext(), blockHeight,
+  return CalcLineHeight(frame->GetContent(), frame->StyleContext(), blockHeight,
                         nsLayoutUtils::FontSizeInflationFor(frame));
 }
 
 /* static */ nscoord
-nsHTMLReflowState::CalcLineHeight(nsStyleContext* aStyleContext,
+nsHTMLReflowState::CalcLineHeight(nsIContent* aContent,
+                                  nsStyleContext* aStyleContext,
                                   nscoord aBlockHeight,
                                   float aFontSizeInflation)
 {
   NS_PRECONDITION(aStyleContext, "Must have a style context");
 
   nscoord lineHeight =
     ComputeLineHeight(aStyleContext, aBlockHeight, aFontSizeInflation);
 
   NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
 
+  if (aContent && aContent->IsHTML(nsGkAtoms::input)) {
+    // For Web-compatibility, input elements cannot have a line-height
+    // smaller than one.
+    nscoord lineHeightOne =
+      aFontSizeInflation * aStyleContext->StyleFont()->mFont.size;
+    if (lineHeight < lineHeightOne) {
+      lineHeight = lineHeightOne;
+    }
+  }
+
   return lineHeight;
 }
 
 bool
 nsCSSOffsetState::ComputeMargin(nscoord aHorizontalPercentBasis,
                                 nscoord aVerticalPercentBasis)
 {
   // SVG text frames have no margin.
--- a/layout/generic/nsHTMLReflowState.h
+++ b/layout/generic/nsHTMLReflowState.h
@@ -543,17 +543,18 @@ public:
    *                     Only used with line-height:-moz-block-height.
    *                     NS_AUTOHEIGHT results in a normal line-height for
    *                     line-height:-moz-block-height.
    * @param aFontSizeInflation The result of the appropriate
    *                           nsLayoutUtils::FontSizeInflationFor call,
    *                           or 1.0 if during intrinsic size
    *                           calculation.
    */
-  static nscoord CalcLineHeight(nsStyleContext* aStyleContext,
+  static nscoord CalcLineHeight(nsIContent* aContent,
+                                nsStyleContext* aStyleContext,
                                 nscoord aBlockHeight,
                                 float aFontSizeInflation);
 
 
   void ComputeContainingBlockRectangle(nsPresContext*          aPresContext,
                                        const nsHTMLReflowState* aContainingBlockRS,
                                        nscoord&                 aContainingBlockWidth,
                                        nscoord&                 aContainingBlockHeight);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1716,17 +1716,17 @@ nsLineLayout::BlockDirAlignFrames(PerSpa
   }
   else {
     // Compute the logical block size for this span. The logical block size
     // is based on the "line-height" value, not the font-size. Also
     // compute the top leading.
     float inflation =
       GetInflationForBlockDirAlignment(spanFrame, mInflationMinFontSize);
     nscoord logicalBSize = nsHTMLReflowState::
-      CalcLineHeight(spanFrame->StyleContext(),
+      CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(),
                      mBlockReflowState->ComputedHeight(),
                      inflation);
     nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) -
       spanFramePFD->mBorderPadding.BStart(frameWM) -
       spanFramePFD->mBorderPadding.BEnd(frameWM);
 
     // Special-case for a ::first-letter frame, set the line height to
     // the frame block size if the user has left line-height == normal
@@ -1976,17 +1976,17 @@ nsLineLayout::BlockDirAlignFrames(PerSpa
     } else {
       // We have either a coord, a percent, or a calc().
       nscoord pctBasis = 0;
       if (verticalAlign.HasPercent()) {
         // Percentages are like lengths, except treated as a percentage
         // of the elements line block size value.
         float inflation =
           GetInflationForBlockDirAlignment(frame, mInflationMinFontSize);
-        pctBasis = nsHTMLReflowState::CalcLineHeight(
+        pctBasis = nsHTMLReflowState::CalcLineHeight(frame->GetContent(),
           frame->StyleContext(), mBlockReflowState->ComputedBSize(),
           inflation);
       }
       nscoord offset =
         nsRuleNode::ComputeCoordPercentCalc(verticalAlign, pctBasis);
       // According to the CSS2 spec (10.8.1), a positive value
       // "raises" the box by the given distance while a negative value
       // "lowers" the box by the given distance (with zero being the
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4947,17 +4947,18 @@ nsTextFrame::UnionAdditionalOverflow(nsP
 
 static gfxFloat
 ComputeDescentLimitForSelectionUnderline(nsPresContext* aPresContext,
                                          nsTextFrame* aFrame,
                                          const gfxFont::Metrics& aFontMetrics)
 {
   gfxFloat app = aPresContext->AppUnitsPerDevPixel();
   nscoord lineHeightApp =
-    nsHTMLReflowState::CalcLineHeight(aFrame->StyleContext(), NS_AUTOHEIGHT,
+    nsHTMLReflowState::CalcLineHeight(aFrame->GetContent(),
+                                      aFrame->StyleContext(), NS_AUTOHEIGHT,
                                       aFrame->GetFontSizeInflation());
   gfxFloat lineHeight = gfxFloat(lineHeightApp) / app;
   if (lineHeight <= aFontMetrics.maxHeight) {
     return aFontMetrics.maxDescent;
   }
   return aFontMetrics.maxDescent + (lineHeight - aFontMetrics.maxHeight) / 2;
 }
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -417,18 +417,18 @@ BuildViewMap(ViewMap& oldContentViews, V
       aAccConfigXScale *= config.mXScale;
       aAccConfigYScale *= config.mYScale;
     } else {
       // View doesn't exist, so generate one. We start the view scroll offset at
       // the same position as the framemetric's scroll offset from the layer.
       // The default scale is 1, so no need to propagate scale down.
       ViewConfig config;
       config.mScrollOffset = nsPoint(
-        NSIntPixelsToAppUnits(metrics.mScrollOffset.x, auPerCSSPixel) * aXScale,
-        NSIntPixelsToAppUnits(metrics.mScrollOffset.y, auPerCSSPixel) * aYScale);
+        NSIntPixelsToAppUnits(metrics.GetScrollOffset().x, auPerCSSPixel) * aXScale,
+        NSIntPixelsToAppUnits(metrics.GetScrollOffset().y, auPerCSSPixel) * aYScale);
       view = new nsContentView(aFrameLoader, scrollId, metrics.mIsRoot, config);
       view->mParentScaleX = aAccConfigXScale;
       view->mParentScaleY = aAccConfigYScale;
     }
 
     // I don't know what units mViewportSize is in, hence use ToUnknownRect
     // here to mark the current frontier in type info propagation
     gfx::Rect viewport = metrics.mViewport.ToUnknownRect();
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-button-0.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+button { line-height: 0.5 }
+
+</style>
+<button>button input</button>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-button-1.0.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+button { line-height: 1.0 }
+
+</style>
+<button>button input</button>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-button-1.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+button { line-height: 1.5 }
+
+</style>
+<button>button input</button>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-input-0.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 0.5 }
+
+</style>
+<input type="button" value="button input" size="20">
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-input-1.0.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 1.0 }
+
+</style>
+<input type="button" value="button input" size="20">
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/line-height-input-1.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 1.5 }
+
+</style>
+<input type="button" value="button input" size="20">
--- a/layout/reftests/forms/button/reftest.list
+++ b/layout/reftests/forms/button/reftest.list
@@ -7,8 +7,13 @@ asserts(1) != first-letter-1.html first-
 # on B2G, despite their "-moz-appearance: none; background: gray", so they
 # don't quite match the reference case's normal <div>. That's why they're fuzzy.
 fuzzy-if(B2G,125,20) == percent-height-child-1.html percent-height-child-1-ref.html
 fuzzy-if(B2G,125,80) == percent-height-child-2.html percent-height-child-2-ref.html
 fuzzy-if(B2G,125,20) == percent-width-child-1.html  percent-width-child-1-ref.html
 fuzzy-if(B2G,125,80) == percent-width-child-2.html  percent-width-child-2-ref.html
 
 == vertical-centering.html vertical-centering-ref.html
+
+!= line-height-button-0.5.html line-height-button-1.0.html
+!= line-height-button-1.5.html line-height-button-1.0.html
+== line-height-input-0.5.html line-height-input-1.0.html
+!= line-height-input-1.5.html line-height-input-1.0.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/text/line-height-0.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 0.5 }
+
+</style>
+<input type="text" value="text input" size="20">
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/text/line-height-1.0.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 1.0 }
+
+</style>
+<input type="text" value="text input" size="20">
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/text/line-height-1.5.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<style>
+
+input { line-height: 1.5 }
+
+</style>
+<input type="text" value="text input" size="20">
--- a/layout/reftests/forms/input/text/reftest.list
+++ b/layout/reftests/forms/input/text/reftest.list
@@ -1,8 +1,10 @@
 == bounds-1.html bounds-1-ref.html
 == size-1.html size-1-ref.html
 skip-if(B2G) fails-if(Android) == size-2.html size-2-ref.html
 HTTP(..) == baseline-1.html baseline-1-ref.html
 skip-if(B2G&&browserIsRemote) HTTP(..) == centering-1.xul centering-1-ref.xul # bug 974780
 skip-if(B2G&&browserIsRemote) == dynamic-height-1.xul dynamic-height-1-ref.xul # bug 974780
 needs-focus == select.html select-ref.html
 == intrinsic-size.html intrinsic-size-ref.html
+== line-height-0.5.html line-height-1.0.html
+!= line-height-1.5.html line-height-1.0.html
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -54,17 +54,17 @@ input {
      must be the same here, for buttons, and for <select> (including its
      internal padding magic) */
   padding: 1px;
   border: 2px inset ThreeDFace;
   background-color: -moz-Field;
   color: -moz-FieldText;
   font: -moz-field;
   text-rendering: optimizeLegibility;
-  line-height: normal !important;
+  line-height: normal;
   text-align: start;
   text-transform: none;
   word-spacing: normal;
   letter-spacing: normal;
   cursor: text;
   -moz-binding: url("chrome://global/content/platformHTMLBindings.xml#inputFields");
   text-indent: 0;
   -moz-user-select: text;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4415,17 +4415,17 @@ nsComputedDOMStyle::GetLineHeightCoord(n
       blockHeight = mInnerFrame->GetContentRect().height;
     } else {
       GetCBContentHeight(blockHeight);
     }
   }
 
   // lie about font size inflation since we lie about font size (since
   // the inflation only applies to text)
-  aCoord = nsHTMLReflowState::CalcLineHeight(mStyleContextHolder,
+  aCoord = nsHTMLReflowState::CalcLineHeight(mContent, mStyleContextHolder,
                                              blockHeight, 1.0f);
 
   // CalcLineHeight uses font->mFont.size, but we want to use
   // font->mSize as the font size.  Adjust for that.  Also adjust for
   // the text zoom, if any.
   const nsStyleFont* font = StyleFont();
   float fCoord = float(aCoord);
   if (font->mAllowZoom) {
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -122,16 +122,25 @@ WebrtcAudioConduit::~WebrtcAudioConduit(
 bool WebrtcAudioConduit::GetLocalSSRC(unsigned int* ssrc) {
   return !mPtrRTP->GetLocalSSRC(mChannel, *ssrc);
 }
 
 bool WebrtcAudioConduit::GetRemoteSSRC(unsigned int* ssrc) {
   return !mPtrRTP->GetRemoteSSRC(mChannel, *ssrc);
 }
 
+bool WebrtcAudioConduit::GetAVStats(int32_t* jitterBufferDelayMs,
+                                    int32_t* playoutBufferDelayMs,
+                                    int32_t* avSyncOffsetMs) {
+  return !mPtrVoEVideoSync->GetDelayEstimate(mChannel,
+                                             jitterBufferDelayMs,
+                                             playoutBufferDelayMs,
+                                             avSyncOffsetMs);
+}
+
 bool WebrtcAudioConduit::GetRTPStats(unsigned int* jitterMs,
                                      unsigned int* cumulativeLost) {
   unsigned int maxJitterMs = 0;
   unsigned int discardedPackets;
   *jitterMs = 0;
   *cumulativeLost = 0;
   return !mPtrRTP->GetRTPStatistics(mChannel, *jitterMs, maxJitterMs,
                                     discardedPackets, *cumulativeLost);
@@ -682,16 +691,31 @@ WebrtcAudioConduit::GetAudioFrame(int16_
     CSFLogError(logTag,  "%s Getting audio data Failed %d", __FUNCTION__, error);
     if(error == VE_RUNTIME_PLAY_ERROR)
     {
       return kMediaConduitPlayoutError;
     }
     return kMediaConduitUnknownError;
   }
 
+  // Not #ifdef DEBUG or on a log module so we can use it for about:webrtc/etc
+  mSamples += lengthSamples;
+  if (mSamples >= mLastSyncLog + samplingFreqHz) {
+    int jitter_buffer_delay_ms = 0;
+    int playout_buffer_delay_ms = 0;
+    int avsync_offset_ms = 0;
+    GetAVStats(&jitter_buffer_delay_ms,
+               &playout_buffer_delay_ms,
+               &avsync_offset_ms); // ignore errors
+    CSFLogError(logTag,
+                "A/V sync: sync delta: %dms, audio jitter delay %dms, playout delay %dms",
+                avsync_offset_ms, jitter_buffer_delay_ms, playout_buffer_delay_ms);
+    mLastSyncLog = mSamples;
+  }
+
 #ifdef MOZILLA_INTERNAL_API
   if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) {
     if (mProcessing.Length() > 0) {
       unsigned int now;
       mPtrVoEVideoSync->GetPlayoutTimestamp(mChannel, now);
       if (static_cast<uint32_t>(now) != mLastTimestamp) {
         mLastTimestamp = static_cast<uint32_t>(now);
         // Find the block that includes this timestamp in the network input
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -158,31 +158,36 @@ public:
                       mVoiceEngine(nullptr),
                       mTransport(nullptr),
                       mEngineTransmitting(false),
                       mEngineReceiving(false),
                       mChannel(-1),
                       mCurSendCodecConfig(nullptr),
                       mCaptureDelay(150),
                       mEchoOn(true),
-                      mEchoCancel(webrtc::kEcAec)
+                      mEchoCancel(webrtc::kEcAec),
 #ifdef MOZILLA_INTERNAL_API
-                      , mLastTimestamp(0)
+                      mLastTimestamp(0),
 #endif // MOZILLA_INTERNAL_API
+                      mSamples(0),
+                      mLastSyncLog(0)
   {
   }
 
   virtual ~WebrtcAudioConduit();
 
   MediaConduitErrorCode Init(WebrtcAudioConduit *other);
 
   int GetChannel() { return mChannel; }
   webrtc::VoiceEngine* GetVoiceEngine() { return mVoiceEngine; }
   bool GetLocalSSRC(unsigned int* ssrc);
   bool GetRemoteSSRC(unsigned int* ssrc);
+  bool GetAVStats(int32_t* jitterBufferDelayMs,
+                  int32_t* playoutBufferDelayMs,
+                  int32_t* avSyncOffsetMs);
   bool GetRTPStats(unsigned int* jitterMs, unsigned int* cumulativeLost);
   bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                              unsigned int* jitterMs,
                              unsigned int* packetsReceived,
                              uint64_t* bytesReceived,
                              unsigned int *cumulativeLost);
   bool GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
                            unsigned int* packetsSent,
@@ -259,13 +264,16 @@ private:
   int32_t mCaptureDelay;
 
   bool mEchoOn;
   webrtc::EcModes  mEchoCancel;
 
 #ifdef MOZILLA_INTERNAL_API
   uint32_t mLastTimestamp;
 #endif // MOZILLA_INTERNAL_API
+
+  uint32_t mSamples;
+  uint32_t mLastSyncLog;
 };
 
 } // end namespace
 
 #endif
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -138,16 +138,19 @@ public:
   virtual MediaConduitErrorCode AttachTransport(RefPtr<TransportInterface> aTransport) = 0;
 
   virtual bool GetLocalSSRC(unsigned int* ssrc) = 0;
   virtual bool GetRemoteSSRC(unsigned int* ssrc) = 0;
 
   /**
    * Functions returning stats needed by w3c stats model.
    */
+  virtual bool GetAVStats(int32_t* jitterBufferDelayMs,
+                          int32_t* playoutBufferDelayMs,
+                          int32_t* avSyncOffsetMs) = 0;
   virtual bool GetRTPStats(unsigned int* jitterMs,
                            unsigned int* cumulativeLost) = 0;
   virtual bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                                      unsigned int* jitterMs,
                                      unsigned int* packetsReceived,
                                      uint64_t* bytesReceived,
                                      unsigned int* cumulativeLost) = 0;
   virtual bool GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -126,16 +126,22 @@ WebrtcVideoConduit::~WebrtcVideoConduit(
 bool WebrtcVideoConduit::GetLocalSSRC(unsigned int* ssrc) {
   return !mPtrRTP->GetLocalSSRC(mChannel, *ssrc);
 }
 
 bool WebrtcVideoConduit::GetRemoteSSRC(unsigned int* ssrc) {
   return !mPtrRTP->GetRemoteSSRC(mChannel, *ssrc);
 }
 
+bool WebrtcVideoConduit::GetAVStats(int32_t* jitterBufferDelayMs,
+                                    int32_t* playoutBufferDelayMs,
+                                    int32_t* avSyncOffsetMs) {
+  return false;
+}
+
 bool WebrtcVideoConduit::GetRTPStats(unsigned int* jitterMs,
                                      unsigned int* cumulativeLost) {
   unsigned int ntpHigh, ntpLow;
   unsigned int packetsSent, bytesSent;
   unsigned short fractionLost;
   unsigned extendedMax;
   int rttMs;
   // GetReceivedRTCPStatistics is a poorly named GetRTPStatistics variant
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -200,16 +200,19 @@ public:
   virtual ~WebrtcVideoConduit() ;
 
   MediaConduitErrorCode Init(WebrtcVideoConduit *other);
 
   int GetChannel() { return mChannel; }
   webrtc::VideoEngine* GetVideoEngine() { return mVideoEngine; }
   bool GetLocalSSRC(unsigned int* ssrc);
   bool GetRemoteSSRC(unsigned int* ssrc);
+  bool GetAVStats(int32_t* jitterBufferDelayMs,
+                  int32_t* playoutBufferDelayMs,
+                  int32_t* avSyncOffsetMs);
   bool GetRTPStats(unsigned int* jitterMs, unsigned int* cumulativeLost);
   bool GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                              unsigned int* jitterMs,
                              unsigned int* packetsReceived,
                              uint64_t* bytesReceived,
                              unsigned int* cumulativeLost);
   bool GetRTCPSenderReport(DOMHighResTimeStamp* timestamp,
                            unsigned int* packetsSent,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2124,17 +2124,18 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
   // to main.
   RefPtr<NrIceCtx> iceCtxTmp(query->iceCtx);
   query->iceCtx = nullptr;
 
   // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
 
   for (size_t p = 0; p < query->pipelines.Length(); ++p) {
     const MediaPipeline& mp = *query->pipelines[p];
-    nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ?
+    bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
+    nsString idstr = isAudio ?
         NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
     idstr.AppendInt(mp.trackid());
 
     switch (mp.direction()) {
       case MediaPipeline::TRANSMIT: {
         nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
         nsString remoteId;
         nsString ssrc;
@@ -2233,16 +2234,28 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
           s.mPacketsLost.Construct(packetsLost);
         }
         if (remoteId.Length()) {
           s.mRemoteId.Construct(remoteId);
         }
         s.mIsRemote = false;
         s.mPacketsReceived.Construct(mp.rtp_packets_received());
         s.mBytesReceived.Construct(mp.rtp_bytes_received());
+
+        if (query->internalStats && isAudio) {
+          int32_t jitterBufferDelay;
+          int32_t playoutBufferDelay;
+          int32_t avSyncDelta;
+          if (mp.Conduit()->GetAVStats(&jitterBufferDelay,
+                                       &playoutBufferDelay,
+                                       &avSyncDelta)) {
+            s.mMozJitterBufferDelay.Construct(jitterBufferDelay);
+            s.mMozAvSyncDelay.Construct(avSyncDelta);
+          }
+        }
         query->report.mInboundRTPStreamStats.Value().AppendElement(s);
         break;
       }
     }
   }
 
   // Gather stats from ICE
   for (size_t s = 0; s != query->streams.Length(); ++s) {
--- a/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_sync_module.cc
@@ -116,19 +116,21 @@ int32_t ViESyncModule::Process() {
   if (voe_channel_id_ == -1) {
     return 0;
   }
   assert(video_rtp_rtcp_ && voe_sync_interface_);
   assert(sync_.get());
 
   int audio_jitter_buffer_delay_ms = 0;
   int playout_buffer_delay_ms = 0;
+  int avsync_offset_ms = 0;
   if (voe_sync_interface_->GetDelayEstimate(voe_channel_id_,
                                             &audio_jitter_buffer_delay_ms,
-                                            &playout_buffer_delay_ms) != 0) {
+                                            &playout_buffer_delay_ms,
+                                            &avsync_offset_ms) != 0) {
     // Could not get VoE delay value, probably not a valid channel Id or
     // the channel have not received enough packets.
     WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceVideo, vie_channel_->Id(),
                  "%s: VE_GetDelayEstimate error for voice_channel %d",
                  __FUNCTION__, voe_channel_id_);
     return 0;
   }
   const int current_audio_delay_ms = audio_jitter_buffer_delay_ms +
@@ -149,21 +151,25 @@ int32_t ViESyncModule::Process() {
   }
 
   if (UpdateMeasurements(&audio_measurement_, *voice_rtp_rtcp,
                          *voice_receiver) != 0) {
     return 0;
   }
 
   int relative_delay_ms;
+  int result;
   // Calculate how much later or earlier the audio stream is compared to video.
-  if (!sync_->ComputeRelativeDelay(audio_measurement_, video_measurement_,
-                                   &relative_delay_ms)) {
+
+  result = sync_->ComputeRelativeDelay(audio_measurement_, video_measurement_,
+                                       &relative_delay_ms);
+  if (!result) {
     return 0;
   }
+  voe_sync_interface_->SetCurrentSyncOffset(voe_channel_id_, relative_delay_ms);
 
   TRACE_COUNTER1("webrtc", "SyncCurrentVideoDelay", current_video_delay_ms);
   TRACE_COUNTER1("webrtc", "SyncCurrentAudioDelay", current_audio_delay_ms);
   TRACE_COUNTER1("webrtc", "SyncRelativeDelay", relative_delay_ms);
   int target_audio_delay_ms = 0;
   int target_video_delay_ms = current_video_delay_ms;
   // Calculate the necessary extra audio delay and desired total video
   // delay to get the streams in sync.
--- a/media/webrtc/trunk/webrtc/voice_engine/channel.cc
+++ b/media/webrtc/trunk/webrtc/voice_engine/channel.cc
@@ -998,16 +998,17 @@ Channel::Channel(int32_t channelId,
     _connectionObserverPtr(NULL),
     _countAliveDetections(0),
     _countDeadDetections(0),
     _outputSpeechType(AudioFrame::kNormalSpeech),
     _average_jitter_buffer_delay_us(0),
     least_required_delay_ms_(0),
     _previousTimestamp(0),
     _recPacketDelayMs(20),
+    _current_sync_offset(0),
     _RxVadDetection(false),
     _rxApmIsEnabled(false),
     _rxAgcIsEnabled(false),
     _rxNsIsEnabled(false),
     restored_packet_in_use_(false)
 {
     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
                  "Channel::Channel() - ctor");
@@ -2190,16 +2191,17 @@ int32_t Channel::ReceivedRTPPacket(const
     WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
                  "Incoming packet: invalid RTP header");
     return -1;
   }
   header.payload_type_frequency =
       rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
   if (header.payload_type_frequency < 0)
     return -1;
+  // MUST call before IncomingPacket() or will always return false
   bool in_order = IsPacketInOrder(header);
   rtp_receive_statistics_->IncomingPacket(header, length,
       IsPacketRetransmitted(header, in_order));
   rtp_payload_registry_->SetIncomingPayloadType(header);
   return ReceivePacket(received_packet, length, header, in_order) ? 0 : -1;
 }
 
 bool Channel::ReceivePacket(const uint8_t* packet,
@@ -4714,25 +4716,27 @@ Channel::GetNetworkStatistics(NetworkSta
     int return_value = audio_coding_->NetworkStatistics(&acm_stats);
     if (return_value >= 0) {
       memcpy(&stats, &acm_stats, sizeof(NetworkStatistics));
     }
     return return_value;
 }
 
 bool Channel::GetDelayEstimate(int* jitter_buffer_delay_ms,
-                               int* playout_buffer_delay_ms) const {
+                               int* playout_buffer_delay_ms,
+                               int* avsync_offset_ms) const {
   if (_average_jitter_buffer_delay_us == 0) {
     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
                  "Channel::GetDelayEstimate() no valid estimate.");
     return false;
   }
   *jitter_buffer_delay_ms = (_average_jitter_buffer_delay_us + 500) / 1000 +
       _recPacketDelayMs;
   *playout_buffer_delay_ms = playout_delay_ms_;
+  *avsync_offset_ms = _current_sync_offset;
   WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
                "Channel::GetDelayEstimate()");
   return true;
 }
 
 int Channel::SetInitialPlayoutDelay(int delay_ms)
 {
   WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
--- a/media/webrtc/trunk/webrtc/voice_engine/channel.h
+++ b/media/webrtc/trunk/webrtc/voice_engine/channel.h
@@ -199,20 +199,22 @@ public:
     int GetRoundTripTimeSummary(StatVal& delaysMs) const;
     int GetDeadOrAliveCounters(int& countDead, int& countAlive) const;
 
     // VoENetEqStats
     int GetNetworkStatistics(NetworkStatistics& stats);
 
     // VoEVideoSync
     bool GetDelayEstimate(int* jitter_buffer_delay_ms,
-                          int* playout_buffer_delay_ms) const;
+                          int* playout_buffer_delay_ms,
+                          int* avsync_offset_ms) const;
     int least_required_delay_ms() const { return least_required_delay_ms_; }
     int SetInitialPlayoutDelay(int delay_ms);
     int SetMinimumPlayoutDelay(int delayMs);
+    void SetCurrentSyncOffset(int offsetMs) { _current_sync_offset = offsetMs; }
     int GetPlayoutTimestamp(unsigned int& timestamp);
     void UpdatePlayoutTimestamp(bool rtcp);
     int SetInitTimestamp(unsigned int timestamp);
     int SetInitSequenceNumber(short sequenceNumber);
 
     // VoEVideoSyncExtended
     int GetRtpRtcp(RtpRtcp** rtpRtcpModule, RtpReceiver** rtp_receiver) const;
 
@@ -555,16 +557,17 @@ private:
     uint32_t _countAliveDetections;
     uint32_t _countDeadDetections;
     AudioFrame::SpeechType _outputSpeechType;
     // VoEVideoSync
     uint32_t _average_jitter_buffer_delay_us;
     int least_required_delay_ms_;
     uint32_t _previousTimestamp;
     uint16_t _recPacketDelayMs;
+    int _current_sync_offset;
     // VoEAudioProcessing
     bool _RxVadDetection;
     bool _rxApmIsEnabled;
     bool _rxAgcIsEnabled;
     bool _rxNsIsEnabled;
     bool restored_packet_in_use_;
 };
 
--- a/media/webrtc/trunk/webrtc/voice_engine/include/voe_video_sync.h
+++ b/media/webrtc/trunk/webrtc/voice_engine/include/voe_video_sync.h
@@ -60,28 +60,34 @@ public:
 
     // Sets a minimum target delay for the jitter buffer. This delay is
     // maintained by the jitter buffer, unless channel condition (jitter in
     // inter-arrival times) dictates a higher required delay. The overall
     // jitter buffer delay is max of |delay_ms| and the latency that NetEq
     // computes based on inter-arrival times and its playout mode.
     virtual int SetMinimumPlayoutDelay(int channel, int delay_ms) = 0;
 
+    // Sets the current a/v delay in ms (negative is video leading) if known,
+    // otherwise 0.
+    virtual int SetCurrentSyncOffset(int channel, int offset_ms) = 0;
+
     // Sets an initial delay for the playout jitter buffer. The playout of the
     // audio is delayed by |delay_ms| in milliseconds. Thereafter, the delay is
     // maintained, unless NetEq's internal mechanism requires a higher latency.
     // Such a latency is computed based on inter-arrival times and NetEq's
     // playout mode.
     virtual int SetInitialPlayoutDelay(int channel, int delay_ms) = 0;
 
-    // Gets the |jitter_buffer_delay_ms| (including the algorithmic delay), and
-    // the |playout_buffer_delay_ms| for a specified |channel|.
+    // Gets the |jitter_buffer_delay_ms| (including the algorithmic delay),
+    // the |playout_buffer_delay_ms| and |avsync_offset_ms| for a specified
+    // |channel|.
     virtual int GetDelayEstimate(int channel,
                                  int* jitter_buffer_delay_ms,
-                                 int* playout_buffer_delay_ms) = 0;
+                                 int* playout_buffer_delay_ms,
+                                 int* avsync_offset_ms) = 0;
 
     // Returns the least required jitter buffer delay. This is computed by the
     // the jitter buffer based on the inter-arrival time of RTP packets and
     // playout mode. NetEq maintains this latency unless a higher value is
     // requested by calling SetMinimumPlayoutDelay().
     virtual int GetLeastRequiredDelayMs(int channel) const = 0;
 
     // Manual initialization of the RTP timestamp.
--- a/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc
+++ b/media/webrtc/trunk/webrtc/voice_engine/test/auto_test/standard/video_sync_test.cc
@@ -29,19 +29,21 @@ class VideoSyncTest : public AfterStream
   // All samples are checked so they are greater than |min_estimate|.
   int CollectEstimatesDuring15Seconds(int min_estimate) {
     Sleep(1000);
 
     std::vector<int> all_delay_estimates;
     for (int second = 0; second < 15; second++) {
       int jitter_buffer_delay_ms = 0;
       int playout_buffer_delay_ms = 0;
+      int avsync_offset_ms = 0;
       EXPECT_EQ(0, voe_vsync_->GetDelayEstimate(channel_,
                                                 &jitter_buffer_delay_ms,
-                                                &playout_buffer_delay_ms));
+                                                &playout_buffer_delay_ms,
+                                                &avsync_offset_ms));
 
       EXPECT_GT(jitter_buffer_delay_ms, min_estimate) <<
           "The delay estimate can not conceivably get lower than " <<
           min_estimate << " ms, it's unrealistic.";
 
       all_delay_estimates.push_back(jitter_buffer_delay_ms);
       Sleep(1000);
     }
--- a/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc
+++ b/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.cc
@@ -135,16 +135,33 @@ int VoEVideoSyncImpl::SetMinimumPlayoutD
     {
         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
             "SetMinimumPlayoutDelay() failed to locate channel");
         return -1;
     }
     return channelPtr->SetMinimumPlayoutDelay(delayMs);
 }
 
+int VoEVideoSyncImpl::SetCurrentSyncOffset(int channel, int offsetMs)
+{
+    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+                 "SetCurrentSyncOffset(channel=%d, offsetMs=%d)",
+                 channel, offsetMs);
+    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+    voe::Channel* channelPtr = ch.channel();
+    if (channelPtr == NULL)
+    {
+        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+            "SetCurrentSyncOffset() failed to locate channel");
+        return -1;
+    }
+    channelPtr->SetCurrentSyncOffset(offsetMs);
+    return 0;
+}
+
 int VoEVideoSyncImpl::SetInitialPlayoutDelay(int channel, int delay_ms)
 {
     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                  "SetInitialPlayoutDelay(channel=%d, delay_ms=%d)",
                  channel, delay_ms);
     IPHONE_NOT_SUPPORTED(_shared->statistics());
 
     if (!_shared->statistics().Initialized())
@@ -160,34 +177,36 @@ int VoEVideoSyncImpl::SetInitialPlayoutD
             "SetInitialPlayoutDelay() failed to locate channel");
         return -1;
     }
     return channelPtr->SetInitialPlayoutDelay(delay_ms);
 }
 
 int VoEVideoSyncImpl::GetDelayEstimate(int channel,
                                        int* jitter_buffer_delay_ms,
-                                       int* playout_buffer_delay_ms) {
+                                       int* playout_buffer_delay_ms,
+                                       int* avsync_offset_ms) {
   WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                "GetDelayEstimate(channel=%d, delayMs=?)", channel);
   IPHONE_NOT_SUPPORTED(_shared->statistics());
 
   if (!_shared->statistics().Initialized()) {
     _shared->SetLastError(VE_NOT_INITED, kTraceError);
     return -1;
   }
   voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
   voe::Channel* channelPtr = ch.channel();
   if (channelPtr == NULL) {
     _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
                           "GetDelayEstimate() failed to locate channel");
     return -1;
   }
   if (!channelPtr->GetDelayEstimate(jitter_buffer_delay_ms,
-                                    playout_buffer_delay_ms)) {
+                                    playout_buffer_delay_ms,
+                                    avsync_offset_ms)) {
     return -1;
   }
   return 0;
 }
 
 int VoEVideoSyncImpl::GetPlayoutBufferSize(int& bufferMs)
 {
     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
--- a/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.h
+++ b/media/webrtc/trunk/webrtc/voice_engine/voe_video_sync_impl.h
@@ -19,21 +19,24 @@ namespace webrtc {
 
 class VoEVideoSyncImpl : public VoEVideoSync
 {
 public:
     virtual int GetPlayoutBufferSize(int& bufferMs);
 
     virtual int SetMinimumPlayoutDelay(int channel, int delayMs);
 
+    virtual int SetCurrentSyncOffset(int channel, int offsetMs);
+
     virtual int SetInitialPlayoutDelay(int channel, int delay_ms);
 
     virtual int GetDelayEstimate(int channel,
                                  int* jitter_buffer_delay_ms,
-                                 int* playout_buffer_delay_ms);
+                                 int* playout_buffer_delay_ms,
+                                 int* avsync_offset_ms);
 
     virtual int GetLeastRequiredDelayMs(int channel) const;
 
     virtual int SetInitTimestamp(int channel, unsigned int timestamp);
 
     virtual int SetInitSequenceNumber(int channel, short sequenceNumber);
 
     virtual int GetPlayoutTimestamp(int channel, unsigned int& timestamp);
--- a/mobile/android/base/tests/BaseTest.java
+++ b/mobile/android/base/tests/BaseTest.java
@@ -53,16 +53,17 @@ abstract class BaseTest extends Activity
     private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko";
     private static final String LAUNCH_ACTIVITY_FULL_CLASSNAME = TestConstants.ANDROID_PACKAGE_NAME + ".App";
     private static final int VERIFY_URL_TIMEOUT = 2000;
     private static final int MAX_LIST_ATTEMPTS = 3;
     private static final int MAX_WAIT_ENABLED_TEXT_MS = 10000;
     private static final int MAX_WAIT_HOME_PAGER_HIDDEN_MS = 15000;
     public static final int MAX_WAIT_MS = 4500;
     public static final int LONG_PRESS_TIME = 6000;
+    private static final int GECKO_READY_WAIT_MS = 180000;
 
     private static Class<Activity> mLauncherActivityClass;
     private Activity mActivity;
     protected Solo mSolo;
     protected Driver mDriver;
     protected Assert mAsserter;
     protected Actions mActions;
     protected String mBaseUrl;
@@ -72,17 +73,17 @@ abstract class BaseTest extends Activity
     public Device mDevice;
     protected DatabaseHelper mDatabaseHelper;
     protected StringHelper mStringHelper;
 
     protected void blockForGeckoReady() {
         try {
             Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready");
             if (!GeckoThread.checkLaunchState(LaunchState.GeckoRunning)) {
-                geckoReadyExpector.blockForEvent();
+                geckoReadyExpector.blockForEvent(GECKO_READY_WAIT_MS, true);
             }
             geckoReadyExpector.unregisterListener();
         } catch (Exception e) {
             mAsserter.dumpLog("Exception in blockForGeckoReady", e);
         }
     }
 
     static {
--- a/mozglue/build/Nuwa.cpp
+++ b/mozglue/build/Nuwa.cpp
@@ -1530,16 +1530,27 @@ CloseAllProtoSockets(NuwaProtoFdInfo *aI
   int i;
 
   for (i = 0; i < aInfoSize; i++) {
     REAL(close)(aInfoList[i].newFds[0]);
     REAL(close)(aInfoList[i].newFds[1]);
   }
 }
 
+static void
+AfterForkHook()
+{
+  void (*AfterNuwaFork)();
+
+  // This is defined in dom/ipc/ContentChild.cpp
+  AfterNuwaFork = (void (*)())
+    dlsym(RTLD_DEFAULT, "AfterNuwaFork");
+  AfterNuwaFork();
+}
+
 /**
  * Fork a new process that is ready for running IPC.
  *
  * @return the PID of the new process.
  */
 static int
 ForkIPCProcess() {
   int pid;
@@ -1560,16 +1571,17 @@ ForkIPCProcess() {
     AddNewProcess(pid, sProtoFdInfos, sProtoFdInfosSize);
     CloseAllProtoSockets(sProtoFdInfos, sProtoFdInfosSize);
   } else {
     // in the child
     if (getenv("MOZ_DEBUG_CHILD_PROCESS")) {
       printf("\n\nNUWA CHILDCHILDCHILDCHILD\n  debug me @ %d\n\n", getpid());
       sleep(30);
     }
+    AfterForkHook();
     ReplaceSignalFds();
     ReplaceIPC(sProtoFdInfos, sProtoFdInfosSize);
     RecreateEpollFds();
     RecreateThreads();
     CloseAllProtoSockets(sProtoFdInfos, sProtoFdInfosSize);
   }
 
   sForkWaitCondChanged = true;
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -250,51 +250,42 @@ DataChannelConnection::Destroy()
   ASSERT_WEBRTC(NS_IsMainThread());
   CloseAll();
 
   MutexAutoLock lock(mLock);
   // If we had a pending reset, we aren't waiting for it - clear the list so
   // we can deregister this DataChannelConnection without leaking.
   ClearResets();
 
-  MOZ_ASSERT(mSTS);
-  ASSERT_WEBRTC(NS_IsMainThread());
-  // Finish Destroy on STS thread to avoid bug 876167 - once that's fixed,
-  // the usrsctp_close() calls can move back here (and just proxy the
-  // disconnect_all())
-  RUN_ON_THREAD(mSTS, WrapRunnable(nsRefPtr<DataChannelConnection>(this),
-                                   &DataChannelConnection::DestroyOnSTS,
-                                   mSocket, mMasterSocket),
-                NS_DISPATCH_NORMAL);
+  if (mSocket && mSocket != mMasterSocket)
+    usrsctp_close(mSocket);
+  if (mMasterSocket)
+    usrsctp_close(mMasterSocket);
 
-  // These will be released on STS
   mSocket = nullptr;
   mMasterSocket = nullptr; // also a flag that we've Destroyed this connection
 
-  // Must do this in Destroy() since we may then delete this object
   if (mUsingDtls) {
     usrsctp_deregister_address(static_cast<void *>(this));
     LOG(("Deregistered %p from the SCTP stack.", static_cast<void *>(this)));
   }
-
   // We can't get any more new callbacks from the SCTP library
   // All existing callbacks have refs to DataChannelConnection
 
   // nsDOMDataChannel objects have refs to DataChannels that have refs to us
-}
-
-void DataChannelConnection::DestroyOnSTS(struct socket *aMasterSocket,
-                                         struct socket *aSocket)
-{
-  if (aSocket && aSocket != aMasterSocket)
-    usrsctp_close(aSocket);
-  if (aMasterSocket)
-    usrsctp_close(aMasterSocket);
-
-  disconnect_all();
+  if (mTransportFlow) {
+    MOZ_ASSERT(mSTS);
+    ASSERT_WEBRTC(NS_IsMainThread());
+    RUN_ON_THREAD(mSTS, WrapRunnable(nsRefPtr<DataChannelConnection>(this),
+                                     &DataChannelConnection::disconnect_all),
+                  NS_DISPATCH_NORMAL);
+    // don't release mTransportFlow until we are destroyed in case
+    // runnables are in flight.  We may well have packets to send as the
+    // SCTP lib may have sent a shutdown.
+  }
 }
 
 NS_IMPL_ISUPPORTS1(DataChannelConnection,
                    nsITimerCallback)
 
 bool
 DataChannelConnection::Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls)
 {
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -118,19 +118,16 @@ public:
     virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0;
   };
 
   DataChannelConnection(DataConnectionListener *listener);
   virtual ~DataChannelConnection();
 
   bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls);
   void Destroy(); // So we can spawn refs tied to runnables in shutdown
-  // Finish Destroy on STS to avoid SCTP race condition with ABORT from far end
-  void DestroyOnSTS(struct socket *aMasterSocket,
-                    struct socket *aSocket);
 
 #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
   // These block; they require something to decide on listener/connector
   // (though you can do simultaneous Connect()).  Do not call these from
   // the main thread!
   bool Listen(unsigned short port);
   bool Connect(const char *addr, unsigned short port);
 #endif
--- a/security/sandbox/linux/Sandbox.cpp
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -52,18 +52,16 @@ namespace mozilla {
 #define LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args)
 #elif defined(PR_LOGGING)
 static PRLogModuleInfo* gSeccompSandboxLog;
 #define LOG_ERROR(args...) PR_LOG(gSeccompSandboxLog, PR_LOG_ERROR, (args))
 #else
 #define LOG_ERROR(args...)
 #endif
 
-// Note: this ifdef includes most of the file.
-#ifdef MOZ_CONTENT_SANDBOX
 struct sock_filter seccomp_filter[] = {
   VALIDATE_ARCHITECTURE,
   EXAMINE_SYSCALL,
   SECCOMP_WHITELIST,
 #ifdef MOZ_CONTENT_SANDBOX_REPORTER
   TRAP_PROCESS,
 #else
   KILL_PROCESS,
@@ -237,78 +235,61 @@ InstallSyscallFilter(void)
     return 1;
   }
 
   if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_prog, 0, 0)) {
     return 1;
   }
   return 0;
 }
-#endif
 
-#if defined(ANDROID) || defined(MOZ_CONTENT_SANDBOX)
 // Use signals for permissions that need to be set per-thread.
-static base::ChildPrivileges sSetPrivilegesTo;
 // The communication channel from the signal handler back to the main thread.
 static mozilla::Atomic<int> sSetSandboxDone;
 // about:memory has the first 3 RT signals.  (We should allocate
 // signals centrally instead of hard-coding them like this.)
 static const int sSetSandboxSignum = SIGRTMIN + 3;
-#endif
 
 static bool
-SetThreadSandbox(base::ChildPrivileges aPrivs, bool aIsMainThread)
+SetThreadSandbox()
 {
   bool didAnything = false;
-  bool shouldSetPrivs = aIsMainThread;
-#if defined(ANDROID)
-  shouldSetPrivs = true;
-#endif
 
-  if (shouldSetPrivs && (aIsMainThread || geteuid() == 0)) {
-    SetCurrentProcessPrivileges(aPrivs);
-    if (aPrivs != base::PRIVILEGES_INHERIT) {
-      didAnything = true;
-    }
-  }
-#if defined(MOZ_CONTENT_SANDBOX)
   if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX") == nullptr &&
       prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) {
     if (InstallSyscallFilter() == 0) {
       didAnything = true;
     }
     /*
      * Bug 880797: when all B2G devices are required to support
      * seccomp-bpf, this should exit/crash if InstallSyscallFilter
      * returns nonzero (ifdef MOZ_WIDGET_GONK).
      */
   }
-#endif
   return didAnything;
 }
 
-#if defined(ANDROID) || defined(MOZ_CONTENT_SANDBOX)
 static void
 SetThreadSandboxHandler(int signum)
 {
   // The non-zero number sent back to the main thread indicates
   // whether action was taken.
-  if (SetThreadSandbox(sSetPrivilegesTo, /* main: */ false)) {
+  if (SetThreadSandbox()) {
     sSetSandboxDone = 2;
   } else {
     sSetSandboxDone = 1;
   }
   // Wake up the main thread.  See the FUTEX_WAIT call, below, for an
   // explanation.
   syscall(__NR_futex, reinterpret_cast<int*>(&sSetSandboxDone),
           FUTEX_WAKE, 1);
 }
 
 static void
-BroadcastSetThreadSandbox(base::ChildPrivileges aPrivs)
+BroadcastSetThreadSandbox()
 {
   pid_t pid, tid;
   DIR *taskdp;
   struct dirent *de;
 
   static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
                 "mozilla::Atomic<int> isn't represented by an int");
   MOZ_ASSERT(NS_IsMainThread());
@@ -321,17 +302,16 @@ BroadcastSetThreadSandbox(base::ChildPri
   if (signal(sSetSandboxSignum, SetThreadSandboxHandler) != SIG_DFL) {
     LOG_ERROR("signal %d in use!\n", sSetSandboxSignum);
     MOZ_CRASH();
   }
 
   // In case this races with a not-yet-deprivileged thread cloning
   // itself, repeat iterating over all threads until we find none
   // that are still privileged.
-  sSetPrivilegesTo = aPrivs;
   bool sandboxProgress;
   do {
     sandboxProgress = false;
     // For each thread...
     while ((de = readdir(taskdp))) {
       char *endptr;
       tid = strtol(de->d_name, &endptr, 10);
       if (*endptr != '\0' || tid <= 0) {
@@ -412,46 +392,38 @@ BroadcastSetThreadSandbox(base::ChildPri
         }
       }
     }
     rewinddir(taskdp);
   } while (sandboxProgress);
   unused << signal(sSetSandboxSignum, SIG_DFL);
   unused << closedir(taskdp);
   // And now, deprivilege the main thread:
-  SetThreadSandbox(aPrivs, /* main: */ true);
+  SetThreadSandbox();
 }
-#else
-static void
-BroadcastSetThreadSandbox(base::ChildPrivileges aPrivs)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  SetThreadSandbox(aPrivs, /* main: */ true);
-}
-#endif
 
 /**
  * Starts the seccomp sandbox for this process and sets user/group-based privileges.
  * Should be called only once, and before any potentially harmful content is loaded.
  *
  * Should normally make the process exit on failure.
 */
 void
-SetCurrentProcessSandbox(base::ChildPrivileges aPrivs)
+SetCurrentProcessSandbox()
 {
 #if !defined(ANDROID) && defined(PR_LOGGING)
   if (!gSeccompSandboxLog) {
     gSeccompSandboxLog = PR_NewLogModule("SeccompSandbox");
   }
   PR_ASSERT(gSeccompSandboxLog);
 #endif
 
-#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_CONTENT_SANDBOX_REPORTER)
+#if defined(MOZ_CONTENT_SANDBOX_REPORTER)
   if (InstallSyscallReporter()) {
     LOG_ERROR("install_syscall_reporter() failed\n");
   }
 #endif
 
-  BroadcastSetThreadSandbox(aPrivs);
+  BroadcastSetThreadSandbox();
 }
 
 } // namespace mozilla
 
--- a/security/sandbox/linux/Sandbox.h
+++ b/security/sandbox/linux/Sandbox.h
@@ -2,18 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_Sandbox_h
 #define mozilla_Sandbox_h
 
-#include "base/process_util.h"
-
 namespace mozilla {
 
-void SetCurrentProcessSandbox(base::ChildPrivileges aPrivs);
+void SetCurrentProcessSandbox();
 
 } // namespace mozilla
 
 #endif // mozilla_Sandbox_h
 
--- a/security/sandbox/linux/seccomp_filter.h
+++ b/security/sandbox/linux/seccomp_filter.h
@@ -106,32 +106,40 @@
 #elif defined(__i386__)
 #define SECCOMP_WHITELIST_ARCH_DESKTOP_LINUX
 #elif defined(__x86_64__)
 #define SECCOMP_WHITELIST_ARCH_DESKTOP_LINUX
 #else
 #define SECCOMP_WHITELIST_ARCH_DESKTOP_LINUX
 #endif
 
+/* Architecture-specific syscalls for B2G */
+#if defined(__i386__)
+#define SECCOMP_WHITELIST_ARCH_B2G_LOW
+#else
+#define SECCOMP_WHITELIST_ARCH_B2G_LOW \
+  ALLOW_SYSCALL(sendto), \
+  ALLOW_SYSCALL(recvfrom),
+#endif
+
 /* B2G specific syscalls */
 #if defined(MOZ_B2G)
 
 #define SECCOMP_WHITELIST_B2G_HIGH \
   ALLOW_SYSCALL(clock_gettime), \
   ALLOW_SYSCALL(epoll_wait), \
   ALLOW_SYSCALL(gettimeofday),
 
 #define SECCOMP_WHITELIST_B2G_MED \
   ALLOW_SYSCALL(getpid), \
   ALLOW_SYSCALL(rt_sigreturn), \
   ALLOW_SYSCALL(poll),
 
 #define SECCOMP_WHITELIST_B2G_LOW \
-  ALLOW_SYSCALL(sendto), \
-  ALLOW_SYSCALL(recvfrom), \
+  SECCOMP_WHITELIST_ARCH_B2G_LOW \
   ALLOW_SYSCALL(getdents64), \
   ALLOW_SYSCALL(epoll_ctl), \
   ALLOW_SYSCALL(sched_yield), \
   ALLOW_SYSCALL(sched_getscheduler), \
   ALLOW_SYSCALL(sched_setscheduler),
 
 #else
 #define SECCOMP_WHITELIST_B2G_HIGH
--- a/services/fxaccounts/FxAccountsClient.jsm
+++ b/services/fxaccounts/FxAccountsClient.jsm
@@ -81,32 +81,41 @@ this.FxAccountsClient.prototype = {
 
   /**
    * Authenticate and create a new session with the Firefox Account API server
    *
    * @param email
    *        The email address for the account (utf8)
    * @param password
    *        The user's password
+   * @param [getKeys=false]
+   *        If set to true the keyFetchToken will be retrieved
+   * @param [retryOK=true]
+   *        If capitalization of the email is wrong and retryOK is set to true,
+   *        we will retry with the suggested capitalization from the server
    * @return Promise
    *        Returns a promise that resolves to an object:
    *        {
    *          uid: the user's unique ID (hex)
    *          sessionToken: a session token (hex)
    *          keyFetchToken: a key fetch token (hex)
    *          verified: flag indicating verification status of the email
+   *          authAt: authentication time for the session (seconds since epoch)
+   *          email: the primary email for this account
    *        }
    */
-  signIn: function signIn(email, password, retryOK=true) {
+  signIn: function signIn(email, password, getKeys=false, retryOK=true) {
     return Credentials.setup(email, password).then((creds) => {
       let data = {
         email: creds.emailUTF8,
         authPW: CommonUtils.bytesAsHex(creds.authPW),
       };
-      return this._request("/account/login", "POST", null, data).then(
+      let keys = getKeys ? "?keys=true" : "";
+
+      return this._request("/account/login" + keys, "POST", null, data).then(
         // Include the canonical capitalization of the email in the response so
         // the caller can set its signed-in user state accordingly.
         result => {
           result.email = data.email;
           return result;
         },
         error => {
           log.debug("signIn error: " + JSON.stringify(error));
@@ -121,17 +130,17 @@ this.FxAccountsClient.prototype = {
           //
           // API reference:
           // https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md
           if (ERRNO_INCORRECT_EMAIL_CASE === error.errno && retryOK) {
             if (!error.email) {
               log.error("Server returned errno 120 but did not provide email");
               throw error;
             }
-            return this.signIn(error.email, password, false);
+            return this.signIn(error.email, password, getKeys, false);
           }
           throw error;
         }
       );
     });
   },
 
   /**
--- a/services/fxaccounts/tests/xpcshell/test_client.js
+++ b/services/fxaccounts/tests/xpcshell/test_client.js
@@ -217,47 +217,103 @@ add_task(function test_signUp() {
   } catch(expectedError) {
     do_check_eq(101, expectedError.errno);
   }
 
   yield deferredStop(server);
 });
 
 add_task(function test_signIn() {
-  let sessionMessage = JSON.stringify({sessionToken: FAKE_SESSION_TOKEN});
-  let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"});
+  let sessionMessage_noKey = JSON.stringify({
+    sessionToken: FAKE_SESSION_TOKEN
+  });
+  let sessionMessage_withKey = JSON.stringify({
+    sessionToken: FAKE_SESSION_TOKEN,
+    keyFetchToken: "keyFetchToken"
+  });
+  let errorMessage_notExistent = JSON.stringify({
+    code: 400,
+    errno: 102,
+    error: "doesn't exist"
+  });
+  let errorMessage_wrongCap = JSON.stringify({
+    code: 400,
+    errno: 120,
+    error: "Incorrect email case",
+    email: "you@example.com"
+  });
 
   let server = httpd_setup({
     "/account/login": function(request, response) {
       let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
       let jsonBody = JSON.parse(body);
 
       if (jsonBody.email == "mé@example.com") {
+        do_check_eq("", request._queryString);
         do_check_eq(jsonBody.authPW, "08b9d111196b8408e8ed92439da49206c8ecfbf343df0ae1ecefcd1e0174a8b6");
         response.setStatusLine(request.httpVersion, 200, "OK");
-        response.bodyOutputStream.write(sessionMessage, sessionMessage.length);
+        response.bodyOutputStream.write(sessionMessage_noKey,
+                                        sessionMessage_noKey.length);
+        return;
+      }
+      else if (jsonBody.email == "you@example.com") {
+        do_check_eq("keys=true", request._queryString);
+        do_check_eq(jsonBody.authPW, "93d20ec50304d496d0707ec20d7e8c89459b6396ec5dd5b9e92809c5e42856c7");
+        response.setStatusLine(request.httpVersion, 200, "OK");
+        response.bodyOutputStream.write(sessionMessage_withKey,
+                                        sessionMessage_withKey.length);
         return;
       }
-
-      // Error trying to sign in to nonexistent account
-      response.setStatusLine(request.httpVersion, 400, "Bad request");
-      response.bodyOutputStream.write(errorMessage, errorMessage.length);
-      return;
+      else if (jsonBody.email == "You@example.com") {
+        // Error trying to sign in with a wrong capitalization
+        response.setStatusLine(request.httpVersion, 400, "Bad request");
+        response.bodyOutputStream.write(errorMessage_wrongCap,
+                                        errorMessage_wrongCap.length);
+        return;
+      }
+      else {
+        // Error trying to sign in to nonexistent account
+        response.setStatusLine(request.httpVersion, 400, "Bad request");
+        response.bodyOutputStream.write(errorMessage_notExistent,
+                                        errorMessage_notExistent.length);
+        return;
+      }
     },
   });
 
+  // Login without retrieving optional keys
   let client = new FxAccountsClient(server.baseURI);
   let result = yield client.signIn('mé@example.com', 'bigsecret');
   do_check_eq(FAKE_SESSION_TOKEN, result.sessionToken);
+  do_check_eq(undefined, result.keyFetchToken);
+
+  // Login with retrieving optional keys
+  let result = yield client.signIn('you@example.com', 'bigsecret', true);
+  do_check_eq(FAKE_SESSION_TOKEN, result.sessionToken);
+  do_check_eq("keyFetchToken", result.keyFetchToken);
+
+  // Retry due to wrong email capitalization
+  let result = yield client.signIn('You@example.com', 'bigsecret', true);
+  do_check_eq(FAKE_SESSION_TOKEN, result.sessionToken);
+  do_check_eq("keyFetchToken", result.keyFetchToken);
+
+  // Don't retry due to wrong email capitalization
+  try {
+    let result = yield client.signIn('You@example.com', 'bigsecret', true, false);
+    do_throw("Expected to catch an exception");
+  } catch (expectedError) {
+    do_check_eq(120, expectedError.errno);
+    do_check_eq("you@example.com", expectedError.email);
+  }
 
   // Trigger error path
   try {
     result = yield client.signIn("yøü@bad.example.org", "nofear");
     do_throw("Expected to catch an exception");
-  } catch(expectedError) {
+  } catch (expectedError) {
     do_check_eq(102, expectedError.errno);
   }
 
   yield deferredStop(server);
 });
 
 add_task(function test_signOut() {
   let signoutMessage = JSON.stringify({});
--- a/toolkit/content/aboutWebrtc.xhtml
+++ b/toolkit/content/aboutWebrtc.xhtml
@@ -130,17 +130,30 @@ function buildEmptyCandTable(local) {
   candTable.appendChild(buildCandTableHeader(local));
   return candTable;
 }
 
 function round00(num) {
   return Math.round(num * 100) / 100;
 }
 
-function dumpStat(stat, label) {
+function dumpAvStat(stat) {
+  var div = document.createElement('div');
+  var statsString = "";
+  if (stat.mozAvSyncDelay !== undefined) {
+    statsString += "A/V sync: " + stat.mozAvSyncDelay + " ms ";
+  }
+  if (stat.mozJitterBufferDelay !== undefined) {
+    statsString += "Jitter-buffer delay: " + stat.mozJitterBufferDelay + " ms";
+  }
+  div.appendChild(document.createTextNode(statsString));
+  return div;
+}
+
+function dumpRtpStat(stat, label) {
   var div = document.createElement('div');
   var statsString = " " + label + new Date(stat.timestamp).toTimeString() +
                     " " + stat.type + " SSRC: " + stat.ssrc;
   if (stat.packetsReceived !== undefined) {
     statsString += " Received: " + stat.packetsReceived + " packets";
     if (stat.bytesReceived !== undefined) {
       statsString += " (" + round00(stat.bytesReceived/1024) + " Kb)";
     }
@@ -246,23 +259,27 @@ function buildPcDiv(stats, pcDivHeading)
   if (stats.outboundRTPStreamStats) {
     stats.outboundRTPStreamStats.forEach(addRemoteStatToMap);
   }
 
   var addRtpStatPairToDocument = function (rtpStat) {
     if (!rtpStat.isRemote) {
       newPcDiv.appendChild(document.createElement('h5'))
               .appendChild(document.createTextNode(rtpStat.id));
-      newPcDiv.appendChild(dumpStat(rtpStat, "Local: "));
+      if (rtpStat.mozAvSyncDelay !== undefined ||
+          rtpStat.mozJitterBufferDelay !== undefined) {
+        newPcDiv.appendChild(dumpAvStat(rtpStat));
+      }
+      newPcDiv.appendChild(dumpRtpStat(rtpStat, "Local: "));
 
       // Might not be receiving RTCP, so we have no idea what the
       // statistics look like from the perspective of the other end.
       if (rtpStat.remoteId) {
         var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId];
-        newPcDiv.appendChild(dumpStat(remoteRtpStat, "Remote: "));
+        newPcDiv.appendChild(dumpRtpStat(remoteRtpStat, "Remote: "));
       }
     }
   }
 
   if (stats.outboundRTPStreamStats) {
     stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument);
   }
 
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -1,17 +1,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['LIBXUL_SDK']:
     error('toolkit.mozbuild is not compatible with --enable-libxul-sdk=')
 
-if CONFIG['MOZ_CONTENT_SANDBOX'] or CONFIG['OS_ARCH'] == 'Linux':
+if CONFIG['MOZ_CONTENT_SANDBOX']:
     add_tier_dir('sandbox', 'security/sandbox')
 
 # Depends on NSS and NSPR, and must be built after sandbox or else B2G emulator
 # builds fail.
 add_tier_dir('platform', 'security/certverifier')
 
 # Depends on certverifier
 add_tier_dir('platform', 'security/apps')
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1875,18 +1875,18 @@ AndroidBridge::SetNativePanZoomControlle
     return old;
 }
 
 void
 AndroidBridge::RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics)
 {
     ALOG_BRIDGE("AndroidBridge::RequestContentRepaint");
 
-    CSSToScreenScale resolution = aFrameMetrics.mZoom;
-    ScreenRect dp = (aFrameMetrics.mDisplayPort + aFrameMetrics.mScrollOffset) * resolution;
+    CSSToScreenScale resolution = aFrameMetrics.GetZoom();
+    ScreenRect dp = (aFrameMetrics.mDisplayPort + aFrameMetrics.GetScrollOffset()) * resolution;
 
     mNativePanZoomController->RequestContentRepaintWrapper(dp.x, dp.y, dp.width, dp.height, resolution.scale);
 }
 
 void
 AndroidBridge::AcknowledgeScrollUpdate(const mozilla::layers::FrameMetrics::ViewID& aScrollId,
                                        const uint32_t& aScrollGeneration)
 {
--- a/widget/gonk/GonkPermission.cpp
+++ b/widget/gonk/GonkPermission.cpp
@@ -16,98 +16,154 @@
 
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/IServiceManager.h>
 #include <binder/IPermissionController.h>
 #include <private/android_filesystem_config.h>
 #include "GonkPermission.h"
 
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsIAppsService.h"
+#include "mozIApplication.h"
+#include "nsThreadUtils.h"
+
 #undef LOG
 #include <android/log.h>
 #define ALOGE(args...)  __android_log_print(ANDROID_LOG_ERROR, "gonkperm" , ## args)
 
 using namespace android;
 using namespace mozilla;
 
+// Checking permissions needs to happen on the main thread, but the
+// binder callback is called on a special binder thread, so we use
+// this runnable for that.
+class GonkPermissionChecker : public nsRunnable {
+  int32_t mPid;
+  bool mCanUseCamera;
+
+  explicit GonkPermissionChecker(int32_t pid)
+    : mPid(pid)
+    , mCanUseCamera(false)
+  {
+  }
+
+public:
+  static already_AddRefed<GonkPermissionChecker> Inspect(int32_t pid)
+  {
+    nsRefPtr<GonkPermissionChecker> that = new GonkPermissionChecker(pid);
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    MOZ_ASSERT(mainThread);
+    SyncRunnable::DispatchToThread(mainThread, that);
+    return that.forget();
+  }
+
+  bool CanUseCamera()
+  {
+    return mCanUseCamera;
+  }
+
+  NS_IMETHOD Run();
+};
+
+NS_IMETHODIMP
+GonkPermissionChecker::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Find our ContentParent.
+  dom::ContentParent *contentParent = nullptr;
+  {
+    nsTArray<dom::ContentParent*> parents;
+    dom::ContentParent::GetAll(parents);
+    for (uint32_t i = 0; i < parents.Length(); ++i) {
+      if (parents[i]->Pid() == mPid) {
+	contentParent = parents[i];
+	break;
+      }
+    }
+  }
+  if (!contentParent) {
+    ALOGE("pid=%d denied: can't find ContentParent", mPid);
+    return NS_OK;
+  }
+
+  // Now iterate its apps...
+  for (uint32_t i = 0; i < contentParent->ManagedPBrowserParent().Length(); i++) {
+    dom::TabParent *tabParent =
+      static_cast<dom::TabParent*>(contentParent->ManagedPBrowserParent()[i]);
+    nsCOMPtr<mozIApplication> mozApp = tabParent->GetOwnOrContainingApp();
+    if (!mozApp) {
+      continue;
+    }
+
+    // ...and check if any of them has camera access.
+    bool appCanUseCamera;
+    nsresult rv = mozApp->HasPermission("camera", &appCanUseCamera);
+    if (NS_SUCCEEDED(rv) && appCanUseCamera) {
+      mCanUseCamera = true;
+      return NS_OK;
+    }
+  }
+  return NS_OK;
+}
+
 bool
 GonkPermissionService::checkPermission(const String16& permission, int32_t pid,
                                      int32_t uid)
 {
-  if (0 == uid)
+  // root can do anything.
+  if (0 == uid) {
     return true;
+  }
 
   String8 perm8(permission);
 
-
   // Some ril implementations need android.permission.MODIFY_AUDIO_SETTINGS
   if (uid == AID_RADIO &&
-      perm8 == "android.permission.MODIFY_AUDIO_SETTINGS")
+      perm8 == "android.permission.MODIFY_AUDIO_SETTINGS") {
     return true;
+  }
 
-  // Camera/audio record permissions are only for apps with the
-  // "camera" permission.  These apps are also the only apps granted
-  // the AID_SDCARD_RW supplemental group (bug 785592)
-
+  // No other permissions apply to non-app processes.
   if (uid < AID_APP) {
     ALOGE("%s for pid=%d,uid=%d denied: not an app",
       String8(permission).string(), pid, uid);
     return false;
   }
 
+  // Only these permissions can be granted to apps through this service.
   if (perm8 != "android.permission.CAMERA" &&
     perm8 != "android.permission.RECORD_AUDIO") {
     ALOGE("%s for pid=%d,uid=%d denied: unsupported permission",
       String8(permission).string(), pid, uid);
     return false;
   }
 
   // Users granted the permission through a prompt dialog.
   // Before permission managment of gUM is done, app cannot remember the
   // permission.
   PermissionGrant permGrant(perm8.string(), pid);
   if (nsTArray<PermissionGrant>::NoIndex != mGrantArray.IndexOf(permGrant)) {
     mGrantArray.RemoveElement(permGrant);
     return true;
   }
 
-  char filename[32];
-  snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
-  FILE *f = fopen(filename, "r");
-  if (!f) {
-    ALOGE("%s for pid=%d,uid=%d denied: unable to open %s",
-      String8(permission).string(), pid, uid, filename);
-    return false;
+  // Camera/audio record permissions are allowed for apps with the
+  // "camera" permission.
+  nsRefPtr<GonkPermissionChecker> checker =
+    GonkPermissionChecker::Inspect(pid);
+  bool canUseCamera = checker->CanUseCamera();
+  if (!canUseCamera) {
+    ALOGE("%s for pid=%d,uid=%d denied: not granted by user or app manifest",
+      String8(permission).string(), pid, uid);
   }
-
-  char line[80];
-  while (fgets(line, sizeof(line), f)) {
-    char *save;
-    char *name = strtok_r(line, "\t", &save);
-    if (!name)
-      continue;
-
-    if (strcmp(name, "Groups:"))
-      continue;
-    char *group;
-    while ((group = strtok_r(NULL, " \n", &save))) {
-      #define _STR(x) #x
-      #define STR(x) _STR(x)
-      if (!strcmp(group, STR(AID_SDCARD_RW))) {
-        fclose(f);
-        return true;
-      }
-    }
-    break;
-  }
-  fclose(f);
-
-  ALOGE("%s for pid=%d,uid=%d denied: missing group",
-    String8(permission).string(), pid, uid);
-  return false;
+  return canUseCamera;
 }
 
 static GonkPermissionService* gGonkPermissionService = NULL;
 
 /* static */
 void
 GonkPermissionService::instantiate()
 {
--- a/widget/xpwidgets/APZCCallbackHelper.cpp
+++ b/widget/xpwidgets/APZCCallbackHelper.cpp
@@ -63,26 +63,26 @@ static CSSRect ExpandDisplayPortToTileBo
 
 static void
 MaybeAlignAndClampDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics,
                               const CSSPoint& aActualScrollOffset)
 {
   // Correct the display-port by the difference between the requested scroll
   // offset and the resulting scroll offset after setting the requested value.
   CSSRect& displayPort = aFrameMetrics.mDisplayPort;
-  displayPort += aFrameMetrics.mScrollOffset - aActualScrollOffset;
+  displayPort += aFrameMetrics.GetScrollOffset() - aActualScrollOffset;
 
   // Expand the display port to the next tile boundaries, if tiled thebes layers
   // are enabled.
   if (gfxPrefs::LayersTilesEnabled()) {
     // We don't use LayersPixelsPerCSSPixel() here as mCumulativeResolution on
     // this FrameMetrics may be incorrect (and is about to be reset by mZoom).
     displayPort =
       ExpandDisplayPortToTileBoundaries(displayPort + aActualScrollOffset,
-                                        aFrameMetrics.mZoom *
+                                        aFrameMetrics.GetZoom() *
                                         ScreenToLayerScale(1.0))
       - aActualScrollOffset;
   }
 
   // Finally, clamp the display port to the expanded scrollable rect.
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
   displayPort = scrollableRect.Intersect(displayPort + aActualScrollOffset)
     - aActualScrollOffset;
@@ -157,45 +157,45 @@ APZCCallbackHelper::UpdateRootFrame(nsID
     // scroll range would be 900. Therefore this calculation depends on the zoom applied
     // to the content relative to the container.
     CSSSize scrollPort = CSSSize(aMetrics.CalculateCompositedRectInCssPixels().Size());
     aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
 
     // Scroll the window to the desired spot
     nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId);
     bool scrollUpdated = false;
-    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.mScrollOffset, scrollUpdated);
+    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated);
 
     if (!scrollUpdated) {
       // For whatever reason we couldn't update the scroll offset on the scroll frame,
       // which means the data APZ used for its displayport calculation is stale. Fall
       // back to a sane default behaviour. Note that we don't tile-align the recentered
       // displayport because tile-alignment depends on the scroll position, and the
       // scroll position here is out of our control. See bug 966507 comment 21 for a
       // more detailed explanation.
       RecenterDisplayPort(aMetrics);
     }
 
     // Correct the display port due to the difference between mScrollOffset and the
     // actual scroll offset, possibly align it to tile boundaries (if tiled layers are
     // enabled), and clamp it to the scrollable rect.
     MaybeAlignAndClampDisplayPort(aMetrics, actualScrollOffset);
 
-    aMetrics.mScrollOffset = actualScrollOffset;
+    aMetrics.SetScrollOffset(actualScrollOffset);
 
     // The mZoom variable on the frame metrics stores the CSS-to-screen scale for this
     // frame. This scale includes all of the (cumulative) resolutions set on the presShells
     // from the root down to this frame. However, when setting the resolution, we only
     // want the piece of the resolution that corresponds to this presShell, rather than
     // all of the cumulative stuff, so we need to divide out the parent resolutions.
     // Finally, we multiply by a ScreenToLayerScale of 1.0f because the goal here is to
     // take the async zoom calculated by the APZC and tell gecko about it (turning it into
     // a "sync" zoom) which will update the resolution at which the layer is painted.
     ParentLayerToLayerScale presShellResolution =
-        aMetrics.mZoom
+        aMetrics.GetZoom()
         / aMetrics.mDevPixelsPerCSSPixel
         / aMetrics.GetParentResolution()
         * ScreenToLayerScale(1.0f);
     aUtils->SetResolution(presShellResolution.scale, presShellResolution.scale);
 
     // Finally, we set the displayport.
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.mScrollId);
     if (!content) {
@@ -227,32 +227,32 @@ APZCCallbackHelper::UpdateSubFrame(nsICo
         return;
     }
 
     // We currently do not support zooming arbitrary subframes. They can only
     // be scrolled, so here we only have to set the scroll position and displayport.
 
     nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId);
     bool scrollUpdated = false;
-    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.mScrollOffset, scrollUpdated);
+    CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.GetScrollOffset(), scrollUpdated);
 
     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
     if (element) {
         if (!scrollUpdated) {
             RecenterDisplayPort(aMetrics);
         }
         MaybeAlignAndClampDisplayPort(aMetrics, actualScrollOffset);
         utils->SetDisplayPortForElement(aMetrics.mDisplayPort.x,
                                         aMetrics.mDisplayPort.y,
                                         aMetrics.mDisplayPort.width,
                                         aMetrics.mDisplayPort.height,
                                         element, 0);
     }
 
-    aMetrics.mScrollOffset = actualScrollOffset;
+    aMetrics.SetScrollOffset(actualScrollOffset);
 }
 
 already_AddRefed<nsIDOMWindowUtils>
 APZCCallbackHelper::GetDOMWindowUtils(const nsIDocument* aDoc)
 {
     nsCOMPtr<nsIDOMWindowUtils> utils;
     nsCOMPtr<nsIDOMWindow> window = aDoc->GetDefaultView();
     if (window) {
@@ -333,17 +333,17 @@ DestroyCSSPoint(void* aObject, nsIAtom* 
 
 void
 APZCCallbackHelper::UpdateCallbackTransform(const FrameMetrics& aApzcMetrics, const FrameMetrics& aActualMetrics)
 {
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aApzcMetrics.mScrollId);
     if (!content) {
         return;
     }
-    CSSPoint scrollDelta = aApzcMetrics.mScrollOffset - aActualMetrics.mScrollOffset;
+    CSSPoint scrollDelta = aApzcMetrics.GetScrollOffset() - aActualMetrics.GetScrollOffset();
     content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(scrollDelta), DestroyCSSPoint);
 }
 
 CSSPoint
 APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput, const ScrollableLayerGuid& aGuid)
 {
     // XXX: technically we need to walk all the way up the layer tree from the layer
     // represented by |aGuid.mScrollId| up to the root of the layer tree and apply