Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 07 Aug 2015 13:21:30 +0200
changeset 288457 90601e5616c31b85973c68a99d4800f98fc1f9c5
parent 288456 67b330295145157812c66e54fbacb6018e22042f (current diff)
parent 288423 3e51753a099f8014b3dc37ed3fa27b887842735b (diff)
child 288458 3e6d4d0f2e268ce676c0d9c77b523e79aed7c394
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone42.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/locales/en-US/chrome/browser/browser.dtd
build/autoconf/gcc-pr49911.m4
dom/workers/test/serviceworkers/client_focus_worker.js
dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
memory/jemalloc/Makefile.in
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -1127,16 +1127,20 @@ a11y::ProxyDestroyed(ProxyAccessible* aP
 }
 
 nsresult
 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
 {
   nsresult rv = Accessible::HandleAccEvent(aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (IPCAccessibilityActive()) {
+    return NS_OK;
+  }
+
     Accessible* accessible = aEvent->GetAccessible();
     NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
 
     // The accessible can become defunct if we have an xpcom event listener
     // which decides it would be fun to change the DOM and flush layout.
     if (accessible->IsDefunct())
         return NS_OK;
 
--- a/accessible/mac/AccessibleWrap.mm
+++ b/accessible/mac/AccessibleWrap.mm
@@ -89,16 +89,20 @@ AccessibleWrap::Shutdown ()
 nsresult
 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   nsresult rv = Accessible::HandleAccEvent(aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (IPCAccessibilityActive()) {
+    return NS_OK;
+  }
+
   uint32_t eventType = aEvent->GetEventType();
 
   // ignore everything but focus-changed, value-changed, caret and selection
   // events for now.
   if (eventType != nsIAccessibleEvent::EVENT_FOCUS &&
       eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
       eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
       eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -1203,16 +1203,20 @@ AccessibleWrap::GetNativeInterface(void*
 // Accessible
 
 nsresult
 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
 {
   nsresult rv = Accessible::HandleAccEvent(aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (IPCAccessibilityActive()) {
+    return NS_OK;
+  }
+
   uint32_t eventType = aEvent->GetEventType();
 
   static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
                 "MSAA event map skewed");
 
   NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE);
 
   uint32_t winEvent = gWinEventMap[eventType];
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -14,17 +14,16 @@ builtin(include, build/autoconf/nspr-bui
 builtin(include, build/autoconf/nss.m4)dnl
 builtin(include, build/autoconf/pkg.m4)dnl
 builtin(include, build/autoconf/codeset.m4)dnl
 builtin(include, build/autoconf/altoptions.m4)dnl
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
 builtin(include, build/autoconf/mozcommonheader.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
-builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/llvm-pr8927.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
 builtin(include, build/autoconf/expandlibs.m4)dnl
 builtin(include, build/autoconf/arch.m4)dnl
 builtin(include, build/autoconf/android.m4)dnl
 builtin(include, build/autoconf/zlib.m4)dnl
 builtin(include, build/autoconf/linux.m4)dnl
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1148,18 +1148,19 @@ pref("dom.requestSync.enabled", true);
 // Resample touch events on b2g
 pref("gfx.touch.resample", true);
 
 // Comma separated list of activity names that can only be provided by
 // the system app in dev mode.
 pref("dom.activities.developer_mode_only", "import-app");
 
 // mulet apparently loads firefox.js as well as b2g.js, so we have to explicitly
-// disable serviceworkers here to get them disabled in mulet.
+// disable serviceworkers and push here to get them disabled in mulet.
 pref("dom.serviceWorkers.enabled", false);
+pref("dom.push.enabled", false);
 
 // Retain at most 10 processes' layers buffers
 pref("layers.compositor-lru-size", 10);
 
 // Enable Cardboard VR on mobile, assuming VR at all is enabled
 pref("dom.vr.cardboard.enabled", true);
 
 // In B2G by deafult any AudioChannelAgent is muted when created.
--- a/b2g/components/MailtoProtocolHandler.js
+++ b/b2g/components/MailtoProtocolHandler.js
@@ -35,16 +35,16 @@ MailtoProtocolHandler.prototype = {
     cpmm.sendAsyncMessage("mail-handler", {
       URI: aURI.spec,
       type: "mail" });
 
     throw Components.results.NS_ERROR_ILLEGAL_VALUE;
   },
 
   newChannel: function Proto_newChannel(aURI) {
-    return newChannel2(aURI, null);
+    return this.newChannel2(aURI, null);
   },
 
   classID: Components.ID("{50777e53-0331-4366-a191-900999be386c}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MailtoProtocolHandler]);
--- a/b2g/components/SmsProtocolHandler.js
+++ b/b2g/components/SmsProtocolHandler.js
@@ -61,16 +61,16 @@ SmsProtocolHandler.prototype = {
         type: "websms/sms",
         body: body });
     }
 
     throw Components.results.NS_ERROR_ILLEGAL_VALUE;
   },
 
   newChannel: function Proto_newChannel(aURI) {
-    return newChannel2(aURI, null);
+    return this.newChannel2(aURI, null);
   },
 
   classID: Components.ID("{81ca20cb-0dad-4e32-8566-979c8998bd73}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsProtocolHandler]);
--- a/b2g/components/TelProtocolHandler.js
+++ b/b2g/components/TelProtocolHandler.js
@@ -47,16 +47,16 @@ TelProtocolHandler.prototype = {
         number: number,
         type: "webtelephony/number" });
     }
 
     throw Components.results.NS_ERROR_ILLEGAL_VALUE;
   },
 
   newChannel: function Proto_newChannel(aURI) {
-    return newChannel2(aURI, null);
+    return this.newChannel2(aURI, null);
   },
 
   classID: Components.ID("{782775dd-7351-45ea-aff1-0ffa872cfdd2}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelProtocolHandler]);
--- 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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- 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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- 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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
   <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- 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="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- 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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "91068221506ff05692aa187ac314e15443db68fd", 
+        "git_revision": "59ce66c60e71b434061aeaf11e945814b234c355", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "ab36b7edaadc88ece94cc213db139d1615dadf64", 
+    "revision": "8ab354e8ef85b6ee872eb7eb1710a8e094c93b18", 
     "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="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
   <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="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="59ce66c60e71b434061aeaf11e945814b234c355"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1939,13 +1939,14 @@ pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
 pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW");
 
 pref("view_source.tab", true);
 
-// Enable Service Workers for desktop on non-release builds
-#ifndef RELEASE_BUILD
+// Enable ServiceWorkers for Push API consumers.
+// Interception is still disabled.
 pref("dom.serviceWorkers.enabled", true);
-pref("dom.serviceWorkers.interception.enabled", true);
-#endif
+
+// Enable Push API.
+pref("dom.push.enabled", true);
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -27,16 +27,17 @@
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()" reserved="true"/>
+    <command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/>
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
@@ -311,16 +312,17 @@
 
     <key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift"
          keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
     <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile"  modifiers="accel"/>
     <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
     <key id="printKb" key="&printCmd.commandkey;" command="cmd_print"  modifiers="accel"/>
     <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
     <key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/>
+    <key id="key_toggleMute" key="&toggleMuteCmd.key;" command="cmd_toggleMute" modifiers="alt,shift"/>
     <key id="key_undo"
          key="&undoCmd.key;"
          modifiers="accel"/>
 #ifdef XP_UNIX
     <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
 #else
     <key id="key_redo" key="&redoCmd.key;" modifiers="accel"/>
 #endif
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -515,16 +515,20 @@ ContentLinkHandler.init(this);
 
 // TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
 let pluginContent = new PluginContent(global);
 
 addEventListener("DOMWebNotificationClicked", function(event) {
   sendAsyncMessage("DOMWebNotificationClicked", {});
 }, false);
 
+addEventListener("DOMServiceWorkerFocusClient", function(event) {
+  sendAsyncMessage("DOMServiceWorkerFocusClient", {});
+}, false);
+
 ContentWebRTC.init();
 addMessageListener("webrtc:Allow", ContentWebRTC);
 addMessageListener("webrtc:Deny", ContentWebRTC);
 addMessageListener("webrtc:StopSharing", ContentWebRTC);
 addMessageListener("webrtc:StartBrowserSharing", () => {
   let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
   sendAsyncMessage("webrtc:response:StartBrowserSharing", {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -641,16 +641,22 @@ let DOMFullscreenHandler = {
       }
       case "MozDOMFullscreen:Exit": {
         sendAsyncMessage("DOMFullscreen:Exit");
         break;
       }
       case "MozDOMFullscreen:Entered":
       case "MozDOMFullscreen:Exited": {
         addEventListener("MozAfterPaint", this);
+        if (!content || !content.document.mozFullScreen) {
+          // If we receive any fullscreen change event, and find we are
+          // actually not in fullscreen, also ask the parent to exit to
+          // ensure that the parent always exits fullscreen when we do.
+          sendAsyncMessage("DOMFullscreen:Exit");
+        }
         break;
       }
       case "MozAfterPaint": {
         removeEventListener("MozAfterPaint", this);
         sendAsyncMessage("DOMFullscreen:Painted");
         break;
       }
     }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3897,30 +3897,30 @@
         <parameter name="event"/>
         <body><![CDATA[
           event.stopPropagation();
           var tab = document.tooltipNode;
           if (tab.localName != "tab") {
             event.preventDefault();
             return;
           }
-          var stringID, label;
+          var label;
           if (tab.mOverCloseButton) {
-            stringID = "tabs.closeTab.tooltip";
+            label = this.mStringBundle.getString("tabs.closeTab.tooltip");
           } else if (tab._overPlayingIcon) {
-            stringID = tab.linkedBrowser.audioMuted ?
-              "tabs.mutedAudio.tooltip" :
-              "tabs.playingAudio.tooltip";
+            var stringID = tab.linkedBrowser.audioMuted ?
+              "tabs.unmuteAudio.tooltip" :
+              "tabs.muteAudio.tooltip";
+            var key = document.getElementById("key_toggleMute");
+            var shortcut = ShortcutUtils.prettifyShortcut(key);
+            label = this.mStringBundle.getFormattedString(stringID, [shortcut]);
           } else {
             label = tab.getAttribute("label") +
                       (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : "");
           }
-          if (stringID && !label) {
-            label = this.mStringBundle.getString(stringID);
-          }
           event.target.setAttribute("label", label);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
@@ -3995,16 +3995,17 @@
                                           selectionInfo: aMessage.data.selectionInfo,
                                           disableSetDesktopBackground: aMessage.data.disableSetDesktopBg,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
+            case "DOMServiceWorkerFocusClient":
             case "DOMWebNotificationClicked": {
               let tab = this.getTabForBrowser(browser);
               if (!tab)
                 return;
               this.selectedTab = tab;
               window.focus();
               break;
             }
@@ -4091,16 +4092,17 @@
             // If this window has remote tabs, switch to our tabpanels fork
             // which does asynchronous tab switching.
             this.mPanelContainer.classList.add("tabbrowser-tabpanels");
           } else {
             this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
                                               this.mCurrentBrowser);
           }
           messageManager.addMessageListener("DOMWebNotificationClicked", this);
+          messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
           messageManager.addMessageListener("Findbar:Keypress", this);
         ]]>
       </constructor>
 
       <method name="_generateUniquePanelID">
         <body><![CDATA[
           if (!this._uniquePanelIDCounter) {
             this._uniquePanelIDCounter = 0;
@@ -5896,17 +5898,17 @@
             tabContainer._afterHoveredTab.removeAttribute("afterhovered");
             tabContainer._afterHoveredTab = null;
           }
 
           tabContainer._hoveredTab = null;
         ]]></body>
       </method>
 
-      <method name="_toggleMuteAudio">
+      <method name="toggleMuteAudio">
         <body>
         <![CDATA[
           let tabContainer = this.parentNode;
           let browser = this.linkedBrowser;
           if (browser.audioMuted) {
             browser.unmute();
             this.removeAttribute("muted");
           } else {
@@ -5968,17 +5970,18 @@
           return;
         }
 
         let anonid = event.originalTarget.getAttribute("anonid");
         let iconVisible = this.hasAttribute("soundplaying") ||
                           this.hasAttribute("muted");
         if ((anonid == "soundplaying-icon") ||
             ((anonid == "overlay-icon") && iconVisible)) {
-          this._toggleMuteAudio();
+          this.toggleMuteAudio();
+          this._overPlayingIcon = false;
         }
       ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
@@ -6077,16 +6080,23 @@
             aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
           else
             aMenuitem.removeAttribute("pending");
 
           if (aTab.selected)
             aMenuitem.setAttribute("selected", "true");
           else
             aMenuitem.removeAttribute("selected");
+
+          if (aTab.hasAttribute("muted"))
+            aMenuitem.setAttribute("endimage", "chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
+          else if (aTab.hasAttribute("soundplaying"))
+            aMenuitem.setAttribute("endimage", "chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
+          else
+            aMenuitem.removeAttribute("endimage");
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="popupshowing">
       <![CDATA[
         document.getElementById("alltabs_undoCloseTab").disabled =
--- a/browser/base/content/test/general/browser_audioTabIcon.js
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -35,17 +35,17 @@ function leave_icon(icon) {
 
   disable_non_test_mouse(false);
 }
 
 function* test_tooltip(icon, expectedTooltip) {
   let tooltip = document.getElementById("tabbrowser-tab-tooltip");
 
   yield hover_icon(icon, tooltip);
-  is(tooltip.getAttribute("label"), expectedTooltip, "Correct tooltip expected");
+  is(tooltip.getAttribute("label").indexOf(expectedTooltip), 0, "Correct tooltip expected");
   leave_icon(icon);
 }
 
 function* test_mute_tab(tab, icon, expectMuted) {
   let mutedPromise = BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
     if (event.detail.changed.indexOf("muted") >= 0) {
       is(tab.hasAttribute("muted"), expectMuted, "The tab should " + (expectMuted ? "" : "not ") + "be muted");
       return true;
@@ -72,38 +72,38 @@ function* test_playing_icon_on_tab(tab, 
 
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.play();
   });
 
   yield wait_for_tab_playing_event(tab, true);
 
-  yield test_tooltip(icon, "This tab is playing audio");
+  yield test_tooltip(icon, "Mute tab");
 
   yield test_mute_tab(tab, icon, true);
 
-  yield test_tooltip(icon, "This tab has been muted");
+  yield test_tooltip(icon, "Unmute tab");
 
   yield test_mute_tab(tab, icon, false);
 
-  yield test_tooltip(icon, "This tab is playing audio");
+  yield test_tooltip(icon, "Mute tab");
 
   yield test_mute_tab(tab, icon, true);
 
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.pause();
   });
   yield wait_for_tab_playing_event(tab, false);
 
   ok(tab.hasAttribute("muted") &&
      !tab.hasAttribute("soundplaying"), "Tab should still be muted but not playing");
 
-  yield test_tooltip(icon, "This tab has been muted");
+  yield test_tooltip(icon, "Unmute tab");
 
   yield test_mute_tab(tab, icon, false);
 
   ok(!tab.hasAttribute("muted") &&
      !tab.hasAttribute("soundplaying"), "Tab should not be be muted or playing");
 }
 
 function* test_swapped_browser(oldTab, newBrowser, isPlaying) {
@@ -154,16 +154,68 @@ function* test_browser_swapping(tab, bro
 
     yield BrowserTestUtils.withNewTab({
       gBrowser,
       url: "about:blank",
     }, newBrowser => test_swapped_browser(tab, newBrowser, false));
   });
 }
 
+function* test_click_on_pinned_tab_after_mute() {
+  function* test_on_browser(browser) {
+    let tab = gBrowser.getTabForBrowser(browser);
+
+    gBrowser.selectedTab = originallySelectedTab;
+    isnot(tab, gBrowser.selectedTab, "Sanity check, the tab should not be selected!");
+
+    // Steps to reproduce the bug:
+    //   Pin the tab.
+    gBrowser.pinTab(tab);
+
+    //   Start playbak.
+    yield ContentTask.spawn(browser, {}, function* () {
+      let audio = content.document.querySelector("audio");
+      audio.play();
+    });
+
+    //   Wait for playback to start.
+    yield wait_for_tab_playing_event(tab, true);
+
+    //   Mute the tab.
+    let icon = document.getAnonymousElementByAttribute(tab, "anonid", "overlay-icon");
+    yield test_mute_tab(tab, icon, true);
+
+    //   Stop playback
+    yield ContentTask.spawn(browser, {}, function* () {
+      let audio = content.document.querySelector("audio");
+      audio.pause();
+    });
+
+    // Unmute tab.
+    yield test_mute_tab(tab, icon, false);
+
+    // Now click on the tab.
+    let image = document.getAnonymousElementByAttribute(tab, "anonid", "tab-icon-image");
+    EventUtils.synthesizeMouseAtCenter(image, {button: 0});
+
+    is(tab, gBrowser.selectedTab, "Tab switch should be successful");
+
+    // Cleanup.
+    gBrowser.unpinTab(tab);
+    gBrowser.selectedTab = originallySelectedTab;
+  }
+
+  let originallySelectedTab = gBrowser.selectedTab;
+
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE
+  }, test_on_browser);
+}
+
 function* test_on_browser(browser) {
   let tab = gBrowser.getTabForBrowser(browser);
 
   // Test the icon in a normal tab.
   yield test_playing_icon_on_tab(tab, browser, false);
 
   gBrowser.pinTab(tab);
 
@@ -175,16 +227,18 @@ function* test_on_browser(browser) {
   // Retest with another browser in the foreground tab
   if (gBrowser.selectedBrowser.currentURI.spec == PAGE) {
     yield BrowserTestUtils.withNewTab({
       gBrowser,
       url: "data:text/html,test"
     }, () => test_on_browser(browser));
   } else {
     yield test_browser_swapping(tab, browser);
+
+    yield test_click_on_pinned_tab_after_mute();
   }
 }
 
 add_task(function*() {
   yield new Promise((resolve) => {
     SpecialPowers.pushPrefEnv({"set": [
                                 ["media.useAudioChannelService", true],
                                 ["browser.tabs.showAudioPlayingIcon", true],
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -50,30 +50,48 @@ function ignoredError(aErrorObject) {
     }
     if (matches) {
       return true;
     }
   }
   return false;
 }
 
+function once(target, name) {
+  return new Promise((resolve, reject) => {
+    let cb = () => {
+      target.removeEventListener(name, cb);
+      resolve();
+    };
+    target.addEventListener(name, cb);
+  });
+}
+
 add_task(function checkAllTheCSS() {
   let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
   // This asynchronously produces a list of URLs (sadly, mostly sync on our
   // test infrastructure because it runs against jarfiles there, and
   // our zipreader APIs are all sync)
   let uris = yield generateURIsFromDirTree(appDir, ".css");
 
-  // Create a clean iframe to load all the files into:
-  let hiddenWin = Services.appShell.hiddenDOMWindow;
-  let iframe = hiddenWin.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
-  hiddenWin.document.documentElement.appendChild(iframe);
+  // Create a clean iframe to load all the files into. This needs to live at a
+  // file or jar URI (depending on whether we're using a packaged build or not)
+  // so that it's allowed to load other same-scheme URIs (i.e. the browser css).
+  let resHandler = Services.io.getProtocolHandler("resource")
+                           .QueryInterface(Ci.nsISubstitutingProtocolHandler);
+  let resURI = Services.io.newURI('resource://testing-common/resource_test_file.html', null, null);
+  let testFile = resHandler.resolveURI(resURI);
+  let windowless = Services.appShell.createWindowlessBrowser();
+  let iframe = windowless.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
+  windowless.document.documentElement.appendChild(iframe);
+  let iframeLoaded = once(iframe, 'load');
+  iframe.contentWindow.location = testFile;
+  yield iframeLoaded;
   let doc = iframe.contentWindow.document;
 
-
   // Listen for errors caused by the CSS:
   let errorListener = {
     observe: function(aMessage) {
       if (!aMessage || !(aMessage instanceof Ci.nsIScriptError)) {
         return;
       }
       // Only care about CSS errors generated by our iframe:
       if (aMessage.category.includes("CSS") && aMessage.innerWindowID === 0 && aMessage.outerWindowID === 0) {
--- a/browser/components/preferences/handlers.xml
+++ b/browser/components/preferences/handlers.xml
@@ -67,15 +67,15 @@
     </implementation>
 
   </binding>
 
   <binding id="offlineapp"
 	   extends="chrome://global/content/bindings/listbox.xml#listitem">
     <content>
       <children>
-	<xul:listcell xbl:inherits="label=host"/>
+	<xul:listcell xbl:inherits="label=origin"/>
 	<xul:listcell xbl:inherits="label=usage"/>
       </children>
     </content>
   </binding>
 
 </bindings>
--- a/browser/components/translation/test/browser_translation_exceptions.js
+++ b/browser/components/translation/test/browser_translation_exceptions.js
@@ -53,17 +53,29 @@ function getDomainExceptions() {
         perm.capability == Services.perms.DENY_ACTION)
       results.push(perm.principal);
   }
 
   return results;
 }
 
 function getInfoBar() {
-  return gBrowser.getNotificationBox().getNotificationWithValue("translation");
+  let deferred = Promise.defer();
+  let infobar =
+    gBrowser.getNotificationBox().getNotificationWithValue("translation");
+
+  if (!infobar) {
+    deferred.resolve();
+  } else {
+    // Wait for all animations to finish
+    Promise.all(infobar.getAnimations().map(animation => animation.finished))
+      .then(() => deferred.resolve(infobar));
+  }
+
+  return deferred.promise;
 }
 
 function openPopup(aPopup) {
   let deferred = Promise.defer();
 
   aPopup.addEventListener("popupshown", function popupShown() {
     aPopup.removeEventListener("popupshown", popupShown);
     deferred.resolve();
@@ -104,17 +116,17 @@ let gTests = [
 {
   desc: "never for language",
   run: function* checkNeverForLanguage() {
     // Show the infobar for example.com and fr.
     Translation.documentStateReceived(gBrowser.selectedBrowser,
                                       {state: Translation.STATE_OFFER,
                                        originalShown: true,
                                        detectedLanguage: "fr"});
-    let notif = getInfoBar();
+    let notif = yield getInfoBar();
     ok(notif, "the infobar is visible");
     let ui = gBrowser.selectedBrowser.translationUI;
     let uri = gBrowser.selectedBrowser.currentURI;
     ok(ui.shouldShowInfoBar(uri, "fr"),
        "check shouldShowInfoBar initially returns true");
 
     // Open the "options" drop down.
     yield openPopup(notif._getAnonElt("options"));
@@ -122,28 +134,29 @@ let gTests = [
        "the options menu is open");
 
     // Check that the item is not disabled.
     ok(!notif._getAnonElt("neverForLanguage").disabled,
        "The 'Never translate <language>' item isn't disabled");
 
     // Click the 'Never for French' item.
     notif._getAnonElt("neverForLanguage").click();
-    ok(!getInfoBar(), "infobar hidden");
+    notif = yield getInfoBar();
+    ok(!notif, "infobar hidden");
 
     // Check this has been saved to the exceptions list.
     let langs = getLanguageExceptions();
     is(langs.length, 1, "one language in the exception list");
     is(langs[0], "fr", "correct language in the exception list");
     ok(!ui.shouldShowInfoBar(uri, "fr"),
        "the infobar wouldn't be shown anymore");
 
     // Reopen the infobar.
     PopupNotifications.getNotification("translate").anchorElement.click();
-    notif = getInfoBar();
+    notif = yield getInfoBar();
     // Open the "options" drop down.
     yield openPopup(notif._getAnonElt("options"));
     ok(notif._getAnonElt("neverForLanguage").disabled,
        "The 'Never translate French' item is disabled");
 
     // Cleanup.
     Services.prefs.setCharPref(kLanguagesPref, "");
     notif.close();
@@ -153,17 +166,17 @@ let gTests = [
 {
   desc: "never for site",
   run: function* checkNeverForSite() {
     // Show the infobar for example.com and fr.
     Translation.documentStateReceived(gBrowser.selectedBrowser,
                                       {state: Translation.STATE_OFFER,
                                        originalShown: true,
                                        detectedLanguage: "fr"});
-    let notif = getInfoBar();
+    let notif = yield getInfoBar();
     ok(notif, "the infobar is visible");
     let ui = gBrowser.selectedBrowser.translationUI;
     let uri = gBrowser.selectedBrowser.currentURI;
     ok(ui.shouldShowInfoBar(uri, "fr"),
        "check shouldShowInfoBar initially returns true");
 
     // Open the "options" drop down.
     yield openPopup(notif._getAnonElt("options"));
@@ -171,28 +184,29 @@ let gTests = [
        "the options menu is open");
 
     // Check that the item is not disabled.
     ok(!notif._getAnonElt("neverForSite").disabled,
        "The 'Never translate site' item isn't disabled");
 
     // Click the 'Never for French' item.
     notif._getAnonElt("neverForSite").click();
-    ok(!getInfoBar(), "infobar hidden");
+    notif = yield getInfoBar();
+    ok(!notif, "infobar hidden");
 
     // Check this has been saved to the exceptions list.
     let sites = getDomainExceptions();
     is(sites.length, 1, "one site in the exception list");
     is(sites[0].origin, "http://example.com", "correct site in the exception list");
     ok(!ui.shouldShowInfoBar(uri, "fr"),
        "the infobar wouldn't be shown anymore");
 
     // Reopen the infobar.
     PopupNotifications.getNotification("translate").anchorElement.click();
-    notif = getInfoBar();
+    notif = yield getInfoBar();
     // Open the "options" drop down.
     yield openPopup(notif._getAnonElt("options"));
     ok(notif._getAnonElt("neverForSite").disabled,
        "The 'Never translate French' item is disabled");
 
     // Cleanup.
     Services.perms.remove(makeURI("http://example.com"), "translate");
     notif.close();
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -638,16 +638,18 @@ you can use these alternative items. Oth
 <!ENTITY quitApplicationCmdMac2.label   "Quit &brandShorterName;">
 <!-- LOCALIZATION NOTE(quitApplicationCmdUnix.key): This keyboard shortcut is used by both Linux and OSX builds. -->
 <!ENTITY quitApplicationCmdUnix.key     "Q">
 
 <!ENTITY closeCmd.label                 "Close">  
 <!ENTITY closeCmd.key                   "W">  
 <!ENTITY closeCmd.accesskey             "C">
 
+<!ENTITY toggleMuteCmd.key              "M">
+
 <!ENTITY pageStyleMenu.label "Page Style">
 <!ENTITY pageStyleMenu.accesskey "y">
 <!ENTITY pageStyleNoStyle.label "No Style">
 <!ENTITY pageStyleNoStyle.accesskey "n">
 <!ENTITY pageStylePersistentOnly.label "Basic Page Style">
 <!ENTITY pageStylePersistentOnly.accesskey "b">
 
 <!ENTITY pageReportIcon.tooltip            "Change pop-up blocking settings for this website">
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties
+++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties
@@ -26,10 +26,14 @@ tabs.closeWarningTitle=Confirm close
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple tabs.
 tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
 tabs.closeButtonMultiple=Close tabs
 tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs
 
 tabs.closeTab.tooltip=Close tab
-tabs.playingAudio.tooltip=This tab is playing audio
-tabs.mutedAudio.tooltip=This tab has been muted
+# LOCALIZATION NOTE (tabs.muteAudio.tooltip):
+# %S is the keyboard shortcut for "Mute tab"
+tabs.muteAudio.tooltip=Mute tab (%S)
+# LOCALIZATION NOTE (tabs.unmuteAudio.tooltip):
+# %S is the keyboard shortcut for "Unmute tab"
+tabs.unmuteAudio.tooltip=Unmute tab (%S)
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -322,17 +322,17 @@ menuitem.bookmark-item {
 }
 
 .bookmark-item[cutting] > .toolbarbutton-text,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
   opacity: 0.7;
 }
 
 /* Stock icons for the menu bar items */
-menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
+menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip):not([endimage]) {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
 }
 
 #placesContext_open\:newwindow,
 #menu_newNavigator,
 #context-openlink,
 #context-openframe {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -89,79 +89,84 @@
 }
 
 .tab-icon-overlay {
   width: 16px;
   height: 16px;
   margin-top: -12px;
   -moz-margin-start: -16px;
   display: none;
+  position: relative;
 }
 
 .tab-icon-overlay[crashed] {
-  display: -moz-box;
   list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
 }
 
+.tab-icon-overlay[crashed],
 .tab-icon-overlay[soundplaying][pinned],
-.tab-icon-overlay[muted][pinned] {
+.tab-icon-overlay[muted][pinned]:not([crashed]) {
   display: -moz-box;
+}
+
+.tab-icon-overlay[soundplaying][pinned],
+.tab-icon-overlay[muted][pinned]:not([crashed]) {
   border-radius: 8px;
 }
 
 .tab-icon-overlay[soundplaying][pinned]:hover,
-.tab-icon-overlay[muted][pinned]:hover {
+.tab-icon-overlay[muted][pinned]:not([crashed]):hover {
   background-color: white;
 }
 
 .tab-icon-overlay[soundplaying][pinned] {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
 }
 
 .tab-icon-overlay[soundplaying][pinned]:hover {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
 }
 
 .tab-icon-overlay[soundplaying][pinned]:hover:active {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
 }
 
-.tab-icon-overlay[muted][pinned] {
+.tab-icon-overlay[muted][pinned]:not([crashed]) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
 }
 
-.tab-icon-overlay[muted][pinned]:hover {
+.tab-icon-overlay[muted][pinned]:not([crashed]):hover {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
 }
 
-.tab-icon-overlay[muted][pinned]:hover:active {
+.tab-icon-overlay[muted][pinned]:not([crashed]):hover:active {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
 }
 
 #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned] {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-dark");
 }
 
 #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
 }
 
 #TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover:active {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned] {
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover {
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
 }
 
-#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover:active {
+#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover:active {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
 }
 
 .tab-throbber[busy] {
   list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
 }
 
 .tab-throbber[progress] {
@@ -416,17 +421,17 @@
 /* Tab pointer-events */
 .tabbrowser-tab {
   pointer-events: none;
 }
 
 .tab-background-middle,
 .tabs-newtab-button,
 .tab-icon-overlay[soundplaying],
-.tab-icon-overlay[muted],
+.tab-icon-overlay[muted]:not([crashed]),
 .tab-icon-sound,
 .tab-close-button {
   pointer-events: auto;
 }
 
 /* Pinned tabs */
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
deleted file mode 100644
--- a/build/autoconf/gcc-pr49911.m4
+++ /dev/null
@@ -1,71 +0,0 @@
-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 if the compiler is gcc and has PR49911. If so
-dnl disable vrp.
-
-AC_DEFUN([MOZ_GCC_PR49911],
-[
-if test "$GNU_CC"; then
-
-AC_MSG_CHECKING(for gcc PR49911)
-ac_have_gcc_pr49911="no"
-AC_LANG_SAVE
-AC_LANG_CPLUSPLUS
-
-_SAVE_CXXFLAGS=$CXXFLAGS
-CXXFLAGS="-O2"
-AC_TRY_RUN([
-extern "C" void abort(void);
-typedef enum {
-eax,         ecx,         edx,         ebx,         esp,         ebp,
-esi,         edi     }
-RegisterID;
-union StateRemat {
-  RegisterID reg_;
-  int offset_;
-};
-static StateRemat FromRegister(RegisterID reg) {
-  StateRemat sr;
-  sr.reg_ = reg;
-  return sr;
-}
-static StateRemat FromAddress3(int address) {
-  StateRemat sr;
-  sr.offset_ = address;
-  if (address < 46 &&    address >= 0) {
-    abort();
-  }
-  return sr;
-}
-struct FrameState {
-  StateRemat dataRematInfo2(bool y, int z) {
-    if (y)         return FromRegister(RegisterID(1));
-    return FromAddress3(z);
-  }
-};
-FrameState frame;
-StateRemat x;
-__attribute__((noinline)) void jsop_setelem(bool y, int z) {
-  x = frame.dataRematInfo2(y, z);
-}
-int main(void) {
-  jsop_setelem(0, 47);
-}
-], true,
-   ac_have_gcc_pr49911="yes",
-   true)
-CXXFLAGS="$_SAVE_CXXFLAGS"
-
-AC_LANG_RESTORE
-
-if test "$ac_have_gcc_pr49911" = "yes"; then
-   AC_MSG_RESULT(yes)
-   CFLAGS="$CFLAGS -fno-tree-vrp"
-   CXXFLAGS="$CXXFLAGS -fno-tree-vrp"
-else
-   AC_MSG_RESULT(no)
-fi
-fi
-])
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -7,17 +7,17 @@
 # Avoid duplication if the file happens to be included twice.
 if test -z "$bucket"; then
 
 read branch platform master <<EOF
 $(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.json").read())["properties"]; print p["branch"], p["platform"], p["master"]' 2> /dev/null)
 EOF
 
 bucket=
-if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache"; then
+if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET"; then
     case "${branch}" in
     try)
         case "${master}" in
         *scl1.mozilla.com*|*.scl3.mozilla.com*)
             bucket=mozilla-releng-s3-cache-us-west-1-try
             ;;
         *use1.mozilla.com*)
             bucket=mozilla-releng-s3-cache-us-east-1-try
@@ -25,24 +25,20 @@ if test -z "$SCCACHE_DISABLE" -a -z "$no
         *usw2.mozilla.com*)
             bucket=mozilla-releng-s3-cache-us-west-2-try
             ;;
         esac
         ;;
     b2g-inbound|mozilla-inbound|fx-team)
         case "${master}" in
         *use1.mozilla.com*)
-            if test -z "$MOZ_PGO"; then
-                bucket=mozilla-releng-s3-cache-us-east-1-prod
-            fi
+            bucket=mozilla-releng-s3-cache-us-east-1-prod
             ;;
         *usw2.mozilla.com*)
-            if test -z "$MOZ_PGO"; then
-                bucket=mozilla-releng-s3-cache-us-west-2-prod
-            fi
+            bucket=mozilla-releng-s3-cache-us-west-2-prod
             ;;
         esac
         ;;
     esac
 fi
 
 if test -z "$bucket"; then
     case "$platform" in
--- a/build/unix/mozconfig.gtk
+++ b/build/unix/mozconfig.gtk
@@ -3,26 +3,28 @@ TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
 # $TOOLTOOL_DIR/gtk3 comes from tooltool, when the tooltool manifest contains it.
 if [ -d "$TOOLTOOL_DIR/gtk3" ]; then
   if [ -z "$PKG_CONFIG_LIBDIR" ]; then
     echo PKG_CONFIG_LIBDIR must be set >&2
     exit 1
   fi
   export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3"
   export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig"
+  PKG_CONFIG="$TOOLTOOL_DIR/gtk3/usr/local/bin/pkg-config"
   export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}"
   # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
   LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}"
   ac_add_options --enable-default-toolkit=cairo-gtk3
 
   # Set things up to use Gtk+3 from the tooltool package
   mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts"
   mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc"
   mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib"
   mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
   mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
   mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib"
 
   # Until a tooltool with bug 1188571 landed is available everywhere
   $TOOLTOOL_DIR/gtk3/setup.sh
 else
+  PKG_CONFIG=pkg-config
   ac_add_options --enable-default-toolkit=cairo-gtk2
 fi
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -838,28 +838,22 @@ nsScriptSecurityManager::CheckLoadURIWit
         // Allow domains that were whitelisted in the prefs. In 99.9% of cases,
         // this array is empty.
         for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
             if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) {
                 return NS_OK;
             }
         }
 
-        // resource: and chrome: are equivalent, securitywise
-        // That's bogus!!  Fix this.  But watch out for
-        // the view-source stylesheet?
-        bool sourceIsChrome;
-        rv = NS_URIChainHasFlags(sourceURI,
-                                 nsIProtocolHandler::URI_IS_UI_RESOURCE,
-                                 &sourceIsChrome);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (sourceIsChrome) {
+        // Allow chrome://
+        if (sourceScheme.EqualsLiteral("chrome")) {
             return NS_OK;
         }
 
+        // Nothing else.
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
         return NS_ERROR_DOM_BAD_URI;
     }
 
     // OK, everyone is allowed to load this, since unflagged handlers are
     // deprecated but treated as URI_LOADABLE_BY_ANYONE.  But check whether we
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -37,16 +37,24 @@ endif
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
 	@echo "$(@F)"
 	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \
 		$(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \
 		$(dist_include_dir) $(@D) $(idl_deps_dir) $(libxul_sdk_includes) \
 		$(basename $(notdir $@)) $($(basename $(notdir $@))_deps)
+# When some IDL is added or removed, if the actual IDL file was already, or
+# still is, in the tree, simple dependencies can't detect that the XPT needs
+# to be rebuilt.
+# Add the current value of $($(xpidl_module)_deps) in the depend file, such that
+# we can later check if the value has changed since last build, which will
+# indicate whether IDLs were added or removed.
+# Note that removing previously built files is not covered.
+	@echo $(basename $(notdir $@))_deps_built = $($(basename $(notdir $@))_deps) >> $(idl_deps_dir)/$(basename $(notdir $@)).pp
 
 # Chrome manifests may be written from several Makefiles at various times during
 # the build. The 'buildlist' action adds to the file if it already exists, but
 # if it does exist, make considers it to be up-to-date (as we have no inputs to
 # depend on). We use FORCE to ensure that we always add the interface manifest,
 # whether or not the chrome manifest already exists.
 %/chrome.manifest: FORCE
 	$(call py_action,buildlist,$@ 'manifest components/interfaces.manifest')
@@ -61,18 +69,21 @@ xpt_files := @xpt_files@
 depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp)
 
 GARBAGE += $(xpt_files) $(depends_files)
 
 xpidl:: $(xpt_files) $(chrome_manifests)
 
 $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir))
 
+-include $(depends_files)
+
 define xpt_deps
 $(1): $(call mkdir_deps,$(dir $(1)))
 $(1): $(addsuffix .idl,$(addprefix $(dist_idl_dir)/,$($(basename $(notdir $(1)))_deps)))
+ifneq ($($(basename $(notdir $(1)))_deps),$($(basename $(notdir $(1)))_deps_built))
+$(1): FORCE
+endif
 endef
 
 $(foreach xpt,$(xpt_files),$(eval $(call xpt_deps,$(xpt))))
 
-$(call include_deps,$(depends_files))
-
 .PHONY: xpidl
--- a/configure.in
+++ b/configure.in
@@ -2638,39 +2638,18 @@ WINNT|Darwin|Android)
   STL_FLAGS='-I$(DIST)/stl_wrappers'
   WRAP_STL_INCLUDES=1
   ;;
 esac
 
 AC_SUBST(WRAP_SYSTEM_INCLUDES)
 AC_SUBST(VISIBILITY_FLAGS)
 
-MOZ_GCC_PR49911
 MOZ_LLVM_PR8927
 
-dnl Check for __force_align_arg_pointer__ for SSE2 on gcc
-dnl ========================================================
-if test "$GNU_CC"; then
-  CFLAGS_save="${CFLAGS}"
-  CFLAGS="${CFLAGS} -Werror"
-  AC_CACHE_CHECK(for __force_align_arg_pointer__ attribute,
-                 ac_cv_force_align_arg_pointer,
-                 [AC_TRY_COMPILE([__attribute__ ((__force_align_arg_pointer__)) void test() {}],
-                                 [],
-                                 ac_cv_force_align_arg_pointer="yes",
-                                 ac_cv_force_align_arg_pointer="no")])
-  CFLAGS="${CFLAGS_save}"
-  if test "$ac_cv_force_align_arg_pointer" = "yes"; then
-    HAVE_GCC_ALIGN_ARG_POINTER=1
-  else
-    HAVE_GCC_ALIGN_ARG_POINTER=
-  fi
-fi
-AC_SUBST(HAVE_GCC_ALIGN_ARG_POINTER)
-
 dnl Checks for header files.
 dnl ========================================================
 AC_HEADER_DIRENT
 case "$target_os" in
 freebsd*|openbsd*)
 # for stuff like -lXshm
     CPPFLAGS="${CPPFLAGS} ${X_CFLAGS}"
     ;;
@@ -5868,19 +5847,19 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
   fi
 
   ######################################
   # Find _46+ for use by Vista+.
 
   # Find a D3D compiler DLL in a Windows SDK.
   MOZ_D3DCOMPILER_VISTA_DLL=
   case "$MOZ_WINSDK_MAXVER" in
-  0x0603*)
+  0x0603*|0x0A00*)
     MOZ_D3DCOMPILER_VISTA_DLL=d3dcompiler_47.dll
-    AC_MSG_RESULT([Found D3D compiler in Windows SDK 8.1.])
+    AC_MSG_RESULT([Found D3D compiler in Windows SDK.])
   ;;
   esac
 
   if test -n "$MOZ_D3DCOMPILER_VISTA_DLL"; then
     # We have a name, now track down the path.
     if test -n "$WINDOWSSDKDIR"; then
       MOZ_D3DCOMPILER_VISTA_DLL_PATH="$WINDOWSSDKDIR/Redist/D3D/$MOZ_D3D_CPU_SUFFIX/$MOZ_D3DCOMPILER_VISTA_DLL"
       if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
@@ -5889,16 +5868,18 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
       else
         AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
         AC_MSG_ERROR([Windows SDK at "$WINDOWSSDKDIR" appears broken. Try updating to MozillaBuild 1.9 final or higher.])
         MOZ_D3DCOMPILER_VISTA_DLL_PATH=
       fi
     else
       AC_MSG_RESULT([Windows SDK not found.])
     fi
+  else
+    AC_MSG_ERROR([Couldn't find Windows SDK 8.1 or higher needed for ANGLE.])
   fi
 
   if test -z "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
     MOZ_D3DCOMPILER_VISTA_DLL=
   fi
 
   # On mingw, check if headers are provided by toolchain.
   if test -n "$GNU_CC"; then
@@ -8194,17 +8175,17 @@ else
     MOZ_PIXMAN_CFLAGS="$PIXMAN_CFLAGS"
     MOZ_PIXMAN_LIBS="$PIXMAN_LIBS"
 fi
 AC_SUBST(MOZ_PIXMAN_CFLAGS)
 AC_SUBST_LIST(MOZ_PIXMAN_LIBS)
 
 # Check for headers defining standard int types.
 if test -n "$COMPILE_ENVIRONMENT"; then
-    MOZ_CHECK_HEADERS(stdint.h inttypes.h sys/int_types.h)
+    MOZ_CHECK_HEADERS(stdint.h inttypes.h)
 
     if test "${ac_cv_header_inttypes_h}" = "yes"; then
         HAVE_INTTYPES_H=1
     fi
 fi
 
 AC_SUBST(HAVE_INTTYPES_H)
 
@@ -8859,17 +8840,17 @@ HOST_CXXFLAGS=`echo \
     $_DEPEND_CFLAGS`
 
 AC_SUBST(MOZ_NATIVE_JPEG)
 AC_SUBST(MOZ_NATIVE_PNG)
 AC_SUBST(MOZ_NATIVE_BZ2)
 
 AC_SUBST(MOZ_JPEG_CFLAGS)
 AC_SUBST_LIST(MOZ_JPEG_LIBS)
-AC_SUBST(MOZ_BZ2_CFLAGS)
+AC_SUBST_LIST(MOZ_BZ2_CFLAGS)
 AC_SUBST_LIST(MOZ_BZ2_LIBS)
 AC_SUBST(MOZ_PNG_CFLAGS)
 AC_SUBST_LIST(MOZ_PNG_LIBS)
 
 if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
     export MOZ_NUWA_PROCESS
     AC_DEFINE(MOZ_NUWA_PROCESS)
 fi
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -512,53 +512,67 @@ Animation::HasLowerCompositeOrderThan(co
              "Sequence numbers should be unique");
 
   return mSequenceNum < aOther.mSequenceNum;
 }
 
 bool
 Animation::CanThrottle() const
 {
-  if (!mEffect ||
-      mEffect->IsFinishedTransition() ||
-      mEffect->Properties().IsEmpty()) {
+  // This method answers the question, "Can we get away with NOT updating
+  // style on the main thread for this animation on this tick?"
+
+  // Ignore animations that were never going to have any effect anyway.
+  if (!mEffect || mEffect->Properties().IsEmpty()) {
     return true;
   }
 
-  if (!mIsRunningOnCompositor) {
-    return false;
+  // Finished animations can be throttled unless this is the first
+  // sample since finishing. In that case we need an unthrottled sample
+  // so we can apply the correct end-of-animation behavior on the main
+  // thread (either removing the animation style or applying the fill mode).
+  if (PlayState() == AnimationPlayState::Finished) {
+    return mFinishedAtLastComposeStyle;
   }
 
-  if (PlayState() != AnimationPlayState::Finished) {
-    // Unfinished animations can be throttled.
+  // We should also ignore animations which are not "in effect"--i.e. not
+  // producing an output. This includes animations that are idle or in their
+  // delay phase but with no backwards fill.
+  //
+  // Note that unlike newly-finished animations, we don't need to worry about
+  // special handling for newly-idle animations or animations that are newly
+  // yet-to-start since any operation that would cause that change (e.g. a call
+  // to cancel() on the animation, or seeking its current time) will trigger an
+  // unthrottled sample.
+  if (!IsInEffect()) {
     return true;
   }
 
-  // The animation has finished but, if this is the first sample since
-  // finishing, we need an unthrottled sample so we can apply the correct
-  // end-of-animation behavior on the main thread (either removing the
-  // animation style or applying the fill mode).
-  return mFinishedAtLastComposeStyle;
+  return mIsRunningOnCompositor;
 }
 
 void
-Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
+Animation::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
                         nsCSSPropertySet& aSetProperties,
                         bool& aNeedsRefreshes)
 {
-  if (!mEffect || mEffect->IsFinishedTransition()) {
+  if (!mEffect) {
     return;
   }
 
   AnimationPlayState playState = PlayState();
   if (playState == AnimationPlayState::Running ||
       playState == AnimationPlayState::Pending) {
     aNeedsRefreshes = true;
   }
 
+  if (!IsInEffect()) {
+    return;
+  }
+
   // In order to prevent flicker, there are a few cases where we want to use
   // a different time for rendering that would otherwise be returned by
   // GetCurrentTime. These are:
   //
   // (a) For animations that are pausing but which are still running on the
   //     compositor. In this case we send a layer transaction that removes the
   //     animation but which also contains the animation values calculated on
   //     the main thread. To prevent flicker when this occurs we want to ensure
@@ -893,19 +907,16 @@ Animation::UpdateFinishedState(SeekFlag 
     }
   }
 
   bool currentFinishedState = PlayState() == AnimationPlayState::Finished;
   if (currentFinishedState && !mFinishedIsResolved) {
     DoFinishNotification(aSyncNotifyFlag);
   } else if (!currentFinishedState && mFinishedIsResolved) {
     ResetFinishedPromise();
-    if (mEffect->AsTransition()) {
-      mEffect->SetIsFinishedTransition(false);
-    }
   }
   // We must recalculate the current time to take account of any mHoldTime
   // changes the code above made.
   mPreviousCurrentTime = GetCurrentTime();
 }
 
 void
 Animation::UpdateEffect()
@@ -1051,17 +1062,17 @@ Animation::GetPresContext() const
     return nullptr;
   }
   return shell->GetPresContext();
 }
 
 AnimationCollection*
 Animation::GetCollection() const
 {
-  css::CommonAnimationManager* manager = GetAnimationManager();
+  CommonAnimationManager* manager = GetAnimationManager();
   if (!manager) {
     return nullptr;
   }
   MOZ_ASSERT(mEffect,
              "An animation with an animation manager must have an effect");
 
   Element* targetElement;
   nsCSSPseudoElements::Type targetPseudoType;
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -31,21 +31,20 @@
 #endif
 
 struct JSContext;
 class nsCSSPropertySet;
 class nsIDocument;
 class nsPresContext;
 
 namespace mozilla {
+
 struct AnimationCollection;
-namespace css {
 class AnimValuesStyleRule;
 class CommonAnimationManager;
-} // namespace css
 
 namespace dom {
 
 class CSSAnimation;
 class CSSTransition;
 
 class Animation
   : public DOMEventTargetHelper
@@ -136,17 +135,17 @@ public:
    * CSSAnimation::PauseFromJS so we leave it for now.
    */
   void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
 
   // Wrapper functions for Animation DOM methods when called from style.
 
   virtual void CancelFromStyle() { DoCancel(); }
 
-  void Tick();
+  virtual void Tick();
 
   /**
    * Set the time to use for starting or pausing a pending animation.
    *
    * Typically, when an animation is played, it does not start immediately but
    * is added to a table of pending animations on the document of its effect.
    * In the meantime it sets its hold time to the time from which playback
    * should begin.
@@ -290,17 +289,17 @@ public:
    * Updates |aStyleRule| with the animation values of this animation's effect,
    * if any.
    * Any properties already contained in |aSetProperties| are not changed. Any
    * properties that are changed are added to |aSetProperties|.
    * |aNeedsRefreshes| will be set to true if this animation expects to update
    * the style rule on the next refresh driver tick as well (because it
    * is running and has an effect to sample).
    */
-  void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
+  void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
                     nsCSSPropertySet& aSetProperties,
                     bool& aNeedsRefreshes);
 protected:
   void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
   void SilentlySetPlaybackRate(double aPlaybackRate);
   void DoCancel();
   void DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   void DoPause(ErrorResult& aRv);
@@ -351,17 +350,17 @@ protected:
    */
   void CancelPendingTasks();
 
   bool IsPossiblyOrphanedPendingAnimation() const;
   StickyTimeDuration EffectEnd() const;
 
   nsIDocument* GetRenderedDocument() const;
   nsPresContext* GetPresContext() const;
-  virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
+  virtual CommonAnimationManager* GetAnimationManager() const = 0;
   AnimationCollection* GetCollection() const;
 
   nsRefPtr<AnimationTimeline> mTimeline;
   nsRefPtr<KeyframeEffectReadOnly> mEffect;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Animation timescale
   Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -231,45 +231,39 @@ KeyframeEffectReadOnly::ActiveDuration(c
   return StickyTimeDuration(
     aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount));
 }
 
 // http://w3c.github.io/web-animations/#in-play
 bool
 KeyframeEffectReadOnly::IsInPlay(const Animation& aAnimation) const
 {
-  if (IsFinishedTransition() ||
-      aAnimation.PlayState() == AnimationPlayState::Finished) {
+  if (aAnimation.PlayState() == AnimationPlayState::Finished) {
     return false;
   }
 
   return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase_Active;
 }
 
 // http://w3c.github.io/web-animations/#current
 bool
 KeyframeEffectReadOnly::IsCurrent(const Animation& aAnimation) const
 {
-  if (IsFinishedTransition() ||
-      aAnimation.PlayState() == AnimationPlayState::Finished) {
+  if (aAnimation.PlayState() == AnimationPlayState::Finished) {
     return false;
   }
 
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
          computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
 }
 
 bool
 KeyframeEffectReadOnly::IsInEffect() const
 {
-  if (IsFinishedTransition()) {
-    return false;
-  }
-
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mProgress != ComputedTiming::kNullProgress;
 }
 
 const AnimationProperty*
 KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (size_t propIdx = 0, propEnd = mProperties.Length();
@@ -294,19 +288,18 @@ KeyframeEffectReadOnly::HasAnimationOfPr
     if (HasAnimationOfProperty(aProperties[i])) {
       return true;
     }
   }
   return false;
 }
 
 void
-KeyframeEffectReadOnly::ComposeStyle(
-                          nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
-                          nsCSSPropertySet& aSetProperties)
+KeyframeEffectReadOnly::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
+                                     nsCSSPropertySet& aSetProperties)
 {
   ComputedTiming computedTiming = GetComputedTiming();
 
   // If the progress is null, we don't have fill data for the current
   // time so we shouldn't animate.
   if (computedTiming.mProgress == ComputedTiming::kNullProgress) {
     return;
   }
@@ -365,17 +358,17 @@ KeyframeEffectReadOnly::ComposeStyle(
     MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
     MOZ_ASSERT(segment >= prop.mSegments.Elements() &&
                size_t(segment - prop.mSegments.Elements()) <
                  prop.mSegments.Length(),
                "out of array bounds");
 
     if (!aStyleRule) {
       // Allocate the style rule now that we know we have animation data.
-      aStyleRule = new css::AnimValuesStyleRule();
+      aStyleRule = new AnimValuesStyleRule();
     }
 
     double positionInSegment =
       (computedTiming.mProgress - segment->mFromKey) /
       (segment->mToKey - segment->mFromKey);
     double valuePosition =
       segment->mTimingFunction.GetValue(positionInSegment);
 
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -21,19 +21,18 @@
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
 
 struct JSContext;
 class nsCSSPropertySet;
 
 namespace mozilla {
-namespace css {
+
 class AnimValuesStyleRule;
-} // namespace css
 
 /**
  * Input timing parameters.
  *
  * Eventually this will represent all the input timing parameters specified
  * by content but for now it encapsulates just the subset of those
  * parameters passed to GetPositionInIteration.
  */
@@ -192,17 +191,16 @@ class KeyframeEffectReadOnly : public An
 public:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          Element* aTarget,
                          nsCSSPseudoElements::Type aPseudoType,
                          const AnimationTiming &aTiming)
     : AnimationEffectReadOnly(aDocument)
     , mTarget(aTarget)
     , mTiming(aTiming)
-    , mIsFinishedTransition(false)
     , mPseudoType(aPseudoType)
   {
     MOZ_ASSERT(aTarget, "null animation target is not yet supported");
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffectReadOnly,
                                                         AnimationEffectReadOnly)
@@ -278,30 +276,16 @@ public:
                                      = nullptr) const {
     return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming);
   }
 
   // Return the duration of the active interval for the given timing parameters.
   static StickyTimeDuration
   ActiveDuration(const AnimationTiming& aTiming);
 
-  // After transitions finish they need to be retained in order to
-  // address the issue described in
-  // https://lists.w3.org/Archives/Public/www-style/2015Jan/0444.html .
-  // However, finished transitions are ignored for many purposes.
-  bool IsFinishedTransition() const {
-    return mIsFinishedTransition;
-  }
-
-  void SetIsFinishedTransition(bool aIsFinished) {
-    MOZ_ASSERT(AsTransition(),
-               "Calling SetIsFinishedTransition but it's not a transition");
-    mIsFinishedTransition = aIsFinished;
-  }
-
   bool IsInPlay(const Animation& aAnimation) const;
   bool IsCurrent(const Animation& aAnimation) const;
   bool IsInEffect() const;
 
   const AnimationProperty*
   GetAnimationOfProperty(nsCSSProperty aProperty) const;
   bool HasAnimationOfProperty(nsCSSProperty aProperty) const {
     return GetAnimationOfProperty(aProperty) != nullptr;
@@ -314,29 +298,26 @@ public:
   InfallibleTArray<AnimationProperty>& Properties() {
     return mProperties;
   }
 
   // Updates |aStyleRule| with the animation values produced by this
   // Animation for the current time except any properties already contained
   // in |aSetProperties|.
   // Any updated properties are added to |aSetProperties|.
-  void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
+  void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
                     nsCSSPropertySet& aSetProperties);
 
 protected:
   virtual ~KeyframeEffectReadOnly() { }
 
   nsCOMPtr<Element> mTarget;
   Nullable<TimeDuration> mParentTime;
 
   AnimationTiming mTiming;
-  // A flag to mark transitions that have finished and are due to
-  // be removed on the next throttle-able cycle.
-  bool mIsFinishedTransition;
   nsCSSPseudoElements::Type mPseudoType;
 
   InfallibleTArray<AnimationProperty> mProperties;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -776,27 +776,27 @@ EventSource::InitChannelAndRequestEventS
 
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        doc,
                        securityFlags,
-                       nsIContentPolicy::TYPE_DATAREQUEST,
+                       nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
                        mLoadGroup,       // loadGroup
                        nullptr,          // aCallbacks
                        loadFlags);       // aLoadFlags
   } else {
     // otherwise use the principal
     rv = NS_NewChannel(getter_AddRefs(channel),
                        mSrc,
                        mPrincipal,
                        securityFlags,
-                       nsIContentPolicy::TYPE_DATAREQUEST,
+                       nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
                        mLoadGroup,       // loadGroup
                        nullptr,          // aCallbacks
                        loadFlags);       // aLoadFlags
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHttpChannel = do_QueryInterface(channel);
--- a/dom/base/nsContentPolicyUtils.h
+++ b/dom/base/nsContentPolicyUtils.h
@@ -87,48 +87,50 @@ NS_CP_ResponseName(int16_t response)
  *
  * @param contentType the content type code
  * @return the name of the given content type code
  */
 inline const char *
 NS_CP_ContentTypeName(uint32_t contentType)
 {
   switch (contentType) {
-    CASE_RETURN( TYPE_OTHER                  );
-    CASE_RETURN( TYPE_SCRIPT                 );
-    CASE_RETURN( TYPE_IMAGE                  );
-    CASE_RETURN( TYPE_STYLESHEET             );
-    CASE_RETURN( TYPE_OBJECT                 );
-    CASE_RETURN( TYPE_DOCUMENT               );
-    CASE_RETURN( TYPE_SUBDOCUMENT            );
-    CASE_RETURN( TYPE_REFRESH                );
-    CASE_RETURN( TYPE_XBL                    );
-    CASE_RETURN( TYPE_PING                   );
-    CASE_RETURN( TYPE_XMLHTTPREQUEST         );
-    CASE_RETURN( TYPE_OBJECT_SUBREQUEST      );
-    CASE_RETURN( TYPE_DTD                    );
-    CASE_RETURN( TYPE_FONT                   );
-    CASE_RETURN( TYPE_MEDIA                  );
-    CASE_RETURN( TYPE_WEBSOCKET              );
-    CASE_RETURN( TYPE_CSP_REPORT             );
-    CASE_RETURN( TYPE_XSLT                   );
-    CASE_RETURN( TYPE_BEACON                 );
-    CASE_RETURN( TYPE_FETCH                  );
-    CASE_RETURN( TYPE_IMAGESET               );
-    CASE_RETURN( TYPE_WEB_MANIFEST           );
-    CASE_RETURN( TYPE_INTERNAL_SCRIPT        );
-    CASE_RETURN( TYPE_INTERNAL_WORKER        );
-    CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER );
-    CASE_RETURN( TYPE_INTERNAL_EMBED         );
-    CASE_RETURN( TYPE_INTERNAL_OBJECT        );
-    CASE_RETURN( TYPE_INTERNAL_FRAME         );
-    CASE_RETURN( TYPE_INTERNAL_IFRAME        );
-    CASE_RETURN( TYPE_INTERNAL_AUDIO         );
-    CASE_RETURN( TYPE_INTERNAL_VIDEO         );
-    CASE_RETURN( TYPE_INTERNAL_TRACK         );
+    CASE_RETURN( TYPE_OTHER                   );
+    CASE_RETURN( TYPE_SCRIPT                  );
+    CASE_RETURN( TYPE_IMAGE                   );
+    CASE_RETURN( TYPE_STYLESHEET              );
+    CASE_RETURN( TYPE_OBJECT                  );
+    CASE_RETURN( TYPE_DOCUMENT                );
+    CASE_RETURN( TYPE_SUBDOCUMENT             );
+    CASE_RETURN( TYPE_REFRESH                 );
+    CASE_RETURN( TYPE_XBL                     );
+    CASE_RETURN( TYPE_PING                    );
+    CASE_RETURN( TYPE_XMLHTTPREQUEST          );
+    CASE_RETURN( TYPE_OBJECT_SUBREQUEST       );
+    CASE_RETURN( TYPE_DTD                     );
+    CASE_RETURN( TYPE_FONT                    );
+    CASE_RETURN( TYPE_MEDIA                   );
+    CASE_RETURN( TYPE_WEBSOCKET               );
+    CASE_RETURN( TYPE_CSP_REPORT              );
+    CASE_RETURN( TYPE_XSLT                    );
+    CASE_RETURN( TYPE_BEACON                  );
+    CASE_RETURN( TYPE_FETCH                   );
+    CASE_RETURN( TYPE_IMAGESET                );
+    CASE_RETURN( TYPE_WEB_MANIFEST            );
+    CASE_RETURN( TYPE_INTERNAL_SCRIPT         );
+    CASE_RETURN( TYPE_INTERNAL_WORKER         );
+    CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER  );
+    CASE_RETURN( TYPE_INTERNAL_EMBED          );
+    CASE_RETURN( TYPE_INTERNAL_OBJECT         );
+    CASE_RETURN( TYPE_INTERNAL_FRAME          );
+    CASE_RETURN( TYPE_INTERNAL_IFRAME         );
+    CASE_RETURN( TYPE_INTERNAL_AUDIO          );
+    CASE_RETURN( TYPE_INTERNAL_VIDEO          );
+    CASE_RETURN( TYPE_INTERNAL_TRACK          );
+    CASE_RETURN( TYPE_INTERNAL_XMLHTTPREQUEST );
+    CASE_RETURN( TYPE_INTERNAL_EVENTSOURCE    );
    default:
     return "<Unknown Type>";
   }
 }
 
 #undef CASE_RETURN
 
 /* Passes on parameters from its "caller"'s context. */
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7918,16 +7918,20 @@ nsContentUtils::InternalContentPolicyTyp
   case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
     return nsIContentPolicy::TYPE_SUBDOCUMENT;
 
   case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
   case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
   case nsIContentPolicy::TYPE_INTERNAL_TRACK:
     return nsIContentPolicy::TYPE_MEDIA;
 
+  case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
+  case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
+    return nsIContentPolicy::TYPE_XMLHTTPREQUEST;
+
   default:
     return aType;
   }
 }
 
 
 nsresult
 nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
--- a/dom/base/nsIContentPolicy.idl
+++ b/dom/base/nsIContentPolicy.idl
@@ -15,17 +15,17 @@ interface nsIPrincipal;
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(b545899e-42bd-434c-8fec-a0af3448ea15)]
+[scriptable,uuid(3663021e-5670-496f-887b-b408d6526b5b)]
 interface nsIContentPolicy : nsIContentPolicyBase
 {
   /**
    * Should the resource at this location be loaded?
    * ShouldLoad will be called before loading the resource at aContentLocation
    * to determine whether to start the load at all.
    *
    * @param aContentType      the type of content being tested. This will be one
--- a/dom/base/nsIContentPolicyBase.idl
+++ b/dom/base/nsIContentPolicyBase.idl
@@ -19,17 +19,17 @@ typedef unsigned long nsContentPolicyTyp
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(11b8d725-7c2b-429e-b51f-8b5b542d5009)]
+[scriptable,uuid(20f7b9bf-d7d5-4987-ade8-b7dc0398d44a)]
 interface nsIContentPolicyBase : nsISupports
 {
   /**
    * Indicates a unset or bogus policy type.
    */
   const nsContentPolicyType TYPE_INVALID = 0;
 
   /**
@@ -252,16 +252,30 @@ interface nsIContentPolicyBase : nsISupp
 
   /**
    * Indicates an internal constant for content loaded from track elements.
    *
    * This will be mapped to TYPE_MEDIA.
    */
   const nsContentPolicyType TYPE_INTERNAL_TRACK = 32;
 
+  /**
+   * Indicates an internal constant for an XMLHttpRequest.
+   *
+   * This will be mapped to TYPE_XMLHTTPREQUEST.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_XMLHTTPREQUEST = 33;
+
+  /**
+   * Indicates an internal constant for EventSource.
+   *
+   * This will be mapped to TYPE_DATAREQUEST.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_EVENTSOURCE = 34;
+
   /* When adding new content types, please update nsContentBlocker,
    * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
    * implementations, the static_assert in dom/cache/DBSchema.cpp,
    * and other things that are not listed here that are related to
    * nsIContentPolicy. */
 
   //////////////////////////////////////////////////////////////////////
 
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1706,17 +1706,17 @@ nsXMLHttpRequest::Open(const nsACString&
   }
 
   rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
   if (NS_FAILED(rv)) return rv;
 
   rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                                  uri,
                                  mPrincipal,
                                  doc,
                                  EmptyCString(), //mime guess
                                  nullptr,         //extra
                                  &shouldLoad,
                                  nsContentUtils::GetContentPolicy(),
                                  nsContentUtils::GetSecurityManager());
@@ -1760,27 +1760,27 @@ nsXMLHttpRequest::Open(const nsACString&
   }
 
   // If we have the document, use it
   if (doc) {
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        doc,
                        secFlags,
-                       nsIContentPolicy::TYPE_XMLHTTPREQUEST,
+                       nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        nsIRequest::LOAD_BACKGROUND);
   } else {
     //otherwise use the principal
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri,
                        mPrincipal,
                        secFlags,
-                       nsIContentPolicy::TYPE_XMLHTTPREQUEST,
+                       nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                        loadGroup,
                        nullptr,   // aCallbacks
                        nsIRequest::LOAD_BACKGROUND);
   }
 
   if (NS_FAILED(rv)) return rv;
 
   mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -136,17 +136,19 @@ static_assert(nsIContentPolicy::TYPE_INV
               nsIContentPolicy::TYPE_INTERNAL_WORKER == 24 &&
               nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER == 25 &&
               nsIContentPolicy::TYPE_INTERNAL_EMBED == 26 &&
               nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 &&
               nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 &&
               nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 &&
               nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
               nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 &&
-              nsIContentPolicy::TYPE_INTERNAL_TRACK == 32,
+              nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 &&
+              nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 &&
+              nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34,
               "nsContentPolicytType values are as expected");
 
 namespace {
 
 typedef int32_t EntryId;
 
 struct IdCount
 {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4263,17 +4263,17 @@ CanvasRenderingContext2D::DrawImage(cons
     mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
   }
 
   MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6);
 
   RefPtr<SourceSurface> srcSurf;
   gfx::IntSize imgSize;
 
-  Element* element;
+  Element* element = nullptr;
 
   EnsureTarget();
   if (image.IsHTMLCanvasElement()) {
     HTMLCanvasElement* canvas = &image.GetAsHTMLCanvasElement();
     element = canvas;
     nsIntSize size = canvas->GetSize();
     if (size.width == 0 || size.height == 0) {
       error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1363,16 +1363,17 @@ WebGLContext::ForceClearFramebufferWithD
 {
     MakeContextCurrent();
 
     bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
     bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
     bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
     bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
     bool shouldOverrideDrawBuffers = false;
+    bool usingDefaultFrameBuffer = !mBoundDrawFramebuffer;
 
     GLenum currentDrawBuffers[WebGLContext::kMaxColorAttachments];
 
     // Fun GL fact: No need to worry about the viewport here, glViewport is just
     // setting up a coordinates transformation, it doesn't affect glClear at all.
     AssertCachedState(); // Can't check cached bindings, as we could
                          // have a different FB bound temporarily.
 
@@ -1380,27 +1381,37 @@ WebGLContext::ForceClearFramebufferWithD
     gl->fDisable(LOCAL_GL_SCISSOR_TEST);
 
     if (initializeColorBuffer) {
 
         if (drawBuffersIsEnabled) {
 
             GLenum drawBuffersCommand[WebGLContext::kMaxColorAttachments] = { LOCAL_GL_NONE };
 
-            for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
+            for (int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
                 GLint temp;
                 gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp);
                 currentDrawBuffers[i] = temp;
 
                 if (colorAttachmentsMask[i]) {
                     drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i;
                 }
                 if (currentDrawBuffers[i] != drawBuffersCommand[i])
                     shouldOverrideDrawBuffers = true;
             }
+
+            // When clearing the default framebuffer, we must be clearing only
+            // GL_BACK, and nothing else, or else gl may return an error. We will
+            // only use the first element of currentDrawBuffers in this case.
+            if (usingDefaultFrameBuffer) {
+                gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK);
+                if (currentDrawBuffers[0] == LOCAL_GL_COLOR_ATTACHMENT0)
+                    currentDrawBuffers[0] = LOCAL_GL_BACK;
+                shouldOverrideDrawBuffers = false;
+            }
             // calling draw buffers can cause resolves on adreno drivers so
             // we try to avoid calling it
             if (shouldOverrideDrawBuffers)
                 gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand);
         }
 
         gl->fColorMask(1, 1, 1, 1);
 
@@ -1436,18 +1447,23 @@ WebGLContext::ForceClearFramebufferWithD
         gl->fEnable(LOCAL_GL_SCISSOR_TEST);
 
     if (mRasterizerDiscardEnabled) {
         gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
     }
 
     // Restore GL state after clearing.
     if (initializeColorBuffer) {
-        if (shouldOverrideDrawBuffers) {
-            gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
+
+        if (drawBuffersIsEnabled) {
+            if (usingDefaultFrameBuffer) {
+                gl->Screen()->SetDrawBuffer(currentDrawBuffers[0]);
+            } else if (shouldOverrideDrawBuffers) {
+                gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
+            }
         }
 
         gl->fColorMask(mColorWriteMask[0],
                        mColorWriteMask[1],
                        mColorWriteMask[2],
                        mColorWriteMask[3]);
         gl->fClearColor(mColorClearValue[0],
                         mColorClearValue[1],
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -3,16 +3,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 "WebGLContext.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLFramebuffer.h"
 #include "GLContext.h"
+#include "GLScreenBuffer.h"
 
 namespace mozilla {
 
 void
 WebGLContext::Clear(GLbitfield mask)
 {
     if (IsContextLost())
         return;
@@ -155,26 +156,18 @@ WebGLContext::DrawBuffers(const dom::Seq
          buffered contexts, or into the back buffer for double-buffered
          contexts. If DrawBuffersEXT is supplied with a constant other than
          BACK and NONE, the error INVALID_OPERATION is generated.
          */
         if (buffersLength != 1) {
             return ErrorInvalidValue("drawBuffers: invalid <buffers> (main framebuffer: buffers.length must be 1)");
         }
 
-        MakeContextCurrent();
-
-        if (buffers[0] == LOCAL_GL_NONE) {
-            const GLenum drawBuffersCommand = LOCAL_GL_NONE;
-            gl->fDrawBuffers(1, &drawBuffersCommand);
-            return;
-        }
-        else if (buffers[0] == LOCAL_GL_BACK) {
-            const GLenum drawBuffersCommand = LOCAL_GL_COLOR_ATTACHMENT0;
-            gl->fDrawBuffers(1, &drawBuffersCommand);
+        if (buffers[0] == LOCAL_GL_NONE || buffers[0] == LOCAL_GL_BACK) {
+            gl->Screen()->SetDrawBuffer(buffers[0]);
             return;
         }
         return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)");
     }
 
     // OK: we are rendering in a framebuffer object
 
     if (buffersLength > size_t(mGLMaxDrawBuffers)) {
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -504,17 +504,17 @@ WebGLFramebuffer::GetAttachPoint(FBAttac
         return mStencilAttachment;
 
     default:
         break;
     }
 
     if (attachPoint >= LOCAL_GL_COLOR_ATTACHMENT1) {
         size_t colorAttachmentId = attachPoint.get() - LOCAL_GL_COLOR_ATTACHMENT0;
-        if (colorAttachmentId < WebGLContext::kMaxColorAttachments) {
+        if (colorAttachmentId < (size_t)mContext->mGLMaxColorAttachments) {
             EnsureColorAttachPoints(colorAttachmentId);
             return mMoreColorAttachments[colorAttachmentId - 1];
         }
     }
 
     MOZ_CRASH("bad `attachPoint` validation");
 }
 
@@ -827,27 +827,29 @@ WebGLFramebuffer::CheckAndInitializeAtta
             mMoreColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData);
     }
 
     return true;
 }
 
 void WebGLFramebuffer::EnsureColorAttachPoints(size_t colorAttachmentId)
 {
-    MOZ_ASSERT(colorAttachmentId < WebGLContext::kMaxColorAttachments);
+    size_t maxColorAttachments = mContext->mGLMaxColorAttachments;
+
+    MOZ_ASSERT(colorAttachmentId < maxColorAttachments);
 
     if (colorAttachmentId < ColorAttachmentCount())
         return;
 
-    while (ColorAttachmentCount() < WebGLContext::kMaxColorAttachments) {
+    while (ColorAttachmentCount() < maxColorAttachments) {
         GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + ColorAttachmentCount();
         mMoreColorAttachments.AppendElement(WebGLFBAttachPoint(this, nextAttachPoint));
     }
 
-    MOZ_ASSERT(ColorAttachmentCount() == WebGLContext::kMaxColorAttachments);
+    MOZ_ASSERT(ColorAttachmentCount() == maxColorAttachments);
 }
 
 static void
 FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined)
 {
     MOZ_ASSERT(gl, "Expected a valid GLContext ptr.");
     // GLES don't support DrawBuffer()/ReadBuffer.
     // According to http://www.opengl.org/wiki/Framebuffer_Object
--- a/dom/contacts/tests/mochitest.ini
+++ b/dom/contacts/tests/mochitest.ini
@@ -1,16 +1,16 @@
 [DEFAULT]
 skip-if = e10s
 support-files = shared.js
 
 [test_contacts_basics.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_basics2.html]
-skip-if = (toolkit == 'gonk' && debug) #debug-only failure
+skip-if = (toolkit == 'gonk' && debug) || (os == 'win' && os_version == '5.1') #debug-only failure, bug 967258 on XP
 [test_contacts_blobs.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_events.html]
 [test_contacts_getall.html]
 skip-if = (toolkit == 'gonk' && debug) || (toolkit == 'android' && processor == 'x86') #debug-only failure #x86 only
 [test_contacts_getall2.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_international.html]
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -147,19 +147,22 @@ InternalRequest::MapContentPolicyTypeToR
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_XBL:
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_PING:
     context = RequestContext::Ping;
     break;
-  case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
+  case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
     context = RequestContext::Xmlhttprequest;
     break;
+  case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
+    context = RequestContext::Eventsource;
+    break;
   case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
     context = RequestContext::Plugin;
     break;
   case nsIContentPolicy::TYPE_DTD:
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_FONT:
     context = RequestContext::Font;
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -55,22 +55,22 @@ namespace dom {
  * prefetch          |
  * script            | TYPE_INTERNAL_SCRIPT
  * sharedworker      | TYPE_INTERNAL_SHARED_WORKER
  * subresource       | Not supported by Gecko
  * style             | TYPE_STYLESHEET
  * track             | TYPE_INTERNAL_TRACK
  * video             | TYPE_INTERNAL_VIDEO
  * worker            | TYPE_INTERNAL_WORKER
- * xmlhttprequest    | TYPE_XMLHTTPREQUEST
+ * xmlhttprequest    | TYPE_INTERNAL_XMLHTTPREQUEST
+ * eventsource       | TYPE_INTERNAL_EVENTSOURCE
  * xslt              | TYPE_XSLT
  *
  * TODO: Figure out if TYPE_REFRESH maps to anything useful
  * TODO: Figure out if TYPE_DTD maps to anything useful
- * TODO: Split TYPE_XMLHTTPREQUEST and TYPE_DATAREQUEST for EventSource
  * TODO: Figure out if TYPE_WEBSOCKET maps to anything useful
  * TODO: Add a content type for prefetch
  * TODO: Use the content type for manifest when it becomes available
  * TODO: Add a content type for location
  * TODO: Add a content type for hyperlink
  * TODO: Add a content type for form
  * TODO: Add a content type for favicon
  * TODO: Add a content type for download
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -361,17 +361,16 @@ bool MediaDecoder::IsInfinite()
 MediaDecoder::MediaDecoder() :
   mWatchManager(this, AbstractThread::MainThread()),
   mDormantSupported(false),
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mLogicalPosition(0.0),
   mDuration(std::numeric_limits<double>::quiet_NaN()),
   mMediaSeekable(true),
-  mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
@@ -407,17 +406,19 @@ MediaDecoder::MediaDecoder() :
                      "MediaDecoder::mEstimatedDuration (Canonical)"),
   mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
                     "MediaDecoder::mExplicitDuration (Canonical)"),
   mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
              "MediaDecoder::mPlayState (Canonical)"),
   mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
              "MediaDecoder::mNextState (Canonical)"),
   mLogicallySeeking(AbstractThread::MainThread(), false,
-                    "MediaDecoder::mLogicallySeeking (Canonical)")
+                    "MediaDecoder::mLogicallySeeking (Canonical)"),
+  mSameOriginMedia(AbstractThread::MainThread(), false,
+                   "MediaDecoder::mSameOriginMedia (Canonical)")
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 
   //
@@ -771,22 +772,16 @@ void MediaDecoder::DecodeError()
 
 void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mSameOriginMedia = aSameOrigin;
 }
 
-bool MediaDecoder::IsSameOriginMedia()
-{
-  GetReentrantMonitor().AssertCurrentThreadIn();
-  return mSameOriginMedia;
-}
-
 bool MediaDecoder::IsSeeking() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mLogicallySeeking;
 }
 
 bool MediaDecoder::IsEndedOrShutdown() const
 {
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -567,20 +567,16 @@ public:
     nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Stop(); });
     AbstractThread::MainThread()->Dispatch(r.forget());
   }
 
   // The actual playback rate computation. The monitor must be held.
   virtual double ComputePlaybackRate(bool* aReliable);
 
-  // Return true when the media is same-origin with the element. The monitor
-  // must be held.
-  bool IsSameOriginMedia();
-
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   bool CanPlayThrough();
 
   void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
   dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
 
   // Send a new set of metadata to the state machine, to be dispatched to the
@@ -937,20 +933,16 @@ protected:
   virtual int64_t CurrentPosition() { return mCurrentPosition; }
 
   // Official duration of the media resource as observed by script.
   double mDuration;
 
   // True if the media is seekable (i.e. supports random access).
   bool mMediaSeekable;
 
-  // True if the media is same-origin with the element. Data can only be
-  // passed to MediaStreams when this is true.
-  bool mSameOriginMedia;
-
   /******
    * The following member variables can be accessed from any thread.
    ******/
 
   // Media data resource.
   nsRefPtr<MediaResource> mResource;
 
 private:
@@ -1115,16 +1107,20 @@ protected:
   // OR on the main thread.
   // Any change to the state must call NotifyAll on the monitor.
   // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.
   Canonical<PlayState> mNextState;
 
   // True if the decoder is seeking.
   Canonical<bool> mLogicallySeeking;
 
+  // True if the media is same-origin with the element. Data can only be
+  // passed to MediaStreams when this is true.
+  Canonical<bool> mSameOriginMedia;
+
 public:
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
   AbstractCanonical<double>* CanonicalVolume() {
     return &mVolume;
   }
   AbstractCanonical<double>* CanonicalPlaybackRate() {
     return &mPlaybackRate;
   }
@@ -1141,13 +1137,16 @@ public:
     return &mPlayState;
   }
   AbstractCanonical<PlayState>* CanonicalNextPlayState() {
     return &mNextState;
   }
   AbstractCanonical<bool>* CanonicalLogicallySeeking() {
     return &mLogicallySeeking;
   }
+  AbstractCanonical<bool>* CanonicalSameOriginMedia() {
+    return &mSameOriginMedia;
+  }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -237,16 +237,18 @@ MediaDecoderStateMachine::MediaDecoderSt
                  "MediaDecoderStateMachine::mNextPlayState (Mirror)"),
   mLogicallySeeking(mTaskQueue, false,
                     "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"),
   mVolume(mTaskQueue, 1.0, "MediaDecoderStateMachine::mVolume (Mirror)"),
   mLogicalPlaybackRate(mTaskQueue, 1.0,
                        "MediaDecoderStateMachine::mLogicalPlaybackRate (Mirror)"),
   mPreservesPitch(mTaskQueue, true,
                   "MediaDecoderStateMachine::mPreservesPitch (Mirror)"),
+  mSameOriginMedia(mTaskQueue, false,
+                   "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"),
   mDuration(mTaskQueue, NullableTimeUnit(),
             "MediaDecoderStateMachine::mDuration (Canonical"),
   mIsShutdown(mTaskQueue, false,
               "MediaDecoderStateMachine::mIsShutdown (Canonical)"),
   mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"),
   mCurrentPosition(mTaskQueue, 0,
                    "MediaDecoderStateMachine::mCurrentPosition (Canonical)")
@@ -312,16 +314,17 @@ MediaDecoderStateMachine::Initialization
   mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration());
   mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(mDecoder->CanonicalPlayState());
   mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
   mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(mDecoder->CanonicalVolume());
   mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
   mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
+  mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia());
 
   // Initialize watchers.
   mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
@@ -368,17 +371,17 @@ int64_t MediaDecoderStateMachine::GetDec
 }
 
 void MediaDecoderStateMachine::SendStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
-  bool finished = mDecodedStream->SendData(mVolume, mDecoder->IsSameOriginMedia());
+  bool finished = mDecodedStream->SendData(mVolume, mSameOriginMedia);
 
   const auto clockTime = GetClock();
   while (true) {
     const MediaData* 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
@@ -2212,16 +2215,17 @@ MediaDecoderStateMachine::FinishShutdown
   mEstimatedDuration.DisconnectIfConnected();
   mExplicitDuration.DisconnectIfConnected();
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
   mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
+  mSameOriginMedia.DisconnectIfConnected();
   mDuration.DisconnectAll();
   mIsShutdown.DisconnectAll();
   mNextFrameStatus.DisconnectAll();
   mCurrentPosition.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -1313,16 +1313,20 @@ private:
   // TODO: The separation between mPlaybackRate and mLogicalPlaybackRate is a
   // kludge to preserve existing fragile logic while converting this setup to
   // state-mirroring. Some hero should clean this up.
   Mirror<double> mLogicalPlaybackRate;
 
   // Pitch preservation for the playback rate.
   Mirror<bool> mPreservesPitch;
 
+  // True if the media is same-origin with the element. Data can only be
+  // passed to MediaStreams when this is true.
+  Mirror<bool> mSameOriginMedia;
+
   // Duration of the media. This is guaranteed to be non-null after we finish
   // decoding the first frame.
   Canonical<media::NullableTimeUnit> mDuration;
 
   // Whether we're currently in or transitioning to shutdown state.
   Canonical<bool> mIsShutdown;
 
   // The status of our next frame. Mirrored on the main thread and used to
--- a/dom/media/MediaEventSource.h
+++ b/dom/media/MediaEventSource.h
@@ -10,16 +10,17 @@
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 
 /**
  * A thread-safe tool to communicate "revocation" across threads. It is used to
  * disconnect a listener from the event source to prevent future notifications
  * from coming. Revoke() can be called on any thread. However, it is recommended
  * to be called on the target thread to avoid race condition.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -149,20 +149,16 @@ MediaFormatReader::Shutdown()
   mPlatform = nullptr;
 
   return MediaDecoderReader::Shutdown();
 }
 
 void
 MediaFormatReader::InitLayersBackendType()
 {
-  if (!IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
-    // Not playing video, we don't care about the layers backend type.
-    return;
-  }
   // Extract the layer manager backend type so that platform decoders
   // can determine whether it's worthwhile using hardware accelerated
   // video decoding.
   MediaDecoderOwner* owner = mDecoder->GetOwner();
   if (!owner) {
     NS_WARNING("MediaFormatReader without a decoder owner, can't get HWAccel");
     return;
   }
@@ -413,18 +409,22 @@ MediaFormatReader::EnsureDecodersSetup()
       // JavaScript player app. Note: we still go through the motions here
       // even if EME is disabled, so that if script tries and fails to create
       // a CDM, we can detect that and notify chrome and show some UI
       // explaining that we failed due to EME being disabled.
       nsRefPtr<CDMProxy> proxy;
       {
         ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
         proxy = mDecoder->GetCDMProxy();
+        MOZ_ASSERT(proxy);
+
+        CDMCaps::AutoLock caps(proxy->Capabilites());
+        mInfo.mVideo.mIsRenderedExternally = caps.CanRenderVideo();
+        mInfo.mAudio.mIsRenderedExternally = caps.CanRenderAudio();
       }
-      MOZ_ASSERT(proxy);
 
       mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy);
       NS_ENSURE_TRUE(mPlatform, false);
 #else
       // EME not supported.
       return false;
 #endif
     } else {
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -41,16 +41,17 @@ public:
     : mId(aId)
     , mKind(aKind)
     , mLabel(aLabel)
     , mLanguage(aLanguage)
     , mEnabled(aEnabled)
     , mTrackId(aTrackId)
     , mDuration(0)
     , mMediaTime(0)
+    , mIsRenderedExternally(false)
     , mType(aType)
   {
     MOZ_COUNT_CTOR(TrackInfo);
   }
 
   // Only used for backward compatibility. Do not use in new code.
   void Init(TrackType aType,
             const nsAString& aId,
@@ -78,16 +79,20 @@ public:
 
   TrackID mTrackId;
 
   nsAutoCString mMimeType;
   int64_t mDuration;
   int64_t mMediaTime;
   CryptoTrack mCrypto;
 
+  // True if the track is gonna be (decrypted)/decoded and
+  // rendered directly by non-gecko components.
+  bool mIsRenderedExternally;
+
   virtual AudioInfo* GetAsAudioInfo()
   {
     return nullptr;
   }
   virtual VideoInfo* GetAsVideoInfo()
   {
     return nullptr;
   }
@@ -142,16 +147,17 @@ protected:
     mLabel = aOther.mLabel;
     mLanguage = aOther.mLanguage;
     mEnabled = aOther.mEnabled;
     mTrackId = aOther.mTrackId;
     mMimeType = aOther.mMimeType;
     mDuration = aOther.mDuration;
     mMediaTime = aOther.mMediaTime;
     mCrypto = aOther.mCrypto;
+    mIsRenderedExternally = aOther.mIsRenderedExternally;
     mType = aOther.mType;
     MOZ_COUNT_CTOR(TrackInfo);
   }
 
 private:
   TrackType mType;
 };
 
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -254,23 +254,23 @@ function setupEnvironment() {
   // We don't care about waiting for this to complete, we just want to ensure
   // that we don't build up a huge backlog of GC work.
   SpecialPowers.exactGC(window);
 }
 
 // This is called by steeplechase; which provides the test configuration options
 // directly to the test through this function.  If we're not on steeplechase,
 // the test is configured directly and immediately.
-function run_test(is_initiator) {
+function run_test(is_initiator,timeout) {
   var options = { is_local: is_initiator,
                   is_remote: !is_initiator };
 
   setTimeout(() => {
-    unexpectedEventArrived(new Error("PeerConnectionTest timed out after 30s"));
-  }, 30000);
+    unexpectedEventArrived(new Error("PeerConnectionTest timed out after "+timeout+"s"));
+  }, timeout);
 
   // Also load the steeplechase test code.
   var s = document.createElement("script");
   s.src = "/test.js";
   s.onload = () => setTestOptions(options);
   document.head.appendChild(s);
 }
 
--- a/dom/media/tests/mochitest/steeplechase_long/long.js
+++ b/dom/media/tests/mochitest/steeplechase_long/long.js
@@ -168,52 +168,50 @@ function verifyConnectionStatus(test) {
     processPcStats(test.pcRemote, 'REMOTE', OPERATIONS);
   }
 }
 
 
 /**
  * Generates a setInterval wrapper command link for use in pc.js command chains
  *
- * The link will repeatedly call the given callback function every interval ms
+ * This function returns a promise that will resolve once the link repeatedly
+ * calls the given callback function every interval ms
  * until duration ms have passed, then it will continue the test.
  *
  * @param {function} callback
  *        Function to be called on each interval
  * @param {number} [interval=1000]
  *        Frequency in milliseconds with which callback will be called
  * @param {number} [duration=3 hours]
  *        Length of time in milliseconds for which callback will be called
- * @param {string} [name='INTERVAL_COMMAND']
- *        Name of the generated command link
+
+
  */
-function generateIntervalCommand(callback, interval, duration, name) {
+function generateIntervalCommand(callback, interval, duration) {
   interval = interval || 1000;
   duration = duration || 1000 * 3600 * 3;
-  name = name || 'INTERVAL_COMMAND';
+
+  return function INTERVAL_COMMAND(test) {
+      return new Promise (resolve=>{
+        var startTime = Date.now();
+        var intervalId = setInterval(function () {
+          if (callback) {
+            callback(test);
+          }
 
-  return [
-    name,
-    function (test) {
-      var startTime = Date.now();
-      var intervalId = setInterval(function () {
-        if (callback) {
-          callback(test);
-        }
-
-        var failed = false;
-        Object.keys(_errorCount).forEach(function (label) {
-          if (_errorCount[label] > MAX_ERROR_CYCLES) {
-            ok(false, "Encountered more then " + MAX_ERROR_CYCLES + " cycles" +
+          var failed = false;
+          Object.keys(_errorCount).forEach(function (label) {
+            if (_errorCount[label] > MAX_ERROR_CYCLES) {
+              ok(false, "Encountered more then " + MAX_ERROR_CYCLES + " cycles" +
               " with errors on " + label);
-            failed = true;
-          }
-        });
+              failed = true;
+            }
+          });
         var timeElapsed = Date.now() - startTime;
         if ((timeElapsed >= duration) || failed) {
           clearInterval(intervalId);
-          test.next();
+          resolve();
         }
       }, interval);
-    }
-  ]
+    });
+  };
 }
-
--- a/dom/media/tests/mochitest/steeplechase_long/steeplechase_long.ini
+++ b/dom/media/tests/mochitest/steeplechase_long/steeplechase_long.ini
@@ -1,13 +1,12 @@
 [DEFAULT]
 support-files =
   long.js
   ../head.js
   ../mediaStreamPlayback.js
   ../pc.js
   ../templates.js
   ../turnConfig.js
+  ../network.js
 
-[test_peerConnection_basicAudio_long.html]
-[test_peerConnection_basicVideo_long.html]
 [test_peerConnection_basicAudioVideoCombined_long.html]
 
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudioVideoCombined_long.html
+++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudioVideoCombined_long.html
@@ -13,23 +13,24 @@
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1014328",
     title: "Basic audio/video (combined) peer connection, long running",
     visible: true
   });
 
+  var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3;
   var test;
   runNetworkTest(function (options) {
     options = options || {};
-    options.commands = commandsPeerConnection.slice(0);
+    options.commands = makeDefaultCommands();
     options.commands.push(generateIntervalCommand(verifyConnectionStatus,
                                                   1000 * 10,
-                                                  1000 * 3600 * 3));
+                                                  STEEPLECHASE_TIMEOUT));
 
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{audio: true, video: true, fake: false}],
                              [{audio: true, video: true, fake: false}]);
     test.run();
   });
 </script>
 </pre>
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudio_long.html
+++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicAudio_long.html
@@ -13,23 +13,24 @@
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796892",
     title: "Basic audio-only peer connection",
     visible: true
   });
 
+  var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3;
   var test;
   runNetworkTest(function (options) {
     options = options || {};
-    options.commands = commandsPeerConnection.slice(0);
+    options.commands = makeDefaultCommands();
     options.commands.push(generateIntervalCommand(verifyConnectionStatus,
                                                   1000 * 10,
-                                                  1000 * 3600 * 3));
+                                                  STEEPLECHASE_TIMEOUT));
 
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{audio: true, fake: false}],
                              [{audio: true, fake: false}]);
     test.run();
   });
 </script>
 </pre>
--- a/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicVideo_long.html
+++ b/dom/media/tests/mochitest/steeplechase_long/test_peerConnection_basicVideo_long.html
@@ -13,23 +13,24 @@
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796888",
     title: "Basic video-only peer connection",
     visible: true
   });
 
+  var STEEPLECHASE_TIMEOUT = 1000 * 3600 * 3;
   var test;
   runNetworkTest(function (options) {
     options = options || {};
-    options.commands = commandsPeerConnection.slice(0);
+    options.commands = makeDefaultCommands();
     options.commands.push(generateIntervalCommand(verifyConnectionStatus,
                                                   1000 * 10,
-                                                  1000 * 3600 * 3));
+                                                  STEEPLECHASE_TIMEOUT));
 
     test = new PeerConnectionTest(options);
     test.setMediaConstraints([{video: true, fake: false}],
                              [{video: true, fake: false}]);
     test.run();
   });
 </script>
 </pre>
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -747,17 +747,17 @@ MediaEngineGonkVideoSource::RotateImage(
 
   uint32_t half_width = dstWidth / 2;
 
   layers::GrallocImage* videoImage = static_cast<layers::GrallocImage*>(image.get());
   MOZ_ASSERT(mTextureClientAllocator);
   RefPtr<layers::TextureClient> textureClient
     = mTextureClientAllocator->CreateOrRecycleForDrawing(gfx::SurfaceFormat::YUV,
                                                          gfx::IntSize(dstWidth, dstHeight),
-                                                         gfx::BackendType::NONE,
+                                                         layers::BackendSelector::Content,
                                                          layers::TextureFlags::DEFAULT,
                                                          layers::ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
   if (textureClient) {
     RefPtr<layers::GrallocTextureClientOGL> grallocTextureClient =
       static_cast<layers::GrallocTextureClientOGL*>(textureClient.get());
 
     android::sp<android::GraphicBuffer> destBuffer = grallocTextureClient->GetGraphicBuffer();
 
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp
@@ -124,19 +124,19 @@ SpeechRecognition::SpeechRecognition(nsP
   mTestConfig.Init();
   if (mTestConfig.mEnableTests) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_EVENT_REQUEST_TOPIC, false);
     obs->AddObserver(this, SPEECH_RECOGNITION_TEST_END_TOPIC, false);
   }
 
   mEndpointer.set_speech_input_complete_silence_length(
-      Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 500000));
+      Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 1250000));
   mEndpointer.set_long_speech_input_complete_silence_length(
-      Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 1000000));
+      Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 2500000));
   mEndpointer.set_long_speech_length(
       Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 3 * 1000000));
   Reset();
 }
 
 bool
 SpeechRecognition::StateBetween(FSMState begin, FSMState end)
 {
--- a/dom/network/NetworkStatsService.jsm
+++ b/dom/network/NetworkStatsService.jsm
@@ -122,17 +122,17 @@ this.NetworkStatsService = {
     this.messages.forEach(function(aMsgName) {
       ppmm.addMessageListener(aMsgName, this);
     }, this);
 
     this._db = new NetworkStatsDB();
 
     // Stats for all interfaces are updated periodically
     this.timer.initWithCallback(this, this._db.sampleRate,
-                                Ci.nsITimer.TYPE_REPEATING_PRECISE);
+                                Ci.nsITimer.TYPE_REPEATING_PRECISE_CAN_SKIP);
 
     // Stats not from netd are firstly stored in the cached.
     this.cachedStats = Object.create(null);
     this.cachedStatsDate = new Date();
 
     this.updateQueue = [];
     this.isQueueRunning = false;
 
--- a/dom/plugins/ipc/PluginInterposeOSX.mm
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -579,17 +579,17 @@ void OnPluginShowWindow(uint32_t window_
 
   CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
 
   if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
       (plugin_fullscreen_windows_set_.find(window_id) ==
        plugin_fullscreen_windows_set_.end())) {
     plugin_fullscreen_windows_set_.insert(window_id);
 
-    nsCocoaUtils::HideOSChromeOnScreen(TRUE, [[NSScreen screens] objectAtIndex:0]);
+    nsCocoaUtils::HideOSChromeOnScreen(true);
   }
 }
 
 static void ActivateProcess(pid_t pid) {
   ProcessSerialNumber process;
   OSStatus status = ::GetProcessForPID(pid, &process);
 
   if (status == noErr) {
@@ -602,17 +602,17 @@ static void ActivateProcess(pid_t pid) {
 // Must be called on the UI thread.
 // If plugin_pid is -1, the browser will be the active process on return,
 // otherwise that process will be given focus back before this function returns.
 static void ReleasePluginFullScreen(pid_t plugin_pid) {
   // Releasing full screen only works if we are the frontmost process; grab
   // focus, but give it back to the plugin process if requested.
   ActivateProcess(base::GetCurrentProcId());
 
-  nsCocoaUtils::HideOSChromeOnScreen(FALSE, [[NSScreen screens] objectAtIndex:0]);
+  nsCocoaUtils::HideOSChromeOnScreen(false);
 
   if (plugin_pid != -1) {
     ActivateProcess(plugin_pid);
   }
 }
 
 void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
   bool had_windows = !plugin_visible_windows_set_.empty();
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -445,19 +445,23 @@ public:
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
     nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
-      nsRefPtr<WorkerPushSubscription> sub =
-        new WorkerPushSubscription(mEndpoint, mScope);
-      promise->MaybeResolve(sub);
+      if (mEndpoint.IsEmpty()) {
+        promise->MaybeResolve(JS::NullHandleValue);
+      } else {
+        nsRefPtr<WorkerPushSubscription> sub =
+          new WorkerPushSubscription(mEndpoint, mScope);
+        promise->MaybeResolve(sub);
+      }
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     proxy->CleanUp(aCx);
     return true;
   }
 private:
--- a/dom/security/test/cors/mochitest.ini
+++ b/dom/security/test/cors/mochitest.ini
@@ -4,9 +4,8 @@ support-files =
   file_CrossSiteXHR_inner.html
   file_CrossSiteXHR_inner.jar
   file_CrossSiteXHR_inner_data.sjs
   file_CrossSiteXHR_server.sjs
 
 [test_CrossSiteXHR.html]
 [test_CrossSiteXHR_cache.html]
 [test_CrossSiteXHR_origin.html]
-skip-if = buildapp == 'b2g' || e10s # last test fails to trigger onload on e10s/b2g
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -927,19 +927,19 @@ var interfaceNamesInGlobalScope =
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PropertyNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PushManager", b2g: false, android: false, release: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PushSubscription", b2g: false, android: false, release: false},
+    {name: "PushManager", b2g: false, android: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PushSubscription", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RadioNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Range",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RecordErrorEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Rect",
@@ -965,21 +965,21 @@ var interfaceNamesInGlobalScope =
     "Screen",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ScriptProcessorNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ScrollAreaEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Selection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorker", release: false, b2g: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorkerContainer", release: false, b2g: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorkerRegistration", release: false, b2g: false},
+    {name: "ServiceWorker", b2g: false, android: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ServiceWorkerContainer", b2g: false, android: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ServiceWorkerRegistration", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SettingsLock",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SettingsManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SharedWorker",
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -91,16 +91,26 @@ static_assert(nsIHttpChannelInternal::CO
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
               "RequestMode enumeration value should match Necko CORS mode value.");
 
 static StaticRefPtr<ServiceWorkerManager> gInstance;
 
+// Tracks the "dom.disable_open_click_delay" preference.  Modified on main
+// thread, read on worker threads. This is set once in the ServiceWorkerManager
+// constructor before any service workers are spawned.
+// It is updated every time a "notificationclick" event is dispatched. While
+// this is done without synchronization, at the worst, the thread will just get
+// an older value within which a popup is allowed to be displayed, which will
+// still be a valid value since it was set in the constructor. I (:nsm) don't
+// think this needs to be synchronized.
+Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
+
 struct ServiceWorkerManager::RegistrationDataPerPrincipal
 {
   // Ordered list of scopes for glob matching.
   // Each entry is an absolute URL representing the scope.
   // Each value of the hash table is an array of an absolute URLs representing
   // the scopes.
   //
   // An array is used for now since the number of controlled scopes per
@@ -381,16 +391,18 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
   : mActor(nullptr)
   , mShuttingDown(false)
 {
   // Register this component to PBackground.
   MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
+
+  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mRegistrationInfos.Clear();
   MOZ_ASSERT(!mActor);
 }
@@ -1609,25 +1621,26 @@ ServiceWorkerManager::AppendPendingOpera
   if (!mShuttingDown) {
     PendingOperation* opt = mPendingOperations.AppendElement();
     opt->mRunnable = aRunnable;
   }
 }
 
 namespace {
 // Just holds a ref to a ServiceWorker until the Promise is fulfilled.
-class KeepAliveHandler final : public PromiseNativeHandler
+class KeepAliveHandler : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
 
+protected:
   virtual ~KeepAliveHandler()
   {}
 
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   explicit KeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
     : mServiceWorker(aServiceWorker)
   {}
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
@@ -1646,16 +1659,160 @@ public:
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 #endif
   }
 };
 
 NS_IMPL_ISUPPORTS0(KeepAliveHandler)
 
+void
+DummyCallback(nsITimer* aTimer, void* aClosure)
+{
+  // Nothing.
+}
+
+class AllowWindowInteractionKeepAliveHandler;
+
+class ClearWindowAllowedRunnable final : public WorkerRunnable
+{
+public:
+  ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate,
+                             AllowWindowInteractionKeepAliveHandler* aHandler)
+  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+  , mHandler(aHandler)
+  { }
+
+private:
+  bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    // WorkerRunnable asserts that the dispatch is from parent thread if
+    // the busy count modification is WorkerThreadUnchangedBusyCount.
+    // Since this runnable will be dispatched from the timer thread, we override
+    // PreDispatch and PostDispatch to skip the check.
+    return true;
+  }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) override
+  {
+    // Silence bad assertions.
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
+
+  nsRefPtr<AllowWindowInteractionKeepAliveHandler> mHandler;
+};
+
+class AllowWindowInteractionKeepAliveHandler final : public KeepAliveHandler
+{
+  friend class ClearWindowAllowedRunnable;
+  nsCOMPtr<nsITimer> mTimer;
+
+  ~AllowWindowInteractionKeepAliveHandler()
+  {
+    MOZ_ASSERT(!mTimer);
+  }
+
+  void
+  ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    if (mTimer) {
+      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
+      mTimer->Cancel();
+      mTimer = nullptr;
+      MOZ_ALWAYS_TRUE(aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), false));
+    }
+  }
+
+  void
+  StartClearWindowTimer(WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(!mTimer);
+
+    nsresult rv;
+    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    nsRefPtr<ClearWindowAllowedRunnable> r =
+      new ClearWindowAllowedRunnable(aWorkerPrivate, this);
+
+    nsRefPtr<TimerThreadEventTarget> target =
+      new TimerThreadEventTarget(aWorkerPrivate, r);
+
+    rv = timer->SetTarget(target);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    // The important stuff that *has* to be reversed.
+    if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), true))) {
+      return;
+    }
+    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
+    timer.swap(mTimer);
+
+    // We swap first and then initialize the timer so that even if initializing
+    // fails, we still clean the busy count and interaction count correctly.
+    // The timer can't be initialized before modifying the busy count since the
+    // timer thread could run and call the timeout but the worker may
+    // already be terminating and modifying the busy count could fail.
+    rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, gDOMDisableOpenClickDelay, nsITimer::TYPE_ONE_SHOT);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ClearWindowAllowed(aWorkerPrivate);
+      return;
+    }
+  }
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  AllowWindowInteractionKeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
+                                         WorkerPrivate* aWorkerPrivate)
+    : KeepAliveHandler(aServiceWorker)
+  {
+    StartClearWindowTimer(aWorkerPrivate);
+  }
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
+    ClearWindowAllowed(worker);
+    KeepAliveHandler::ResolvedCallback(aCx, aValue);
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
+    ClearWindowAllowed(worker);
+    KeepAliveHandler::RejectedCallback(aCx, aValue);
+  }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(AllowWindowInteractionKeepAliveHandler, KeepAliveHandler)
+
+bool
+ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+{
+  mHandler->ClearWindowAllowed(aWorkerPrivate);
+  return true;
+}
+
 // Returns a Promise if the event was successfully dispatched and no exceptions
 // were raised, otherwise returns null.
 already_AddRefed<Promise>
 DispatchExtendableEventOnWorkerScope(JSContext* aCx,
                                      WorkerGlobalScope* aWorkerScope,
                                      ExtendableEvent* aEvent)
 {
   MOZ_ASSERT(aWorkerScope);
@@ -2376,22 +2533,27 @@ public:
     nei.mCancelable = true;
 
     nsRefPtr<NotificationEvent> event =
       NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
+    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
     event->SetTrusted(true);
     nsRefPtr<Promise> waitUntilPromise =
       DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+    // If the handler calls WaitUntil(), that will manage its own interaction
+    // 'stack'.
+    aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
 
     if (waitUntilPromise) {
-      nsRefPtr<KeepAliveHandler> handler = new KeepAliveHandler(mServiceWorker);
+      nsRefPtr<AllowWindowInteractionKeepAliveHandler> handler =
+        new AllowWindowInteractionKeepAliveHandler(mServiceWorker, aWorkerPrivate);
       waitUntilPromise->AppendNativeHandler(handler);
     }
 
     return true;
   }
 };
 
 NS_IMETHODIMP
@@ -2407,16 +2569,18 @@ ServiceWorkerManager::SendNotificationCl
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
+
   nsRefPtr<ServiceWorker> serviceWorker =
     CreateServiceWorkerForScope(attrs, aScope, nullptr);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -85,19 +85,17 @@ public:
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
     UniquePtr<ServiceWorkerClientInfo> clientInfo;
 
     if (window) {
-      mozilla::ErrorResult result;
-      //FIXME(catalinb): Bug 1144660 - check if we are allowed to focus here.
-      window->Focus(result);
+      nsContentUtils::DispatchChromeEvent(window->GetExtantDoc(), window->GetOuterWindow(), NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), true, true);
       clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument(),
                                                    window->GetOuterWindow()));
     }
 
     DispatchResult(Move(clientInfo));
     return NS_OK;
   }
 
@@ -137,24 +135,28 @@ ServiceWorkerWindowClient::Focus(ErrorRe
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  nsRefPtr<PromiseWorkerProxy> promiseProxy =
-    PromiseWorkerProxy::Create(workerPrivate, promise);
-  if (!promiseProxy->GetWorkerPromise()) {
-    // Don't dispatch if adding the worker feature failed.
-    return promise.forget();
-  }
+  if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
+    nsRefPtr<PromiseWorkerProxy> promiseProxy =
+      PromiseWorkerProxy::Create(workerPrivate, promise);
+    if (!promiseProxy->GetWorkerPromise()) {
+      // Don't dispatch if adding the worker feature failed.
+      return promise.forget();
+    }
 
-  nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
-                                                            promiseProxy);
-  aRv = NS_DispatchToMainThread(r);
-  if (NS_WARN_IF(aRv.Failed())) {
-    promise->MaybeReject(aRv);
+    nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
+                                                              promiseProxy);
+    aRv = NS_DispatchToMainThread(r);
+    if (NS_WARN_IF(aRv.Failed())) {
+      promise->MaybeReject(aRv);
+    }
+  } else {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
   return promise.forget();
 }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1861,78 +1861,16 @@ private:
 };
 
 void
 DummyCallback(nsITimer* aTimer, void* aClosure)
 {
   // Nothing!
 }
 
-class TimerThreadEventTarget final : public nsIEventTarget
-{
-  ~TimerThreadEventTarget() {}
-
-  WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<WorkerRunnable> mWorkerRunnable;
-
-public:
-  TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
-                         WorkerRunnable* aWorkerRunnable)
-  : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aWorkerRunnable);
-  }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-protected:
-  NS_IMETHOD
-  DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override
-  {
-    nsCOMPtr<nsIRunnable> runnable(aRunnable);
-    return Dispatch(runnable.forget(), aFlags);
-  }
-
-  NS_IMETHOD
-  Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override
-  {
-    // This should only happen on the timer thread.
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
-
-    nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
-
-    // Run the runnable we're given now (should just call DummyCallback()),
-    // otherwise the timer thread will leak it...  If we run this after
-    // dispatch running the event can race against resetting the timer.
-    nsCOMPtr<nsIRunnable> runnable(aRunnable);
-    runnable->Run();
-
-    // This can fail if we're racing to terminate or cancel, should be handled
-    // by the terminate or cancel code.
-    mWorkerRunnable->Dispatch(nullptr);
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  IsOnCurrentThread(bool* aIsOnCurrentThread) override
-  {
-    MOZ_ASSERT(aIsOnCurrentThread);
-
-    nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    return NS_OK;
-  }
-};
-
 class KillCloseEventRunnable final : public WorkerRunnable
 {
   nsCOMPtr<nsITimer> mTimer;
 
   class KillScriptRunnable final : public WorkerControlRunnable
   {
   public:
     explicit KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
@@ -2376,16 +2314,70 @@ PRThreadFromThread(nsIThread* aThread)
 }
 
 } /* anonymous namespace */
 
 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
 
+TimerThreadEventTarget::TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
+                                               WorkerRunnable* aWorkerRunnable)
+  : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  MOZ_ASSERT(aWorkerRunnable);
+}
+
+TimerThreadEventTarget::~TimerThreadEventTarget()
+{
+}
+
+NS_IMETHODIMP
+TimerThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
+{
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  return Dispatch(runnable.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+TimerThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags)
+{
+  // This should only happen on the timer thread.
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
+
+  nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
+
+  // Run the runnable we're given now (should just call DummyCallback()),
+  // otherwise the timer thread will leak it...  If we run this after
+  // dispatch running the event can race against resetting the timer.
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  runnable->Run();
+
+  // This can fail if we're racing to terminate or cancel, should be handled
+  // by the terminate or cancel code.
+  mWorkerRunnable->Dispatch(nullptr);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TimerThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
+{
+  MOZ_ASSERT(aIsOnCurrentThread);
+
+  nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
 
 WorkerLoadInfo::WorkerLoadInfo()
   : mWindowID(UINT64_MAX)
   , mServiceWorkerID(0)
   , mFromWindow(false)
   , mEvalAllowed(false)
   , mReportCSPViolations(false)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1570,11 +1570,35 @@ public:
 
   nsIEventTarget*
   EventTarget() const
   {
     return mTarget;
   }
 };
 
+class TimerThreadEventTarget final : public nsIEventTarget
+{
+  ~TimerThreadEventTarget();
+
+  WorkerPrivate* mWorkerPrivate;
+  nsRefPtr<WorkerRunnable> mWorkerRunnable;
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
+                         WorkerRunnable* aWorkerRunnable);
+
+protected:
+  NS_IMETHOD
+  DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
+
+
+  NS_IMETHOD
+  Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override;
+
+  NS_IMETHOD
+  IsOnCurrentThread(bool* aIsOnCurrentThread) override;
+};
+
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -54,17 +54,18 @@ USING_WORKERS_NAMESPACE
 
 using mozilla::dom::cache::CacheStorage;
 using mozilla::dom::indexedDB::IDBFactory;
 using mozilla::ipc::PrincipalInfo;
 
 BEGIN_WORKERS_NAMESPACE
 
 WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
-: mWorkerPrivate(aWorkerPrivate)
+: mWindowInteractionsAllowed(0)
+, mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 WorkerGlobalScope::~WorkerGlobalScope()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -54,16 +54,18 @@ class WorkerGlobalScope : public DOMEven
 
   nsRefPtr<Console> mConsole;
   nsRefPtr<WorkerLocation> mLocation;
   nsRefPtr<WorkerNavigator> mNavigator;
   nsRefPtr<Performance> mPerformance;
   nsRefPtr<IDBFactory> mIndexedDB;
   nsRefPtr<cache::CacheStorage> mCacheStorage;
 
+  uint32_t mWindowInteractionsAllowed;
+
 protected:
   WorkerPrivate* mWorkerPrivate;
 
   explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
 
 public:
   virtual JSObject*
@@ -157,16 +159,35 @@ public:
 
   already_AddRefed<Promise>
   CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   CreateImageBitmap(const ImageBitmapSource& aImage,
                     int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
                     ErrorResult& aRv);
+
+  bool
+  WindowInteractionAllowed() const
+  {
+    return mWindowInteractionsAllowed > 0;
+  }
+
+  void
+  AllowWindowInteraction()
+  {
+    mWindowInteractionsAllowed++;
+  }
+
+  void
+  ConsumeWindowInteraction()
+  {
+    MOZ_ASSERT(mWindowInteractionsAllowed > 0);
+    mWindowInteractionsAllowed--;
+  }
 };
 
 class DedicatedWorkerGlobalScope final : public WorkerGlobalScope
 {
   ~DedicatedWorkerGlobalScope() { }
 
 public:
   explicit DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
--- a/dom/workers/test/serviceworkers/browser_force_refresh.js
+++ b/dom/workers/test/serviceworkers/browser_force_refresh.js
@@ -12,16 +12,17 @@ function forceRefresh() {
   EventUtils.synthesizeKey('R', { accelKey: true, shiftKey: true });
 }
 
 function test() {
   waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true],
                                      ['dom.serviceWorkers.exemptFromPerDomainMax', true],
                                      ['dom.serviceWorkers.testing.enabled', true],
+                                     ['dom.serviceWorkers.interception.enabled', true],
                                      ['dom.caches.enabled', true]]},
                             function() {
     var url = gTestRoot + 'browser_base_force_refresh.html';
     var tab = gBrowser.addTab(url);
     gBrowser.selectedTab = tab;
 
     var cachedLoad = false;
 
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/client_focus_worker.js
+++ /dev/null
@@ -1,15 +0,0 @@
-onmessage = function(e) {
-  if (!e.source) {
-    dump("ERROR: message doesn't have a source.");
-  }
-
-  // The client should be a window client
-  if (e.source instanceof WindowClient) {
-    // this will dispatch a focus event on the client
-    e.source.focus().then(function(client) {
-      client.postMessage(client.focused);
-    });
-  } else {
-    dump("ERROR: client should be a WindowClient");
-  }
-};
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -91,16 +91,21 @@ fetchXHR('nonresponse.txt', null, functi
   finish();
 });
 
 fetchXHR('nonresponse2.txt', null, function(xhr) {
   my_ok(xhr.status == 0, "load should not complete");
   finish();
 });
 
+fetchXHR('nonpromise.txt', null, function(xhr) {
+  my_ok(xhr.status == 0, "load should not complete");
+  finish();
+});
+
 fetchXHR('headers.txt', function(xhr) {
   my_ok(xhr.status == 200, "load should be successful");
   my_ok(xhr.responseText == "1", "request header checks should have passed");
   finish();
 }, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]);
 
 var expectedUncompressedResponse = "";
 for (var i = 0; i < 10; ++i) {
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -71,16 +71,27 @@ onfetch = function(ev) {
   else if (ev.request.url.includes("nonresponse.txt")) {
     ev.respondWith(Promise.resolve(5));
   }
 
   else if (ev.request.url.includes("nonresponse2.txt")) {
     ev.respondWith(Promise.resolve({}));
   }
 
+  else if (ev.request.url.includes("nonpromise.txt")) {
+    try {
+      // This should coerce to Promise(5) instead of throwing
+      ev.respondWith(5);
+    } catch (e) {
+      // test is expecting failure, so return a success if we get a thrown
+      // exception
+      ev.respondWith(new Response('respondWith(5) threw ' + e));
+    }
+  }
+
   else if (ev.request.url.includes("headers.txt")) {
     var ok = true;
     ok &= ev.request.headers.get("X-Test1") == "header1";
     ok &= ev.request.headers.get("X-Test2") == "header2";
     ev.respondWith(Promise.resolve(
       new Response(ok.toString(), {})
     ));
   }
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -12,17 +12,16 @@ support-files =
   simpleregister/index.html
   simpleregister/ready.html
   controller/index.html
   unregister/index.html
   unregister/unregister.html
   workerUpdate/update.html
   sw_clients/simple.html
   sw_clients/service_worker_controlled.html
-  sw_clients/focus_stealing_client.html
   match_all_worker.js
   match_all_advanced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
@@ -85,21 +84,22 @@ support-files =
   serviceworker_not_sharedworker.js
   match_all_client/match_all_client_id.html
   match_all_client_id_worker.js
   source_message_posting_worker.js
   scope/scope_worker.js
   redirect_serviceworker.sjs
   importscript.sjs
   importscript_worker.js
-  client_focus_worker.js
   bug1151916_worker.js
   bug1151916_driver.html
   notificationclick.html
   notificationclick.js
+  notificationclick_focus.html
+  notificationclick_focus.js
   worker_updatefoundevent.js
   worker_updatefoundevent2.js
   updatefoundevent.html
   empty.js
   periodic_update_test.js
   periodic.sjs
   periodic/frame.html
   periodic/register.html
@@ -158,17 +158,16 @@ support-files =
   sw_clients/dummy.html
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
-[test_client_focus.html]
 [test_close.html]
 [test_controller.html]
 [test_cross_origin_url_after_redirect.html]
 [test_empty_serviceworker.html]
 [test_eval_allowed.html]
 [test_eval_not_allowed.html]
 [test_fetch_event.html]
 [test_force_refresh.html]
@@ -222,16 +221,18 @@ skip-if = release_build
 [test_request_context_track.html]
 [test_request_context_video.html]
 [test_request_context_worker.html]
 [test_request_context_xhr.html]
 [test_request_context_xslt.html]
 [test_scopes.html]
 [test_sandbox_intercept.html]
 [test_notificationclick.html]
+[test_notificationclick_focus.html]
+skip-if = toolkit == "android" || toolkit == "gonk"
 [test_notification_constructor_error.html]
 [test_notification_get.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
 [test_service_worker_allowed.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_skip_waiting.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick_focus.html
@@ -0,0 +1,28 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1144660 - controlled page</title>
+<script class="testbody" type="text/javascript">
+  var testWindow = parent;
+  if (opener) {
+    testWindow = opener;
+  }
+
+  navigator.serviceWorker.ready.then(function(swr) {
+    swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.");
+  });
+
+  navigator.serviceWorker.onmessage = function(msg) {
+    dump("GOT Message " + JSON.stringify(msg.data) + "\n");
+    testWindow.callback(msg.data.ok);
+  };
+</script>
+
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick_focus.js
@@ -0,0 +1,40 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+
+function promisifyTimerFocus(client, delay) {
+  return new Promise(function(resolve, reject) {
+    setTimeout(function() {
+      client.focus().then(resolve, reject);
+    }, delay);
+  });
+}
+
+onnotificationclick = function(e) {
+  e.waitUntil(self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return Promise.resolve();
+    }
+
+    var immediatePromise = clients[0].focus();
+    var withinTimeout = promisifyTimerFocus(clients[0], 100);
+
+    var afterTimeout = promisifyTimerFocus(clients[0], 2000).then(function() {
+      throw "Should have failed!";
+    }, function() {
+      return Promise.resolve();
+    });
+
+    return Promise.all([immediatePromise, withinTimeout, afterTimeout]).then(function() {
+      clients.forEach(function(client) {
+        client.postMessage({ok: true});
+      });
+    }).catch(function(e) {
+      dump("Error " + e + "\n");
+      clients.forEach(function(client) {
+        client.postMessage({ok: false});
+      });
+    });
+  }));
+}
--- a/dom/workers/test/serviceworkers/periodic_update_test.js
+++ b/dom/workers/test/serviceworkers/periodic_update_test.js
@@ -63,12 +63,13 @@ function unregisterSW() {
 function runTheTest() {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
   ]}, function() {
     start();
   });
 }
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1130686 - Test service worker client.focus: client </title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <!--
-    We load this as an iframe to blur the main test window.
-  -->
-</head>
-<body>/
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-  if (!parent) {
-    info("error: sw_clients/focus_stealing_client.html shouldn't be launched directly!");
-  }
-
-  window.onload = function() {
-    navigator.serviceWorker.ready.then(function() {
-      parent.postMessage("READY", "*");
-    });
-  }
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/workers/test/serviceworkers/test_app_installation.html
+++ b/dom/workers/test/serviceworkers/test_app_installation.html
@@ -42,17 +42,18 @@ addLoadEvent(go);
 function setup() {
   info('Setting up');
   return new Promise((resolve, reject) => {
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.pushPrefEnv({'set': [
       ['dom.mozBrowserFramesEnabled', true],
       ['dom.serviceWorkers.exemptFromPerDomainMax', true],
       ['dom.serviceWorkers.enabled', true],
-      ['dom.serviceWorkers.testing.enabled', true]
+      ['dom.serviceWorkers.testing.enabled', true],
+      ['dom.serviceWorkers.interception.enabled', true],
     ]}, () => {
       SpecialPowers.pushPermissions([
         { 'type': 'webapps-manage', 'allow': 1, 'context': document },
         { 'type': 'browser', 'allow': 1, 'context': document },
         { 'type': 'embed-apps', 'allow': 1, 'context': document }
       ], () => {
         SpecialPowers.autoConfirmAppInstall(() => {
           SpecialPowers.autoConfirmAppUninstall(resolve);
--- a/dom/workers/test/serviceworkers/test_client_focus.html
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -4,18 +4,18 @@
 -->
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Bug 1130686 - Test service worker client.focus </title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 <!--
-  This test checks that client.focus is able to restore focus to the main window
-  when an iframe is holding the focus.
+  This test checks that client.focus is available.
+  Actual focusing is tested by test_notificationclick_focus.html since only notification events have permission to change focus.
 -->
 </head>
 <body>
 <p id="display"></p>
 <div id="content"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
   var registration;
--- a/dom/workers/test/serviceworkers/test_eval_allowed.html
+++ b/dom/workers/test/serviceworkers/test_eval_allowed.html
@@ -29,14 +29,15 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
+    ["dom.serviceWorkers.testing.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_eval_not_allowed.html
+++ b/dom/workers/test/serviceworkers/test_eval_not_allowed.html
@@ -32,14 +32,15 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
+    ["dom.serviceWorkers.testing.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html
+++ b/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html
@@ -60,12 +60,14 @@
         }).then(SimpleTest.finish);
     }
 
     SimpleTest.waitForExplicitFinish();
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
     ]}, runTest);
   </script>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_gzip_redirect.html
+++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html
@@ -71,14 +71,15 @@
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
+    ["dom.serviceWorkers.testing.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_notificationclick.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -44,13 +44,14 @@ https://bugzilla.mozilla.org/show_bug.cg
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.webnotifications.workers.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
     ["notification.prompt.testing", true],
   ]}, runTest);
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notificationclick_focus.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+  <title>Bug 1144660 - Test client.focus() permissions on notification click</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+  SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+  function testFrame(src) {
+    var iframe = document.createElement("iframe");
+    iframe.src = src;
+    window.callback = function(result) {
+      window.callback = null;
+      document.body.removeChild(iframe);
+      iframe = null;
+      ok(result, "All tests passed.");
+      MockServices.unregister();
+      SimpleTest.finish();
+    };
+    document.body.appendChild(iframe);
+  }
+
+  function runTest() {
+    MockServices.register();
+    testFrame('notificationclick_focus.html');
+    navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) {
+    }, function(e) {
+      ok(false, "registration should have passed!");
+    });
+  };
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.webnotifications.workers.enabled", true],
+    ["notification.prompt.testing", true],
+    ["dom.disable_open_click_delay", 1000],
+  ]}, runTest);
+</script>
+</body>
+</html>
--- a/dom/workers/test/serviceworkers/test_opaque_intercept.html
+++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html
@@ -72,15 +72,16 @@
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
+    ['dom.serviceWorkers.interception.enabled', true],
     ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
@@ -43,15 +43,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
+      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -169,23 +169,23 @@ var interfaceNamesInGlobalScope =
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushEvent", b2g: false, android: false, release: false },
+    { name: "PushEvent", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushManager", b2g: false, android: false, release: false },
+    { name: "PushManager", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushMessageData", b2g: false, android: false, release: false },
+    { name: "PushMessageData", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushSubscription", b2g: false, android: false, release: false },
+    { name: "PushSubscription", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerGlobalScope",
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -138,16 +138,17 @@ const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
 const COOKIE_BEHAVIOR_REJECT        = 2;
 const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
 
 let steps = [() => {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.serviceWorkers.interception.enabled", true],
     ["browser.dom.window.dump.enabled", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
   ]}, next);
 }, () => {
   testShouldIntercept(next);
 }, () => {
   SpecialPowers.pushPrefEnv({"set": [
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -161,25 +161,25 @@ var interfaceNamesInGlobalScope =
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushManager", b2g: false, android: false, release: false },
+    { name: "PushManager", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushSubscription", b2g: false, android: false, release: false },
+    { name: "PushSubscription", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "ServiceWorkerRegistration", release: false, b2g: false },
+    { name: "ServiceWorkerRegistration", b2g: false, android: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequestEventTarget",
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -343,17 +343,17 @@ XMLDocument::Load(const nsAString& aUrl,
   if (!nsContentUtils::IsSystemPrincipal(principal)) {
     rv = principal->CheckMayLoad(uri, false, false);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return false;
     }
 
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
+    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                                    uri,
                                    principal,
                                    callingDoc ? callingDoc.get() :
                                      static_cast<nsIDocument*>(this),
                                    NS_LITERAL_CSTRING("application/xml"),
                                    nullptr,
                                    &shouldLoad,
                                    nsContentUtils::GetContentPolicy(),
@@ -440,17 +440,17 @@ XMLDocument::Load(const nsAString& aUrl,
   nsCOMPtr<nsIChannel> channel;
   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active,
   // which in turn keeps STOP button from becoming active  
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      callingDoc ? callingDoc.get() :
                                   static_cast<nsIDocument*>(this),
                      nsILoadInfo::SEC_NORMAL,
-                     nsIContentPolicy::TYPE_XMLHTTPREQUEST,
+                     nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
                      loadGroup,
                      req,
                      nsIRequest::LOAD_BACKGROUND);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
--- a/extensions/permissions/nsContentBlocker.cpp
+++ b/extensions/permissions/nsContentBlocker.cpp
@@ -50,17 +50,19 @@ static const char *kTypeString[] = {
                                     "", // TYPE_INTERNAL_WORKER
                                     "", // TYPE_INTERNAL_SHARED_WORKER
                                     "", // TYPE_INTERNAL_EMBED
                                     "", // TYPE_INTERNAL_OBJECT
                                     "", // TYPE_INTERNAL_FRAME
                                     "", // TYPE_INTERNAL_IFRAME
                                     "", // TYPE_INTERNAL_AUDIO
                                     "", // TYPE_INTERNAL_VIDEO
-                                    ""  // TYPE_INTERNAL_TRACK
+                                    "", // TYPE_INTERNAL_TRACK
+                                    "", // TYPE_INTERNAL_XMLHTTPREQUEST
+                                    ""  // TYPE_INTERNAL_EVENTSOURCE
 };
 
 #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
 uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
 
 NS_IMPL_ISUPPORTS(nsContentBlocker, 
                   nsIContentPolicy,
                   nsIObserver,
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-54837
+54838
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -49506,16 +49506,17 @@ substation/MS
 substituent/MS
 substitute/XMGNDS
 substitution/M
 substrata
 substrate/MS
 substratum/M
 substructure/SM
 subsume/DSG
+subsumption/S
 subsurface/M
 subsystem/SM
 subteen/SM
 subtenancy/M
 subtenant/SM
 subtend/SDG
 subterfuge/SM
 subterranean
--- a/gfx/cairo/libpixman/src/moz.build
+++ b/gfx/cairo/libpixman/src/moz.build
@@ -74,28 +74,22 @@ DEFINES['PACKAGE'] = 'mozpixman'
 DEFINES['_USE_MATH_DEFINES'] = True
 
 use_mmx = False
 use_sse2 = False
 use_vmx = False
 use_arm_simd_gcc = False
 use_arm_neon_gcc = False
 if '86' in CONFIG['OS_TEST']:
-    if '64' in CONFIG['OS_TEST']:
-        if CONFIG['GNU_CC']:
-            use_sse2 = True
-    else:
+    use_sse2 = True
+    if '64' not in CONFIG['OS_TEST']:
         if CONFIG['_MSC_VER']:
             use_mmx = True
-        if CONFIG['HAVE_GCC_ALIGN_ARG_POINTER']:
-            use_sse2 = True
     if CONFIG['GNU_CC']:
         use_mmx = True
-    if CONFIG['_MSC_VER']:
-        use_sse2 = True
 elif 'ppc' in CONFIG['OS_TEST']:
     if CONFIG['GNU_CC']:
         use_vmx = True
 # Apple's arm assembler doesn't support the same syntax as
 # the standard GNU assembler, so use the C fallback paths for now.
 # This may be fixable if clang's ARM/iOS assembler improves into a
 # viable solution in the future.
 elif 'arm' in CONFIG['OS_TEST']:
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -54,16 +54,17 @@ GLScreenBuffer::Create(GLContext* gl,
 GLScreenBuffer::GLScreenBuffer(GLContext* gl,
                                const SurfaceCaps& caps,
                                UniquePtr<SurfaceFactory> factory)
     : mGL(gl)
     , mCaps(caps)
     , mFactory(Move(factory))
     , mNeedsBlit(true)
     , mUserReadBufferMode(LOCAL_GL_BACK)
+    , mUserDrawBufferMode(LOCAL_GL_BACK)
     , mUserDrawFB(0)
     , mUserReadFB(0)
     , mInternalDrawFB(0)
     , mInternalReadFB(0)
 #ifdef DEBUG
     , mInInternalMode_DrawFB(true)
     , mInInternalMode_ReadFB(true)
 #endif
@@ -440,16 +441,22 @@ GLScreenBuffer::Attach(SharedSurface* su
     MOZ_ASSERT(SharedSurf() == surf);
 
     // Update the ReadBuffer mode.
     if (mGL->IsSupported(gl::GLFeature::read_buffer)) {
         BindFB(0);
         mRead->SetReadBuffer(mUserReadBufferMode);
     }
 
+    // Update the DrawBuffer mode.
+    if (mGL->IsSupported(gl::GLFeature::draw_buffers)) {
+        BindFB(0);
+        SetDrawBuffer(mUserDrawBufferMode);
+    }
+
     RequireBlit();
 
     return true;
 }
 
 bool
 GLScreenBuffer::Swap(const gfx::IntSize& size)
 {
@@ -544,16 +551,48 @@ GLScreenBuffer::CreateRead(SharedSurface
     GLContext* gl = mFactory->mGL;
     const GLFormats& formats = mFactory->mFormats;
     const SurfaceCaps& caps = mFactory->ReadCaps();
 
     return ReadBuffer::Create(gl, caps, formats, surf);
 }
 
 void
+GLScreenBuffer::SetDrawBuffer(GLenum mode)
+{
+    MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::draw_buffers));
+    MOZ_ASSERT(GetDrawFB() == 0);
+
+    if (!mGL->IsSupported(GLFeature::draw_buffers))
+        return;
+
+    mUserDrawBufferMode = mode;
+
+    GLuint fb = mDraw ? mDraw->mFB : mRead->mFB;
+    GLenum internalMode;
+
+    switch (mode) {
+    case LOCAL_GL_BACK:
+        internalMode = (fb == 0) ? LOCAL_GL_BACK
+                                 : LOCAL_GL_COLOR_ATTACHMENT0;
+        break;
+
+    case LOCAL_GL_NONE:
+        internalMode = LOCAL_GL_NONE;
+        break;
+
+    default:
+        MOZ_CRASH("Bad value.");
+    }
+
+    mGL->MakeCurrent();
+    mGL->fDrawBuffers(1, &internalMode);
+}
+
+void
 GLScreenBuffer::SetReadBuffer(GLenum mode)
 {
     MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::read_buffer));
     MOZ_ASSERT(GetReadFB() == 0);
 
     mUserReadBufferMode = mode;
     mRead->SetReadBuffer(mUserReadBufferMode);
 }
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -144,16 +144,17 @@ protected:
     RefPtr<layers::SharedSurfaceTextureClient> mFront;
 
     UniquePtr<DrawBuffer> mDraw;
     UniquePtr<ReadBuffer> mRead;
 
     bool mNeedsBlit;
 
     GLenum mUserReadBufferMode;
+    GLenum mUserDrawBufferMode;
 
     // Below are the parts that help us pretend to be framebuffer 0:
     GLuint mUserDrawFB;
     GLuint mUserReadFB;
     GLuint mInternalDrawFB;
     GLuint mInternalReadFB;
 
 #ifdef DEBUG
@@ -217,16 +218,17 @@ public:
     void AssureBlitted();
     void AfterDrawCall();
     void BeforeReadCall();
 
     bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
                         GLint y, GLsizei width, GLsizei height, GLint border);
 
     void SetReadBuffer(GLenum userMode);
+    void SetDrawBuffer(GLenum userMode);
 
     /**
      * Attempts to read pixels from the current bound framebuffer, if
      * it is backed by a SharedSurface.
      *
      * Returns true if the pixel data has been read back, false
      * otherwise.
      */
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -618,27 +618,27 @@ CairoImage::GetTextureClient(Compositabl
 // TextureHost is not used by CompositableHost.
 #ifdef MOZ_WIDGET_GONK
   RefPtr<TextureClientRecycleAllocator> recycler =
     aClient->GetTextureClientRecycler();
   if (recycler) {
     textureClient =
       recycler->CreateOrRecycleForDrawing(surface->GetFormat(),
                                           surface->GetSize(),
-                                          gfx::BackendType::NONE,
+                                          BackendSelector::Content,
                                           aClient->GetTextureFlags());
   }
 #endif
 
 #endif
   if (!textureClient) {
     // gfx::BackendType::NONE means default to content backend
     textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
                                                            surface->GetSize(),
-                                                           gfx::BackendType::NONE,
+                                                           BackendSelector::Content,
                                                            TextureFlags::DEFAULT);
   }
   if (!textureClient) {
     return nullptr;
   }
 
   if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
     return nullptr;
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -121,24 +121,23 @@ CanvasClient2D::CreateTextureClientForCa
     // We want a cairo backend here as we don't want to be copying into
     // an accelerated backend and we like LockBits to work. This is currently
     // the most effective way to make this work.
     return TextureClient::CreateForRawBufferAccess(GetForwarder(),
                                                    aFormat, aSize, BackendType::CAIRO,
                                                    mTextureFlags | aFlags);
   }
 
-  gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
 #ifdef XP_WIN
-  return CreateTextureClientForDrawing(aFormat, aSize, backend, aFlags);
+  return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags);
 #else
   // XXX - We should use CreateTextureClientForDrawing, but we first need
   // to use double buffering.
   return TextureClient::CreateForRawBufferAccess(GetForwarder(),
-                                                 aFormat, aSize, backend,
+                                                 aFormat, aSize, gfx::BackendType::NONE,
                                                  mTextureFlags | aFlags);
 #endif
 }
 
 ////////////////////////////////////////////////////////////////////////
 
 CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
                                                      TextureFlags aFlags)
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -202,22 +202,22 @@ CompositableClient::CreateBufferTextureC
   return TextureClient::CreateForRawBufferAccess(GetForwarder(),
                                                  aFormat, aSize, aMoz2DBackend,
                                                  aTextureFlags | mTextureFlags);
 }
 
 already_AddRefed<TextureClient>
 CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
                                                   gfx::IntSize aSize,
-                                                  gfx::BackendType aMoz2DBackend,
+                                                  BackendSelector aSelector,
                                                   TextureFlags aTextureFlags,
                                                   TextureAllocationFlags aAllocFlags)
 {
   return TextureClient::CreateForDrawing(GetForwarder(),
-                                         aFormat, aSize, aMoz2DBackend,
+                                         aFormat, aSize, aSelector,
                                          aTextureFlags | mTextureFlags,
                                          aAllocFlags);
 }
 
 bool
 CompositableClient::AddTextureClient(TextureClient* aClient)
 {
   if(!aClient || !aClient->IsAllocated()) {
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -135,17 +135,17 @@ public:
   CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
                             gfx::IntSize aSize,
                             gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE,
                             TextureFlags aFlags = TextureFlags::DEFAULT);
 
   already_AddRefed<TextureClient>
   CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
                                 gfx::IntSize aSize,
-                                gfx::BackendType aMoz2DBackend,
+                                BackendSelector aSelector,
                                 TextureFlags aTextureFlags,
                                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
 
   /**
    * Establishes the connection with compositor side through IPDL
    */
   virtual bool Connect(ImageContainer* aImageContainer = nullptr);
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -288,17 +288,17 @@ ContentClientRemoteBuffer::BuildTextureC
   CreateBackBuffer(mBufferRect);
 }
 
 void
 ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
 {
   // gfx::BackendType::NONE means fallback to the content backend
   mTextureClient = CreateTextureClientForDrawing(
-    mSurfaceFormat, mSize, gfx::BackendType::NONE,
+    mSurfaceFormat, mSize, BackendSelector::Content,
     mTextureFlags | ExtraTextureFlags(),
     TextureAllocationFlags::ALLOC_CLEAR_BUFFER
   );
   if (!mTextureClient || !AddTextureClient(mTextureClient)) {
     AbortTextureClientCreation();
     return;
   }
 
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -211,17 +211,17 @@ ImageClientSingle::UpdateImage(ImageCont
 #endif
         } else {
           MOZ_ASSERT(false, "Bad ImageFormat.");
         }
       } else {
         RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
         MOZ_ASSERT(surface);
         texture = CreateTextureClientForDrawing(surface->GetFormat(), image->GetSize(),
-                                                gfx::BackendType::NONE, mTextureFlags);
+                                                BackendSelector::Content, mTextureFlags);
         if (!texture) {
           return false;
         }
 
         MOZ_ASSERT(texture->CanExposeDrawTarget());
 
         if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
           return false;
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -91,17 +91,17 @@ ClientSingleTiledLayerBuffer::GetSurface
                                 mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
 
 already_AddRefed<TextureClient>
 ClientSingleTiledLayerBuffer::GetTextureClient()
 {
   return mCompositableClient->CreateTextureClientForDrawing(
-    gfx::ImageFormatToSurfaceFormat(mFormat), mSize, gfx::BackendType::NONE,
+    gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content,
     TextureFlags::IMMEDIATE_UPLOAD);
 }
 
 void
 ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                           const nsIntRegion& aPaintRegion,
                                           const nsIntRegion& aDirtyRegion,
                                           LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -315,75 +315,87 @@ CreateBufferTextureClient(ISurfaceAlloca
     return result.forget();
   }
   RefPtr<BufferTextureClient> result = new ShmemTextureClient(aAllocator, aFormat,
                                                               aMoz2DBackend,
                                                               aTextureFlags);
   return result.forget();
 }
 
+static inline gfx::BackendType
+BackendTypeForBackendSelector(BackendSelector aSelector)
+{
+  switch (aSelector) {
+    case BackendSelector::Canvas:
+      return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+    case BackendSelector::Content:
+      return gfxPlatform::GetPlatform()->GetContentBackend();
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
+      return gfx::BackendType::NONE;
+  }
+};
+
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
                                 gfx::SurfaceFormat aFormat,
                                 gfx::IntSize aSize,
-                                gfx::BackendType aMoz2DBackend,
+                                BackendSelector aSelector,
                                 TextureFlags aTextureFlags,
                                 TextureAllocationFlags aAllocFlags)
 {
-  if (aMoz2DBackend == gfx::BackendType::NONE) {
-    aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend();
-  }
+  gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aSelector);
 
   RefPtr<TextureClient> texture;
 
 #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN)
   int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
 #endif
 
 #ifdef XP_WIN
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   if (parentBackend == LayersBackend::LAYERS_D3D11 &&
-      ((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) ||
-       (aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) &&
+      (moz2DBackend == gfx::BackendType::DIRECT2D ||
+       moz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize)
   {
     texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
-      aMoz2DBackend == gfx::BackendType::CAIRO &&
+      moz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize &&
       NS_IsMainThread()) {
     if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
       texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags);
     }
   }
 
   if (!texture && aFormat == SurfaceFormat::B8G8R8X8 &&
       aAllocator->IsSameProcess() &&
-      aMoz2DBackend == gfx::BackendType::CAIRO &&
+      moz2DBackend == gfx::BackendType::CAIRO &&
       NS_IsMainThread()) {
     if (aAllocator->IsSameProcess()) {
       texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags);
     } else {
       texture = new TextureClientShmemDIB(aAllocator, aFormat, aTextureFlags);
     }
   }
 #endif
 
 #ifdef MOZ_X11
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   gfxSurfaceType type =
     gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
 
   if (parentBackend == LayersBackend::LAYERS_BASIC &&
-      aMoz2DBackend == gfx::BackendType::CAIRO &&
+      moz2DBackend == gfx::BackendType::CAIRO &&
       type == gfxSurfaceType::Xlib)
   {
     texture = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
   }
 #ifdef GL_PROVIDER_GLX
   if (parentBackend == LayersBackend::LAYERS_OPENGL &&
       type == gfxSurfaceType::Xlib &&
       aFormat != SurfaceFormat::A8 &&
@@ -394,17 +406,17 @@ TextureClient::CreateForDrawing(ISurface
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_GONK
   if (!DisableGralloc(aFormat, aSize)) {
     // Don't allow Gralloc texture clients to exceed the maximum texture size.
     // BufferTextureClients have code to handle tiling the surface client-side.
     if (aSize.width <= maxTextureSize && aSize.height <= maxTextureSize) {
-      texture = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend,
+      texture = new GrallocTextureClientOGL(aAllocator, aFormat, moz2DBackend,
                                            aTextureFlags);
     }
   }
 #endif
 
   MOZ_ASSERT(!texture || texture->CanExposeDrawTarget(), "texture cannot expose a DrawTarget?");
 
   if (texture && texture->AllocateForSurface(aSize, aAllocFlags)) {
@@ -415,17 +427,17 @@ TextureClient::CreateForDrawing(ISurface
     return nullptr;
   }
 
   if (texture) {
     NS_WARNING("Failed to allocate a TextureClient, falling back to BufferTextureClient.");
   }
 
   // Can't do any better than a buffer texture client.
-  texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, aMoz2DBackend);
+  texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, moz2DBackend);
 
   if (!texture->AllocateForSurface(aSize, aAllocFlags)) {
     return nullptr;
   }
 
   return texture.forget();
 }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -133,16 +133,22 @@ public:
    * DataSourceSurface beyond the execution of this function.
    */
   virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0;
 
 protected:
   virtual ~TextureReadbackSink() {}
 };
 
+enum class BackendSelector
+{
+  Content,
+  Canvas
+};
+
 /**
  * TextureClient is a thin abstraction over texture data that need to be shared
  * between the content process and the compositor process. It is the
  * content-side half of a TextureClient/TextureHost pair. A corresponding
  * TextureHost lives on the compositor-side.
  *
  * TextureClient's primary purpose is to present texture data in a way that is
  * understood by the IPC system. There are two ways to use it:
@@ -169,17 +175,17 @@ public:
                          TextureFlags aFlags = TextureFlags::DEFAULT);
   virtual ~TextureClient();
 
   // Creates and allocates a TextureClient usable with Moz2D.
   static already_AddRefed<TextureClient>
   CreateForDrawing(ISurfaceAllocator* aAllocator,
                    gfx::SurfaceFormat aFormat,
                    gfx::IntSize aSize,
-                   gfx::BackendType aMoz2dBackend,
+                   BackendSelector aSelector,
                    TextureFlags aTextureFlags,
                    TextureAllocationFlags flags = ALLOC_DEFAULT);
 
   // Creates and allocates a BufferTextureClient supporting the YCbCr format.
   static already_AddRefed<BufferTextureClient>
   CreateForYCbCr(ISurfaceAllocator* aAllocator,
                  gfx::IntSize aYSize,
                  gfx::IntSize aCbCrSize,
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -106,17 +106,17 @@ TextureClientPool::GetTextureClient()
   // No unused clients in the pool, create one
   if (gfxPrefs::ForceShmemTiles()) {
     // gfx::BackendType::NONE means use the content backend
     textureClient = TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
       mFormat, mSize, gfx::BackendType::NONE,
       TextureFlags::IMMEDIATE_UPLOAD, ALLOC_DEFAULT);
   } else {
     textureClient = TextureClient::CreateForDrawing(mSurfaceAllocator,
-      mFormat, mSize, gfx::BackendType::NONE, TextureFlags::IMMEDIATE_UPLOAD);
+      mFormat, mSize, BackendSelector::Content, TextureFlags::IMMEDIATE_UPLOAD);
   }
 
   mOutstandingClients++;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   if (textureClient) {
     textureClient->mPoolTracker = this;
   }
 #endif
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -29,17 +29,17 @@ public:
       mMaxPooledSize = aMax;
     }
   }
 
   // Creates and allocates a TextureClient.
   already_AddRefed<TextureClient>
   CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat,
                             gfx::IntSize aSize,
-                            gfx::BackendType aMoz2dBackend,
+                            BackendSelector aSelector,
                             TextureFlags aTextureFlags,
                             TextureAllocationFlags flags);
 
   void Destroy();
 
   void RecycleCallbackImp(TextureClient* aClient);
 
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
@@ -136,33 +136,29 @@ TextureClientRecycleAllocatorImp::~Textu
   MOZ_ASSERT(mPooledClients.empty());
   MOZ_ASSERT(mInUseClients.empty());
 }
 
 already_AddRefed<TextureClient>
 TextureClientRecycleAllocatorImp::CreateOrRecycleForDrawing(
                                              gfx::SurfaceFormat aFormat,
                                              gfx::IntSize aSize,
-                                             gfx::BackendType aMoz2DBackend,
+                                             BackendSelector aSelector,
                                              TextureFlags aTextureFlags,
                                              TextureAllocationFlags aAllocFlags)
 {
   // TextureAllocationFlags is actually used only by ContentClient.
   // This class does not handle ConteClient's TextureClient allocation.
   MOZ_ASSERT(aAllocFlags == TextureAllocationFlags::ALLOC_DEFAULT ||
              aAllocFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
   MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
   aTextureFlags = aTextureFlags | TextureFlags::RECYCLE; // Set recycle flag
 
   RefPtr<TextureClientHolder> textureHolder;
 
-  if (aMoz2DBackend == gfx::BackendType::NONE) {
-    aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend();
-  }
-
   {
     MutexAutoLock lock(mLock);
     if (mDestroyed) {
       return nullptr;
     } else if (!mPooledClients.empty()) {
       textureHolder = mPooledClients.top();
       mPooledClients.pop();
       // If a pooled TextureClient is not compatible, release it.
@@ -178,17 +174,17 @@ TextureClientRecycleAllocatorImp::Create
         textureHolder->GetTextureClient()->RecycleTexture(aTextureFlags);
       }
     }
   }
 
   if (!textureHolder) {
     // Allocate new TextureClient
     RefPtr<TextureClient> texture;
-    texture = TextureClient::CreateForDrawing(this, aFormat, aSize, aMoz2DBackend,
+    texture = TextureClient::CreateForDrawing(this, aFormat, aSize, aSelector,
                                               aTextureFlags, aAllocFlags);
     if (!texture) {
       return nullptr;
     }
     textureHolder = new TextureClientHolder(texture);
   }
 
   {
@@ -256,21 +252,21 @@ TextureClientRecycleAllocator::SetMaxPoo
 {
   mAllocator->SetMaxPoolSize(aMax);
 }
 
 already_AddRefed<TextureClient>
 TextureClientRecycleAllocator::CreateOrRecycleForDrawing(
                                             gfx::SurfaceFormat aFormat,
                                             gfx::IntSize aSize,
-                                            gfx::BackendType aMoz2DBackend,
+                                            BackendSelector aSelector,
                                             TextureFlags aTextureFlags,
                                             TextureAllocationFlags aAllocFlags)
 {
   return mAllocator->CreateOrRecycleForDrawing(aFormat,
                                                aSize,
-                                               aMoz2DBackend,
+                                               aSelector,
                                                aTextureFlags,
                                                aAllocFlags);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientRecycleAllocator.h
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -33,17 +33,17 @@ public:
   explicit TextureClientRecycleAllocator(ISurfaceAllocator* aAllocator);
 
   void SetMaxPoolSize(uint32_t aMax);
 
   // Creates and allocates a TextureClient.
   already_AddRefed<TextureClient>
   CreateOrRecycleForDrawing(gfx::SurfaceFormat aFormat,
                             gfx::IntSize aSize,
-                            gfx::BackendType aMoz2dBackend,
+                            BackendSelector aSelector,
                             TextureFlags aTextureFlags,
                             TextureAllocationFlags flags = ALLOC_DEFAULT);
 
 private:
   RefPtr<TextureClientRecycleAllocatorImp> mAllocator;
 };
 
 } // namespace layers
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -135,17 +135,19 @@ elif CONFIG['CLANG_CL']:
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-overloaded-virtual',
         '-Wno-unused-function',
     ]
-    if not CONFIG['CLANG_CXX']:
+    if CONFIG['CLANG_CXX']:
+        CXXFLAGS += ['-Wno-inconsistent-missing-override']
+    else:
         CXXFLAGS += ['-Wno-logical-op']
     if CONFIG['CPU_ARCH'] == 'arm':
         SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'):
     CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
     CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
 
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -663,17 +663,19 @@ elif CONFIG['CLANG_CL']:
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 DEFINES['GR_IMPLEMENTATION'] = 1
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-overloaded-virtual',
         '-Wno-unused-function',
     ]
-    if not CONFIG['CLANG_CXX']:
+    if CONFIG['CLANG_CXX']:
+        CXXFLAGS += ['-Wno-inconsistent-missing-override']
+    else:
         CXXFLAGS += ['-Wno-logical-op']
     if CONFIG['CPU_ARCH'] == 'arm':
         SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'android', 'gonk', 'qt'):
     CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
     CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
 
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -94,16 +94,17 @@ using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
 using JS::CompileOptions;
 using JS::IsAcceptableThis;
 using JS::NativeImpl;
 using JS::OwningCompileOptions;
 using JS::ReadOnlyCompileOptions;
 using JS::SourceBufferHolder;
+using JS::TransitiveCompileOptions;
 
 using JS::Rooted;
 using JS::RootedFunction;
 using JS::RootedId;
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using JS::RootedSymbol;
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -13,17 +13,16 @@ builtin(include, ../../build/autoconf/pk
 builtin(include, ../../build/autoconf/nspr.m4)dnl
 builtin(include, ../../build/autoconf/nspr-build.m4)dnl
 builtin(include, ../../build/autoconf/codeset.m4)dnl
 builtin(include, ../../build/autoconf/altoptions.m4)dnl
 builtin(include, ../../build/autoconf/mozprog.m4)dnl
 builtin(include, ../../build/autoconf/mozheader.m4)dnl
 builtin(include, ../../build/autoconf/mozcommonheader.m4)dnl
 builtin(include, ../../build/autoconf/lto.m4)dnl
-builtin(include, ../../build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, ../../build/autoconf/llvm-pr8927.m4)dnl
 builtin(include, ../../build/autoconf/frameptr.m4)dnl
 builtin(include, ../../build/autoconf/compiler-opts.m4)dnl
 builtin(include, ../../build/autoconf/expandlibs.m4)dnl
 builtin(include, ../../build/autoconf/arch.m4)dnl
 builtin(include, ../../build/autoconf/android.m4)dnl
 builtin(include, ../../build/autoconf/zlib.m4)dnl
 builtin(include, ../../build/autoconf/linux.m4)dnl
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2153,17 +2153,16 @@ WINNT|Darwin|Android)
   STL_FLAGS='-I$(DIST)/stl_wrappers'
   WRAP_STL_INCLUDES=1
   ;;
 esac
 
 AC_SUBST(WRAP_SYSTEM_INCLUDES)
 AC_SUBST(VISIBILITY_FLAGS)
 
-MOZ_GCC_PR49911
 MOZ_LLVM_PR8927
 
 dnl Checks for header files.
 dnl ========================================================
 AC_HEADER_DIRENT
 case "$target_os" in
 freebsd*)
 # for stuff like -lXshm
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5762,23 +5762,23 @@ BytecodeEmitter::emitFunction(ParseNode*
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             SharedContext* outersc = sc;
 
             if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
                 funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
-            // Inherit most things (principals, version, etc) from the parent.
+            // Inherit most things (principals, version, etc) from the
+            // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
-            CompileOptions options(cx, parser->options());
-            options.setMutedErrors(parent->mutedErrors())
-                   .setNoScriptRval(false)
-                   .setForEval(false)
-                   .setVersion(parent->getVersion());
+            MOZ_ASSERT(parent->getVersion() == parser->options().version);
+            MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
+            const TransitiveCompileOptions& transitiveOptions = parser->options();
+            CompileOptions options(cx, transitiveOptions);
 
             Rooted<JSObject*> enclosingScope(cx, enclosingStaticScope());
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
                                                           parent->staticLevel() + 1,
                                                           sourceObject,
                                                           funbox->bufStart, funbox->bufEnd));
             if (!script)
--- a/js/src/jit-test/lib/class.js
+++ b/js/src/jit-test/lib/class.js
@@ -1,1 +1,1 @@
-load(libdir + "../../tests/ecma_6/Class/shell.js");
+load(libdir + "../../tests/ecma_6/shell.js");
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -36,62 +36,16 @@ using JS::GenericNaN;
 using JS::ToInt32;
 
 // shared
 CodeGeneratorARM::CodeGeneratorARM(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
-bool
-CodeGeneratorARM::generatePrologue()
-{
-    MOZ_ASSERT(masm.framePushed() == 0);
-    MOZ_ASSERT(!gen->compilingAsmJS());
-#ifdef JS_USE_LINK_REGISTER
-    masm.pushReturnAddress();
-#endif
-
-    // If profiling, save the current frame pointer to a per-thread global field.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerEnterFrame(StackPointer, CallTempReg0);
-
-    // Ensure that the Ion frames is properly aligned.
-    masm.assertStackAlignment(JitStackAlignment, 0);
-
-    // Note that this automatically sets MacroAssembler::framePushed().
-    masm.reserveStack(frameSize());
-    masm.checkStackAlignment();
-
-    emitTracelogIonStart();
-
-    return true;
-}
-
-bool
-CodeGeneratorARM::generateEpilogue()
-{
-    MOZ_ASSERT(!gen->compilingAsmJS());
-    masm.bind(&returnLabel_);
-
-    emitTracelogIonStop();
-
-    masm.freeStack(frameSize());
-    MOZ_ASSERT(masm.framePushed() == 0);
-
-    // If profiling, reset the per-thread global lastJitFrame to point to
-    // the previous frame.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerExitFrame();
-
-    masm.pop(pc);
-    masm.flushBuffer();
-    return true;
-}
-
 void
 CodeGeneratorARM::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse)
 {
     if (isNextBlock(mirFalse->lir())) {
         jumpToBlock(mirTrue, cond);
     } else {
         jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
         jumpToBlock(mirTrue);
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -18,18 +18,16 @@ class OutOfLineTableSwitch;
 
 class CodeGeneratorARM : public CodeGeneratorShared
 {
     friend class MoveResolverARM;
 
     CodeGeneratorARM* thisFromCtor() {return this;}
 
   protected:
-    // Label for the common return path.
-    NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     MoveOperand toMoveOperand(LAllocation a) const;
 
     void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot);
     void bailoutFrom(Label* label, LSnapshot* snapshot);
     void bailout(LSnapshot* snapshot);
 
@@ -57,18 +55,16 @@ class CodeGeneratorARM : public CodeGene
         bailoutIf(Assembler::Zero, snapshot);
     }
 
     template<class T>
     void generateUDivModZeroCheck(Register rhs, Register output, Label* done, LSnapshot* snapshot,
                                   T* mir);
 
   protected:
-    bool generatePrologue();
-    bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitRoundDouble(FloatRegister src, Register dest, Label* fail);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse);
 
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -5,37 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_arm_LIR_arm_h
 #define jit_arm_LIR_arm_h
 
 namespace js {
 namespace jit {
 
-class LBox : public LInstructionHelper<2, 1, 0>
-{
-    MIRType type_;
-
-  public:
-    LIR_HEADER(Box);
-
-    LBox(const LAllocation& in_payload, MIRType type)
-      : type_(type)
-    {
-        setOperand(0, in_payload);
-    }
-
-    MIRType type() const {
-        return type_;
-    }
-    const char* extraName() const {
-        return StringFromMIRType(type_);
-    }
-};
-
 class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1>
 {
     MIRType type_;
 
   public:
     LIR_HEADER(BoxFloatingPoint);
 
     LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type)
@@ -283,32 +262,16 @@ class LModMaskI : public LInstructionHel
         return shift_;
     }
 
     MMod* mir() const {
         return mir_->toMod();
     }
 };
 
-class LPowHalfD : public LInstructionHelper<1, 1, 0>
-{
-  public:
-    LIR_HEADER(PowHalfD);
-    LPowHalfD(const LAllocation& input) {
-        setOperand(0, input);
-    }
-
-    const LAllocation* input() {
-        return getOperand(0);
-    }
-    const LDefinition* output() {
-        return getDef(0);
-    }
-};
-
 // Takes a tableswitch with an integer to decide.
 class LTableSwitch : public LInstructionHelper<0, 1, 1>
 {
   public:
     LIR_HEADER(TableSwitch);
 
     LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, MTableSwitch* ins) {
         setOperand(0, in);
--- a/js/src/jit/arm64/AtomicOperations-arm64.h
+++ b/js/src/jit/arm64/AtomicOperations-arm64.h
@@ -10,95 +10,117 @@
 #define jit_arm64_AtomicOperations_arm64_h
 
 #include "jit/arm64/Architecture-arm64.h"
 #include "jit/AtomicOperations.h"
 
 inline bool
 js::jit::AtomicOperations::isLockfree8()
 {
-    MOZ_CRASH("isLockfree8()");
+    MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0));
+    MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0));
+    MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0));
+    return true;
 }
 
 inline void
 js::jit::AtomicOperations::fenceSeqCst()
 {
-    MOZ_CRASH("fenceSeqCst()");
+    __atomic_thread_fence(__ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::loadSeqCst(T* addr)
 {
-    MOZ_CRASH("loadSeqCst()");
+    MOZ_ASSERT(sizeof(T) < 8 || isLockfree8());
+    T v;
+    __atomic_load(addr, &v, __ATOMIC_SEQ_CST);
+    return v;
 }
 
 template<typename T>
 inline void
 js::jit::AtomicOperations::storeSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("storeSeqCst()");
+    MOZ_ASSERT(sizeof(T) < 8 || isLockfree8());
+    __atomic_store(addr, &val, __ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("exchangeSeqCst()");
+    MOZ_ASSERT(sizeof(T) < 8 || isLockfree8());
+    T v;
+    __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST);
+    return v;
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval)
 {
-    MOZ_CRASH("compareExchangeSeqCst()");
+    MOZ_ASSERT(sizeof(T) < 8 || isLockfree8());
+    __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+    return oldval;
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("fetchAddSeqCst()");
+    static_assert(sizeof(T) <= 4, "not available for 8-byte values yet");
+    return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("fetchSubSeqCst()");
+    static_assert(sizeof(T) <= 4, "not available for 8-byte values yet");
+    return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("fetchAndSeqCst()");
+    static_assert(sizeof(T) <= 4, "not available for 8-byte values yet");
+    return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("fetchOrSeqCst()");
+    static_assert(sizeof(T) <= 4, "not available for 8-byte values yet");
+    return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST);
 }
 
 template<typename T>
 inline T
 js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val)
 {
-    MOZ_CRASH("fetchXorSeqCst()");
+    static_assert(sizeof(T) <= 4, "not available for 8-byte values yet");
+    return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST);
 }
 
 template<size_t nbytes>
 inline void
 js::jit::RegionLock::acquire(void* addr)
 {
-    MOZ_CRASH("acquire()");
+    uint32_t zero = 0;
+    uint32_t one = 1;
+    while (!__atomic_compare_exchange(&spinlock, &zero, &one, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE))
+        continue;
 }
 
 template<size_t nbytes>
 inline void
 js::jit::RegionLock::release(void* addr)
 {
-    MOZ_CRASH("release()");
+    MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock");
+    uint32_t zero = 0;
+    __atomic_store(&spinlock, &zero, __ATOMIC_SEQ_CST);
 }
 
 #endif // jit_arm64_AtomicOperations_arm64_h
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -33,28 +33,16 @@ using JS::GenericNaN;
 
 // shared
 CodeGeneratorARM64::CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
 bool
-CodeGeneratorARM64::generatePrologue()
-{
-    MOZ_CRASH("generatePrologue");
-}
-
-bool
-CodeGeneratorARM64::generateEpilogue()
-{
-    MOZ_CRASH("generateEpilogue");
-}
-
-bool
 CodeGeneratorARM64::generateOutOfLineCode()
 {
     MOZ_CRASH("generateOutOfLineCode");
 }
 
 void
 CodeGeneratorARM64::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse)
 {
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -21,18 +21,16 @@ class CodeGeneratorARM64 : public CodeGe
     friend class MoveResolverARM64;
 
     CodeGeneratorARM64* thisFromCtor() { return this; }
 
   public:
     CodeGeneratorARM64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm);
 
   protected:
-    // Label for the common return path.
-    NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     // FIXME: VIXL Operand does not match the platform-agnostic Operand,
     // which is just a union of possible arguments.
     inline Operand ToOperand(const LAllocation& a) {
         MOZ_CRASH("ToOperand");
     }
     inline Operand ToOperand(const LAllocation* a) {
@@ -68,18 +66,16 @@ class CodeGeneratorARM64 : public CodeGe
         return bailoutIf(c, snapshot);
     }
     void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) {
         masm.test32(reg, Imm32(0xFF));
         return bailoutIf(Assembler::Zero, snapshot);
     }
 
   protected:
-    bool generatePrologue();
-    bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitRoundDouble(FloatRegister src, Register dest, Label* fail);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse);
 
--- a/js/src/jit/arm64/LIR-arm64.h
+++ b/js/src/jit/arm64/LIR-arm64.h
@@ -5,37 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_arm64_LIR_arm64_h
 #define jit_arm64_LIR_arm64_h
 
 namespace js {
 namespace jit {
 
-class LBox : public LInstructionHelper<1, 1, 0>
-{
-    MIRType type_;
-
-  public:
-    LIR_HEADER(Box);
-
-    LBox(const LAllocation& in_payload, MIRType type)
-      : type_(type)
-    {
-        setOperand(0, in_payload);
-    }
-
-    MIRType type() const {
-        return type_;
-    }
-    const char* extraName() const {
-        return StringFromMIRType(type_);
-    }
-};
-
 class LUnboxBase : public LInstructionHelper<1, 1, 0>
 {
   public:
     LUnboxBase(const LAllocation& input) {
         setOperand(0, input);
     }
 
     static const size_t Input = 0;
@@ -264,32 +243,16 @@ class LModMaskI : public LInstructionHel
         return shift_;
     }
 
     MMod* mir() const {
         return mir_->toMod();
     }
 };
 
-class LPowHalfD : public LInstructionHelper<1, 1, 0>
-{
-  public:
-    LIR_HEADER(PowHalfD);
-    LPowHalfD(const LAllocation& input) {
-        setOperand(0, input);
-    }
-
-    const LAllocation* input() {
-        return getOperand(0);
-    }
-    const LDefinition* output() {
-        return getDef(0);
-    }
-};
-
 // Takes a tableswitch with an integer to decide
 class LTableSwitch : public LInstructionHelper<0, 1, 1>
 {
   public:
     LIR_HEADER(TableSwitch);
 
     LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, MTableSwitch* ins) {
         setOperand(0, in);
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -13,23 +13,16 @@
 #include "jit/shared/Lowering-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::FloorLog2;
 
 void
-LIRGeneratorARM64::useBox(LInstruction* lir, size_t n, MDefinition* mir,
-                          LUse::Policy policy, bool useAtStart)
-{
-    MOZ_CRASH("useBox");
-}
-
-void
 LIRGeneratorARM64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register)
 {
     MOZ_CRASH("useBoxFixed");
 }
 
 LAllocation
 LIRGeneratorARM64::useByteOpRegister(MDefinition* mir)
 {
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -17,18 +17,16 @@ class LIRGeneratorARM64 : public LIRGene
   public:
     LIRGeneratorARM64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
       : LIRGeneratorShared(gen, graph, lirGraph)
     { }
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
-    void useBox(LInstruction* lir, size_t n, MDefinition* mir,
-                LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2);
 
     LAllocation useByteOpRegister(MDefinition* mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
 
     inline LDefinition tempToUnbox() {
         return temp();
     }
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -54,58 +54,16 @@ CodeGeneratorMIPS::ToAddress(const LAllo
 
 
 // shared
 CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
-bool
-CodeGeneratorMIPS::generatePrologue()
-{
-    MOZ_ASSERT(masm.framePushed() == 0);
-    MOZ_ASSERT(!gen->compilingAsmJS());
-
-    // If profiling, save the current frame pointer to a per-thread global field.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerEnterFrame(StackPointer, CallTempReg0);
-
-    // Ensure that the Ion frames is properly aligned.
-    masm.assertStackAlignment(JitStackAlignment, 0);
-
-    // Note that this automatically sets MacroAssembler::framePushed().
-    masm.reserveStack(frameSize());
-    masm.checkStackAlignment();
-
-    emitTracelogIonStart();
-
-    return true;
-}
-
-bool
-CodeGeneratorMIPS::generateEpilogue()
-{
-    MOZ_ASSERT(!gen->compilingAsmJS());
-    masm.bind(&returnLabel_);
-
-    emitTracelogIonStop();
-
-    masm.freeStack(frameSize());
-    MOZ_ASSERT(masm.framePushed() == 0);
-
-    // If profiling, reset the per-thread global lastJitFrame to point to
-    // the previous frame.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerExitFrame();
-
-    masm.ret();
-    return true;
-}
-
 void
 CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
                                  MBasicBlock* mir, Assembler::DoubleCondition cond)
 {
     // Skip past trivial blocks.
     mir = skipTrivialBlocks(mir);
 
     Label* label = mir->lir()->label();
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -20,18 +20,16 @@ class CodeGeneratorMIPS : public CodeGen
 {
     friend class MoveResolverMIPS;
 
     CodeGeneratorMIPS* thisFromCtor() {
         return this;
     }
 
   protected:
-    // Label for the common return path.
-    NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     inline Address ToAddress(const LAllocation& a);
     inline Address ToAddress(const LAllocation* a);
 
     MoveOperand toMoveOperand(LAllocation a) const;
 
     template <typename T1, typename T2>
@@ -70,18 +68,16 @@ class CodeGeneratorMIPS : public CodeGen
         masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail);
         bailoutFrom(&bail, snapshot);
     }
 
     void bailoutFrom(Label* label, LSnapshot* snapshot);
     void bailout(LSnapshot* snapshot);
 
   protected:
-    bool generatePrologue();
-    bool generateEpilogue();
     bool generateOutOfLineCode();
 
     template <typename T>
     void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond)
     {
         mir = skipTrivialBlocks(mir);
 
         Label* label = mir->lir()->label();
--- a/js/src/jit/mips/LIR-mips.h
+++ b/js/src/jit/mips/LIR-mips.h
@@ -5,37 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_mips_LIR_mips_h
 #define jit_mips_LIR_mips_h
 
 namespace js {
 namespace jit {
 
-class LBox : public LInstructionHelper<2, 1, 0>
-{
-    MIRType type_;
-
-  public:
-    LIR_HEADER(Box);
-
-    LBox(const LAllocation& in_payload, MIRType type)
-      : type_(type)
-    {
-        setOperand(0, in_payload);
-    }
-
-    MIRType type() const {
-        return type_;
-    }
-    const char* extraName() const {
-        return StringFromMIRType(type_);
-    }
-};
-
 class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1>
 {
     MIRType type_;
 
   public:
     LIR_HEADER(BoxFloatingPoint);
 
     LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type)
@@ -228,32 +207,16 @@ class LModMaskI : public LInstructionHel
         return shift_;
     }
 
     MMod* mir() const {
         return mir_->toMod();
     }
 };
 
-class LPowHalfD : public LInstructionHelper<1, 1, 0>
-{
-  public:
-    LIR_HEADER(PowHalfD);
-    LPowHalfD(const LAllocation& input) {
-        setOperand(0, input);
-    }
-
-    const LAllocation* input() {
-        return getOperand(0);
-    }
-    const LDefinition* output() {
-        return getDef(0);
-    }
-};
-
 // Takes a tableswitch with an integer to decide
 class LTableSwitch : public LInstructionHelper<0, 1, 2>
 {
   public:
     LIR_HEADER(TableSwitch);
 
     LTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                  const LDefinition& jumpTablePointer, MTableSwitch* ins) {
--- a/js/src/jit/none/CodeGenerator-none.h
+++ b/js/src/jit/none/CodeGenerator-none.h
@@ -10,18 +10,16 @@
 #include "jit/shared/CodeGenerator-shared.h"
 
 namespace js {
 namespace jit {
 
 class CodeGeneratorNone : public CodeGeneratorShared
 {
   public:
-    NonAssertingLabel returnLabel_;
-
     CodeGeneratorNone(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
       : CodeGeneratorShared(gen, graph, masm)
     {
         MOZ_CRASH();
     }
 
     MoveOperand toMoveOperand(LAllocation) const { MOZ_CRASH(); }
     template <typename T1, typename T2>
@@ -30,18 +28,16 @@ class CodeGeneratorNone : public CodeGen
     void bailoutTest32(Assembler::Condition, Register, T, LSnapshot*) { MOZ_CRASH(); }
     template <typename T1, typename T2>
     void bailoutCmpPtr(Assembler::Condition, T1, T2, LSnapshot*) { MOZ_CRASH(); }
     void bailoutTestPtr(Assembler::Condition, Register, Register, LSnapshot*) { MOZ_CRASH(); }
     void bailoutIfFalseBool(Register, LSnapshot*) { MOZ_CRASH(); }
     void bailoutFrom(Label*, LSnapshot*) { MOZ_CRASH(); }
     void bailout(LSnapshot*) { MOZ_CRASH(); }
     void bailoutIf(Assembler::Condition, LSnapshot*) { MOZ_CRASH(); }
-    bool generatePrologue() { MOZ_CRASH(); }
-    bool generateEpilogue() { MOZ_CRASH(); }
     bool generateOutOfLineCode() { MOZ_CRASH(); }
     void testNullEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) {
         MOZ_CRASH();
     }
     void testUndefinedEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) {
         MOZ_CRASH();
     }
     void testObjectEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock*, MBasicBlock*) {
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -49,16 +49,17 @@ CodeGeneratorShared::CodeGeneratorShared
     snapshots_(),
     recovers_(),
     deoptTable_(nullptr),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
     safepoints_(graph->totalSlotCount(), (gen->info().nargs() + 1) * sizeof(Value)),
+    returnLabel_(),
     nativeToBytecodeMap_(nullptr),
     nativeToBytecodeMapSize_(0),
     nativeToBytecodeTableOffset_(0),
     nativeToBytecodeNumRegions_(0),
     nativeToBytecodeScriptList_(nullptr),
     nativeToBytecodeScriptListLength_(0),
     trackedOptimizationsMap_(nullptr),
     trackedOptimizationsMapSize_(0),
@@ -105,16 +106,64 @@ CodeGeneratorShared::CodeGeneratorShared
         // asm.js code.
         frameClass_ = FrameSizeClass::None();
     } else {
         frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
     }
 }
 
 bool
+CodeGeneratorShared::generatePrologue()
+{
+    MOZ_ASSERT(masm.framePushed() == 0);
+    MOZ_ASSERT(!gen->compilingAsmJS());
+
+#ifdef JS_USE_LINK_REGISTER
+    masm.pushReturnAddress();
+#endif
+
+    // If profiling, save the current frame pointer to a per-thread global field.
+    if (isProfilerInstrumentationEnabled())
+        masm.profilerEnterFrame(masm.getStackPointer(), CallTempReg0);
+
+    // Ensure that the Ion frame is properly aligned.
+    masm.assertStackAlignment(JitStackAlignment, 0);
+
+    // Note that this automatically sets MacroAssembler::framePushed().
+    masm.reserveStack(frameSize());
+    masm.checkStackAlignment();
+
+    emitTracelogIonStart();
+    return true;
+}
+
+bool
+CodeGeneratorShared::generateEpilogue()
+{
+    MOZ_ASSERT(!gen->compilingAsmJS());
+    masm.bind(&returnLabel_);
+
+    emitTracelogIonStop();
+
+    masm.freeStack(frameSize());
+    MOZ_ASSERT(masm.framePushed() == 0);
+
+    // If profiling, reset the per-thread global lastJitFrame to point to
+    // the previous frame.
+    if (isProfilerInstrumentationEnabled())
+        masm.profilerExitFrame();
+
+    masm.ret();
+
+    // On systems that use a constant pool, this is a good time to emit.
+    masm.flushBuffer();
+    return true;
+}
+
+bool
 CodeGeneratorShared::generateOutOfLineCode()
 {
     for (size_t i = 0; i < outOfLineCode_.length(); i++) {
         // Add native => bytecode mapping entries for OOL sites.
         // Not enabled on asm.js yet since asm doesn't contain bytecode mappings.
         if (!gen->compilingAsmJS()) {
             if (!addNativeToBytecodeEntry(outOfLineCode_[i]->bytecodeSite()))
                 return false;
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -100,16 +100,19 @@ class CodeGeneratorShared : public LElem
     // Patchable backedges generated for loops.
     Vector<PatchableBackedgeInfo, 0, SystemAllocPolicy> patchableBackedges_;
 
 #ifdef JS_TRACE_LOGGING
     js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTraceLoggers_;
     js::Vector<CodeOffsetLabel, 0, SystemAllocPolicy> patchableTLScripts_;
 #endif
 
+    // Label for the common return path.
+    NonAssertingLabel returnLabel_;
+
   public:
     struct NativeToBytecode {
         CodeOffsetLabel nativeOffset;
         InlineScriptTree* tree;
         jsbytecode* pc;
     };
 
   protected:
@@ -445,16 +448,19 @@ class CodeGeneratorShared : public LElem
     inline OutOfLineCode* oolCallVM(const VMFunction& fun, LInstruction* ins, const ArgSeq& args,
                                     const StoreOutputTo& out);
 
     void addCache(LInstruction* lir, size_t cacheIndex);
     size_t addCacheLocations(const CacheLocationList& locs, size_t* numLocs);
     ReciprocalMulConstants computeDivisionConstants(uint32_t d, int maxLog);
 
   protected:
+    bool generatePrologue();
+    bool generateEpilogue();
+
     void addOutOfLineCode(OutOfLineCode* code, const MInstruction* mir);
     void addOutOfLineCode(OutOfLineCode* code, const BytecodeSite* site);
     bool generateOutOfLineCode();
 
     Label* labelForBackedgeWithImplicitCheck(MBasicBlock* mir);
 
     // Generate a jump to the start of the specified block, adding information
     // if this is a loop backedge. Use this in place of jumping directly to
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -12,17 +12,36 @@
 #include "jit/AtomicOp.h"
 #include "jit/shared/Assembler-shared.h"
 
 // This file declares LIR instructions that are common to every platform.
 
 namespace js {
 namespace jit {
 
-class Range;
+class LBox : public LInstructionHelper<BOX_PIECES, 1, 0>
+{
+    MIRType type_;
+
+  public:
+    LIR_HEADER(Box);
+
+    LBox(const LAllocation& payload, MIRType type)
+      : type_(type)
+    {
+        setOperand(0, payload);
+    }
+
+    MIRType type() const {
+        return type_;
+    }
+    const char* extraName() const {
+        return StringFromMIRType(type_);
+    }
+};
 
 template <size_t Temps, size_t ExtraUses = 0>
 class LBinaryMath : public LInstructionHelper<1, 2 + ExtraUses, Temps>
 {
   public:
     const LAllocation* lhs() {
         return this->getOperand(0);
     }
@@ -3719,16 +3738,36 @@ class LFloat32x4ToInt32x4 : public LInst
     const LDefinition* temp() {
         return getTemp(0);
     }
     const MSimdConvert* mir() const {
         return mir_->toSimdConvert();
     }
 };
 
+// Double raised to a half power.
+class LPowHalfD : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(PowHalfD);
+    explicit LPowHalfD(const LAllocation& input) {
+        setOperand(0, input);
+    }
+
+    const LAllocation* input() {
+        return getOperand(0);
+    }
+    const LDefinition* output() {
+        return getDef(0);
+    }
+    MPowHalf* mir() const {
+        return mir_->toPowHalf();
+    }
+};
+
 // No-op instruction that is used to hold the entry snapshot. This simplifies
 // register allocation as it doesn't need to sniff the snapshot out of the
 // LIRGraph.
 class LStart : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Start)
 };
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -5,38 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_x64_LIR_x64_h
 #define jit_x64_LIR_x64_h
 
 namespace js {
 namespace jit {
 
-// Given a typed input, returns an untyped box.
-class LBox : public LInstructionHelper<1, 1, 0>
-{
-    MIRType type_;
-
-  public:
-    LIR_HEADER(Box)
-
-    LBox(MIRType type, const LAllocation& payload)
-      : type_(type)
-    {
-        setOperand(0, payload);
-    }
-
-    MIRType type() const {
-        return type_;
-    }
-    const char* extraName() const {
-        return StringFromMIRType(type_);
-    }
-};
-
 // Given an untyped input, guards on whether it's a specific type and returns
 // the unboxed payload.
 class LUnboxBase : public LInstructionHelper<1, 1, 0>
 {
   public:
     explicit LUnboxBase(const LAllocation& input) {
         setOperand(0, input);
     }
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -56,17 +56,17 @@ LIRGeneratorX64::visitBox(MBox* box)
     if (opd->isConstant() && box->canEmitAtUses()) {
         emitAtUses(box);
         return;
     }
 
     if (opd->isConstant()) {
         define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX));
     } else {
-        LBox* ins = new(alloc()) LBox(opd->type(), useRegister(opd));
+        LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
         define(ins, box, LDefinition(LDefinition::BOX));
     }
 }
 
 void
 LIRGeneratorX64::visitUnbox(MUnbox* unbox)
 {
     MDefinition* box = unbox->getOperand(0);
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -34,59 +34,16 @@ using JS::GenericNaN;
 namespace js {
 namespace jit {
 
 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
-bool
-CodeGeneratorX86Shared::generatePrologue()
-{
-    MOZ_ASSERT(masm.framePushed() == 0);
-    MOZ_ASSERT(!gen->compilingAsmJS());
-
-    // If profiling, save the current frame pointer to a per-thread global field.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerEnterFrame(StackPointer, CallTempReg0);
-
-    // Ensure that the Ion frames is properly aligned.
-    masm.assertStackAlignment(JitStackAlignment, 0);
-
-    // Note that this automatically sets MacroAssembler::framePushed().
-    masm.reserveStack(frameSize());
-
-    emitTracelogIonStart();
-
-    return true;
-}
-
-bool
-CodeGeneratorX86Shared::generateEpilogue()
-{
-    MOZ_ASSERT(!gen->compilingAsmJS());
-
-    masm.bind(&returnLabel_);
-
-    emitTracelogIonStop();
-
-    // Pop the stack we allocated at the start of the function.
-    masm.freeStack(frameSize());
-    MOZ_ASSERT(masm.framePushed() == 0);
-
-    // If profiling, reset the per-thread global lastJitFrame to point to
-    // the previous frame.
-    if (isProfilerInstrumentationEnabled())
-        masm.profilerExitFrame();
-
-    masm.ret();
-    return true;
-}
-
 void
 OutOfLineBailout::accept(CodeGeneratorX86Shared* codegen)
 {
     codegen->visitOutOfLineBailout(this);
 }
 
 void
 CodeGeneratorX86Shared::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue,
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -92,18 +92,16 @@ class CodeGeneratorX86Shared : public Co
     };
 
     // Functions for emitting bounds-checking code with branches.
     MOZ_WARN_UNUSED_RESULT
     uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins,
                                         Register ptr, Label* fail);
     void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr);
 
-    // Label for the common return path.
-    NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     MoveOperand toMoveOperand(LAllocation a) const;
 
     void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot);
     void bailoutIf(Assembler::DoubleCondition condition, LSnapshot* snapshot);
     void bailoutFrom(Label* label, LSnapshot* snapshot);
     void bailout(LSnapshot* snapshot);
@@ -143,18 +141,16 @@ class CodeGeneratorX86Shared : public Co
     void bailoutCvttss2si(FloatRegister src, Register dest, LSnapshot* snapshot) {
         // Same trick as explained in the above comment.
         masm.vcvttss2si(src, dest);
         masm.cmp32(dest, Imm32(1));
         bailoutIf(Assembler::Overflow, snapshot);
     }
 
   protected:
-    bool generatePrologue();
-    bool generateEpilogue();
     bool generateOutOfLineCode();
 
     void emitCompare(MCompare::CompareType type, const LAllocation* left, const LAllocation* right);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue, MBasicBlock* ifFalse,
                     Assembler::NaNCond ifNaN = Assembler::NaN_HandledByCond);
--- a/js/src/jit/x86-shared/LIR-x86-shared.h
+++ b/js/src/jit/x86-shared/LIR-x86-shared.h
@@ -213,36 +213,16 @@ class LModPowTwoI : public LInstructionH
     const LDefinition* remainder() {
         return getDef(0);
     }
     MMod* mir() const {
         return mir_->toMod();
     }
 };
 
-// Double raised to a half power.
-class LPowHalfD : public LInstructionHelper<1, 1, 0>
-{
-  public:
-    LIR_HEADER(PowHalfD)
-    explicit LPowHalfD(const LAllocation& input) {
-        setOperand(0, input);
-    }
-
-    const LAllocation* input() {
-        return getOperand(0);
-    }
-    const LDefinition* output() {
-        return getDef(0);
-    }
-    MPowHalf* mir() const {
-        return mir_->toPowHalf();
-    }
-};
-
 // Takes a tableswitch with an integer to decide
 class LTableSwitch : public LInstructionHelper<0, 1, 2>
 {
   public:
     LIR_HEADER(TableSwitch)
 
     LTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                  const LDefinition& jumpTablePointer, MTableSwitch* ins)
--- a/js/src/jit/x86/LIR-x86.h
+++ b/js/src/jit/x86/LIR-x86.h
@@ -5,37 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_x86_LIR_x86_h
 #define jit_x86_LIR_x86_h
 
 namespace js {
 namespace jit {
 
-class LBox : public LInstructionHelper<2, 1, 0>
-{
-    MIRType type_;
-
-  public:
-    LIR_HEADER(Box);
-
-    LBox(const LAllocation& in_payload, MIRType type)
-      : type_(type)
-    {
-        setOperand(0, in_payload);
-    }
-
-    MIRType type() const {
-        return type_;
-    }
-    const char* extraName() const {
-        return StringFromMIRType(type_);
-    }
-};
-
 class LBoxFloatingPoint : public LInstructionHelper<2, 1, 1>
 {
     MIRType type_;
 
   public:
     LIR_HEADER(BoxFloatingPoint);
 
     LBoxFloatingPoint(const LAllocation& in, const LDefinition& temp, MIRType type)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3808,41 +3808,47 @@ AutoFile::open(JSContext* cx, const char
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN,
                                  filename, "No such file or directory");
             return false;
         }
     }
     return true;
 }
 
-JSObject * const JS::ReadOnlyCompileOptions::nullObjectPtr = nullptr;
-
 void
-JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs)
-{
+JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOptions& rhs)
+{
+    mutedErrors_ = rhs.mutedErrors_;
     version = rhs.version;
     versionSet = rhs.versionSet;
     utf8 = rhs.utf8;
-    lineno = rhs.lineno;
-    column = rhs.column;
-    forEval = rhs.forEval;
-    noScriptRval = rhs.noScriptRval;
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
     forceAsync = rhs.forceAsync;
     installedFile = rhs.installedFile;
     sourceIsLazy = rhs.sourceIsLazy;
     introductionType = rhs.introductionType;
     introductionLineno = rhs.introductionLineno;
     introductionOffset = rhs.introductionOffset;
     hasIntroductionInfo = rhs.hasIntroductionInfo;
+};
+
+void
+JS::ReadOnlyCompileOptions::copyPODOptions(const ReadOnlyCompileOptions& rhs)
+{
+    copyPODTransitiveOptions(rhs);
+    lineno = rhs.lineno;
+    column = rhs.column;
+    isRunOnce = rhs.isRunOnce;
+    forEval = rhs.forEval;
+    noScriptRval = rhs.noScriptRval;
 }
 
 JS::OwningCompileOptions::OwningCompileOptions(JSContext* cx)
     : ReadOnlyCompileOptions(),
       runtime(GetRuntime(cx)),
       elementRoot(cx),
       elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
@@ -3857,17 +3863,16 @@ JS::OwningCompileOptions::~OwningCompile
     js_free(const_cast<char*>(introducerFilename_));
 }
 
 bool
 JS::OwningCompileOptions::copy(JSContext* cx, const ReadOnlyCompileOptions& rhs)
 {
     copyPODOptions(rhs);
 
-    setMutedErrors(rhs.mutedErrors());
     setElement(rhs.element());
     setElementAttributeName(rhs.elementAttributeName());
     setIntroductionScript(rhs.introductionScript());
 
     return setFileAndLine(cx, rhs.filename(), rhs.lineno) &&
            setSourceMapURL(cx, rhs.sourceMapURL()) &&
            setIntroducerFilename(cx, rhs.introducerFilename());
 }
@@ -4265,18 +4270,17 @@ CompileFunction(JSContext* cx, const Rea
     if (!fun)
         return false;
 
     // Make sure the static scope chain matches up when we have a
     // non-syntactic scope.
     MOZ_ASSERT_IF(!enclosingDynamicScope->is<GlobalObject>(),
                   HasNonSyntacticStaticScopeChain(enclosingStaticScope));
 
-    CompileOptions options(cx, optionsArg);
-    if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingStaticScope))
+    if (!frontend::CompileFunctionBody(cx, fun, optionsArg, formals, srcBuf, enclosingStaticScope))
         return false;
 
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext* cx, AutoObjectVector& scopeChain,
                     const ReadOnlyCompileOptions& options,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3379,42 +3379,48 @@ namespace JS {
  * compilation options where a worker thread can find them, and then return
  * immediately. The worker thread will come along at some later point, and use
  * the options.
  *
  * The compiler itself just needs to be able to access a collection of options;
  * it doesn't care who owns them, or what's keeping them alive. It does its own
  * addrefs/copies/tracing/etc.
  *
- * So, we have a class hierarchy that reflects these three use cases:
+ * Furthermore, in some cases compile options are propagated from one entity to
+ * another (e.g. from a scriipt to a function defined in that script).  This
+ * involves copying over some, but not all, of the options.
+ *
+ * So, we have a class hierarchy that reflects these four use cases:
  *
- * - ReadOnlyCompileOptions is the common base class. It can be used by code
- *   that simply needs to access options set elsewhere, like the compiler.
+ * - TransitiveCompileOptions is the common base class, representing options
+ *   that should get propagated from a script to functions defined in that
+ *   script.  This is never instantiated directly.
+ *
+ * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
+ *   representing a full set of compile options.  It can be used by code that
+ *   simply needs to access options set elsewhere, like the compiler.  This,
+ *   again, is never instantiated directly.
  *
  * - The usual CompileOptions class must be stack-allocated, and holds
  *   non-owning references to the filename, element, and so on. It's derived
  *   from ReadOnlyCompileOptions, so the compiler can use it.
  *
  * - OwningCompileOptions roots / copies / reference counts of all its values,
  *   and unroots / frees / releases them when it is destructed. It too is
  *   derived from ReadOnlyCompileOptions, so the compiler accepts it.
  */
 
 /*
  * The common base class for the CompileOptions hierarchy.
  *
- * Use this in code that only needs to access compilation options created
- * elsewhere, like the compiler. Don't instantiate this class (the constructor
- * is protected anyway); instead, create instances only of the derived classes:
- * CompileOptions and OwningCompileOptions.
- */
-class JS_FRIEND_API(ReadOnlyCompileOptions)
+ * Use this in code that needs to propagate compile options from one compilation
+ * unit to another.
+ */
+class JS_FRIEND_API(TransitiveCompileOptions)
 {
-    friend class CompileOptions;
-
   protected:
     // The Web Platform allows scripts to be loaded from arbitrary cross-origin
     // sources. This allows an attack by which a malicious website loads a
     // sensitive file (say, a bank statement) cross-origin (using the user's
     // cookies), and sniffs the generated syntax errors (via a window.onerror
     // handler) for juicy morsels of its contents.
     //
     // To counter this attack, HTML5 specifies that script errors should be
@@ -3426,29 +3432,24 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     const char* filename_;
     const char* introducerFilename_;
     const char16_t* sourceMapURL_;
 
     // This constructor leaves 'version' set to JSVERSION_UNKNOWN. The structure
     // is unusable until that's set to something more specific; the derived
     // classes' constructors take care of that, in ways appropriate to their
     // purpose.
-    ReadOnlyCompileOptions()
+    TransitiveCompileOptions()
       : mutedErrors_(false),
         filename_(nullptr),
         introducerFilename_(nullptr),
         sourceMapURL_(nullptr),
         version(JSVERSION_UNKNOWN),
         versionSet(false),
         utf8(false),
-        lineno(1),
-        column(0),
-        isRunOnce(false),
-        forEval(false),
-        noScriptRval(false),
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
         asmJSOption(false),
         forceAsync(false),
         installedFile(false),
@@ -3456,58 +3457,100 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
         introductionType(nullptr),
         introductionLineno(0),
         introductionOffset(0),
         hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
+    void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
+
+  public:
+    // Read-only accessors for non-POD options. The proper way to set these
+    // depends on the derived type.
+    bool mutedErrors() const { return mutedErrors_; }
+    const char* filename() const { return filename_; }
+    const char* introducerFilename() const { return introducerFilename_; }
+    const char16_t* sourceMapURL() const { return sourceMapURL_; }
+    virtual JSObject* element() const = 0;
+    virtual JSString* elementAttributeName() const = 0;
+    virtual JSScript* introductionScript() const = 0;
+
+    // POD options.
+    JSVersion version;
+    bool versionSet;
+    bool utf8;
+    bool selfHostingMode;
+    bool canLazilyParse;
+    bool strictOption;
+    bool extraWarningsOption;
+    bool werrorOption;
+    bool asmJSOption;
+    bool forceAsync;
+    bool installedFile;  // 'true' iff pre-compiling js file in packaged app
+    bool sourceIsLazy;
+
+    // |introductionType| is a statically allocated C string:
+    // one of "eval", "Function", or "GeneratorFunction".
+    const char* introductionType;
+    unsigned introductionLineno;
+    uint32_t introductionOffset;
+    bool hasIntroductionInfo;
+
+  private:
+    void operator=(const TransitiveCompileOptions&) = delete;
+};
+
+/*
+ * The class representing a full set of compile options.
+ *
+ * Use this in code that only needs to access compilation options created
+ * elsewhere, like the compiler. Don't instantiate this class (the constructor
+ * is protected anyway); instead, create instances only of the derived classes:
+ * CompileOptions and OwningCompileOptions.
+ */
+class JS_FRIEND_API(ReadOnlyCompileOptions) : public TransitiveCompileOptions
+{
+    friend class CompileOptions;
+
+  protected:
+    ReadOnlyCompileOptions()
+      : TransitiveCompileOptions(),
+        lineno(1),
+        column(0),
+        isRunOnce(false),
+        forEval(false),
+        noScriptRval(false)
+    { }
+
+    // Set all POD options (those not requiring reference counts, copies,
+    // rooting, or other hand-holding) to their values in |rhs|.
     void copyPODOptions(const ReadOnlyCompileOptions& rhs);
 
   public:
     // Read-only accessors for non-POD options. The proper way to set these
     // depends on the derived type.
     bool mutedErrors() const { return mutedErrors_; }
     const char* filename() const { return filename_; }
     const char* introducerFilename() const { return introducerFilename_; }
     const char16_t* sourceMapURL() const { return sourceMapURL_; }
     virtual JSObject* element() const = 0;
     virtual JSString* elementAttributeName() const = 0;
     virtual JSScript* introductionScript() const = 0;
 
     // POD options.
-    JSVersion version;
-    bool versionSet;
-    bool utf8;
     unsigned lineno;
     unsigned column;
     // isRunOnce only applies to non-function scripts.
     bool isRunOnce;
     bool forEval;
     bool noScriptRval;
-    bool selfHostingMode;
-    bool canLazilyParse;
-    bool strictOption;
-    bool extraWarningsOption;
-    bool werrorOption;
-    bool asmJSOption;
-    bool forceAsync;
-    bool installedFile;  // 'true' iff pre-compiling js file in packaged app
-    bool sourceIsLazy;
-
-    // |introductionType| is a statically allocated C string:
-    // one of "eval", "Function", or "GeneratorFunction".
-    const char* introductionType;
-    unsigned introductionLineno;
-    uint32_t introductionOffset;
-    bool hasIntroductionInfo;
 
   private:
-    static JSObject * const nullObjectPtr;
     void operator=(const ReadOnlyCompileOptions&) = delete;
 };
 
 /*
  * Compilation options, with dynamic lifetime. An instance of this type
  * makes a copy of / holds / roots all dynamically allocated resources
  * (principals; elements; strings) that it refers to. Its destructor frees
  * / drops / unroots them. This is heavier than CompileOptions, below, but
@@ -3612,18 +3655,32 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
   public:
     explicit CompileOptions(JSContext* cx, JSVersion version = JSVERSION_UNKNOWN);
     CompileOptions(js::ContextFriendFields* cx, const ReadOnlyCompileOptions& rhs)
       : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
         introductionScriptRoot(cx)
     {
         copyPODOptions(rhs);
 
-        mutedErrors_ = rhs.mutedErrors_;
         filename_ = rhs.filename();
+        introducerFilename_ = rhs.introducerFilename();
+        sourceMapURL_ = rhs.sourceMapURL();
+        elementRoot = rhs.element();
+        elementAttributeNameRoot = rhs.elementAttributeName();
+        introductionScriptRoot = rhs.introductionScript();
+    }
+
+    CompileOptions(js::ContextFriendFields* cx, const TransitiveCompileOptions& rhs)
+      : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
+        introductionScriptRoot(cx)
+    {
+        copyPODTransitiveOptions(rhs);
+
+        filename_ = rhs.filename();
+        introducerFilename_ = rhs.introducerFilename();
         sourceMapURL_ = rhs.sourceMapURL();
         elementRoot = rhs.element();
         elementAttributeNameRoot = rhs.elementAttributeName();
         introductionScriptRoot = rhs.introductionScript();
     }
 
     JSObject* element() const override { return elementRoot; }
     JSString* elementAttributeName() const override { return elementAttributeNameRoot; }
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -32,16 +32,17 @@ typedef AutoVectorRooter<jsid> AutoIdVec
 class CallArgs;
 
 template <typename T>
 class Rooted;
 
 class JS_FRIEND_API(CompileOptions);
 class JS_FRIEND_API(ReadOnlyCompileOptions);
 class JS_FRIEND_API(OwningCompileOptions);
+class JS_FRIEND_API(TransitiveCompileOptions);
 class JS_PUBLIC_API(CompartmentOptions);
 
 class Value;
 struct Zone;
 
 } /* namespace JS */
 
 namespace js {
--- a/js/src/tests/ecma_6/Class/shell.js
+++ b/js/src/tests/ecma_6/Class/shell.js
@@ -1,23 +1,14 @@
 // NOTE: This only turns on 1.8.5 in shell builds.  The browser requires the
 //       futzing in js/src/tests/browser.js (which only turns on 1.8, the most
 //       the browser supports).
 if (typeof version != 'undefined')
   version(185);
 
-function classesEnabled() {
-    try {
-        new Function("class Foo { constructor() { } }");
-        return true;
-    } catch (e if e instanceof SyntaxError) {
-        return false;
-    }
-}
-
 function assertThrownErrorContains(thunk, substr) {
     try {
         thunk();
     } catch (e) {
         if (e.message.indexOf(substr) !== -1)
             return;
         throw new Error("Expected error containing " + substr + ", got " + e);
     }
--- a/js/src/tests/ecma_6/Reflect/apply.js
+++ b/js/src/tests/ecma_6/Reflect/apply.js
@@ -1,17 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/ */
 
 // Reflect.apply calls functions.
 assertEq(Reflect.apply(Math.floor, undefined, [1.75]), 1);
 
 // Reflect.apply requires a target object that's callable.
-class clsX { constructor() {} }  // classes are not callable
-var nonCallable = [{}, [], clsX];
+var nonCallable = [{}, []];
+if (classesEnabled()) {
+    // classes are not callable
+    nonCallable.push(eval("(class clsX { constructor() {} })"));
+}
 for (var value of nonCallable) {
     assertThrowsInstanceOf(() => Reflect.apply(nonCallable), TypeError);
 }
 
 // When target is not callable, Reflect.apply does not try to get argumentList.length before throwing.
 var hits = 0;
 var bogusArgumentList = {get length() { hit++; throw "FAIL";}};
 assertThrowsInstanceOf(() => Reflect.apply({callable: false}, null, bogusArgumentList),
--- a/js/src/tests/ecma_6/Reflect/construct.js
+++ b/js/src/tests/ecma_6/Reflect/construct.js
@@ -25,26 +25,16 @@ assertDeepEq(Reflect.construct(f, [3]), 
 f.prototype = Array.prototype;
 assertDeepEq(Reflect.construct(f, [3]), new f(3));
 
 // Bound functions:
 var bound = f.bind(null, "carrot");
 assertDeepEq(Reflect.construct(bound, []), new bound);
 
 // Classes:
-function classesEnabled(testCode = "class Foo { constructor() {} }") {
-    try {
-        new Function(testCode);
-        return true;
-    } catch (e) {
-        if (!(e instanceof SyntaxError))
-            throw e;
-        return false;
-    }
-}
 if (classesEnabled()) {
     eval(`{
         class Base {
             constructor(...args) {
                 this.args = args;
                 this.newTarget = new.target;
             }
         }
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -210,8 +210,19 @@ if (typeof assertWarning === 'undefined'
         enableLastWarning();
         func();
         var warning = getLastWarning();
         assertEq(warning !== null, true);
         assertEq(warning.name, name);
         disableLastWarning();
     }
 }
+
+function classesEnabled(testCode = "class Foo { constructor() {} }") {
+    try {
+        new Function(testCode);
+        return true;
+    } catch (e) {
+        if (!(e instanceof SyntaxError))
+            throw e;
+        return false;
+    }
+}
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -968,17 +968,17 @@ RestyleManager::ProcessRestyledFrames(ns
     aChangeList.ChangeAt(index, &changeData);
     if (changeData->mFrame) {
       propTable->Delete(changeData->mFrame, ChangeListProperty());
     }
 
 #ifdef DEBUG
     // reget frame from content since it may have been regenerated...
     if (changeData->mContent) {
-      if (!css::CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) {
+      if (!CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) {
         nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
         if (frame) {
           DebugVerifyStyleTree(frame);
         }
       }
     } else if (!changeData->mFrame ||
                changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
       NS_WARNING("Unable to test style tree integrity -- no content node "
@@ -2653,17 +2653,17 @@ ElementRestyler::AddLayerChangesForAnima
 {
   // Bug 847286 - We should have separate animation generation counters
   // on layers for transitions and animations and use != comparison below
   // rather than a > comparison.
   uint64_t frameGeneration =
     RestyleManager::GetMaxAnimationGenerationForFrame(mFrame);
 
   nsChangeHint hint = nsChangeHint(0);
-  const auto& layerInfo = css::CommonAnimationManager::sLayerAnimationInfo;
+  const auto& layerInfo = CommonAnimationManager::sLayerAnimationInfo;
   for (size_t i = 0; i < ArrayLength(layerInfo); i++) {
     Layer* layer =
       FrameLayerBuilder::GetDedicatedLayer(mFrame, layerInfo[i].mLayerType);
     if (layer && frameGeneration > layer->GetAnimationGeneration()) {
       // If we have a transform layer but don't have any transform style, we
       // probably just removed the transform but haven't destroyed the layer
       // yet. In this case we will add the appropriate change hint
       // (nsChangeHint_UpdateContainingBlock) when we compare style contexts
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -1171,21 +1171,18 @@ ComputeCornerSkirtSize(Float aAlpha1, Fl
     aSizeResult = slopeScale - sqrtf(discrim);
     aSlopeResult = slope;
   }
 }
 
 // Draws a border radius with possibly different sides.
 // A skirt is drawn underneath the corner intersection to hide possible
 // seams when anti-aliased drawing is used.
-// As an optimization, this tries to combine the drawing of the side itself
-// with the drawing of the border radius where possible.
 static void
 DrawBorderRadius(DrawTarget* aDrawTarget,
-                 const Point& aSideStart, Float aSideWidth,
                  mozilla::css::Corner c,
                  const Point& aOuterCorner, const Point& aInnerCorner,
                  const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext,
                  const Size& aCornerDims,
                  const Size& aOuterRadius, const Size& aInnerRadius,
                  const Color& aFirstColor, const Color& aSecondColor,
                  Float aSkirtSize, Float aSkirtSlope)
 {
@@ -1216,24 +1213,17 @@ DrawBorderRadius(DrawTarget* aDrawTarget
   // Inner radius center
   Point innerCenter = aInnerCorner + (aCornerMultPrev + aCornerMultNext) * aInnerRadius;
 
   RefPtr<PathBuilder> builder;
   RefPtr<Path> path;
 
   if (aFirstColor.a > 0) {
     builder = aDrawTarget->CreatePathBuilder();
-    // Combine stroke with corner if color matches.
-    if (aSideWidth > 0) {
-      builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth);
-      builder->LineTo(aSideStart);
-      builder->LineTo(outerCornerStart);
-    } else {
-      builder->MoveTo(outerCornerStart);
-    }
+    builder->MoveTo(outerCornerStart);
   }
 
   if (aFirstColor != aSecondColor) {
     // Start and end angles of corner quadrant
     Float startAngle = (c * M_PI) / 2.0f - M_PI,
           endAngle = startAngle + M_PI / 2.0f,
           outerSplitAngle, innerSplitAngle;
     Point outerSplit, innerSplit;
@@ -1303,21 +1293,18 @@ DrawBorderRadius(DrawTarget* aDrawTarget
     path = builder->Finish();
     aDrawTarget->Fill(path, ColorPattern(aFirstColor));
   }
 }
 
 // Draw a corner with possibly different sides.
 // A skirt is drawn underneath the corner intersection to hide possible
 // seams when anti-aliased drawing is used.
-// As an optimization, this tries to combine the drawing of the side itself
-// with the drawing of the corner where possible.
 static void
 DrawCorner(DrawTarget* aDrawTarget,
-           const Point& aSideStart, Float aSideWidth,
            mozilla::css::Corner c,
            const Point& aOuterCorner, const Point& aInnerCorner,
            const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext,
            const Size& aCornerDims,
            const Color& aFirstColor, const Color& aSecondColor,
            Float aSkirtSize, Float aSkirtSlope)
 {
   // Corner box start point
@@ -1325,23 +1312,17 @@ DrawCorner(DrawTarget* aDrawTarget,
   // Corner box end point
   Point cornerEnd = aOuterCorner + aCornerMultNext * aCornerDims;
 
   RefPtr<PathBuilder> builder;
   RefPtr<Path> path;
 
   if (aFirstColor.a > 0) {
     builder = aDrawTarget->CreatePathBuilder();
-    // Combine stroke with corner if color matches.
-    if (aSideWidth > 0) {
-      builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth);
-      builder->LineTo(aSideStart);
-    } else {
-      builder->MoveTo(cornerStart);
-    }
+    builder->MoveTo(cornerStart);
   }
 
   if (aFirstColor != aSecondColor) {
     // Draw first half with first color
     if (aFirstColor.a > 0) {
       builder->LineTo(aOuterCorner);
       // Draw skirt as part of first half
       if (aSkirtSize > 0) {
@@ -1393,18 +1374,18 @@ nsCSSBorderRenderer::DrawNoCompositeColo
 
   Rect strokeRect = mOuterRect;
   strokeRect.Deflate(Margin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0,
                             mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0));
 
   NS_FOR_CSS_SIDES(i) {
     // We now draw the current side and the CW corner following it.
     // The CCW corner of this side was already drawn in the previous iteration.
-    // The side will either be drawn as an explicit stroke or combined
-    // with the drawing of the CW corner.
+    // The side will be drawn as an explicit stroke, and the CW corner will be
+    // filled separately.
     // If the next side does not have a matching color, then we split the
     // corner into two halves, one of each side's color and draw both.
     // Thus, the CCW corner of the next side will end up drawn here.
 
     // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
     mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4);
     mozilla::css::Corner prevCorner = mozilla::css::Corner(i);
 
@@ -1438,55 +1419,51 @@ nsCSSBorderRenderer::DrawNoCompositeColo
     Point outerCorner = mOuterRect.AtCorner(c);
     Point innerCorner = mInnerRect.AtCorner(c);
 
     // start and end points of border side stroke between corners
     Point sideStart =
       mOuterRect.AtCorner(prevCorner) +
         cornerMults[i2] * mBorderCornerDimensions[prevCorner];
     Point sideEnd = outerCorner + cornerMults[i] * mBorderCornerDimensions[c];
-    // if the side is inverted, don't draw it
-    if (-(sideEnd - sideStart).DotProduct(cornerMults[i]) <= 0) {
-      sideWidth = 0.0f;
+    // check if the side is visible and not inverted
+    if (sideWidth > 0 && firstColor.a > 0 &&
+        -(sideEnd - sideStart).DotProduct(cornerMults[i]) > 0) {
+      mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth,
+                              sideEnd + centerAdjusts[i] * sideWidth,
+                              ColorPattern(firstColor),
+                              StrokeOptions(sideWidth));
     }
 
     Float skirtSize = 0.0f, skirtSlope = 0.0f;
     // the sides don't match, so compute a skirt
     if (firstColor != secondColor &&
         mPresContextType != nsPresContext::eContext_Print) {
       Point cornerDir = outerCorner - innerCorner;
       ComputeCornerSkirtSize(firstColor.a, secondColor.a,
                              cornerDir.DotProduct(cornerMults[i]),
                              cornerDir.DotProduct(cornerMults[i3]),
                              skirtSize, skirtSlope);
     }
 
     if (!mBorderRadii[c].IsEmpty()) {
       // the corner has a border radius
       DrawBorderRadius(mDrawTarget,
-                       sideStart, sideWidth,
                        c, outerCorner, innerCorner,
                        cornerMults[i], cornerMults[i3],
                        mBorderCornerDimensions[c],
                        mBorderRadii[c], innerRadii[c],
                        firstColor, secondColor, skirtSize, skirtSlope);
     } else if (!mBorderCornerDimensions[c].IsEmpty()) {
       // a corner with no border radius
       DrawCorner(mDrawTarget,
-                 sideStart, sideWidth,
                  c, outerCorner, innerCorner,
                  cornerMults[i], cornerMults[i3],
                  mBorderCornerDimensions[c],
                  firstColor, secondColor, skirtSize, skirtSlope);
-    } else if (sideWidth > 0 && firstColor.a > 0) {
-      // if there is no corner, then stroke the border side separately
-      mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth,
-                              sideEnd + centerAdjusts[i] * sideWidth,
-                              ColorPattern(firstColor),
-                              StrokeOptions(sideWidth));
     }
   }
 }
 
 void
 nsCSSBorderRenderer::DrawRectangularCompositeColors()
 {
   nsBorderColors *currentColors[4];
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -376,30 +376,30 @@ nsLayoutUtils::HasAnimationsForComposito
                                           nsCSSProperty aProperty)
 {
   nsPresContext* presContext = aFrame->PresContext();
   return presContext->AnimationManager()->GetAnimationsForCompositor(aFrame, aProperty) ||
          presContext->TransitionManager()->GetAnimationsForCompositor(aFrame, aProperty);
 }
 
 bool
-nsLayoutUtils::HasAnimations(const nsIFrame* aFrame,
-                             nsCSSProperty aProperty)
+nsLayoutUtils::HasCurrentAnimationOfProperty(const nsIFrame* aFrame,
+                                             nsCSSProperty aProperty)
 {
   nsPresContext* presContext = aFrame->PresContext();
   AnimationCollection* collection =
     presContext->AnimationManager()->GetAnimationCollection(aFrame);
   if (collection &&
-      collection->HasAnimationOfProperty(aProperty)) {
+      collection->HasCurrentAnimationOfProperty(aProperty)) {
     return true;
   }
   collection =
     presContext->TransitionManager()->GetAnimationCollection(aFrame);
   if (collection &&
-      collection->HasAnimationOfProperty(aProperty)) {
+      collection->HasCurrentAnimationOfProperty(aProperty)) {
     return true;
   }
   return false;
 }
 
 bool
 nsLayoutUtils::HasCurrentAnimations(const nsIFrame* aFrame)
 {
@@ -459,17 +459,17 @@ GetSuitableScale(float aMaxScale, float 
 static void
 GetMinAndMaxScaleForAnimationProperty(const nsIFrame* aFrame,
                                       AnimationCollection* aAnimations,
                                       gfxSize& aMaxScale,
                                       gfxSize& aMinScale)
 {
   for (size_t animIdx = aAnimations->mAnimations.Length(); animIdx-- != 0; ) {
     dom::Animation* anim = aAnimations->mAnimations[animIdx];
-    if (!anim->GetEffect() || anim->GetEffect()->IsFinishedTransition()) {
+    if (!anim->IsRelevant()) {
       continue;
     }
     dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
     for (size_t propIdx = effect->Properties().Length(); propIdx-- != 0; ) {
       AnimationProperty& prop = effect->Properties()[propIdx];
       if (prop.mProperty == eCSSProperty_transform) {
         for (uint32_t segIdx = prop.mSegments.Length(); segIdx-- != 0; ) {
           AnimationPropertySegment& segment = prop.mSegments[segIdx];
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2155,20 +2155,21 @@ public:
   /**
    * Returns true if the frame has animations or transitions that can be
    * performed on the compositor.
    */
   static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
                                          nsCSSProperty aProperty);
 
   /**
-   * Returns true if the frame has animations or transitions for the
-   * property.
+   * Returns true if the frame has current (i.e. running or scheduled-to-run)
+   * animations or transitions for the property.
    */
-  static bool HasAnimations(const nsIFrame* aFrame, nsCSSProperty aProperty);
+  static bool HasCurrentAnimationOfProperty(const nsIFrame* aFrame,
+                                            nsCSSProperty aProperty);
 
   /**
    * Returns true if the frame has any current animations.
    * A current animation is any animation that has not yet finished playing
    * including paused animations.
    */
   static bool HasCurrentAnimations(const nsIFrame* aFrame);
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -355,28 +355,30 @@ nsPresContext::LastRelease()
   if (mMissingFonts) {
     mMissingFonts->Clear();
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationManager);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
 
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrefChangedTimer);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationManager);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
   // NS_RELEASE(tmp->mLanguage); // an atom
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
 
   tmp->Destroy();
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1245,16 +1245,21 @@ PresShell::Destroy()
     NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
     mDocument->DeleteShell();
 
     if (mDocument->HasAnimationController()) {
       mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
     }
   }
 
+  if (mPresContext) {
+    mPresContext->AnimationManager()->ClearEventQueue();
+    mPresContext->TransitionManager()->ClearEventQueue();
+  }
+
   // Revoke any pending events.  We need to do this and cancel pending reflows
   // before we destroy the frame manager, since apparently frame destruction
   // sometimes spins the event queue when plug-ins are involved(!).
   rd->RemoveLayoutFlushObserver(this);
   if (mHiddenInvalidationObserverRefreshDriver) {
     mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
   }
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -59,16 +59,17 @@
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "VsyncSource.h"
 #include "mozilla/VsyncDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimelineConsumers.h"
+#include "nsAnimationManager.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::widget;
 using namespace mozilla::ipc;
@@ -1484,16 +1485,80 @@ nsRefreshDriver::DispatchPendingEvents()
   // Swap out the current pending events
   nsTArray<PendingEvent> pendingEvents(Move(mPendingEvents));
   for (PendingEvent& event : pendingEvents) {
     bool dummy;
     event.mTarget->DispatchEvent(event.mEvent, &dummy);
   }
 }
 
+namespace {
+  enum class AnimationEventType {
+    CSSAnimations,
+    CSSTransitions
+  };
+
+  struct DispatchAnimationEventParams {
+    AnimationEventType mEventType;
+    nsRefreshDriver* mRefreshDriver;
+  };
+}
+
+static bool
+DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
+                                      void* aParams)
+{
+  MOZ_ASSERT(aParams, "Animation event parameters should be set");
+  auto params = static_cast<DispatchAnimationEventParams*>(aParams);
+
+  nsIPresShell* shell = aDocument->GetShell();
+  if (!shell) {
+    return true;
+  }
+
+  nsPresContext* context = shell->GetPresContext();
+  if (!context || context->RefreshDriver() != params->mRefreshDriver) {
+    return true;
+  }
+
+  nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
+
+  if (params->mEventType == AnimationEventType::CSSAnimations) {
+    context->AnimationManager()->DispatchEvents();
+  } else {
+    context->TransitionManager()->DispatchEvents();
+  }
+  aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
+                                   aParams);
+
+  return true;
+}
+
+void
+nsRefreshDriver::DispatchAnimationEvents()
+{
+  if (!mPresContext) {
+    return;
+  }
+
+  nsIDocument* doc = mPresContext->Document();
+
+  // Dispatch transition events first since transitions conceptually sit
+  // below animations in terms of compositing order.
+  DispatchAnimationEventParams params { AnimationEventType::CSSTransitions,
+                                        this };
+  DispatchAnimationEventsOnSubDocuments(doc, &params);
+  if (!mPresContext) {
+    return;
+  }
+
+  params.mEventType = AnimationEventType::CSSAnimations;
+  DispatchAnimationEventsOnSubDocuments(doc, &params);
+}
+
 void
 nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
 {
   // Grab all of our frame request callbacks up front.
   nsTArray<DocumentFrameCallbacks>
     frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
                           mThrottledFrameRequestCallbackDocs.Length());
 
@@ -1659,16 +1724,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
         StopTimer();
         return;
       }
     }
 
     if (i == 0) {
       // This is the Flush_Style case.
 
+      DispatchAnimationEvents();
       DispatchPendingEvents();
       RunFrameRequestCallbacks(aNowTime);
 
       if (mPresContext && mPresContext->GetPresShell()) {
         bool tracingStyleFlush = false;
         nsAutoTArray<nsIPresShell*, 16> observers;
         observers.AppendElements(mStyleFlushObservers);
         for (uint32_t j = observers.Length();
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -317,16 +317,17 @@ private:
     }
 
     mozilla::Maybe<mozilla::TimeStamp> mStartTime;
     RequestTable mEntries;
   };
   typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
 
   void DispatchPendingEvents();
+  void DispatchAnimationEvents();
   void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
 
   void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
 
   enum EnsureTimerStartedFlags {
     eNone = 0,
     eAdjustingTimer = 1 << 0,
     eAllowTimeToGoBackwards = 1 << 1
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -305,17 +305,17 @@ nsLayoutStatics::Initialize()
   ServiceWorkerRegistrar::Initialize();
 
 #ifdef MOZ_B2G
   RequestSyncWifiService::Init();
 #endif
 
 #ifdef DEBUG
   nsStyleContext::Initialize();
-  mozilla::css::CommonAnimationManager::Initialize();
+  mozilla::CommonAnimationManager::Initialize();
 #endif
 
   MediaDecoder::InitStatics();
 
   PromiseDebugging::Init();
 
   layers::CompositorLRU::Init();
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1183431.html
@@ -0,0 +1,6 @@
+<!DOCTYPE>
+<html>
+<body>
+<div style="writing-mode: vertical-lr;"><div style="position: fixed;"></div></div>
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -582,8 +582,9 @@ asserts(0-3) load 1134667.html
 asserts(0-3) load 1134667.html
 load 1146103.html
 load 1146107.html
 load 1146114.html
 load 1156222.html
 load 1157011.html
 load 1169420-1.html
 load 1169420-2.html
+load 1183431.html
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1941,17 +1941,18 @@ nsIFrame::BuildDisplayListForStackingCon
   // we're painting, and we're not animating opacity. Don't do this
   // if we're going to compute plugin geometry, since opacity-0 plugins
   // need to have display items built for them.
   bool needEventRegions = aBuilder->IsBuildingLayerEventRegions() &&
       StyleVisibility()->GetEffectivePointerEvents(this) != NS_STYLE_POINTER_EVENTS_NONE;
   if (disp->mOpacity == 0.0 && aBuilder->IsForPainting() &&
       !aBuilder->WillComputePluginGeometry() &&
       !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) &&
-      !nsLayoutUtils::HasAnimations(this, eCSSProperty_opacity) &&
+      !nsLayoutUtils::HasCurrentAnimationOfProperty(this,
+                                                    eCSSProperty_opacity) &&
       !needEventRegions) {
     return;
   }
 
   if (disp->mWillChangeBitField != 0) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -990,19 +990,19 @@ nsHTMLReflowState::ApplyRelativePosition
       StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
     if (ssc) {
       *aPosition = ssc->ComputePosition(aFrame);
     }
   }
 }
 
 nsIFrame*
-nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame,
-                                               nscoord& aCBIStartEdge,
-                                               nscoord& aCBISize)
+nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame*    aFrame,
+                                               nscoord&     aCBIStartEdge,
+                                               LogicalSize& aCBSize)
 {
   aFrame = aFrame->GetContainingBlock();
   NS_ASSERTION(aFrame != frame, "How did that happen?");
 
   /* Now aFrame is the containing block we want */
 
   /* Check whether the containing block is currently being reflowed.
      If so, use the info from the reflow state. */
@@ -1016,26 +1016,26 @@ nsHTMLReflowState::GetHypotheticalBoxCon
     state = nullptr;
   }
 
   WritingMode wm = aFrame->GetWritingMode();
   if (state) {
     WritingMode stateWM = state->GetWritingMode();
     aCBIStartEdge =
       state->ComputedLogicalBorderPadding().ConvertTo(wm, stateWM).IStart(wm);
-    aCBISize = state->ComputedSize(wm).ISize(wm);
+    aCBSize = state->ComputedSize(wm);
   } else {
     /* Didn't find a reflow state for aFrame.  Just compute the information we
        want, on the assumption that aFrame already knows its size.  This really
        ought to be true by now. */
     NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
                  "aFrame shouldn't be in reflow; we'll lie if it is");
     LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm);
     aCBIStartEdge = borderPadding.IStart(wm);
-    aCBISize = aFrame->ISize(wm) - borderPadding.IStartEnd(wm);
+    aCBSize = aFrame->GetLogicalSize(wm) - borderPadding.Size(wm);
   }
 
   return aFrame;
 }
 
 // When determining the hypothetical box that would have been if the element
 // had been in the flow we may not be able to exactly determine both the IStart
 // and IEnd edges. For example, if the element is a non-replaced inline-level
@@ -1075,81 +1075,91 @@ GetIntrinsicSizeFor(nsIFrame* aFrame, ns
     if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) {
       success = (aIntrinsicSize != nsSize(0, 0));
     }
   }
   return success;
 }
 
 /**
- * aInsideBoxSizing returns the part of the horizontal padding, border,
- * and margin that goes inside the edge given by box-sizing;
+ * aInsideBoxSizing returns the part of the padding, border, and margin
+ * in the aAxis dimension that goes inside the edge given by box-sizing;
  * aOutsideBoxSizing returns the rest.
  */
 void
-nsHTMLReflowState::CalculateInlineBorderPaddingMargin(
-                       nscoord aContainingBlockISize,
+nsHTMLReflowState::CalculateBorderPaddingMargin(
+                       LogicalAxis aAxis,
+                       nscoord aContainingBlockSize,
                        nscoord* aInsideBoxSizing,
                        nscoord* aOutsideBoxSizing)
 {
   WritingMode wm = GetWritingMode();
-  mozilla::css::Side inlineStart = wm.PhysicalSide(eLogicalSideIStart);
-  mozilla::css::Side inlineEnd   = wm.PhysicalSide(eLogicalSideIEnd);
+  mozilla::css::Side startSide =
+    wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart));
+  mozilla::css::Side endSide =
+    wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd));
 
-  const LogicalMargin& border =
-    LogicalMargin(wm, mStyleBorder->GetComputedBorder());
-  LogicalMargin padding(wm), margin(wm);
+  nsMargin styleBorder = mStyleBorder->GetComputedBorder();
+  nscoord borderStartEnd =
+    styleBorder.Side(startSide) + styleBorder.Side(endSide);
+
+  nscoord paddingStartEnd, marginStartEnd;
 
   // See if the style system can provide us the padding directly
   nsMargin stylePadding;
   if (mStylePadding->GetPadding(stylePadding)) {
-    padding = LogicalMargin(wm, stylePadding);
+    paddingStartEnd =
+      stylePadding.Side(startSide) + stylePadding.Side(endSide);
   } else {
-    // We have to compute the inline start and end values
-    padding.IStart(wm) = nsLayoutUtils::
-      ComputeCBDependentValue(aContainingBlockISize,
-                              mStylePadding->mPadding.Get(inlineStart));
-    padding.IEnd(wm) = nsLayoutUtils::
-      ComputeCBDependentValue(aContainingBlockISize,
-                              mStylePadding->mPadding.Get(inlineEnd));
+    // We have to compute the start and end values
+    nscoord start, end;
+    start = nsLayoutUtils::
+      ComputeCBDependentValue(aContainingBlockSize,
+                              mStylePadding->mPadding.Get(startSide));
+    end = nsLayoutUtils::
+      ComputeCBDependentValue(aContainingBlockSize,
+                              mStylePadding->mPadding.Get(endSide));
+    paddingStartEnd = start + end;
   }
 
   // See if the style system can provide us the margin directly
   nsMargin styleMargin;
   if (mStyleMargin->GetMargin(styleMargin)) {
-    margin = LogicalMargin(wm, styleMargin);
+    marginStartEnd =
+      styleMargin.Side(startSide) + styleMargin.Side(endSide);
   } else {
-    // We have to compute the left and right values
-    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineStart)) {
+    nscoord start, end;
+    // We have to compute the start and end values
+    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) {
       // XXX FIXME (or does CalculateBlockSideMargins do this?)
-      margin.IStart(wm) = 0;  // just ignore
+      start = 0;  // just ignore
     } else {
-      margin.IStart(wm) = nsLayoutUtils::
-        ComputeCBDependentValue(aContainingBlockISize,
-                                mStyleMargin->mMargin.Get(inlineStart));
+      start = nsLayoutUtils::
+        ComputeCBDependentValue(aContainingBlockSize,
+                                mStyleMargin->mMargin.Get(startSide));
     }
-    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineEnd)) {
+    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) {
       // XXX FIXME (or does CalculateBlockSideMargins do this?)
-      margin.IEnd(wm) = 0;  // just ignore
+      end = 0;  // just ignore
     } else {
-      margin.IEnd(wm) = nsLayoutUtils::
-        ComputeCBDependentValue(aContainingBlockISize,
-                                mStyleMargin->mMargin.Get(inlineEnd));
+      end = nsLayoutUtils::
+        ComputeCBDependentValue(aContainingBlockSize,
+                                mStyleMargin->mMargin.Get(endSide));
     }
+    marginStartEnd = start + end;
   }
 
-  nscoord outside =
-    padding.IStartEnd(wm) + border.IStartEnd(wm) + margin.IStartEnd(wm);
+  nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd;
   nscoord inside = 0;
   switch (mStylePosition->mBoxSizing) {
     case NS_STYLE_BOX_SIZING_BORDER:
-      inside += border.IStartEnd(wm);
+      inside += borderStartEnd;
       // fall through
     case NS_STYLE_BOX_SIZING_PADDING:
-      inside += padding.IStartEnd(wm);
+      inside += paddingStartEnd;
   }
   outside -= inside;
   *aInsideBoxSizing = inside;
   *aOutsideBoxSizing = outside;
   return;
 }
 
 /**
@@ -1173,40 +1183,48 @@ static bool AreAllEarlierInFlowFramesEmp
     }
   }
   *aFound = false;
   return true;
 }
 
 // Calculate the hypothetical box that the element would have if it were in
 // the flow. The values returned are relative to the padding edge of the
-// absolute containing block, in the actual containing block's writing mode.
-// cbrs->frame is the actual containing block
+// absolute containing block. The writing-mode of the hypothetical box will
+// have the same block direction as the absolute containing block, but may
+// differ in inline-bidi direction.
+// In the code below, |cbrs->frame| is the absolute containing block, while
+// |containingBlock| is the nearest block container of the placeholder frame,
+// which may be different from the absolute containing block.
 void
 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext*    aPresContext,
                                             nsIFrame*         aPlaceholderFrame,
                                             const nsHTMLReflowState* cbrs,
                                             nsHypotheticalBox& aHypotheticalBox,
                                             nsIAtom*          aFrameType)
 {
   NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
                "mOriginalDisplay has not been properly initialized");
 
   // Find the nearest containing block frame to the placeholder frame,
   // and its inline-start edge and width.
-  nscoord blockIStartContentEdge, blockContentISize;
+  nscoord blockIStartContentEdge;
+  // Dummy writing mode for blockContentSize, will be changed as needed by
+  // GetHypotheticalBoxContainer.
+  WritingMode cbwm = cbrs->GetWritingMode();
+  LogicalSize blockContentSize(cbwm);
   nsIFrame* containingBlock =
     GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge,
-                                blockContentISize);
+                                blockContentSize);
+  // Now blockContentSize is in containingBlock's writing mode.
 
   // If it's a replaced element and it has a 'auto' value for
   //'inline size', see if we can get the intrinsic size. This will allow
   // us to exactly determine both the inline edges
   WritingMode wm = containingBlock->GetWritingMode();
-  aHypotheticalBox.mWritingMode = wm;
 
   nsStyleCoord styleISize = mStylePosition->ISize(wm);
   bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto;
   nsSize      intrinsicSize;
   bool        knowIntrinsicSize = false;
   if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
     // See if we can get the intrinsic size of the element
     knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType);
@@ -1225,50 +1243,50 @@ nsHTMLReflowState::CalculateHypothetical
   } else {
     // It's either a replaced inline-level element or a block-level element
 
     // Determine the total amount of inline direction
     // border/padding/margin that the element would have had if it had
     // been in the flow. Note that we ignore any 'auto' and 'inherit'
     // values
     nscoord insideBoxSizing, outsideBoxSizing;
-    CalculateInlineBorderPaddingMargin(blockContentISize,
-                                       &insideBoxSizing, &outsideBoxSizing);
+    CalculateBorderPaddingMargin(eLogicalAxisInline,
+                                 blockContentSize.ISize(wm),
+                                 &insideBoxSizing, &outsideBoxSizing);
 
     if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) {
       // It's a replaced element with an 'auto' inline size so the box
       // inline size is its intrinsic size plus any border/padding/margin
       if (knowIntrinsicSize) {
         boxISize = LogicalSize(wm, intrinsicSize).ISize(wm) +
                    outsideBoxSizing + insideBoxSizing;
         knowBoxISize = true;
       }
 
     } else if (isAutoISize) {
       // The box inline size is the containing block inline size
-      boxISize = blockContentISize;
+      boxISize = blockContentSize.ISize(wm);
       knowBoxISize = true;
 
     } else {
       // We need to compute it. It's important we do this, because if it's
       // percentage based this computed value may be different from the computed
       // value calculated using the absolute containing block width
-      boxISize = ComputeISizeValue(blockContentISize,
+      boxISize = ComputeISizeValue(blockContentSize.ISize(wm),
                                    insideBoxSizing, outsideBoxSizing,
                                    styleISize) +
                  insideBoxSizing + outsideBoxSizing;
       knowBoxISize = true;
     }
   }
 
   // Get the placeholder x-offset and y-offset in the coordinate
   // space of its containing block
   // XXXbz the placeholder is not fully reflowed yet if our containing block is
   // relatively positioned...
-  WritingMode cbwm = cbrs->GetWritingMode();
   nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW
     ? cbrs->ComputedSizeAsContainerIfConstrained()
     : containingBlock->GetSize();
   LogicalPoint
     placeholderOffset(wm, aPlaceholderFrame->GetOffsetTo(containingBlock),
                       containerSize);
 
   // First, determine the hypothetical box's mBStart.  We want to check the
@@ -1363,17 +1381,18 @@ nsHTMLReflowState::CalculateHypothetical
     aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + boxISize;
 #ifdef DEBUG
     aHypotheticalBox.mIEndIsExact = true;
 #endif
   } else {
     // We can't compute the inline-end edge because we don't know the desired
     // inline-size. So instead use the end content edge of the block parent,
     // but remember it's not exact
-    aHypotheticalBox.mIEnd = blockIStartContentEdge + blockContentISize;
+    aHypotheticalBox.mIEnd =
+      blockIStartContentEdge + blockContentSize.ISize(wm);
 #ifdef DEBUG
     aHypotheticalBox.mIEndIsExact = false;
 #endif
   }
 
   // The current coordinate space is that of the nearest block to the placeholder.
   // Convert to the coordinate space of the absolute containing block
   // One weird thing here is that for fixed-positioned elements we want to do
@@ -1406,33 +1425,99 @@ nsHTMLReflowState::CalculateHypothetical
     } while (containingBlock != cbrs->frame);
   } else {
     // XXXldb We need to either ignore scrolling for the absolute
     // positioning case too (and take the incompatibility) or figure out
     // how to make these positioned elements actually *move* when we
     // scroll, and thus avoid the resulting incremental reflow bugs.
     cbOffset = containingBlock->GetOffsetTo(cbrs->frame);
   }
-  nsSize cbrsSize =
-    cbrs->ComputedPhysicalSize() +
-    cbrs->ComputedLogicalBorderPadding().Size(cbwm).GetPhysicalSize(cbwm);
+  nsSize cbrsSize = cbrs->ComputedSizeAsContainerIfConstrained();
   LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize);
   aHypotheticalBox.mIStart += logCBOffs.I(wm);
   aHypotheticalBox.mIEnd += logCBOffs.I(wm);
   aHypotheticalBox.mBStart += logCBOffs.B(wm);
 
   // The specified offsets are relative to the absolute containing block's
   // padding edge and our current values are relative to the border edge, so
   // translate.
   LogicalMargin border =
     cbrs->ComputedLogicalBorderPadding() - cbrs->ComputedLogicalPadding();
   border = border.ConvertTo(wm, cbrs->GetWritingMode());
   aHypotheticalBox.mIStart -= border.IStart(wm);
   aHypotheticalBox.mIEnd -= border.IStart(wm);
   aHypotheticalBox.mBStart -= border.BStart(wm);
+
+  // At this point, we have computed aHypotheticalBox using the writing mode
+  // of the placeholder's containing block.
+
+  if (cbwm.GetBlockDir() != wm.GetBlockDir()) {
+    // If the block direction we used in calculating aHypotheticalBox does not
+    // match the absolute containing block's, we need to convert here so that
+    // aHypotheticalBox is usable in relation to the absolute containing block.
+    // This requires computing or measuring the abspos frame's block-size,
+    // which is not otherwise required/used here (as aHypotheticalBox records
+    // only the block-start coordinate).
+
+    // This is similar to the inline-size calculation for a replaced
+    // inline-level element or a block-level element (above), except that
+    // 'auto' sizing is handled differently in the block direction for non-
+    // replaced elements and replaced elements lacking an intrinsic size.
+
+    // Determine the total amount of block direction
+    // border/padding/margin that the element would have had if it had
+    // been in the flow. Note that we ignore any 'auto' and 'inherit'
+    // values.
+    nscoord insideBoxSizing, outsideBoxSizing;
+    CalculateBorderPaddingMargin(eLogicalAxisBlock,
+                                 blockContentSize.BSize(wm),
+                                 &insideBoxSizing, &outsideBoxSizing);
+
+    nscoord boxBSize;
+    nsStyleCoord styleBSize = mStylePosition->BSize(wm);
+    bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto;
+    if (isAutoBSize) {
+      if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) {
+        // It's a replaced element with an 'auto' block size so the box
+        // block size is its intrinsic size plus any border/padding/margin
+        boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) +
+                   outsideBoxSizing + insideBoxSizing;
+      } else {
+        // XXX Bug 1191801
+        // Figure out how to get the correct boxBSize here (need to reflow the
+        // positioned frame?)
+        boxBSize = 0;
+      }
+    } else {
+      // We need to compute it. It's important we do this, because if it's
+      // percentage-based this computed value may be different from the
+      // computed value calculated using the absolute containing block height.
+      boxBSize = ComputeBSizeValue(blockContentSize.BSize(wm),
+                                   insideBoxSizing, styleBSize) +
+                 insideBoxSizing + outsideBoxSizing;
+    }
+
+    LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize);
+
+    LogicalPoint origin(wm, aHypotheticalBox.mIStart,
+                        aHypotheticalBox.mBStart);
+    origin = origin.ConvertTo(cbwm, wm, cbrsSize -
+                              boxSize.GetPhysicalSize(wm));
+
+    aHypotheticalBox.mIStart = origin.I(cbwm);
+    aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart +
+                             boxSize.ConvertTo(cbwm, wm).ISize(cbwm);
+#ifdef DEBUG
+    aHypotheticalBox.mIEndIsExact = false; // it may be fake
+#endif
+    aHypotheticalBox.mBStart = origin.B(cbwm);
+    aHypotheticalBox.mWritingMode = cbwm;
+  } else {
+    aHypotheticalBox.mWritingMode = wm;
+  }
 }
 
 void
 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
                                            const nsHTMLReflowState* cbrs,
                                            const LogicalSize& aCBSize,
                                            nsIAtom* aFrameType)
 {
@@ -1518,18 +1603,16 @@ nsHTMLReflowState::InitAbsoluteConstrain
   } else {
     offsets.BEnd(cbwm) = nsLayoutUtils::
       ComputeBSizeDependentValue(cbSize.BSize(cbwm),
                                  mStylePosition->mOffset.GetBEnd(cbwm));
   }
 
   if (bStartIsAuto && bEndIsAuto) {
     // Treat 'top' like 'static-position'
-    NS_ASSERTION(hypotheticalBox.mWritingMode.GetBlockDir() == cbwm.GetBlockDir(),
-                 "block direction mismatch");
     offsets.BStart(cbwm) = hypotheticalBox.mBStart;
     bStartIsAuto = false;
   }
 
   SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm));
 
   bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit();
   bool bSizeIsAuto = eStyleUnit_Auto == mStylePosition->BSize(cbwm).GetUnit();
--- a/layout/generic/nsHTMLReflowState.h
+++ b/layout/generic/nsHTMLReflowState.h
@@ -904,40 +904,49 @@ protected:
   void InitConstraints(nsPresContext*              aPresContext,
                        const mozilla::LogicalSize& aContainingBlockSize,
                        const nsMargin*             aBorder,
                        const nsMargin*             aPadding,
                        nsIAtom*                    aFrameType);
 
   // Returns the nearest containing block or block frame (whether or not
   // it is a containing block) for the specified frame.  Also returns
-  // the inline-start edge and inline size of the containing block's
+  // the inline-start edge and logical size of the containing block's
   // content area.
   // These are returned in the coordinate space of the containing block.
   nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame,
                                         nscoord& aCBIStartEdge,
-                                        nscoord& aCBISize);
+                                        mozilla::LogicalSize& aCBSize);
 
+  // Calculate a "hypothetical box" position where the placeholder frame
+  // (for a position:fixed/absolute element) would have been placed if it were
+  // positioned statically. The hypothetical box will have a writing mode with
+  // the same block direction as the absolute containing block (cbrs->frame),
+  // though it may differ in inline-bidi direction.
   void CalculateHypotheticalBox(nsPresContext*    aPresContext,
                                 nsIFrame*         aPlaceholderFrame,
                                 const nsHTMLReflowState* cbrs,
                                 nsHypotheticalBox& aHypotheticalBox,
                                 nsIAtom*          aFrameType);
 
   void InitAbsoluteConstraints(nsPresContext* aPresContext,
                                const nsHTMLReflowState* cbrs,
                                const mozilla::LogicalSize& aContainingBlockSize,
                                nsIAtom* aFrameType);
 
   // Calculates the computed values for the 'min-Width', 'max-Width',
   // 'min-Height', and 'max-Height' properties, and stores them in the assorted
   // data members
   void ComputeMinMaxValues(const mozilla::LogicalSize& aContainingBlockSize);
 
-  void CalculateInlineBorderPaddingMargin(nscoord aContainingBlockISize,
-                                          nscoord* aInsideBoxSizing,
-                                          nscoord* aOutsideBoxSizing);
+  // aInsideBoxSizing returns the part of the padding, border, and margin
+  // in the aAxis dimension that goes inside the edge given by box-sizing;
+  // aOutsideBoxSizing returns the rest.
+  void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis,
+                                    nscoord aContainingBlockSize,
+                                    nscoord* aInsideBoxSizing,
+                                    nscoord* aOutsideBoxSizing);
 
   void CalculateBlockSideMargins(nsIAtom* aFrameType);
 };
 
 #endif /* nsHTMLReflowState_h___ */
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       writing-mode: vertical-lr; position: absolute; left: 0; top: 0; }
+.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+img { position: absolute; left: 0; top: 34px;
+      width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr rel">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test rel"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       writing-mode: vertical-lr; position: absolute; left: 0; top: 0; }
+.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+img { position: absolute; left: 0; top: 34px;
+      background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr rel">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test rel"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vlr { writing-mode: vertical-lr; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { margin: 0; padding: 2px; border: 2px solid green; inline-size: -moz-fit-content; }
+</style>
+<body>
+<div>
+  <div class="vlr"><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3a.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vlr">
+  <div><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3b.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vlr rel">
+  <div><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3c.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vlr">
+  <div class="rel"><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       position: absolute; right: 0; top: 0; }
+.test { writing-mode: vertical-rl; background: #aaa;
+        position: absolute; right: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+img { position: absolute; right: 0; top: 34px;
+      width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl rel">
+  <div class="test"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; width: 30px; height: 40px; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test rel"><span class="abc">abc</span><img src=""></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       position: absolute; right: 0; top: 0; }
+.test { writing-mode: vertical-rl; background: #aaa;
+        position: absolute; right: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+img { position: absolute; right: 0; top: 34px;
+      background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl rel">
+  <div class="test"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 100px; height: 150px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+img { position: absolute; background: green; border: 5px solid yellow; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test rel"><span class="abc">abc</span><img src="blue-32x32.png"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vrl { writing-mode: vertical-rl; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { margin: 0; padding: 2px; border: 2px solid green; inline-size: -moz-fit-content; }
+</style>
+<body>
+<div class="vrl">
+  <div><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6a.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vrl">
+  <div><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6b.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vrl rel">
+  <div><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6c.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 120px; height: 150px; border: 5px solid blue; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+span { display: inline-block; padding: 5px; block-size: 1.5em; border: 2px solid red; }
+p { position: absolute; margin: 0; padding: 2px; border: 2px solid green; }
+</style>
+<body>
+<div class="vrl">
+  <div class="rel"><span>abc def</span><p>xyzzy</p></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       writing-mode: vertical-lr; position: absolute; left: 0; top: 0; }
+.test { background: #aaa; position: absolute; left: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+iframe {
+  position: absolute; left: 0; top: 34px;
+  background: yellow; border: 5px solid green;
+  width: 150px; height: 300px; /* XXX this is probably wrong, pending CSSWG clarification */
+}
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vlr rel">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vlr { writing-mode: vertical-lr; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vlr">
+  <div class="test rel"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px;
+       writing-mode: vertical-rl; position: absolute; right: 0; top: 0; }
+.test { background: #aaa; position: absolute; right: 0; top: 0; width: 2em; height: 100%; }
+.rel { position: relative; }
+iframe {
+  position: absolute; right: 0; top: 34px;
+  background: yellow; border: 5px solid green;
+  width: 150px; height: 300px; /* XXX this is probably wrong, pending CSSWG clarification */
+}
+</style>
+<body>
+<div class="rel">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8a.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vrl rel">
+  <div class="test"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8c.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<meta charset="utf-8">
+<style>
+body>div { margin: 50px 20px; width: 400px; height: 500px; border: 5px solid blue; }
+.abc { display: inline-block; border: 2px solid red; inline-size: 30px; }
+.test { background: #aaa; width: 2em; }
+.vrl { writing-mode: vertical-rl; }
+.rel { position: relative; }
+iframe { position: absolute; background: yellow; border: 5px solid green; }
+</style>
+<body>
+<div class="vrl">
+  <div class="test rel"><span class="abc">abc</span><iframe src="data:text/html,hello"></iframe></div>
+</div>
copy from layout/reftests/writing-mode/blue-32x32.png
copy to layout/reftests/writing-mode/abspos/blue-32x32.png
--- a/layout/reftests/writing-mode/abspos/reftest.list
+++ b/layout/reftests/writing-mode/abspos/reftest.list
@@ -94,8 +94,33 @@ fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-082.xht s71-abs-pos-non-replaced-vrl-082-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-084.xht s71-abs-pos-non-replaced-vrl-084-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-086.xht s71-abs-pos-non-replaced-vrl-086-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-088.xht s71-abs-pos-non-replaced-vrl-088-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-090.xht s71-abs-pos-non-replaced-vrl-090-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-092.xht s71-abs-pos-non-replaced-vrl-092-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-094.xht s71-abs-pos-non-replaced-vrl-094-ref.xht
 fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-096.xht s71-abs-pos-non-replaced-vrl-096-ref.xht
+
+== 1183431-orthogonal-modes-1a.html 1183431-orthogonal-modes-1-ref.html
+== 1183431-orthogonal-modes-1b.html 1183431-orthogonal-modes-1-ref.html
+== 1183431-orthogonal-modes-1c.html 1183431-orthogonal-modes-1-ref.html
+== 1183431-orthogonal-modes-2a.html 1183431-orthogonal-modes-2-ref.html
+== 1183431-orthogonal-modes-2b.html 1183431-orthogonal-modes-2-ref.html
+== 1183431-orthogonal-modes-2c.html 1183431-orthogonal-modes-2-ref.html
+== 1183431-orthogonal-modes-3a.html 1183431-orthogonal-modes-3-ref.html
+== 1183431-orthogonal-modes-3b.html 1183431-orthogonal-modes-3-ref.html
+== 1183431-orthogonal-modes-3c.html 1183431-orthogonal-modes-3-ref.html
+== 1183431-orthogonal-modes-4a.html 1183431-orthogonal-modes-4-ref.html
+== 1183431-orthogonal-modes-4b.html 1183431-orthogonal-modes-4-ref.html
+== 1183431-orthogonal-modes-4c.html 1183431-orthogonal-modes-4-ref.html
+== 1183431-orthogonal-modes-5a.html 1183431-orthogonal-modes-5-ref.html
+== 1183431-orthogonal-modes-5b.html 1183431-orthogonal-modes-5-ref.html
+== 1183431-orthogonal-modes-5c.html 1183431-orthogonal-modes-5-ref.html
+fails == 1183431-orthogonal-modes-6a.html 1183431-orthogonal-modes-6-ref.html # bug 1191801
+== 1183431-orthogonal-modes-6b.html 1183431-orthogonal-modes-6-ref.html
+== 1183431-orthogonal-modes-6c.html 1183431-orthogonal-modes-6-ref.html
+== 1183431-orthogonal-modes-7a.html 1183431-orthogonal-modes-7-ref.html
+== 1183431-orthogonal-modes-7b.html 1183431-orthogonal-modes-7-ref.html
+== 1183431-orthogonal-modes-7c.html 1183431-orthogonal-modes-7-ref.html
+fails == 1183431-orthogonal-modes-8a.html 1183431-orthogonal-modes-8-ref.html # bug 1191801
+== 1183431-orthogonal-modes-8b.html 1183431-orthogonal-modes-8-ref.html
+== 1183431-orthogonal-modes-8c.html 1183431-orthogonal-modes-8-ref.html
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -46,18 +46,16 @@ IsGeometricProperty(nsCSSProperty aPrope
     case eCSSProperty_top:
     case eCSSProperty_width:
       return true;
     default:
       return false;
   }
 }
 
-namespace css {
-
 CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext)
   : mPresContext(aPresContext)
   , mIsObservingRefreshDriver(false)
 {
   PR_INIT_CLIST(&mElementCollections);
 }
 
 CommonAnimationManager::~CommonAnimationManager()
@@ -170,32 +168,26 @@ CommonAnimationManager::GetAnimationColl
 }
 
 AnimationCollection*
 CommonAnimationManager::GetAnimationsForCompositor(const nsIFrame* aFrame,
                                                    nsCSSProperty aProperty)
 {
   AnimationCollection* collection = GetAnimationCollection(aFrame);
   if (!collection ||
-      !collection->HasAnimationOfProperty(aProperty) ||
+      !collection->HasCurrentAnimationOfProperty(aProperty) ||
       !collection->CanPerformOnCompositorThread(
         AnimationCollection::CanAnimate_AllowPartial)) {
     return nullptr;
   }
 
   // This animation can be done on the compositor.
   return collection;
 }
 
-/*
- * nsISupports implementation
- */
-
-NS_IMPL_ISUPPORTS(CommonAnimationManager, nsIStyleRuleProcessor)
-
 nsRestyleHint
 CommonAnimationManager::HasStateDependentStyle(StateRuleProcessorData* aData)
 {
   return nsRestyleHint(0);
 }
 
 nsRestyleHint
 CommonAnimationManager::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
@@ -443,17 +435,17 @@ CommonAnimationManager::LayerAnimationRe
   }
   return nullptr;
 }
 
 #ifdef DEBUG
 /* static */ void
 CommonAnimationManager::Initialize()
 {
-  const auto& info = css::CommonAnimationManager::sLayerAnimationInfo;
+  const auto& info = CommonAnimationManager::sLayerAnimationInfo;
   for (size_t i = 0; i < ArrayLength(info); i++) {
     auto record = info[i];
     MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty,
                                         CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
                "CSS property with entry in sLayerAnimationInfo does not "
                "have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag");
   }
 
@@ -537,18 +529,16 @@ AnimValuesStyleRule::List(FILE* out, int
     AppendUTF16toUTF8(value, str);
     str.AppendLiteral("; ");
   }
   str.AppendLiteral("}\n");
   fprintf_stderr(out, "%s", str.get());
 }
 #endif
 
-} // namespace css
-
 bool
 AnimationCollection::CanAnimatePropertyOnCompositor(
   const dom::Element *aElement,
   nsCSSProperty aProperty,
   CanAnimateFlags aFlags)
 {
   bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
   if (!gfxPlatform::OffMainThreadCompositingEnabled()) {
@@ -700,41 +690,56 @@ AnimationCollection::PostUpdateLayerAnim
   for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     const auto& properties = mAnimations[animIdx]->GetEffect()->Properties();
     for (size_t propIdx = properties.Length(); propIdx-- != 0; ) {
       nsCSSProperty prop = properties[propIdx].mProperty;
       if (nsCSSProps::PropHasFlags(prop,
                                    CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
           !propsHandled.HasProperty(prop)) {
         propsHandled.AddProperty(prop);
-        nsChangeHint changeHint = css::CommonAnimationManager::
+        nsChangeHint changeHint = CommonAnimationManager::
           LayerAnimationRecordFor(prop)->mChangeHint;
         dom::Element* element = GetElementToRestyle();
         if (element) {
           mManager->mPresContext->RestyleManager()->
             PostRestyleEvent(element, nsRestyleHint(0), changeHint);
         }
       }
     }
   }
 }
 
 bool
-AnimationCollection::HasAnimationOfProperty(nsCSSProperty aProperty) const
+AnimationCollection::HasCurrentAnimationOfProperty(nsCSSProperty
+                                                     aProperty) const
 {
-  for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
-    const KeyframeEffectReadOnly* effect = mAnimations[animIdx]->GetEffect();
-    if (effect && effect->HasAnimationOfProperty(aProperty) &&
-        !effect->IsFinishedTransition()) {
+  for (Animation* animation : mAnimations) {
+    if (animation->HasCurrentEffect() &&
+        animation->GetEffect()->HasAnimationOfProperty(aProperty)) {
       return true;
     }
   }
   return false;
 }
 
+/*static*/ nsString
+AnimationCollection::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType)
+{
+  switch (aPseudoType) {
+    case nsCSSPseudoElements::ePseudo_before:
+      return NS_LITERAL_STRING("::before");
+    case nsCSSPseudoElements::ePseudo_after:
+      return NS_LITERAL_STRING("::after");
+    default:
+      MOZ_ASSERT(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
+                 "Unexpected pseudo type");
+      return EmptyString();
+  }
+}
+
 mozilla::dom::Element*
 AnimationCollection::GetElementToRestyle() const
 {
   if (IsForElement()) {
     return mElement;
   }
 
   nsIFrame* primaryFrame = mElement->GetPrimaryFrame();
@@ -924,21 +929,28 @@ AnimationCollection::CanThrottleAnimatio
   if (!element) {
     return false;
   }
   nsIFrame* frame = nsLayoutUtils::GetStyleFrame(element);
   if (!frame) {
     return false;
   }
 
-
-  const auto& info = css::CommonAnimationManager::sLayerAnimationInfo;
+  const auto& info = CommonAnimationManager::sLayerAnimationInfo;
   for (size_t i = 0; i < ArrayLength(info); i++) {
     auto record = info[i];
-    if (!HasAnimationOfProperty(record.mProperty)) {
+    // We only need to worry about *current* animations here.
+    // - If we have a newly-finished animation, Animation::CanThrottle will
+    //   detect that and force an unthrottled sample.
+    // - If we have a newly-idle animation, then whatever caused the animation
+    //   to be idle will update the animation generation so we'll return false
+    //   from the layer generation check below for any other running compositor
+    //   animations (and if no other compositor animations exist we won't get
+    //   this far).
+    if (!HasCurrentAnimationOfProperty(record.mProperty)) {
       continue;
     }
 
     Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
                      frame, record.mLayerType);
     if (!layer || mAnimationGeneration > layer->GetAnimationGeneration()) {
       return false;
     }
@@ -992,9 +1004,29 @@ AnimationCollection::HasCurrentAnimation
         effect->HasAnimationOfProperties(aProperties, aPropertyCount)) {
       return true;
     }
   }
 
   return false;
 }
 
+nsPresContext*
+OwningElementRef::GetRenderedPresContext() const
+{
+  if (!mElement) {
+    return nullptr;
+  }
+
+  nsIDocument* doc = mElement->GetComposedDoc();
+  if (!doc) {
+    return nullptr;
+  }
+
+  nsIPresShell* shell = doc->GetShell();
+  if (!shell) {
+    return nullptr;
+  }
+
+  return shell->GetPresContext();
+}
+
 } // namespace mozilla
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -8,16 +8,17 @@
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsIStyleRule.h"
 #include "nsRefreshDriver.h"
 #include "prclist.h"
 #include "nsChangeHint.h"
 #include "nsCSSProperty.h"
 #include "nsDisplayList.h" // For nsDisplayItem::Type
+#include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsStyleStruct.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Assertions.h"
@@ -30,45 +31,40 @@
 class nsIFrame;
 class nsPresContext;
 
 namespace mozilla {
 
 class RestyleTracker;
 struct AnimationCollection;
 
-namespace css {
-
 bool IsGeometricProperty(nsCSSProperty aProperty);
 
 class CommonAnimationManager : public nsIStyleRuleProcessor,
                                public nsARefreshObserver {
 public:
   explicit CommonAnimationManager(nsPresContext *aPresContext);
 
-  // nsISupports
-  NS_DECL_ISUPPORTS
-
   // nsIStyleRuleProcessor (parts)
   virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) override;
   virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override;
   virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override;
   virtual nsRestyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData,
                                RestyleHintData& aRestyleHintDataResult) override;
   virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override;
   virtual void RulesMatching(ElementRuleProcessorData* aData) override;
   virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override;
   virtual void RulesMatching(AnonBoxRuleProcessorData* aData) override;
 #ifdef MOZ_XUL
   virtual void RulesMatching(XULTreeRuleProcessorData* aData) override;
 #endif
-  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE override;
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE override;
 
 #ifdef DEBUG
   static void Initialize();
 #endif
 
   // NOTE:  This can return null after Disconnect().
   nsPresContext* PresContext() const { return mPresContext; }
@@ -76,17 +72,17 @@ public:
   /**
    * Notify the manager that the pres context is going away.
    */
   void Disconnect();
 
   // Tell the restyle tracker about all the styles that we're currently
   // animating, so that it can update the animation rule for these
   // elements.
-  void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
+  void AddStyleUpdatesTo(RestyleTracker& aTracker);
 
   AnimationCollection*
   GetAnimations(dom::Element *aElement,
                 nsCSSPseudoElements::Type aPseudoType,
                 bool aCreateIfNeeded);
 
   // Returns true if aContent or any of its ancestors has an animation
   // or transition.
@@ -105,23 +101,23 @@ public:
   // has been updated.
   void NotifyCollectionUpdated(AnimationCollection& aCollection);
 
   enum FlushFlags {
     Can_Throttle,
     Cannot_Throttle
   };
 
-  nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
+  nsIStyleRule* GetAnimationRule(dom::Element* aElement,
                                  nsCSSPseudoElements::Type aPseudoType);
 
   static bool ExtractComputedValueForTransition(
                   nsCSSProperty aProperty,
                   nsStyleContext* aStyleContext,
-                  mozilla::StyleAnimationValue& aComputedValue);
+                  StyleAnimationValue& aComputedValue);
 
   // For CSS properties that may be animated on a separate layer, represents
   // a record of the corresponding layer type and change hint.
   struct LayerAnimationRecord {
     nsCSSProperty mProperty;
     nsDisplayItem::Type mLayerType;
     nsChangeHint mChangeHint;
   };
@@ -137,17 +133,17 @@ public:
   // on such properties.
   static const LayerAnimationRecord*
     LayerAnimationRecordFor(nsCSSProperty aProperty);
 
 protected:
   virtual ~CommonAnimationManager();
 
   // For ElementCollectionRemoved
-  friend struct mozilla::AnimationCollection;
+  friend struct AnimationCollection;
 
   void AddElementCollection(AnimationCollection* aCollection);
   void ElementCollectionRemoved() { MaybeStartOrStopObservingRefreshDriver(); }
   void RemoveAllElementCollections();
 
   // We should normally only call MaybeStartOrStopObservingRefreshDriver in
   // situations where we will also queue events since otherwise we may stop
   // getting refresh driver ticks before we queue the necessary events.
@@ -198,63 +194,60 @@ public:
   NS_DECL_ISUPPORTS
 
   // nsIStyleRule implementation
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
-  void AddValue(nsCSSProperty aProperty,
-                mozilla::StyleAnimationValue &aStartValue)
+  void AddValue(nsCSSProperty aProperty, StyleAnimationValue &aStartValue)
   {
     PropertyValuePair v = { aProperty, aStartValue };
     mPropertyValuePairs.AppendElement(v);
   }
 
   // Caller must fill in returned value.
-  mozilla::StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty)
+  StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty)
   {
     PropertyValuePair *p = mPropertyValuePairs.AppendElement();
     p->mProperty = aProperty;
     return &p->mValue;
   }
 
   struct PropertyValuePair {
     nsCSSProperty mProperty;
-    mozilla::StyleAnimationValue mValue;
+    StyleAnimationValue mValue;
   };
 
   void AddPropertiesToSet(nsCSSPropertySet& aSet) const
   {
     for (size_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
       const PropertyValuePair &cv = mPropertyValuePairs[i];
       aSet.AddProperty(cv.mProperty);
     }
   }
 
 private:
   ~AnimValuesStyleRule() {}
 
   InfallibleTArray<PropertyValuePair> mPropertyValuePairs;
 };
 
-} // namespace css
-
 typedef InfallibleTArray<nsRefPtr<dom::Animation>> AnimationPtrArray;
 
 enum EnsureStyleRuleFlags {
   EnsureStyleRule_IsThrottled,
   EnsureStyleRule_IsNotThrottled
 };
 
 struct AnimationCollection : public PRCList
 {
   AnimationCollection(dom::Element *aElement, nsIAtom *aElementProperty,
-                      mozilla::css::CommonAnimationManager *aManager)
+                      CommonAnimationManager *aManager)
     : mElement(aElement)
     , mElementProperty(aElementProperty)
     , mManager(aManager)
     , mAnimationGeneration(0)
     , mCheckGeneration(0)
     , mNeedsRefreshes(true)
 #ifdef DEBUG
     , mCalledPropertyDtor(false)
@@ -280,19 +273,19 @@ struct AnimationCollection : public PRCL
 
   static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
                            void *aPropertyValue, void *aData);
 
   void Tick();
 
   void EnsureStyleRuleFor(TimeStamp aRefreshTime, EnsureStyleRuleFlags aFlags);
 
-  bool CanThrottleTransformChanges(mozilla::TimeStamp aTime);
+  bool CanThrottleTransformChanges(TimeStamp aTime);
 
-  bool CanThrottleAnimation(mozilla::TimeStamp aTime);
+  bool CanThrottleAnimation(TimeStamp aTime);
 
   enum CanAnimateFlags {
     // Testing for width, height, top, right, bottom, or left.
     CanAnimate_HasGeometricProperty = 1,
     // Allow the case where OMTA is allowed in general, but not for the
     // specified property.
     CanAnimate_AllowPartial = 2
   };
@@ -322,17 +315,17 @@ public:
   //
   // Note that this does not test whether the element's layer uses
   // off-main-thread compositing, although it does check whether
   // off-main-thread compositing is enabled as a whole.
   bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const;
 
   void PostUpdateLayerAnimations();
 
-  bool HasAnimationOfProperty(nsCSSProperty aProperty) const;
+  bool HasCurrentAnimationOfProperty(nsCSSProperty aProperty) const;
 
   bool IsForElement() const { // rather than for a pseudo-element
     return mElementProperty == nsGkAtoms::animationsProperty ||
            mElementProperty == nsGkAtoms::transitionsProperty;
   }
 
   bool IsForBeforePseudo() const {
     return mElementProperty == nsGkAtoms::animationsOfBeforeProperty ||
@@ -351,46 +344,35 @@ public:
   }
 
   bool IsForAnimations() const {
     return mElementProperty == nsGkAtoms::animationsProperty ||
            mElementProperty == nsGkAtoms::animationsOfBeforeProperty ||
            mElementProperty == nsGkAtoms::animationsOfAfterProperty;
   }
 
-  nsString PseudoElement() const
-  {
-    if (IsForElement()) {
-      return EmptyString();
-    }
-    if (IsForBeforePseudo()) {
-      return NS_LITERAL_STRING("::before");
-    }
-    MOZ_ASSERT(IsForAfterPseudo(),
-               "::before & ::after should be the only pseudo-elements here");
-    return NS_LITERAL_STRING("::after");
-  }
-
   nsCSSPseudoElements::Type PseudoElementType() const
   {
     if (IsForElement()) {
       return nsCSSPseudoElements::ePseudo_NotPseudoElement;
     }
     if (IsForBeforePseudo()) {
       return nsCSSPseudoElements::ePseudo_before;
     }
     MOZ_ASSERT(IsForAfterPseudo(),
                "::before & ::after should be the only pseudo-elements here");
     return nsCSSPseudoElements::ePseudo_after;
   }
 
-  mozilla::dom::Element* GetElementToRestyle() const;
+  static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
+
+  dom::Element* GetElementToRestyle() const;
 
   void PostRestyleForAnimation(nsPresContext *aPresContext) {
-    mozilla::dom::Element* element = GetElementToRestyle();
+    dom::Element* element = GetElementToRestyle();
     if (element) {
       nsRestyleHint hint = IsForTransitions() ? eRestyle_CSSTransitions
                                               : eRestyle_CSSAnimations;
       aPresContext->PresShell()->RestyleForAnimation(element, hint);
     }
   }
 
   void NotifyAnimationUpdated();
@@ -399,28 +381,28 @@ public:
                                        const nsIContent* aContent = nullptr);
 
   dom::Element *mElement;
 
   // the atom we use in mElement's prop table (must be a static atom,
   // i.e., in an atom list)
   nsIAtom *mElementProperty;
 
-  mozilla::css::CommonAnimationManager *mManager;
+  CommonAnimationManager *mManager;
 
-  mozilla::AnimationPtrArray mAnimations;
+  AnimationPtrArray mAnimations;
 
   // This style rule contains the style data for currently animating
   // values.  It only matches when styling with animation.  When we
   // style without animation, we need to not use it so that we can
   // detect any new changes; if necessary we restyle immediately
   // afterwards with animation.
   // NOTE: If we don't need to apply any styles, mStyleRule will be
   // null, but mStyleRuleRefreshTime will still be valid.
-  nsRefPtr<mozilla::css::AnimValuesStyleRule> mStyleRule;
+  nsRefPtr<AnimValuesStyleRule> mStyleRule;
 
   // RestyleManager keeps track of the number of animation
   // 'mini-flushes' (see nsTransitionManager::UpdateAllThrottledStyles()).
   // mAnimationGeneration is the sequence number of the last flush where a
   // transition/animation changed.  We keep a similar count on the
   // corresponding layer so we can check that the layer is up to date with
   // the animation manager.
   uint64_t mAnimationGeneration;
@@ -502,16 +484,90 @@ public:
 
     return mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
           (mPseudoType == nsCSSPseudoElements::ePseudo_before &&
            aOther.mPseudoType == nsCSSPseudoElements::ePseudo_after);
   }
 
   bool IsSet() const { return !!mElement; }
 
+  void GetElement(dom::Element*& aElement,
+                  nsCSSPseudoElements::Type& aPseudoType) const {
+    aElement = mElement;
+    aPseudoType = mPseudoType;
+  }
+
+  nsPresContext* GetRenderedPresContext() const;
+
 private:
   dom::Element* MOZ_NON_OWNING_REF mElement;
   nsCSSPseudoElements::Type        mPseudoType;
 };
 
+template <class EventInfo>
+class DelayedEventDispatcher
+{
+public:
+  void QueueEvent(EventInfo&& aEventInfo)
+  {
+    mPendingEvents.AppendElement(mozilla::Forward<EventInfo>(aEventInfo));
+  }
+
+  // Takes a reference to the owning manager's pres context so it can
+  // detect if the pres context is destroyed while dispatching one of
+  // the events.
+  void DispatchEvents(nsPresContext* const & aPresContext)
+  {
+    if (!aPresContext || mPendingEvents.IsEmpty()) {
+      return;
+    }
+
+    EventArray events;
+    mPendingEvents.SwapElements(events);
+    // FIXME: Sort events here in timeline order, then document order
+    for (EventInfo& info : events) {
+      EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
+
+      if (!aPresContext) {
+        break;
+      }
+    }
+  }
+
+  void ClearEventQueue() { mPendingEvents.Clear(); }
+  bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
+
+  // Methods for supporting cycle-collection
+  void Traverse(nsCycleCollectionTraversalCallback* aCallback,
+                const char* aName)
+  {
+    for (EventInfo& info : mPendingEvents) {
+      ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
+    }
+  }
+  void Unlink() { mPendingEvents.Clear(); }
+
+protected:
+  typedef nsTArray<EventInfo> EventArray;
+
+  EventArray mPendingEvents;
+};
+
+template <class EventInfo>
+inline void
+ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
+{
+  aField.Unlink();
+}
+
+template <class EventInfo>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            DelayedEventDispatcher<EventInfo>& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aField.Traverse(&aCallback, aName);
+}
+
 } // namespace mozilla
 
 #endif /* !defined(mozilla_css_AnimationCommon_h) */
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -2,17 +2,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/. */
 
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 #include "mozilla/dom/CSSAnimationBinding.h"
 
-#include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/KeyframeEffect.h"
 
 #include "nsPresContext.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
@@ -26,16 +25,18 @@
 
 using namespace mozilla;
 using namespace mozilla::css;
 using mozilla::dom::Animation;
 using mozilla::dom::AnimationPlayState;
 using mozilla::dom::KeyframeEffectReadOnly;
 using mozilla::dom::CSSAnimation;
 
+////////////////////////// CSSAnimation ////////////////////////////
+
 JSObject*
 CSSAnimation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::CSSAnimationBinding::Wrap(aCx, this, aGivenProto);
 }
 
 mozilla::dom::Promise*
 CSSAnimation::GetReady(ErrorResult& aRv)
@@ -108,16 +109,23 @@ CSSAnimation::PauseFromStyle()
   // and then updated animation-play-state. It's an unusual case and there's
   // no obvious way to pass on the exception information so we just silently
   // fail for now.
   if (rv.Failed()) {
     NS_WARNING("Unexpected exception pausing animation - silently failing");
   }
 }
 
+void
+CSSAnimation::Tick()
+{
+  Animation::Tick();
+  QueueEvents();
+}
+
 bool
 CSSAnimation::HasLowerCompositeOrderThan(const Animation& aOther) const
 {
   // 0. Object-equality case
   if (&aOther == this) {
     return false;
   }
 
@@ -153,22 +161,50 @@ CSSAnimation::HasLowerCompositeOrderThan
     return mOwningElement.LessThan(otherAnimation->OwningElement());
   }
 
   // 4. (Same element and pseudo): Sort by position in animation-name
   return mSequenceNum < otherAnimation->mSequenceNum;
 }
 
 void
-CSSAnimation::QueueEvents(EventArray& aEventsToDispatch)
+CSSAnimation::QueueEvents()
 {
   if (!mEffect) {
     return;
   }
 
+  // CSS animations dispatch events at their owning element. This allows
+  // script to repurpose a CSS animation to target a different element,
+  // to use a group effect (which has no obvious "target element"), or
+  // to remove the animation effect altogether whilst still getting
+  // animation events.
+  //
+  // It does mean, however, that for a CSS animation that has no owning
+  // element (e.g. it was created using the CSSAnimation constructor or
+  // disassociated from CSS) no events are fired. If it becomes desirable
+  // for these animations to still fire events we should spec the concept
+  // of the "original owning element" or "event target" and allow script
+  // to set it when creating a CSSAnimation object.
+  if (!mOwningElement.IsSet()) {
+    return;
+  }
+
+  dom::Element* owningElement;
+  nsCSSPseudoElements::Type owningPseudoType;
+  mOwningElement.GetElement(owningElement, owningPseudoType);
+  MOZ_ASSERT(owningElement, "Owning element should be set");
+
+  // Get the nsAnimationManager so we can queue events on it
+  nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
+  if (!presContext) {
+    return;
+  }
+  nsAnimationManager* manager = presContext->AnimationManager();
+
   ComputedTiming computedTiming = mEffect->GetComputedTiming();
 
   if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Null) {
     return; // do nothing
   }
 
   // Note that script can change the start time, so we have to handle moving
   // backwards through the animation as well as forwards. An 'animationstart'
@@ -195,38 +231,33 @@ CSSAnimation::QueueEvents(EventArray& aE
   if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Before) {
     mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE;
   } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) {
     mPreviousPhaseOrIteration = computedTiming.mCurrentIteration;
   } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) {
     mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER;
   }
 
-  dom::Element* target;
-  nsCSSPseudoElements::Type targetPseudoType;
-  mEffect->GetTarget(target, targetPseudoType);
-
   uint32_t message;
 
   if (!wasActive && isActive) {
     message = NS_ANIMATION_START;
   } else if (wasActive && !isActive) {
     message = NS_ANIMATION_END;
   } else if (wasActive && isActive && !isSameIteration) {
     message = NS_ANIMATION_ITERATION;
   } else if (skippedActivePhase) {
     // First notifying for start of 0th iteration by appending an
     // 'animationstart':
     StickyTimeDuration elapsedTime =
       std::min(StickyTimeDuration(mEffect->InitialAdvance()),
                computedTiming.mActiveDuration);
-    AnimationEventInfo ei(target, mAnimationName, NS_ANIMATION_START,
-                          elapsedTime,
-                          PseudoTypeAsString(targetPseudoType));
-    aEventsToDispatch.AppendElement(ei);
+    manager->QueueEvent(
+      AnimationEventInfo(owningElement, mAnimationName, NS_ANIMATION_START,
+                         elapsedTime, owningPseudoType));
     // Then have the shared code below append an 'animationend':
     message = NS_ANIMATION_END;
   } else {
     return; // No events need to be sent
   }
 
   StickyTimeDuration elapsedTime;
 
@@ -236,64 +267,43 @@ CSSAnimation::QueueEvents(EventArray& aE
                                     computedTiming.mCurrentIteration;
     elapsedTime = StickyTimeDuration(std::max(iterationStart,
                                               mEffect->InitialAdvance()));
   } else {
     MOZ_ASSERT(message == NS_ANIMATION_END);
     elapsedTime = computedTiming.mActiveDuration;
   }
 
-  AnimationEventInfo ei(target, mAnimationName, message, elapsedTime,
-                        PseudoTypeAsString(targetPseudoType));
-  aEventsToDispatch.AppendElement(ei);
+  manager->QueueEvent(
+    AnimationEventInfo(owningElement, mAnimationName, message, elapsedTime,
+                       owningPseudoType));
 }
 
 CommonAnimationManager*
 CSSAnimation::GetAnimationManager() const
 {
   nsPresContext* context = GetPresContext();
   if (!context) {
     return nullptr;
   }
 
   return context->AnimationManager();
 }
 
-/* static */ nsString
-CSSAnimation::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType)
-{
-  switch (aPseudoType) {
-    case nsCSSPseudoElements::ePseudo_before:
-      return NS_LITERAL_STRING("::before");
-    case nsCSSPseudoElements::ePseudo_after:
-      return NS_LITERAL_STRING("::after");
-    default:
-      return EmptyString();
-  }
-}
+////////////////////////// nsAnimationManager ////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher)
 
-void
-nsAnimationManager::UpdateStyleAndEvents(AnimationCollection* aCollection,
-                                         TimeStamp aRefreshTime,
-                                         EnsureStyleRuleFlags aFlags)
-{
-  aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags);
-  QueueEvents(aCollection, mPendingEvents);
-}
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnimationManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnimationManager)
 
-void
-nsAnimationManager::QueueEvents(AnimationCollection* aCollection,
-                                EventArray& aEventsToDispatch)
-{
-  for (size_t animIdx = aCollection->mAnimations.Length(); animIdx-- != 0; ) {
-    CSSAnimation* anim = aCollection->mAnimations[animIdx]->AsCSSAnimation();
-    MOZ_ASSERT(anim, "Expected a collection of CSS Animations");
-    anim->QueueEvents(aEventsToDispatch);
-  }
-}
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAnimationManager)
+  NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRuleProcessor)
+NS_INTERFACE_MAP_END
 
 void
 nsAnimationManager::MaybeUpdateCascadeResults(AnimationCollection* aCollection)
 {
   for (size_t animIdx = aCollection->mAnimations.Length(); animIdx-- != 0; ) {
     CSSAnimation* anim = aCollection->mAnimations[animIdx]->AsCSSAnimation();
     if (anim->IsInEffect() != anim->mInEffectForCascadeResults) {
       // Update our own cascade results.
@@ -328,17 +338,17 @@ nsAnimationManager::MaybeUpdateCascadeRe
 
 /* virtual */ size_t
 nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
-  // - mPendingEvents
+  // - mEventDispatcher
 }
 
 /* virtual */ size_t
 nsAnimationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
@@ -498,23 +508,22 @@ nsAnimationManager::CheckAnimationRule(n
   // Cancel removed animations
   for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
     newAnimations[newAnimIdx]->CancelFromStyle();
   }
 
   UpdateCascadeResults(aStyleContext, collection);
 
   TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
-  UpdateStyleAndEvents(collection, refreshTime,
-                       EnsureStyleRule_IsNotThrottled);
-  // We don't actually dispatch the mPendingEvents now.  We'll either
+  collection->EnsureStyleRuleFor(refreshTime, EnsureStyleRule_IsNotThrottled);
+  // We don't actually dispatch the pending events now.  We'll either
   // dispatch them the next time we get a refresh driver notification
   // or the next time somebody calls
   // nsPresShell::FlushPendingNotifications.
-  if (!mPendingEvents.IsEmpty()) {
+  if (mEventDispatcher.HasQueuedEvents()) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
   return GetAnimationRule(aElement, aStyleContext->GetPseudoType());
 }
 
 struct KeyframeData {
   float mKey;
@@ -962,42 +971,25 @@ nsAnimationManager::FlushAnimations(Flus
     nsAutoAnimationMutationBatch mb(collection->mElement);
 
     collection->Tick();
     bool canThrottleTick = aFlags == Can_Throttle &&
       collection->CanPerformOnCompositorThread(
         AnimationCollection::CanAnimateFlags(0)) &&
       collection->CanThrottleAnimation(now);
 
-    nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = collection->mStyleRule;
-    UpdateStyleAndEvents(collection, now, canThrottleTick
-                                          ? EnsureStyleRule_IsThrottled
-                                          : EnsureStyleRule_IsNotThrottled);
+    nsRefPtr<AnimValuesStyleRule> oldStyleRule = collection->mStyleRule;
+    collection->EnsureStyleRuleFor(now, canThrottleTick
+                                        ? EnsureStyleRule_IsThrottled
+                                        : EnsureStyleRule_IsNotThrottled);
     if (oldStyleRule != collection->mStyleRule) {
       collection->PostRestyleForAnimation(mPresContext);
     } else {
       didThrottle = true;
     }
   }
 
   if (didThrottle) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
   MaybeStartOrStopObservingRefreshDriver();
-
-  DispatchEvents(); // may destroy us
 }
-
-void
-nsAnimationManager::DoDispatchEvents()
-{
-  EventArray events;
-  mPendingEvents.SwapElements(events);
-  for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) {
-    AnimationEventInfo &info = events[i];
-    EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
-
-    if (!mPresContext) {
-      break;
-    }
-  }
-}
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -27,36 +27,34 @@ class Promise;
 struct AnimationEventInfo {
   nsRefPtr<mozilla::dom::Element> mElement;
   mozilla::InternalAnimationEvent mEvent;
 
   AnimationEventInfo(mozilla::dom::Element *aElement,
                      const nsSubstring& aAnimationName,
                      uint32_t aMessage,
                      const mozilla::StickyTimeDuration& aElapsedTime,
-                     const nsAString& aPseudoElement)
+                     nsCSSPseudoElements::Type aPseudoType)
     : mElement(aElement), mEvent(true, aMessage)
   {
     // XXX Looks like nobody initialize WidgetEvent::time
     mEvent.animationName = aAnimationName;
     mEvent.elapsedTime = aElapsedTime.ToSeconds();
-    mEvent.pseudoElement = aPseudoElement;
+    mEvent.pseudoElement = AnimationCollection::PseudoTypeAsString(aPseudoType);
   }
 
   // InternalAnimationEvent doesn't support copy-construction, so we need
   // to ourselves in order to work with nsTArray
   AnimationEventInfo(const AnimationEventInfo &aOther)
     : mElement(aOther.mElement), mEvent(true, aOther.mEvent.message)
   {
     mEvent.AssignAnimationEventData(aOther.mEvent, false);
   }
 };
 
-typedef InfallibleTArray<AnimationEventInfo> EventArray;
-
 namespace dom {
 
 class CSSAnimation final : public Animation
 {
 public:
  explicit CSSAnimation(nsIGlobalObject* aGlobal,
                        const nsSubstring& aAnimationName)
     : dom::Animation(aGlobal)
@@ -96,16 +94,18 @@ public:
   void PauseFromStyle();
   void CancelFromStyle() override
   {
     mOwningElement = OwningElementRef();
     Animation::CancelFromStyle();
     MOZ_ASSERT(mSequenceNum == kUnsequenced);
   }
 
+  void Tick() override;
+
   bool IsStylePaused() const { return mIsStylePaused; }
 
   bool HasLowerCompositeOrderThan(const Animation& aOther) const override;
   bool IsUsingCustomCompositeOrder() const override
   {
     return mOwningElement.IsSet();
   }
 
@@ -116,18 +116,16 @@ public:
   }
   void CopyAnimationIndex(const CSSAnimation& aOther)
   {
     MOZ_ASSERT(IsUsingCustomCompositeOrder() &&
                aOther.IsUsingCustomCompositeOrder());
     mSequenceNum = aOther.mSequenceNum;
   }
 
-  void QueueEvents(EventArray& aEventsToDispatch);
-
   // Returns the element or pseudo-element whose animation-name property
   // this CSSAnimation corresponds to (if any). This is used for determining
   // the relative composite order of animations generated from CSS markup.
   //
   // Typically this will be the same as the target element of the keyframe
   // effect associated with this animation. However, it can differ in the
   // following circumstances:
   //
@@ -158,19 +156,19 @@ public:
   bool mInEffectForCascadeResults;
 
 protected:
   virtual ~CSSAnimation()
   {
     MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
                                         "before a CSS animation is destroyed");
   }
-  virtual css::CommonAnimationManager* GetAnimationManager() const override;
+  virtual CommonAnimationManager* GetAnimationManager() const override;
 
-  static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
+  void QueueEvents();
 
   nsString mAnimationName;
 
   // The (pseudo-)element whose computed animation-name refers to this
   // animation (if any).
   OwningElementRef mOwningElement;
 
   // When combining animation-play-state with play() / pause() the following
@@ -233,29 +231,26 @@ protected:
   // whose start we last notified on.
   uint64_t mPreviousPhaseOrIteration;
 };
 
 } /* namespace dom */
 } /* namespace mozilla */
 
 class nsAnimationManager final
-  : public mozilla::css::CommonAnimationManager
+  : public mozilla::CommonAnimationManager
 {
 public:
   explicit nsAnimationManager(nsPresContext *aPresContext)
-    : mozilla::css::CommonAnimationManager(aPresContext)
+    : mozilla::CommonAnimationManager(aPresContext)
   {
   }
 
-  void UpdateStyleAndEvents(mozilla::AnimationCollection* aEA,
-                            mozilla::TimeStamp aRefreshTime,
-                            mozilla::EnsureStyleRuleFlags aFlags);
-  void QueueEvents(mozilla::AnimationCollection* aEA,
-                   mozilla::EventArray &aEventsToDispatch);
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsAnimationManager)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   void MaybeUpdateCascadeResults(mozilla::AnimationCollection* aCollection);
 
   // nsIStyleRuleProcessor (parts)
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE override;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE override;
@@ -275,59 +270,63 @@ public:
    *
    * aStyleContext may be a style context for aElement or for its
    * :before or :after pseudo-element.
    */
   nsIStyleRule* CheckAnimationRule(nsStyleContext* aStyleContext,
                                    mozilla::dom::Element* aElement);
 
   /**
+   * Add a pending event.
+   */
+  void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo)
+  {
+    mEventDispatcher.QueueEvent(
+      mozilla::Forward<mozilla::AnimationEventInfo>(aEventInfo));
+  }
+
+  /**
    * Dispatch any pending events.  We accumulate animationend and
    * animationiteration events only during refresh driver notifications
    * (and dispatch them at the end of such notifications), but we
    * accumulate animationstart events at other points when style
    * contexts are created.
    */
-  void DispatchEvents() {
-    // Fast-path the common case: no events
-    if (!mPendingEvents.IsEmpty()) {
-      DoDispatchEvents();
-    }
-  }
+  void DispatchEvents()  { mEventDispatcher.DispatchEvents(mPresContext); }
+  void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 
 protected:
+  virtual ~nsAnimationManager() {}
+
   virtual nsIAtom* GetAnimationsAtom() override {
     return nsGkAtoms::animationsProperty;
   }
   virtual nsIAtom* GetAnimationsBeforeAtom() override {
     return nsGkAtoms::animationsOfBeforeProperty;
   }
   virtual nsIAtom* GetAnimationsAfterAtom() override {
     return nsGkAtoms::animationsOfAfterProperty;
   }
   virtual bool IsAnimationManager() override {
     return true;
   }
 
+  mozilla::DelayedEventDispatcher<mozilla::AnimationEventInfo> mEventDispatcher;
+
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
                        mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPtrArray& aAnimations);
   bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
                       aSegments,
                     nsCSSProperty aProperty,
                     const mozilla::StyleAnimation& aAnimation,
                     float aFromKey, nsStyleContext* aFromContext,
                     mozilla::css::Declaration* aFromDeclaration,
                     float aToKey, nsStyleContext* aToContext);
 
   static void UpdateCascadeResults(nsStyleContext* aStyleContext,
                                    mozilla::AnimationCollection*
                                      aElementAnimations);
-
-  // The guts of DispatchEvents
-  void DoDispatchEvents();
-
-  mozilla::EventArray mPendingEvents;
 };
 
 #endif /* !defined(nsAnimationManager_h_) */
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -7,25 +7,23 @@
 /* Code to start and animate CSS transitions. */
 
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "mozilla/dom/CSSTransitionBinding.h"
 
 #include "nsIContent.h"
 #include "nsStyleContext.h"
-#include "nsCSSProps.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
 #include "nsRuleWalker.h"
 #include "nsCSSPropertySet.h"
 #include "mozilla/EventDispatcher.h"
-#include "mozilla/ContentEvents.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsCSSProps.h"
 #include "nsDisplayList.h"
@@ -40,21 +38,16 @@ using mozilla::dom::Animation;
 using mozilla::dom::KeyframeEffectReadOnly;
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 double
 ElementPropertyTransition::CurrentValuePortion() const
 {
-  // It would be easy enough to handle finished transitions by using a
-  // progress of 1 but currently we should not be called for finished
-  // transitions.
-  MOZ_ASSERT(!IsFinishedTransition(),
-             "Getting the value portion of a finished transition");
   MOZ_ASSERT(!GetLocalTime().IsNull(),
              "Getting the value portion of an animation that's not being "
              "sampled");
 
   // Transitions use a fill mode of 'backwards' so GetComputedTiming will
   // never return a null time progress due to being *before* the animation
   // interval. However, it might be possible that we're behind on flushing
   // causing us to get called *after* the animation interval. So, just in
@@ -69,19 +62,17 @@ ElementPropertyTransition::CurrentValueP
   MOZ_ASSERT(mProperties.Length() == 1,
              "Should have one animation property for a transition");
   MOZ_ASSERT(mProperties[0].mSegments.Length() == 1,
              "Animation property should have one segment for a transition");
   return mProperties[0].mSegments[0].mTimingFunction
          .GetValue(computedTiming.mProgress);
 }
 
-/*****************************************************************************
- * CSSTransition                                                             *
- *****************************************************************************/
+////////////////////////// CSSTransition ////////////////////////////
 
 JSObject*
 CSSTransition::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::CSSTransitionBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
@@ -116,16 +107,52 @@ CSSTransition::GetAnimationManager() con
   nsPresContext* context = GetPresContext();
   if (!context) {
     return nullptr;
   }
 
   return context->TransitionManager();
 }
 
+void
+CSSTransition::QueueEvents()
+{
+  AnimationPlayState playState = PlayState();
+  bool newlyFinished = !mWasFinishedOnLastTick &&
+                       playState == AnimationPlayState::Finished;
+  mWasFinishedOnLastTick = playState == AnimationPlayState::Finished;
+
+  if (!newlyFinished || !mEffect || !mOwningElement.IsSet()) {
+    return;
+  }
+
+  dom::Element* owningElement;
+  nsCSSPseudoElements::Type owningPseudoType;
+  mOwningElement.GetElement(owningElement, owningPseudoType);
+  MOZ_ASSERT(owningElement, "Owning element should be set");
+
+  nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
+  if (!presContext) {
+    return;
+  }
+  nsTransitionManager* manager = presContext->TransitionManager();
+
+  manager->QueueEvent(
+    TransitionEventInfo(owningElement, TransitionProperty(),
+                        mEffect->Timing().mIterationDuration,
+                        owningPseudoType));
+}
+
+void
+CSSTransition::Tick()
+{
+  Animation::Tick();
+  QueueEvents();
+}
+
 nsCSSProperty
 CSSTransition::TransitionProperty() const
 {
   // FIXME: Once we support replacing/removing the effect (bug 1049975)
   // we'll need to store the original transition property so we keep
   // returning the same value in that case.
   dom::KeyframeEffectReadOnly* effect = GetEffect();
   MOZ_ASSERT(effect && effect->AsTransition(),
@@ -171,19 +198,27 @@ CSSTransition::HasLowerCompositeOrderTha
     return mSequenceNum < otherTransition->mSequenceNum;
   }
 
   // 5. (Same transition generation): Sort by transition property
   return nsCSSProps::GetStringValue(TransitionProperty()) <
          nsCSSProps::GetStringValue(otherTransition->TransitionProperty());
 }
 
-/*****************************************************************************
- * nsTransitionManager                                                       *
- *****************************************************************************/
+////////////////////////// nsTransitionManager ////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION(nsTransitionManager, mEventDispatcher)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTransitionManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTransitionManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTransitionManager)
+  NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
 void
 nsTransitionManager::StyleContextChanged(dom::Element *aElement,
                                          nsStyleContext *aOldStyleContext,
                                          nsRefPtr<nsStyleContext>* aNewStyleContext /* inout */)
 {
   nsStyleContext* newStyleContext = *aNewStyleContext;
 
@@ -401,17 +436,17 @@ nsTransitionManager::StyleContextChanged
           // did not start a new transition (because delay and
           // duration are both zero, or because the new value is not
           // interpolable); a new transition would have segment.mToValue
           // matching currentValue
           !ExtractComputedValueForTransition(prop.mProperty, afterChangeStyle,
                                              currentValue) ||
           currentValue != segment.mToValue) {
         // stop the transition
-        if (!anim->GetEffect()->IsFinishedTransition()) {
+        if (anim->HasCurrentEffect()) {
           collection->UpdateAnimationGeneration(mPresContext);
         }
         anim->CancelFromStyle();
         animations.RemoveElementAt(i);
       }
     } while (i != 0);
 
     if (animations.IsEmpty()) {
@@ -530,17 +565,17 @@ nsTransitionManager::ConsiderStartingTra
              "Should have one animation property segment for a transition");
   if (haveCurrentTransition && haveValues &&
       oldPT->Properties()[0].mSegments[0].mToValue == endValue) {
     // GetAnimationRule already called RestyleForAnimation.
     return;
   }
 
   if (!shouldAnimate) {
-    if (haveCurrentTransition && !oldPT->IsFinishedTransition()) {
+    if (haveCurrentTransition) {
       // We're in the middle of a transition, and just got a non-transition
       // style change to something that we can't animate.  This might happen
       // because we got a non-transition style change changing to the current
       // in-progress value (which is particularly easy to cause when we're
       // currently in the 'transition-delay').  It also might happen because we
       // just got a style change to a value that can't be interpolated.
       AnimationPtrArray& animations = aElementTransitions->mAnimations;
       animations[currentIndex]->CancelFromStyle();
@@ -567,17 +602,17 @@ nsTransitionManager::ConsiderStartingTra
   }
 
   StyleAnimationValue startForReversingTest = startValue;
   double reversePortion = 1.0;
 
   // If the new transition reverses an existing one, we'll need to
   // handle the timing differently.
   if (haveCurrentTransition &&
-      !oldPT->IsFinishedTransition() &&
+      aElementTransitions->mAnimations[currentIndex]->HasCurrentEffect() &&
       oldPT->mStartForReversingTest == endValue) {
     // Compute the appropriate negative transition-delay such that right
     // now we'd end up at the current position.
     double valuePortion =
       oldPT->CurrentValuePortion() * oldPT->mReversePortion +
       (1.0 - oldPT->mReversePortion);
     // A timing function with negative y1 (or y2!) might make
     // valuePortion negative.  In this case, we still want to apply our
@@ -700,25 +735,25 @@ nsTransitionManager::PruneCompletedTrans
   // FIXME (bug 1158431): Really, we should also cancel running
   // transitions whose destination doesn't match as well.
   AnimationPtrArray& animations = collection->mAnimations;
   size_t i = animations.Length();
   MOZ_ASSERT(i != 0, "empty transitions list?");
   do {
     --i;
     Animation* anim = animations[i];
-    dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
 
-    if (!effect->IsFinishedTransition()) {
+    if (anim->HasCurrentEffect()) {
       continue;
     }
 
-    MOZ_ASSERT(effect && effect->Properties().Length() == 1,
+    dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
+    MOZ_ASSERT(effect->Properties().Length() == 1,
                "Should have one animation property for a transition");
-    MOZ_ASSERT(effect && effect->Properties()[0].mSegments.Length() == 1,
+    MOZ_ASSERT(effect->Properties()[0].mSegments.Length() == 1,
                "Animation property should have one segment for a transition");
     const AnimationProperty& prop = effect->Properties()[0];
     const AnimationPropertySegment& segment = prop.mSegments[0];
 
     // Since effect is a finished transition, we know it didn't
     // influence style.
     StyleAnimationValue currentValue;
     if (!ExtractComputedValueForTransition(prop.mProperty, aNewStyleContext,
@@ -836,50 +871,28 @@ nsTransitionManager::UpdateCascadeResult
 /*
  * nsIStyleRuleProcessor implementation
  */
 
 /* virtual */ size_t
 nsTransitionManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
+
+  // Measurement of the following members may be added later if DMD finds it is
+  // worthwhile:
+  // - mEventDispatcher
 }
 
 /* virtual */ size_t
 nsTransitionManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
-struct TransitionEventInfo {
-  nsCOMPtr<nsIContent> mElement;
-  InternalTransitionEvent mEvent;
-
-  TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty,
-                      TimeDuration aDuration, const nsAString& aPseudoElement)
-    : mElement(aElement)
-    , mEvent(true, NS_TRANSITION_END)
-  {
-    // XXX Looks like nobody initialize WidgetEvent::time
-    mEvent.propertyName =
-      NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
-    mEvent.elapsedTime = aDuration.ToSeconds();
-    mEvent.pseudoElement = aPseudoElement;
-  }
-
-  // InternalTransitionEvent doesn't support copy-construction, so we need
-  // to ourselves in order to work with nsTArray
-  TransitionEventInfo(const TransitionEventInfo &aOther)
-    : mElement(aOther.mElement)
-    , mEvent(true, NS_TRANSITION_END)
-  {
-    mEvent.AssignTransitionEventData(aOther.mEvent, false);
-  }
-};
-
 /* virtual */ void
 nsTransitionManager::WillRefresh(mozilla::TimeStamp aTime)
 {
   MOZ_ASSERT(mPresContext,
              "refresh driver should not notify additional observers "
              "after pres context has been destroyed");
   if (!mPresContext->GetPresShell()) {
     // Someone might be keeping mPresContext alive past the point
@@ -896,17 +909,16 @@ nsTransitionManager::WillRefresh(mozilla
 void
 nsTransitionManager::FlushTransitions(FlushFlags aFlags)
 {
   if (PR_CLIST_IS_EMPTY(&mElementCollections)) {
     // no transitions, leave early
     return;
   }
 
-  nsTArray<TransitionEventInfo> events;
   TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
   bool didThrottle = false;
   // Trim transitions that have completed, post restyle events for frames that
   // are still transitioning, and start transitions with delays.
   {
     PRCList *next = PR_LIST_HEAD(&mElementCollections);
     while (next != &mElementCollections) {
       AnimationCollection* collection = static_cast<AnimationCollection*>(next);
@@ -920,68 +932,34 @@ nsTransitionManager::FlushTransitions(Fl
           AnimationCollection::CanAnimateFlags(0)) &&
         collection->CanThrottleAnimation(now);
 
       MOZ_ASSERT(collection->mElement->GetCrossShadowCurrentDoc() ==
                    mPresContext->Document(),
                  "Element::UnbindFromTree should have "
                  "destroyed the element transitions object");
 
-      size_t i = collection->mAnimations.Length();
-      MOZ_ASSERT(i != 0, "empty transitions list?");
-      bool transitionStartedOrEnded = false;
-      do {
-        --i;
-        Animation* anim = collection->mAnimations[i];
-        if (!anim->GetEffect()->IsFinishedTransition()) {
-          MOZ_ASSERT(anim->GetEffect(), "Transitions should have an effect");
-          ComputedTiming computedTiming =
-            anim->GetEffect()->GetComputedTiming();
-          if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) {
-            nsCSSProperty prop =
-              anim->GetEffect()->AsTransition()->TransitionProperty();
-            TimeDuration duration =
-              anim->GetEffect()->Timing().mIterationDuration;
-            events.AppendElement(
-              TransitionEventInfo(collection->mElement, prop,
-                                  duration,
-                                  collection->PseudoElement()));
-
-            // Leave this transition in the list for one more refresh
-            // cycle, since we haven't yet processed its style change, and
-            // if we also have (already, or will have from processing
-            // transitionend events or other refresh driver notifications)
-            // a non-animation style change that would affect it, we need
-            // to know not to start a new transition for the transition
-            // from the almost-completed value to the final value.
-            anim->GetEffect()->SetIsFinishedTransition(true);
-            collection->UpdateAnimationGeneration(mPresContext);
-            transitionStartedOrEnded = true;
-          } else if ((computedTiming.mPhase ==
-                      ComputedTiming::AnimationPhase_Active) &&
-                     canThrottleTick &&
-                     !anim->IsRunningOnCompositor()) {
-            // Start a transition with a delay where we should start the
-            // transition proper.
-            collection->UpdateAnimationGeneration(mPresContext);
-            transitionStartedOrEnded = true;
-          }
-        }
-      } while (i != 0);
+      // Look for any transitions that can't be throttled because they
+      // may be starting or stopping
+      for (auto iter = collection->mAnimations.cbegin();
+           canThrottleTick && iter != collection->mAnimations.cend();
+           ++iter) {
+        canThrottleTick &= (*iter)->CanThrottle();
+      }
 
       // We need to restyle even if the transition rule no longer
       // applies (in which case we just made it not apply).
       MOZ_ASSERT(collection->mElementProperty ==
                    nsGkAtoms::transitionsProperty ||
                  collection->mElementProperty ==
                    nsGkAtoms::transitionsOfBeforeProperty ||
                  collection->mElementProperty ==
                    nsGkAtoms::transitionsOfAfterProperty,
                  "Unexpected element property; might restyle too much");
-      if (!canThrottleTick || transitionStartedOrEnded) {
+      if (!canThrottleTick) {
         collection->PostRestyleForAnimation(mPresContext);
       } else {
         didThrottle = true;
       }
 
       if (collection->mAnimations.IsEmpty()) {
         collection->Destroy();
         // |collection| is now a dangling pointer!
@@ -990,18 +968,9 @@ nsTransitionManager::FlushTransitions(Fl
     }
   }
 
   if (didThrottle) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
   MaybeStartOrStopObservingRefreshDriver();
-
-  for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) {
-    TransitionEventInfo &info = events[i];
-    EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
-
-    if (!mPresContext) {
-      break;
-    }
-  }
 }
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -4,20 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Code to start and animate CSS transitions. */
 
 #ifndef nsTransitionManager_h_
 #define nsTransitionManager_h_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/ContentEvents.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/KeyframeEffect.h"
 #include "AnimationCommon.h"
+#include "nsCSSProps.h"
 #include "nsCSSPseudoElements.h"
 
 class nsIGlobalObject;
 class nsStyleContext;
 class nsPresContext;
 class nsCSSPropertySet;
 
 namespace mozilla {
@@ -77,16 +79,17 @@ struct ElementPropertyTransition : publi
 
 namespace dom {
 
 class CSSTransition final : public Animation
 {
 public:
  explicit CSSTransition(nsIGlobalObject* aGlobal)
     : dom::Animation(aGlobal)
+    , mWasFinishedOnLastTick(false)
   {
   }
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   CSSTransition* AsCSSTransition() override { return this; }
   const CSSTransition* AsCSSTransition() const override { return this; }
@@ -110,16 +113,18 @@ public:
 
   void CancelFromStyle() override
   {
     mOwningElement = OwningElementRef();
     Animation::CancelFromStyle();
     MOZ_ASSERT(mSequenceNum == kUnsequenced);
   }
 
+  void Tick() override;
+
   nsCSSProperty TransitionProperty() const;
 
   bool HasLowerCompositeOrderThan(const Animation& aOther) const override;
   bool IsUsingCustomCompositeOrder() const override
   {
     return mOwningElement.IsSet();
   }
 
@@ -157,36 +162,71 @@ public:
 
 protected:
   virtual ~CSSTransition()
   {
     MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared "
                                         "before a CSS transition is destroyed");
   }
 
-  virtual css::CommonAnimationManager* GetAnimationManager() const override;
+  virtual CommonAnimationManager* GetAnimationManager() const override;
+
+  void QueueEvents();
 
   // The (pseudo-)element whose computed transition-property refers to this
   // transition (if any).
   OwningElementRef mOwningElement;
+
+  bool mWasFinishedOnLastTick;
 };
 
 } // namespace dom
+
+struct TransitionEventInfo {
+  nsCOMPtr<nsIContent> mElement;
+  InternalTransitionEvent mEvent;
+
+  TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty,
+                      TimeDuration aDuration,
+                      nsCSSPseudoElements::Type aPseudoType)
+    : mElement(aElement)
+    , mEvent(true, NS_TRANSITION_END)
+  {
+    // XXX Looks like nobody initialize WidgetEvent::time
+    mEvent.propertyName =
+      NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
+    mEvent.elapsedTime = aDuration.ToSeconds();
+    mEvent.pseudoElement = AnimationCollection::PseudoTypeAsString(aPseudoType);
+  }
+
+  // InternalTransitionEvent doesn't support copy-construction, so we need
+  // to ourselves in order to work with nsTArray
+  TransitionEventInfo(const TransitionEventInfo &aOther)
+    : mElement(aOther.mElement)
+    , mEvent(true, NS_TRANSITION_END)
+  {
+    mEvent.AssignTransitionEventData(aOther.mEvent, false);
+  }
+};
+
 } // namespace mozilla
 
 class nsTransitionManager final
-  : public mozilla::css::CommonAnimationManager
+  : public mozilla::CommonAnimationManager
 {
 public:
   explicit nsTransitionManager(nsPresContext *aPresContext)
-    : mozilla::css::CommonAnimationManager(aPresContext)
+    : mozilla::CommonAnimationManager(aPresContext)
     , mInAnimationOnlyStyleUpdate(false)
   {
   }
 
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsTransitionManager)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
   typedef mozilla::AnimationCollection AnimationCollection;
 
   /**
    * StyleContextChanged
    *
    * To be called from nsFrameManager::ReResolveStyleContext when the
    * style of an element has changed, to initiate transitions from
    * that style change.  For style contexts with :before and :after
@@ -234,17 +274,28 @@ public:
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE override;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) override;
 
   void FlushTransitio