Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 10 Jun 2015 15:25:21 +0200
changeset 248140 1bd6254c5f41d21145696dc6c7496a45d73fab6a
parent 248139 846253a39b05f5449f8750d92dcbdb7a480388d4 (current diff)
parent 247936 e101c589c242cc1b414442acfb72d79d55b9ceac (diff)
child 248141 4776477b6357eaab96ad3c63556d86b97e674fbe
push id60888
push userkwierso@gmail.com
push dateThu, 11 Jun 2015 01:38:38 +0000
treeherdermozilla-inbound@39e638ed06bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone41.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
browser/app/profile/firefox.js
browser/base/content/tabbrowser.xml
dom/canvas/WebGLBindableName.h
mobile/android/chrome/content/browser.js
security/manager/ssl/tests/unit/test_cert_trust/ca.der
security/manager/ssl/tests/unit/test_cert_trust/ee.der
security/manager/ssl/tests/unit/test_cert_trust/generate.py
security/manager/ssl/tests/unit/test_cert_trust/int.der
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -450,17 +450,18 @@ DocAccessible::Shutdown()
   // array as they are shutdown.
   int32_t childDocCount = mChildDocuments.Length();
   for (int32_t idx = childDocCount - 1; idx >= 0; idx--)
     mChildDocuments[idx]->Shutdown();
 
   mChildDocuments.Clear();
 
   // XXX thinking about ordering?
-  if (IPCAccessibilityActive()) {
+  if (mIPCDoc) {
+    MOZ_ASSERT(IPCAccessibilityActive());
     mIPCDoc->Shutdown();
     MOZ_ASSERT(!mIPCDoc);
   }
 
   if (mVirtualCursor) {
     mVirtualCursor->RemoveObserver(this);
     mVirtualCursor = nullptr;
   }
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -28,16 +28,17 @@ builtin(include, build/autoconf/arch.m4)
 builtin(include, build/autoconf/android.m4)dnl
 builtin(include, build/autoconf/zlib.m4)dnl
 builtin(include, build/autoconf/linux.m4)dnl
 builtin(include, build/autoconf/python-virtualenv.m4)dnl
 builtin(include, build/autoconf/winsdk.m4)dnl
 builtin(include, build/autoconf/icu.m4)dnl
 builtin(include, build/autoconf/ffi.m4)dnl
 builtin(include, build/autoconf/clang-plugin.m4)dnl
+builtin(include, build/autoconf/alloc.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- 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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
   <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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "9844b7c9d4429a041cff7201f1a63d0264e155ff", 
+        "git_revision": "e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "8edfc1021ec1ceb0a7f7fb0b23e96b44a0fe8254", 
+    "revision": "c33bf7766d1b19c407c3bbab943403ddee172915", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
   <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/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="9844b7c9d4429a041cff7201f1a63d0264e155ff"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e3eaf72ccd1bfe6d60d37efde6d3b92c1dbc5ff9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="70b7fcbf7ff0ef38d04f82d68a56f2bb44ec694a"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -970,16 +970,17 @@ pref("gecko.handlerService.schemes.ircs.
 // By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
 pref("gecko.handlerService.allowRegisterFromDifferentHost", false);
 
 #ifdef MOZ_SAFE_BROWSING
 pref("browser.safebrowsing.enabled", true);
 pref("browser.safebrowsing.malware.enabled", true);
 pref("browser.safebrowsing.downloads.enabled", true);
 pref("browser.safebrowsing.downloads.remote.enabled", true);
+pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000);
 pref("browser.safebrowsing.debug", false);
 
 pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
 pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.safebrowsing.reportURL", "https://safebrowsing.google.com/safebrowsing/report?");
 pref("browser.safebrowsing.reportGenericURL", "http://%LOCALE%.phish-generic.mozilla.com/?hl=%LOCALE%");
 pref("browser.safebrowsing.reportErrorURL", "http://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%");
 pref("browser.safebrowsing.reportPhishURL", "http://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%");
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -687,17 +687,17 @@
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
 
                 if (this.mTab.hasAttribute("busy")) {
                   this.mTab.removeAttribute("busy");
-                  this.mTabBrowser._tabAttrModified(this.mTab);
+                  this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
                   if (!this.mTab.selected)
                     this.mTab.setAttribute("unread", "true");
                 }
                 this.mTab.removeAttribute("progress");
 
                 if (aWebProgress.isTopLevel) {
                   if (!Components.isSuccessCode(aStatus) &&
                       !isTabEmpty(this.mTab)) {
@@ -887,17 +887,17 @@
             if (sizedIconUrl) {
               sizedIconUrl = this.PlacesUtils.getImageURLForResolution(window, sizedIconUrl);
             }
             if (sizedIconUrl != aTab.getAttribute("image")) {
               if (sizedIconUrl)
                 aTab.setAttribute("image", sizedIconUrl);
               else
                 aTab.removeAttribute("image");
-              this._tabAttrModified(aTab);
+              this._tabAttrModified(aTab, ["image"]);
             }
 
             this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
           ]]>
         </body>
       </method>
 
       <method name="getIcon">
@@ -1162,18 +1162,18 @@
                 bubbles: true,
                 cancelable: false,
                 detail: {
                   previousTab: oldTab
                 }
               });
               this.mCurrentTab.dispatchEvent(event);
 
-              this._tabAttrModified(oldTab);
-              this._tabAttrModified(this.mCurrentTab);
+              this._tabAttrModified(oldTab, ["selected"]);
+              this._tabAttrModified(this.mCurrentTab, ["selected"]);
 
               if (oldBrowser != newBrowser &&
                   oldBrowser.docShell &&
                   oldBrowser.docShell.contentViewer.inPermitUnload) {
                 // Since the user is switching away from a tab that has
                 // a beforeunload prompt active, we remove the prompt.
                 // This prevents confusing user flows like the following:
                 //   1. User attempts to close Firefox
@@ -1295,35 +1295,39 @@
         }
 
         fm.setFocus(newBrowser, focusFlags);
         ]]></body>
       </method>
 
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
+        <parameter name="aChanged"/>
         <body><![CDATA[
           if (aTab.closing)
             return;
 
-          // This event should be dispatched when any of these attributes change:
-          // label, crop, busy, image, selected
-          var event = document.createEvent("Events");
-          event.initEvent("TabAttrModified", true, false);
+          let event = new CustomEvent("TabAttrModified", {
+            bubbles: true,
+            cancelable: false,
+            detail: {
+              changed: aChanged,
+            }
+          });
           aTab.dispatchEvent(event);
         ]]></body>
       </method>
 
       <method name="setTabTitleLoading">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             aTab.label = this.mStringBundle.getString("tabs.connecting");
             aTab.crop = "end";
-            this._tabAttrModified(aTab);
+            this._tabAttrModified(aTab, ["label", "crop"]);
           ]]>
         </body>
       </method>
 
       <method name="setTabTitle">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
@@ -1358,17 +1362,17 @@
             }
 
             if (aTab.label == title &&
                 aTab.crop == crop)
               return false;
 
             aTab.label = title;
             aTab.crop = crop;
-            this._tabAttrModified(aTab);
+            this._tabAttrModified(aTab, ["label", "crop"]);
 
             if (aTab.selected)
               this.updateTitlebar();
 
             return true;
           ]]>
         </body>
       </method>
@@ -2456,17 +2460,17 @@
             } else {
               // Workarounds for bug 458697
               // Icon might have been set on DOMLinkAdded, don't override that.
               if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
                 this.setIcon(aOurTab, otherBrowser.mIconURL);
               var isBusy = aOtherTab.hasAttribute("busy");
               if (isBusy) {
                 aOurTab.setAttribute("busy", "true");
-                this._tabAttrModified(aOurTab);
+                this._tabAttrModified(aOurTab, ["busy"]);
                 if (aOurTab.selected)
                   this.mIsBusy = true;
               }
 
               this._swapBrowserDocShells(aOurTab, otherBrowser);
             }
 
             // Handle findbar data (if any)
new file mode 100644
--- /dev/null
+++ b/build/autoconf/alloc.m4
@@ -0,0 +1,53 @@
+dnl This Source Code Form is subject to the terms of the Mozilla Public
+dnl License, v. 2.0. If a copy of the MPL was not distributed with this
+dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+dnl Check for the existence of various allocation headers/functions
+AC_DEFUN([MOZ_CHECK_ALLOCATOR],[
+
+MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h"
+MALLOC_H=
+
+for file in $MALLOC_HEADERS; do
+  MOZ_CHECK_HEADER($file, [MALLOC_H=$file])
+  if test "$MALLOC_H" != ""; then
+    AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
+    break
+  fi
+done
+
+MOZ_CHECK_HEADERS(alloca.h)
+
+AC_CHECK_FUNCS(strndup posix_memalign memalign)
+
+AC_CHECK_FUNCS(malloc_usable_size)
+MALLOC_USABLE_SIZE_CONST_PTR=const
+MOZ_CHECK_HEADERS([malloc.h], [
+  AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument])
+  AC_TRY_COMPILE([#include <malloc.h>
+                  #include <stddef.h>
+                  size_t malloc_usable_size(const void *ptr);],
+                  [return malloc_usable_size(0);],
+                  AC_MSG_RESULT([yes]),
+                  AC_MSG_RESULT([no])
+                  MALLOC_USABLE_SIZE_CONST_PTR=)
+])
+AC_DEFINE_UNQUOTED([MALLOC_USABLE_SIZE_CONST_PTR],[$MALLOC_USABLE_SIZE_CONST_PTR])
+
+
+dnl In newer bionic headers, valloc is built but not defined,
+dnl so we check more carefully here.
+AC_MSG_CHECKING([for valloc in malloc.h])
+AC_EGREP_HEADER(valloc, malloc.h,
+                AC_DEFINE(HAVE_VALLOC)
+                AC_MSG_RESULT([yes]),
+                AC_MSG_RESULT([no]))
+
+AC_MSG_CHECKING([for valloc in unistd.h])
+AC_EGREP_HEADER(valloc, unistd.h,
+                AC_DEFINE(HAVE_VALLOC)
+                AC_MSG_RESULT([yes]),
+                AC_MSG_RESULT([no]))
+
+
+])
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -42,16 +42,17 @@ SEARCH_PATHS = [
     'build/pymake',
     'config',
     'dom/bindings',
     'dom/bindings/parser',
     'layout/tools/reftest',
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
+    'testing/tools/autotry',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
     'testing/marionette/client',
     'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py',
     'testing/marionette/transport',
     'testing/marionette/driver',
--- a/configure.in
+++ b/configure.in
@@ -3394,62 +3394,16 @@ if test -n "$MOZ_LINKER" -a "$OS_TARGET"
     dnl we need to use LDFLAGS because nspr doesn't inherit DSO_LDOPTS.
     dnl Using LDFLAGS in nspr is safe, since we only really build
     dnl libraries there.
     DSO_LDOPTS="$DSO_LDOPTS -nostartfiles"
     NSPR_LDFLAGS="$NSPR_LDFLAGS -nostartfiles"
   fi
 fi
 
-dnl Check for the existence of various allocation headers/functions
-
-MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h"
-MALLOC_H=
-
-for file in $MALLOC_HEADERS; do
-  MOZ_CHECK_HEADER($file, [MALLOC_H=$file])
-  if test "$MALLOC_H" != ""; then
-    AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
-    break
-  fi
-done
-
-MOZ_CHECK_HEADERS(alloca.h)
-
-AC_CHECK_FUNCS(strndup posix_memalign memalign)
-
-AC_CHECK_FUNCS(malloc_usable_size)
-MALLOC_USABLE_SIZE_CONST_PTR=const
-MOZ_CHECK_HEADERS([malloc.h], [
-  AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument])
-  AC_TRY_COMPILE([#include <malloc.h>
-                  #include <stddef.h>
-                  size_t malloc_usable_size(const void *ptr);],
-                  [return malloc_usable_size(0);],
-                  AC_MSG_RESULT([yes]),
-                  AC_MSG_RESULT([no])
-                  MALLOC_USABLE_SIZE_CONST_PTR=)
-])
-AC_DEFINE_UNQUOTED([MALLOC_USABLE_SIZE_CONST_PTR],[$MALLOC_USABLE_SIZE_CONST_PTR])
-
-
-dnl In newer bionic headers, valloc is built but not defined,
-dnl so we check more carefully here.
-AC_MSG_CHECKING([for valloc in malloc.h])
-AC_EGREP_HEADER(valloc, malloc.h,
-                AC_DEFINE(HAVE_VALLOC)
-                AC_MSG_RESULT([yes]),
-                AC_MSG_RESULT([no]))
-
-AC_MSG_CHECKING([for valloc in unistd.h])
-AC_EGREP_HEADER(valloc, unistd.h,
-                AC_DEFINE(HAVE_VALLOC)
-                AC_MSG_RESULT([yes]),
-                AC_MSG_RESULT([no]))
-
 dnl See if compiler supports some gcc-style attributes
 
 AC_CACHE_CHECK(for __attribute__((always_inline)),
                ac_cv_attribute_always_inline,
                [AC_TRY_COMPILE([inline void f(void) __attribute__((always_inline));],
                                [],
                                ac_cv_attribute_always_inline=yes,
                                ac_cv_attribute_always_inline=no)])
@@ -3474,16 +3428,18 @@ AC_CACHE_CHECK(for LC_MESSAGES,
                                ac_cv_i18n_lc_messages=no)])
 if test "$ac_cv_i18n_lc_messages" = yes; then
    AC_DEFINE(HAVE_I18N_LC_MESSAGES)
 fi
 
 AC_HAVE_FUNCS(localeconv)
 fi # ! SKIP_COMPILER_CHECKS
 
+MOZ_CHECK_ALLOCATOR
+
 TARGET_XPCOM_ABI=
 if test -n "${CPU_ARCH}" -a -n "${TARGET_COMPILER_ABI}"; then
     TARGET_XPCOM_ABI="${CPU_ARCH}-${TARGET_COMPILER_ABI}"
     AC_DEFINE_UNQUOTED(TARGET_XPCOM_ABI, ["${TARGET_XPCOM_ABI}"])
 fi
 
 dnl We can't run TRY_COMPILE tests on Windows, so hard-code some
 dnl features that Windows actually does support.
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -11191,22 +11191,18 @@ ExitFullscreenInDocTree(nsIDocument* aMa
   // Move the top-level window out of fullscreen mode.
   SetWindowFullScreen(root, false);
 }
 
 /* static */
 void
 nsDocument::ExitFullscreen(nsIDocument* aDoc)
 {
-  // Unlock the pointer, if it's locked.
-  nsCOMPtr<Element> pointerLockedElement =
-    do_QueryReferent(EventStateManager::sPointerLockedElement);
-  if (pointerLockedElement) {
-    UnlockPointer();
-  }
+  // Unlock the pointer
+  UnlockPointer();
 
   if (aDoc)  {
     ExitFullscreenInDocTree(aDoc);
     return;
   }
 
   // Clear fullscreen stacks in all fullscreen roots' descendant documents.
   FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
@@ -11253,39 +11249,33 @@ nsDocument::RestorePreviousFullScreenSta
   NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
     "Should have at least 1 fullscreen root when fullscreen!");
 
   if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
     return;
   }
 
   // If fullscreen mode is updated the pointer should be unlocked
-  nsCOMPtr<Element> pointerLockedElement =
-    do_QueryReferent(EventStateManager::sPointerLockedElement);
-  if (pointerLockedElement) {
-    UnlockPointer();
-  }
+  UnlockPointer();
 
   nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
 
   // Clear full-screen stacks in all descendant in process documents, bottom up.
   nsIDocument* doc = fullScreenDoc;
   while (doc != this) {
     NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
     static_cast<nsDocument*>(doc)->CleanupFullscreenState();
-    UnlockPointer();
     DispatchFullScreenChange(doc);
     doc = doc->GetParentDocument();
   }
 
   // Roll-back full-screen state to previous full-screen element.
   NS_ASSERTION(doc == this, "Must have reached this doc.");
   while (doc != nullptr) {
     static_cast<nsDocument*>(doc)->FullScreenStackPop();
-    UnlockPointer();
     DispatchFullScreenChange(doc);
     if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
       // Full-screen stack in document is empty. Go back up to the parent
       // document. We'll pop the containing element off its stack, and use
       // its next full-screen element as the full-screen element.
       static_cast<nsDocument*>(doc)->CleanupFullscreenState();
       doc = doc->GetParentDocument();
     } else {
@@ -11661,29 +11651,20 @@ nsDocument::RequestFullScreen(Element* a
   // order, but we traverse the doctree in a leaf-to-root order, so we save
   // references to the documents we must dispatch to so that we get the order
   // as specified.
   nsAutoTArray<nsIDocument*, 8> changed;
 
   // Remember the root document, so that if a full-screen document is hidden
   // we can reset full-screen state in the remaining visible full-screen documents.
   nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
-  if (fullScreenRootDoc->IsFullScreenDoc()) {
-    // A document is already in fullscreen, unlock the mouse pointer
-    // before setting a new document to fullscreen
-    UnlockPointer();
-  }
 
   // If a document is already in fullscreen, then unlock the mouse pointer
   // before setting a new document to fullscreen
-  nsCOMPtr<Element> pointerLockedElement =
-    do_QueryReferent(EventStateManager::sPointerLockedElement);
-  if (pointerLockedElement) {
-    UnlockPointer();
-  }
+  UnlockPointer();
 
   // Process options -- in this case, just HMD
   if (aOptions.mVRHMDDevice) {
     nsRefPtr<gfx::VRHMDInfo> hmdRef = aOptions.mVRHMDDevice;
     aElement->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
                           ReleaseHMDInfoRef,
                           true);
   }
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -1,27 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #include "BluetoothSocket.h"
-
 #include <fcntl.h>
 #include <sys/socket.h>
-
-#include "base/message_loop.h"
 #include "BluetoothSocketObserver.h"
 #include "BluetoothInterface.h"
 #include "BluetoothUtils.h"
 #include "mozilla/ipc/UnixSocketWatcher.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/RefPtr.h"
-#include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
 static const size_t MAX_READ_SIZE = 1 << 16;
 static BluetoothSocketInterface* sBluetoothSocketInterface;
 
@@ -69,21 +65,21 @@ public:
    */
   enum ConnectionStatus {
     SOCKET_IS_DISCONNECTED = 0,
     SOCKET_IS_LISTENING,
     SOCKET_IS_CONNECTING,
     SOCKET_IS_CONNECTED
   };
 
-  DroidSocketImpl(nsIThread* aConsumerThread,
+  DroidSocketImpl(MessageLoop* aConsumerLoop,
                   MessageLoop* aIOLoop,
                   BluetoothSocket* aConsumer)
     : ipc::UnixFdWatcher(aIOLoop)
-    , DataSocketIO(aConsumerThread)
+    , DataSocketIO(aConsumerLoop)
     , mConsumer(aConsumer)
     , mShuttingDownOnIOThread(false)
     , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   ~DroidSocketImpl()
   {
     MOZ_ASSERT(IsConsumerThread());
@@ -168,17 +164,17 @@ public:
     MOZ_ASSERT(!IsConsumerThread());
     MOZ_ASSERT(!mShuttingDownOnIOThread);
 
     Close(); // will also remove fd from I/O loop
     mShuttingDownOnIOThread = true;
   }
 
 private:
-  class ReceiveRunnable;
+  class ReceiveTask;
 
   /**
    * libevent triggered functions that reads data from socket when available and
    * guarenteed non-blocking. Only to be called on IO thread.
    *
    * @param aFd [in] File descriptor to read from
    */
   virtual void OnFileCanReadWithoutBlocking(int aFd);
@@ -314,19 +310,18 @@ DroidSocketImpl::Accept(int aFd)
   if (!(flags & O_NONBLOCK)) {
     int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
   SetFd(aFd);
   mConnectionStatus = SOCKET_IS_CONNECTED;
 
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
 void
@@ -421,35 +416,33 @@ public:
       mImpl->mConsumer->NotifyDisconnect();
     }
   }
 
 private:
   DroidSocketImpl* mImpl;
 };
 
-class AcceptRunnable final : public SocketIORunnable<DroidSocketImpl>
+class InvokeAcceptTask final : public SocketTask<DroidSocketImpl>
 {
 public:
-  AcceptRunnable(DroidSocketImpl* aImpl, int aFd)
-  : SocketIORunnable<DroidSocketImpl>(aImpl)
-  , mFd(aFd)
+  InvokeAcceptTask(DroidSocketImpl* aImpl, int aFd)
+    : SocketTask<DroidSocketImpl>(aImpl)
+    , mFd(aFd)
   { }
 
-  NS_IMETHOD Run() override
+  void Run() override
   {
     MOZ_ASSERT(GetIO()->IsConsumerThread());
     MOZ_ASSERT(sBluetoothSocketInterface);
 
     BluetoothSocketResultHandler* res = new AcceptResultHandler(GetIO());
     GetIO()->mConsumer->SetCurrentResultHandler(res);
 
     sBluetoothSocketInterface->Accept(mFd, res);
-
-    return NS_OK;
   }
 
 private:
   int mFd;
 };
 
 void
 DroidSocketImpl::OnSocketCanAcceptWithoutBlocking(int aFd)
@@ -457,18 +450,17 @@ DroidSocketImpl::OnSocketCanAcceptWithou
   MOZ_ASSERT(!IsConsumerThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
   /* When a listening socket is ready for receiving data,
    * we can call |Accept| on it.
    */
 
   RemoveWatchers(READ_WATCHER);
-  GetConsumerThread()->Dispatch(new AcceptRunnable(this, aFd),
-                                NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(FROM_HERE, new InvokeAcceptTask(this, aFd));
 }
 
 void
 DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
 {
   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
     OnSocketCanSendWithoutBlocking(aFd);
   } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
@@ -502,19 +494,18 @@ DroidSocketImpl::OnSocketCanConnectWitho
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
   /* We follow Posix behaviour here: Connect operations are
    * complete once we can write to the connecting socket.
    */
 
   mConnectionStatus = SOCKET_IS_CONNECTED;
 
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
 // |DataSocketIO|
@@ -532,54 +523,51 @@ DroidSocketImpl::QueryReceiveBuffer(
 
   return NS_OK;
 }
 
 /**
  * |ReceiveRunnable| transfers data received on the I/O thread
  * to an instance of |BluetoothSocket| on the consumer thread.
  */
-class DroidSocketImpl::ReceiveRunnable final
-  : public SocketIORunnable<DroidSocketImpl>
+class DroidSocketImpl::ReceiveTask final : public SocketTask<DroidSocketImpl>
 {
 public:
-  ReceiveRunnable(DroidSocketImpl* aIO, UnixSocketBuffer* aBuffer)
-    : SocketIORunnable<DroidSocketImpl>(aIO)
+  ReceiveTask(DroidSocketImpl* aIO, UnixSocketBuffer* aBuffer)
+    : SocketTask<DroidSocketImpl>(aIO)
     , mBuffer(aBuffer)
   { }
 
-  NS_IMETHOD Run() override
+  void Run() override
   {
-    DroidSocketImpl* io = SocketIORunnable<DroidSocketImpl>::GetIO();
+    DroidSocketImpl* io = SocketTask<DroidSocketImpl>::GetIO();
 
     MOZ_ASSERT(io->IsConsumerThread());
 
     if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
       // Since we've already explicitly closed and the close
       // happened before this, this isn't really an error.
-      return NS_OK;
+      return;
     }
 
     BluetoothSocket* bluetoothSocket = io->GetBluetoothSocket();
     MOZ_ASSERT(bluetoothSocket);
 
     bluetoothSocket->ReceiveSocketData(mBuffer);
-
-    return NS_OK;
   }
 
 private:
   nsAutoPtr<UnixSocketBuffer> mBuffer;
 };
 
 void
 DroidSocketImpl::ConsumeBuffer()
 {
-  GetConsumerThread()->Dispatch(new ReceiveRunnable(this, mBuffer.forget()),
-                                NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(FROM_HERE,
+                                new ReceiveTask(this, mBuffer.forget()));
 }
 
 void
 DroidSocketImpl::DiscardBuffer()
 {
   // Nothing to do.
 }
 
@@ -646,24 +634,24 @@ private:
 };
 
 nsresult
 BluetoothSocket::Connect(const nsAString& aDeviceAddress,
                          const BluetoothUuid& aServiceUuid,
                          BluetoothSocketType aType,
                          int aChannel,
                          bool aAuth, bool aEncrypt,
-                         nsIThread* aConsumerThread,
+                         MessageLoop* aConsumerLoop,
                          MessageLoop* aIOLoop)
 {
   MOZ_ASSERT(!mImpl);
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
-  mImpl = new DroidSocketImpl(aConsumerThread, aIOLoop, this);
+  mImpl = new DroidSocketImpl(aConsumerLoop, aIOLoop, this);
 
   BluetoothSocketResultHandler* res = new ConnectSocketResultHandler(mImpl);
   SetCurrentResultHandler(res);
 
   sBluetoothSocketInterface->Connect(
     aDeviceAddress, aType,
     aServiceUuid.mUuid, aChannel,
     aEncrypt, aAuth, res);
@@ -673,24 +661,18 @@ BluetoothSocket::Connect(const nsAString
 
 nsresult
 BluetoothSocket::Connect(const nsAString& aDeviceAddress,
                          const BluetoothUuid& aServiceUuid,
                          BluetoothSocketType aType,
                          int aChannel,
                          bool aAuth, bool aEncrypt)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
   return Connect(aDeviceAddress, aServiceUuid, aType, aChannel, aAuth,
-                 aEncrypt, consumerThread, XRE_GetIOMessageLoop());
+                 aEncrypt, MessageLoop::current(), XRE_GetIOMessageLoop());
 }
 
 class ListenResultHandler final : public BluetoothSocketResultHandler
 {
 public:
   ListenResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
@@ -716,24 +698,24 @@ private:
 };
 
 nsresult
 BluetoothSocket::Listen(const nsAString& aServiceName,
                         const BluetoothUuid& aServiceUuid,
                         BluetoothSocketType aType,
                         int aChannel,
                         bool aAuth, bool aEncrypt,
-                        nsIThread* aConsumerThread,
+                        MessageLoop* aConsumerLoop,
                         MessageLoop* aIOLoop)
 {
   MOZ_ASSERT(!mImpl);
 
   SetConnectionStatus(SOCKET_LISTENING);
 
-  mImpl = new DroidSocketImpl(aConsumerThread, aIOLoop, this);
+  mImpl = new DroidSocketImpl(aConsumerLoop, aIOLoop, this);
 
   BluetoothSocketResultHandler* res = new ListenResultHandler(mImpl);
   SetCurrentResultHandler(res);
 
   sBluetoothSocketInterface->Listen(
     aType,
     aServiceName, aServiceUuid.mUuid, aChannel,
     aEncrypt, aAuth, res);
@@ -743,24 +725,18 @@ BluetoothSocket::Listen(const nsAString&
 
 nsresult
 BluetoothSocket::Listen(const nsAString& aServiceName,
                         const BluetoothUuid& aServiceUuid,
                         BluetoothSocketType aType,
                         int aChannel,
                         bool aAuth, bool aEncrypt)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
   return Listen(aServiceName, aServiceUuid, aType, aChannel, aAuth, aEncrypt,
-                consumerThread, XRE_GetIOMessageLoop());
+                MessageLoop::current(), XRE_GetIOMessageLoop());
 }
 
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
 {
   MOZ_ASSERT(mObserver);
 
   mObserver->ReceiveSocketData(this, aBuffer);
--- a/dom/bluetooth/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_bluetooth_BluetoothSocket_h
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
 #include "mozilla/ipc/DataSocket.h"
 
 class MessageLoop;
-class nsIThread;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
 class BluetoothSocketResultHandler;
 class DroidSocketImpl;
 
 class BluetoothSocket final : public mozilla::ipc::DataSocket
@@ -24,31 +23,31 @@ class BluetoothSocket final : public moz
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver);
 
   nsresult Connect(const nsAString& aDeviceAddress,
                    const BluetoothUuid& aServiceUuid,
                    BluetoothSocketType aType,
                    int aChannel,
                    bool aAuth, bool aEncrypt,
-                   nsIThread* aConsumerThread,
+                   MessageLoop* aConsumerLoop,
                    MessageLoop* aIOLoop);
 
   nsresult Connect(const nsAString& aDeviceAddress,
                    const BluetoothUuid& aServiceUuid,
                    BluetoothSocketType aType,
                    int aChannel,
                    bool aAuth, bool aEncrypt);
 
   nsresult Listen(const nsAString& aServiceName,
                   const BluetoothUuid& aServiceUuid,
                   BluetoothSocketType aType,
                   int aChannel,
                   bool aAuth, bool aEncrypt,
-                  nsIThread* aConsumerThread,
+                  MessageLoop* aConsumerLoop,
                   MessageLoop* aIOLoop);
 
   nsresult Listen(const nsAString& aServiceName,
                   const BluetoothUuid& aServiceUuid,
                   BluetoothSocketType aType,
                   int aChannel,
                   bool aAuth, bool aEncrypt);
 
--- a/dom/bluetooth/bluez/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluez/BluetoothSocket.cpp
@@ -3,19 +3,17 @@
 /* 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/. */
 
 #include "BluetoothSocket.h"
 #include <fcntl.h>
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUnixSocketConnector.h"
-#include "mozilla/unused.h"
-#include "nsTArray.h"
-#include "nsThreadUtils.h"
+#include "mozilla/RefPtr.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 static const size_t MAX_READ_SIZE = 1 << 16;
 
@@ -23,17 +21,17 @@ static const size_t MAX_READ_SIZE = 1 <<
 // BluetoothSocketIO
 //
 
 class BluetoothSocket::BluetoothSocketIO final
   : public UnixSocketWatcher
   , public DataSocketIO
 {
 public:
-  BluetoothSocketIO(nsIThread* aConsumerThread,
+  BluetoothSocketIO(MessageLoop* aConsumerLoop,
                     MessageLoop* aIOLoop,
                     BluetoothSocket* aConsumer,
                     UnixSocketConnector* aConnector);
   ~BluetoothSocketIO();
 
   void GetSocketAddr(nsAString& aAddrStr) const;
 
   BluetoothSocket* GetBluetoothSocket();
@@ -85,17 +83,17 @@ public:
 
   bool IsShutdownOnConsumerThread() const override;
   bool IsShutdownOnIOThread() const override;
 
   void ShutdownOnConsumerThread() override;
   void ShutdownOnIOThread() override;
 
 private:
-  class ReceiveRunnable;
+  class ReceiveTask;
 
   void FireSocketError();
 
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
    * directly from consumer thread. All non-consumer-thread accesses should
    * happen with mIO as container.
    */
@@ -129,22 +127,22 @@ private:
 
   /**
    * I/O buffer for received data
    */
   nsAutoPtr<UnixSocketRawData> mBuffer;
 };
 
 BluetoothSocket::BluetoothSocketIO::BluetoothSocketIO(
-  nsIThread* aConsumerThread,
+  MessageLoop* aConsumerLoop,
   MessageLoop* aIOLoop,
   BluetoothSocket* aConsumer,
   UnixSocketConnector* aConnector)
   : UnixSocketWatcher(aIOLoop)
-  , DataSocketIO(aConsumerThread)
+  , DataSocketIO(aConsumerLoop)
   , mConsumer(aConsumer)
   , mConnector(aConnector)
   , mShuttingDownOnIOThread(false)
   , mAddressLength(0)
   , mDelayedConnectTask(nullptr)
 {
   MOZ_ASSERT(mConsumer);
   MOZ_ASSERT(mConnector);
@@ -276,19 +274,18 @@ BluetoothSocket::BluetoothSocketIO::Send
 }
 
 void
 BluetoothSocket::BluetoothSocketIO::OnConnected()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
   MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
 
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
 void
@@ -326,19 +323,18 @@ BluetoothSocket::BluetoothSocketIO::OnSo
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FireSocketError();
     return;
   }
 
   Close();
   SetSocket(fd, SOCKET_IS_CONNECTED);
 
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
 void
@@ -377,20 +373,18 @@ void
 BluetoothSocket::BluetoothSocketIO::FireSocketError()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
 
   // Clean up watchers, statuses, fds
   Close();
 
   // Tell the consumer thread we've errored
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_ERROR),
-    NS_DISPATCH_NORMAL);
-
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_ERROR));
 }
 
 // |DataSocketIO|
 
 nsresult
 BluetoothSocket::BluetoothSocketIO::QueryReceiveBuffer(
   UnixSocketIOBuffer** aBuffer)
 {
@@ -400,57 +394,55 @@ BluetoothSocket::BluetoothSocketIO::Quer
     mBuffer = new UnixSocketRawData(MAX_READ_SIZE);
   }
   *aBuffer = mBuffer.get();
 
   return NS_OK;
 }
 
 /**
- * |ReceiveRunnable| transfers data received on the I/O thread
+ * |ReceiveTask| transfers data received on the I/O thread
  * to an instance of |BluetoothSocket| on the consumer thread.
  */
-class BluetoothSocket::BluetoothSocketIO::ReceiveRunnable final
-  : public SocketIORunnable<BluetoothSocketIO>
+class BluetoothSocket::BluetoothSocketIO::ReceiveTask final
+  : public SocketTask<BluetoothSocketIO>
 {
 public:
-  ReceiveRunnable(BluetoothSocketIO* aIO, UnixSocketBuffer* aBuffer)
-    : SocketIORunnable<BluetoothSocketIO>(aIO)
+  ReceiveTask(BluetoothSocketIO* aIO, UnixSocketBuffer* aBuffer)
+    : SocketTask<BluetoothSocketIO>(aIO)
     , mBuffer(aBuffer)
   { }
 
-  NS_IMETHOD Run() override
+  void Run() override
   {
-    BluetoothSocketIO* io = SocketIORunnable<BluetoothSocketIO>::GetIO();
+    BluetoothSocketIO* io = SocketTask<BluetoothSocketIO>::GetIO();
 
     MOZ_ASSERT(io->IsConsumerThread());
 
     if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
       // Since we've already explicitly closed and the close
       // happened before this, this isn't really an error.
-      return NS_OK;
+      return;
     }
 
     BluetoothSocket* bluetoothSocket = io->GetBluetoothSocket();
     MOZ_ASSERT(bluetoothSocket);
 
     bluetoothSocket->ReceiveSocketData(mBuffer);
-
-    return NS_OK;
   }
 
 private:
   nsAutoPtr<UnixSocketBuffer> mBuffer;
 };
 
 void
 BluetoothSocket::BluetoothSocketIO::ConsumeBuffer()
 {
-  GetConsumerThread()->Dispatch(new ReceiveRunnable(this, mBuffer.forget()),
-                                NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(FROM_HERE,
+                                new ReceiveTask(this, mBuffer.forget()));
 }
 
 void
 BluetoothSocket::BluetoothSocketIO::DiscardBuffer()
 {
   // Nothing to do.
 }
 
@@ -645,24 +637,24 @@ BluetoothSocket::SendSocketData(const ns
   SendSocketData(new UnixSocketRawData(aStr.BeginReading(), aStr.Length()));
 
   return true;
 }
 
 nsresult
 BluetoothSocket::Connect(BluetoothUnixSocketConnector* aConnector,
                          int aDelayMs,
-                         nsIThread* aConsumerThread, MessageLoop* aIOLoop)
+                         MessageLoop* aConsumerLoop, MessageLoop* aIOLoop)
 {
   MOZ_ASSERT(aConnector);
-  MOZ_ASSERT(aConsumerThread);
+  MOZ_ASSERT(aConsumerLoop);
   MOZ_ASSERT(aIOLoop);
   MOZ_ASSERT(!mIO);
 
-  mIO = new BluetoothSocketIO(aConsumerThread, aIOLoop, this, aConnector);
+  mIO = new BluetoothSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
   SetConnectionStatus(SOCKET_CONNECTING);
 
   if (aDelayMs > 0) {
     DelayedConnectTask* connectTask = new DelayedConnectTask(mIO);
     mIO->SetDelayedConnectTask(connectTask);
     MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs);
   } else {
     aIOLoop->PostTask(FROM_HERE, new ConnectTask(mIO));
@@ -670,52 +662,41 @@ BluetoothSocket::Connect(BluetoothUnixSo
 
   return NS_OK;
 }
 
 nsresult
 BluetoothSocket::Connect(BluetoothUnixSocketConnector* aConnector,
                          int aDelayMs)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return Connect(aConnector, aDelayMs, consumerThread, XRE_GetIOMessageLoop());
+  return Connect(aConnector, aDelayMs, MessageLoop::current(),
+                 XRE_GetIOMessageLoop());
 }
 
 nsresult
 BluetoothSocket::Listen(BluetoothUnixSocketConnector* aConnector,
-                        nsIThread* aConsumerThread, MessageLoop* aIOLoop)
+                        MessageLoop* aConsumerLoop, MessageLoop* aIOLoop)
 {
   MOZ_ASSERT(aConnector);
-  MOZ_ASSERT(aConsumerThread);
+  MOZ_ASSERT(aConsumerLoop);
   MOZ_ASSERT(aIOLoop);
   MOZ_ASSERT(!mIO);
 
-  mIO = new BluetoothSocketIO(aConsumerThread, aIOLoop, this, aConnector);
+  mIO = new BluetoothSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
   SetConnectionStatus(SOCKET_LISTENING);
 
   aIOLoop->PostTask(FROM_HERE, new ListenTask(mIO));
 
   return NS_OK;
 }
 
 nsresult
 BluetoothSocket::Listen(BluetoothUnixSocketConnector* aConnector)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return Listen(aConnector, consumerThread, XRE_GetIOMessageLoop());
+  return Listen(aConnector, MessageLoop::current(), XRE_GetIOMessageLoop());
 }
 
 void
 BluetoothSocket::GetAddress(nsAString& aAddrStr)
 {
   aAddrStr.Truncate();
   if (!mIO || GetConnectionStatus() != SOCKET_CONNECTED) {
     NS_WARNING("No socket currently open!");
--- a/dom/bluetooth/bluez/BluetoothSocket.h
+++ b/dom/bluetooth/bluez/BluetoothSocket.h
@@ -3,23 +3,20 @@
 /* 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_dom_bluetooth_BluetoothSocket_h
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
-#include <stdlib.h>
 #include "mozilla/ipc/DataSocket.h"
 #include "mozilla/ipc/UnixSocketWatcher.h"
-#include "mozilla/RefPtr.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
-#include "nsThreadUtils.h"
 
 class MessageLoop;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
 class BluetoothUnixSocketConnector;
 
@@ -61,22 +58,22 @@ public:
   bool SendSocketData(const nsACString& aMessage);
 
   /**
    * Starts a task on the socket that will try to connect to a socket in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
    * @param aDelayMs Time delay in milli-seconds.
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O thread.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   nsresult Connect(BluetoothUnixSocketConnector* aConnector, int aDelayMs,
-                   nsIThread* aConsumerThread, MessageLoop* aIOLoop);
+                   MessageLoop* aConsumerLoop, MessageLoop* aIOLoop);
 
   /**
    * Starts a task on the socket that will try to connect to a socket in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
    * @param aDelayMs Time delay in milli-seconds.
    * @return NS_OK on success, or an XPCOM error code otherwise.
@@ -84,22 +81,22 @@ public:
   nsresult Connect(BluetoothUnixSocketConnector* aConnector,
                    int aDelayMs = 0);
 
   /**
    * Starts a task on the socket that will try to accept a new connection in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O thread.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   nsresult Listen(BluetoothUnixSocketConnector* aConnector,
-                  nsIThread* aConsumerThread, MessageLoop* aIOLoop);
+                  MessageLoop* aConsumerLoop, MessageLoop* aIOLoop);
 
   /**
    * Starts a task on the socket that will try to accept a new connection in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -28,17 +28,16 @@ public:
 
     // nsWrapperCache
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
-    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_1_CONTEXT_H_
--- a/dom/canvas/WebGL1ContextBuffers.cpp
+++ b/dom/canvas/WebGL1ContextBuffers.cpp
@@ -30,32 +30,16 @@ WebGL1Context::ValidateBufferTarget(GLen
 
 bool
 WebGL1Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
 {
     ErrorInvalidEnumInfo(info, target);
     return false;
 }
 
-/** Buffer and Target validation for BindBuffer */
-bool
-WebGL1Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
-                                       const char* info)
-{
-    if (!buffer)
-        return true;
-
-    if (buffer->HasEverBeenBound() && target != buffer->Target()) {
-        ErrorInvalidOperation("%s: buffer already bound to a different target", info);
-        return false;
-    }
-
-    return true;
-}
-
 bool
 WebGL1Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
 {
     switch (usage) {
     case LOCAL_GL_STREAM_DRAW:
     case LOCAL_GL_STATIC_DRAW:
     case LOCAL_GL_DYNAMIC_DRAW:
         return true;
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -360,20 +360,21 @@ private:
 
     void UpdateBoundQuery(GLenum target, WebGLQuery* query);
 
     bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
     bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
                                 GLsizei width, GLsizei height, GLsizei depth,
                                 const char* info);
 
+    // CreateVertexArrayImpl is assumed to be infallible.
+    virtual WebGLVertexArray* CreateVertexArrayImpl() override;
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
-    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) override;
     virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -41,46 +41,16 @@ WebGL2Context::ValidateBufferIndexedTarg
 
     default:
         ErrorInvalidEnumInfo(info, target);
         return false;
     }
 }
 
 bool
-WebGL2Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
-                                       const char* info)
-{
-    if (!buffer)
-        return true;
-
-    switch (target) {
-    case LOCAL_GL_COPY_READ_BUFFER:
-    case LOCAL_GL_COPY_WRITE_BUFFER:
-        return true;
-
-    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
-        return !buffer->HasEverBeenBound() ||
-            buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER;
-
-    case LOCAL_GL_ARRAY_BUFFER:
-    case LOCAL_GL_PIXEL_PACK_BUFFER:
-    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
-    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
-    case LOCAL_GL_UNIFORM_BUFFER:
-        return !buffer->HasEverBeenBound() ||
-            buffer->Target() != LOCAL_GL_ELEMENT_ARRAY_BUFFER;
-    }
-
-    ErrorInvalidOperation("%s: buffer already bound to a incompatible target %s",
-                          info, EnumName(buffer->Target().get()));
-    return false;
-}
-
-bool
 WebGL2Context::ValidateBufferUsageEnum(GLenum usage, const char* info)
 {
     switch (usage) {
     case LOCAL_GL_DYNAMIC_COPY:
     case LOCAL_GL_DYNAMIC_DRAW:
     case LOCAL_GL_DYNAMIC_READ:
     case LOCAL_GL_STATIC_COPY:
     case LOCAL_GL_STATIC_DRAW:
@@ -118,17 +88,17 @@ WebGL2Context::CopyBufferSubData(GLenum 
     const WebGLRefPtr<WebGLBuffer>& writeBufferSlot = GetBufferSlotByTarget(writeTarget);
     if (!readBufferSlot || !writeBufferSlot)
         return;
 
     const WebGLBuffer* readBuffer = readBufferSlot.get();
     if (!readBuffer)
         return ErrorInvalidOperation("copyBufferSubData: No buffer bound to readTarget");
 
-    const WebGLBuffer* writeBuffer = writeBufferSlot.get();
+    WebGLBuffer* writeBuffer = writeBufferSlot.get();
     if (!writeBuffer)
         return ErrorInvalidOperation("copyBufferSubData: No buffer bound to writeTarget");
 
     if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(),
         "copyBufferSubData"))
     {
         return;
     }
@@ -140,51 +110,70 @@ WebGL2Context::CopyBufferSubData(GLenum 
     }
 
     if (readTarget == writeTarget &&
         !ValidateDataRanges(readOffset, writeOffset, size, "copyBufferSubData"))
     {
         return;
     }
 
+    WebGLBuffer::Kind readType = readBuffer->Content();
+    WebGLBuffer::Kind writeType = writeBuffer->Content();
+
+    if (readType != WebGLBuffer::Kind::Undefined &&
+        writeType != WebGLBuffer::Kind::Undefined &&
+        writeType != readType)
+    {
+        ErrorInvalidOperation("copyBufferSubData: Can't copy %s data to %s data",
+                              (readType == WebGLBuffer::Kind::OtherData) ? "other" : "element",
+                              (writeType == WebGLBuffer::Kind::OtherData) ? "other" : "element");
+        return;
+    }
+
     WebGLContextUnchecked::CopyBufferSubData(readTarget, writeTarget, readOffset,
                                              writeOffset, size);
+
+    if (writeType == WebGLBuffer::Kind::Undefined) {
+        writeBuffer->BindTo(
+            (readType == WebGLBuffer::Kind::OtherData) ? LOCAL_GL_ARRAY_BUFFER
+                                                       : LOCAL_GL_ELEMENT_ARRAY_BUFFER);
+    }
 }
 
 void
 WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset,
                                 const dom::Nullable<dom::ArrayBuffer>& maybeData)
 {
     if (IsContextLost())
         return;
-    
+
     // For the WebGLBuffer bound to the passed target, read
     // returnedData.byteLength bytes from the buffer starting at byte
     // offset offset and write them to returnedData.
 
     // If zero is bound to target, an INVALID_OPERATION error is
     // generated.
     if (!ValidateBufferTarget(target, "getBufferSubData"))
         return;
 
     // If offset is less than zero, an INVALID_VALUE error is
     // generated.
     if (offset < 0)
-        return ErrorInvalidValue("getBufferSubData: negative offset"); 
+        return ErrorInvalidValue("getBufferSubData: negative offset");
 
     // If returnedData is null then an INVALID_VALUE error is
     // generated.
     if (maybeData.IsNull())
         return ErrorInvalidValue("getBufferSubData: returnedData is null");
 
     WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
     WebGLBuffer* boundBuffer = bufferSlot.get();
     if (!boundBuffer)
         return ErrorInvalidOperation("getBufferSubData: no buffer bound");
-    
+
     // If offset + returnedData.byteLength would extend beyond the end
     // of the buffer an INVALID_VALUE error is generated.
     const dom::ArrayBuffer& data = maybeData.Value();
     data.ComputeLengthAndData();
 
     CheckedInt<WebGLsizeiptr> neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + data.Length();
     if (!neededByteLength.isValid()) {
         ErrorInvalidValue("getBufferSubData: Integer overflow computing the needed"
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -49,17 +49,18 @@ WebGL2Context::IsSampler(WebGLSampler* s
         return false;
 
     if (!ValidateObjectAllowDeleted("isSampler", sampler))
         return false;
 
     if (sampler->IsDeleted())
         return false;
 
-    return !sampler->HasEverBeenBound();
+    MakeContextCurrent();
+    return gl->fIsSampler(sampler->mGLName);
 }
 
 void
 WebGL2Context::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
     if (IsContextLost())
         return;
 
--- a/dom/canvas/WebGL2ContextTransformFeedback.cpp
+++ b/dom/canvas/WebGL2ContextTransformFeedback.cpp
@@ -55,17 +55,17 @@ WebGL2Context::IsTransformFeedback(WebGL
 
     if (!ValidateObjectAllowDeleted("isTransformFeedback", tf))
         return false;
 
     if (tf->IsDeleted())
         return false;
 
     MakeContextCurrent();
-    return gl->fIsTransformFeedback(tf->GLName());
+    return gl->fIsTransformFeedback(tf->mGLName);
 }
 
 void
 WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf)
 {
     if (IsContextLost())
         return;
 
@@ -79,21 +79,18 @@ WebGL2Context::BindTransformFeedback(GLe
     if (currentTF && currentTF->mIsActive && !currentTF->mIsPaused) {
         return ErrorInvalidOperation("bindTransformFeedback: Currently bound transform "
                                      "feedback is active and not paused");
     }
 
     if (tf && tf->IsDeleted())
         return ErrorInvalidOperation("bindTransformFeedback: Attempt to bind deleted id");
 
-    if (tf)
-        tf->BindTo(LOCAL_GL_TRANSFORM_FEEDBACK);
-
     MakeContextCurrent();
-    gl->fBindTransformFeedback(target, tf ? tf->GLName() : 0);
+    gl->fBindTransformFeedback(target, tf ? tf->mGLName : 0);
     if (tf)
         mBoundTransformFeedback = tf;
     else
         mBoundTransformFeedback = mDefaultTransformFeedback;
 }
 
 void
 WebGL2Context::BeginTransformFeedback(GLenum primitiveMode)
--- a/dom/canvas/WebGL2ContextVAOs.cpp
+++ b/dom/canvas/WebGL2ContextVAOs.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/. */
 
 #include "WebGL2Context.h"
 #include "GLContext.h"
+#include "WebGLVertexArrayObject.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 // -------------------------------------------------------------------------
 // Vertex Array Object
-// TODO(djg): Implemented in WebGLContext
-/*
-    already_AddRefed<WebGLVertexArrayObject> CreateVertexArray();
-    void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
-    bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
-    void BindVertexArray(WebGLVertexArrayObject* vertexArray);
-*/
+
+WebGLVertexArray*
+WebGL2Context::CreateVertexArrayImpl()
+{
+    return dom::WebGLVertexArrayObject::Create(this);
+}
+
+} // namespace mozilla
deleted file mode 100644
--- a/dom/canvas/WebGLBindableName.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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 WEBGLBINDABLENAME_H_
-#define WEBGLBINDABLENAME_H_
-
-#include "WebGLTypes.h"
-
-#include "GLDefs.h"
-#include "mozilla/TypeTraits.h"
-#include "mozilla/Assertions.h"
-
-namespace mozilla {
-
-/** Represents a binding to a GL binding point
- */
-template<typename T>
-class WebGLBindable
-{
-public:
-    WebGLBindable() : mTarget(LOCAL_GL_NONE) { }
-    bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
-
-    void BindTo(T target) {
-        MOZ_ASSERT(target != LOCAL_GL_NONE, "Can't bind to GL_NONE.");
-        MOZ_ASSERT(!HasEverBeenBound() || mTarget == target, "Rebinding is illegal.");
-
-        bool targetChanged = (target != mTarget);
-        mTarget = target;
-        if (targetChanged)
-            OnTargetChanged();
-    }
-
-    T Target() const {
-        MOZ_ASSERT(HasEverBeenBound());
-        return mTarget;
-    }
-
-protected:
-    //! Called after mTarget has been changed by BindTo(target).
-    virtual void OnTargetChanged() {}
-
-    T mTarget;
-};
-
-
-/** Represents a GL name that can be bound to a target.
- */
-template<typename T>
-class WebGLBindableName
-    : public WebGLBindable<T>
-{
-public:
-
-    explicit WebGLBindableName(GLuint aName)
-        : WebGLBindable<T>()
-        , mGLName(aName)
-    { }
-    GLuint GLName() const { return mGLName; }
-
-protected:
-    const GLuint mGLName;
-};
-
-
-} // namespace mozilla
-
-#endif // !WEBGLBINDABLENAME_H_
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -8,60 +8,82 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "WebGLContext.h"
 #include "WebGLElementArrayCache.h"
 
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
-    : WebGLBindableName<BufferBinding>(buf)
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
+    , mGLName(buf)
+    , mContent(Kind::Undefined)
     , mByteLength(0)
 {
     mContext->mBuffers.insertBack(this);
 }
 
 WebGLBuffer::~WebGLBuffer()
 {
     DeleteOnce();
 }
 
 void
+WebGLBuffer::BindTo(GLenum target)
+{
+    switch (target) {
+    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
+        mContent = Kind::ElementArray;
+        if (!mCache)
+            mCache = new WebGLElementArrayCache;
+        break;
+
+    case LOCAL_GL_ARRAY_BUFFER:
+    case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+    case LOCAL_GL_UNIFORM_BUFFER:
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+        mContent = Kind::OtherData;
+        break;
+
+    case LOCAL_GL_COPY_READ_BUFFER:
+    case LOCAL_GL_COPY_WRITE_BUFFER:
+        /* Do nothing. Doesn't set the type of the buffer contents. */
+        break;
+
+    default:
+        MOZ_CRASH();
+    }
+}
+
+void
 WebGLBuffer::Delete()
 {
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteBuffers(1, &mGLName);
     mByteLength = 0;
     mCache = nullptr;
     LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
 }
 
-void
-WebGLBuffer::OnTargetChanged()
-{
-    if (!mCache && mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
-        mCache = new WebGLElementArrayCache;
-}
-
 bool
 WebGLBuffer::ElementArrayCacheBufferData(const void* ptr,
                                          size_t bufferSizeInBytes)
 {
-    if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
+    if (mContent == Kind::ElementArray)
         return mCache->BufferData(ptr, bufferSizeInBytes);
 
     return true;
 }
 
 void
 WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr,
                                             size_t updateSizeInBytes)
 {
-    if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
+    if (mContent == Kind::ElementArray)
         mCache->BufferSubData(pos, ptr, updateSizeInBytes);
 }
 
 size_t
 WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
     size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(mallocSizeOf)
                                 : 0;
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -5,35 +5,43 @@
 
 #ifndef WEBGL_BUFFER_H_
 #define WEBGL_BUFFER_H_
 
 #include "GLDefs.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLElementArrayCache;
 
 class WebGLBuffer final
     : public nsWrapperCache
-    , public WebGLBindableName<BufferBinding>
     , public WebGLRefCountedObject<WebGLBuffer>
     , public LinkedListElement<WebGLBuffer>
     , public WebGLContextBoundObject
 {
 public:
+
+    enum class Kind {
+        Undefined,
+        ElementArray,
+        OtherData
+    };
+
     explicit WebGLBuffer(WebGLContext* webgl, GLuint buf);
 
+    void BindTo(GLenum target);
+    Kind Content() const { return mContent; }
+
     void Delete();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     WebGLsizeiptr ByteLength() const { return mByteLength; }
     void SetByteLength(WebGLsizeiptr byteLength) { mByteLength = byteLength; }
 
     bool ElementArrayCacheBufferData(const void* ptr, size_t bufferSizeInBytes);
@@ -47,23 +55,24 @@ public:
     bool IsElementArrayUsedWithMultipleTypes() const;
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
+    const GLenum mGLName;
+
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLBuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLBuffer)
 
 protected:
     ~WebGLBuffer();
 
-    virtual void OnTargetChanged() override;
-
+    Kind mContent;
     WebGLsizeiptr mByteLength;
     nsAutoPtr<WebGLElementArrayCache> mCache;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1875,17 +1875,17 @@ WebGLContext::TexImageFromVideoElement(c
                         srcImage->GetSize().width, srcImage->GetSize().height,
                         0, format, type, nullptr);
     }
 
     const gl::OriginPos destOrigin = mPixelStoreFlipY ? gl::OriginPos::BottomLeft
                                                       : gl::OriginPos::TopLeft;
     bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage.get(),
                                                    srcImage->GetSize(),
-                                                   tex->GLName(),
+                                                   tex->mGLName,
                                                    texImageTarget.get(),
                                                    destOrigin);
     if (ok) {
         TexInternalFormat effectiveInternalFormat =
             EffectiveInternalFormatFromInternalFormatAndType(internalFormat,
                                                              type);
         MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
         tex->SetImageInfo(texImageTarget, level, srcImage->GetSize().width,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -934,16 +934,18 @@ protected:
 
     nsTArray<WebGLRefPtr<WebGLBuffer>> mBoundUniformBuffers;
     nsTArray<WebGLRefPtr<WebGLBuffer>> mBoundTransformFeedbackBuffers;
 
     WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTarget(GLenum target);
     WebGLRefPtr<WebGLBuffer>& GetBufferSlotByTargetIndexed(GLenum target,
                                                            GLuint index);
 
+    GLenum GetCurrentBinding(WebGLBuffer* buffer) const;
+
 // -----------------------------------------------------------------------------
 // Queries (WebGL2ContextQueries.cpp)
 protected:
     WebGLRefPtr<WebGLQuery>& GetQuerySlotByTarget(GLenum target);
 
     WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
     WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
 
@@ -1385,20 +1387,22 @@ private:
     // Like ValidateObject, but only for cases when `object` is known to not be
     // null already.
     template<class ObjectType>
     bool ValidateObjectAssumeNonNull(const char* info, ObjectType* object);
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
+    virtual WebGLVertexArray* CreateVertexArrayImpl();
+
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) = 0;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
-    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) = 0;
+    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info);
     virtual bool ValidateBufferUsageEnum(GLenum usage, const char* info) = 0;
     virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
     virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
 
 protected:
     int32_t MaxTextureSizeForTarget(TexTarget target) const {
         return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize
                                                : mGLMaxCubeMapTextureSize;
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -15,34 +15,17 @@ void
 WebGLContext::UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer)
 {
     WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target);
     bufferSlot = buffer;
 
     if (!buffer)
         return;
 
-    /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
-     *
-     * In the WebGL 2 API, buffers have their WebGL buffer type
-     * initially set to undefined. Calling bindBuffer, bindBufferRange
-     * or bindBufferBase with the target argument set to any buffer
-     * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
-     * then set the WebGL buffer type of the buffer being bound
-     * according to the table above.
-     *
-     * Any call to one of these functions which attempts to bind a
-     * WebGLBuffer that has the element array WebGL buffer type to a
-     * binding point that falls under other data, or bind a
-     * WebGLBuffer which has the other data WebGL buffer type to
-     * ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
-     * and the state of the binding point will remain untouched.
-     */
-    if (target != LOCAL_GL_COPY_READ_BUFFER && target != LOCAL_GL_COPY_WRITE_BUFFER)
-        buffer->BindTo(target);
+    buffer->BindTo(target);
 }
 
 void
 WebGLContext::UpdateBoundBufferIndexed(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
     UpdateBoundBuffer(target, buffer);
 
     WebGLRefPtr<WebGLBuffer>& bufferIndexSlot =
@@ -405,22 +388,60 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
         return;
 
     if (!ValidateObjectAllowDeletedOrNull("deleteBuffer", buffer))
         return;
 
     if (!buffer || buffer->IsDeleted())
         return;
 
-    if (mBoundArrayBuffer == buffer)
-        BindBuffer(LOCAL_GL_ARRAY_BUFFER, static_cast<WebGLBuffer*>(nullptr));
+    // TODO: Extract this into a helper function?
+    if (mBoundArrayBuffer == buffer) {
+        WebGLContextUnchecked::BindBuffer(LOCAL_GL_ARRAY_BUFFER, nullptr);
+        mBoundArrayBuffer = nullptr;
+    }
 
     if (mBoundVertexArray->mElementArrayBuffer == buffer) {
-        BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER,
-                   static_cast<WebGLBuffer*>(nullptr));
+        WebGLContextUnchecked::BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, nullptr);
+        mBoundVertexArray->mElementArrayBuffer = nullptr;
+    }
+
+    // WebGL binding points
+    if (IsWebGL2()) {
+        if (mBoundCopyReadBuffer == buffer)
+            mBoundCopyReadBuffer = nullptr;
+
+        if (mBoundCopyWriteBuffer == buffer)
+            mBoundCopyWriteBuffer = nullptr;
+
+        if (mBoundPixelPackBuffer == buffer)
+            mBoundPixelPackBuffer = nullptr;
+
+        if (mBoundPixelUnpackBuffer == buffer)
+            mBoundPixelUnpackBuffer = nullptr;
+
+        if (mBoundTransformFeedbackBuffer == buffer)
+            mBoundTransformFeedbackBuffer = nullptr;
+
+        if (mBoundUniformBuffer == buffer)
+            mBoundUniformBuffer = nullptr;
+
+        const size_t xfBufferCount = mBoundTransformFeedbackBuffers.Length();
+        for (size_t n = 0; n < xfBufferCount; n++) {
+            if (mBoundTransformFeedbackBuffers[n] == buffer) {
+                mBoundTransformFeedbackBuffers[n] = nullptr;
+            }
+        }
+
+        const size_t uniformBufferCount = mBoundUniformBuffers.Length();
+        for (size_t n = 0; n < uniformBufferCount; n++) {
+            if (mBoundUniformBuffers[n] == buffer) {
+                mBoundUniformBuffers[n] = nullptr;
+            }
+        }
     }
 
     for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) {
         if (mBoundVertexArray->HasAttrib(i) &&
             mBoundVertexArray->mAttribs[i].buf == buffer)
         {
             mBoundVertexArray->mAttribs[i].buf = nullptr;
         }
@@ -430,19 +451,99 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
 }
 
 bool
 WebGLContext::IsBuffer(WebGLBuffer* buffer)
 {
     if (IsContextLost())
         return false;
 
-    return ValidateObjectAllowDeleted("isBuffer", buffer) &&
-           !buffer->IsDeleted() &&
-           buffer->HasEverBeenBound();
+    if (!ValidateObjectAllowDeleted("isBuffer", buffer))
+        return false;
+
+    if (buffer->IsDeleted())
+        return false;
+
+    MakeContextCurrent();
+    return gl->fIsBuffer(buffer->mGLName);
+}
+
+bool
+WebGLContext::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer,
+                                      const char* info)
+{
+    if (!buffer)
+        return true;
+
+    /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
+     *
+     * In the WebGL 2 API, buffers have their WebGL buffer type
+     * initially set to undefined. Calling bindBuffer, bindBufferRange
+     * or bindBufferBase with the target argument set to any buffer
+     * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
+     * then set the WebGL buffer type of the buffer being bound
+     * according to the table above.
+     *
+     * Any call to one of these functions which attempts to bind a
+     * WebGLBuffer that has the element array WebGL buffer type to a
+     * binding point that falls under other data, or bind a
+     * WebGLBuffer which has the other data WebGL buffer type to
+     * ELEMENT_ARRAY_BUFFER will generate an INVALID_OPERATION error,
+     * and the state of the binding point will remain untouched.
+     */
+
+    GLenum boundTo = GetCurrentBinding(buffer);
+    if (boundTo != LOCAL_GL_NONE) {
+        if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
+            boundTo != LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER)
+        {
+            ErrorInvalidOperation("Can't bind buffer to TRANSFORM_FEEDBACK_BUFFER as the "
+                                  "buffer is already bound to another bind point.");
+            return false;
+        }
+        else if (target != LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
+                 boundTo == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER)
+        {
+            ErrorInvalidOperation("Can't bind buffer to bind point as it is currently "
+                                  "bound to TRANSFORM_FEEDBACK_BUFFER.");
+            return false;
+        }
+    }
+
+    WebGLBuffer::Kind content = buffer->Content();
+    if (content == WebGLBuffer::Kind::Undefined)
+        return true;
+
+    switch (target) {
+    case LOCAL_GL_COPY_READ_BUFFER:
+    case LOCAL_GL_COPY_WRITE_BUFFER:
+        return true;
+
+    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
+        if (content == WebGLBuffer::Kind::ElementArray)
+            return true;
+        break;
+
+    case LOCAL_GL_ARRAY_BUFFER:
+    case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+    case LOCAL_GL_UNIFORM_BUFFER:
+        if (content == WebGLBuffer::Kind::OtherData)
+            return true;
+        break;
+
+    default:
+        MOZ_CRASH();
+    }
+
+    ErrorInvalidOperation("%s: buffer already contains %s data.", info,
+                          content == WebGLBuffer::Kind::OtherData ? "other" : "element");
+
+    return false;
 }
 
 bool
 WebGLContext::ValidateBufferUsageEnum(GLenum target, const char* info)
 {
     switch (target) {
     case LOCAL_GL_STREAM_DRAW:
     case LOCAL_GL_STATIC_DRAW:
@@ -506,16 +607,47 @@ WebGLContext::GetBufferSlotByTargetIndex
         return mBoundUniformBuffers[index];
 
     default:
         MOZ_CRASH("Should not get here.");
     }
 }
 
 GLenum
+WebGLContext::GetCurrentBinding(WebGLBuffer* buffer) const
+{
+    if (mBoundArrayBuffer == buffer)
+        return LOCAL_GL_ARRAY_BUFFER;
+
+    if (mBoundCopyReadBuffer == buffer)
+        return LOCAL_GL_COPY_READ_BUFFER;
+
+    if (mBoundCopyWriteBuffer == buffer)
+        return LOCAL_GL_COPY_WRITE_BUFFER;
+
+    if (mBoundPixelPackBuffer == buffer)
+        return LOCAL_GL_PIXEL_PACK_BUFFER;
+
+    if (mBoundPixelUnpackBuffer == buffer)
+        return LOCAL_GL_PIXEL_UNPACK_BUFFER;
+
+    if (mBoundTransformFeedbackBuffer == buffer ||
+        mBoundTransformFeedbackBuffers.Contains(buffer)) {
+        return LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER;
+    }
+
+    if (mBoundUniformBuffer == buffer ||
+        mBoundUniformBuffers.Contains(buffer)) {
+        return LOCAL_GL_UNIFORM_BUFFER;
+    }
+
+    return LOCAL_GL_NONE;
+}
+
+GLenum
 WebGLContext::CheckedBufferData(GLenum target, GLsizeiptr size,
                                 const GLvoid* data, GLenum usage)
 {
 #ifdef XP_MACOSX
     // bug 790879
     if (gl->WorkAroundDriverBugs() &&
         int64_t(size) > INT32_MAX) // cast avoids a potential always-true warning on 32bit
     {
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -595,17 +595,17 @@ WebGLContext::DoFakeVertexAttrib0(GLuint
                 array[4 * i + 3] = mVertexAttrib0Vector[3];
             }
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array.get(), LOCAL_GL_DYNAMIC_DRAW);
         } else {
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
         }
         GLenum error = GetAndFlushUnderlyingGLErrors();
 
-        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
+        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 
         // note that we do this error checking and early return AFTER having restored the buffer binding above
         if (error) {
             ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
                              "with %d vertices. Try reducing the number of vertices.", vertexCount);
             return false;
         }
     }
@@ -621,17 +621,17 @@ WebGLContext::UndoFakeVertexAttrib0()
 {
     WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
 
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return;
 
     if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
         const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
-        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
+        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->mGLName);
         if (attrib0.integer) {
             gl->fVertexAttribIPointer(0,
                                       attrib0.size,
                                       attrib0.type,
                                       attrib0.stride,
                                       reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
         } else {
             gl->fVertexAttribPointer(0,
@@ -640,17 +640,17 @@ WebGLContext::UndoFakeVertexAttrib0()
                                      attrib0.normalized,
                                      attrib0.stride,
                                      reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
         }
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
-    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
+    gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 }
 
 WebGLContextFakeBlackStatus
 WebGLContext::ResolvedFakeBlackStatus()
 {
     // handle this case first, it's the generic case
     if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded))
         return mFakeBlackStatus;
@@ -732,21 +732,21 @@ WebGLContext::UnbindFakeBlackTextures()
 {
     // this is the generic case: try to return early
     if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded))
         return;
 
     for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) {
         if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
             gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-            gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
+            gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->mGLName);
         }
         if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) {
             gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-            gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
+            gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->mGLName);
         }
     }
 
     gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
 }
 
 WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext* gl, TexTarget target, GLenum format)
     : mGL(gl)
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -154,19 +154,21 @@ WebGLContext::BindFramebuffer(GLenum tar
     if (wfb && wfb->IsDeleted())
         return;
 
     MakeContextCurrent();
 
     if (!wfb) {
         gl->fBindFramebuffer(target, 0);
     } else {
-        wfb->BindTo(target);
-        GLuint framebuffername = wfb->GLName();
+        GLuint framebuffername = wfb->mGLName;
         gl->fBindFramebuffer(target, framebuffername);
+#ifdef ANDROID
+        wfb->mIsFB = true;
+#endif
     }
 
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
         mBoundDrawFramebuffer = wfb;
         mBoundReadFramebuffer = wfb;
         break;
     case LOCAL_GL_DRAW_FRAMEBUFFER:
@@ -191,25 +193,25 @@ WebGLContext::BindRenderbuffer(GLenum ta
 
     if (!ValidateObjectAllowDeletedOrNull("bindRenderbuffer", wrb))
         return;
 
     // silently ignore a deleted buffer
     if (wrb && wrb->IsDeleted())
         return;
 
-    if (wrb)
-        wrb->BindTo(target);
-
     MakeContextCurrent();
 
     // Sometimes we emulate renderbuffers (depth-stencil emu), so there's not
     // always a 1-1 mapping from `wrb` to GL name. Just have `wrb` handle it.
     if (wrb) {
         wrb->BindRenderbuffer();
+#ifdef ANDROID
+        wrb->mIsRB = true;
+#endif
     } else {
         gl->fBindRenderbuffer(target, 0);
     }
 
     mBoundRenderbuffer = wrb;
 }
 
 void
@@ -732,17 +734,17 @@ WebGLContext::DeleteTexture(WebGLTexture
 
     GLuint activeTexture = mActiveTexture;
     for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
         if ((mBound2DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_2D) ||
             (mBoundCubeMapTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) ||
             (mBound3DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_3D))
         {
             ActiveTexture(LOCAL_GL_TEXTURE0 + i);
-            BindTexture(tex->Target().get(), nullptr);
+            BindTexture(tex->Target(), nullptr);
         }
     }
     ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture);
 
     tex->RequestDelete();
 }
 
 void
@@ -1686,19 +1688,32 @@ WebGLContext::Hint(GLenum target, GLenum
 }
 
 bool
 WebGLContext::IsFramebuffer(WebGLFramebuffer* fb)
 {
     if (IsContextLost())
         return false;
 
-    return ValidateObjectAllowDeleted("isFramebuffer", fb) &&
-        !fb->IsDeleted() &&
-        fb->HasEverBeenBound();
+    if (!ValidateObjectAllowDeleted("isFramebuffer", fb))
+        return false;
+
+    if (fb->IsDeleted())
+        return false;
+
+#ifdef ANDROID
+    if (gl->WorkAroundDriverBugs() &&
+        gl->Renderer() == GLRenderer::AndroidEmulator)
+    {
+        return fb->mIsFB;
+    }
+#endif
+
+    MakeContextCurrent();
+    return gl->fIsFramebuffer(fb->mGLName);
 }
 
 bool
 WebGLContext::IsProgram(WebGLProgram* prog)
 {
     if (IsContextLost())
         return false;
 
@@ -1706,19 +1721,32 @@ WebGLContext::IsProgram(WebGLProgram* pr
 }
 
 bool
 WebGLContext::IsRenderbuffer(WebGLRenderbuffer* rb)
 {
     if (IsContextLost())
         return false;
 
-    return ValidateObjectAllowDeleted("isRenderBuffer", rb) &&
-        !rb->IsDeleted() &&
-        rb->HasEverBeenBound();
+    if (!ValidateObjectAllowDeleted("isRenderBuffer", rb))
+        return false;
+
+    if (rb->IsDeleted())
+        return false;
+
+#ifdef ANDROID
+    if (gl->WorkAroundDriverBugs() &&
+        gl->Renderer() == GLRenderer::AndroidEmulator)
+    {
+         return rb->mIsRB;
+    }
+#endif
+
+    MakeContextCurrent();
+    return gl->fIsRenderbuffer(rb->PrimaryGLName());
 }
 
 bool
 WebGLContext::IsShader(WebGLShader* shader)
 {
     if (IsContextLost())
         return false;
 
--- a/dom/canvas/WebGLContextReporter.cpp
+++ b/dom/canvas/WebGLContextReporter.cpp
@@ -121,18 +121,17 @@ WebGLMemoryTracker::GetBufferCacheMemory
 {
     const ContextsArrayType& contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLBuffer* buffer = contexts[i]->mBuffers.getFirst();
              buffer;
              buffer = buffer->getNext())
         {
-            if (buffer->HasEverBeenBound() &&
-                buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
+            if (buffer->Content() == WebGLBuffer::Kind::ElementArray) {
                 result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
             }
         }
     }
     return result;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(WebGLShaderMallocSizeOf)
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -189,31 +189,48 @@ WebGLContext::GetParameter(JSContext* cx
                 gl->fGetBooleanv(pname, &disjoint);
             }
             return JS::BooleanValue(bool(disjoint));
         }
     }
 
     if (IsWebGL2()) {
         switch (pname) {
-            case LOCAL_GL_MAX_SAMPLES:
-            case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
-            case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
-                GLint val;
-                gl->fGetIntegerv(pname, &val);
-                return JS::NumberValue(uint32_t(val));
-            }
+        case LOCAL_GL_MAX_SAMPLES:
+        case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
+        case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
+            GLint val;
+            gl->fGetIntegerv(pname, &val);
+            return JS::NumberValue(uint32_t(val));
+        }
+
+        case LOCAL_GL_TEXTURE_BINDING_3D:
+            return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
+
+        // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
+        case LOCAL_GL_READ_FRAMEBUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundReadFramebuffer.get(), rv);
 
-            case LOCAL_GL_TEXTURE_BINDING_3D: {
-                return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv);
-            }
+        case LOCAL_GL_PIXEL_PACK_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundPixelPackBuffer.get(), rv);
+
+        case LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundPixelUnpackBuffer.get(), rv);
+
+        case LOCAL_GL_UNIFORM_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundUniformBuffer.get(), rv);
 
-            // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING.
-            case LOCAL_GL_READ_FRAMEBUFFER_BINDING:
-                return WebGLObjectAsJSValue(cx, mBoundReadFramebuffer.get(), rv);
+        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv);
+
+        case LOCAL_GL_COPY_READ_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundCopyReadBuffer.get(), rv);
+
+        case LOCAL_GL_COPY_WRITE_BUFFER_BINDING:
+            return WebGLObjectAsJSValue(cx, mBoundCopyWriteBuffer.get(), rv);
         }
     }
 
     switch (pname) {
         //
         // String params
         //
         case LOCAL_GL_VENDOR:
@@ -528,23 +545,16 @@ WebGLContext::GetParameter(JSContext* cx
             }
             return arr;
         }
 
         case LOCAL_GL_ARRAY_BUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv);
         }
 
-        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: {
-            if (!IsWebGL2()) {
-                break;
-            }
-            return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv);
-        }
-
         case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundVertexArray->mElementArrayBuffer.get(), rv);
         }
 
         case LOCAL_GL_RENDERBUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv);
         }
 
--- a/dom/canvas/WebGLContextUnchecked.cpp
+++ b/dom/canvas/WebGLContextUnchecked.cpp
@@ -19,31 +19,42 @@ WebGLContextUnchecked::WebGLContextUnche
 
 // -----------------------------------------------------------------------------
 // Buffer Objects
 
 void
 WebGLContextUnchecked::BindBuffer(GLenum target, WebGLBuffer* buffer)
 {
     gl->MakeCurrent();
-    gl->fBindBuffer(target, buffer ? buffer->GLName() : 0);
+    gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
 }
 
 void
 WebGLContextUnchecked::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
     gl->MakeCurrent();
-    gl->fBindBufferBase(target, index, buffer ? buffer->GLName() : 0);
+    gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
 }
 
 void
 WebGLContextUnchecked::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size)
 {
     gl->MakeCurrent();
-    gl->fBindBufferRange(target, index, buffer ? buffer->GLName() : 0, offset, size);
+
+#ifdef XP_MACOSX
+    if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
+        gl->WorkAroundDriverBugs())
+    {
+        // BindBufferRange will fail if the buffer's contents is undefined.
+        // Bind so driver initializes the buffer.
+        gl->fBindBuffer(target, buffer->mGLName);
+    }
+#endif
+
+    gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
 }
 
 void
 WebGLContextUnchecked::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)
 {
     gl->MakeCurrent();
     gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
 }
@@ -51,79 +62,77 @@ WebGLContextUnchecked::CopyBufferSubData
 
 // -----------------------------------------------------------------------------
 // Sampler Objects
 
 void
 WebGLContextUnchecked::BindSampler(GLuint unit, WebGLSampler* sampler)
 {
     gl->MakeCurrent();
-    gl->fBindSampler(unit, sampler ? sampler->GLName() : 0);
-    if (sampler)
-        sampler->BindTo(LOCAL_GL_SAMPLER_BINDING);
+    gl->fBindSampler(unit, sampler ? sampler->mGLName : 0);
 }
 
 GLint
 WebGLContextUnchecked::GetSamplerParameteriv(WebGLSampler* sampler,
                                              GLenum pname)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
 
     GLint param = 0;
     gl->MakeCurrent();
-    gl->fGetSamplerParameteriv(sampler->GLName(), pname, &param);
+    gl->fGetSamplerParameteriv(sampler->mGLName, pname, &param);
 
     return param;
 }
 
 GLfloat
 WebGLContextUnchecked::GetSamplerParameterfv(WebGLSampler* sampler,
                                              GLenum pname)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
 
     GLfloat param = 0.0f;
     gl->MakeCurrent();
-    gl->fGetSamplerParameterfv(sampler->GLName(), pname, &param);
+    gl->fGetSamplerParameterfv(sampler->mGLName, pname, &param);
     return param;
 }
 
 void
 WebGLContextUnchecked::SamplerParameteri(WebGLSampler* sampler,
                                          GLenum pname,
                                          GLint param)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
     gl->MakeCurrent();
-    gl->fSamplerParameteri(sampler->GLName(), pname, param);
+    gl->fSamplerParameteri(sampler->mGLName, pname, param);
 }
 
 void
 WebGLContextUnchecked::SamplerParameteriv(WebGLSampler* sampler,
                                           GLenum pname,
                                           const GLint* param)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
     gl->MakeCurrent();
-    gl->fSamplerParameteriv(sampler->GLName(), pname, param);
+    gl->fSamplerParameteriv(sampler->mGLName, pname, param);
 }
 
 void
 WebGLContextUnchecked::SamplerParameterf(WebGLSampler* sampler,
                                          GLenum pname,
                                          GLfloat param)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
     gl->MakeCurrent();
-    gl->fSamplerParameterf(sampler->GLName(), pname, param);
+    gl->fSamplerParameterf(sampler->mGLName, pname, param);
 }
 
 void
 WebGLContextUnchecked::SamplerParameterfv(WebGLSampler* sampler,
                                           GLenum pname,
                                           const GLfloat* param)
 {
     MOZ_ASSERT(sampler, "Did you validate?");
     gl->MakeCurrent();
-    gl->fSamplerParameterfv(sampler->GLName(), pname, param);
+    gl->fSamplerParameterfv(sampler->mGLName, pname, param);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -1093,51 +1093,51 @@ WebGLContext::AssertCachedBindings()
 
     if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
     // Bound object state
     if (IsWebGL2()) {
-        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName()
+        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
                                              : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound);
 
-        bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->GLName() : 0;
+        bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->mGLName : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound);
     } else {
         MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
-        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->GLName()
+        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
                                              : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
     }
 
     GLuint bound = mCurrentProgram ? mCurrentProgram->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_CURRENT_PROGRAM, bound);
 
     // Textures
     GLenum activeTexture = mActiveTexture + LOCAL_GL_TEXTURE0;
     AssertUintParamCorrect(gl, LOCAL_GL_ACTIVE_TEXTURE, activeTexture);
 
     WebGLTexture* curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_2D);
-    bound = curTex ? curTex->GLName() : 0;
+    bound = curTex ? curTex->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_2D, bound);
 
     curTex = ActiveBoundTextureForTarget(LOCAL_GL_TEXTURE_CUBE_MAP);
-    bound = curTex ? curTex->GLName() : 0;
+    bound = curTex ? curTex->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, bound);
 
     // Buffers
-    bound = mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0;
+    bound = mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ARRAY_BUFFER_BINDING, bound);
 
     MOZ_ASSERT(mBoundVertexArray);
     WebGLBuffer* curBuff = mBoundVertexArray->mElementArrayBuffer;
-    bound = curBuff ? curBuff->GLName() : 0;
+    bound = curBuff ? curBuff->mGLName : 0;
     AssertUintParamCorrect(gl, LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING, bound);
 
     MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
 #endif
 }
 
 void
 WebGLContext::AssertCachedState()
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -261,18 +261,20 @@ WebGLContext::ValidateDataOffsetSize(Web
  */
 bool
 WebGLContext::ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info)
 {
     MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(readOffset) + size).isValid());
     MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
 
     bool separate = (readOffset + size < writeOffset || writeOffset + size < readOffset);
-    if (!separate)
-        ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, writeOffset + size) overlap");
+    if (!separate) {
+        ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, "
+                          "writeOffset + size) overlap", info);
+    }
 
     return separate;
 }
 
 bool
 WebGLContext::ValidateTextureTargetEnum(GLenum target, const char* info)
 {
     switch (target) {
--- a/dom/canvas/WebGLContextVertexArray.cpp
+++ b/dom/canvas/WebGLContextVertexArray.cpp
@@ -46,24 +46,30 @@ WebGLContext::BindVertexArray(WebGLVerte
 }
 
 already_AddRefed<WebGLVertexArray>
 WebGLContext::CreateVertexArray()
 {
     if (IsContextLost())
         return nullptr;
 
-    nsRefPtr<WebGLVertexArray> globj = WebGLVertexArray::Create(this);
+    nsRefPtr<WebGLVertexArray> globj = CreateVertexArrayImpl();
 
     MakeContextCurrent();
     globj->GenVertexArray();
 
     return globj.forget();
 }
 
+WebGLVertexArray*
+WebGLContext::CreateVertexArrayImpl()
+{
+    return WebGLVertexArray::Create(this);
+}
+
 void
 WebGLContext::DeleteVertexArray(WebGLVertexArray* array)
 {
     if (IsContextLost())
         return;
 
     if (array == nullptr)
         return;
@@ -81,14 +87,19 @@ bool
 WebGLContext::IsVertexArray(WebGLVertexArray* array)
 {
     if (IsContextLost())
         return false;
 
     if (!array)
         return false;
 
-    return ValidateObjectAllowDeleted("isVertexArray", array) &&
-           !array->IsDeleted() &&
-           array->HasEverBeenBound();
+    if (!ValidateObjectAllowDeleted("isVertexArray", array))
+        return false;
+
+    if (array->IsDeleted())
+        return false;
+
+    MakeContextCurrent();
+    return array->IsVertexArray();
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -88,18 +88,18 @@ WebGLExtensionDisjointTimerQuery::BeginQ
   if (mActiveQuery) {
     mContext->ErrorInvalidOperation("beginQueryEXT: A query is already"
                                     " active.");
     return;
   }
 
   mContext->MakeContextCurrent();
   gl::GLContext* gl = mContext->GL();
-  gl->fBeginQuery(target, query->GLName());
-  query->BindTo(LOCAL_GL_TIME_ELAPSED_EXT);
+  gl->fBeginQuery(target, query->mGLName);
+  query->mTarget = LOCAL_GL_TIME_ELAPSED_EXT;
   mActiveQuery = query;
 }
 
 void
 WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target)
 {
   if (mIsLost)
     return;
@@ -132,18 +132,18 @@ WebGLExtensionDisjointTimerQuery::QueryC
 
   if (target != LOCAL_GL_TIMESTAMP_EXT) {
     mContext->ErrorInvalidEnumInfo("queryCounterEXT: requires"
                                    " TIMESTAMP_EXT.", target);
     return;
   }
 
   mContext->MakeContextCurrent();
-  mContext->GL()->fQueryCounter(query->GLName(), target);
-  query->BindTo(LOCAL_GL_TIMESTAMP_EXT);
+  mContext->GL()->fQueryCounter(query->mGLName, target);
+  query->mTarget = LOCAL_GL_TIMESTAMP_EXT;
 }
 
 void
 WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
                                               GLenum pname,
                                               JS::MutableHandle<JS::Value> retval)
 {
   if (mIsLost)
@@ -205,25 +205,25 @@ WebGLExtensionDisjointTimerQuery::GetQue
   mContext->MakeContextCurrent();
   // XXX: Note that the query result *may change* within the same task!
   // This does not follow the specification, which states that all calls
   // checking query results must return the same value until the event loop
   // is empty.
   switch (pname) {
   case LOCAL_GL_QUERY_RESULT_EXT: {
     GLuint64 result = 0;
-    mContext->GL()->fGetQueryObjectui64v(query->GLName(),
+    mContext->GL()->fGetQueryObjectui64v(query->mGLName,
                                          LOCAL_GL_QUERY_RESULT_EXT,
                                          &result);
     retval.set(JS::NumberValue(result));
     break;
   }
   case LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT: {
     GLuint avail = 0;
-    mContext->GL()->fGetQueryObjectuiv(query->GLName(),
+    mContext->GL()->fGetQueryObjectuiv(query->mGLName,
                                        LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT,
                                        &avail);
     retval.set(JS::BooleanValue(bool(avail)));
     break;
   }
   default:
     mContext->ErrorInvalidEnumInfo("getQueryObjectEXT: Invalid query"
                                    " property.", pname);
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -373,17 +373,17 @@ WebGLFramebuffer::AttachPoint::FinalizeA
     }
     MOZ_ASSERT(HasImage());
 
     if (Texture()) {
         MOZ_ASSERT(gl == Texture()->Context()->GL());
 
         const GLenum imageTarget = ImageTarget().get();
         const GLint mipLevel = MipLevel();
-        const GLuint glName = Texture()->GLName();
+        const GLuint glName = Texture()->mGLName;
 
         if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
                                       imageTarget, glName, mipLevel);
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
                                       imageTarget, glName, mipLevel);
         } else {
             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc.get(),
@@ -399,24 +399,28 @@ WebGLFramebuffer::AttachPoint::FinalizeA
 
     MOZ_CRASH();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
-    : WebGLBindableName<FBTarget>(fbo)
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
+    , mGLName(fbo)
     , mStatus(0)
     , mReadBufferMode(LOCAL_GL_COLOR_ATTACHMENT0)
     , mColorAttachment0(this, LOCAL_GL_COLOR_ATTACHMENT0)
     , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
+#ifdef ANDROID
+    , mIsFB(false)
+#endif
+
 {
     mContext->mFramebuffers.insertBack(this);
 }
 
 void
 WebGLFramebuffer::Delete()
 {
     mColorAttachment0.Clear();
@@ -427,16 +431,20 @@ WebGLFramebuffer::Delete()
     const size_t moreColorAttachmentCount = mMoreColorAttachments.Length();
     for (size_t i = 0; i < moreColorAttachmentCount; i++) {
         mMoreColorAttachments[i].Clear();
     }
 
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteFramebuffers(1, &mGLName);
     LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
+
+#ifdef ANDROID
+    mIsFB = false;
+#endif
 }
 
 void
 WebGLFramebuffer::FramebufferRenderbuffer(FBAttachment attachPointEnum,
                                           RBTarget rbtarget,
                                           WebGLRenderbuffer* rb)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -3,37 +3,37 @@
  * 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 WEBGL_FRAMEBUFFER_H_
 #define WEBGL_FRAMEBUFFER_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 class WebGLRenderbuffer;
 class WebGLTexture;
 
 namespace gl {
     class GLContext;
 }
 
 class WebGLFramebuffer final
     : public nsWrapperCache
-    , public WebGLBindableName<FBTarget>
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
     , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
 {
+    friend class WebGLContext;
+
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     class AttachPoint
     {
     public:
         WebGLFramebuffer* const mFB;
     private:
@@ -48,17 +48,16 @@ public:
         ~AttachPoint();
 
         void Unlink() {
             mRenderbufferPtr = nullptr;
             mTexturePtr = nullptr;
         }
 
         bool IsDefined() const;
-
         bool IsDeleteRequested() const;
 
         TexInternalFormat EffectiveInternalFormat() const;
 
         bool HasAlpha() const;
         bool IsReadableFloat() const;
 
         void Clear() {
@@ -94,28 +93,39 @@ public:
 
         bool HasImage() const;
         bool IsComplete() const;
 
         void FinalizeAttachment(gl::GLContext* gl,
                                 FBAttachment attachmentLoc) const;
     };
 
+    const GLuint mGLName;
+
 private:
     mutable GLenum mStatus;
 
     GLenum mReadBufferMode;
 
     // No need to chase pointers for the oft-used color0.
     AttachPoint mColorAttachment0;
     AttachPoint mDepthAttachment;
     AttachPoint mStencilAttachment;
     AttachPoint mDepthStencilAttachment;
     nsTArray<AttachPoint> mMoreColorAttachments;
 
+#ifdef ANDROID
+    // Bug 1140459: Some drivers (including our test slaves!) don't
+    // give reasonable answers for IsRenderbuffer, maybe others.
+    // This shows up on Android 2.3 emulator.
+    //
+    // So we track the `is a Framebuffer` state ourselves.
+    bool mIsFB;
+#endif
+
 public:
     WebGLFramebuffer(WebGLContext* webgl, GLuint fbo);
 
 private:
     ~WebGLFramebuffer() {
         DeleteOnce();
     }
 
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -42,24 +42,26 @@ NeedsDepthStencilEmu(gl::GLContext* gl, 
 
 JSObject*
 WebGLRenderbuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLRenderbufferBinding::Wrap(cx, this, aGivenProto);
 }
 
 WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl)
-    : WebGLBindable<RBTarget>()
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
     , mPrimaryRB(0)
     , mSecondaryRB(0)
     , mInternalFormat(0)
     , mInternalFormatForGL(0)
     , mImageDataStatus(WebGLImageDataStatus::NoImageData)
     , mSamples(1)
+#ifdef ANDROID
+    , mIsRB(false)
+#endif
 {
     mContext->MakeContextCurrent();
 
     mContext->gl->fGenRenderbuffers(1, &mPrimaryRB);
     if (!SupportsDepthStencil(mContext->gl))
         mContext->gl->fGenRenderbuffers(1, &mSecondaryRB);
 
     mContext->mRenderbuffers.insertBack(this);
@@ -70,16 +72,19 @@ WebGLRenderbuffer::Delete()
 {
     mContext->MakeContextCurrent();
 
     mContext->gl->fDeleteRenderbuffers(1, &mPrimaryRB);
     if (mSecondaryRB)
         mContext->gl->fDeleteRenderbuffers(1, &mSecondaryRB);
 
     LinkedListElement<WebGLRenderbuffer>::removeFrom(mContext->mRenderbuffers);
+#ifdef ANDROID
+    mIsRB = false;
+#endif
 }
 
 int64_t
 WebGLRenderbuffer::MemoryUsage() const
 {
     int64_t pixels = int64_t(Width()) * int64_t(Height());
 
     GLenum primaryFormat = InternalFormatForGL();
--- a/dom/canvas/WebGLRenderbuffer.h
+++ b/dom/canvas/WebGLRenderbuffer.h
@@ -3,25 +3,23 @@
  * 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 WEBGL_RENDERBUFFER_H_
 #define WEBGL_RENDERBUFFER_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLRenderbuffer final
     : public nsWrapperCache
-    , public WebGLBindable<RBTarget>
     , public WebGLRefCountedObject<WebGLRenderbuffer>
     , public LinkedListElement<WebGLRenderbuffer>
     , public WebGLRectangleObject
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
 public:
     explicit WebGLRenderbuffer(WebGLContext* webgl);
@@ -36,16 +34,18 @@ public:
         MOZ_ASSERT(x != WebGLImageDataStatus::NoImageData ||
                    mImageDataStatus == WebGLImageDataStatus::NoImageData);
         mImageDataStatus = x;
     }
 
     GLsizei Samples() const { return mSamples; }
     void SetSamples(GLsizei samples) { mSamples = samples; }
 
+    GLuint PrimaryGLName() const { return mPrimaryRB; }
+
     GLenum InternalFormat() const { return mInternalFormat; }
     void SetInternalFormat(GLenum internalFormat) {
         mInternalFormat = internalFormat;
     }
 
     GLenum InternalFormatForGL() const { return mInternalFormatForGL; }
     void SetInternalFormatForGL(GLenum internalFormatForGL) {
         mInternalFormatForGL = internalFormatForGL;
@@ -75,15 +75,24 @@ protected:
     }
 
     GLuint mPrimaryRB;
     GLuint mSecondaryRB;
     GLenum mInternalFormat;
     GLenum mInternalFormatForGL;
     WebGLImageDataStatus mImageDataStatus;
     GLsizei mSamples;
+#ifdef ANDROID
+    // Bug 1140459: Some drivers (including our test slaves!) don't
+    // give reasonable answers for IsRenderbuffer, maybe others.
+    // This shows up on Android 2.3 emulator.
+    //
+    // So we track the `is a Renderbuffer` state ourselves.
+    bool mIsRB;
+#endif
 
+    friend class WebGLContext;
     friend class WebGLFramebuffer;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_RENDERBUFFER_H_
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -7,18 +7,18 @@
 
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 WebGLSampler::WebGLSampler(WebGLContext* webgl, GLuint sampler)
-    : WebGLBindableName<GLenum>(sampler),
-      WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
+    , mGLName(sampler)
 {
     mContext->mSamplers.insertBack(this);
 }
 
 WebGLSampler::~WebGLSampler()
 {
     DeleteOnce();
 }
--- a/dom/canvas/WebGLSampler.h
+++ b/dom/canvas/WebGLSampler.h
@@ -3,33 +3,33 @@
  * 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 WEBGL_SAMPLER_H_
 #define WEBGL_SAMPLER_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLSampler final
     : public nsWrapperCache
-    , public WebGLBindableName<GLenum>
     , public WebGLRefCountedObject<WebGLSampler>
     , public LinkedListElement<WebGLSampler>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext2;
 
 public:
     explicit WebGLSampler(WebGLContext* webgl, GLuint sampler);
 
+    const GLuint mGLName;
+
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSampler)
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -87,16 +87,21 @@ TranslateWithoutValidation(const nsACStr
         glesslVersion = 100;
     }
 
     std::string reversionedSource = source;
     reversionedSource.erase(versionStrStart, versionStrLen);
 
     switch (glesslVersion) {
     case 100:
+        if (!versionStrLen) {
+            /* According to ARB_ES2_compatibility extension glsl
+             * should accept #version 100 for ES 2 shaders. */
+            reversionedSource.insert(versionStrStart, "#version 100\n");
+        }
         break;
     case 300:
         reversionedSource.insert(versionStrStart, "#version 330\n");
         break;
     default:
         MOZ_CRASH("Bad `glesslVersion`.");
     }
 
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -19,18 +19,19 @@
 namespace mozilla {
 
 JSObject*
 WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
     return dom::WebGLTextureBinding::Wrap(cx, this, aGivenProto);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
-    : WebGLBindableName<TexTarget>(tex)
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
+    , mGLName(tex)
+    , mTarget(LOCAL_GL_NONE)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
     , mFacesCount(0)
     , mMaxLevelWithCustomImages(0)
     , mHaveGeneratedMipmap(false)
     , mImmutable(false)
@@ -137,29 +138,27 @@ void
 WebGLTexture::Bind(TexTarget texTarget)
 {
     // This function should only be called by bindTexture(). It assumes that the
     // GL context is already current.
 
     bool firstTimeThisTextureIsBound = !HasEverBeenBound();
 
     if (firstTimeThisTextureIsBound) {
-        BindTo(texTarget);
+        mTarget = texTarget.get();
     } else if (texTarget != Target()) {
         mContext->ErrorInvalidOperation("bindTexture: This texture has already"
                                         " been bound to a different target.");
         // Very important to return here before modifying texture state! This
         // was the place when I lost a whole day figuring very strange "invalid
         // write" crashes.
         return;
     }
 
-    GLuint name = GLName();
-
-    mContext->gl->fBindTexture(texTarget.get(), name);
+    mContext->gl->fBindTexture(texTarget.get(), mGLName);
 
     if (firstTimeThisTextureIsBound) {
         mFacesCount = (texTarget == LOCAL_GL_TEXTURE_CUBE_MAP) ? 6 : 1;
         EnsureMaxLevelWithCustomImagesAtLeast(0);
         SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
 
         // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R
         // is not present in GLES 2, but is present in GL and it seems as if for
@@ -224,17 +223,17 @@ WebGLTexture::SetCustomMipmap()
         EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
 
         for (size_t level = EffectiveBaseMipmapLevel() + 1;
              level <= EffectiveMaxMipmapLevel(); ++level)
         {
             imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
             imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
             imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
-            for(size_t face = 0; face < mFacesCount; ++face) {
+            for (size_t face = 0; face < mFacesCount; ++face) {
                 ImageInfoAtFace(face, level) = imageInfo;
             }
         }
     }
     mHaveGeneratedMipmap = false;
 }
 
 bool
@@ -271,18 +270,18 @@ WebGLTexture::IsCubeComplete() const
 bool
 WebGLTexture::IsMipmapCubeComplete() const
 {
     // In particular, this checks that this is a cube map:
     if (!IsCubeComplete())
         return false;
 
     for (int i = 0; i < 6; i++) {
-        const TexImageTarget face = TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP,
-                                                                   i);
+        const TexImageTarget face =
+            TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP, i);
         if (!DoesMipmapHaveAllLevelsConsistentlyDefined(face))
             return false;
     }
     return true;
 }
 
 bool
 WebGLTexture::IsMipmapRangeValid() const
@@ -621,28 +620,28 @@ WebGLTexture::EnsureNoUninitializedImage
     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
     if (!imageInfo.HasUninitializedImageData())
         return;
 
     mContext->MakeContextCurrent();
 
     // Try to clear with glClear.
     if (imageTarget == LOCAL_GL_TEXTURE_2D) {
-        bool cleared = ClearWithTempFB(mContext, GLName(), imageTarget, level,
+        bool cleared = ClearWithTempFB(mContext, mGLName, imageTarget, level,
                                        imageInfo.mEffectiveInternalFormat,
                                        imageInfo.mHeight, imageInfo.mWidth);
         if (cleared) {
             SetImageDataStatus(imageTarget, level,
                                WebGLImageDataStatus::InitializedImageData);
             return;
         }
     }
 
     // That didn't work. Try uploading zeros then.
-    gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget.get());
+    gl::ScopedBindTexture autoBindTex(mContext->gl, mGLName, mTarget);
 
     size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat);
     MOZ_ASSERT((bitspertexel % 8) == 0); // That would only happen for
                                          // compressed images, which cannot use
                                          // deferred initialization.
     size_t bytespertexel = bitspertexel / 8;
     CheckedUint32 checked_byteLength
         = WebGLContext::GetImageSize(
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -7,17 +7,16 @@
 #define WEBGL_TEXTURE_H_
 
 #include <algorithm>
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
 #include "nsAlgorithm.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLFramebufferAttachable.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool
@@ -26,27 +25,29 @@ IsPOTAssumingNonnegative(GLsizei x)
     MOZ_ASSERT(x >= 0);
     return x && (x & (x-1)) == 0;
 }
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture final
     : public nsWrapperCache
-    , public WebGLBindableName<TexTarget>
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
     , public WebGLContextBoundObject
     , public WebGLFramebufferAttachable
 {
 public:
     explicit WebGLTexture(WebGLContext* webgl, GLuint tex);
 
     void Delete();
 
+    bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
+    GLenum Target() const { return mTarget; }
+
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture)
@@ -58,16 +59,22 @@ protected:
 
     friend class WebGLContext;
     friend class WebGLFramebuffer;
 
     // We store information about the various images that are part of this
     // texture. (cubemap faces, mipmap levels)
 
 public:
+    const GLuint mGLName;
+
+protected:
+    GLenum mTarget;
+
+public:
     class ImageInfo
         : public WebGLRectangleObject
     {
     public:
         ImageInfo()
             : mEffectiveInternalFormat(LOCAL_GL_NONE)
             , mDepth(0)
             , mImageDataStatus(WebGLImageDataStatus::NoImageData)
--- a/dom/canvas/WebGLTimerQuery.cpp
+++ b/dom/canvas/WebGLTimerQuery.cpp
@@ -15,35 +15,48 @@ namespace mozilla {
 
 JSObject*
 WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::WebGLTimerQueryEXTBinding::Wrap(cx, this, aGivenProto);
 }
 
 WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint aName)
-  : WebGLBindableName<QueryBinding>(aName)
-  , WebGLContextBoundObject(webgl)
+  : WebGLContextBoundObject(webgl)
+  , mGLName(aName)
+  , mTarget(LOCAL_GL_NONE)
 {
 }
 
+WebGLTimerQuery::~WebGLTimerQuery()
+{
+  DeleteOnce();
+}
+
 WebGLTimerQuery*
 WebGLTimerQuery::Create(WebGLContext* webgl)
 {
   GLuint name = 0;
   webgl->MakeContextCurrent();
   webgl->gl->fGenQueries(1, &name);
   return new WebGLTimerQuery(webgl, name);
 }
 
 void
 WebGLTimerQuery::Delete()
 {
   mContext->MakeContextCurrent();
   mContext->gl->fDeleteQueries(1, &mGLName);
 }
 
+WebGLContext*
+WebGLTimerQuery::GetParentObject() const
+{
+  return Context();
+}
+
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLTimerQuery.h
+++ b/dom/canvas/WebGLTimerQuery.h
@@ -9,41 +9,41 @@
 
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLTimerQuery final
   : public nsWrapperCache
-  , public WebGLBindableName<QueryBinding>
   , public WebGLRefCountedObject<WebGLTimerQuery>
   , public WebGLContextBoundObject
 {
 public:
   static WebGLTimerQuery* Create(WebGLContext* webgl);
 
-  // WebGLRefCountedObject
   void Delete();
 
-  // nsWrapperCache
-  WebGLContext* GetParentObject() const {
-    return Context();
-  }
+  bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
+  GLenum Target() const { return mTarget; }
+
+  WebGLContext* GetParentObject() const;
 
   // NS
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
+  const GLenum mGLName;
+
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
 
 private:
   explicit WebGLTimerQuery(WebGLContext* webgl, GLuint aName);
-  ~WebGLTimerQuery() {
-    DeleteOnce();
-  }
+  ~WebGLTimerQuery();
+
+  GLenum mTarget;
 
   friend class WebGLExtensionDisjointTimerQuery;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_TIMER_QUERY_H_
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -8,18 +8,18 @@
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "WebGL2Context.h"
 
 namespace mozilla {
 
 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl,
                                                GLuint tf)
-    : WebGLBindableName<GLenum>(tf)
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
+    , mGLName(tf)
     , mMode(LOCAL_GL_NONE)
     , mIsActive(false)
     , mIsPaused(false)
 {
     mContext->mTransformFeedbacks.insertBack(this);
 }
 
 WebGLTransformFeedback::~WebGLTransformFeedback()
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -3,43 +3,44 @@
  * 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 WEBGL_TRANSFORM_FEEDBACK_H_
 #define WEBGL_TRANSFORM_FEEDBACK_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLTransformFeedback final
     : public nsWrapperCache
-    , public WebGLBindableName<GLenum>
     , public WebGLRefCountedObject<WebGLTransformFeedback>
     , public LinkedListElement<WebGLTransformFeedback>
     , public WebGLContextBoundObject
 {
     friend class WebGLContext;
     friend class WebGL2Context;
 
 public:
     explicit WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 
     void Delete();
     WebGLContext* GetParentObject() const;
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
+    const GLuint mGLName;
+
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
 
 private:
     ~WebGLTransformFeedback();
+
     GLenum mMode;
     bool mIsActive;
     bool mIsPaused;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_TRANSFORM_FEEDBACK_H_
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -16,18 +16,17 @@ namespace mozilla {
 
 JSObject*
 WebGLVertexArray::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLVertexArrayObjectOESBinding::Wrap(cx, this, aGivenProto);
 }
 
 WebGLVertexArray::WebGLVertexArray(WebGLContext* webgl)
-    : WebGLBindable<VAOBinding>()
-    , WebGLContextBoundObject(webgl)
+    : WebGLContextBoundObject(webgl)
     , mGLName(0)
 {
     mContext->mVertexArrays.insertBack(this);
 }
 
 WebGLVertexArray*
 WebGLVertexArray::Create(WebGLContext* webgl)
 {
@@ -45,16 +44,22 @@ WebGLVertexArray::Delete()
 {
     DeleteImpl();
 
     LinkedListElement<WebGLVertexArray>::removeFrom(mContext->mVertexArrays);
     mElementArrayBuffer = nullptr;
     mAttribs.Clear();
 }
 
+bool
+WebGLVertexArray::IsVertexArray()
+{
+    return IsVertexArrayImpl();
+}
+
 void
 WebGLVertexArray::EnsureAttrib(GLuint index)
 {
     MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs));
 
     if (index >= mAttribs.Length()) {
         mAttribs.SetLength(index + 1);
     }
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -3,57 +3,51 @@
  * 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 WEBGL_VERTEX_ARRAY_H_
 #define WEBGL_VERTEX_ARRAY_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
-#include "WebGLBindableName.h"
 #include "WebGLBuffer.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake;
 
 class WebGLVertexArray
     : public nsWrapperCache
-    , public WebGLBindable<VAOBinding>
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
     , public WebGLContextBoundObject
 {
 public:
     static WebGLVertexArray* Create(WebGLContext* webgl);
 
     void BindVertexArray() {
         // Bind to dummy value to signal that this vertex array has ever been
         // bound.
-        BindTo(LOCAL_GL_VERTEX_ARRAY_BINDING);
         BindVertexArrayImpl();
     };
 
-    virtual void GenVertexArray() = 0;
-    virtual void BindVertexArrayImpl() = 0;
-    virtual void DeleteImpl() = 0;
-
     void EnsureAttrib(GLuint index);
     bool HasAttrib(GLuint index) const {
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) const {
         return HasAttrib(index) && mAttribs[index].enabled;
     }
 
     // Implement parent classes:
     void Delete();
+    bool IsVertexArray();
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
@@ -63,16 +57,21 @@ public:
 
 protected:
     explicit WebGLVertexArray(WebGLContext* webgl);
 
     virtual ~WebGLVertexArray() {
         MOZ_ASSERT(IsDeleted());
     }
 
+    virtual void GenVertexArray() = 0;
+    virtual void BindVertexArrayImpl() = 0;
+    virtual void DeleteImpl() = 0;
+    virtual bool IsVertexArrayImpl() = 0;
+
     GLuint mGLName;
     nsTArray<WebGLVertexAttribData> mAttribs;
     WebGLRefPtr<WebGLBuffer> mElementArrayBuffer;
 
     friend class WebGLContext;
     friend class WebGLVertexArrayFake;
     friend class WebGL2Context;
 };
--- a/dom/canvas/WebGLVertexArrayFake.cpp
+++ b/dom/canvas/WebGLVertexArrayFake.cpp
@@ -5,16 +5,21 @@
 
 #include "WebGLVertexArrayFake.h"
 
 #include "GLContext.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
+WebGLVertexArrayFake::WebGLVertexArrayFake(WebGLContext* webgl)
+    : WebGLVertexArray(webgl)
+    , mIsVAO(false)
+{ }
+
 void
 WebGLVertexArrayFake::BindVertexArrayImpl()
 {
     // Go through and re-bind all buffers and setup all
     // vertex attribute pointers
     gl::GLContext* gl = mContext->gl;
 
     WebGLRefPtr<WebGLVertexArray> prevVertexArray = mContext->mBoundVertexArray;
@@ -47,11 +52,24 @@ WebGLVertexArrayFake::BindVertexArrayImp
     for (size_t i = mAttribs.Length(); i < len; ++i) {
         const WebGLVertexAttribData& vd = prevVertexArray->mAttribs[i];
 
         if (vd.enabled)
             gl->fDisableVertexAttribArray(i);
     }
 
     mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, prevBuffer);
+    mIsVAO = true;
+}
+
+void
+WebGLVertexArrayFake::DeleteImpl()
+{
+    mIsVAO = false;
+}
+
+bool
+WebGLVertexArrayFake::IsVertexArrayImpl()
+{
+    return mIsVAO;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLVertexArrayFake.h
+++ b/dom/canvas/WebGLVertexArrayFake.h
@@ -8,28 +8,29 @@
 
 #include "WebGLVertexArray.h"
 
 namespace mozilla {
 
 class WebGLVertexArrayFake final
     : public WebGLVertexArray
 {
-public:
+    friend class WebGLVertexArray;
+
+protected:
     virtual void BindVertexArrayImpl() override;
-    virtual void DeleteImpl() override {};
+    virtual void DeleteImpl() override;
     virtual void GenVertexArray() override {};
+    virtual bool IsVertexArrayImpl() override;
 
 private:
-    explicit WebGLVertexArrayFake(WebGLContext* webgl)
-        : WebGLVertexArray(webgl)
-    { }
+    explicit WebGLVertexArrayFake(WebGLContext* webgl);
 
     ~WebGLVertexArrayFake() {
         DeleteOnce();
     }
 
-    friend class WebGLVertexArray;
+    bool mIsVAO;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_VERTEX_ARRAY_FAKE_H_
--- a/dom/canvas/WebGLVertexArrayGL.cpp
+++ b/dom/canvas/WebGLVertexArrayGL.cpp
@@ -5,32 +5,68 @@
 
 #include "WebGLVertexArrayGL.h"
 
 #include "GLContext.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
+WebGLVertexArrayGL::WebGLVertexArrayGL(WebGLContext* webgl)
+    : WebGLVertexArray(webgl)
+#if defined(XP_LINUX)
+    , mIsVAO(false)
+#endif
+{ }
+
+WebGLVertexArrayGL::~WebGLVertexArrayGL()
+{
+    DeleteOnce();
+}
+
 void
 WebGLVertexArrayGL::DeleteImpl()
 {
     mElementArrayBuffer = nullptr;
 
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteVertexArrays(1, &mGLName);
+
+#if defined(XP_LINUX)
+    mIsVAO = false;
+#endif
 }
 
 void
 WebGLVertexArrayGL::BindVertexArrayImpl()
 {
     mContext->mBoundVertexArray = this;
+    mContext->gl->fBindVertexArray(mGLName);
 
-    mContext->gl->fBindVertexArray(mGLName);
+#if defined(XP_LINUX)
+    mIsVAO = true;
+#endif
 }
 
 void
 WebGLVertexArrayGL::GenVertexArray()
 {
     mContext->gl->fGenVertexArrays(1, &mGLName);
 }
 
+bool
+WebGLVertexArrayGL::IsVertexArrayImpl()
+{
+#if defined(XP_LINUX)
+    gl::GLContext* gl = mContext->gl;
+    if (gl->WorkAroundDriverBugs() &&
+        gl->Vendor() == gl::GLVendor::VMware &&
+        gl->Renderer() == gl::GLRenderer::GalliumLlvmpipe)
+    {
+        return mIsVAO;
+    }
+#endif
+
+    mContext->MakeContextCurrent();
+    return mContext->gl->fIsVertexArray(mGLName) != 0;
+}
+
 } // namespace mozilla
--- a/dom/canvas/WebGLVertexArrayGL.h
+++ b/dom/canvas/WebGLVertexArrayGL.h
@@ -5,31 +5,35 @@
 
 #ifndef WEBGL_VERTEX_ARRAY_GL_H_
 #define WEBGL_VERTEX_ARRAY_GL_H_
 
 #include "WebGLVertexArray.h"
 
 namespace mozilla {
 
-class WebGLVertexArrayGL final
+class WebGLVertexArrayGL
     : public WebGLVertexArray
 {
+    friend class WebGLVertexArray;
+
 public:
     virtual void DeleteImpl() override;
     virtual void BindVertexArrayImpl() override;
     virtual void GenVertexArray() override;
+    virtual bool IsVertexArrayImpl() override;
 
-private:
-    explicit WebGLVertexArrayGL(WebGLContext* webgl)
-        : WebGLVertexArray(webgl)
-    { }
+protected:
+    explicit WebGLVertexArrayGL(WebGLContext* webgl);
+    ~WebGLVertexArrayGL();
 
-    ~WebGLVertexArrayGL() {
-        DeleteOnce();
-    }
-
-    friend class WebGLVertexArray;
+#if defined(XP_LINUX)
+    // Bug 1140459: Some drivers (including our test slaves!) don't
+    // give reasonable answers for IsRenderbuffer, maybe others.
+    //
+    // So we track the `is a VAO` state ourselves.
+    bool mIsVAO;
+#endif
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_VERTEX_ARRAY_GL_H_
copy from dom/canvas/WebGLVertexArrayGL.h
copy to dom/canvas/WebGLVertexArrayObject.cpp
--- a/dom/canvas/WebGLVertexArrayGL.h
+++ b/dom/canvas/WebGLVertexArrayObject.cpp
@@ -1,35 +1,35 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 WEBGL_VERTEX_ARRAY_GL_H_
-#define WEBGL_VERTEX_ARRAY_GL_H_
+#include "WebGLVertexArrayObject.h"
 
-#include "WebGLVertexArray.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
 namespace mozilla {
+namespace dom {
 
-class WebGLVertexArrayGL final
-    : public WebGLVertexArray
+WebGLVertexArray*
+WebGLVertexArrayObject::Create(WebGLContext* webgl)
 {
-public:
-    virtual void DeleteImpl() override;
-    virtual void BindVertexArrayImpl() override;
-    virtual void GenVertexArray() override;
+  // WebGL 2: This is core in GL ES 3. If support is missing something
+  // is very wrong.
+  bool vaoSupport = webgl->GL()->IsSupported(gl::GLFeature::vertex_array_object);
+  MOZ_RELEASE_ASSERT(vaoSupport, "Vertex Array Objects aren't supported.");
+  if (vaoSupport)
+    return new WebGLVertexArrayObject(webgl);
 
-private:
-    explicit WebGLVertexArrayGL(WebGLContext* webgl)
-        : WebGLVertexArray(webgl)
-    { }
+  return nullptr;
+}
 
-    ~WebGLVertexArrayGL() {
-        DeleteOnce();
-    }
+JSObject*
+WebGLVertexArrayObject::WrapObject(JSContext* cx,
+                                   JS::Handle<JSObject*> givenProto)
+{
+  return dom::WebGLVertexArrayObjectBinding::Wrap(cx, this, givenProto);
+}
 
-    friend class WebGLVertexArray;
-};
-
+} // namespace dom
 } // namespace mozilla
-
-#endif // WEBGL_VERTEX_ARRAY_GL_H_
copy from dom/canvas/WebGLVertexArrayGL.h
copy to dom/canvas/WebGLVertexArrayObject.h
--- a/dom/canvas/WebGLVertexArrayGL.h
+++ b/dom/canvas/WebGLVertexArrayObject.h
@@ -1,35 +1,42 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_dom_WebGLVertexArrayObject_h
+#define mozilla_dom_WebGLVertexArrayObject_h
 
-#ifndef WEBGL_VERTEX_ARRAY_GL_H_
-#define WEBGL_VERTEX_ARRAY_GL_H_
-
-#include "WebGLVertexArray.h"
+#include "WebGLVertexArrayGL.h"
 
 namespace mozilla {
+namespace dom {
 
-class WebGLVertexArrayGL final
-    : public WebGLVertexArray
+/**
+ * This class implements the DOM bindings for WebGL 2 VAO.
+ *
+ * This exists to so the object returned from gl.createVertexArray()
+ * is an instance of WebGLVertexArrayObject (to match the WebGL 2
+ * spec.)
+ */
+class WebGLVertexArrayObject final
+  : public WebGLVertexArrayGL
 {
 public:
-    virtual void DeleteImpl() override;
-    virtual void BindVertexArrayImpl() override;
-    virtual void GenVertexArray() override;
+  static WebGLVertexArray* Create(WebGLContext* webgl);
+
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
 
 private:
-    explicit WebGLVertexArrayGL(WebGLContext* webgl)
-        : WebGLVertexArray(webgl)
-    { }
+  explicit WebGLVertexArrayObject(WebGLContext* webgl)
+    : WebGLVertexArrayGL(webgl)
+  { }
 
-    ~WebGLVertexArrayGL() {
-        DeleteOnce();
-    }
-
-    friend class WebGLVertexArray;
+  ~WebGLVertexArrayObject() {
+    DeleteOnce();
+  }
 };
 
+} // namespace dom
 } // namespace mozilla
 
-#endif // WEBGL_VERTEX_ARRAY_GL_H_
+#endif // !mozilla_dom_WebGLVertexArrayObject_h
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -26,16 +26,17 @@ EXPORTS.mozilla.ipc += [
 EXPORTS.mozilla.dom += [
     'CanvasGradient.h',
     'CanvasPath.h',
     'CanvasPattern.h',
     'CanvasRenderingContext2D.h',
     'CanvasUtils.h',
     'ImageData.h',
     'TextMetrics.h',
+    'WebGLVertexArrayObject.h',
 ]
 
 # http://support.microsoft.com/kb/143208
 DEFINES['NOMINMAX'] = True
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
     'CanvasImageCache.cpp',
@@ -123,16 +124,17 @@ UNIFIED_SOURCES += [
     'WebGLTexture.cpp',
     'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
+    'WebGLVertexArrayObject.cpp',
 ]
 LOCAL_INCLUDES += [
     '/js/xpconnect/wrappers',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -543,24 +543,26 @@ FetchDriver::ContinueHttpFetchAfterNetwo
   workers::AssertIsOnMainThread();
   MOZ_ASSERT(mResponse);
   MOZ_ASSERT(!mResponse->IsError());
 
   return SucceedWithResponse();
 }
 
 already_AddRefed<InternalResponse>
-FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse)
+FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI)
 {
   MOZ_ASSERT(aResponse);
-  if (!aResponse->FinalURL()) {
-    nsAutoCString reqURL;
+  nsAutoCString reqURL;
+  if (aFinalURI) {
+    aFinalURI->GetSpec(reqURL);
+  } else {
     mRequest->GetURL(reqURL);
-    aResponse->SetUrl(reqURL);
   }
+  aResponse->SetUrl(reqURL);
 
   // FIXME(nsm): Handle mixed content check, step 7 of fetch.
 
   nsRefPtr<InternalResponse> filteredResponse;
   switch (mRequest->GetResponseTainting()) {
     case InternalRequest::RESPONSETAINT_BASIC:
       filteredResponse = aResponse->BasicResponse();
       break;
@@ -579,17 +581,17 @@ FetchDriver::BeginAndGetFilteredResponse
   mObserver->OnResponseAvailable(filteredResponse);
   mResponseAvailableCalled = true;
   return filteredResponse.forget();
 }
 
 void
 FetchDriver::BeginResponse(InternalResponse* aResponse)
 {
-  nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse);
+  nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse, nullptr);
   // Release the ref.
 }
 
 nsresult
 FetchDriver::SucceedWithResponse()
 {
   workers::AssertIsOnMainThread();
   if (mObserver) {
@@ -711,19 +713,27 @@ FetchDriver::OnStartRequest(nsIRequest* 
     // Cancel request.
     return rv;
   }
   response->SetBody(pipeInputStream);
 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   response->InitChannelInfo(channel);
 
+  nsCOMPtr<nsIURI> channelURI;
+  rv = channel->GetURI(getter_AddRefs(channelURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    FailWithNetworkError();
+    // Cancel request.
+    return rv;
+  }
+
   // Resolves fetch() promise which may trigger code running in a worker.  Make
   // sure the Response is fully initialized before calling this.
-  mResponse = BeginAndGetFilteredResponse(response);
+  mResponse = BeginAndGetFilteredResponse(response, channelURI);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
 
--- a/dom/fetch/FetchDriver.h
+++ b/dom/fetch/FetchDriver.h
@@ -83,18 +83,19 @@ private:
   ~FetchDriver();
 
   nsresult Fetch(bool aCORSFlag);
   nsresult ContinueFetch(bool aCORSFlag);
   nsresult BasicFetch();
   nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false);
   nsresult ContinueHttpFetchAfterNetworkFetch();
   // Returns the filtered response sent to the observer.
+  // Callers who don't have access to a channel can pass null for aFinalURI.
   already_AddRefed<InternalResponse>
-  BeginAndGetFilteredResponse(InternalResponse* aResponse);
+  BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI);
   // Utility since not all cases need to do any post processing of the filtered
   // response.
   void BeginResponse(InternalResponse* aResponse);
   nsresult FailWithNetworkError();
   nsresult SucceedWithResponse();
   nsresult DoesNotRequirePreflight(nsIChannel* aChannel);
 };
 
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/dom/InternalHeaders.h"
 #include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
-  , mFinalURL(false)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
 {
 }
 
 already_AddRefed<InternalResponse>
 InternalResponse::Clone()
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -43,17 +43,16 @@ public:
   already_AddRefed<InternalResponse>
   OpaqueResponse()
   {
     MOZ_ASSERT(!mWrappedResponse, "Can't OpaqueResponse a already wrapped response");
     nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
     response->mType = ResponseType::Opaque;
     response->mTerminationReason = mTerminationReason;
     response->mURL = mURL;
-    response->mFinalURL = mFinalURL;
     response->mChannelInfo = mChannelInfo;
     response->mWrappedResponse = this;
     return response.forget();
   }
 
   already_AddRefed<InternalResponse>
   BasicResponse();
 
@@ -85,28 +84,16 @@ public:
   }
 
   void
   SetUrl(const nsACString& aURL)
   {
     mURL.Assign(aURL);
   }
 
-  bool
-  FinalURL() const
-  {
-    return mFinalURL;
-  }
-
-  void
-  SetFinalURL(bool aFinalURL)
-  {
-    mFinalURL = aFinalURL;
-  }
-
   uint16_t
   GetStatus() const
   {
     return mStatus;
   }
 
   const nsCString&
   GetStatusText() const
@@ -192,25 +179,23 @@ private:
   // InternalResponse, except headers, body and wrapped response (if any) which
   // are left uninitialized. Used for cloning and filtering.
   already_AddRefed<InternalResponse> CreateIncompleteCopy()
   {
     nsRefPtr<InternalResponse> copy = new InternalResponse(mStatus, mStatusText);
     copy->mType = mType;
     copy->mTerminationReason = mTerminationReason;
     copy->mURL = mURL;
-    copy->mFinalURL = mFinalURL;
     copy->mChannelInfo = mChannelInfo;
     return copy.forget();
   }
 
   ResponseType mType;
   nsCString mTerminationReason;
   nsCString mURL;
-  bool mFinalURL;
   const uint16_t mStatus;
   const nsCString mStatusText;
   nsRefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
   ChannelInfo mChannelInfo;
 
   // For filtered responses.
   // Cache, and SW interception should always serialize/access the underlying
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -853,17 +853,18 @@ ReadCompressedIndexDataValuesFromBlob(
       return NS_ERROR_FILE_CORRUPTED;
     }
 
     nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
                         uint32_t(keyBufferLength));
     blobDataIter += keyBufferLength;
 
     if (NS_WARN_IF(!aIndexValues.InsertElementSorted(
-                      IndexDataValue(indexId, unique, Key(keyBuffer))))) {
+                      IndexDataValue(indexId, unique, Key(keyBuffer)),
+                      fallible))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   MOZ_ASSERT(blobDataIter == blobDataEnd);
 
   return NS_OK;
@@ -2729,17 +2730,18 @@ InsertIndexDataValuesFunction::OnFunctio
   // Update the array with the new addition.
   if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1,
                                           fallible))) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   MOZ_ALWAYS_TRUE(
-    indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value)));
+    indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value),
+                                    fallible));
 
   // Compress the array.
   UniqueFreePtr<uint8_t> indexValuesBlob;
   uint32_t indexValuesBlobLength;
   rv = MakeCompressedIndexDataValues(indexValues,
                                      indexValuesBlob,
                                      &indexValuesBlobLength);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -17851,17 +17853,18 @@ DatabaseOperationBase::IndexDataValuesFr
     const IndexUpdateInfo& updateInfo = aUpdateInfos[idxIndex];
     const int64_t& indexId = updateInfo.indexId();
     const Key& key = updateInfo.value();
 
     bool unique;
     MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(indexId, &unique));
 
     MOZ_ALWAYS_TRUE(
-      aIndexValues.InsertElementSorted(IndexDataValue(indexId, unique, key)));
+      aIndexValues.InsertElementSorted(IndexDataValue(indexId, unique, key),
+                                       fallible));
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
 DatabaseOperationBase::InsertIndexTableRows(
@@ -22286,17 +22289,18 @@ UpdateIndexDataValuesFunction::OnFunctio
 
   // First construct the full list to update the index_data_values row.
   for (uint32_t index = 0; index < updateInfoCount; index++) {
     const IndexUpdateInfo& info = updateInfos[index];
 
     MOZ_ALWAYS_TRUE(
       indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
                                                      metadata.unique(),
-                                                     info.value())));
+                                                     info.value()),
+                                      fallible));
   }
 
   UniqueFreePtr<uint8_t> indexValuesBlob;
   uint32_t indexValuesBlobLength;
   rv = MakeCompressedIndexDataValues(indexValues,
                                      indexValuesBlob,
                                      &indexValuesBlobLength);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -22322,17 +22326,18 @@ UpdateIndexDataValuesFunction::OnFunctio
     MOZ_ASSERT(indexValues.Capacity() >= updateInfoCount);
 
     for (uint32_t index = 0; index < updateInfoCount; index++) {
       const IndexUpdateInfo& info = updateInfos[index];
 
       MOZ_ALWAYS_TRUE(
         indexValues.InsertElementSorted(IndexDataValue(metadata.id(),
                                                        metadata.unique(),
-                                                       info.value())));
+                                                       info.value()),
+                                        fallible));
     }
   }
 
   rv = InsertIndexTableRows(mConnection, objectStoreId, key, indexValues);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/media/AbstractThread.cpp
+++ b/dom/media/AbstractThread.cpp
@@ -128,9 +128,21 @@ AbstractThread::InitStatics()
   ClearOnShutdown(&sMainThread);
 
   if (!sCurrentThreadTLS.init()) {
     MOZ_CRASH();
   }
   sCurrentThreadTLS.set(sMainThread);
 }
 
+void
+AbstractThread::DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable)
+{
+  GetCurrent()->TailDispatcher().AddStateChangeTask(this, Move(aRunnable));
+}
+
+/* static */ void
+AbstractThread::DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable)
+{
+  GetCurrent()->TailDispatcher().AddDirectTask(Move(aRunnable));
+}
+
 } // namespace mozilla
--- a/dom/media/AbstractThread.h
+++ b/dom/media/AbstractThread.h
@@ -74,16 +74,20 @@ public:
   virtual nsIThread* AsXPCOMThread() { MOZ_CRASH("Not an XPCOM thread!"); }
 
   // Convenience method for getting an AbstractThread for the main thread.
   static AbstractThread* MainThread();
 
   // Must be called exactly once during startup.
   static void InitStatics();
 
+  void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable);
+
+  static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable);
+
 protected:
   virtual ~AbstractThread() {}
   static ThreadLocal<AbstractThread*> sCurrentThreadTLS;
 
   // True if we want to require that every task dispatched from tasks running in
   // this queue go through our queue's tail dispatcher.
   const bool mSupportsTailDispatch;
 };
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -160,28 +160,20 @@ void
 AudioSink::SetPreservesPitch(bool aPreservesPitch)
 {
   AssertCurrentThreadInMonitor();
   mPreservesPitch = aPreservesPitch;
   mSetPreservesPitch = true;
 }
 
 void
-AudioSink::StartPlayback()
+AudioSink::SetPlaying(bool aPlaying)
 {
   AssertCurrentThreadInMonitor();
-  mPlaying = true;
-  GetReentrantMonitor().NotifyAll();
-}
-
-void
-AudioSink::StopPlayback()
-{
-  AssertCurrentThreadInMonitor();
-  mPlaying = false;
+  mPlaying = aPlaying;
   GetReentrantMonitor().NotifyAll();
 }
 
 void
 AudioSink::AudioLoop()
 {
   AssertOnAudioThread();
   SINK_LOG("AudioLoop started");
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -37,18 +37,17 @@ public:
   // Shut down the AudioSink's resources.  The decoder monitor must not be
   // held during this call, as it may block processing thread event queues.
   void Shutdown();
 
   void SetVolume(double aVolume);
   void SetPlaybackRate(double aPlaybackRate);
   void SetPreservesPitch(bool aPreservesPitch);
 
-  void StartPlayback();
-  void StopPlayback();
+  void SetPlaying(bool aPlaying);
 
 private:
   ~AudioSink() {}
 
   // The main loop for the audio thread. Sent to the thread as
   // an nsRunnableMethod. This continually does blocking writes to
   // to audio stream to play audio data.
   void AudioLoop();
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -64,33 +64,45 @@ public:
 private:
   Mutex mMutex;
   // Members below are protected by mMutex.
   nsRefPtr<MediaStream> mStream;
   int64_t mLastOutputTime; // microseconds
   bool mStreamFinishedOnMainThread;
 };
 
+static void
+UpdateStreamBlocking(MediaStream* aStream, bool aBlocking)
+{
+  int32_t delta = aBlocking ? 1 : -1;
+  if (NS_IsMainThread()) {
+    aStream->ChangeExplicitBlockerCount(delta);
+  } else {
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<int32_t>(
+      aStream, &MediaStream::ChangeExplicitBlockerCount, delta);
+    AbstractThread::MainThread()->Dispatch(r.forget());
+  }
+}
+
 DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream)
   : mAudioFramesWritten(0)
   , mNextVideoTime(-1)
   , mNextAudioTime(-1)
   , mStreamInitialized(false)
   , mHaveSentFinish(false)
   , mHaveSentFinishAudio(false)
   , mHaveSentFinishVideo(false)
   , mStream(aStream)
-  , mHaveBlockedForPlayState(false)
-  , mHaveBlockedForStateMachineNotPlaying(false)
+  , mPlaying(false)
   , mEOSVideoCompensation(false)
 {
   mListener = new DecodedStreamGraphListener(mStream);
   mStream->AddListener(mListener);
-  // Block the stream until the initialization is done.
-  mStream->ChangeExplicitBlockerCount(1);
+  // Block the stream as mPlaying is initially false.
+  UpdateStreamBlocking(mStream, true);
 }
 
 DecodedStreamData::~DecodedStreamData()
 {
   mListener->Forget();
   mStream->Destroy();
 }
 
@@ -101,16 +113,25 @@ DecodedStreamData::IsFinished() const
 }
 
 int64_t
 DecodedStreamData::GetPosition() const
 {
   return mListener->GetLastOutputTime();
 }
 
+void
+DecodedStreamData::SetPlaying(bool aPlaying)
+{
+  if (mPlaying != aPlaying) {
+    mPlaying = aPlaying;
+    UpdateStreamBlocking(mStream, !mPlaying);
+  }
+}
+
 class OutputStreamListener : public MediaStreamListener {
   typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
 public:
   OutputStreamListener(DecodedStream* aDecodedStream, MediaStream* aStream)
     : mDecodedStream(aDecodedStream), mStream(aStream) {}
 
   void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
   {
@@ -285,9 +306,17 @@ DecodedStream::Connect(ProcessedMediaStr
   os->Init(this, aStream);
   Connect(os);
   if (aFinishWhenEnded) {
     // Ensure that aStream finishes the moment mDecodedStream does.
     aStream->SetAutofinish(true);
   }
 }
 
+void
+DecodedStream::SetPlaying(bool aPlaying)
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  MOZ_ASSERT(mData);
+  mData->SetPlaying(aPlaying);
+}
+
 } // namespace mozilla
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -36,16 +36,17 @@ class Image;
  * not connected to streams created by captureStreamUntilEnded.
  */
 class DecodedStreamData {
 public:
   explicit DecodedStreamData(SourceMediaStream* aStream);
   ~DecodedStreamData();
   bool IsFinished() const;
   int64_t GetPosition() const;
+  void SetPlaying(bool aPlaying);
 
   /* The following group of fields are protected by the decoder's monitor
    * and can be read or written on any thread.
    */
   // Count of audio frames written to the stream
   int64_t mAudioFramesWritten;
   // mNextVideoTime is the end timestamp for the last packet sent to the stream.
   // Therefore video packets starting at or after this time need to be copied
@@ -61,22 +62,17 @@ public:
   bool mStreamInitialized;
   bool mHaveSentFinish;
   bool mHaveSentFinishAudio;
   bool mHaveSentFinishVideo;
 
   // The decoder is responsible for calling Destroy() on this stream.
   const nsRefPtr<SourceMediaStream> mStream;
   nsRefPtr<DecodedStreamGraphListener> mListener;
-  // True when we've explicitly blocked this stream because we're
-  // not in PLAY_STATE_PLAYING. Used on the main thread only.
-  bool mHaveBlockedForPlayState;
-  // We also have an explicit blocker on the stream when
-  // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
-  bool mHaveBlockedForStateMachineNotPlaying;
+  bool mPlaying;
   // True if we need to send a compensation video frame to ensure the
   // StreamTime going forward.
   bool mEOSVideoCompensation;
 };
 
 class OutputStreamData {
 public:
   ~OutputStreamData();
@@ -91,16 +87,17 @@ class DecodedStream {
 public:
   explicit DecodedStream(ReentrantMonitor& aMonitor);
   DecodedStreamData* GetData() const;
   void DestroyData();
   void RecreateData(MediaStreamGraph* aGraph);
   nsTArray<OutputStreamData>& OutputStreams();
   ReentrantMonitor& GetReentrantMonitor() const;
   void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
+  void SetPlaying(bool aPlaying);
 
 private:
   void Connect(OutputStreamData* aStream);
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
   nsTArray<OutputStreamData> mOutputStreams;
   ReentrantMonitor& mMonitor;
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -634,17 +634,18 @@ MediaRawDataWriter::SetSize(size_t aSize
 bool
 MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
 {
   if (!EnsureSize(aSize + mTarget->mSize)) {
     return false;
   }
 
   // We ensure sufficient capacity above so this shouldn't fail.
-  MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(mTarget->mPadding, aData, aSize));
+  MOZ_ALWAYS_TRUE(mBuffer->InsertElementsAt(mTarget->mPadding, aData, aSize,
+                                            fallible));
   mTarget->mSize += aSize;
   mSize = mTarget->mSize;
   return true;
 }
 
 bool
 MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
 {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -267,16 +267,24 @@ MediaDecoderStateMachine::MediaDecoderSt
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
   timeBeginPeriod(1);
 #endif
+
+  AudioQueue().AddPopListener(
+    NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnAudioPopped),
+    mTaskQueue);
+
+  VideoQueue().AddPopListener(
+    NS_NewRunnableMethod(this, &MediaDecoderStateMachine::OnVideoPopped),
+    mTaskQueue);
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
   NS_ASSERTION(!mPendingWakeDecoder.get(),
                "WakeDecoder should have been revoked already");
@@ -309,18 +317,16 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
-  mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
-  mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
@@ -425,29 +431,16 @@ static bool ZeroDurationAtLastChunk(Vide
   // Get the last video frame's start time in VideoSegment aInput.
   // If the start time is equal to the duration of aInput, means the last video
   // frame's duration is zero.
   StreamTime lastVideoStratTime;
   aInput.GetLastFrame(&lastVideoStratTime);
   return lastVideoStratTime == aInput.GetDuration();
 }
 
-static void
-UpdateStreamBlocking(MediaStream* aStream, bool aBlocking)
-{
-  int32_t delta = aBlocking ? 1 : -1;
-  if (NS_IsMainThread()) {
-    aStream->ChangeExplicitBlockerCount(delta);
-  } else {
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<int32_t>(
-      aStream, &MediaStream::ChangeExplicitBlockerCount, delta);
-    AbstractThread::MainThread()->Dispatch(r.forget());
-  }
-}
-
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
   MOZ_ASSERT(mStreamStartTime != -1);
 
   DecodedStreamData* stream = GetDecodedStream();
@@ -476,22 +469,16 @@ void MediaDecoderStateMachine::SendStrea
                               SourceMediaStream::ADDTRACK_QUEUED);
         stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
             TaskQueue(), GetWakeDecoderRunnable());
 
         stream->mNextVideoTime = mStreamStartTime;
       }
       mediaStream->FinishAddTracks();
       stream->mStreamInitialized = true;
-
-      // Make sure stream blocking is updated before sending stream data so we
-      // don't 'leak' data when the stream is supposed to be blocked.
-      UpdateStreamBlockingForPlayState();
-      UpdateStreamBlockingForStateMachinePlaying();
-      UpdateStreamBlocking(mediaStream, false);
     }
 
     if (mInfo.HasAudio()) {
       MOZ_ASSERT(stream->mNextAudioTime != -1, "Should've been initialized");
       TrackID audioTrackId = mInfo.mAudio.mTrackId;
       nsAutoTArray<nsRefPtr<AudioData>,10> audio;
       // It's OK to hold references to the AudioData because AudioData
       // is ref-counted.
@@ -508,16 +495,22 @@ void MediaDecoderStateMachine::SendStrea
       }
       if (AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
         mediaStream->EndTrack(audioTrackId);
         stream->mHaveSentFinishAudio = true;
       }
       endPosition = std::max(endPosition,
           mediaStream->TicksToTimeRoundDown(mInfo.mAudio.mRate,
                                             stream->mAudioFramesWritten));
+
+      CheckedInt64 playedUsecs = mStreamStartTime +
+          FramesToUsecs(stream->mAudioFramesWritten, mInfo.mAudio.mRate);
+      if (playedUsecs.isValid()) {
+        OnAudioEndTimeUpdate(playedUsecs.value());
+      }
     }
 
     if (mInfo.HasVideo()) {
       MOZ_ASSERT(stream->mNextVideoTime != -1, "Should've been initialized");
       TrackID videoTrackId = mInfo.mVideo.mTrackId;
       nsAutoTArray<nsRefPtr<VideoData>,10> video;
       // It's OK to hold references to the VideoData because VideoData
       // is ref-counted.
@@ -596,18 +589,17 @@ void MediaDecoderStateMachine::SendStrea
   const auto clockTime = GetClock();
   while (true) {
     const AudioData* a = AudioQueue().PeekFront();
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
     // the decoding speed.
     if (a && a->mTime <= clockTime) {
-      OnAudioEndTimeUpdate(std::max(mAudioEndTime, a->GetEndTime()));
-      nsRefPtr<AudioData> releaseMe = PopAudio();
+      nsRefPtr<AudioData> releaseMe = AudioQueue().PopFront();
       continue;
     }
     break;
   }
 
   // To be consistent with AudioSink, |mAudioCompleted| is not set
   // until all samples are drained.
   if (finished && AudioQueue().GetSize() == 0) {
@@ -921,32 +913,35 @@ MediaDecoderStateMachine::PushFront(Vide
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
 
   VideoQueue().PushFront(aSample);
   UpdateNextFrameStatus();
 }
 
-already_AddRefed<AudioData>
-MediaDecoderStateMachine::PopAudio()
+void
+MediaDecoderStateMachine::OnAudioPopped()
 {
   MOZ_ASSERT(OnTaskQueue());
-  nsRefPtr<AudioData> sample = AudioQueue().PopFront();
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   UpdateNextFrameStatus();
-  return sample.forget();
+  DispatchAudioDecodeTaskIfNeeded();
 }
 
-already_AddRefed<VideoData>
-MediaDecoderStateMachine::PopVideo()
+void
+MediaDecoderStateMachine::OnVideoPopped()
 {
   MOZ_ASSERT(OnTaskQueue());
-  nsRefPtr<VideoData> sample = VideoQueue().PopFront();
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   UpdateNextFrameStatus();
-  return sample.forget();
+  DispatchVideoDecodeTaskIfNeeded();
+  // Notify the decode thread that the video queue's buffers may have
+  // free'd up space for more frames.
+  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void
 MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
                                        MediaDecoderReader::NotDecodedReason aReason)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -1290,17 +1285,16 @@ void MediaDecoderStateMachine::StopPlayb
   if (IsPlaying()) {
     mPlayDuration = GetClock() - mStartTime;
     SetPlayStartTime(TimeStamp());
   }
   // Notify the audio sink, so that it notices that we've stopped playing,
   // so it can pause audio playback.
   mDecoder->GetReentrantMonitor().NotifyAll();
   NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
-  UpdateStreamBlockingForStateMachinePlaying();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -1328,17 +1322,16 @@ void MediaDecoderStateMachine::MaybeStar
   mDecoder->NotifyPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
   nsresult rv = StartAudioThread();
   NS_ENSURE_SUCCESS_VOID(rv);
 
   mDecoder->GetReentrantMonitor().NotifyAll();
-  UpdateStreamBlockingForStateMachinePlaying();
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld) (mStartTime=%lld)", aTime, mStartTime);
   AssertCurrentThreadInMonitor();
@@ -2301,27 +2294,16 @@ MediaDecoderStateMachine::CallDecodeFirs
 nsresult
 MediaDecoderStateMachine::DecodeFirstFrame()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
   DECODER_LOG("DecodeFirstFrame started");
 
-  if (HasAudio()) {
-    RefPtr<nsIRunnable> decodeTask(
-      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
-    AudioQueue().AddPopListener(decodeTask, TaskQueue());
-  }
-  if (HasVideo()) {
-    RefPtr<nsIRunnable> decodeTask(
-      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
-    VideoQueue().AddPopListener(decodeTask, TaskQueue());
-  }
-
   if (IsRealTime()) {
     SetStartTime(0);
     nsresult res = FinishDecodeFirstFrame();
     NS_ENSURE_SUCCESS(res, res);
   } else if (mSentFirstFrameLoadedEvent) {
     // We're resuming from dormant state, so we don't need to request
     // the first samples in order to determine the media start time,
     // we have the start time from last time we loaded.
@@ -2943,20 +2925,17 @@ void MediaDecoderStateMachine::AdvanceFr
     while (IsRealTime() || clock_time >= frame->mTime) {
       mVideoFrameEndTime = frame->GetEndTime();
       if (currentFrame) {
         mDecoder->NotifyDecodedFrames(0, 0, 1);
         VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld (%d so far)",
                     currentFrame->mTime, clock_time, ++droppedFrames);
       }
       currentFrame = frame;
-      nsRefPtr<VideoData> releaseMe = PopVideo();
-      // Notify the decode thread that the video queue's buffers may have
-      // free'd up space for more frames.
-      mDecoder->GetReentrantMonitor().NotifyAll();
+      nsRefPtr<VideoData> releaseMe = VideoQueue().PopFront();
       OnPlaybackOffsetUpdate(frame->mOffset);
       if (VideoQueue().GetSize() == 0)
         break;
       frame = VideoQueue().PeekFront();
     }
     // Current frame has already been presented, wait until it's time to
     // present the next frame.
     if (frame && !currentFrame) {
@@ -3291,23 +3270,21 @@ void MediaDecoderStateMachine::StartBuff
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 }
 
 void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
 {
   AssertCurrentThreadInMonitor();
   mPlayStartTime = aTimeStamp;
-  if (!mAudioSink) {
-    return;
-  }
-  if (!mPlayStartTime.IsNull()) {
-    mAudioSink->StartPlayback();
-  } else {
-    mAudioSink->StopPlayback();
+
+  if (mAudioSink) {
+    mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
+  } else if (mAudioCaptured) {
+    mDecodedStream.SetPlaying(!mPlayStartTime.IsNull());
   }
 }
 
 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   DispatchAudioDecodeTaskIfNeeded();
@@ -3500,16 +3477,19 @@ void MediaDecoderStateMachine::DispatchA
     ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
     if (!self->mAudioCaptured) {
       // Stop the audio sink if it's running.
       self->StopAudioThread();
       // GetMediaTime() could return -1 because we haven't decoded
       // the 1st frame. But this is OK since we will update mStreamStartTime
       // again in SetStartTime().
       self->mStreamStartTime = self->GetMediaTime();
+      // Reset mAudioEndTime which will be updated as we send audio data to
+      // stream. Otherwise it will remain -1 if we don't have audio.
+      self->mAudioEndTime = -1;
       self->mAudioCaptured = true;
       self->ScheduleStateMachine();
     }
   });
   TaskQueue()->Dispatch(r.forget());
 }
 
 void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
@@ -3521,54 +3501,22 @@ void MediaDecoderStateMachine::AddOutput
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (!GetDecodedStream()) {
     RecreateDecodedStream(aStream->Graph());
   }
   mDecodedStream.Connect(aStream, aFinishWhenEnded);
   DispatchAudioCaptured();
 }
 
-void MediaDecoderStateMachine::UpdateStreamBlockingForPlayState()
-{
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-
-   auto stream = GetDecodedStream();
-   if (!stream) {
-     return;
-   }
-
-  bool blocking = mPlayState != MediaDecoder::PLAY_STATE_PLAYING ||
-                  mLogicallySeeking;
-  if (blocking != stream->mHaveBlockedForPlayState) {
-    stream->mHaveBlockedForPlayState = blocking;
-    UpdateStreamBlocking(stream->mStream, blocking);
-  }
-}
-
-void MediaDecoderStateMachine::UpdateStreamBlockingForStateMachinePlaying()
-{
-  AssertCurrentThreadInMonitor();
-
-  auto stream = GetDecodedStream();
-  if (!stream) {
-    return;
-  }
-
-  bool blocking = !IsPlaying();
-  if (blocking != stream->mHaveBlockedForStateMachineNotPlaying) {
-    stream->mHaveBlockedForStateMachineNotPlaying = blocking;
-    UpdateStreamBlocking(stream->mStream, blocking);
-  }
-}
-
 void MediaDecoderStateMachine::RecreateDecodedStream(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecodedStream.RecreateData(aGraph);
+  mDecodedStream.SetPlaying(IsPlaying());
 }
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
 #undef VERBOSE_LOG
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -157,23 +157,16 @@ public:
 private:
   // Initialization that needs to happen on the task queue. This is the first
   // task that gets run on the task queue, and is dispatched from the MDSM
   // constructor immediately after the task queue is created.
   void InitializationTask();
 
   void DispatchAudioCaptured();
 
-  // Update blocking state of mDecodedStream when mPlayState or
-  // mLogicallySeeking change. Decoder monitor must be held.
-  void UpdateStreamBlockingForPlayState();
-
-  // Call this IsPlaying() changes. Decoder monitor must be held.
-  void UpdateStreamBlockingForStateMachinePlaying();
-
   // Recreates mDecodedStream. Call this to create mDecodedStream at first,
   // and when seeking, to ensure a new stream is set up with fresh buffers.
   // Decoder monitor must be held.
   void RecreateDecodedStream(MediaStreamGraph* aGraph);
 
   void Shutdown();
 public:
 
@@ -422,22 +415,18 @@ protected:
 
   // Inserts MediaData* samples into their respective MediaQueues.
   // aSample must not be null.
   void Push(AudioData* aSample);
   void Push(VideoData* aSample);
   void PushFront(AudioData* aSample);
   void PushFront(VideoData* aSample);
 
-  // Pops MediaData* samples from their respective MediaQueues.
-  // Note that the audio queue is also drained on the audio thread,
-  // which we can't easily react to - This should be fixed when we
-  // remove the audio thread in bug 750596.
-  already_AddRefed<AudioData> PopAudio();
-  already_AddRefed<VideoData> PopVideo();
+  void OnAudioPopped();
+  void OnVideoPopped();
 
   void VolumeChanged();
   void LogicalPlaybackRateChanged();
   void PreservesPitchChanged();
 
   class WakeDecoderRunnable : public nsRunnable {
   public:
     explicit WakeDecoderRunnable(MediaDecoderStateMachine* aSM)
--- a/dom/media/StateMirroring.h
+++ b/dom/media/StateMirroring.h
@@ -192,17 +192,17 @@ private:
       }
       mValue = aNewValue;
 
       // We wait until things have stablized before sending state updates so that
       // we can avoid sending multiple updates, and possibly avoid sending any
       // updates at all if the value ends up where it started.
       if (!alreadyNotifying) {
         nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &Impl::DoNotify);
-        AbstractThread::GetCurrent()->TailDispatcher().AddDirectTask(r.forget());
+        AbstractThread::DispatchDirectTask(r.forget());
       }
     }
 
     Impl& operator=(const T& aNewValue) { Set(aNewValue); return *this; }
     Impl& operator=(const Impl& aOther) { Set(aOther); return *this; }
     Impl(const Impl& aOther) = delete;
 
   protected:
@@ -217,17 +217,17 @@ private:
       mInitialValue.reset();
 
       if (same) {
         MIRROR_LOG("%s [%p] unchanged - not sending update", mName, this);
         return;
       }
 
       for (size_t i = 0; i < mMirrors.Length(); ++i) {
-        OwnerThread()->TailDispatcher().AddStateChangeTask(mMirrors[i]->OwnerThread(), MakeNotifier(mMirrors[i]));
+        mMirrors[i]->OwnerThread()->DispatchStateChange(MakeNotifier(mMirrors[i]));
       }
     }
 
     already_AddRefed<nsIRunnable> MakeNotifier(AbstractMirror<T>* aMirror)
     {
       nsCOMPtr<nsIRunnable> r =
         NS_NewRunnableMethodWithArg<T>(aMirror, &AbstractMirror<T>::UpdateValue, mValue);
       return r.forget();
--- a/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
+++ b/dom/media/eme/GMPVideoDecoderTrialCreator.cpp
@@ -55,16 +55,20 @@ TrialCreatePrefName(const nsAString& aKe
   }
   return nullptr;
 }
 
 /* static */
 GMPVideoDecoderTrialCreator::TrialCreateState
 GMPVideoDecoderTrialCreator::GetCreateTrialState(const nsAString& aKeySystem)
 {
+  if (Preferences::GetBool("media.gmp.always-trial-create", false)) {
+    return Pending;
+  }
+
   const char* pref = TrialCreatePrefName(aKeySystem);
   if (!pref) {
     return Pending;
   }
   switch (Preferences::GetInt(pref, (int)Pending)) {
     case 0: return Pending;
     case 1: return Succeeded;
     case 2: return Failed;
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "GMPLoader.h"
 #include <stdio.h>
 #include "mozilla/Attributes.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
+#include "prenv.h"
 
 #include <string>
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 #include "mozilla/Scoped.h"
 #include "windows.h"
 #include <intrin.h>
 #include <assert.h>
@@ -163,32 +164,35 @@ GMPLoaderImpl::Load(const char* aLibPath
     memset(aOriginSalt, 0, aOriginSaltLen);
     volumeId = 0;
     memset(&deviceId[0], '*', sizeof(string16::value_type) * deviceId.size());
     deviceId = L"";
 
     if (!rlz_lib::BytesToString(digest, SHA256_LENGTH, &nodeId)) {
       return false;
     }
-    // We've successfully bound the origin salt to node id.
-    // rlz_lib::GetRawMachineId and/or the system functions it
-    // called could have left user identifiable data on the stack,
-    // so carefully zero the stack down to the guard page.
-    uint8_t* top;
-    uint8_t* bottom;
-    if (!GetStackAfterCurrentFrame(&top, &bottom)) {
-      return false;
-    }
-    assert(top >= bottom);
-    // Inline instructions equivalent to RtlSecureZeroMemory().
-    // We can't just use RtlSecureZeroMemory here directly, as in debug
-    // builds, RtlSecureZeroMemory() can't be inlined, and the stack
-    // memory it uses would get wiped by itself running, causing crashes.
-    for (volatile uint8_t* p = (volatile uint8_t*)bottom; p < top; p++) {
-      *p = 0;
+
+    if (!PR_GetEnv("MOZ_GMP_DISABLE_NODE_ID_CLEANUP")) {
+      // We've successfully bound the origin salt to node id.
+      // rlz_lib::GetRawMachineId and/or the system functions it
+      // called could have left user identifiable data on the stack,
+      // so carefully zero the stack down to the guard page.
+      uint8_t* top;
+      uint8_t* bottom;
+      if (!GetStackAfterCurrentFrame(&top, &bottom)) {
+        return false;
+      }
+      assert(top >= bottom);
+      // Inline instructions equivalent to RtlSecureZeroMemory().
+      // We can't just use RtlSecureZeroMemory here directly, as in debug
+      // builds, RtlSecureZeroMemory() can't be inlined, and the stack
+      // memory it uses would get wiped by itself running, causing crashes.
+      for (volatile uint8_t* p = (volatile uint8_t*)bottom; p < top; p++) {
+        *p = 0;
+      }
     }
   } else
 #endif
   {
     nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
   }
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -232,17 +232,18 @@ TCPSocketChild::SendSend(JS::Handle<JS::
     uint32_t nbytes = std::min(buflen - aByteOffset, aByteLength);
     FallibleTArray<uint8_t> fallibleArr;
     {
         JS::AutoCheckCannotGC nogc;
         uint8_t* data = JS_GetArrayBufferData(obj, nogc);
         if (!data) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
-        if (!fallibleArr.InsertElementsAt(0, data + aByteOffset, nbytes)) {
+        if (!fallibleArr.InsertElementsAt(0, data + aByteOffset, nbytes,
+                                          fallible)) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
     InfallibleTArray<uint8_t> arr;
     arr.SwapElements(fallibleArr);
     SendData(arr, aTrackingNumber);
   }
   return NS_OK;
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -306,17 +306,17 @@ TCPSocketParent::SendEvent(const nsAStri
       do {
           JS::AutoCheckCannotGC nogc;
           uint32_t nbytes = JS_GetArrayBufferByteLength(obj);
           uint8_t* buffer = JS_GetArrayBufferData(obj, nogc);
           if (!buffer) {
               errLine = __LINE__;
               break;
           }
-          if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
+          if (!fallibleArr.InsertElementsAt(0, buffer, nbytes, fallible)) {
               errLine = __LINE__;
               break;
           }
       } while (false);
 
       if (errLine) {
           FireInteralError(this, errLine);
           return NS_ERROR_OUT_OF_MEMORY;
--- a/dom/network/UDPSocketChild.cpp
+++ b/dom/network/UDPSocketChild.cpp
@@ -244,17 +244,17 @@ UDPSocketChild::SendWithAddress(const Ne
 nsresult
 UDPSocketChild::SendDataInternal(const UDPSocketAddr& aAddr,
                                  const uint8_t* aData,
                                  const uint32_t aByteLength)
 {
   NS_ENSURE_ARG(aData);
 
   FallibleTArray<uint8_t> fallibleArray;
-  if (!fallibleArray.InsertElementsAt(0, aData, aByteLength)) {
+  if (!fallibleArray.InsertElementsAt(0, aData, aByteLength, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   InfallibleTArray<uint8_t> array;
   array.SwapElements(fallibleArray);
 
   SendOutgoingData(array, aAddr);
 
--- a/dom/network/UDPSocketParent.cpp
+++ b/dom/network/UDPSocketParent.cpp
@@ -446,17 +446,17 @@ UDPSocketParent::OnPacketReceived(nsIUDP
       if (!allowed) {
         UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
       }
       return NS_OK;
     }
   }
 
   FallibleTArray<uint8_t> fallibleArray;
-  if (!fallibleArray.InsertElementsAt(0, buffer, len)) {
+  if (!fallibleArray.InsertElementsAt(0, buffer, len, fallible)) {
     FireInternalError(__LINE__);
     return NS_ERROR_OUT_OF_MEMORY;
   }
   InfallibleTArray<uint8_t> infallibleArray;
   infallibleArray.SwapElements(fallibleArray);
 
   // compose callback
   mozilla::unused << SendCallbackReceivedData(UDPAddressInfo(ip, port), infallibleArray);
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -5093,17 +5093,19 @@ OriginParser::HandleToken(const nsDepend
 
       if (!aToken.IsEmpty()) {
         QM_WARNING("Expected the third empty token!");
 
         mError = true;
         return;
       }
 
-      mState = eExpectingDriveLetterOrPathnameComponent;
+      mState =
+        mTokenizer.hasMoreTokens() ? eExpectingDriveLetterOrPathnameComponent
+                                   : eComplete;
 
       return;
     }
 
     case eExpectingHost: {
       if (aToken.IsEmpty()) {
         QM_WARNING("Expected a host (not an empty string)!");
 
@@ -5211,15 +5213,15 @@ OriginParser::HandleToken(const nsDepend
     default:
       MOZ_CRASH("Should never get here!");
   }
 }
 
 void
 OriginParser::HandleTrailingSeparator()
 {
-  MOZ_ASSERT(mState = eComplete);
+  MOZ_ASSERT(mState == eComplete);
   MOZ_ASSERT(mSchemaType == eFile);
 
   mPathnameComponents.AppendElement(EmptyCString());
 
   mState = eHandledTrailingSeparator;
 }
--- a/dom/svg/DOMSVGPathSegList.cpp
+++ b/dom/svg/DOMSVGPathSegList.cpp
@@ -380,17 +380,18 @@ DOMSVGPathSegList::InsertItemBefore(DOMS
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount);
 
   float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
   domItem->ToSVGPathSegEncodedData(segAsRaw);
 
   MOZ_ALWAYS_TRUE(InternalList().mData.InsertElementsAt(internalIndex,
                                                         segAsRaw,
-                                                        1 + argCount));
+                                                        1 + argCount,
+                                                        fallible));
   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(aIndex,
                                          ItemProxy(domItem.get(),
                                                    internalIndex),
                                          fallible));
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -22,22 +22,20 @@ support-files =
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_headers_mainthread.html]
 [test_fetch_app_protocol.html]
 [test_fetch_basic.html]
 [test_fetch_basic_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_fetch_basic_http.html]
 [test_fetch_basic_http_sw_reroute.html]
-skip-if = true # Bug 1170937, need fully support for redirects
-#skip-if = buildapp == 'b2g' # Bug 1137683
+skip-if = e10s || buildapp == 'b2g' # Bug 1173163 for e10s, bug 1137683 for b2g
 [test_fetch_cors.html]
 [test_fetch_cors_sw_reroute.html]
-skip-if = true # Bug 1170937, need fully support for redirects
-#skip-if = buildapp == 'b2g' # Bug 1137683
+skip-if = e10s || buildapp == 'b2g' # Bug 1173163 for e10s, bug 1137683 for b2g
 [test_formdataparsing.html]
 [test_formdataparsing_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_request.html]
 [test_request_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_response.html]
 [test_response_sw_reroute.html]
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -21,21 +21,19 @@ interface WebGLSampler {
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLSync {
 };
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLTransformFeedback {
 };
 
-/*
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLVertexArrayObject {
 };
-*/
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGL2RenderingContext : WebGLRenderingContext
 {
     const GLenum READ_BUFFER                                   = 0x0C02;
     const GLenum UNPACK_ROW_LENGTH                             = 0x0CF2;
     const GLenum UNPACK_SKIP_ROWS                              = 0x0CF3;
     const GLenum UNPACK_SKIP_PIXELS                            = 0x0CF4;
@@ -233,17 +231,16 @@ interface WebGL2RenderingContext : WebGL
     const GLenum RG32I                                         = 0x823B;
     const GLenum RG32UI                                        = 0x823C;
     const GLenum VERTEX_ARRAY_BINDING                          = 0x85B5;
     const GLenum R8_SNORM                                      = 0x8F94;
     const GLenum RG8_SNORM                                     = 0x8F95;
     const GLenum RGB8_SNORM                                    = 0x8F96;
     const GLenum RGBA8_SNORM                                   = 0x8F97;
     const GLenum SIGNED_NORMALIZED                             = 0x8F9C;
-    const GLenum PRIMITIVE_RESTART_FIXED_INDEX                 = 0x8D69;
     const GLenum COPY_READ_BUFFER                              = 0x8F36;
     const GLenum COPY_WRITE_BUFFER                             = 0x8F37;
     const GLenum COPY_READ_BUFFER_BINDING                      = 0x8F36; /* Same as COPY_READ_BUFFER */
     const GLenum COPY_WRITE_BUFFER_BINDING                     = 0x8F37; /* Same as COPY_WRITE_BUFFER */
     const GLenum UNIFORM_BUFFER                                = 0x8A11;
     const GLenum UNIFORM_BUFFER_BINDING                        = 0x8A28;
     const GLenum UNIFORM_BUFFER_START                          = 0x8A29;
     const GLenum UNIFORM_BUFFER_SIZE                           = 0x8A2A;
@@ -286,20 +283,16 @@ interface WebGL2RenderingContext : WebGL
     const GLenum CONDITION_SATISFIED                           = 0x911C;
     const GLenum WAIT_FAILED                                   = 0x911D;
     const GLenum SYNC_FLUSH_COMMANDS_BIT                       = 0x00000001;
     const GLenum VERTEX_ATTRIB_ARRAY_DIVISOR                   = 0x88FE;
     const GLenum ANY_SAMPLES_PASSED                            = 0x8C2F;
     const GLenum ANY_SAMPLES_PASSED_CONSERVATIVE               = 0x8D6A;
     const GLenum SAMPLER_BINDING                               = 0x8919;
     const GLenum RGB10_A2UI                                    = 0x906F;
-    const GLenum TEXTURE_SWIZZLE_R                             = 0x8E42;
-    const GLenum TEXTURE_SWIZZLE_G                             = 0x8E43;
-    const GLenum TEXTURE_SWIZZLE_B                             = 0x8E44;
-    const GLenum TEXTURE_SWIZZLE_A                             = 0x8E45;
     const GLenum GREEN                                         = 0x1904;
     const GLenum BLUE                                          = 0x1905;
     const GLenum INT_2_10_10_10_REV                            = 0x8D9F;
     const GLenum TRANSFORM_FEEDBACK                            = 0x8E22;
     const GLenum TRANSFORM_FEEDBACK_PAUSED                     = 0x8E23;
     const GLenum TRANSFORM_FEEDBACK_ACTIVE                     = 0x8E24;
     const GLenum TRANSFORM_FEEDBACK_BINDING                    = 0x8E25;
     const GLenum COMPRESSED_R11_EAC                            = 0x9270;
@@ -470,15 +463,13 @@ interface WebGL2RenderingContext : WebGL
     sequence<GLint>? getActiveUniforms(WebGLProgram? program, sequence<GLuint> uniformIndices, GLenum pname);
     GLuint getUniformBlockIndex(WebGLProgram? program, DOMString uniformBlockName);
     [Throws]
     (GLuint or Uint32Array or GLboolean)? getActiveUniformBlockParameter(WebGLProgram? program, GLuint uniformBlockIndex, GLenum pname);
     DOMString? getActiveUniformBlockName(WebGLProgram? program, GLuint uniformBlockIndex);
     void uniformBlockBinding(WebGLProgram? program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
 
     /* Vertex Array Objects */
-    /*
     WebGLVertexArrayObject? createVertexArray();
     void deleteVertexArray(WebGLVertexArrayObject? vertexArray);
     [WebGLHandlesContextLoss] GLboolean isVertexArray(WebGLVertexArrayObject? vertexArray);
     void bindVertexArray(WebGLVertexArrayObject? array);
-    */
 };
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -978,19 +978,21 @@ private:
 
       nsCOMPtr<nsILoadGroup> channelLoadGroup;
       rv = channel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
       NS_ENSURE_SUCCESS(rv, rv);
       MOZ_ASSERT(channelLoadGroup);
 
       // If the load principal is the system principal then the channel
       // principal must also be the system principal (we do not allow chrome
-      // code to create workers with non-chrome scripts). Otherwise this channel
-      // principal must be same origin with the load principal (we check again
-      // here in case redirects changed the location of the script).
+      // code to create workers with non-chrome scripts, and if we ever decide
+      // to change this we need to make sure we don't always set
+      // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
+      // this channel principal must be same origin with the load principal (we
+      // check again here in case redirects changed the location of the script).
       if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
         if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
           // See if this is a resource URI. Since JSMs usually come from
           // resource:// URIs we're currently considering all URIs with the
           // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
           bool isResource;
           rv = NS_URIChainHasFlags(finalURI,
                                    nsIProtocolHandler::URI_IS_UI_RESOURCE,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4880,22 +4880,25 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
     bool isChrome = nsContentUtils::IsCallerChrome();
 
     // First check to make sure the caller has permission to make a privileged
     // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
     if (aIsChromeWorker && !isChrome) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
 
-    // Chrome callers (whether ChromeWorker of Worker) always get the system
-    // principal here as they're allowed to load anything. The script loader may
-    // change the principal later depending on the script uri.
+    // Chrome callers (whether creating a ChromeWorker or Worker) always get the
+    // system principal here as they're allowed to load anything. The script
+    // loader will refuse to run any script that does not also have the system
+    // principal.
     if (isChrome) {
       rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
       NS_ENSURE_SUCCESS(rv, rv);
+
+      loadInfo.mPrincipalIsSystem = true;
     }
 
     // See if we're being called from a window.
     nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow;
     if (!globalWindow) {
       nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
         nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
       if (scriptGlobal) {
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -1959,17 +1959,17 @@ DrawTargetD2D::CreateRTForTexture(ID3D10
   HRESULT hr;
 
   RefPtr<IDXGISurface> surface;
   RefPtr<ID2D1RenderTarget> rt;
 
   hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << "Failed to QI texture to surface. Code: " << hr;
+    gfxCriticalError() << "Failed to QI texture to surface. Code: " << hexa(hr);
     return nullptr;
   }
 
   D3D10_TEXTURE2D_DESC desc;
   aTexture->GetDesc(&desc);
 
   D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
 
@@ -1977,17 +1977,18 @@ DrawTargetD2D::CreateRTForTexture(ID3D10
     alphaMode = D2D1_ALPHA_MODE_IGNORE;
   }
 
   D2D1_RENDER_TARGET_PROPERTIES props =
     D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode));
   hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
 
   if (FAILED(hr)) {
-    gfxCriticalError() << "Failed to create D2D render target for texture. Code:" << hr << " " << mSize << " Format: " << uint32_t(aFormat);
+    gfxCriticalError() << "Failed to create D2D render target for texture. Code: "
+                       << hexa(hr) << " " << mSize << " Format: " << uint32_t(aFormat);
     return nullptr;
   }
 
   return rt.forget();
 }
 
 void
 DrawTargetD2D::EnsureViews()
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -408,17 +408,18 @@ CompositorD3D11::GetTextureFactoryIdenti
 {
   TextureFactoryIdentifier ident;
   ident.mMaxTextureSize = GetMaxTextureSize();
   ident.mParentProcessId = XRE_GetProcessType();
   ident.mParentBackend = LayersBackend::LAYERS_D3D11;
   if (mAttachments->mSyncTexture) {
     HRESULT hr = mAttachments->mSyncTexture->GetSharedHandle(&ident.mSyncHandle);
     if (FAILED(hr) || !ident.mSyncHandle) {
-      gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: " << hr;
+      gfxCriticalError() << "Failed to get SharedHandle for sync texture. Result: "
+                         << hexa(hr);
       MOZ_CRASH();
     }
   }
   return ident;
 }
 
 bool
 CompositorD3D11::CanUseCanvasLayerForSize(const gfx::IntSize& aSize)
@@ -1337,26 +1338,26 @@ bool
 CompositorD3D11::UpdateConstantBuffers()
 {
   HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE resource;
   resource.pData = nullptr;
 
   hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
   if (Failed(hr) || !resource.pData) {
-    gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hr;
+    gfxCriticalError() << "Failed to map VSConstantBuffer. Result: " << hexa(hr);
     return false;
   }
   *(VertexShaderConstants*)resource.pData = mVSConstants;
   mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
   resource.pData = nullptr;
 
   hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
   if (Failed(hr) || !resource.pData) {
-    gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hr;
+    gfxCriticalError() << "Failed to map PSConstantBuffer. Result: " << hexa(hr);
     return false;
   }
   *(PixelShaderConstants*)resource.pData = mPSConstants;
   mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
 
   ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
 
   mContext->VSSetConstantBuffers(0, 1, &buffer);
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -905,17 +905,20 @@ gfxFontconfigFont::GetGlyphRenderingOpti
   }
 
   // We don't want to force the use of the autohinter over the font's built in hints
   return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting, false);
 }
 #endif
 
 gfxFcPlatformFontList::gfxFcPlatformFontList()
-    : mLocalNames(64), mGenericMappings(32), mLastConfig(nullptr)
+    : mLocalNames(64)
+    , mGenericMappings(32)
+    , mFcSubstituteCache(64)
+    , mLastConfig(nullptr)
 {
     // if the rescan interval is set, start the timer
     int rescanInterval = FcConfigGetRescanInterval(nullptr);
     if (rescanInterval) {
         mLastConfig = FcConfigGetCurrent();
         mCheckFontUpdatesTimer = do_CreateInstance("@mozilla.org/timer;1");
         if (mCheckFontUpdatesTimer) {
             mCheckFontUpdatesTimer->
@@ -1031,29 +1034,30 @@ gfxFcPlatformFontList::InitFontList()
 {
     mLastConfig = FcConfigGetCurrent();
 
     // reset font lists
     gfxPlatformFontList::InitFontList();
 
     mLocalNames.Clear();
     mGenericMappings.Clear();
+    mFcSubstituteCache.Clear();
+    sSentinelFirstFamily = nullptr;
 
     // iterate over available fonts
     FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
     AddFontSetFamilies(systemFonts);
 
 #ifdef MOZ_BUNDLED_FONTS
     ActivateBundledFonts();
     FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
     AddFontSetFamilies(appFonts);
 #endif
 
     mOtherFamilyNamesInitialized = true;
-    sSentinelFirstFamily = nullptr;
 
     return NS_OK;
 }
 
 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
 // FcFontList results in the list containing the localized names as dictated
 // by system defaults.
 static void
@@ -1235,27 +1239,34 @@ gfxFcPlatformFontList::FindFamily(const 
     // Example:
     //
     //   serif ==> DejaVu Serif, ...
     //   Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
     //
     // In this case fontconfig is including Tex Gyre Heros and
     // Nimbus Sans L as alternatives for Helvetica.
 
+    // Because the FcConfigSubstitute call is quite expensive, we cache the
+    // actual font family found via this process. So check the cache first:
+    NS_ConvertUTF16toUTF8 familyToFind(familyName);
+    gfxFontFamily* cached = mFcSubstituteCache.GetWeak(familyToFind);
+    if (cached) {
+        return cached;
+    }
+
     const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
     if (!sSentinelFirstFamily) {
         nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
         FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
         FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
         FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sSentinelFirstFamily);
     }
 
     // substitutions for font, -moz-sentinel pattern
     nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
-    NS_ConvertUTF16toUTF8 familyToFind(familyName);
     FcPatternAddString(fontWithSentinel, FC_FAMILY, ToFcChar8Ptr(familyToFind.get()));
     FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
     FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
 
     // iterate through substitutions until hitting the sentinel
     FcChar8* substName = nullptr;
     for (int i = 0;
          FcPatternGetString(fontWithSentinel, FC_FAMILY,
@@ -1264,16 +1275,19 @@ gfxFcPlatformFontList::FindFamily(const 
     {
         NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
         if (sSentinelFirstFamily &&
             FcStrCmp(substName, sSentinelFirstFamily) == 0) {
             break;
         }
         gfxFontFamily* foundFamily = gfxPlatformFontList::FindFamily(subst);
         if (foundFamily) {
+            // We've figured out what family the given name maps to, after any
+            // fontconfig subsitutions. Cache it to speed up future lookups.
+            mFcSubstituteCache.Put(familyToFind, foundFamily);
             return foundFamily;
         }
     }
 
     return nullptr;
 }
 
 bool
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -251,16 +251,19 @@ protected:
     // names to family
     nsBaseHashtable<nsStringHashKey,
                     nsCountedRef<FcPattern>,
                     FcPattern*> mLocalNames;
 
     // caching generic/lang ==> font family
     nsRefPtrHashtable<nsCStringHashKey, gfxFontFamily> mGenericMappings;
 
+    // caching family lookups as found by FindFamily after resolving substitutions
+    nsRefPtrHashtable<nsCStringHashKey, gfxFontFamily> mFcSubstituteCache;
+
     nsCOMPtr<nsITimer> mCheckFontUpdatesTimer;
     nsCountedRef<FcConfig> mLastConfig;
 
     static FT_Library sCairoFTLibrary;
     static FcChar8* sSentinelFirstFamily;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1975,17 +1975,17 @@ gfxWindowsPlatform::InitD3D11Devices()
                              // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                              // to prevent bug 1092260. IE 11 also uses this flag.
                              D3D11_CREATE_DEVICE_BGRA_SUPPORT,
                              featureLevels.Elements(), featureLevels.Length(),
                              D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
       if (FAILED(hr)) {
         // This should always succeed... in theory.
-        gfxCriticalError() << "Failed to initialize WARP D3D11 device!" << hr;
+        gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
         return;
       }
 
       mIsWARP = true;
       reporterWARP.SetSuccessful();
     } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
       gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
       return;
--- a/ipc/bluetooth/BluetoothDaemonConnection.cpp
+++ b/ipc/bluetooth/BluetoothDaemonConnection.cpp
@@ -209,17 +209,17 @@ BluetoothDaemonPDUConsumer::~BluetoothDa
 
 //
 // BluetoothDaemonConnectionIO
 //
 
 class BluetoothDaemonConnectionIO final : public ConnectionOrientedSocketIO
 {
 public:
-  BluetoothDaemonConnectionIO(nsIThread* aConsumerThread,
+  BluetoothDaemonConnectionIO(MessageLoop* aConsumerLoop,
                               MessageLoop* aIOLoop,
                               int aFd, ConnectionStatus aConnectionStatus,
                               UnixSocketConnector* aConnector,
                               BluetoothDaemonConnection* aConnection,
                               BluetoothDaemonPDUConsumer* aConsumer);
 
   // Methods for |DataSocketIO|
   //
@@ -242,24 +242,24 @@ public:
 private:
   BluetoothDaemonConnection* mConnection;
   BluetoothDaemonPDUConsumer* mConsumer;
   nsAutoPtr<BluetoothDaemonPDU> mPDU;
   bool mShuttingDownOnIOThread;
 };
 
 BluetoothDaemonConnectionIO::BluetoothDaemonConnectionIO(
-  nsIThread* aConsumerThread,
+  MessageLoop* aConsumerLoop,
   MessageLoop* aIOLoop,
   int aFd,
   ConnectionStatus aConnectionStatus,
   UnixSocketConnector* aConnector,
   BluetoothDaemonConnection* aConnection,
   BluetoothDaemonPDUConsumer* aConsumer)
-  : ConnectionOrientedSocketIO(aConsumerThread,
+  : ConnectionOrientedSocketIO(aConsumerLoop,
                                aIOLoop,
                                aFd,
                                aConnectionStatus,
                                aConnector)
   , mConnection(aConnection)
   , mConsumer(aConsumer)
   , mShuttingDownOnIOThread(false)
 {
@@ -356,26 +356,26 @@ BluetoothDaemonConnection::BluetoothDaem
 
 BluetoothDaemonConnection::~BluetoothDaemonConnection()
 { }
 
 // |ConnectionOrientedSocket|
 
 nsresult
 BluetoothDaemonConnection::PrepareAccept(UnixSocketConnector* aConnector,
-                                         nsIThread* aConsumerThread,
+                                         MessageLoop* aConsumerLoop,
                                          MessageLoop* aIOLoop,
                                          ConnectionOrientedSocketIO*& aIO)
 {
   MOZ_ASSERT(!mIO);
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
   mIO = new BluetoothDaemonConnectionIO(
-    aConsumerThread, aIOLoop, -1, UnixSocketWatcher::SOCKET_IS_CONNECTING,
+    aConsumerLoop, aIOLoop, -1, UnixSocketWatcher::SOCKET_IS_CONNECTING,
     aConnector, this, mPDUConsumer);
   aIO = mIO;
 
   return NS_OK;
 }
 
 // |DataSocket|
 
--- a/ipc/bluetooth/BluetoothDaemonConnection.h
+++ b/ipc/bluetooth/BluetoothDaemonConnection.h
@@ -121,17 +121,17 @@ public:
                             BluetoothDaemonConnectionConsumer* aConsumer,
                             int aIndex);
   virtual ~BluetoothDaemonConnection();
 
   // Methods for |ConnectionOrientedSocket|
   //
 
   nsresult PrepareAccept(UnixSocketConnector* aConnector,
-                         nsIThread* aConsumerThread,
+                         MessageLoop* aConsumerLoop,
                          MessageLoop* aIOLoop,
                          ConnectionOrientedSocketIO*& aIO) override;
 
   // Methods for |DataSocket|
   //
 
   void SendSocketData(UnixSocketIOBuffer* aBuffer) override;
 
--- a/ipc/unixsocket/ConnectionOrientedSocket.cpp
+++ b/ipc/unixsocket/ConnectionOrientedSocket.cpp
@@ -10,34 +10,33 @@
 namespace mozilla {
 namespace ipc {
 
 //
 // ConnectionOrientedSocketIO
 //
 
 ConnectionOrientedSocketIO::ConnectionOrientedSocketIO(
-  nsIThread* aConsumerThread,
+  MessageLoop* aConsumerLoop,
   MessageLoop* aIOLoop,
-  int aFd,
-  ConnectionStatus aConnectionStatus,
+  int aFd, ConnectionStatus aConnectionStatus,
   UnixSocketConnector* aConnector)
-  : DataSocketIO(aConsumerThread)
+  : DataSocketIO(aConsumerLoop)
   , UnixSocketWatcher(aIOLoop, aFd, aConnectionStatus)
   , mConnector(aConnector)
   , mPeerAddressLength(0)
 {
   MOZ_ASSERT(mConnector);
 }
 
 ConnectionOrientedSocketIO::ConnectionOrientedSocketIO(
-  nsIThread* aConsumerThread,
+  MessageLoop* aConsumerLoop,
   MessageLoop* aIOLoop,
   UnixSocketConnector* aConnector)
-  : DataSocketIO(aConsumerThread)
+  : DataSocketIO(aConsumerLoop)
   , UnixSocketWatcher(aIOLoop)
   , mConnector(aConnector)
   , mPeerAddressLength(0)
 {
   MOZ_ASSERT(mConnector);
 }
 
 ConnectionOrientedSocketIO::~ConnectionOrientedSocketIO()
@@ -74,19 +73,18 @@ ConnectionOrientedSocketIO::Connect()
   mPeerAddressLength = sizeof(mPeerAddress);
 
   int fd;
   nsresult rv = mConnector->CreateStreamSocket(peerAddress,
                                                &mPeerAddressLength,
                                                fd);
   if (NS_FAILED(rv)) {
     // Tell the consumer thread we've errored
-    GetConsumerThread()->Dispatch(
-      new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_ERROR),
-      NS_DISPATCH_NORMAL);
+    GetConsumerThread()->PostTask(
+      FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_ERROR));
     return NS_ERROR_FAILURE;
   }
 
   SetFd(fd);
 
   // calls OnConnected() on success, or OnError() otherwise
   rv = UnixSocketWatcher::Connect(peerAddress, mPeerAddressLength);
 
@@ -142,19 +140,18 @@ ConnectionOrientedSocketIO::OnSocketCanS
 }
 
 void
 ConnectionOrientedSocketIO::OnConnected()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
   MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
 
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
 void
@@ -171,19 +168,18 @@ ConnectionOrientedSocketIO::OnError(cons
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
 
   UnixFdWatcher::OnError(aFunction, aErrno);
 
   // Clean up watchers, status, fd
   Close();
 
   // Tell the consumer thread we've errored
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_ERROR),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_ERROR));
 }
 
 //
 // ConnectionOrientedSocket
 //
 
 ConnectionOrientedSocket::~ConnectionOrientedSocket()
 { }
--- a/ipc/unixsocket/ConnectionOrientedSocket.h
+++ b/ipc/unixsocket/ConnectionOrientedSocket.h
@@ -48,35 +48,35 @@ public:
   void OnListening() final;
   void OnConnected() final;
   void OnError(const char* aFunction, int aErrno) final;
 
 protected:
   /**
    * Constructs an instance of |ConnectionOrientedSocketIO|
    *
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O loop.
    * @param aFd The socket file descriptor.
    * @param aConnectionStatus The connection status for |aFd|.
    * @param aConnector Connector object for socket-type-specific methods.
    */
-  ConnectionOrientedSocketIO(nsIThread* aConsumerThread,
+  ConnectionOrientedSocketIO(MessageLoop* aConsumerLoop,
                              MessageLoop* aIOLoop,
                              int aFd, ConnectionStatus aConnectionStatus,
                              UnixSocketConnector* aConnector);
 
   /**
    * Constructs an instance of |ConnectionOrientedSocketIO|
    *
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O loop.
    * @param aConnector Connector object for socket-type-specific methods.
    */
-  ConnectionOrientedSocketIO(nsIThread* aConsumerThread,
+  ConnectionOrientedSocketIO(MessageLoop* aConsumerLoop,
                              MessageLoop* aIOLoop,
                              UnixSocketConnector* aConnector);
 
 private:
   /**
    * Connector object used to create the connection we are currently using.
    */
   nsAutoPtr<UnixSocketConnector> mConnector;
@@ -96,23 +96,23 @@ class ConnectionOrientedSocket : public 
 {
 public:
   /**
    * Prepares an instance of |ConnectionOrientedSocket| in DISCONNECTED
    * state for accepting a connection. Consumer-thread only.
    *
    * @param aConnector The new connector object, owned by the
    *                   connection-oriented socket.
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O thread.
    * @param[out] aIO, Returns an instance of |ConnectionOrientedSocketIO|.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   virtual nsresult PrepareAccept(UnixSocketConnector* aConnector,
-                                 nsIThread* aConsumerThread,
+                                 MessageLoop* aConsumerLoop,
                                  MessageLoop* aIOLoop,
                                  ConnectionOrientedSocketIO*& aIO) = 0;
 
 protected:
   virtual ~ConnectionOrientedSocket();
 };
 
 }
--- a/ipc/unixsocket/DataSocket.cpp
+++ b/ipc/unixsocket/DataSocket.cpp
@@ -45,33 +45,33 @@ ssize_t
 DataSocketIO::ReceiveData(int aFd)
 {
   MOZ_ASSERT(aFd >= 0);
 
   UnixSocketIOBuffer* incoming;
   nsresult rv = QueryReceiveBuffer(&incoming);
   if (NS_FAILED(rv)) {
     /* an error occured */
-    GetConsumerThread()->Dispatch(new SocketIORequestClosingRunnable(this),
-                                  NS_DISPATCH_NORMAL);
+    GetConsumerThread()->PostTask(FROM_HERE,
+                                  new SocketRequestClosingTask(this));
     return -1;
   }
 
   ssize_t res = incoming->Receive(aFd);
   if (res < 0) {
     /* an I/O error occured */
     DiscardBuffer();
-    GetConsumerThread()->Dispatch(new SocketIORequestClosingRunnable(this),
-                                  NS_DISPATCH_NORMAL);
+    GetConsumerThread()->PostTask(FROM_HERE,
+                                  new SocketRequestClosingTask(this));
     return -1;
   } else if (!res) {
     /* EOF or peer shut down sending */
     DiscardBuffer();
-    GetConsumerThread()->Dispatch(new SocketIORequestClosingRunnable(this),
-                                  NS_DISPATCH_NORMAL);
+    GetConsumerThread()->PostTask(FROM_HERE,
+                                  new SocketRequestClosingTask(this));
     return 0;
   }
 
 #ifdef MOZ_TASK_TRACER
   /* Make unix socket creation events to be the source events of TaskTracer,
    * and originate the rest correlation tasks from here.
    */
   AutoSourceEvent taskTracerEvent(SourceEventType::Unixsocket);
@@ -88,34 +88,34 @@ DataSocketIO::SendPendingData(int aFd)
   MOZ_ASSERT(aFd >= 0);
 
   while (HasPendingData()) {
     UnixSocketIOBuffer* outgoing = mOutgoingQ.ElementAt(0);
 
     ssize_t res = outgoing->Send(aFd);
     if (res < 0) {
       /* an I/O error occured */
-      GetConsumerThread()->Dispatch(new SocketIORequestClosingRunnable(this),
-                                    NS_DISPATCH_NORMAL);
+      GetConsumerThread()->PostTask(FROM_HERE,
+                                    new SocketRequestClosingTask(this));
       return NS_ERROR_FAILURE;
     } else if (!res && outgoing->GetSize()) {
       /* I/O is currently blocked; try again later */
       return NS_OK;
     }
     if (!outgoing->GetSize()) {
       mOutgoingQ.RemoveElementAt(0);
       delete outgoing;
     }
   }
 
   return NS_OK;
 }
 
-DataSocketIO::DataSocketIO(nsIThread* aConsumerThread)
-  : SocketIOBase(aConsumerThread)
+DataSocketIO::DataSocketIO(MessageLoop* aConsumerLoop)
+  : SocketIOBase(aConsumerLoop)
 { }
 
 //
 // DataSocket
 //
 
 DataSocket::~DataSocket()
 { }
--- a/ipc/unixsocket/DataSocket.h
+++ b/ipc/unixsocket/DataSocket.h
@@ -5,16 +5,17 @@
  * 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_ipc_datasocket_h
 #define mozilla_ipc_datasocket_h
 
 #include "mozilla/ipc/SocketBase.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace ipc {
 
 //
 // DataSocket
 //
 
@@ -85,17 +86,17 @@ public:
   void EnqueueData(UnixSocketIOBuffer* aBuffer);
   bool HasPendingData() const;
 
   ssize_t ReceiveData(int aFd);
 
   nsresult SendPendingData(int aFd);
 
 protected:
-  DataSocketIO(nsIThread* aConsumerThread);
+  DataSocketIO(MessageLoop* aConsumerLoop);
 
 private:
   /**
    * Raw data queue. Must be pushed/popped from I/O thread only.
    */
   nsTArray<UnixSocketIOBuffer*> mOutgoingQ;
 };
 
--- a/ipc/unixsocket/ListenSocket.cpp
+++ b/ipc/unixsocket/ListenSocket.cpp
@@ -22,17 +22,17 @@ namespace ipc {
 
 class ListenSocketIO final
   : public UnixSocketWatcher
   , public SocketIOBase
 {
 public:
   class ListenTask;
 
-  ListenSocketIO(nsIThread* aConsumerThread,
+  ListenSocketIO(MessageLoop* aConsumerLoop,
                  MessageLoop* aIOLoop,
                  ListenSocket* aListenSocket,
                  UnixSocketConnector* aConnector);
   ~ListenSocketIO();
 
   UnixSocketConnector* GetConnector() const;
 
   // Task callback methods
@@ -90,22 +90,22 @@ private:
   /**
    * Address structure of the socket currently in use
    */
   struct sockaddr_storage mAddress;
 
   ConnectionOrientedSocketIO* mCOSocketIO;
 };
 
-ListenSocketIO::ListenSocketIO(nsIThread* aConsumerThread,
+ListenSocketIO::ListenSocketIO(MessageLoop* aConsumerLoop,
                                MessageLoop* aIOLoop,
                                ListenSocket* aListenSocket,
                                UnixSocketConnector* aConnector)
   : UnixSocketWatcher(aIOLoop)
-  , SocketIOBase(aConsumerThread)
+  , SocketIOBase(aConsumerLoop)
   , mListenSocket(aListenSocket)
   , mConnector(aConnector)
   , mShuttingDownOnIOThread(false)
   , mAddressLength(0)
   , mCOSocketIO(nullptr)
 {
   MOZ_ASSERT(mListenSocket);
   MOZ_ASSERT(mConnector);
@@ -163,19 +163,18 @@ void
 ListenSocketIO::OnListening()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
   MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
 
   AddWatchers(READ_WATCHER, true);
 
   /* We signal a successful 'connection' to a local address for listening. */
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_SUCCESS),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_SUCCESS));
 }
 
 void
 ListenSocketIO::OnError(const char* aFunction, int aErrno)
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
 
   UnixFdWatcher::OnError(aFunction, aErrno);
@@ -186,19 +185,18 @@ void
 ListenSocketIO::FireSocketError()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
 
   // Clean up watchers, statuses, fds
   Close();
 
   // Tell the consumer thread we've errored
-  GetConsumerThread()->Dispatch(
-    new SocketIOEventRunnable(this, SocketIOEventRunnable::CONNECT_ERROR),
-    NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(
+    FROM_HERE, new SocketEventTask(this, SocketEventTask::CONNECT_ERROR));
 }
 
 void
 ListenSocketIO::OnSocketCanAcceptWithoutBlocking()
 {
   MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
   MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
   MOZ_ASSERT(mCOSocketIO);
@@ -306,46 +304,41 @@ ListenSocket::ListenSocket(ListenSocketC
 
 ListenSocket::~ListenSocket()
 {
   MOZ_ASSERT(!mIO);
 }
 
 nsresult
 ListenSocket::Listen(UnixSocketConnector* aConnector,
-                     nsIThread* aConsumerThread,
+                     MessageLoop* aConsumerLoop,
                      MessageLoop* aIOLoop,
                      ConnectionOrientedSocket* aCOSocket)
 {
   MOZ_ASSERT(!mIO);
 
-  mIO = new ListenSocketIO(aConsumerThread, aIOLoop, this, aConnector);
+  mIO = new ListenSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
 
   // Prepared I/O object, now start listening.
   nsresult rv = Listen(aCOSocket);
   if (NS_FAILED(rv)) {
     delete mIO;
     mIO = nullptr;
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 ListenSocket::Listen(UnixSocketConnector* aConnector,
                      ConnectionOrientedSocket* aCOSocket)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return Listen(aConnector, consumerThread, XRE_GetIOMessageLoop(), aCOSocket);
+  return Listen(aConnector, MessageLoop::current(), XRE_GetIOMessageLoop(),
+                aCOSocket);
 }
 
 nsresult
 ListenSocket::Listen(ConnectionOrientedSocket* aCOSocket)
 {
   MOZ_ASSERT(aCOSocket);
   MOZ_ASSERT(mIO);
 
--- a/ipc/unixsocket/ListenSocket.h
+++ b/ipc/unixsocket/ListenSocket.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_ipc_listensocket_h
 #define mozilla_ipc_listensocket_h
 
 #include "nsString.h"
 #include "mozilla/ipc/SocketBase.h"
 
 class MessageLoop;
-class nsIThread;
 
 namespace mozilla {
 namespace ipc {
 
 class ConnectionOrientedSocket;
 class ListenSocketConsumer;
 class ListenSocketIO;
 class UnixSocketConnector;
@@ -32,24 +31,24 @@ public:
    */
   ListenSocket(ListenSocketConsumer* aConsumer, int aIndex);
 
   /**
    * Starts a task on the socket that will try to accept a new connection
    * in a non-blocking manner.
    *
    * @param aConnector Connector object for socket-type-specific functions
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O thread.
    * @param aCOSocket The connection-oriented socket for handling the
    *                  accepted connection.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   nsresult Listen(UnixSocketConnector* aConnector,
-                  nsIThread* aConsumerThread,
+                  MessageLoop* aConsumerLoop,
                   MessageLoop* aIOLoop,
                   ConnectionOrientedSocket* aCOSocket);
 
   /**
    * Starts a task on the socket that will try to accept a new connection
    * in a non-blocking manner.
    *
    * @param aConnector Connector object for socket-type-specific functions
--- a/ipc/unixsocket/SocketBase.cpp
+++ b/ipc/unixsocket/SocketBase.cpp
@@ -249,123 +249,112 @@ SocketBase::SetConnectionStatus(SocketCo
 {
   mConnectionStatus = aConnectionStatus;
 }
 
 //
 // SocketIOBase
 //
 
-SocketIOBase::SocketIOBase(nsIThread* aConsumerThread)
-  : mConsumerThread(aConsumerThread)
+SocketIOBase::SocketIOBase(MessageLoop* aConsumerLoop)
+  : mConsumerLoop(aConsumerLoop)
 {
-  MOZ_ASSERT(mConsumerThread);
+  MOZ_ASSERT(mConsumerLoop);
 }
 
 SocketIOBase::~SocketIOBase()
 { }
 
-nsIThread*
+MessageLoop*
 SocketIOBase::GetConsumerThread() const
 {
-  return mConsumerThread;
+  return mConsumerLoop;
 }
 
 bool
 SocketIOBase::IsConsumerThread() const
 {
-  nsIThread* thread = nullptr;
-  if (NS_FAILED(NS_GetCurrentThread(&thread))) {
-    return false;
-  }
-  return thread == GetConsumerThread();
+  return GetConsumerThread() == MessageLoop::current();
 }
 
 //
-// SocketIOEventRunnable
+// SocketEventTask
 //
 
-SocketIOEventRunnable::SocketIOEventRunnable(SocketIOBase* aIO,
-                                             SocketEvent aEvent)
-  : SocketIORunnable<SocketIOBase>(aIO)
+SocketEventTask::SocketEventTask(SocketIOBase* aIO, SocketEvent aEvent)
+  : SocketTask<SocketIOBase>(aIO)
   , mEvent(aEvent)
 { }
 
-NS_METHOD
-SocketIOEventRunnable::Run()
+void
+SocketEventTask::Run()
 {
-  SocketIOBase* io = SocketIORunnable<SocketIOBase>::GetIO();
+  SocketIOBase* io = SocketTask<SocketIOBase>::GetIO();
 
   MOZ_ASSERT(io->IsConsumerThread());
 
   if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
     // Since we've already explicitly closed and the close
     // happened before this, this isn't really an error.
-    return NS_OK;
+    return;
   }
 
   SocketBase* socketBase = io->GetSocketBase();
   MOZ_ASSERT(socketBase);
 
   if (mEvent == CONNECT_SUCCESS) {
     socketBase->NotifySuccess();
   } else if (mEvent == CONNECT_ERROR) {
     socketBase->NotifyError();
   } else if (mEvent == DISCONNECT) {
     socketBase->NotifyDisconnect();
   }
-
-  return NS_OK;
 }
 
 //
-// SocketIORequestClosingRunnable
+// SocketRequestClosingTask
 //
 
-SocketIORequestClosingRunnable::SocketIORequestClosingRunnable(
+SocketRequestClosingTask::SocketRequestClosingTask(
   SocketIOBase* aIO)
-  : SocketIORunnable<SocketIOBase>(aIO)
+  : SocketTask<SocketIOBase>(aIO)
 { }
 
-NS_METHOD
-SocketIORequestClosingRunnable::Run()
+void
+SocketRequestClosingTask::Run()
 {
-  SocketIOBase* io = SocketIORunnable<SocketIOBase>::GetIO();
+  SocketIOBase* io = SocketTask<SocketIOBase>::GetIO();
 
   MOZ_ASSERT(io->IsConsumerThread());
 
   if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
     // Since we've already explicitly closed and the close
     // happened before this, this isn't really an error.
-    return NS_OK;
+    return;
   }
 
   SocketBase* socketBase = io->GetSocketBase();
   MOZ_ASSERT(socketBase);
 
   socketBase->Close();
-
-  return NS_OK;
 }
 
 //
-// SocketIODeleteInstanceRunnable
+// SocketDeleteInstanceTask
 //
 
-SocketIODeleteInstanceRunnable::SocketIODeleteInstanceRunnable(
+SocketDeleteInstanceTask::SocketDeleteInstanceTask(
   SocketIOBase* aIO)
   : mIO(aIO)
 { }
 
-NS_METHOD
-SocketIODeleteInstanceRunnable::Run()
+void
+SocketDeleteInstanceTask::Run()
 {
   mIO = nullptr; // delete instance
-
-  return NS_OK;
 }
 
 //
 // SocketIOShutdownTask
 //
 
 SocketIOShutdownTask::SocketIOShutdownTask(SocketIOBase* aIO)
   : SocketIOTask<SocketIOBase>(aIO)
@@ -380,14 +369,14 @@ SocketIOShutdownTask::Run()
   MOZ_ASSERT(!io->IsShutdownOnIOThread());
 
   // At this point, there should be no new events on the I/O thread
   // after this one with the possible exception of an accept task,
   // which ShutdownOnIOThread will cancel for us. We are now fully
   // shut down, so we can send a message to the consumer thread to
   // delete |io| safely knowing that it's not reference any longer.
   io->ShutdownOnIOThread();
-  io->GetConsumerThread()->Dispatch(new SocketIODeleteInstanceRunnable(io),
-                                    NS_DISPATCH_NORMAL);
+  io->GetConsumerThread()->PostTask(FROM_HERE,
+                                    new SocketDeleteInstanceTask(io));
 }
 
 }
 }
--- a/ipc/unixsocket/SocketBase.h
+++ b/ipc/unixsocket/SocketBase.h
@@ -6,18 +6,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 #ifndef mozilla_ipc_SocketBase_h
 #define mozilla_ipc_SocketBase_h
 
 #include "base/message_loop.h"
 #include "nsAutoPtr.h"
-#include "nsTArray.h"
-#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace ipc {
 
 //
 // UnixSocketBuffer
 //
 
@@ -377,104 +375,103 @@ public:
    */
   virtual void ShutdownOnConsumerThread() = 0;
 
   /**
    * Returns the consumer thread.
    *
    * @return A pointer to the consumer thread.
    */
-  nsIThread* GetConsumerThread() const;
+  MessageLoop* GetConsumerThread() const;
 
   /**
-   * @return True if the current thread is thre consumer thread, or false
+   * @return True if the current thread is the consumer thread, or false
    *         otherwise.
    */
   bool IsConsumerThread() const;
 
 protected:
-  SocketIOBase(nsIThread* nsConsumerThread);
+  SocketIOBase(MessageLoop* aConsumerLoop);
 
 private:
-  nsCOMPtr<nsIThread> mConsumerThread;
+  MessageLoop* mConsumerLoop;
 };
 
 //
-// Socket I/O runnables
+// Socket tasks
 //
 
-/* |SocketIORunnable| is a runnable for sending a message from
+/* |SocketTask| is a task for sending a message from
  * the I/O thread to the consumer thread.
  */
 template <typename T>
-class SocketIORunnable : public nsRunnable
+class SocketTask : public Task
 {
 public:
-  virtual ~SocketIORunnable()
+  virtual ~SocketTask()
   { }
 
   T* GetIO() const
   {
     return mIO;
   }
 
 protected:
-  SocketIORunnable(T* aIO)
-  : mIO(aIO)
+  SocketTask(T* aIO)
+    : mIO(aIO)
   {
     MOZ_ASSERT(aIO);
   }
 
 private:
   T* mIO;
 };
 
 /**
- * |SocketIOEventRunnable| reports the connection state on the
+ * |SocketEventTask| reports the connection state on the
  * I/O thread back to the consumer thread.
  */
-class SocketIOEventRunnable final : public SocketIORunnable<SocketIOBase>
+class SocketEventTask final : public SocketTask<SocketIOBase>
 {
 public:
   enum SocketEvent {
     CONNECT_SUCCESS,
     CONNECT_ERROR,
     DISCONNECT
   };
 
-  SocketIOEventRunnable(SocketIOBase* aIO, SocketEvent aEvent);
+  SocketEventTask(SocketIOBase* aIO, SocketEvent aEvent);
 
-  NS_IMETHOD Run() override;
+  void Run() override;
 
 private:
   SocketEvent mEvent;
 };
 
 /**
- * |SocketIORequestClosingRunnable| closes an instance of |SocketBase|
- * to the consumer thread.
+ * |SocketRequestClosingTask| closes an instance of |SocketBase|
+ * on the consumer thread.
  */
-class SocketIORequestClosingRunnable final
-  : public SocketIORunnable<SocketIOBase>
+class SocketRequestClosingTask final : public SocketTask<SocketIOBase>
 {
 public:
-  SocketIORequestClosingRunnable(SocketIOBase* aIO);
+  SocketRequestClosingTask(SocketIOBase* aIO);
 
-  NS_IMETHOD Run() override;
+  void Run() override;
 };
 
 /**
- * |SocketIODeleteInstanceRunnable| deletes an object on the consumer thread.
+ * |SocketDeleteInstanceTask| deletes an object on the consumer thread.
  */
-class SocketIODeleteInstanceRunnable final : public nsRunnable
+class SocketDeleteInstanceTask final : public Task
 {
 public:
-  SocketIODeleteInstanceRunnable(SocketIOBase* aIO);
+  SocketDeleteInstanceTask(SocketIOBase* aIO);
 
-  NS_IMETHOD Run() override;
+  void Run() override;
 
 private:
   nsAutoPtr<SocketIOBase> mIO;
 };
 
 //
 // Socket I/O tasks
 //
--- a/ipc/unixsocket/StreamSocket.cpp
+++ b/ipc/unixsocket/StreamSocket.cpp
@@ -20,25 +20,25 @@ namespace ipc {
 // StreamSocketIO
 //
 
 class StreamSocketIO final : public ConnectionOrientedSocketIO
 {
 public:
   class ConnectTask;
   class DelayedConnectTask;
-  class ReceiveRunnable;
+  class ReceiveTask;
 
-  StreamSocketIO(nsIThread* aConsumerThread,
-                 MessageLoop* mIOLoop,
+  StreamSocketIO(MessageLoop* aConsumerLoop,
+                 MessageLoop* aIOLoop,
                  StreamSocket* aStreamSocket,
                  UnixSocketConnector* aConnector);
-  StreamSocketIO(nsIThread* aConsumerThread,
-                 MessageLoop* mIOLoop, int aFd,
-                 ConnectionStatus aConnectionStatus,
+  StreamSocketIO(MessageLoop* aConsumerLoop,
+                 MessageLoop* aIOLoop,
+                 int aFd, ConnectionStatus aConnectionStatus,
                  StreamSocket* aStreamSocket,
                  UnixSocketConnector* aConnector);
   ~StreamSocketIO();
 
   StreamSocket* GetStreamSocket();
   DataSocket* GetDataSocket();
 
   // Delayed-task handling
@@ -86,34 +86,34 @@ private:
   CancelableTask* mDelayedConnectTask;
 
   /**
    * I/O buffer for received data
    */
   nsAutoPtr<UnixSocketRawData> mBuffer;
 };
 
-StreamSocketIO::StreamSocketIO(nsIThread* aConsumerThread,
+StreamSocketIO::StreamSocketIO(MessageLoop* aConsumerLoop,
                                MessageLoop* aIOLoop,
                                StreamSocket* aStreamSocket,
                                UnixSocketConnector* aConnector)
-  : ConnectionOrientedSocketIO(aConsumerThread, aIOLoop, aConnector)
+  : ConnectionOrientedSocketIO(aConsumerLoop, aIOLoop, aConnector)
   , mStreamSocket(aStreamSocket)
   , mShuttingDownOnIOThread(false)
   , mDelayedConnectTask(nullptr)
 {
   MOZ_ASSERT(mStreamSocket);
 }
 
-StreamSocketIO::StreamSocketIO(nsIThread* aConsumerThread,
+StreamSocketIO::StreamSocketIO(MessageLoop* aConsumerLoop,
                                MessageLoop* aIOLoop,
                                int aFd, ConnectionStatus aConnectionStatus,
                                StreamSocket* aStreamSocket,
                                UnixSocketConnector* aConnector)
-  : ConnectionOrientedSocketIO(aConsumerThread,
+  : ConnectionOrientedSocketIO(aConsumerLoop,
                                aIOLoop,
                                aFd,
                                aConnectionStatus,
                                aConnector)
   , mStreamSocket(aStreamSocket)
   , mShuttingDownOnIOThread(false)
   , mDelayedConnectTask(nullptr)
 {
@@ -178,57 +178,54 @@ StreamSocketIO::QueryReceiveBuffer(UnixS
     mBuffer = new UnixSocketRawData(MAX_READ_SIZE);
   }
   *aBuffer = mBuffer.get();
 
   return NS_OK;
 }
 
 /**
- * |ReceiveRunnable| transfers data received on the I/O thread
+ * |ReceiveTask| transfers data received on the I/O thread
  * to an instance of |StreamSocket| on the consumer thread.
  */
-class StreamSocketIO::ReceiveRunnable final
-  : public SocketIORunnable<StreamSocketIO>
+class StreamSocketIO::ReceiveTask final : public SocketTask<StreamSocketIO>
 {
 public:
-  ReceiveRunnable(StreamSocketIO* aIO, UnixSocketBuffer* aBuffer)
-    : SocketIORunnable<StreamSocketIO>(aIO)
+  ReceiveTask(StreamSocketIO* aIO, UnixSocketBuffer* aBuffer)
+    : SocketTask<StreamSocketIO>(aIO)
     , mBuffer(aBuffer)
   { }
 
-  NS_IMETHOD Run() override
+  void Run() override
   {
-    StreamSocketIO* io = SocketIORunnable<StreamSocketIO>::GetIO();
+    StreamSocketIO* io = SocketTask<StreamSocketIO>::GetIO();
 
     MOZ_ASSERT(io->IsConsumerThread());
 
     if (NS_WARN_IF(io->IsShutdownOnConsumerThread())) {
       // Since we've already explicitly closed and the close
       // happened before this, this isn't really an error.
-      return NS_OK;
+      return;
     }
 
     StreamSocket* streamSocket = io->GetStreamSocket();
     MOZ_ASSERT(streamSocket);
 
     streamSocket->ReceiveSocketData(mBuffer);
-
-    return NS_OK;
   }
 
 private:
   nsAutoPtr<UnixSocketBuffer> mBuffer;
 };
 
 void
 StreamSocketIO::ConsumeBuffer()
 {
-  GetConsumerThread()->Dispatch(new ReceiveRunnable(this, mBuffer.forget()),
-                                NS_DISPATCH_NORMAL);
+  GetConsumerThread()->PostTask(FROM_HERE,
+                                new ReceiveTask(this, mBuffer.forget()));
 }
 
 void
 StreamSocketIO::DiscardBuffer()
 {
   // Nothing to do.
 }
 
@@ -340,21 +337,21 @@ StreamSocket::~StreamSocket()
 void
 StreamSocket::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
 {
   mConsumer->ReceiveSocketData(mIndex, aBuffer);
 }
 
 nsresult
 StreamSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs,
-                      nsIThread* aConsumerThread, MessageLoop* aIOLoop)
+                      MessageLoop* aConsumerLoop, MessageLoop* aIOLoop)
 {
   MOZ_ASSERT(!mIO);
 
-  mIO = new StreamSocketIO(aConsumerThread, aIOLoop, this, aConnector);
+  mIO = new StreamSocketIO(aConsumerLoop, aIOLoop, this, aConnector);
   SetConnectionStatus(SOCKET_CONNECTING);
 
   if (aDelayMs > 0) {
     StreamSocketIO::DelayedConnectTask* connectTask =
       new StreamSocketIO::DelayedConnectTask(mIO);
     mIO->SetDelayedConnectTask(connectTask);
     MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs);
   } else {
@@ -362,39 +359,34 @@ StreamSocket::Connect(UnixSocketConnecto
   }
 
   return NS_OK;
 }
 
 nsresult
 StreamSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs)
 {
-  nsIThread* consumerThread = nullptr;
-  nsresult rv = NS_GetCurrentThread(&consumerThread);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return Connect(aConnector, aDelayMs, consumerThread, XRE_GetIOMessageLoop());
+  return Connect(aConnector, aDelayMs,
+                 MessageLoop::current(), XRE_GetIOMessageLoop());
 }
 
 // |ConnectionOrientedSocket|
 
 nsresult
 StreamSocket::PrepareAccept(UnixSocketConnector* aConnector,
-                            nsIThread* aConsumerThread,
+                            MessageLoop* aConsumerLoop,
                             MessageLoop* aIOLoop,
                             ConnectionOrientedSocketIO*& aIO)
 {
   MOZ_ASSERT(!mIO);
   MOZ_ASSERT(aConnector);
 
   SetConnectionStatus(SOCKET_CONNECTING);
 
-  mIO = new StreamSocketIO(aConsumerThread, aIOLoop,
+  mIO = new StreamSocketIO(aConsumerLoop, aIOLoop,
                            -1, UnixSocketWatcher::SOCKET_IS_CONNECTING,
                            this, aConnector);
   aIO = mIO;
 
   return NS_OK;
 }
 
 // |DataSocket|
--- a/ipc/unixsocket/StreamSocket.h
+++ b/ipc/unixsocket/StreamSocket.h
@@ -37,38 +37,38 @@ public:
   void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer);
 
   /**
    * Starts a task on the socket that will try to connect to a socket in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
    * @param aDelayMs Time delay in milliseconds.
-   * @param aConsumerThread The socket's consumer thread.
+   * @param aConsumerLoop The socket's consumer thread.
    * @param aIOLoop The socket's I/O thread.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs,
-                   nsIThread* aConsumerThread, MessageLoop* aIOLoop);
+                   MessageLoop* aConsumerLoop, MessageLoop* aIOLoop);
 
   /**
    * Starts a task on the socket that will try to connect to a socket in a
    * non-blocking manner.
    *
    * @param aConnector Connector object for socket type specific functions
    * @param aDelayMs Time delay in milliseconds.
    * @return NS_OK on success, or an XPCOM error code otherwise.
    */
   nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs = 0);
 
   // Methods for |ConnectionOrientedSocket|
   //
 
   nsresult PrepareAccept(UnixSocketConnector* aConnector,
-                         nsIThread* aConsumerThread,
+                         MessageLoop* aConsumerLoop,
                          MessageLoop* aIOLoop,
                          ConnectionOrientedSocketIO*& aIO) override;
 
   // Methods for |DataSocket|
   //
 
   void SendSocketData(UnixSocketIOBuffer* aBuffer) override;
 
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -7,18 +7,16 @@
 ifeq (,$(MAKE_VERSION))
 $(error GNU Make is required)
 endif
 make_min_ver := 3.81
 ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
 $(error GNU Make $(make_min_ver) or higher is required)
 endif
 
-TOPLEVEL_BUILD := 1
-
 run_for_side_effects := $(shell echo 'MAKE: $(MAKE)')
 
 ifdef JS_HAS_CTYPES
 ifdef MOZ_NATIVE_FFI
 LOCAL_INCLUDES = $(MOZ_FFI_CFLAGS)
 endif
 
 # Windows needs this to be linked with a static library.
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -27,16 +27,17 @@ builtin(include, ../../build/autoconf/ar
 builtin(include, ../../build/autoconf/android.m4)dnl
 builtin(include, ../../build/autoconf/zlib.m4)dnl
 builtin(include, ../../build/autoconf/linux.m4)dnl
 builtin(include, ../../build/autoconf/python-virtualenv.m4)dnl
 builtin(include, ../../build/autoconf/winsdk.m4)dnl
 builtin(include, ../../build/autoconf/icu.m4)dnl
 builtin(include, ../../build/autoconf/ffi.m4)dnl
 builtin(include, ../../build/autoconf/clang-plugin.m4)dnl
+builtin(include, ../../build/autoconf/alloc.m4)dnl
 
 define([__MOZ_AC_INIT_PREPARE], defn([AC_INIT_PREPARE]))
 define([AC_INIT_PREPARE],
 [if test -z "$srcdir"; then
   srcdir=`dirname "[$]0"`
 fi
 srcdir="$srcdir/../.."
 __MOZ_AC_INIT_PREPARE($1)
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3842,28 +3842,19 @@ dnl top-level configure may override thi
 MOZ_CONFIG_ICU()
 
 MOZ_SUBCONFIGURE_ICU()
 
 dnl ========================================================
 dnl JavaScript shell
 dnl ========================================================
 
-MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h"
-MALLOC_H=
-
-for file in $MALLOC_HEADERS; do
-  MOZ_CHECK_HEADER($file, [MALLOC_H=$file])
-  if test "$MALLOC_H" != ""; then
-    AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>)
-    break
-  fi
-done
-
-AC_CHECK_FUNCS(setlocale localeconv malloc_size malloc_usable_size)
+MOZ_CHECK_ALLOCATOR
+
+AC_CHECK_FUNCS(setlocale localeconv)
 
 AC_SUBST(MOZILLA_VERSION)
 
 AC_SUBST(ac_configure_args)
 
 AC_SUBST(TOOLCHAIN_PREFIX)
 
 if test -n "$JS_STANDALONE"; then
--- a/js/src/devtools/rootAnalysis/analyzeRoots.js
+++ b/js/src/devtools/rootAnalysis/analyzeRoots.js
@@ -3,20 +3,27 @@
 "use strict";
 
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 loadRelativeToScript('CFG.js');
 
 var sourceRoot = (os.getenv('SOURCE') || '') + '/'
 
+var functionName;
 var functionBodies;
 
 if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string')
-    throw "Usage: analyzeRoots.js <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> [start end [tmpfile]]";
+    throw "Usage: analyzeRoots.js [-f function_name] <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> [start end [tmpfile]]";
+
+var theFunctionNameToFind;
+if (scriptArgs[0] == '--function') {
+    theFunctionNameToFind = scriptArgs[1];
+    scriptArgs = scriptArgs.slice(2);
+}
 
 var gcFunctionsFile = scriptArgs[0];
 var gcEdgesFile = scriptArgs[1];
 var suppressedFunctionsFile = scriptArgs[2];
 var gcTypesFile = scriptArgs[3];
 var batch = (scriptArgs[4]|0) || 1;
 var numBatches = (scriptArgs[5]|0) || 1;
 var tmpfile = scriptArgs[6] || "tmp.txt";
@@ -204,17 +211,17 @@ function edgeTakesVariableAddress(edge, 
         return false;
     default:
         return false;
     }
 }
 
 function edgeKillsVariable(edge, variable)
 {
-    // Direct assignments kill their lhs.
+    // Direct assignments kill their lhs: var = value
     if (edge.Kind == "Assign") {
         var lhs = edge.Exp[0];
         if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable))
             return !isReturningImmobileValue(edge, variable);
     }
 
     if (edge.Kind != "Call")
         return false;
@@ -291,105 +298,143 @@ function edgeCanGC(edge)
             return null;
         return (fullFieldName in suppressedFunctions) ? null : fullFieldName;
     }
     assert(callee.Exp[0].Kind == "Var");
     var varName = callee.Exp[0].Variable.Name[0];
     return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
 }
 
-function variableUsePrecedesGC(suppressed, variable, worklist)
+// Search recursively through predecessors from a variable use, returning
+// whether a GC call is reachable (in the reverse direction; this means that
+// the variable use is reachable from the GC call, and therefore the variable
+// is live after the GC call), along with some additional information. What
+// info we want depends on whether the variable turns out to be live across any
+// GC call. We are looking for both hazards (unrooted variables live across GC
+// calls) and unnecessary roots (rooted variables that have no GC calls in
+// their live ranges.)
+//
+// If not:
+//
+//  - 'minimumUse': the earliest point in each body that uses the variable, for
+//    reporting on unnecessary roots.
+//
+// If so:
+//
+//  - 'why': a path from the GC call to a use of the variable after the GC
+//    call, chained through a 'why' field in the returned edge descriptor
+//
+//  - 'gcInfo': a direct pointer to the GC call edge
+//
+function findGCBeforeVariableUse(suppressed, variable, worklist)
 {
     // Scan through all edges preceding an unrooted variable use, using an
-    // explicit worklist. A worklist contains an incoming edge together with a
-    // description of where it or one of its successors GC'd (if any).
+    // explicit worklist, looking for a GC call. A worklist contains an
+    // incoming edge together with a description of where it or one of its
+    // successors GC'd (if any).
 
     while (worklist.length) {
         var entry = worklist.pop();
-        var body = entry.body, ppoint = entry.ppoint;
+        var { body, ppoint, gcInfo } = entry;
 
         if (body.seen) {
             if (ppoint in body.seen) {
                 var seenEntry = body.seen[ppoint];
-                if (!entry.gcInfo || seenEntry.gcInfo)
+                if (!gcInfo || seenEntry.gcInfo)
                     continue;
             }
         } else {
             body.seen = [];
         }
-        body.seen[ppoint] = {body:body, gcInfo:entry.gcInfo};
+        body.seen[ppoint] = {body: body, gcInfo: gcInfo};
 
         if (ppoint == body.Index[0]) {
             if (body.BlockId.Kind == "Loop") {
                 // propagate to parents that enter the loop body.
                 if ("BlockPPoint" in body) {
                     for (var parent of body.BlockPPoint) {
                         var found = false;
                         for (var xbody of functionBodies) {
                             if (sameBlockId(xbody.BlockId, parent.BlockId)) {
                                 assert(!found);
                                 found = true;
-                                worklist.push({body:xbody, ppoint:parent.Index,
-                                               gcInfo:entry.gcInfo, why:entry});
+                                worklist.push({body: xbody, ppoint: parent.Index,
+                                               gcInfo: gcInfo, why: entry});
                             }
                         }
                         assert(found);
                     }
                 }
-            } else if (variable.Kind == "Arg" && entry.gcInfo) {
+            } else if (variable.Kind == "Arg" && gcInfo) {
                 // The scope of arguments starts at the beginning of the
                 // function
-                return {gcInfo:entry.gcInfo, why:entry};
+                return {gcInfo: gcInfo, why: entry};
             }
         }
 
         var predecessors = getPredecessors(body);
         if (!(ppoint in predecessors))
             continue;
 
         for (var edge of predecessors[ppoint]) {
             var source = edge.Index[0];
 
-            if (edgeKillsVariable(edge, variable)) {
-                if (entry.gcInfo)
-                    return {gcInfo: entry.gcInfo, why: {body:body, ppoint:source, gcInfo:entry.gcInfo, why:entry } }
+            var edge_kills = edgeKillsVariable(edge, variable);
+            var edge_uses = edgeUsesVariable(edge, variable, body);
+
+            if (edge_kills || edge_uses) {
                 if (!body.minimumUse || source < body.minimumUse)
                     body.minimumUse = source;
+            }
+
+            if (edge_kills) {
+                // This is a beginning of the variable's live range. If we can
+                // reach a GC call from here, then we're done -- we have a path
+                // from the beginning of the live range, through the GC call,
+                // to a use after the GC call that proves its live range
+                // extends at least that far.
+                if (gcInfo)
+                    return {gcInfo: gcInfo, why: {body: body, ppoint: source, gcInfo: gcInfo, why: entry } }
+
+                // Otherwise, we want to continue searching for the true
+                // minimumUse, for use in reporting unnecessary rooting, but we
+                // truncate this particular branch of the search at this edge.
                 continue;
             }
 
-            var gcInfo = entry.gcInfo;
             if (!gcInfo && !(source in body.suppressed) && !suppressed) {
                 var gcName = edgeCanGC(edge, body);
                 if (gcName)
                     gcInfo = {name:gcName, body:body, ppoint:source};
             }
 
-            if (edgeUsesVariable(edge, variable, body)) {
+            if (edge_uses) {
+                // The live range starts at least this far back, so we're done
+                // for the same reason as with edge_kills.
                 if (gcInfo)
                     return {gcInfo:gcInfo, why:entry};
-                if (!body.minimumUse || source < body.minimumUse)
-                    body.minimumUse = source;
             }
 
             if (edge.Kind == "Loop") {
-                // propagate to exit points of the loop body, in addition to the
-                // predecessor of the loop edge itself.
+                // Additionally propagate the search into a loop body, starting
+                // with the exit point.
                 var found = false;
                 for (var xbody of functionBodies) {
                     if (sameBlockId(xbody.BlockId, edge.BlockId)) {
                         assert(!found);
                         found = true;
                         worklist.push({body:xbody, ppoint:xbody.Index[1],
                                        gcInfo:gcInfo, why:entry});
                     }
                 }
                 assert(found);
                 break;
             }
+
+            // Propagate the search to the predecessors of this edge.
             worklist.push({body:body, ppoint:source, gcInfo:gcInfo, why:entry});
         }
     }
 
     return null;
 }
 
 function variableLiveAcrossGC(suppressed, variable)
@@ -402,24 +447,31 @@ function variableLiveAcrossGC(suppressed
         body.minimumUse = 0;
     }
 
     for (var body of functionBodies) {
         if (!("PEdge" in body))
             continue;
         for (var edge of body.PEdge) {
             var usePoint = edgeUsesVariable(edge, variable, body);
+            // Example for !edgeKillsVariable:
+            //
+            //   JSObject* obj = NewObject();
+            //   cangc();
+            //   obj = NewObject();    <-- uses 'obj', but kills previous value
+            //
             if (usePoint && !edgeKillsVariable(edge, variable)) {
                 // Found a use, possibly after a GC.
                 var worklist = [{body:body, ppoint:usePoint, gcInfo:null, why:null}];
-                var call = variableUsePrecedesGC(suppressed, variable, worklist);
-                if (call) {
-                    call.afterGCUse = usePoint;
-                    return call;
-                }
+                var call = findGCBeforeVariableUse(suppressed, variable, worklist);
+                if (!call)
+                    continue;
+
+                call.afterGCUse = usePoint;
+                return call;
             }
         }
     }
     return null;
 }
 
 // An unrooted variable has its address stored in another variable via
 // assignment, or passed into a function that can GC. If the address is
@@ -630,34 +682,38 @@ var end = Math.min(minStream + each * ba
 
 // For debugging: Set this variable to the function name you're interested in
 // debugging and run once. That will print out the nameIndex of that function.
 // Insert that into the following statement to go directly to just that
 // function. Add your debugging printouts or debugger; statements or whatever.
 var theFunctionNameToFind;
 // var start = end = 12345;
 
-for (var nameIndex = start; nameIndex <= end; nameIndex++) {
-    var name = xdb.read_key(nameIndex);
-    var functionName = name.readString();
-    var data = xdb.read_entry(name);
-    xdb.free_string(name);
-    var json = data.readString();
-    xdb.free_string(data);
+function process(name, json) {
+    functionName = name;
     functionBodies = JSON.parse(json);
 
-    if (theFunctionNameToFind) {
-        if (functionName == theFunctionNameToFind) {
-            printErr("nameIndex = " + nameIndex);
-            quit(1);
-        } else {
-            continue;
-        }
-    }
-
     for (var body of functionBodies)
         body.suppressed = [];
     for (var body of functionBodies) {
         for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor))
             pbody.suppressed[id] = true;
     }
     processBodies(functionName);
 }
+
+if (theFunctionNameToFind) {
+    var data = xdb.read_entry(theFunctionNameToFind);
+    var json = data.readString();
+    process(theFunctionNameToFind, json);
+    xdb.free_string(data);
+    quit(0);
+}
+
+for (var nameIndex = start; nameIndex <= end; nameIndex++) {
+    var name = xdb.read_key(nameIndex);
+    var functionName = name.readString();
+    var data = xdb.read_entry(name);
+    xdb.free_string(name);
+    var json = data.readString();
+    process(functionName, json);
+    xdb.free_string(data);
+}
--- a/js/src/jit-test/tests/asm.js/testBug1164391.js
+++ b/js/src/jit-test/tests/asm.js/testBug1164391.js
@@ -1,8 +1,11 @@
+if (!this.SharedArrayBuffer)
+    quit(0);
+
 function m(stdlib, ffi, heap) {
     "use asm";
     var HEAP32 = new stdlib.SharedInt32Array(heap);
     var add = stdlib.Atomics.add;
     var load = stdlib.Atomics.load;
     function add_sharedEv(i1) {
 	i1 = i1 | 0;
 	load(HEAP32, i1 >> 2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1139376.js
@@ -0,0 +1,13 @@
+// |jit-test| error:ReferenceError
+
+(function() {
+    var $10=0;
+    while (1) {
+      switch (stack.label & 2) {
+       case 1:
+        return $8|0;
+       case 49:
+        if ($10) {}
+    }
+  }
+})()()
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -166,17 +166,17 @@ void DisableChannel(JitSpewChannel chann
 void EnableIonDebugSyncLogging();
 void EnableIonDebugAsyncLogging();
 
 #else
 
 class GraphSpewer
 {
   public:
-    GraphSpewer(TempAllocator *alloc) { }
+    explicit GraphSpewer(TempAllocator *alloc) { }
 
     bool isSpewing() { return false; }
     void init(MIRGraph* graph, JSScript* function) { }
     void beginFunction(JSScript* function) { }
     void spewPass(const char* pass) { }
     void spewPass(const char* pass, BacktrackingAllocator* ra) { }
     void endFunction() { }
 
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -3330,25 +3330,26 @@ RangeAnalysis::prepareForUCE(bool* shoul
                 test->id());
 
         *shouldRemoveDeadCode = true;
     }
 
     return tryRemovingGuards();
 }
 
-bool RangeAnalysis::tryRemovingGuards() {
-
+bool RangeAnalysis::tryRemovingGuards()
+{
     MDefinitionVector guards(alloc());
 
     for (ReversePostorderIterator block = graph_.rpoBegin(); block != graph_.rpoEnd(); block++) {
         for (MInstructionReverseIterator iter = block->rbegin(); iter != block->rend(); iter++) {
             if (!iter->isGuardRangeBailouts())
                 continue;
 
+            iter->setInWorklist();
             if (!guards.append(*iter))
                 return false;
         }
     }
 
     // Flag all fallible instructions which were indirectly used in the
     // computation of the condition, such that we do not ignore
     // bailout-paths which are used to shrink the input range of the
@@ -3359,46 +3360,56 @@ bool RangeAnalysis::tryRemovingGuards() 
 #ifdef DEBUG
         // There is no need to mark an instructions if there is
         // already a more restrictive flag on it.
         guard->setNotGuardRangeBailouts();
         MOZ_ASSERT(DeadIfUnused(guard));
         guard->setGuardRangeBailouts();
 #endif
 
-        if (!guard->range())
-            continue;
-
-        // Filter the range of the instruction based on its MIRType.
-        Range typeFilteredRange(guard);
-
-        // If the output range is updated by adding the inner range,
-        // then the MIRType act as an effectful filter. As we do not know if
-        // this filtered Range might change or not the result of the
-        // previous comparison, we have to keep this instruction as a guard
-        // because it has to bailout in order to restrict the Range to its
-        // MIRType.
-        if (typeFilteredRange.update(guard->range()))
-            continue;
+        if (!guard->isPhi()) {
+            if (!guard->range())
+                continue;
+
+            // Filter the range of the instruction based on its MIRType.
+            Range typeFilteredRange(guard);
+
+            // If the output range is updated by adding the inner range,
+            // then the MIRType act as an effectful filter. As we do not know if
+            // this filtered Range might change or not the result of the
+            // previous comparison, we have to keep this instruction as a guard
+            // because it has to bailout in order to restrict the Range to its
+            // MIRType.
+            if (typeFilteredRange.update(guard->range()))
+                continue;
+        }
 
         guard->setNotGuardRangeBailouts();
 
         // Propagate the guard to its operands.
         for (size_t op = 0, e = guard->numOperands(); op < e; op++) {
             MDefinition* operand = guard->getOperand(op);
 
             // Already marked.
-            if (operand->isGuardRangeBailouts())
+            if (operand->isInWorklist())
                 continue;
 
+            MOZ_ASSERT(!operand->isGuardRangeBailouts());
+
             // No need to mark as a guard, since it is has already an even more
             // restrictive flag set.
             if (!DeadIfUnused(operand))
                 continue;
 
+            operand->setInWorklist();
             operand->setGuardRangeBailouts();
             if (!guards.append(operand))
                 return false;
         }
     }
 
+    for (size_t i = 0; i < guards.length(); i++) {
+        MDefinition* guard = guards[i];
+        guard->setNotInWorklist();
+    }
+
     return true;
 }
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -677,17 +677,17 @@ ValueNumberer::loopHasOptimizablePhi(MBa
     // If the header is unreachable, don't bother re-optimizing it.
     if (header->isMarked())
         return false;
 
     // Rescan the phis for any that can be simplified, since they may be reading
     // values from backedges.
     for (MPhiIterator iter(header->phisBegin()), end(header->phisEnd()); iter != end; ++iter) {
         MPhi* phi = *iter;
-        MOZ_ASSERT(phi->hasUses(), "Missed an unused phi");
+        MOZ_ASSERT_IF(!phi->hasUses(), !DeadIfUnused(phi));
 
         if (phi->operandIfRedundant() || hasLeader(phi, header))
             return true; // Phi can be simplified.
     }
     return false;
 }
 
 // Visit |def|.
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -262,16 +262,20 @@ static const JSFunctionSpecWithHelp osfi
 "  Read filename into returned string. Filename is relative to the current\n"
                "  working directory."),
 
     JS_FN_HELP("readRelativeToScript", osfile_readRelativeToScript, 1, 0,
 "readRelativeToScript(filename, [\"binary\"])",
 "  Read filename into returned string. Filename is relative to the directory\n"
 "  containing the current script."),
 
+    JS_FS_HELP_END
+};
+
+static const JSFunctionSpecWithHelp osfile_unsafe_functions[] = {
     JS_FN_HELP("redirect", osfile_redirect, 2, 0,
 "redirect(stdoutFilename[, stderrFilename])",
 "  Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
 "   redirecting. Filenames are relative to the current working directory."),
 
     JS_FS_HELP_END
 };
 
@@ -561,16 +565,21 @@ DefineOS(JSContext* cx, HandleObject glo
     RootedObject osfile(cx, JS_NewPlainObject(cx));
     if (!osfile ||
         !JS_DefineFunctionsWithHelp(cx, osfile, osfile_functions) ||
         !JS_DefineProperty(cx, obj, "file", osfile, 0))
     {
         return false;
     }
 
+    if (!fuzzingSafe) {
+        if (!JS_DefineFunctionsWithHelp(cx, osfile, osfile_unsafe_functions))
+            return false;
+    }
+
     // For backwards compatibility, expose various os.file.* functions as
     // direct methods on the global.
     RootedValue val(cx);
 
     struct {
         const char* src;
         const char* dst;
     } osfile_exports[] = {
@@ -578,18 +587,20 @@ DefineOS(JSContext* cx, HandleObject glo
         { "readFile", "snarf" },
         { "readRelativeToScript", "readRelativeToScript" },
         { "redirect", "redirect" }
     };
 
     for (auto pair : osfile_exports) {
         if (!JS_GetProperty(cx, osfile, pair.src, &val))
             return false;
-        RootedObject function(cx, &val.toObject());
-        if (!JS_DefineProperty(cx, global, pair.dst, function, 0))
-            return false;
+        if (val.isObject()) {
+            RootedObject function(cx, &val.toObject());
+            if (!JS_DefineProperty(cx, global, pair.dst, function, 0))
+                return false;
+        }
     }
 
     return true;
 }
 
 }
 }
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -2388,17 +2388,18 @@ nsCSSOffsetState::InitOffsets(const Logi
     nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame);
 
     if (tableFrame->IsBorderCollapse()) {
       // border-collapsed tables don't use any of their padding, and
       // only part of their border.  We need to do this here before we
       // try to do anything like handling 'auto' widths,
       // 'box-sizing', or 'auto' margins.
       ComputedPhysicalPadding().SizeTo(0,0,0,0);
-      ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder();
+      SetComputedLogicalBorderPadding(
+        tableFrame->GetIncludedOuterBCBorder(mWritingMode));
     }
 
     // The margin is inherited to the outer table frame via
     // the ::-moz-table-outer rule in ua.css.
     ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
   } else if (aFrameType == nsGkAtoms::scrollbarFrame) {
     // scrollbars may have had their width or height smashed to zero
     // by the associated scrollframe, in which case we must not report
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -74,35 +74,36 @@ struct nsTableReflowState {
                      nscoord                  aAvailWidth,
                      nscoord                  aAvailHeight)
     : reflowState(aReflowState)
   {
     MOZ_ASSERT(reflowState.frame->GetType() == nsGkAtoms::tableFrame,
                "nsTableReflowState should only be created for nsTableFrame");
     nsTableFrame* table =
       static_cast<nsTableFrame*>(reflowState.frame->FirstInFlow());
-    nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
-
-    x = borderPadding.left + table->GetColSpacing(-1);
-    y = borderPadding.top; //cellspacing added during reflow
+    WritingMode wm = aReflowState.GetWritingMode();
+    LogicalMargin borderPadding = table->GetChildAreaOffset(wm, &reflowState);
+
+    x = borderPadding.IStart(wm) + table->GetColSpacing(-1);
+    y = borderPadding.BStart(wm); //cellspacing added during reflow
 
     availSize.width  = aAvailWidth;
     if (NS_UNCONSTRAINEDSIZE != availSize.width) {
       int32_t colCount = table->GetColCount();
-      availSize.width -= borderPadding.left + borderPadding.right
-                         + table->GetColSpacing(-1)
-                         + table->GetColSpacing(colCount);
+      availSize.width -= borderPadding.IStartEnd(wm) +
+                         table->GetColSpacing(-1) +
+                         table->GetColSpacing(colCount);
       availSize.width = std::max(0, availSize.width);
     }
 
     availSize.height = aAvailHeight;
     if (NS_UNCONSTRAINEDSIZE != availSize.height) {
-      availSize.height -= borderPadding.top + borderPadding.bottom
-                          + table->GetRowSpacing(-1)
-                          + table->GetRowSpacing(table->GetRowCount());
+      availSize.height -= borderPadding.BStartEnd(wm) +
+                          table->GetRowSpacing(-1) +
+                          table->GetRowSpacing(table->GetRowCount());
       availSize.height = std::max(0, availSize.height);
     }
   }
 };
 
 /********************************************************************************
  ** nsTableFrame                                                               **
  ********************************************************************************/
@@ -1345,17 +1346,18 @@ nsTableFrame::BuildDisplayList(nsDisplay
 
 nsMargin
 nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
 {
   if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
       !IsBorderCollapse())
     return nsMargin(0,0,0,0);
 
-  return GetOuterBCBorder();
+  WritingMode wm = GetWritingMode();
+  return GetOuterBCBorder(wm).GetPhysicalMargin(wm);
 }
 
 // XXX We don't put the borders and backgrounds in tree order like we should.
 // That requires some major surgery which we aren't going to do right now.
 DrawResult
 nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
                                          const nsRect& aDirtyRect,
                                          nsPoint aPt, uint32_t aBGPaintFlags)
@@ -1413,31 +1415,31 @@ nsTableFrame::GetLogicalSkipSides(const 
   }
   if (nullptr != GetNextInFlow()) {
     skip |= eLogicalSideBitsBEnd;
   }
   return skip;
 }
 
 void
-nsTableFrame::SetColumnDimensions(nscoord         aHeight,
-                                  const nsMargin& aBorderPadding)
-{
-  nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
+nsTableFrame::SetColumnDimensions(nscoord aHeight, WritingMode aWM,
+                                  const LogicalMargin& aBorderPadding)
+{
+  nscoord colHeight = aHeight -= aBorderPadding.BStartEnd(aWM) +
                                  GetRowSpacing(-1) +
                                  GetRowSpacing(GetRowCount());
 
   nsTableIterator iter(mColGroups);
   nsIFrame* colGroupFrame = iter.First();
   bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
   int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1);
   nscoord cellSpacingX = GetColSpacing(colX);
   int32_t tableColIncr = tableIsLTR ? 1 : -1;
-  nsPoint colGroupOrigin(aBorderPadding.left + GetColSpacing(-1),
-                         aBorderPadding.top + GetRowSpacing(-1));
+  nsPoint colGroupOrigin(aBorderPadding.IStart(aWM) + GetColSpacing(-1),
+                         aBorderPadding.BStart(aWM) + GetRowSpacing(-1));
   while (colGroupFrame) {
     MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame);
     nscoord colGroupWidth = 0;
     nsTableIterator iterCol(*colGroupFrame);
     nsIFrame* colFrame = iterCol.First();
     nsPoint colOrigin(0,0);
     while (colFrame) {
       if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
@@ -1547,18 +1549,19 @@ nsTableFrame::IntrinsicISizeOffsets(nsRe
 
   result.hMargin = 0;
   result.hPctMargin = 0;
 
   if (IsBorderCollapse()) {
     result.hPadding = 0;
     result.hPctPadding = 0;
 
-    nsMargin outerBC = GetIncludedOuterBCBorder();
-    result.hBorder = outerBC.LeftRight();
+    WritingMode wm = GetWritingMode();
+    LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
+    result.hBorder = outerBC.IStartEnd(wm);
   }
 
   return result;
 }
 
 /* virtual */
 LogicalSize
 nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext,
@@ -1780,16 +1783,17 @@ nsTableFrame::Reflow(nsPresContext*     
                      nsHTMLReflowMetrics&     aDesiredSize,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   bool isPaginated = aPresContext->IsPaginated();
+  WritingMode wm = aReflowState.GetWritingMode();
 
   aStatus = NS_FRAME_COMPLETE;
   if (!GetPrevInFlow() && !mTableLayoutStrategy) {
     NS_ERROR("strategy should have been created in Init");
     return;
   }
 
   // see if collapsing borders need to be calculated
@@ -1872,18 +1876,18 @@ nsTableFrame::Reflow(nsPresContext*     
       CalcDesiredHeight(aReflowState, aDesiredSize);
       mutable_rs.mFlags.mSpecialHeightReflow = true;
 
       ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(),
                   lastChildReflowed, aStatus);
 
       if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
         // if there is an incomplete child, then set the desired height to include it but not the next one
-        nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
-        aDesiredSize.Height() = borderPadding.bottom + GetRowSpacing(GetRowCount()) +
+        LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState);
+        aDesiredSize.Height() = borderPadding.BEnd(wm) + GetRowSpacing(GetRowCount()) +
                               lastChildReflowed->GetNormalRect().YMost();
       }
       haveDesiredHeight = true;
 
       mutable_rs.mFlags.mSpecialHeightReflow = false;
     }
   }
   else {
@@ -1897,34 +1901,34 @@ nsTableFrame::Reflow(nsPresContext*     
                        aReflowState.ComputedPhysicalBorderPadding().LeftRight();
   if (!haveDesiredHeight) {
     CalcDesiredHeight(aReflowState, aDesiredSize);
   }
   if (IsRowInserted()) {
     ProcessRowInserted(aDesiredSize.Height());
   }
 
-  nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
-  SetColumnDimensions(aDesiredSize.Height(), borderPadding);
+  LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState);
+  SetColumnDimensions(aDesiredSize.Height(), wm, borderPadding);
   if (NeedToCollapse() &&
       (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) {
-    AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
+    AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding);
   }
 
   // If there are any relatively-positioned table parts, we need to reflow their
   // absolutely-positioned descendants now that their dimensions are final.
   FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState);
 
   // make sure the table overflow area does include the table rect.
   nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ;
 
   if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
     // collapsed border may leak out
-    nsMargin bcMargin = GetExcludedOuterBCBorder();
-    tableRect.Inflate(bcMargin);
+    LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
+    tableRect.Inflate(bcMargin.GetPhysicalMargin(wm));
   }
   aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
 
   if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
       nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
       nsIFrame::InvalidateFrame();
   }
 
@@ -2002,18 +2006,19 @@ nsTableFrame::FixupPositionedTableParts(
 bool
 nsTableFrame::UpdateOverflow()
 {
   nsRect bounds(nsPoint(0, 0), GetSize());
 
   // As above in Reflow, make sure the table overflow area includes the table
   // rect, and check for collapsed borders leaking out.
   if (!ShouldApplyOverflowClipping(this, StyleDisplay())) {
-    nsMargin bcMargin = GetExcludedOuterBCBorder();
-    bounds.Inflate(bcMargin);
+    WritingMode wm = GetWritingMode();
+    LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
+    bounds.Inflate(bcMargin.GetPhysicalMargin(wm));
   }
 
   nsOverflowAreas overflowAreas(bounds, bounds);
   nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
 
   return FinishAndStoreOverflow(overflowAreas, GetSize());
 }
 
@@ -2118,31 +2123,32 @@ nsTableFrame::PushChildren(const RowGrou
     SetOverflowFrames(frames);
   }
 }
 
 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
 // reflow so that it has no effect on the calculations of reflow.
 void
 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
-                                          nsMargin             aBorderPadding)
+                                          const WritingMode aWM,
+                                          const LogicalMargin& aBorderPadding)
 {
   nscoord yTotalOffset = 0; // total offset among all rows in all row groups
 
   // reset the bit, it will be set again if row/rowgroup or col/colgroup are
   // collapsed
   SetNeedToCollapse(false);
 
   // collapse the rows and/or row groups as necessary
   // Get the ordered children
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
 
   nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
-  nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding);
+  nscoord width = firstInFlow->GetCollapsedWidth(aWM, aBorderPadding);
   nscoord rgWidth = width - GetColSpacing(-1) -
                     GetColSpacing(GetColCount());
   nsOverflowAreas overflow;
   // Walk the list of children
   for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[childX];
     NS_ASSERTION(rgFrame, "Must have row group frame here");
     yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
@@ -2153,21 +2159,22 @@ nsTableFrame::AdjustForCollapsingRowsCol
   aDesiredSize.Width() = width;
   overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
   FinishAndStoreOverflow(overflow,
                          nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
 }
 
 
 nscoord
-nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
+nsTableFrame::GetCollapsedWidth(const WritingMode aWM,
+                                const LogicalMargin& aBorderPadding)
 {
   NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow");
   nscoord width = GetColSpacing(GetColCount());
-  width += aBorderPadding.left + aBorderPadding.right;
+  width += aBorderPadding.IStartEnd(aWM);
   for (nsIFrame* groupFrame : mColGroups) {
     const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
     bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
     nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
     for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
          colFrame = colFrame->GetNextCol()) {
       const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
       int32_t colX = colFrame->GetColIndex();
@@ -2545,17 +2552,18 @@ nsTableFrame::RemoveFrame(ChildListID   
 }
 
 /* virtual */ nsMargin
 nsTableFrame::GetUsedBorder() const
 {
   if (!IsBorderCollapse())
     return nsContainerFrame::GetUsedBorder();
 
-  return GetIncludedOuterBCBorder();
+  WritingMode wm = GetWritingMode();
+  return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm);
 }
 
 /* virtual */ nsMargin
 nsTableFrame::GetUsedPadding() const
 {
   if (!IsBorderCollapse())
     return nsContainerFrame::GetUsedPadding();
 
@@ -2590,84 +2598,82 @@ static void
 DivideBCBorderSize(BCPixelSize  aPixelSize,
                    BCPixelSize& aSmallHalf,
                    BCPixelSize& aLargeHalf)
 {
   aSmallHalf = aPixelSize / 2;
   aLargeHalf = aPixelSize - aSmallHalf;
 }
 
-nsMargin
-nsTableFrame::GetOuterBCBorder() const
+LogicalMargin
+nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const
 {
   if (NeedToCalcBCBorders())
     const_cast<nsTableFrame*>(this)->CalcBCBorders();
 
-  nsMargin border(0, 0, 0, 0);
   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
   BCPropertyData* propData = GetBCProperty();
   if (propData) {
-    border.top = BC_BORDER_START_HALF_COORD(p2t, propData->mTopBorderWidth);
-    border.right = BC_BORDER_END_HALF_COORD(p2t, propData->mRightBorderWidth);
-    border.bottom = BC_BORDER_END_HALF_COORD(p2t, propData->mBottomBorderWidth);
-    border.left = BC_BORDER_START_HALF_COORD(p2t, propData->mLeftBorderWidth);
-  }
-  return border;
-}
-
-nsMargin
-nsTableFrame::GetIncludedOuterBCBorder() const
+    return LogicalMargin(
+      aWM,
+      BC_BORDER_START_HALF_COORD(p2t, propData->mTopBorderWidth),
+      BC_BORDER_END_HALF_COORD(p2t, propData->mRightBorderWidth),
+      BC_BORDER_END_HALF_COORD(p2t, propData->mBottomBorderWidth),
+      BC_BORDER_START_HALF_COORD(p2t, propData->mLeftBorderWidth));
+  }
+  return LogicalMargin(GetWritingMode());
+}
+
+LogicalMargin
+nsTableFrame::GetIncludedOuterBCBorder(const WritingMode aWM) const
 {
   if (NeedToCalcBCBorders())
     const_cast<nsTableFrame*>(this)->CalcBCBorders();
 
-  nsMargin border(0, 0, 0, 0);
   int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
   BCPropertyData* propData = GetBCProperty();
   if (propData) {
-    border.top += BC_BORDER_START_HALF_COORD(p2t, propData->mTopBorderWidth);
-    border.right += BC_BORDER_END_HALF_COORD(p2t, propData->mRightCellBorderWidth);
-    border.bottom += BC_BORDER_END_HALF_COORD(p2t, propData->mBottomBorderWidth);
-    border.left += BC_BORDER_START_HALF_COORD(p2t, propData->mLeftCellBorderWidth);
-  }
-  return border;
-}
-
-nsMargin
-nsTableFrame::GetExcludedOuterBCBorder() const
-{
-  return GetOuterBCBorder() - GetIncludedOuterBCBorder();
-}
-
-static
-void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
-                                   nsStyleContext&          aStyleContext,
-                                   nsMargin&                aBorderPadding)
+    return LogicalMargin(
+      aWM,
+      BC_BORDER_START_HALF_COORD(p2t, propData->mTopBorderWidth),
+      BC_BORDER_END_HALF_COORD(p2t, propData->mRightCellBorderWidth),
+      BC_BORDER_END_HALF_COORD(p2t, propData->mBottomBorderWidth),
+      BC_BORDER_START_HALF_COORD(p2t, propData->mLeftCellBorderWidth));
+  }
+  return LogicalMargin(GetWritingMode());
+}
+
+LogicalMargin
+nsTableFrame::GetExcludedOuterBCBorder(const WritingMode aWM) const
+{
+  return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM);
+}
+
+static LogicalMargin
+GetSeparateModelBorderPadding(const WritingMode aWM,
+                              const nsHTMLReflowState* aReflowState,
+                              nsStyleContext* aStyleContext)
 {
   // XXXbz Either we _do_ have a reflow state and then we can use its
   // mComputedBorderPadding or we don't and then we get the padding
   // wrong!
-  const nsStyleBorder* border = aStyleContext.StyleBorder();
-  aBorderPadding = border->GetComputedBorder();
+  const nsStyleBorder* border = aStyleContext->StyleBorder();
+  LogicalMargin borderPadding(aWM, border->GetComputedBorder());
   if (aReflowState) {
-    aBorderPadding += aReflowState->ComputedPhysicalPadding();
-  }
-}
-
-nsMargin
-nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
-{
-  nsMargin offset(0,0,0,0);
-  if (IsBorderCollapse()) {
-    offset = GetIncludedOuterBCBorder();
-  }
-  else {
-    GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
-  }
-  return offset;
+    borderPadding += aReflowState->ComputedLogicalPadding();
+  }
+  return borderPadding;
+}
+
+LogicalMargin
+nsTableFrame::GetChildAreaOffset(const WritingMode aWM,
+                                 const nsHTMLReflowState* aReflowState) const
+{
+  return IsBorderCollapse() ? GetIncludedOuterBCBorder(aWM) :
+    GetSeparateModelBorderPadding(aWM, aReflowState, mStyleContext);
 }
 
 void
 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
 {
   nsMargin collapseBorder;
   nsMargin padding(0,0,0,0);
   nsMargin* pCollapseBorder = nullptr;
@@ -3213,17 +3219,18 @@ nsTableFrame::CalcDesiredHeight(const ns
                                 nsHTMLReflowMetrics& aDesiredSize)
 {
   nsTableCellMap* cellMap = GetCellMap();
   if (!cellMap) {
     NS_ERROR("never ever call me until the cell map is built!");
     aDesiredSize.Height() = 0;
     return;
   }
-  nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
+  WritingMode wm = aReflowState.GetWritingMode();
+  LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState);
 
   // get the natural height based on the last child's (row group) rect
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
   if (rowGroups.IsEmpty()) {
     // tables can be used as rectangular items without content
     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
     if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
@@ -3233,17 +3240,17 @@ nsTableFrame::CalcDesiredHeight(const ns
       aDesiredSize.Height() = tableSpecifiedHeight;
     }
     else
       aDesiredSize.Height() = 0;
     return;
   }
   int32_t rowCount = cellMap->GetRowCount();
   int32_t colCount = cellMap->GetColCount();
-  nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
+  nscoord desiredHeight = borderPadding.BStartEnd(wm);
   if (rowCount > 0 && colCount > 0) {
     desiredHeight += GetRowSpacing(-1);
     for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) {
       desiredHeight += rowGroups[rgX]->GetSize().height +
                        GetRowSpacing(rowGroups[rgX]->GetRowCount() +
                                      rowGroups[rgX]->GetStartRowIndex());
     }
   }
@@ -3296,27 +3303,28 @@ void ResizeCells(nsTableFrame& aTableFra
   }
   aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
 }
 
 void
 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
                                      nscoord                  aAmount)
 {
-  nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
+  WritingMode wm = aReflowState.GetWritingMode();
+  LogicalMargin borderPadding = GetChildAreaOffset(wm, &aReflowState);
 
   RowGroupArray rowGroups;
   OrderRowGroups(rowGroups);
 
   nscoord amountUsed = 0;
   // distribute space to each pct height row whose row group doesn't have a computed
   // height, and base the pct on the table height. If the row group had a computed
   // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
   nscoord pctBasis = aReflowState.ComputedHeight() - GetRowSpacing(-1, GetRowCount());
-  nscoord yOriginRG = borderPadding.top + GetRowSpacing(0);
+  nscoord yOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0);
   nscoord yEndRG = yOriginRG;
   uint32_t rgX;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgNormalRect = rgFrame->GetNormalRect();
     if (!rgFrame->HasStyleHeight()) {
@@ -3444,17 +3452,17 @@ nsTableFrame::DistributeHeightToRows(con
       else {
         NS_ERROR("invalid divisor");
         return;
       }
     }
   }
   // allocate the extra height to the unstyled row groups and rows
   nscoord heightToDistribute = aAmount - amountUsed;
-  yOriginRG = borderPadding.top + GetRowSpacing(-1);
+  yOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1);
   yEndRG = yOriginRG;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
     nsRect rgNormalRect = rgFrame->GetNormalRect();
     nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
     // see if there is an eligible row group or we distribute to all rows
@@ -3721,18 +3729,19 @@ nsTableFrame::IsAutoHeight()
           height.GetPercentValue() <= 0.0f);
 }
 
 nscoord
 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
 {
   nscoord height = aState.ComputedHeight();
   if (NS_AUTOHEIGHT != height) {
-    nsMargin borderPadding = GetChildAreaOffset(&aState);
-    height += borderPadding.top + borderPadding.bottom;
+    WritingMode wm = aState.GetWritingMode();
+    LogicalMargin borderPadding = GetChildAreaOffset(wm, &aState);
+    height += borderPadding.BStartEnd(wm);
   }
   height = std::max(0, height);
 
   return height;
 }
 
 bool
 nsTableFrame::IsAutoLayout()
@@ -6381,19 +6390,20 @@ private:
 
 BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
   : mTable(aTable)
   , mTableFirstInFlow(static_cast<nsTableFrame*>(aTable->FirstInFlow()))
   , mTableCellMap(aTable->GetCellMap())
   , mTableWM(aTable->StyleContext())
 {
   mVerInfo    = nullptr;
-  nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
+  LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
   // y position of first row in damage area
-  mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top;
+  mInitialOffsetY =
+    mTable->GetPrevInFlow() ? 0 : childAreaOffset.BStart(mTableWM);
   mNumTableRows  = mTable->GetRowCount();
   mNumTableCols  = mTable->GetColCount();
 
   // Get the ordered row groups
   mTable->OrderRowGroups(mRowGroups);
   // initialize to a non existing index
   mRepeatedHeaderRowIndex = -99;
 
@@ -6460,25 +6470,26 @@ BCPaintBorderIterator::SetDamageArea(con
   if (!haveIntersect)
     return false;
   // find startColIndex, endColIndex, startColX
   haveIntersect = false;
   if (0 == mNumTableCols)
     return false;
   int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol)
 
-  nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr);
+  LogicalMargin childAreaOffset = mTable->GetChildAreaOffset(mTableWM, nullptr);
   if (mTableWM.IsBidiLTR()) {
-    mInitialOffsetX = childAreaOffset.left; // x position of first col in
-                                            // damage area
+    // x position of first col in damage area
+    mInitialOffsetX = childAreaOffset.IStart(mTableWM);
     leftCol = 0;
     rightCol = mNumTableCols;
   } else {
     // x position of first col in damage area
-    mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
+    mInitialOffsetX =
+      mTable->GetRect().width - childAreaOffset.IStart(mTableWM);
     leftCol = mNumTableCols-1;
     rightCol = -1;
   }
   nscoord x = 0;
   int32_t colX;
   for (colX = leftCol; colX != rightCol; colX += mColInc) {
     nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX);
     if (!colFrame) ABORT1(false);
@@ -6504,17 +6515,18 @@ BCPaintBorderIterator::SetDamageArea(con
       else {
         mInitialOffsetX += mColInc * size.width;
       }
     }
     x += size.width;
   }
   if (!mTableWM.IsBidiLTR()) {
     uint32_t temp;
-    mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
+    mInitialOffsetX =
+      mTable->GetRect().width - childAreaOffset.IStart(mTableWM);
     temp = startColIndex; startColIndex = endColIndex; endColIndex = temp;
     for (uint32_t column = 0; column < startColIndex; column++) {
       nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column);
       if (!colFrame) ABORT1(false);
       nsSize size = colFrame->GetSize();
       mInitialOffsetX += mColInc * size.width;
     }
   }
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -20,16 +20,20 @@
 class nsTableCellFrame;
 class nsTableCellMap;
 class nsTableColFrame;
 class nsTableRowGroupFrame;
 class nsTableRowFrame;
 class nsTableColGroupFrame;
 class nsITableLayoutStrategy;
 class nsStyleContext;
+namespace mozilla {
+class WritingMode;
+class LogicalMargin;
+}
 
 struct nsTableReflowState;
 struct BCPropertyData;
 
 static inline bool IS_TABLE_CELL(nsIAtom* frameType) {
   return nsGkAtoms::tableCellFrame == frameType ||
     nsGkAtoms::bcTableCellFrame == frameType;
 }
@@ -118,16 +122,18 @@ enum nsTableColType {
   * stand-alone as the top-level frame.
   *
   * The principal child list contains row group frames. There is also an
   * additional child list, kColGroupList, which contains the col group frames.
   */
 class nsTableFrame : public nsContainerFrame
 {
   typedef mozilla::image::DrawResult DrawResult;
+  typedef mozilla::WritingMode WritingMode;
+  typedef mozilla::LogicalMargin LogicalMargin;
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsTableFrame)
   NS_DECL_FRAMEARENA_HELPERS
 
   NS_DECLARE_FRAME_PROPERTY(PositionedTablePartArray,
                             DeleteValue<nsTArray<nsIFrame*>>)
 
@@ -201,17 +207,18 @@ public:
   virtual void RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame) override;
 
   virtual nsMargin GetUsedBorder() const override;
   virtual nsMargin GetUsedPadding() const override;
   virtual nsMargin GetUsedMargin() const override;
 
   // Get the offset from the border box to the area where the row groups fit
-  nsMargin GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const;
+  LogicalMargin GetChildAreaOffset(const WritingMode aWM,
+                                   const nsHTMLReflowState* aReflowState) const;
 
   /** helper method to find the table parent of any table frame object */
   static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame);
 
   /* Like GetTableFrame, but will return nullptr if we don't pass through
    * aMustPassThrough on the way to the table.
    */
   static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough,
@@ -268,28 +275,28 @@ public:
   DrawResult PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
                                         const nsRect& aDirtyRect,
                                         nsPoint aPt, uint32_t aBGPaintFlags);
 
   /** Get the outer half (i.e., the part outside the height and width of
    *  the table) of the largest segment (?) of border-collapsed border on
    *  the table on each side, or 0 for non border-collapsed tables.
    */
-  nsMargin GetOuterBCBorder() const;
+  LogicalMargin GetOuterBCBorder(const WritingMode aWM) const;
 
   /** Same as above, but only if it's included from the border-box width
    *  of the table.
    */
-  nsMargin GetIncludedOuterBCBorder() const;
+  LogicalMargin GetIncludedOuterBCBorder(const WritingMode aWM) const;
 
   /** Same as above, but only if it's excluded from the border-box width
    *  of the table.  This is the area that leaks out into the margin
    *  (or potentially past it, if there is no margin).
    */
-  nsMargin GetExcludedOuterBCBorder() const;
+  LogicalMargin GetExcludedOuterBCBorder(const WritingMode aWM) const;
 
   /**
    * In quirks mode, the size of the table background is reduced
    * by the outer BC border. Compute the reduction needed.
    */
   nsMargin GetDeflationForBackground(nsPresContext* aPresContext) const;
 
   /** Get width of table + colgroup + col collapse: elements that
@@ -627,26 +634,28 @@ protected:
   //  (1) set all the dimensions to 0
   //  (2) notify the table about colgroups or columns with hidden visibility
   void ReflowColGroups(nsRenderingContext* aRenderingContext);
 
   /** return the width of the table taking into account visibility collapse
     * on columns and colgroups
     * @param aBorderPadding  the border and padding of the table
     */
-  nscoord GetCollapsedWidth(nsMargin aBorderPadding);
+  nscoord GetCollapsedWidth(const WritingMode aWM,
+                            const LogicalMargin& aBorderPadding);
 
 
   /** Adjust the table for visibility.collapse set on rowgroups, rows,
     * colgroups and cols
     * @param aDesiredSize    the metrics of the table
     * @param aBorderPadding  the border and padding of the table
     */
   void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
-                                   nsMargin             aBorderPadding);
+                                   const WritingMode aWM,
+                                   const LogicalMargin& aBorderPadding);
 
   /** FixupPositionedTableParts is called at the end of table reflow to reflow
    *  the absolutely positioned descendants of positioned table parts. This is
    *  necessary because the dimensions of table parts may change after they've
    *  been reflowed (e.g. in AdjustForCollapsingRowsCols).
    */
   void FixupPositionedTableParts(nsPresContext*           aPresContext,
                                  nsHTMLReflowMetrics&     aDesiredSize,
@@ -796,18 +805,18 @@ protected:
   void SetBorderCollapse(bool aValue);
 
   BCPropertyData* GetBCProperty(bool aCreateIfNecessary = false) const;
   void SetFullBCDamageArea();
   void CalcBCBorders();
 
   void ExpandBCDamageArea(mozilla::TableArea& aRect) const;
 
-  void SetColumnDimensions(nscoord         aHeight,
-                           const nsMargin& aReflowState);
+  void SetColumnDimensions(nscoord aHeight, WritingMode aWM,
+                           const LogicalMargin& aBorderPadding);
 
   int32_t CollectRows(nsIFrame*                   aFrame,
                       nsTArray<nsTableRowFrame*>& aCollection);
 
 public: /* ----- Cell Map public methods ----- */
 
   int32_t GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame);
 
--- a/layout/tables/nsTableOuterFrame.cpp
+++ b/layout/tables/nsTableOuterFrame.cpp
@@ -237,17 +237,19 @@ nsTableOuterFrame::InitChildReflowState(
                                     
 {
   nsMargin collapseBorder;
   nsMargin collapsePadding(0,0,0,0);
   nsMargin* pCollapseBorder  = nullptr;
   nsMargin* pCollapsePadding = nullptr;
   if (aReflowState.frame == InnerTableFrame() &&
       InnerTableFrame()->IsBorderCollapse()) {
-    collapseBorder  = InnerTableFrame()->GetIncludedOuterBCBorder();
+    WritingMode wm = aReflowState.GetWritingMode();
+    LogicalMargin border = InnerTableFrame()->GetIncludedOuterBCBorder(wm);
+    collapseBorder = border.GetPhysicalMargin(wm);
     pCollapseBorder = &collapseBorder;
     pCollapsePadding = &collapsePadding;
   }
   aReflowState.Init(&aPresContext, nullptr, pCollapseBorder, pCollapsePadding);
 }
 
 // get the margin and padding data. nsHTMLReflowState doesn't handle the
 // case of auto margins
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -6509,23 +6509,18 @@ malloc_good_size_impl(size_t size)
 		 * malloc_good_size(n).
 		 */
 		size = PAGE_CEILING(size);
 	}
 	return size;
 }
 
 
-#if defined(MOZ_MEMORY_ANDROID) && (ANDROID_VERSION < 19)
 MOZ_MEMORY_API size_t
-malloc_usable_size_impl(void *ptr)
-#else
-MOZ_MEMORY_API size_t
-malloc_usable_size_impl(const void *ptr)
-#endif
+malloc_usable_size_impl(MALLOC_USABLE_SIZE_CONST_PTR void *ptr)
 {
 	DARWIN_ONLY(return (szone->size)(szone, ptr));
 
 #ifdef MALLOC_VALIDATE
 	return (isalloc_validate(ptr));
 #else
 	assert(ptr != NULL);
 
--- a/memory/replace/dummy/moz.build
+++ b/memory/replace/dummy/moz.build
@@ -4,11 +4,11 @@
 # 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/.
 DIST_INSTALL = False
 
 SOURCES += [
     'dummy_replace_malloc.c',
 ]
 
-SharedLibrary('replace_malloc')
+SharedLibrary('dummy_replace_malloc')
 
 DISABLE_STL_WRAPPING = True
--- a/mobile/android/base/BrowserLocaleManager.java
+++ b/mobile/android/base/BrowserLocaleManager.java
@@ -395,17 +395,17 @@ public class BrowserLocaleManager implem
      *              "pt-PT", "ro", "ru", "sk", "sl", "sv-SE", "th",
      *              "tr", "uk", "zh-CN", "zh-TW", "en-US"]}
      * </code>
      */
     public static Collection<String> getPackagedLocaleTags(final Context context) {
         final String resPath = "res/multilocale.json";
         final String jarURL = GeckoJarReader.getJarURL(context, resPath);
 
-        final String contents = GeckoJarReader.getText(jarURL);
+        final String contents = GeckoJarReader.getText(context, jarURL);
         if (contents == null) {
             // GeckoJarReader logs and swallows exceptions.
             return null;
         }
 
         try {
             final JSONObject multilocale = new JSONObject(contents);
             final JSONArray locales = multilocale.getJSONArray("locales");
--- a/mobile/android/base/GeckoApplication.java
+++ b/mobile/android/base/GeckoApplication.java
@@ -6,17 +6,16 @@ package org.mozilla.gecko;
 
 import org.mozilla.gecko.AdjustConstants;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.lwt.LightweightTheme;
-import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Application;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
@@ -122,17 +121,16 @@ public class GeckoApplication extends Ap
     }
 
     @Override
     public void onCreate() {
         final Context context = getApplicationContext();
         HardwareUtils.init(context);
         Clipboard.init(context);
         FilePicker.init(context);
-        GeckoLoader.loadMozGlue(context);
         DownloadsIntegration.init();
         HomePanelsManager.getInstance().init(context);
 
         // This getInstance call will force initialization of the NotificationHelper, but does nothing with the result
         NotificationHelper.getInstance(context).init();
 
         // Make sure that all browser-ish applications default to the real LocalBrowserDB.
         // GeckoView consumers use their own Application class, so this doesn't affect them.
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -85,16 +85,18 @@ public class GeckoThread extends Thread 
     public static boolean isLaunched() {
         return !checkLaunchState(LaunchState.Launching);
     }
 
     private String initGeckoEnvironment() {
         final Locale locale = Locale.getDefault();
 
         final Context context = GeckoAppShell.getContext();
+        GeckoLoader.loadMozGlue(context);
+
         final Resources res = context.getResources();
         if (locale.toString().equalsIgnoreCase("zh_hk")) {
             final Locale mappedLocale = Locale.TRADITIONAL_CHINESE;
             Locale.setDefault(mappedLocale);
             Configuration config = res.getConfiguration();
             config.locale = mappedLocale;
             res.updateConfiguration(config, null);
         }
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -114,16 +114,24 @@ public class GeckoView extends LayerView
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GeckoView);
         String url = a.getString(R.styleable.GeckoView_url);
         boolean doInit = a.getBoolean(R.styleable.GeckoView_doinit, true);
         a.recycle();
         init(context, url, doInit);
     }
 
     private void init(Context context, String url, boolean doInit) {
+
+        // Set the GeckoInterface if the context is an activity and the GeckoInterface
+        // has not already been set
+        if (context instanceof Activity && getGeckoInterface() == null) {
+            setGeckoInterface(new BaseGeckoInterface(context));
+            GeckoAppShell.setContextGetter(this);
+        }
+
         // Perform common initialization for Fennec/GeckoView.
         GeckoAppShell.setLayerView(this);
 
         initializeView(EventDispatcher.getInstance());
         GeckoAppShell.sendEventToGecko(GeckoEvent.createObjectEvent(
                 GeckoEvent.ACTION_OBJECT_LAYER_CLIENT, getLayerClientObject()));
 
         // TODO: Fennec currently takes care of its own initialization, so this
@@ -136,40 +144,31 @@ public class GeckoView extends LayerView
         // If running outside of a GeckoActivity (eg, from a library project),
         // load the native code and disable content providers
         boolean isGeckoActivity = false;
         try {
             isGeckoActivity = context instanceof GeckoActivity;
         } catch (NoClassDefFoundError ex) {}
 
         if (!isGeckoActivity) {
-            // Set the GeckoInterface if the context is an activity and the GeckoInterface
-            // has not already been set
-            if (context instanceof Activity && getGeckoInterface() == null) {
-                setGeckoInterface(new BaseGeckoInterface(context));
-            }
-
             Clipboard.init(context);
             HardwareUtils.init(context);
 
             // If you want to use GeckoNetworkManager, start it.
 
-            GeckoLoader.loadMozGlue(context);
-
             final GeckoProfile profile = GeckoProfile.get(context);
          }
 
         if (url != null) {
             GeckoThread.ensureInit(null, Intent.ACTION_VIEW, url);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(url));
         } else {
             GeckoThread.ensureInit(null, null, null);
         }
 
-        GeckoAppShell.setContextGetter(this);
         if (context instanceof Activity) {
             Tabs tabs = Tabs.getInstance();
             tabs.attachToContext(context);
         }
 
         EventDispatcher.getInstance().registerGeckoThreadListener(mGeckoEventListener,
             "Gecko:Ready",
             "Accessibility:Event",
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -478,17 +478,17 @@ public class LocalBrowserDB implements B
      */
     private static ConsumedInputStream getDefaultFaviconFromPath(Context context, String name) {
         final int faviconId = getFaviconId(name);
         if (faviconId == FAVICON_ID_NOT_FOUND) {
             return null;
         }
 
         final String bitmapPath = GeckoJarReader.getJarURL(context, context.getString(faviconId));
-        final InputStream iStream = GeckoJarReader.getStream(bitmapPath);
+        final InputStream iStream = GeckoJarReader.getStream(context, bitmapPath);
 
         return IOUtils.readFully(iStream, DEFAULT_FAVICON_BUFFER_SIZE);
     }
 
     private static ConsumedInputStream getDefaultFaviconFromDrawable(Context context, String name) {
         int faviconId = getFaviconId(name);
         if (faviconId == FAVICON_ID_NOT_FOUND) {
             return null;
--- a/mobile/android/base/favicons/Favicons.java
+++ b/mobile/android/base/favicons/Favicons.java
@@ -489,17 +489,18 @@ public class Favicons {
      * Compute a string like:
      * "jar:jar:file:///data/app/org.mozilla.firefox-1.apk!/assets/omni.ja!/chrome/chrome/content/branding/favicon64.png"
      */
     private static String getBrandingBitmapPath(Context context, String name) {
         return GeckoJarReader.getJarURL(context, "chrome/chrome/content/branding/" + name);
     }
 
     private static Bitmap loadBrandingBitmap(Context context, String name) {
-        Bitmap b = GeckoJarReader.getBitmap(context.getResources(),
+        Bitmap b = GeckoJarReader.getBitmap(context,
+                                            context.getResources(),
                                             getBrandingBitmapPath(context, name));
         if (b == null) {
             throw new IllegalStateException("Bitmap " + name + " missing from JAR!");
         }
         return b;
     }
 
     /**
--- a/mobile/android/base/favicons/LoadFaviconTask.java
+++ b/mobile/android/base/favicons/LoadFaviconTask.java
@@ -191,17 +191,17 @@ public class LoadFaviconTask {
      */
     private Bitmap fetchJARFavicon(String uri) {
         if (uri == null) {
             return null;
         }
         if (uri.startsWith("jar:jar:")) {
             Log.d(LOGTAG, "Fetching favicon from JAR.");
             try {
-                return GeckoJarReader.getBitmap(context.getResources(), uri);
+                return GeckoJarReader.getBitmap(context, context.getResources(), uri);
             } catch (Exception e) {
                 // Just about anything could happen here.
                 Log.w(LOGTAG, "Error fetching favicon from JAR.", e);
                 return null;
             }
         }
         return null;
     }
--- a/mobile/android/base/gfx/BitmapUtils.java
+++ b/mobile/android/base/gfx/BitmapUtils.java
@@ -81,22 +81,24 @@ public final class BitmapUtils {
         }
 
         if (data.startsWith("jar:") || data.startsWith("file://")) {
             (new UIAsyncTask.WithoutParams<Drawable>(ThreadUtils.getBackgroundHandler()) {
                 @Override
                 public Drawable doInBackground() {
                     try {
                         if (data.startsWith("jar:jar")) {
-                            return GeckoJarReader.getBitmapDrawable(context.getResources(), data);
+                            return GeckoJarReader.getBitmapDrawable(
+                                    context, context.getResources(), data);
                         }
 
                         // Don't attempt to validate the JAR signature when loading an add-on icon
                         if (data.startsWith("jar:file")) {
-                            return GeckoJarReader.getBitmapDrawable(context.getResources(), Uri.decode(data));
+                            return GeckoJarReader.getBitmapDrawable(
+                                    context, context.getResources(), Uri.decode(data));
                         }
 
                         final URL url = new URL(data);
                         final InputStream is = (InputStream) url.getContent();
                         try {
                             return Drawable.createFromStream(is, "src");
                         } finally {
                             is.close();
--- a/mobile/android/base/gfx/BufferedImage.java
+++ b/mobile/android/base/gfx/BufferedImage.java
@@ -10,49 +10,58 @@ import org.mozilla.gecko.mozglue.DirectB
 import android.graphics.Bitmap;
 import android.util.Log;
 
 import java.nio.ByteBuffer;
 
 /** A buffered image that simply saves a buffer of pixel data. */
 public class BufferedImage {
     private ByteBuffer mBuffer;
+    private Bitmap mBitmap;
     private IntSize mSize;
     private int mFormat;
 
     private static final String LOGTAG = "GeckoBufferedImage";
 
     /** Creates an empty buffered image */
     public BufferedImage() {
         mSize = new IntSize(0, 0);
     }
 
     /** Creates a buffered image from an Android bitmap. */
     public BufferedImage(Bitmap bitmap) {
         mFormat = bitmapConfigToFormat(bitmap.getConfig());
         mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
-
-        int bpp = bitsPerPixelForFormat(mFormat);
-        mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
-        bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
+        mBitmap = bitmap;
     }
 
     private synchronized void freeBuffer() {
-        mBuffer = DirectBufferAllocator.free(mBuffer);
+        if (mBuffer != null) {
+            mBuffer = DirectBufferAllocator.free(mBuffer);
+        }
     }
 
     public void destroy() {
         try {
             freeBuffer();
         } catch (Exception ex) {
             Log.e(LOGTAG, "error clearing buffer: ", ex);
         }
     }
 
-    public ByteBuffer getBuffer() { return mBuffer; }
+    public ByteBuffer getBuffer() {
+        if (mBuffer == null) {
+            int bpp = bitsPerPixelForFormat(mFormat);
+            mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
+            mBitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
+            mBitmap = null;
+        }
+        return mBuffer;
+    }
+
     public IntSize getSize() { return mSize; }
     public int getFormat() { return mFormat; }
 
     public static final int FORMAT_INVALID = -1;
     public static final int FORMAT_ARGB32 = 0;
     public static final int FORMAT_RGB24 = 1;
     public static final int FORMAT_A8 = 2;
     public static final int FORMAT_A1 = 3;
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -156,22 +156,16 @@ public class LayerRenderer implements Ta
 
         mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true);
         mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false);
         mFadeRunnable = new FadeRunnable();
 
         mFrameTimings = new int[60];
         mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
 
-        // Initialize the FloatBuffer that will be used to store all vertices and texture
-        // coordinates in draw() commands.
-        mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
-        mCoordByteBuffer.order(ByteOrder.nativeOrder());
-        mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
-
         Tabs.registerOnTabsChangedListener(this);
         mZoomedViewListeners = new ArrayList<LayerView.ZoomedViewListener>();
     }
 
     private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) {
         IntSize potSize = size.nextPowerOfTwo();
         if (size.equals(potSize)) {
             return image;
@@ -185,19 +179,21 @@ public class LayerRenderer implements Ta
     private Bitmap diagonalFlip(Bitmap image) {
         Matrix rotation = new Matrix();
         rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x)
         Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true);
         return rotated;
     }
 
     public void destroy() {
-        DirectBufferAllocator.free(mCoordByteBuffer);
-        mCoordByteBuffer = null;
-        mCoordBuffer = null;
+        if (mCoordByteBuffer != null) {
+            DirectBufferAllocator.free(mCoordByteBuffer);
+            mCoordByteBuffer = null;
+            mCoordBuffer = null;
+        }
         mHorizScrollLayer.destroy();
         mVertScrollLayer.destroy();
         Tabs.unregisterOnTabsChangedListener(this);
         mZoomedViewListeners.clear();
     }
 
     void onSurfaceCreated(EGLConfig config) {
         checkMonitoringEnabled();
@@ -343,20 +339,27 @@ public class LayerRenderer implements Ta
         RectF pageRect = metrics.getPageRect();
         float zoomFactor = metrics.zoomFactor;
 
         return createContext(new RectF(RectUtils.round(viewport)), pageRect, zoomFactor, offset);
     }
 
     private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) {
         if (mCoordBuffer == null) {
-            throw new IllegalStateException();
+            // Initialize the FloatBuffer that will be used to store all vertices and texture
+            // coordinates in draw() commands.
+            mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4);
+            mCoordByteBuffer.order(ByteOrder.nativeOrder());
+            mCoordBuffer = mCoordByteBuffer.asFloatBuffer();
+            if (mCoordBuffer == null) {
+                throw new IllegalStateException();
+            }
         }
-        return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle,
-                                 mCoordBuffer);
+        return new RenderContext(viewport, pageRect, zoomFactor, offset,
+                                 mPositionHandle, mTextureHandle, mCoordBuffer);
     }
 
     private void updateDroppedFrames(long frameStartTime) {
         int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
 
         /* Update the running statistics. */
         mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
         mFrameTimingsSum += frameElapsedTime;
--- a/mobile/android/base/updater/UpdateServiceHelper.java
+++ b/mobile/android/base/updater/UpdateServiceHelper.java
@@ -98,17 +98,17 @@ public class UpdateServiceHelper {
                             "-" + AppConstants.MOZ_PKG_SPECIAL :
                             "";
         String locale = DEFAULT_UPDATE_LOCALE;
 
         try {
             ApplicationInfo info = pm.getApplicationInfo(AppConstants.ANDROID_PACKAGE_NAME, 0);
             String updateLocaleUrl = "jar:jar:file://" + info.sourceDir + "!/" + AppConstants.OMNIJAR_NAME + "!/update.locale";
 
-            final String jarLocale = GeckoJarReader.getText(updateLocaleUrl);
+            final String jarLocale = GeckoJarReader.getText(context, updateLocaleUrl);
             if (jarLocale != null) {
                 locale = jarLocale.trim();
             }
         } catch (android.content.pm.PackageManager.NameNotFoundException e) {
             // Shouldn't really be possible, but fallback to default locale
             Log.i(LOGTAG, "Failed to read update locale file, falling back to " + locale);
         }
 
--- a/mobile/android/base/util/GeckoJarReader.java
+++ b/mobile/android/base/util/GeckoJarReader.java
@@ -1,16 +1,17 @@
 /* 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/. */
 
 package org.mozilla.gecko.util;
 
 import android.content.Context;
 import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.NativeZip;
 
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.util.Log;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
@@ -26,30 +27,31 @@ import java.util.Stack;
 /* Reads out of a multiple level deep jar file such as
  *  jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png
  */
 public final class GeckoJarReader {
     private static final String LOGTAG = "GeckoJarReader";
 
     private GeckoJarReader() {}
 
-    public static Bitmap getBitmap(Resources resources, String url) {
-        BitmapDrawable drawable = getBitmapDrawable(resources, url);
+    public static Bitmap getBitmap(Context context, Resources resources, String url) {
+        BitmapDrawable drawable = getBitmapDrawable(context, resources, url);
         return (drawable != null) ? drawable.getBitmap() : null;
     }
 
-    public static BitmapDrawable getBitmapDrawable(Resources resources, String url) {
+    public static BitmapDrawable getBitmapDrawable(Context context, Resources resources,
+                                                   String url) {
         Stack<String> jarUrls = parseUrl(url);
         InputStream inputStream = null;
         BitmapDrawable bitmap = null;
 
         NativeZip zip = null;
         try {
             // Load the initial jar file as a zip
-            zip = getZipFile(jarUrls.pop());
+            zip = getZipFile(context, jarUrls.pop());
             inputStream = getStream(zip, jarUrls, url);
             if (inputStream != null) {
                 bitmap = new BitmapDrawable(resources, inputStream);
             }
         } catch (IOException | URISyntaxException ex) {
             Log.e(LOGTAG, "Exception ", ex);
         } finally {
             if (inputStream != null) {
@@ -62,24 +64,24 @@ public final class GeckoJarReader {
             if (zip != null) {
                 zip.close();
             }
         }
 
         return bitmap;
     }
 
-    public static String getText(String url) {
+    public static String getText(Context context, String url) {
         Stack<String> jarUrls = parseUrl(url);
 
         NativeZip zip = null;
         BufferedReader reader = null;
         String text = null;
         try {
-            zip = getZipFile(jarUrls.pop());
+            zip = getZipFile(context, jarUrls.pop());
             InputStream input = getStream(zip, jarUrls, url);
             if (input != null) {
                 reader = new BufferedReader(new InputStreamReader(input));
                 text = reader.readLine();
             }
         } catch (IOException | URISyntaxException ex) {
             Log.e(LOGTAG, "Exception ", ex);
         } finally {
@@ -93,26 +95,28 @@ public final class GeckoJarReader {
             if (zip != null) {
                 zip.close();
             }
         }
 
         return text;
     }
 
-    private static NativeZip getZipFile(String url) throws IOException, URISyntaxException {
+    private static NativeZip getZipFile(Context context, String url)
+            throws IOException, URISyntaxException {
         URI fileUrl = new URI(url);
+        GeckoLoader.loadMozGlue(context);
         return new NativeZip(fileUrl.getPath());
     }
 
     @RobocopTarget
-    public static InputStream getStream(String url) {
+    public static InputStream getStream(Context context, String url) {
         Stack<String> jarUrls = parseUrl(url);
         try {
-            NativeZip zip = getZipFile(jarUrls.pop());
+            NativeZip zip = getZipFile(context, jarUrls.pop());
             return getStream(zip, jarUrls, url);
         } catch (Exception ex) {
             // Some JNI code throws IllegalArgumentException on a bad file name;
             // swallow the error and return null.  We could also see legitimate
             // IOExceptions here.
             Log.e(LOGTAG, "Exception getting input stream from jar URL: " + url, ex);
             return null;
         }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -8,16 +8,17 @@ let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 let Cr = Components.results;
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
+Cu.import("resource://gre/modules/DelayedInit.jsm");
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/NotificationDB.jsm");
 Cu.import("resource://gre/modules/SpatialNavigation.jsm");
 
 if (AppConstants.ACCESSIBILITY) {
   Cu.import("resource://gre/modules/accessibility/AccessFu.jsm");
 }
 
@@ -370,51 +371,16 @@ var BrowserApp = {
 
   deck: null,
 
   startup: function startup() {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
     dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
 
     this.deck = document.getElementById("browsers");
-    this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() {
-      try {
-        BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false);
-        Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
-        Messaging.sendRequest({ type: "Gecko:DelayedStartup" });
-
-        // Queue up some other performance-impacting initializations
-        Services.tm.mainThread.dispatch(function() {
-          // Spin up some services which impact performance.
-          Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
-          Services.search.init();
-
-          // Spin up some features which impact performance.
-          CastingApps.init();
-          DownloadNotifications.init();
-
-          if (AppConstants.MOZ_SAFE_BROWSING) {
-            // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
-            SafeBrowsing.init();
-          };
-
-          // Delay this a minute because there's no rush
-          setTimeout(() => {
-            BrowserApp.gmpInstallManager = new GMPInstallManager();
-            BrowserApp.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
-          }, 1000 * 60);
-        }, Ci.nsIThread.DISPATCH_NORMAL);
-
-        if (AppConstants.NIGHTLY_BUILD) {
-          WebcompatReporter.init();
-          Telemetry.addData("TRACKING_PROTECTION_ENABLED",
-            Services.prefs.getBoolPref("privacy.trackingprotection.enabled"));
-        }
-      } catch(ex) { console.log(ex); }
-    }, false);
 
     BrowserEventHandler.init();
     ViewportHandler.init();
 
     Services.androidBridge.browserApp = this;
 
     Services.obs.addObserver(this, "Locale:OS", false);
     Services.obs.addObserver(this, "Locale:Changed", false);
@@ -548,16 +514,50 @@ var BrowserApp = {
       // Tiles reporting is disabled.
     }
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/content.js", true);
 
     // Notify Java that Gecko has loaded.
     Messaging.sendRequest({ type: "Gecko:Ready" });
+
+    this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() {
+      BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false);
+
+      function InitLater(fn, object, name) {
+        return DelayedInit.schedule(fn, object, name, 15000 /* 15s max wait */);
+      }
+
+      InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", ""));
+      InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" }));
+
+      if (AppConstants.NIGHTLY_BUILD) {
+        InitLater(() => Telemetry.addData("TRACKING_PROTECTION_ENABLED",
+            Services.prefs.getBoolPref("privacy.trackingprotection.enabled")));
+        InitLater(() => WebcompatReporter.init());
+      }
+
+      InitLater(() => CastingApps.init(), window, "CastingApps");
+      InitLater(() => Services.search.init(), Services, "search");
+      InitLater(() => DownloadNotifications.init(), window, "DownloadNotifications");
+
+      if (AppConstants.MOZ_SAFE_BROWSING) {
+        // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
+        InitLater(() => SafeBrowsing.init(), window, "SafeBrowsing");
+      }
+
+      InitLater(() => Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager));
+
+      InitLater(() => {
+          BrowserApp.gmpInstallManager = new GMPInstallManager();
+          BrowserApp.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
+      }, BrowserApp, "gmpInstallManager");
+
+    }, false);
   },
 
   get _startupStatus() {
     delete this._startupStatus;
 
     let savedMilestone = null;
     try {
       savedMilestone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/DelayedInit.jsm
@@ -0,0 +1,175 @@
+/* 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/. */
+"use strict"
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+this.EXPORTED_SYMBOLS = ["DelayedInit"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "MessageLoop",
+                                   "@mozilla.org/message-loop;1",
+                                   "nsIMessageLoop");
+
+/**
+ * Use DelayedInit to schedule initializers to run some time after startup.
+ * Initializers are added to a list of pending inits. Whenever the main thread
+ * message loop is idle, DelayedInit will start running initializers from the
+ * pending list. To prevent monopolizing the message loop, every idling period
+ * has a maximum duration. When that's reached, we give up the message loop and
+ * wait for the next idle.
+ *
+ * DelayedInit is compatible with lazy getters like those from XPCOMUtils. When
+ * the lazy getter is first accessed, its corresponding initializer is run
+ * automatically if it hasn't been run already. Each initializer also has a
+ * maximum wait parameter that specifies a mandatory timeout; when the timeout
+ * is reached, the initializer is forced to run.
+ *
+ *   DelayedInit.schedule(() => Foo.init(), null, null, 5000);
+ *
+ * In the example above, Foo.init will run automatically when the message loop
+ * becomes idle, or when 5000ms has elapsed, whichever comes first.
+ *
+ *   DelayedInit.schedule(() => Foo.init(), this, "Foo", 5000);
+ *
+ * In the example above, Foo.init will run automatically when the message loop
+ * becomes idle, when |this.Foo| is accessed, or when 5000ms has elapsed,
+ * whichever comes first.
+ *
+ * It may be simpler to have a wrapper for DelayedInit.schedule. For example,
+ *
+ *   function InitLater(fn, obj, name) {
+ *     return DelayedInit.schedule(fn, obj, name, 5000); // constant max wait
+ *   }
+ *   InitLater(() => Foo.init());
+ *   InitLater(() => Bar.init(), this, "Bar");
+ */
+let DelayedInit = {
+  schedule: function (fn, object, name, maxWait) {
+    return Impl.scheduleInit(fn, object, name, maxWait);
+  },
+};
+
+// Maximum duration for each idling period. Pending inits are run until this
+// duration is exceeded; then we wait for next idling period.
+const MAX_IDLE_RUN_MS = 50;
+
+let Impl = {
+  pendingInits: [],
+
+  onIdle: function () {
+    let startTime = Cu.now();
+    let time = startTime;
+    let nextDue;
+
+    // Go through all the pending inits. Even if we don't run them,
+    // we still need to find out when the next timeout should be.
+    for (let init of this.pendingInits) {
+      if (init.complete) {
+        continue;
+      }
+
+      if (time - startTime < MAX_IDLE_RUN_MS) {
+        init.maybeInit();
+        time = Cu.now();
+      } else {
+        // We ran out of time; find when the next closest due time is.
+        nextDue = nextDue ? Math.min(nextDue, init.due) : init.due;
+      }
+    }
+
+    // Get rid of completed ones.
+    this.pendingInits = this.pendingInits.filter((init) => !init.complete);
+
+    if (nextDue !== undefined) {
+      // Schedule the next idle, if we still have pending inits.
+      MessageLoop.postIdleTask(() => this.onIdle(),
+                               Math.max(0, nextDue - time));
+    }
+  },
+
+  addPendingInit: function (fn, wait) {
+    let init = {
+      fn: fn,
+      due: Cu.now() + wait,
+      complete: false,
+      maybeInit: function () {
+        if (this.complete) {
+          return false;
+        }
+        this.complete = true;
+        this.fn.call();
+        this.fn = null;
+        return true;
+      },
+    };
+
+    if (!this.pendingInits.length) {
+      // Schedule for the first idle.
+      MessageLoop.postIdleTask(() => this.onIdle(), wait);
+    }
+    this.pendingInits.push(init);
+    return init;
+  },
+
+  scheduleInit: function (fn, object, name, wait) {
+    let init = this.addPendingInit(fn, wait);
+
+    if (!object || !name) {
+      // No lazy getter needed.
+      return;
+    }
+
+    // Get any existing information about the property.
+    let prop = Object.getOwnPropertyDescriptor(object, name) ||
+               { configurable: true, enumerable: true, writable: true };
+
+    if (!prop.configurable) {
+      // Object.defineProperty won't work, so just perform init here.
+      init.maybeInit();
+      return;
+    }
+
+    // Define proxy getter/setter that will call first initializer first,
+    // before delegating the get/set to the original target.
+    Object.defineProperty(object, name, {
+      get: function proxy_getter() {
+        init.maybeInit();
+
+        // If the initializer actually ran, it may have replaced our proxy
+        // property with a real one, so we need to reload he property.
+        let newProp = Object.getOwnPropertyDescriptor(object, name);
+        if (newProp.get !== proxy_getter) {
+          // Set prop if newProp doesn't refer to our proxy property.
+          prop = newProp;
+        } else {
+          // Otherwise, reset to the original property.
+          Object.defineProperty(object, name, prop);
+        }
+
+        if (prop.get) {
+          return prop.get.call(object);
+        }
+        return prop.value;
+      },
+      set: function (newVal) {
+        init.maybeInit();
+
+        // Since our initializer already ran,
+        // we can get rid of our proxy property.
+        if (prop.get || prop.set) {
+          Object.defineProperty(object, name, prop);
+          return prop.set.call(object);
+        }
+
+        prop.value = newVal;
+        Object.defineProperty(object, name, prop);
+        return newVal;
+      },
+      configurable: true,
+      enumerable: true,
+    });
+  }
+};
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 EXTRA_JS_MODULES += [
     'Accounts.jsm',
     'AndroidLog.jsm',
     'ContactService.jsm',
     'dbg-browser-actors.js',
+    'DelayedInit.jsm',
     'DownloadNotifications.jsm',
     'HelperApps.jsm',
     'Home.jsm',
     'HomeProvider.jsm',
     'JNI.jsm',
     'LightweightThemeConsumer.jsm',
     'MatchstickApp.jsm',
     'MediaPlayerApp.jsm',
--- a/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
+++ b/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
@@ -614,48 +614,49 @@ public class SearchEngineManager impleme
      */
     private InputStream getInputStreamFromSearchPluginsJar(String fileName) {
         final Locale locale = Locale.getDefault();
 
         // First, try a file path for the full locale.
         final String languageTag = Locales.getLanguageTag(locale);
         String url = getSearchPluginsJarURL(context, languageTag, fileName);
 
-        InputStream in = GeckoJarReader.getStream(url);
+        InputStream in = GeckoJarReader.getStream(context, url);
         if (in != null) {
             return in;
         }
 
         // If that doesn't work, try a file path for just the language.
         final String language = Locales.getLanguage(locale);
         if (!languageTag.equals(language)) {
             url = getSearchPluginsJarURL(context, language, fileName);
-            in = GeckoJarReader.getStream(url);
+            in = GeckoJarReader.getStream(context, url);
             if (in != null) {
                 return in;
             }
         }
 
         // Finally, fall back to default locale defined in chrome registry.
         url = getSearchPluginsJarURL(context, getFallbackLocale(), fileName);
-        return GeckoJarReader.getStream(url);
+        return GeckoJarReader.getStream(context, url);
     }
 
     /**
      * Finds a fallback locale in the Gecko chrome registry. If a locale is declared
      * here, we should be guaranteed to find a searchplugins directory for it.
      *
      * This method should only be accessed from the background thread.
      */
     private String getFallbackLocale() {
         if (fallbackLocale != null) {
             return fallbackLocale;
         }
 
-        final InputStream in = GeckoJarReader.getStream(GeckoJarReader.getJarURL(context, "chrome/chrome.manifest"));
+        final InputStream in = GeckoJarReader.getStream(
+                context, GeckoJarReader.getJarURL(context, "chrome/chrome.manifest"));
         final BufferedReader br = getBufferedReader(in);
 
         try {
             String line;
             while ((line = br.readLine()) != null) {
                 // We're looking for a line like "locale global en-US en-US/locale/en-US/global/"
                 // https://developer.mozilla.org/en/docs/Chrome_Registration#locale
                 if (line.startsWith("locale global ")) {
--- a/mobile/android/tests/browser/junit3/src/org/mozilla/tests/browser/junit3/TestJarReader.java
+++ b/mobile/android/tests/browser/junit3/src/org/mozilla/tests/browser/junit3/TestJarReader.java
@@ -4,43 +4,46 @@
 package org.mozilla.tests.browser.junit3;
 
 import java.io.InputStream;
 
 import android.test.InstrumentationTestCase;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.util.GeckoJarReader;
 
+import android.content.Context;
+
 /**
  * A basic jar reader test. Tests reading a png from fennec's apk, as well as
  * loading some invalid jar urls.
  */
 public class TestJarReader extends InstrumentationTestCase {
     public void testJarReader() {
+        final Context context = getInstrumentation().getTargetContext().getApplicationContext();
         String appPath = getInstrumentation().getTargetContext().getPackageResourcePath();
         assertNotNull(appPath);
 
         // Test reading a file from a jar url that looks correct.
         String url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
-        InputStream stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        InputStream stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         assertNotNull(stream);
 
         // Test looking for an non-existent file in a jar.
         url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
         assertNull(stream);
 
         // Test looking for a file that doesn't exist in the APK.
         url = "jar:file://" + appPath + "!/" + "BAD" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         assertNull(stream);
 
         // Test looking for an jar with an invalid url.
         url = "jar:file://" + appPath + "!" + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
         assertNull(stream);
 
         // Test looking for a file that doesn't exist on disk.
         url = "jar:file://" + appPath + "BAD" + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         assertNull(stream);
     }
 }
--- a/mobile/android/tests/browser/robocop/testJarReader.java
+++ b/mobile/android/tests/browser/robocop/testJarReader.java
@@ -4,55 +4,58 @@
 
 package org.mozilla.gecko.tests;
 
 import java.io.InputStream;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.util.GeckoJarReader;
 
+import android.content.Context;
+
 /**
  * A basic jar reader test. Tests reading a png from fennec's apk, as well
  * as loading some invalid jar urls.
  */
 public class testJarReader extends BaseTest {
     public void testGetJarURL() {
         // Invalid characters are escaped.
         final String s = GeckoJarReader.computeJarURI("some[1].apk", "something/else");
         mAsserter.ok(!s.contains("["), "Illegal characters are escaped away.", null);
         mAsserter.ok(!s.toLowerCase().contains("%2f"), "Path characters aren't escaped.", null);
     }
 
     public void testJarReader() {
+        final Context context = getInstrumentation().getTargetContext().getApplicationContext();
         String appPath = getActivity().getApplication().getPackageResourcePath();
         mAsserter.isnot(appPath, null, "getPackageResourcePath is non-null");
 
         // Test reading a file from a jar url that looks correct.
         String url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
-        InputStream stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        InputStream stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         mAsserter.isnot(stream, null, "JarReader returned non-null for valid file in valid jar");
 
         // Test looking for an non-existent file in a jar.
         url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
         mAsserter.is(stream, null, "JarReader returned null for non-existent file in valid jar");
 
         // Test looking for a file that doesn't exist in the APK.
         url = "jar:file://" + appPath + "!/" + "BAD" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         mAsserter.is(stream, null, "JarReader returned null for valid file in invalid jar file");
 
         // Test looking for an jar with an invalid url.
         url = "jar:file://" + appPath + "!" + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");
         mAsserter.is(stream, null, "JarReader returned null for bad jar url");
 
         // Test looking for a file that doesn't exist on disk.
         url = "jar:file://" + appPath + "BAD" + "!/" + AppConstants.OMNIJAR_NAME;
-        stream = GeckoJarReader.getStream("jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
+        stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
         mAsserter.is(stream, null, "JarReader returned null for a non-existent APK");
 
         // This test completes very quickly. If it completes too soon, the
         // minidumps directory may not be created before the process is
         // taken down, causing bug 722166.
         blockForGeckoReady();
     }
 
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -407,11 +407,9 @@ int main(int argc, char **argv) {
   case 'r':
     return strip_signature_block(argv[2], argv[3]);
 #endif /* endif NO_SIGN_VERIFY disabled */
 
   default:
     print_usage();
     return -1;
   }
-
-  return 0;
 }
--- a/mozglue/build/replace_malloc.mk
+++ b/mozglue/build/replace_malloc.mk
@@ -20,11 +20,13 @@ OS_LDFLAGS += \
   -Wl,-U,_replace_jemalloc_purge_freed_pages \
   -Wl,-U,_replace_jemalloc_free_dirty_pages \
   $(NULL)
 
 ifneq ($(MOZ_REPLACE_MALLOC_LINKAGE),compiler support)
 OS_LDFLAGS += -flat_namespace
 endif
 ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
-OS_LDFLAGS += -Wl,-weak_library,$(DEPTH)/memory/replace/dummy/$(DLL_PREFIX)replace_malloc$(DLL_SUFFIX)
+OS_LDFLAGS += -Wl,-weak_library,$(DEPTH)/memory/replace/dummy/$(DLL_PREFIX)dummy_replace_malloc$(DLL_SUFFIX)
 endif
+
+EXTRA_DEPS += $(topsrcdir)/mozglue/build/replace_malloc.mk
 endif
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -2,18 +2,21 @@
 /* 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/. */
 
 #include "mozilla/LoadInfo.h"
 
 #include "mozilla/Assertions.h"
+#include "nsFrameLoader.h"
+#include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIFrameLoader.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 
 namespace mozilla {
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsINode* aLoadingContext,
@@ -23,43 +26,77 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   : mLoadingPrincipal(aLoadingContext ?
                         aLoadingContext->NodePrincipal() : aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal ?
                            aTriggeringPrincipal : mLoadingPrincipal.get())
   , mLoadingContext(do_GetWeakReference(aLoadingContext))
   , mSecurityFlags(aSecurityFlags)
   , mContentPolicyType(aContentPolicyType)
   , mBaseURI(aBaseURI)
-  , mInnerWindowID(aLoadingContext ?
-                     aLoadingContext->OwnerDoc()->InnerWindowID() : 0)
+  , mInnerWindowID(0)
+  , mOuterWindowID(0)
+  , mParentOuterWindowID(0)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if consumers pass both, aLoadingContext and aLoadingPrincipal
   // then the loadingPrincipal must be the same as the node's principal
   MOZ_ASSERT(!aLoadingContext || !aLoadingPrincipal ||
              aLoadingContext->NodePrincipal() == aLoadingPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
     mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
+
+  if (aLoadingContext) {
+    nsCOMPtr<nsPIDOMWindow> outerWindow;
+
+    // When the element being loaded is a frame, we choose the frame's window
+    // for the window ID and the frame element's window as the parent
+    // window. This is the behavior that Chrome exposes to add-ons.
+    nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(aLoadingContext);
+    if (frameLoaderOwner) {
+      nsCOMPtr<nsIFrameLoader> fl = frameLoaderOwner->GetFrameLoader();
+      nsCOMPtr<nsIDocShell> docShell;
+      if (fl && NS_SUCCEEDED(fl->GetDocShell(getter_AddRefs(docShell))) && docShell) {
+        outerWindow = do_GetInterface(docShell);
+      }
+    } else {
+      outerWindow = aLoadingContext->OwnerDoc()->GetWindow();
+    }
+
+    if (outerWindow) {
+      nsCOMPtr<nsPIDOMWindow> inner = outerWindow->GetCurrentInnerWindow();
+      mInnerWindowID = inner ? inner->WindowID() : 0;
+      mOuterWindowID = outerWindow->WindowID();
+
+      nsCOMPtr<nsIDOMWindow> parent;
+      outerWindow->GetParent(getter_AddRefs(parent));
+      nsCOMPtr<nsPIDOMWindow> piParent = do_QueryInterface(parent);
+      mParentOuterWindowID = piParent->WindowID();
+    }
+  }
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
-                   uint32_t aInnerWindowID)
+                   uint64_t aInnerWindowID,
+                   uint64_t aOuterWindowID,
+                   uint64_t aParentOuterWindowID)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mSecurityFlags(aSecurityFlags)
   , mContentPolicyType(aContentPolicyType)
   , mInnerWindowID(aInnerWindowID)
+  , mOuterWindowID(aOuterWindowID)
+  , mParentOuterWindowID(aParentOuterWindowID)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
 }
 
 LoadInfo::~LoadInfo()
 {
 }
@@ -149,15 +186,29 @@ LoadInfo::GetBaseURI(nsIURI** aBaseURI)
 
 nsIURI*
 LoadInfo::BaseURI()
 {
   return mBaseURI;
 }
 
 NS_IMETHODIMP
-LoadInfo::GetInnerWindowID(uint32_t* aResult)
+LoadInfo::GetInnerWindowID(uint64_t* aResult)
 {
   *aResult = mInnerWindowID;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetOuterWindowID(uint64_t* aResult)
+{
+  *aResult = mOuterWindowID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetParentOuterWindowID(uint64_t* aResult)
+{
+  *aResult = mParentOuterWindowID;
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -48,29 +48,33 @@ public:
 private:
   // private constructor that is only allowed to be called from within
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
-           uint32_t aInnerWindowID);
+           uint64_t aInnerWindowID,
+           uint64_t aOuterWindowID,
+           uint64_t aParentOuterWindowID);
 
   friend class net::HttpChannelParent;
   friend class net::FTPChannelParent;
   friend class net::WebSocketChannelParent;
 
   ~LoadInfo();
 
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsWeakPtr mLoadingContext;
   nsSecurityFlags mSecurityFlags;
   nsContentPolicyType mContentPolicyType;
   nsCOMPtr<nsIURI> mBaseURI;
-  uint32_t mInnerWindowID;
+  uint64_t mInnerWindowID;
+  uint64_t mOuterWindowID;
+  uint64_t mParentOuterWindowID;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -12,17 +12,17 @@ interface nsINode;
 interface nsIPrincipal;
 interface nsIURI;
 
 typedef unsigned long nsSecurityFlags;
 
 /**
  * An nsILoadOwner represents per-load information about who started the load.
  */
-[scriptable, builtinclass, uuid(768a1f20-57d4-462a-812a-41c04e5d1e19)]
+[scriptable, builtinclass, uuid(dcf54f49-2d63-4c34-9da1-54df235f354c)]
 interface nsILoadInfo : nsISupports
 {
   /**
    * No special security flags:
    */
   const unsigned long SEC_NORMAL = 0;
 
   /**
@@ -177,27 +177,49 @@ interface nsILoadInfo : nsISupports
 
   /**
    * A C++-friendly version of baseURI.
    */
   [noscript, notxpcom, nostdcall, binaryname(BaseURI)]
   nsIURI binaryBaseURI();
 
   /**
-   * The innerWindowId of the loadingDocument, used to identify
-   * the loadingDocument in e10s where the loadingDocument is
-   * not available.
+   * Typically these are the window IDs of the window in which the element being
+   * loaded lives. However, if the element being loaded is <frame
+   * src="foo.html"> (or, more generally, if the element QIs to
+   * nsIFrameLoaderOwner) then the window IDs are for the window containing the
+   * foo.html document. In this case, parentOuterWindowID is the window ID of
+   * the window containing the <frame> element.
    *
-   * Warning: If the loadingDocument is null, then the
-   * innerWindowId is 0.
+   * Note that these window IDs can be 0 if the window is not
+   * available. parentOuterWindowID will be the same as outerWindowID if the
+   * window has no parent.
    */
-  readonly attribute unsigned long innerWindowID;
+  readonly attribute unsigned long long innerWindowID;
+  readonly attribute unsigned long long outerWindowID;
+  readonly attribute unsigned long long parentOuterWindowID;
 
 %{ C++
-  inline uint32_t GetInnerWindowID()
+  inline uint64_t GetInnerWindowID()
   {
-    uint32_t result;
+    uint64_t result;
     mozilla::DebugOnly<nsresult> rv = GetInnerWindowID(&result);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     return result;
   }
-%}
+
+  inline uint64_t GetOuterWindowID()
+  {
+    uint64_t result;
+    mozilla::DebugOnly<nsresult> rv = GetOuterWindowID(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+
+  inline uint64_t GetParentOuterWindowID()
+  {
+    uint64_t result;
+    mozilla::DebugOnly<nsresult> rv = GetParentOuterWindowID(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+  %}
 };
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -1306,17 +1306,17 @@ nsUDPSocket::Send(const nsACString &aHos
                   uint32_t *_retval)
 {
   NS_ENSURE_ARG(aData);
   NS_ENSURE_ARG_POINTER(_retval);
 
   *_retval = 0;
 
   FallibleTArray<uint8_t> fallibleArray;
-  if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
+  if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray);
 
   nsresult rv = ResolveHost(aHost, listener);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1364,17 +1364,17 @@ nsUDPSocket::SendWithAddress(const NetAd
     if (count < 0) {
       PRErrorCode code = PR_GetError();
       return ErrorAccordingToNSPR(code);
     }
     this->AddOutputBytes(count);
     *_retval = count;
   } else {
     FallibleTArray<uint8_t> fallibleArray;
-    if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
+    if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray),
                                  NS_DISPATCH_NORMAL);
     NS_ENSURE_SUCCESS(rv, rv);
     *_retval = aDataLength;
   }
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -60,17 +60,19 @@ struct HttpChannelOpenArgs
   nsCString                   appCacheClientID;
   bool                        allowSpdy;
   bool                        allowAltSvc;
   OptionalFileDescriptorSet   fds;
   PrincipalInfo               requestingPrincipalInfo;
   PrincipalInfo               triggeringPrincipalInfo;
   uint32_t                    securityFlags;
   uint32_t                    contentPolicyType;
-  uint32_t                    innerWindowID;
+  uint64_t                    innerWindowID;
+  uint64_t                    outerWindowID;
+  uint64_t                    parentOuterWindowID;
   OptionalHttpResponseHead    synthesizedResponseHead;
   uint32_t                    cacheKey;
 };
 
 struct HttpChannelConnectArgs
 {
   uint32_t channelId;
   bool shouldIntercept;
@@ -91,17 +93,19 @@ struct FTPChannelOpenArgs
   URIParams                 uri;
   uint64_t                  startPos;
   nsCString                 entityID;
   OptionalInputStreamParams uploadStream;
   PrincipalInfo             requestingPrincipalInfo;
   PrincipalInfo             triggeringPrincipalInfo;
   uint32_t                  securityFlags;
   uint32_t                  contentPolicyType;
-  uint32_t                  innerWindowID;
+  uint64_t                  innerWindowID;
+  uint64_t                  outerWindowID;
+  uint64_t                  parentOuterWindowID;
 };
 
 struct FTPChannelConnectArgs
 {
   uint32_t channelId;
 };
 
 union FTPChannelCreationArgs
@@ -137,13 +141,15 @@ struct RtspChannelConnectArgs
 //-----------------------------------------------------------------------------
 
 struct WebSocketLoadInfoArgs
 {
   PrincipalInfo requestingPrincipalInfo;
   PrincipalInfo triggeringPrincipalInfo;
   uint32_t      securityFlags;
   uint32_t      contentPolicyType;
-  uint32_t      innerWindowID;
+  uint64_t      innerWindowID;
+  uint64_t      outerWindowID;
+  uint64_t      parentOuterWindowID;
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -158,27 +158,31 @@ propagateLoadInfo(nsILoadInfo *aLoadInfo
 
     mozilla::ipc::PrincipalToPrincipalInfo(aLoadInfo->TriggeringPrincipal(),
                                            &triggeringPrincipalInfo);
     openArgs.triggeringPrincipalInfo() = triggeringPrincipalInfo;
 
     openArgs.securityFlags() = aLoadInfo->GetSecurityFlags();
     openArgs.contentPolicyType() = aLoadInfo->GetContentPolicyType();
     openArgs.innerWindowID() = aLoadInfo->GetInnerWindowID();
+    openArgs.outerWindowID() = aLoadInfo->GetOuterWindowID();
+    openArgs.parentOuterWindowID() = aLoadInfo->GetParentOuterWindowID();
     return;
   }
 
   // use default values if no loadInfo is provided
   mozilla::ipc::PrincipalToPrincipalInfo(nsContentUtils::GetSystemPrincipal(),
                                          &requestingPrincipalInfo);
   openArgs.requestingPrincipalInfo() = requestingPrincipalInfo;
   openArgs.triggeringPrincipalInfo() = requestingPrincipalInfo;
   openArgs.securityFlags() = nsILoadInfo::SEC_NORMAL;
   openArgs.contentPolicyType() = nsIContentPolicy::TYPE_OTHER;
   openArgs.innerWindowID() = 0;
+  openArgs.outerWindowID() = 0;
+  openArgs.parentOuterWindowID() = 0;
 }
 
 NS_IMETHODIMP
 FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
 {
   LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
 
   NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -84,17 +84,18 @@ bool
 FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
 {
   switch (aArgs.type()) {
   case FTPChannelCreationArgs::TFTPChannelOpenArgs:
   {
     const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
     return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
                        a.requestingPrincipalInfo(), a.triggeringPrincipalInfo(),
-                       a.securityFlags(), a.contentPolicyType(), a.innerWindowID());
+                       a.securityFlags(), a.contentPolicyType(),
+                       a.innerWindowID(), a.outerWindowID(), a.parentOuterWindowID());
   }
   case FTPChannelCreationArgs::TFTPChannelConnectArgs:
   {
     const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
     return ConnectChannel(cArgs.channelId());
   }
   default:
     NS_NOTREACHED("unknown open type");
@@ -106,17 +107,19 @@ bool
 FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
                               const uint64_t& aStartPos,
                               const nsCString& aEntityID,
                               const OptionalInputStreamParams& aUploadStream,
                               const ipc::PrincipalInfo& aRequestingPrincipalInfo,
                               const ipc::PrincipalInfo& aTriggeringPrincipalInfo,
                               const uint32_t& aSecurityFlags,
                               const uint32_t& aContentPolicyType,
-                              const uint32_t& aInnerWindowID)
+                              const uint64_t& aInnerWindowID,
+                              const uint64_t& aOuterWindowID,
+                              const uint64_t& aParentOuterWindowID)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri)
       return false;
 
 #ifdef DEBUG
   nsCString uriSpec;
   uri->GetSpec(uriSpec);
@@ -149,17 +152,17 @@ FTPChannelParent::DoAsyncOpen(const URIP
     mozilla::ipc::PrincipalInfoToPrincipal(aTriggeringPrincipalInfo, &rv);
   if (NS_FAILED(rv)) {
     return SendFailedAsyncOpen(rv);
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(requestingPrincipal, triggeringPrincipal,
                           aSecurityFlags, aContentPolicyType,
-                          aInnerWindowID);
+                          aInnerWindowID, aOuterWindowID, aParentOuterWindowID);
 
   nsCOMPtr<nsIChannel> chan;
   rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo,
                              nullptr, nullptr,
                              nsIRequest::LOAD_NORMAL, ios);
 
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
--- a/netwerk/protocol/ftp/FTPChannelParent.h
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -63,17 +63,19 @@ protected:
 
   bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
                    const nsCString& aEntityID,
                    const OptionalInputStreamParams& aUploadStream,
                    const ipc::PrincipalInfo& aRequestingPrincipalInfo,
                    const ipc::PrincipalInfo& aTriggeringPrincipalInfo,
                    const uint32_t& aSecurityFlags,
                    const uint32_t& aContentPolicyType,
-                   const uint32_t& aInnerWindowID);
+                   const uint64_t& aInnerWindowID,
+                   const uint64_t& aOuterWindowID,
+                   const uint64_t& aParentOuterWindowID);
 
   // used to connect redirected-to channel in parent with just created
   // ChildChannel.  Used during HTTP->FTP redirects.
   bool ConnectChannel(const uint32_t& channelId);
 
   virtual bool RecvCancel(const nsresult& status) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1484,27 +1484,31 @@ propagateLoadInfo(nsILoadInfo *aLoadInfo
 
     mozilla::ipc::PrincipalToPrincipalInfo(aLoadInfo->TriggeringPrincipal(),
                                            &triggeringPrincipalInfo);
     openArgs.triggeringPrincipalInfo() = triggeringPrincipalInfo;
 
     openArgs.securityFlags() = aLoadInfo->GetSecurityFlags();
     openArgs.contentPolicyType() = aLoadInfo->GetContentPolicyType();
     openArgs.innerWindowID() = aLoadInfo->GetInnerWindowID();
+    openArgs.outerWindowID() = aLoadInfo->GetOuterWindowID();
+    openArgs.parentOuterWindowID() = aLoadInfo->GetParentOuterWindowID();
     return;
   }
 
   // use default values if no loadInfo is provided
   mozilla::ipc::PrincipalToPrincipalInfo(nsContentUtils::GetSystemPrincipal(),
                                          &requestingPrincipalInfo);
   openArgs.requestingPrincipalInfo() = requestingPrincipalInfo;
   openArgs.triggeringPrincipalInfo() = requestingPrincipalInfo;
   openArgs.securityFlags() = nsILoadInfo::SEC_NORMAL;
   openArgs.contentPolicyType() = nsIContentPolicy::TYPE_OTHER;
   openArgs.innerWindowID() = 0;
+  openArgs.outerWindowID() = 0;
+  openArgs.parentOuterWindowID() = 0;
 }
 
 NS_IMETHODIMP
 HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo)
 {
   NS_ENSURE_ARG_POINTER(aSecurityInfo);
   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
   return NS_OK;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -116,17 +116,18 @@ HttpChannelParent::Init(const HttpChanne
                        a.loadFlags(), a.requestHeaders(),
                        a.requestMethod(), a.uploadStream(),
                        a.uploadStreamHasHeaders(), a.priority(), a.classOfService(),
                        a.redirectionLimit(), a.allowPipelining(), a.allowSTS(),
                        a.thirdPartyFlags(), a.resumeAt(), a.startPos(),
                        a.entityID(), a.chooseApplicationCache(),
                        a.appCacheClientID(), a.allowSpdy(), a.allowAltSvc(), a.fds(),
                        a.requestingPrincipalInfo(), a.triggeringPrincipalInfo(),
-                       a.securityFlags(), a.contentPolicyType(), a.innerWindowID(),
+                       a.securityFlags(), a.contentPolicyType(),
+                       a.innerWindowID(), a.outerWindowID(), a.parentOuterWindowID(),
                        a.synthesizedResponseHead(), a.cacheKey());
   }
   case HttpChannelCreationArgs::THttpChannelConnectArgs:
   {
     const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
     return ConnectChannel(cArgs.channelId(), cArgs.shouldIntercept());
   }
   default:
@@ -275,17 +276,19 @@ HttpChannelParent::DoAsyncOpen(  const U
                                  const nsCString&           appCacheClientID,
                                  const bool&                allowSpdy,
                                  const bool&                allowAltSvc,
                                  const OptionalFileDescriptorSet& aFds,
                                  const ipc::PrincipalInfo&  aRequestingPrincipalInfo,
                                  const ipc::PrincipalInfo&  aTriggeringPrincipalInfo,
                                  const uint32_t&            aSecurityFlags,
                                  const uint32_t&            aContentPolicyType,
-                                 const uint32_t&            aInnerWindowID,
+                                 const uint64_t&            aInnerWindowID,
+                                 const uint64_t&            aOuterWindowID,
+                                 const uint64_t&            aParentOuterWindowID,
                                  const OptionalHttpResponseHead& aSynthesizedResponseHead,
                                  const uint32_t&            aCacheKey)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
     return false;
@@ -330,17 +333,17 @@ HttpChannelParent::DoAsyncOpen(  const U
     loadFlags |= nsICachingChannel::LOAD_ONLY_FROM_CACHE;
     loadFlags |= nsIRequest::LOAD_FROM_CACHE;
     loadFlags |= nsICachingChannel::LOAD_NO_NETWORK_IO;
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(requestingPrincipal, triggeringPrincipal,
                           aSecurityFlags, aContentPolicyType,
-                          aInnerWindowID);
+                          aInnerWindowID, aOuterWindowID, aParentOuterWindowID);
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannelInternal(getter_AddRefs(channel), uri, loadInfo,
                              nullptr, nullptr, loadFlags, ios);
 
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -115,17 +115,19 @@ protected:
                    const nsCString&           appCacheClientID,
                    const bool&                allowSpdy,
                    const bool&                allowAltSvc,
                    const OptionalFileDescriptorSet& aFds,
                    const ipc::PrincipalInfo&  aRequestingPrincipalInfo,
                    const ipc::PrincipalInfo&  aTriggeringPrincipalInfo,
                    const uint32_t&            aSecurityFlags,
                    const uint32_t&            aContentPolicyType,
-                   const uint32_t&            aInnerWindowID,
+                   const uint64_t&            aInnerWindowID,
+                   const uint64_t&            aOuterWindowID,
+                   const uint64_t&            aParentOuterWindowID,
                    const OptionalHttpResponseHead& aSynthesizedResponseHead,
                    const uint32_t&            aCacheKey);
 
   virtual bool RecvSetPriority(const uint16_t& priority) override;
   virtual bool RecvSetClassOfService(const uint32_t& cos) override;
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
--- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp
@@ -465,16 +465,18 @@ propagateLoadInfo(nsILoadInfo *aLoadInfo
                                          &requestingPrincipalInfo);
   wsArgs.requestingPrincipalInfo() = requestingPrincipalInfo;
   mozilla::ipc::PrincipalToPrincipalInfo(aLoadInfo->TriggeringPrincipal(),
                                          &triggeringPrincipalInfo);
   wsArgs.triggeringPrincipalInfo() = triggeringPrincipalInfo;
   wsArgs.securityFlags() = aLoadInfo->GetSecurityFlags();
   wsArgs.contentPolicyType() = aLoadInfo->GetContentPolicyType();
   wsArgs.innerWindowID() = aLoadInfo->GetInnerWindowID();
+  wsArgs.outerWindowID() = aLoadInfo->GetOuterWindowID();
+  wsArgs.parentOuterWindowID() = aLoadInfo->GetParentOuterWindowID();
 }
 
 NS_IMETHODIMP
 WebSocketChannelChild::AsyncOpen(nsIURI *aURI,
                                  const nsACString &aOrigin,
                                  nsIWebSocketListener *aListener,
                                  nsISupports *aContext)
 {
--- a/netwerk/protocol/websocket/WebSocketChannelParent.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannelParent.cpp
@@ -106,17 +106,19 @@ WebSocketChannelParent::RecvAsyncOpen(co
   if (NS_FAILED(rv)) {
     goto fail;
   }
 
   loadInfo = new LoadInfo(requestingPrincipal,
                           triggeringPrincipal,
                           aLoadInfoArgs.securityFlags(),
                           aLoadInfoArgs.contentPolicyType(),
-                          aLoadInfoArgs.innerWindowID());
+                          aLoadInfoArgs.innerWindowID(),
+                          aLoadInfoArgs.outerWindowID(),
+                          aLoadInfoArgs.parentOuterWindowID());
   rv = mChannel->SetLoadInfo(loadInfo);
   if (NS_FAILED(rv)) {
     goto fail;
   }
 
   rv = mChannel->SetNotificationCallbacks(this);
   if (NS_FAILED(rv))
     goto fail;
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -695,16 +695,30 @@ class MachCommandBase(MozbuildObject):
                 print('')
                 print('mozconfig output:')
                 print('')
                 for line in e.output:
                     print(line)
 
             sys.exit(1)
 
+        # Always keep a log of the last command, but don't do that for mach
+        # invokations from scripts (especially not the ones done by the build
+        # system itself).
+        if (self.log_manager and self.log_manager.terminal and
+                not getattr(self, 'NO_AUTO_LOG', False)):
+            self._ensure_state_subdir_exists('.')
+            logfile = self._get_state_filename('last_log.json')
+            try:
+                fd = open(logfile, "wb")
+                self.log_manager.add_json_handler(fd)
+            except Exception as e:
+                self.log(logging.WARNING, 'mach', {'error': e},
+                         'Log will not be kept for this command: {error}.')
+
 
 class MachCommandConditions(object):
     """A series of commonly used condition functions which can be applied to
     mach commands with providers deriving from MachCommandBase.
     """
     @staticmethod
     def is_firefox(cls):
         """Must have a Firefox build."""
--- a/python/mozbuild/mozbuild/frontend/mach_commands.py
+++ b/python/mozbuild/mozbuild/frontend/mach_commands.py
@@ -1,15 +1,16 @@
 # 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/.
 
 from __future__ import print_function, unicode_literals
 
 from collections import defaultdict
+import os
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
     SubCommand,
 )
 
@@ -83,70 +84,98 @@ class MozbuildFileCommands(MachCommandBa
 
     @Command('file-info', category='build-dev',
              description='Query for metadata about files.')
     def file_info(self):
         pass
 
     @SubCommand('file-info', 'bugzilla-component',
                 'Show Bugzilla component info for files listed.')
+    @CommandArgument('-r', '--rev',
+                     help='Version control revision to look up info from')
     @CommandArgument('paths', nargs='+',
                      help='Paths whose data to query')
-    def file_info_bugzilla(self, paths):
+    def file_info_bugzilla(self, paths, rev=None):
         components = defaultdict(set)
         try:
-            for p, m in self._get_files_info(paths).items():
+            for p, m in self._get_files_info(paths, rev=rev).items():
                 components[m.get('BUG_COMPONENT')].add(p)
         except InvalidPathException as e:
             print(e.message)
             return 1
 
         for component, files in sorted(components.items(), key=lambda x: (x is None, x)):
             print('%s :: %s' % (component.product, component.component) if component else 'UNKNOWN')
             for f in sorted(files):
                 print('  %s' % f)
 
     @SubCommand('file-info', 'missing-bugzilla',
                 'Show files missing Bugzilla component info')
+    @CommandArgument('-r', '--rev',
+                     help='Version control revision to look up info from')
     @CommandArgument('paths', nargs='+',
                      help='Paths whose data to query')
-    def file_info_missing_bugzilla(self, paths):
+    def file_info_missing_bugzilla(self, paths, rev=None):
         try:
-            for p, m in sorted(self._get_files_info(paths).items()):
+            for p, m in sorted(self._get_files_info(paths, rev=rev).items()):
                 if 'BUG_COMPONENT' not in m:
                     print(p)
         except InvalidPathException as e:
             print(e.message)
             return 1
 
-    def _get_reader(self):
-        from mozbuild.frontend.reader import BuildReader, EmptyConfig
+    def _get_reader(self, finder):
+        from mozbuild.frontend.reader import (
+            BuildReader,
+            EmptyConfig,
+        )
+
         config = EmptyConfig(self.topsrcdir)
-        return BuildReader(config)
+        return BuildReader(config, finder=finder)
 
-    def _get_files_info(self, paths):
-        from mozpack.files import FileFinder
+    def _get_files_info(self, paths, rev=None):
+        from mozbuild.frontend.reader import default_finder
+        from mozpack.files import FileFinder, MercurialRevisionFinder
 
         # Normalize to relative from topsrcdir.
         relpaths = []
         for p in paths:
             a = mozpath.abspath(p)
             if not mozpath.basedir(a, [self.topsrcdir]):
                 raise InvalidPathException('path is outside topsrcdir: %s' % p)
 
             relpaths.append(mozpath.relpath(a, self.topsrcdir))
 
+        repo = None
+        if rev:
+            hg_path = os.path.join(self.topsrcdir, '.hg')
+            if not os.path.exists(hg_path):
+                raise InvalidPathException('a Mercurial repo is required '
+                        'when specifying a revision')
+
+            repo = self.topsrcdir
+
+        # We need two finders because the reader's finder operates on
+        # absolute paths.
         finder = FileFinder(self.topsrcdir, find_executables=False)
+        if repo:
+            reader_finder = MercurialRevisionFinder(repo, rev=rev,
+                                                    recognize_repo_paths=True)
+        else:
+            reader_finder = default_finder
 
         # Expand wildcards.
         allpaths = []
         for p in relpaths:
             if '*' not in p:
                 if p not in allpaths:
                     allpaths.append(p)
                 continue
 
+            if repo:
+                raise InvalidPathException('cannot use wildcard in version control mode')
+
             for path, f in finder.find(p):
                 if path not in allpaths:
                     allpaths.append(path)
 
-        reader = self._get_reader()
+        reader = self._get_reader(finder=reader_finder)
         return reader.files_info(allpaths)
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -34,30 +34,30 @@ from collections import (
     OrderedDict,
 )
 from io import StringIO
 
 from mozbuild.util import (
     EmptyValue,
     memoize,
     ReadOnlyDefaultDict,
-    ReadOnlyDict,
 )
 
 from mozbuild.backend.configenvironment import ConfigEnvironment
 
 from mozpack.files import FileFinder
 import mozpack.path as mozpath
 
 from .data import (
     AndroidEclipseProjectData,
     JavaJarData,
 )
 
 from .sandbox import (
+    default_finder,
     SandboxError,
     SandboxExecutionError,
     SandboxLoadError,
     Sandbox,
 )
 
 from .context import (
     Context,
@@ -167,20 +167,20 @@ class MozbuildSandbox(Sandbox):
     We expose a few useful functions and expose the set of variables defining
     Mozilla's build system.
 
     context is a Context instance.
 
     metadata is a dict of metadata that can be used during the sandbox
     evaluation.
     """
-    def __init__(self, context, metadata={}):
+    def __init__(self, context, metadata={}, finder=default_finder):
         assert isinstance(context, Context)
 
-        Sandbox.__init__(self, context)
+        Sandbox.__init__(self, context, finder=finder)
 
         self._log = logging.getLogger(__name__)
 
         self.metadata = dict(metadata)
         exports = self.metadata.get('exports', {})
         self.exports = set(exports.keys())
         context.update(exports)
         self.templates = self.metadata.setdefault('templates', {})
@@ -193,19 +193,26 @@ class MozbuildSandbox(Sandbox):
     def __getitem__(self, key):
         if key in self.special_variables:
             return self.special_variables[key][0](self._context)
         if key in self.functions:
             return self._create_function(self.functions[key])
         if key in self.subcontext_types:
             return self._create_subcontext(self.subcontext_types[key])
         if key in self.templates:
-            return self._create_template_function(self.templates[key])
+            return self._create_template_wrapper(self.templates[key])
         return Sandbox.__getitem__(self, key)
 
+    def __contains__(self, key):
+        if any(key in d for d in (self.special_variables, self.functions,
+                                  self.subcontext_types, self.templates)):
+            return True
+
+        return Sandbox.__contains__(self, key)
+
     def __setitem__(self, key, value):
         if key in self.special_variables or key in self.functions or key in self.subcontext_types:
             raise KeyError('Cannot set "%s" because it is a reserved keyword'
                            % key)
         if key in self.exports:
             self._context[key] = value
             self.exports.remove(key)
             return
@@ -303,75 +310,33 @@ class MozbuildSandbox(Sandbox):
     def _warning(self, message):
         # FUTURE consider capturing warnings in a variable instead of printing.
         print('WARNING: %s' % message, file=sys.stderr)
 
     def _error(self, message):
         raise SandboxCalledError(self._context.source_stack, message)
 
     def _template_decorator(self, func):
-        """Registers template as expected by _create_template_function.
-
-        The template data consists of:
-        - the function object as it comes from the sandbox evaluation of the
-          template declaration.
-        - its code, modified as described in the comments of this method.
-        - the path of the file containing the template definition.
-        """
+        """Registers a template function."""
 
         if not inspect.isfunction(func):
             raise Exception('`template` is a function decorator. You must '
                 'use it as `@template` preceding a function declaration.')
 
         name = func.func_name
 
         if name in self.templates:
             raise KeyError(
                 'A template named "%s" was already declared in %s.' % (name,
-                self.templates[name][2]))
+                self.templates[name].path))
 
         if name.islower() or name.isupper() or name[0].islower():
             raise NameError('Template function names must be CamelCase.')
 
-        lines, firstlineno = inspect.getsourcelines(func)
-        first_op = None
-        generator = tokenize.generate_tokens(iter(lines).next)
-        # Find the first indent token in the source of this template function,
-        # which corresponds to the beginning of the function body.
-        for typ, s, begin, end, line in generator:
-            if typ == tokenize.OP:
-                first_op = True
-            if first_op and typ == tokenize.INDENT:
-                break
-        if typ != tokenize.INDENT:
-            # This should never happen.
-            raise Exception('Could not find the first line of the template %s' %
-                func.func_name)
-        # The code of the template in moz.build looks like this:
-        # m      def Foo(args):
-        # n          FOO = 'bar'
-        # n+1        (...)
-        #
-        # where,
-        # - m is firstlineno - 1,
-        # - n is usually m + 1, but in case the function signature takes more
-        # lines, is really m + begin[0] - 1
-        #
-        # We want that to be replaced with:
-        # m       if True:
-        # n           FOO = 'bar'
-        # n+1         (...)
-        #
-        # (this is simpler than trying to deindent the function body)
-        # So we need to prepend with n - 1 newlines so that line numbers
-        # are unchanged.
-        code = '\n' * (firstlineno + begin[0] - 3) + 'if True:\n'
-        code += ''.join(lines[begin[0] - 1:])
-
-        self.templates[name] = func, code, self._context.current_path
+        self.templates[name] = TemplateFunction(func, self)
 
     @memoize
     def _create_subcontext(self, cls):
         """Return a function object that creates SubContext instances."""
         def fn(*args, **kwargs):
             return cls(self._context, *args, **kwargs)
 
         return fn
@@ -393,51 +358,47 @@ class MozbuildSandbox(Sandbox):
                         arg = type(arg)
                 return arg
             args = [coerce(arg, type) for arg, type in zip(args, args_def)]
             return func(self)(*args)
 
         return function
 
     @memoize
-    def _create_template_function(self, template):
+    def _create_template_wrapper(self, template):
         """Returns a function object for use within the sandbox for the given
-        template.
+        TemplateFunction instance..
 
         When a moz.build file contains a reference to a template call, the
         sandbox needs a function to execute. This is what this method returns.
         That function creates a new sandbox for execution of the template.
         After the template is executed, the data from its execution is merged
         with the context of the calling sandbox.
         """
-        func, code, path = template
-
-        def template_function(*args, **kwargs):
+        def template_wrapper(*args, **kwargs):
             context = TemplateContext(
-                template=func.func_name,
+                template=template.name,
                 allowed_variables=self._context._allowed_variables,
                 config=self._context.config)
             context.add_source(self._context.current_path)
             for p in self._context.all_paths:
                 context.add_source(p)
 
             sandbox = MozbuildSandbox(context, metadata={
                 # We should arguably set these defaults to something else.
                 # Templates, for example, should arguably come from the state
                 # of the sandbox from when the template was declared, not when
                 # it was instantiated. Bug 1137319.
                 'functions': self.metadata.get('functions', {}),
                 'special_variables': self.metadata.get('special_variables', {}),
                 'subcontexts': self.metadata.get('subcontexts', {}),
                 'templates': self.metadata.get('templates', {})
-            })
-            for k, v in inspect.getcallargs(func, *args, **kwargs).items():
-                sandbox[k] = v
+            }, finder=self._finder)
 
-            sandbox.exec_source(code, path, becomes_current_path=False)
+            template.exec_in_sandbox(sandbox, *args, **kwargs)
 
             # This is gross, but allows the merge to happen. Eventually, the
             # merging will go away and template contexts emitted independently.
             klass = self._context.__class__
             self._context.__class__ = TemplateContext
             # The sandbox will do all the necessary checks for these merges.
             for key, value in context.items():
                 if isinstance(value, dict):
@@ -446,17 +407,115 @@ class MozbuildSandbox(Sandbox):
                     self[key] += value
                 else:
                     self[key] = value
             self._context.__class__ = klass
 
             for p in context.all_paths:
                 self._context.add_source(p)
 
-        return template_function
+        return template_wrapper
+
+
+class TemplateFunction(object):
+    def __init__(self, func, sandbox):
+        self.path = func.func_code.co_filename
+        self.name = func.func_name
+
+        code = func.func_code
+        firstlineno = code.co_firstlineno
+        lines = sandbox._current_source.splitlines(True)
+        lines = inspect.getblock(lines[firstlineno - 1:])
+
+        # The code lines we get out of inspect.getsourcelines look like
+        #   @template
+        #   def Template(*args, **kwargs):
+        #       VAR = 'value'
+        #       ...
+        func_ast = ast.parse(''.join(lines), self.path)
+        # Remove decorators
+        func_ast.body[0].decorator_list = []
+        # Adjust line numbers accordingly
+        ast.increment_lineno(func_ast, firstlineno - 1)
+
+        # When using a custom dictionary for function globals/locals, Cpython
+        # actually never calls __getitem__ and __setitem__, so we need to
+        # modify the AST so that accesses to globals are properly directed
+        # to a dict.
+        self._global_name = b'_data' # AST wants str for this, not unicode
+        # In case '_data' is a name used for a variable in the function code,
+        # prepend more underscores until we find an unused name.
+        while (self._global_name in code.co_names or
+                self._global_name in code.co_varnames):
+            self._global_name += '_'
+        func_ast = self.RewriteName(sandbox, self._global_name).visit(func_ast)
+
+        # Execute the rewritten code. That code now looks like:
+        #   def Template(*args, **kwargs):
+        #       _data['VAR'] = 'value'
+        #       ...
+        # The result of executing this code is the creation of a 'Template'
+        # function object in the global namespace.
+        glob = {'__builtins__': sandbox._builtins}
+        func = types.FunctionType(
+            compile(func_ast, self.path, 'exec'),
+            glob,
+            self.name,
+            func.func_defaults,
+            func.func_closure,
+        )
+        func()
+
+        self._func = glob[self.name]
+
+    def exec_in_sandbox(self, sandbox, *args, **kwargs):
+        """Executes the template function in the given sandbox."""
+        # Create a new function object associated with the execution sandbox
+        glob = {
+            self._global_name: sandbox,
+            '__builtins__': sandbox._builtins
+        }
+        func = types.FunctionType(
+            self._func.func_code,
+            glob,
+            self.name,
+            self._func.func_defaults,
+            self._func.func_closure
+        )
+        sandbox.exec_function(func, args, kwargs, self.path,
+                              becomes_current_path=False)
+
+    class RewriteName(ast.NodeTransformer):
+        """AST Node Transformer to rewrite variable accesses to go through
+        a dict.
+        """
+        def __init__(self, sandbox, global_name):
+            self._sandbox = sandbox
+            self._global_name = global_name
+
+        def visit_Str(self, node):
+            # String nodes we got from the AST parser are str, but we want
+            # unicode literals everywhere, so transform them.
+            node.s = unicode(node.s)
+            return node
+
+        def visit_Name(self, node):
+            # Modify uppercase variable references and names known to the
+            # sandbox as if they were retrieved from a dict instead.
+            if not node.id.isupper() and node.id not in self._sandbox:
+                return node
+
+            def c(new_node):
+                return ast.copy_location(new_node, node)
+
+            return c(ast.Subscript(
+                value=c(ast.Name(id=self._global_name, ctx=ast.Load())),
+                slice=c(ast.Index(value=c(ast.Str(s=node.id)))),
+                ctx=node.ctx
+            ))
 
 
 class SandboxValidationError(Exception):
     """Represents an error encountered when validating sandbox results."""
     def __init__(self, message, context):
         Exception.__init__(self, message)
         self.context = context
 
@@ -584,17 +643,17 @@ class BuildReaderError(Exception):
         if trace:
             frames = traceback.extract_tb(trace)
         for frame in frames:
             if frame[0] == self.actual_file:
                 script_frame = frame
 
             # Reset if we enter a new execution context. This prevents errors
             # in this module from being attributes to a script.
-            elif frame[0] == __file__ and frame[2] == 'exec_source':
+            elif frame[0] == __file__ and frame[2] == 'exec_function':
                 script_frame = None
 
         if script_frame is not None:
             s.write('The error was triggered on line %d ' % script_frame[1])
             s.write('of this file:\n')
             s.write('\n')
             s.write('    %s\n' % script_frame[3])
             s.write('\n')
@@ -790,22 +849,23 @@ class BuildReader(object):
 
     The reader can optionally call a callable after each sandbox is evaluated
     but before its evaluated content is processed. This gives callers the
     opportunity to modify contexts before side-effects occur from their
     content. This callback receives the ``Context`` containing the result of
     each sandbox evaluation. Its return value is ignored.
     """
 
-    def __init__(self, config):
+    def __init__(self, config, finder=default_finder):
         self.config = config
 
         self._log = logging.getLogger(__name__)
         self._read_files = set()
         self._execution_stack = []
+        self._finder = finder
 
     def read_topsrcdir(self):
         """Read the tree of linked moz.build files.
 
         This starts with the tree's top-most moz.build file and descends into
         all linked moz.build files until all relevant files have been evaluated.
 
         This is a generator of Context instances. As each moz.build file is
@@ -1028,17 +1088,18 @@ class BuildReader(object):
         if mozpath.dirname(relpath) == 'js/src' and \
                 not config.substs.get('JS_STANDALONE'):
             config = ConfigEnvironment.from_config_status(
                 mozpath.join(topobjdir, reldir, 'config.status'))
             config.topobjdir = topobjdir
             config.external_source_dir = None
 
         context = Context(VARIABLES, config)
-        sandbox = MozbuildSandbox(context, metadata=metadata)
+        sandbox = MozbuildSandbox(context, metadata=metadata,
+                                  finder=self._finder)
         sandbox.exec_file(path)
         context.execution_time = time.time() - time_start
 
         # Yield main context before doing any processing. This gives immediate
         # consumers an opportunity to change state before our remaining
         # processing is performed.
         yield context
 
@@ -1064,17 +1125,17 @@ class BuildReader(object):
             # emitted after their parent, so accumulate the gyp contexts.
             # We could emit the parent context before processing gyp
             # configuration, but we need to add the gyp objdirs to that context
             # first.
             from .gyp_reader import read_from_gyp
             non_unified_sources = set()
             for s in gyp_dir.non_unified_sources:
                 source = SourcePath(context, s)