Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 30 Apr 2015 13:56:09 -0400
changeset 273159 7723b15ea695e321e6acda04beef9bc98728dd13
parent 273074 985244566b19fb277fd4fc900eb615e0f01c21d9 (current diff)
parent 273158 da60d90cc685bfc079c54cd4abcd1c888c9e843c (diff)
child 273162 4869fe8e1487bc202bd1166c94dc73c8eb01b0db
child 273214 2f1bfe2f725ae198f09f65ecfbcf544bb8a4cb5f
child 273251 fe44cfb27e75ac966193b7128024d0939a676af3
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
7723b15ea695 / 40.0a1 / 20150501030205 / files
nightly linux64
7723b15ea695 / 40.0a1 / 20150501030205 / files
nightly mac
7723b15ea695 / 40.0a1 / 20150501030205 / files
nightly win32
7723b15ea695 / 40.0a1 / 20150501030205 / files
nightly win64
7723b15ea695 / 40.0a1 / 20150501030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
CLOBBER
configure.in
dom/animation/AnimationEffectReadonly.cpp
dom/animation/AnimationEffectReadonly.h
dom/webidl/AnimationEffectReadonly.webidl
extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/dupe-dictionary.pl
extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/hunspell-en_US-20081205.dic
extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/merge-dictionaries
extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
testing/marionette/client/marionette/tests/unit/test_submit.py
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1154356: escape variable name in Declaration::AppendVariableAndValueToString;
+Bug 1159082 - Renaming *Readonly animation interfaces to *ReadOnly causes
+build bustage on case-insensitive filesystems.
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -313,16 +313,21 @@ pref("media.cache_size", 4096);    // 4M
 // below 10s of buffered data.
 pref("media.cache_resume_threshold", 10);
 pref("media.cache_readahead_limit", 30);
 
 #ifdef MOZ_FMP4
 // Enable/Disable Gonk Decoder Module
 pref("media.fragmented-mp4.gonk.enabled", true);
 #endif
+
+//Encrypted media extensions.
+pref("media.eme.enabled", true);
+pref("media.eme.apiVisible", true);
+
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.decode-only-on-draw.enabled", false);
 pref("image.mem.allow_locking_in_content_processes", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -459,28 +459,32 @@
 
       <!-- UI for option to report certificate errors to Mozilla. Removed on
            init for other error types .-->
       <div id="certificateErrorReporting">
         <a id="showCertificateErrorReportingPanel" href="#">&errorReporting.title;<span class="downArrow"> ▼</span></a>
       </div>
 
       <div id="certificateErrorReportingPanel">
-        <p>&errorReporting.longDesc;</p>
-        <p>
-          <input type="checkbox" id="automaticallyReportInFuture" />
-          <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic;</label>
-        </p>
-        <a href="https://support.mozilla.org/kb/tls-error-reports" id="learnMoreLink" target="new">&errorReporting.learnMore;</a>
-        <span id="reportingState">
-          <button id="reportCertificateError">&errorReporting.report;</button>
-          <button id="reportCertificateErrorRetry">&errorReporting.tryAgain;</button>
-          <span id="reportSendingMessage">&errorReporting.sending;</span>
-          <span id="reportSentMessage">&errorReporting.sent;</span>
-        </span>
+        <div id="certificateErrorReportingDescription">
+          <p>&errorReporting.longDesc;</p>
+          <p>
+            <input type="checkbox" id="automaticallyReportInFuture" />
+            <label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic;</label>
+          </p>
+        </div>
+        <div id="errorStatePanel">
+          <a href="https://support.mozilla.org/kb/tls-error-reports" id="learnMoreLink" target="new">&errorReporting.learnMore;</a>
+          <span id="reportingState">
+            <button id="reportCertificateError">&errorReporting.report;</button>
+            <button id="reportCertificateErrorRetry">&errorReporting.tryAgain;</button>
+            <span id="reportSendingMessage">&errorReporting.sending;</span>
+            <span id="reportSentMessage">&errorReporting.sent;</span>
+          </span>
+        </div>
       </div>
 
     </div>
 
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
     - LOAD_BACKGROUND, which means that onload handlers will not be executed.
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -23,20 +23,16 @@
                           key="key_newNavigator"
                           command="cmd_newNavigator"/>
                 <menuitem id="menu_newPrivateWindow"
                           label="&newPrivateWindow.label;"
                           accesskey="&newPrivateWindow.accesskey;"
                           command="Tools:PrivateBrowsing"
                           key="key_privatebrowsing"/>
 #ifdef E10S_TESTING_ONLY
-                <menuitem id="menu_newRemoteWindow"
-                          label="New e10s Window"
-                          hidden="true"
-                          command="Tools:RemoteWindow"/>
                 <menuitem id="menu_newNonRemoteWindow"
                           label="New Non-e10s Window"
                           hidden="true"
                           command="Tools:NonRemoteWindow"/>
 #endif
 #ifdef MAC_NON_BROWSER_WINDOW
                 <menuitem id="menu_openLocation"
                           label="&openLocationCmd.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -109,18 +109,16 @@
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
     <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
     <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});" reserved="true"/>
 #ifdef E10S_TESTING_ONLY
-    <command id="Tools:RemoteWindow"
-      oncommand="OpenBrowserWindow({remote: true});"/>
     <command id="Tools:NonRemoteWindow"
       oncommand="OpenBrowserWindow({remote: false});"/>
 #endif
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Social:SharePage" oncommand="SocialShare.sharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7225,20 +7225,18 @@ let gRemoteTabsUI = {
 #ifdef XP_MACOSX
     if (Services.prefs.getBoolPref("layers.acceleration.disabled")) {
       // On OS X, "Disable Hardware Acceleration" also disables OMTC and forces
       // a fallback to Basic Layers. This is incompatible with e10s.
       return;
     }
 #endif
 
-    let newRemoteWindow = document.getElementById("menu_newRemoteWindow");
     let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
     let autostart = Services.appinfo.browserTabsRemoteAutostart;
-    newRemoteWindow.hidden = autostart;
     newNonRemoteWindow.hidden = !autostart;
   }
 };
 
 /**
  * Switch to a tab that has a given URI, and focusses its browser window.
  * If a matching tab is in this window, it will be switched to. Otherwise, other
  * windows will be searched.
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -203,16 +203,25 @@ let AboutNetErrorListener = {
               enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
             automatic: automatic
             })
           }
     ));
     if (automatic) {
       this.onSendReport(evt);
     }
+    // hide parts of the UI we don't need yet
+    let contentDoc = content.document;
+
+    let reportSendingMsg = contentDoc.getElementById("reportSendingMessage");
+    let reportSentMsg = contentDoc.getElementById("reportSentMessage");
+    let retryBtn = contentDoc.getElementById("reportCertificateErrorRetry");
+    reportSendingMsg.style.display = "none";
+    reportSentMsg.style.display = "none";
+    retryBtn.style.display = "none";
   },
 
   onSetAutomatic: function(evt) {
     sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
         automatic: evt.detail
       });
   },
 
@@ -229,33 +238,32 @@ let AboutNetErrorListener = {
       // document - we'll compare on document URI
       if (contentDoc.documentURI === message.data.documentURI) {
         switch(message.data.reportStatus) {
         case "activity":
           // Hide the button that was just clicked
           reportBtn.style.display = "none";
           retryBtn.style.display = "none";
           reportSentMsg.style.display = "none";
-          reportSendingMsg.style.display = "inline";
+          reportSendingMsg.style.removeProperty("display");
           break;
         case "error":
           // show the retry button
-          retryBtn.style.display = "inline";
+          retryBtn.style.removeProperty("display");
           reportSendingMsg.style.display = "none";
           break;
         case "complete":
           // Show a success indicator
-          reportSentMsg.style.display = "inline";
+          reportSentMsg.style.removeProperty("display");
           reportSendingMsg.style.display = "none";
           break;
         }
       }
     });
 
-
     let failedChannel = docShell.failedChannel;
     let location = contentDoc.location.href;
 
     let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                      .getService(Ci.nsISerializationHelper);
 
     let serializable =  docShell.failedChannel.securityInfo
                                 .QueryInterface(Ci.nsITransportSecurityInfo)
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -526,48 +526,42 @@
         <parameter name="aBrowser"/>
         <parameter name="aMethod"/>
         <parameter name="aArguments"/>
         <parameter name="aCallGlobalListeners"/>
         <parameter name="aCallTabsListeners"/>
         <body><![CDATA[
           var rv = true;
 
-          if (!aBrowser)
-            aBrowser = this.mCurrentBrowser;
-
-          if (aCallGlobalListeners != false &&
-              aBrowser == this.mCurrentBrowser) {
-            for (let p of this.mProgressListeners) {
+          function callListeners(listeners, args) {
+            for (let p of listeners) {
               if (aMethod in p) {
                 try {
-                  if (!p[aMethod].apply(p, aArguments))
+                  if (!p[aMethod].apply(p, args))
                     rv = false;
                 } catch (e) {
                   // don't inhibit other listeners
                   Components.utils.reportError(e);
                 }
               }
             }
           }
 
+          if (!aBrowser)
+            aBrowser = this.mCurrentBrowser;
+
+          if (aCallGlobalListeners != false &&
+              aBrowser == this.mCurrentBrowser) {
+            callListeners(this.mProgressListeners, aArguments);
+          }
+
           if (aCallTabsListeners != false) {
             aArguments.unshift(aBrowser);
 
-            for (let p of this.mTabsProgressListeners) {
-              if (aMethod in p) {
-                try {
-                  if (!p[aMethod].apply(p, aArguments))
-                    rv = false;
-                } catch (e) {
-                  // don't inhibit other listeners
-                  Components.utils.reportError(e);
-                }
-              }
-            }
+            callListeners(this.mTabsProgressListeners, aArguments);
           }
 
           return rv;
         ]]></body>
       </method>
 
       <!-- A web progress listener object definition for a given tab. -->
       <method name="mTabProgressListener">
@@ -1513,23 +1507,21 @@
             // keep the old one. Re-set it explicitly after unbinding from DOM.
             aBrowser._permanentKey = permanentKey;
             parent.appendChild(aBrowser);
 
             // Restore the progress listener.
             aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
             if (aShouldBeRemote) {
-              tab.setAttribute("remote", "true");
               // Switching the browser to be remote will connect to a new child
               // process so the browser can no longer be considered to be
               // crashed.
               tab.removeAttribute("crashed");
             } else {
-              tab.removeAttribute("remote");
               aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
             }
 
             if (wasActive)
               aBrowser.focus();
 
             let evt = document.createEvent("Events");
             evt.initEvent("TabRemotenessChange", true, false);
@@ -1736,18 +1728,16 @@
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             // The new browser should be remote if this is an e10s window and
             // the uri to load can be loaded remotely.
             let remote = gMultiProcessBrowser &&
                          !aForceNotRemote &&
                          E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
-            if (remote)
-              t.setAttribute("remote", "true");
 
             this.tabContainer._unlockTabSizing();
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
             // transition in that case.
             let animate = !aSkipAnimation &&
                           this.tabContainer.getAttribute("overflow") != "true" &&
@@ -3680,19 +3670,21 @@
         <parameter name="event"/>
         <body><![CDATA[
           event.stopPropagation();
           var tab = document.tooltipNode;
           if (tab.localName != "tab") {
             event.preventDefault();
             return;
           }
-          event.target.setAttribute("label", tab.mOverCloseButton ?
-                                             tab.getAttribute("closetabtext") :
-                                             tab.getAttribute("label"));
+          event.target.setAttribute("label",
+                                    tab.mOverCloseButton ?
+                                    tab.getAttribute("closetabtext") :
+                                    tab.getAttribute("label") +
+                                      (this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : ""));
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
             case "keydown":
--- a/browser/base/content/test/general/browser_bug623155.js
+++ b/browser/base/content/test/general/browser_bug623155.js
@@ -110,17 +110,17 @@ function delayed(aIsSelectedTab) {
 
   ok(isRedirectedURISpec(content.location.href),
      "The content area is redirected. aIsSelectedTab:" + aIsSelectedTab);
   is(gURLBar.value, content.location.href,
      "The URL bar shows the content URI. aIsSelectedTab:" + aIsSelectedTab);
 
   if (!aIsSelectedTab) {
     // If this was a background request, go on a foreground request.
-    content.location = REDIRECT_FROM + "#FG";
+    gBrowser.selectedBrowser.loadURI(REDIRECT_FROM + "#FG");
   }
   else {
     // Othrewise, nothing to do remains.
     finish();
   }
 }
 
 /* Cleanup */
--- a/browser/base/content/test/general/browser_e10s_switchbrowser.js
+++ b/browser/base/content/test/general/browser_e10s_switchbrowser.js
@@ -102,158 +102,158 @@ let forward = Task.async(function*() {
   gBrowser.goForward();
   yield waitForDocLoadComplete();
   gExpectedHistory.index++;
 });
 
 // Tests that navigating from a page that should be in the remote process and
 // a page that should be in the main process works and retains history
 add_task(function* test_navigation() {
-  let expectedRemote = gMultiProcessBrowser ? "true" : "";
+  let expectedRemote = gMultiProcessBrowser;
 
   info("1");
   // Create a tab and load a remote page in it
   gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
   let {permanentKey} = gBrowser.selectedBrowser;
   yield waitForLoad("http://example.org/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   info("2");
   // Load another page
   yield waitForLoad("http://example.com/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("3");
   // Load a non-remote page
   yield waitForLoad("about:robots");
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("4");
   // Load a remote page
   yield waitForLoad("http://example.org/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("5");
   yield back();
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("6");
   yield back();
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("7");
   yield forward();
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("8");
   yield forward();
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("9");
   yield back();
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("10");
   // Load a new remote page, this should replace the last history entry
   gExpectedHistory.entries.splice(gExpectedHistory.entries.length - 1, 1);
   yield waitForLoad("http://example.com/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
   yield check_history();
 
   info("11");
   gBrowser.removeCurrentTab();
   clear_history();
 });
 
 // Tests that calling gBrowser.loadURI or browser.loadURI to load a page in a
 // different process updates the browser synchronously
 add_task(function* test_synchronous() {
-  let expectedRemote = gMultiProcessBrowser ? "true" : "";
+  let expectedRemote = gMultiProcessBrowser;
 
   info("1");
   // Create a tab and load a remote page in it
   gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
   let {permanentKey} = gBrowser.selectedBrowser;
   yield waitForLoad("http://example.org/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   info("2");
   // Load another page
   info("Loading about:robots");
   gBrowser.selectedBrowser.loadURI("about:robots");
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   yield waitForDocLoadComplete();
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   info("3");
   // Load the remote page again
   info("Loading http://example.org/" + DUMMY_PATH);
   gBrowser.loadURI("http://example.org/" + DUMMY_PATH);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   yield waitForDocLoadComplete();
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   is(gBrowser.selectedBrowser.permanentKey, permanentKey, "browser.permanentKey is still the same");
 
   info("4");
   gBrowser.removeCurrentTab();
   clear_history();
 });
 
 // Tests that load flags are correctly passed through to the child process with
 // normal loads
 add_task(function* test_loadflags() {
-  let expectedRemote = gMultiProcessBrowser ? "true" : "";
+  let expectedRemote = gMultiProcessBrowser;
 
   info("1");
   // Create a tab and load a remote page in it
   gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
   yield waitForLoadWithFlags("about:robots");
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   yield check_history();
 
   info("2");
   // Load a page in the remote process with some custom flags
   yield waitForLoadWithFlags("http://example.com/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   yield check_history();
 
   info("3");
   // Load a non-remote page
   yield waitForLoadWithFlags("about:robots");
-  is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, false, "Remote attribute should be correct");
   yield check_history();
 
   info("4");
   // Load another remote page
   yield waitForLoadWithFlags("http://example.org/" + DUMMY_PATH, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY);
-  is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
+  is(gBrowser.selectedBrowser.isRemoteBrowser, expectedRemote, "Remote attribute should be correct");
   yield check_history();
 
   is(gExpectedHistory.entries.length, 2, "Should end with the right number of history entries");
 
   info("5");
   gBrowser.removeCurrentTab();
   clear_history();
 });
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1212,45 +1212,31 @@ if (Services.prefs.getBoolPref("browser.
     };
 
     CustomizableWidgets.push(pocketButton);
     CustomizableUI.addListener(pocketButton);
   }
 }
 
 #ifdef E10S_TESTING_ONLY
-/**
-  * The e10s button's purpose is to lower the barrier of entry
-  * for our Nightly testers to use e10s windows. We'll be removing it
-  * once remote tabs are enabled. This button should never ever make it
-  * to production. If it does, that'd be bad, and we should all feel bad.
-  */
-let getCommandFunction = function(aOpenRemote) {
-  return function(aEvent) {
-    let win = aEvent.view;
-    if (win && typeof win.OpenBrowserWindow == "function") {
-      win.OpenBrowserWindow({remote: aOpenRemote});
-    }
-  };
-}
-
-let openRemote = !Services.appinfo.browserTabsRemoteAutostart;
-// Like the XUL menuitem counterparts, we hard-code these strings in because
-// this button should never roll into production.
-let buttonLabel = openRemote ? "New e10s Window"
-                              : "New Non-e10s Window";
-
 let e10sDisabled = Services.appinfo.inSafeMode;
 #ifdef XP_MACOSX
 // On OS X, "Disable Hardware Acceleration" also disables OMTC and forces
 // a fallback to Basic Layers. This is incompatible with e10s.
 e10sDisabled |= Services.prefs.getBoolPref("layers.acceleration.disabled");
 #endif
 
-CustomizableWidgets.push({
-  id: "e10s-button",
-  label: buttonLabel,
-  tooltiptext: buttonLabel,
-  disabled: e10sDisabled,
-  defaultArea: CustomizableUI.AREA_PANEL,
-  onCommand: getCommandFunction(openRemote),
-});
+if (Services.appinfo.browserTabsRemoteAutostart) {
+  CustomizableWidgets.push({
+    id: "e10s-button",
+    label: "New Non-e10s Window",
+    tooltiptext: "New Non-e10s Window",
+    disabled: e10sDisabled,
+    defaultArea: CustomizableUI.AREA_PANEL,
+    onCommand: function(aEvent) {
+      let win = aEvent.view;
+      if (win && typeof win.OpenBrowserWindow == "function") {
+        win.OpenBrowserWindow({remote: false});
+      }
+    },
+  });
+}
 #endif
--- a/browser/themes/shared/aboutNetError.css
+++ b/browser/themes/shared/aboutNetError.css
@@ -115,38 +115,34 @@ div#certificateErrorReportingPanel {
 div#certificateErrorReportingPanel:-moz-dir(ltr) {
   left: 34%;
 }
 
 div#certificateErrorReportingPanel:-moz-dir(rtl) {
   right: 0;
 }
 
+#errorStatePanel {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  align-content: space-between;
+  align-items: flex-start;
+}
+
 span#hostname {
   font-weight: bold;
 }
 
 #automaticallyReportInFuture {
   cursor: pointer;
 }
 
-#reportingState {
-  padding-left: 150px;
-}
-
 #reportSendingMessage {
-  position: relative;
-  display: none;
+  /* adjust the line-height to match the link */
+  line-height: 22px;
 }
 
 #reportSentMessage {
-  position: relative;
-  display: none;
+  /* adjust the line-height to match the link */
+  line-height: 22px;
 }
-
-button#reportCertificateError {
-  position: relative;
-}
-
-button#reportCertificateErrorRetry {
-  position: relative;
-  display: none;
-}
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -37,20 +37,16 @@
   margin: 0;
   padding: 0;
 }
 
 .tabbrowser-tab {
   -moz-box-align: stretch;
 }
 
-.tabbrowser-tab[remote] {
-  text-decoration: underline;
-}
-
 /* The selected tab should appear above adjacent tabs, .tabs-newtab-button and the highlight of #nav-bar */
 .tabbrowser-tab[visuallyselected=true] {
   position: relative;
   z-index: 2;
 }
 
 .tab-background-middle {
   -moz-box-flex: 1;
--- a/configure.in
+++ b/configure.in
@@ -1495,19 +1495,21 @@ if test "$GNU_CC"; then
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=int-to-pointer-cast"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=multichar"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=nonnull"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-arith"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-sign"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=return-type"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=sequence-point"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=trigraphs"
+        _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=uninitialized"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=unknown-pragmas"
 
         MOZ_C_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_c_has_werror_non_literal_null_conversion)
+        MOZ_C_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_c_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-unused - lots of violations in third-party code
     #
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-unused"
 
     if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then
@@ -1588,19 +1590,21 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=missing-braces"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=parentheses"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=type-limits"
+        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=uninitialized"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
 
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_cxx_has_werror_non_literal_null_conversion)
+        MOZ_CXX_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_cxx_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
     # -Wno-inline-new-delete - we inline 'new' and 'delete' in mozalloc
     #   for performance reasons, and because GCC and clang accept it (though
     #   clang warns about it).
     #
@@ -6903,24 +6907,21 @@ AC_SUBST(MOZ_STACKWALKING)
 
 dnl ========================================================
 dnl = Disable treating compiler warnings as errors
 dnl ========================================================
 if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
    WARNINGS_AS_ERRORS=''
 elif test "$GNU_CC"; then
     # Prevent the following GCC warnings from being treated as errors:
-    # -Wuninitialized - too many false positives
     # -Wmaybe-uninitialized - too many false positives
     # -Wdeprecated-declarations - we don't want our builds held hostage when a
     #   platform-specific API becomes deprecated.
     # -Wfree-nonheap-object - false positives during PGO
     # -Warray-bounds - false positives depending on optimization
-    MOZ_C_SUPPORTS_WARNING(-W, no-error=uninitialized, ac_c_has_noerror_uninitialized)
-    MOZ_CXX_SUPPORTS_WARNING(-W, no-error=uninitialized, ac_cxx_has_noerror_uninitialized)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=maybe-uninitialized, ac_c_has_noerror_maybe_uninitialized)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=maybe-uninitialized, ac_cxx_has_noerror_maybe_uninitialized)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=deprecated-declarations, ac_c_has_noerror_deprecated_declarations)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=deprecated-declarations, ac_cxx_has_noerror_deprecated_declarations)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=array-bounds, ac_c_has_noerror_array_bounds)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=array-bounds, ac_cxx_has_noerror_array_bounds)
 
     if test -n "$MOZ_PGO"; then
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -34,17 +34,17 @@ Animation::WrapObject(JSContext* aCx, JS
 
 // ---------------------------------------------------------------------------
 //
 // Animation interface:
 //
 // ---------------------------------------------------------------------------
 
 void
-Animation::SetEffect(KeyframeEffectReadonly* aEffect)
+Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
 {
   if (mEffect) {
     mEffect->SetParentTime(Nullable<TimeDuration>());
   }
   mEffect = aEffect;
   if (mEffect) {
     mEffect->SetParentTime(GetCurrentTime());
   }
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -7,17 +7,17 @@
 #define mozilla_dom_Animation_h
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
 #include "mozilla/dom/AnimationBinding.h" // for AnimationPlayState
 #include "mozilla/dom/DocumentTimeline.h" // for DocumentTimeline
-#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadonly
+#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadOnly
 #include "mozilla/dom/Promise.h" // for Promise
 #include "nsCSSProperty.h" // for nsCSSProperty
 
 // X11 has a #define for CurrentTime.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
@@ -81,18 +81,18 @@ public:
    */
   enum class LimitBehavior {
     AutoRewind,
     Continue
   };
 
   // Animation interface methods
 
-  KeyframeEffectReadonly* GetEffect() const { return mEffect; }
-  void SetEffect(KeyframeEffectReadonly* aEffect);
+  KeyframeEffectReadOnly* GetEffect() const { return mEffect; }
+  void SetEffect(KeyframeEffectReadOnly* aEffect);
   DocumentTimeline* Timeline() const { return mTimeline; }
   Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
   void SetStartTime(const Nullable<TimeDuration>& aNewStartTime);
   Nullable<TimeDuration> GetCurrentTime() const;
   void SetCurrentTime(const TimeDuration& aNewCurrentTime);
   double PlaybackRate() const { return mPlaybackRate; }
   void SetPlaybackRate(double aPlaybackRate);
   AnimationPlayState PlayState() const;
@@ -313,17 +313,17 @@ protected:
   StickyTimeDuration EffectEnd() const;
 
   nsIDocument* GetRenderedDocument() const;
   nsPresContext* GetPresContext() const;
   virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
   AnimationCollection* GetCollection() const;
 
   nsRefPtr<DocumentTimeline> mTimeline;
-  nsRefPtr<KeyframeEffectReadonly> mEffect;
+  nsRefPtr<KeyframeEffectReadOnly> mEffect;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Animation timescale
   Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
   Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale
   double mPlaybackRate;
 
   // A Promise that is replaced on each call to Play() (and in future Pause())
rename from dom/animation/AnimationEffectReadonly.cpp
rename to dom/animation/AnimationEffectReadOnly.cpp
--- a/dom/animation/AnimationEffectReadonly.cpp
+++ b/dom/animation/AnimationEffectReadOnly.cpp
@@ -1,23 +1,23 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/AnimationEffectReadonly.h"
-#include "mozilla/dom/AnimationEffectReadonlyBinding.h"
+#include "mozilla/dom/AnimationEffectReadOnly.h"
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectReadonly, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectReadOnly, mParent)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationEffectReadonly)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationEffectReadonly)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationEffectReadOnly)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationEffectReadOnly)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffectReadonly)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffectReadOnly)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 } // namespace dom
 } // namespace mozilla
rename from dom/animation/AnimationEffectReadonly.h
rename to dom/animation/AnimationEffectReadOnly.h
--- a/dom/animation/AnimationEffectReadonly.h
+++ b/dom/animation/AnimationEffectReadOnly.h
@@ -9,28 +9,28 @@
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 
-class AnimationEffectReadonly
+class AnimationEffectReadOnly
   : public nsISupports
   , public nsWrapperCache
 {
 protected:
-  virtual ~AnimationEffectReadonly() { }
+  virtual ~AnimationEffectReadOnly() { }
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadonly)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadOnly)
 
-  explicit AnimationEffectReadonly(nsISupports* aParent)
+  explicit AnimationEffectReadOnly(nsISupports* aParent)
     : mParent(aParent)
   {
   }
 
   nsISupports* GetParentObject() const { return mParent; }
 
 protected:
   nsCOMPtr<nsISupports> mParent;
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -54,45 +54,45 @@ ComputedTimingFunction::GetValue(double 
 }
 
 // In the Web Animations model, the time fraction can be outside the range
 // [0.0, 1.0] but it shouldn't be Infinity.
 const double ComputedTiming::kNullTimeFraction = PositiveInfinity<double>();
 
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadonly,
-                                   AnimationEffectReadonly,
+NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
+                                   AnimationEffectReadOnly,
                                    mTarget)
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadonly,
-                                               AnimationEffectReadonly)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
+                                               AnimationEffectReadOnly)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadonly)
-NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadonly)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
+NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
 
-NS_IMPL_ADDREF_INHERITED(KeyframeEffectReadonly, AnimationEffectReadonly)
-NS_IMPL_RELEASE_INHERITED(KeyframeEffectReadonly, AnimationEffectReadonly)
+NS_IMPL_ADDREF_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
+NS_IMPL_RELEASE_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
 
 JSObject*
-KeyframeEffectReadonly::WrapObject(JSContext* aCx,
+KeyframeEffectReadOnly::WrapObject(JSContext* aCx,
                                    JS::Handle<JSObject*> aGivenProto)
 {
-  return KeyframeEffectReadonlyBinding::Wrap(aCx, this, aGivenProto);
+  return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-KeyframeEffectReadonly::SetParentTime(Nullable<TimeDuration> aParentTime)
+KeyframeEffectReadOnly::SetParentTime(Nullable<TimeDuration> aParentTime)
 {
   mParentTime = aParentTime;
 }
 
 ComputedTiming
-KeyframeEffectReadonly::GetComputedTimingAt(
+KeyframeEffectReadOnly::GetComputedTimingAt(
                           const Nullable<TimeDuration>& aLocalTime,
                           const AnimationTiming& aTiming)
 {
   const TimeDuration zeroDuration;
 
   // Currently we expect negative durations to be picked up during CSS
   // parsing but when we start receiving timing parameters from other sources
   // we will need to clamp negative durations here.
@@ -212,99 +212,99 @@ KeyframeEffectReadonly::GetComputedTimin
   if (thisIterationReverse) {
     result.mTimeFraction = 1.0 - result.mTimeFraction;
   }
 
   return result;
 }
 
 StickyTimeDuration
-KeyframeEffectReadonly::ActiveDuration(const AnimationTiming& aTiming)
+KeyframeEffectReadOnly::ActiveDuration(const AnimationTiming& aTiming)
 {
   if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
     // An animation that repeats forever has an infinite active duration
     // unless its iteration duration is zero, in which case it has a zero
     // active duration.
     const StickyTimeDuration zeroDuration;
     return aTiming.mIterationDuration == zeroDuration
            ? zeroDuration
            : StickyTimeDuration::Forever();
   }
   return StickyTimeDuration(
     aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount));
 }
 
 // http://w3c.github.io/web-animations/#in-play
 bool
-KeyframeEffectReadonly::IsInPlay(const Animation& aAnimation) const
+KeyframeEffectReadOnly::IsInPlay(const Animation& aAnimation) const
 {
   if (IsFinishedTransition() ||
       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
+KeyframeEffectReadOnly::IsCurrent(const Animation& aAnimation) const
 {
   if (IsFinishedTransition() ||
       aAnimation.PlayState() == AnimationPlayState::Finished) {
     return false;
   }
 
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
          computedTiming.mPhase == ComputedTiming::AnimationPhase_Active;
 }
 
 bool
-KeyframeEffectReadonly::IsInEffect() const
+KeyframeEffectReadOnly::IsInEffect() const
 {
   if (IsFinishedTransition()) {
     return false;
   }
 
   ComputedTiming computedTiming = GetComputedTiming();
   return computedTiming.mTimeFraction != ComputedTiming::kNullTimeFraction;
 }
 
 const AnimationProperty*
-KeyframeEffectReadonly::GetAnimationOfProperty(nsCSSProperty aProperty) const
+KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (size_t propIdx = 0, propEnd = mProperties.Length();
        propIdx != propEnd; ++propIdx) {
     if (aProperty == mProperties[propIdx].mProperty) {
       const AnimationProperty* result = &mProperties[propIdx];
       if (!result->mWinsInCascade) {
         result = nullptr;
       }
       return result;
     }
   }
   return nullptr;
 }
 
 bool
-KeyframeEffectReadonly::HasAnimationOfProperties(
+KeyframeEffectReadOnly::HasAnimationOfProperties(
                           const nsCSSProperty* aProperties,
                           size_t aPropertyCount) const
 {
   for (size_t i = 0; i < aPropertyCount; i++) {
     if (HasAnimationOfProperty(aProperties[i])) {
       return true;
     }
   }
   return false;
 }
 
 void
-KeyframeEffectReadonly::ComposeStyle(
+KeyframeEffectReadOnly::ComposeStyle(
                           nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
                           nsCSSPropertySet& aSetProperties)
 {
   ComputedTiming computedTiming = GetComputedTiming();
 
   // If the time fraction is null, we don't have fill data for the current
   // time so we shouldn't animate.
   if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -10,17 +10,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIDocument.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/dom/AnimationEffectReadonly.h"
+#include "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
 
 struct JSContext;
 class nsCSSPropertySet;
 
@@ -180,47 +180,47 @@ struct AnimationProperty
     return !(*this == aOther);
   }
 };
 
 struct ElementPropertyTransition;
 
 namespace dom {
 
-class KeyframeEffectReadonly : public AnimationEffectReadonly
+class KeyframeEffectReadOnly : public AnimationEffectReadOnly
 {
 public:
-  KeyframeEffectReadonly(nsIDocument* aDocument,
+  KeyframeEffectReadOnly(nsIDocument* aDocument,
                          Element* aTarget,
                          nsCSSPseudoElements::Type aPseudoType,
                          const AnimationTiming &aTiming,
                          const nsSubstring& aName)
-    : AnimationEffectReadonly(aDocument)
+    : AnimationEffectReadOnly(aDocument)
     , mTarget(aTarget)
     , mTiming(aTiming)
     , mName(aName)
     , 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)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffectReadOnly,
+                                                        AnimationEffectReadOnly)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   virtual ElementPropertyTransition* AsTransition() { return nullptr; }
   virtual const ElementPropertyTransition* AsTransition() const {
     return nullptr;
   }
 
-  // KeyframeEffectReadonly interface
+  // KeyframeEffectReadOnly interface
   Element* GetTarget() const {
     // Currently we only implement Element.getAnimations() which only
     // returns animations targetting Elements so this should never
     // be called for an animation that targets a pseudo-element.
     MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
                "Requesting the target of a KeyframeEffect that targets a"
                " pseudo-element is not yet supported.");
     return mTarget;
@@ -327,17 +327,17 @@ public:
   // 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,
                     nsCSSPropertySet& aSetProperties);
 
 protected:
-  virtual ~KeyframeEffectReadonly() { }
+  virtual ~KeyframeEffectReadOnly() { }
 
   nsCOMPtr<Element> mTarget;
   Nullable<TimeDuration> mParentTime;
 
   AnimationTiming mTiming;
   nsString mName;
   // A flag to mark transitions that have finished and are due to
   // be removed on the next throttle-able cycle.
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -4,30 +4,30 @@
 # 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/.
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 EXPORTS.mozilla.dom += [
     'Animation.h',
-    'AnimationEffectReadonly.h',
+    'AnimationEffectReadOnly.h',
     'AnimationTimeline.h',
     'DocumentTimeline.h',
     'KeyframeEffect.h',
 ]
 
 EXPORTS.mozilla += [
     'AnimationUtils.h',
     'PendingAnimationTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'Animation.cpp',
-    'AnimationEffectReadonly.cpp',
+    'AnimationEffectReadOnly.cpp',
     'AnimationTimeline.cpp',
     'DocumentTimeline.cpp',
     'KeyframeEffect.cpp',
     'PendingAnimationTracker.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -340,17 +340,17 @@ void nsMutationReceiver::NodeWillBeDestr
   NS_ASSERTION(!mParent, "Shouldn't have mParent here!");
   Disconnect(true);
 }
 
 void
 nsAnimationReceiver::RecordAnimationMutation(Animation* aAnimation,
                                              AnimationMutation aMutationType)
 {
-  KeyframeEffectReadonly* effect = aAnimation->GetEffect();
+  KeyframeEffectReadOnly* effect = aAnimation->GetEffect();
   if (!effect) {
     return;
   }
 
   Element* animationTarget = effect->GetTarget();
   if (!animationTarget) {
     return;
   }
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -213,17 +213,17 @@ nsNodeUtils::ContentRemoved(nsINode* aCo
   IMPL_MUTATION_NOTIFICATION(ContentRemoved, aContainer,
                              (document, container, aChild, aIndexInContainer,
                               aPreviousSibling));
 }
 
 static inline Element*
 GetTarget(Animation* aAnimation)
 {
-  KeyframeEffectReadonly* effect = aAnimation->GetEffect();
+  KeyframeEffectReadOnly* effect = aAnimation->GetEffect();
   if (!effect) {
     return nullptr;
   }
 
   Element* target;
   nsCSSPseudoElements::Type pseudoType;
   effect->GetTarget(target, pseudoType);
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -83,17 +83,17 @@ DOMInterfaces = {
 'MozAbortablePromise': {
     'nativeType': 'mozilla::dom::AbortablePromise',
 },
 
 'AbstractWorker': {
     'concrete': False
 },
 
-'AnimationEffectReadonly': {
+'AnimationEffectReadOnly': {
     'concrete': False
 },
 
 'AnimationTimeline': {
     'concrete': False
 },
 
 'AnonymousContent': {
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_AllowEmbedAppsInNestedOOIframe.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1097479 - Allow embed remote apps or widgets in content
+// process if nested-oop is enabled
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+SpecialPowers.setAllAppsLaunchable(true);
+
+function runTest() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+    is(e.detail.message == 'app', true, e.detail.message);
+    SimpleTest.finish();
+  });
+
+  document.body.appendChild(iframe);
+
+  var context = { 'url': 'http://example.org',
+                  'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
+                  'isInBrowserElement': true };
+  SpecialPowers.pushPermissions([
+    {'type': 'browser', 'allow': 1, 'context': context},
+    {'type': 'embed-apps', 'allow': 1, 'context': context}
+  ], function() {
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AllowEmbedAppsInNestedOOIframe.html';
+  });
+}
+
+addEventListener('testready', () => {
+  SpecialPowers.pushPrefEnv({"set": [["dom.ipc.tabs.nested.enabled", true]]}, runTest);
+});
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_AllowEmbedAppsInNestedOOIframe.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<script type="text/javascript">
+  addEventListener('load', function(e) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', 'true');
+    iframe.setAttribute('remote', 'true');
+    iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+    iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+      alert(e.detail.message);
+    });
+    document.body.appendChild(iframe);
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AppFramePermission.html';
+  });
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -11,16 +11,18 @@ support-files =
 
 [test_browserElement_oop_ThemeColor.html]
 [test_browserElement_inproc_ErrorSecurity.html]
 skip-if = toolkit=='gonk'
 [test_browserElement_inproc_OpenMixedProcess.html]
 skip-if = toolkit=='gonk' || (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Alert.html]
 [test_browserElement_oop_AlertInFrame.html]
+[test_browserElement_oop_AllowEmbedAppsInNestedOOIframe.html]
+skip-if = toolkit=='gonk'
 [test_browserElement_oop_AppFramePermission.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_AppWindowNamespace.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Auth.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BackForward.html]
 [test_browserElement_oop_BadScreenshot.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
   ../../../browser/base/content/test/general/audio.ogg
   ../../../dom/media/test/short-video.ogv
   browserElementTestHelpers.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
+  browserElement_AllowEmbedAppsInNestedOOIframe.js
   browserElement_AppFramePermission.js
   browserElement_AppWindowNamespace.js
   browserElement_Auth.js
   browserElement_BackForward.js
   browserElement_BadScreenshot.js
   browserElement_ThemeColor.js
   browserElement_BrowserWindowNamespace.js
   browserElement_BrowserWindowResize.js
@@ -68,16 +69,17 @@ support-files =
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
+  file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_DisallowEmbedAppsInOOP.html
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_AllowEmbedAppsInNestedOOIframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1097479</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_AllowEmbedAppsInNestedOOIframe.js">
+</script>
+</body>
+</html>
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -632,16 +632,20 @@ public:
                        dom::ImageData* pixels, ErrorResult& rv);
     // Allow whatever element types the bindings are willing to pass
     // us in TexSubImage2D
     template<class ElementType>
     void TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xoffset,
                        GLint yoffset, GLenum format, GLenum type,
                        ElementType& elt, ErrorResult& rv)
     {
+        // TODO: Consolidate all the parameter validation
+        // checks. Instead of spreading out the cheks in multple
+        // places, consolidate into one spot.
+
         if (IsContextLost())
             return;
 
         if (!ValidateTexImageTarget(rawTexImageTarget,
                                     WebGLTexImageFunc::TexSubImage,
                                     WebGLTexDimensions::Tex2D))
         {
             ErrorInvalidEnumInfo("texSubImage2D: target", rawTexImageTarget);
@@ -1303,17 +1307,17 @@ protected:
 
     // If jsArrayType is MaxTypedArrayViewType, it means no array.
     void TexImage2D_base(TexImageTarget texImageTarget, GLint level,
                          GLenum internalFormat, GLsizei width,
                          GLsizei height, GLsizei srcStrideOrZero, GLint border,
                          GLenum format, GLenum type, void* data,
                          uint32_t byteLength, js::Scalar::Type jsArrayType,
                          WebGLTexelFormat srcFormat, bool srcPremultiplied);
-    void TexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
+    void TexSubImage2D_base(GLenum texImageTarget, GLint level,
                             GLint xoffset, GLint yoffset, GLsizei width,
                             GLsizei height, GLsizei srcStrideOrZero,
                             GLenum format, GLenum type, void* pixels,
                             uint32_t byteLength, js::Scalar::Type jsArrayType,
                             WebGLTexelFormat srcFormat, bool srcPremultiplied);
 
     void TexParameter_base(GLenum target, GLenum pname,
                            GLint* const out_intParam,
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -3418,30 +3418,35 @@ WebGLContext::TexImage2D(GLenum rawTarge
     return TexImage2D_base(rawTarget, level, internalformat, pixels->Width(),
                            pixels->Height(), 4*pixels->Width(), 0,
                            format, type, pixelData, pixelDataLength, js::Scalar::MaxTypedArrayViewType,
                            WebGLTexelFormat::RGBA8, false);
 }
 
 
 void
-WebGLContext::TexSubImage2D_base(TexImageTarget texImageTarget, GLint level,
+WebGLContext::TexSubImage2D_base(GLenum rawImageTarget, GLint level,
                                  GLint xoffset, GLint yoffset,
                                  GLsizei width, GLsizei height, GLsizei srcStrideOrZero,
                                  GLenum format, GLenum type,
                                  void* data, uint32_t byteLength,
                                  js::Scalar::Type jsArrayType,
                                  WebGLTexelFormat srcFormat, bool srcPremultiplied)
 {
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage;
     const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D;
 
     if (type == LOCAL_GL_HALF_FLOAT_OES)
         type = LOCAL_GL_HALF_FLOAT;
 
+    if (!ValidateTexImageTarget(rawImageTarget, func, dims))
+        return;
+
+    TexImageTarget texImageTarget(rawImageTarget);
+
     WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget);
     if (!tex)
         return ErrorInvalidOperation("texSubImage2D: no texture bound on active texture unit");
 
     if (!tex->HasImageInfoAt(texImageTarget, level))
         return ErrorInvalidOperation("texSubImage2D: no previously defined texture image");
 
     const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
--- a/dom/canvas/WebGLElementArrayCache.cpp
+++ b/dom/canvas/WebGLElementArrayCache.cpp
@@ -14,17 +14,20 @@
 #include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 static void
 UpdateUpperBound(uint32_t* const out_upperBound, uint32_t newBound)
 {
     MOZ_ASSERT(out_upperBound);
-    *out_upperBound = std::max(*out_upperBound, newBound);
+    // Move *out_upperBound to a local variable to work around a false positive
+    // -Wuninitialized gcc warning about std::max() in PGO builds.
+    uint32_t upperBound = *out_upperBound;
+    *out_upperBound = std::max(upperBound, newBound);
 }
 
 /* WebGLElementArrayCacheTree contains most of the implementation of
  * WebGLElementArrayCache, which performs WebGL element array buffer validation
  * for drawElements.
  *
  * Attention: Here lie nontrivial data structures, bug-prone algorithms, and
  * non-canonical tweaks! Whence the explanatory comments, and compiled unit
--- a/dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
+++ b/dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
@@ -110,17 +110,17 @@ void CheckValidateAllTypes(WebGLElementA
 template<typename T>
 void
 CheckSanity()
 {
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                         // ensure we exercise some nontrivial tree-walking
   T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
   size_t numBytes = numElems * sizeof(T);
-  MOZ_ASSERT(numBytes == sizeof(data));
+  MOZ_RELEASE_ASSERT(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
   WebGLElementArrayCache c;
   c.BufferData(data, numBytes);
   CheckValidate(true,  c, type, 6, 0, 8);
   CheckValidate(false, c, type, 5, 0, 8);
   CheckValidate(true,  c, type, 3, 0, 3);
@@ -134,34 +134,34 @@ CheckSanity()
 
   // now test a somewhat larger size to ensure we exceed the size of a tree leaf
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
   CheckValidate(true,  c, type, numElems,     0, numElems);
   CheckValidate(false, c, type, numElems - 1, 0, numElems);
 
-  MOZ_ASSERT(numElems > 10);
+  MOZ_RELEASE_ASSERT(numElems > 10);
   CheckValidate(true,  c, type, numElems - 10, 10, numElems - 10);
   CheckValidate(false, c, type, numElems - 11, 10, numElems - 10);
 }
 
 template<typename T>
 void
 CheckUintOverflow()
 {
   // This test is only for integer types smaller than uint32_t
   static_assert(sizeof(T) < sizeof(uint32_t), "This test is only for integer types \
                 smaller than uint32_t");
 
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                               // ensure we exercise some nontrivial tree-walking
   T data[numElems];
   size_t numBytes = numElems * sizeof(T);
-  MOZ_ASSERT(numBytes == sizeof(data));
+  MOZ_RELEASE_ASSERT(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
   WebGLElementArrayCache c;
 
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -72,16 +72,22 @@ public:
   }
 
   InternalHeaders*
   GetInternalHeaders() const
   {
     return mInternalResponse->Headers();
   }
 
+  const nsCString&
+  GetSecurityInfo() const
+  {
+    return mInternalResponse->GetSecurityInfo();
+  }
+
   Headers* Headers_();
 
   void
   GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
 
   static already_AddRefed<Response>
   Error(const GlobalObject& aGlobal);
 
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -474,16 +474,30 @@ bool WidgetsEnabled()
     sBoolVarCacheInitialized = true;
     Preferences::AddBoolVarCache(&sMozWidgetsEnabled,
                                  "dom.enable_widgets");
   }
 
   return sMozWidgetsEnabled;
 }
 
+bool NestedEnabled()
+{
+  static bool sMozNestedEnabled = false;
+  static bool sBoolVarCacheInitialized = false;
+
+  if (!sBoolVarCacheInitialized) {
+    sBoolVarCacheInitialized = true;
+    Preferences::AddBoolVarCache(&sMozNestedEnabled,
+                                 "dom.ipc.tabs.nested.enabled");
+  }
+
+  return sMozNestedEnabled;
+}
+
 } // anonymous namespace
 
 /* [infallible] */ NS_IMETHODIMP
 nsGenericHTMLFrameElement::GetReallyIsWidget(bool *aOut)
 {
   *aOut = false;
   if (!WidgetsEnabled()) {
     return NS_OK;
@@ -576,18 +590,22 @@ nsGenericHTMLFrameElement::GetAppManifes
 {
   aOut.Truncate();
 
   // At the moment, you can't be an app without being a browser.
   if (!nsIMozBrowserFrame::GetReallyIsBrowserOrApp()) {
     return NS_OK;
   }
 
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    NS_WARNING("Can't embed-apps. Embed-apps is restricted to in-proc apps, see bug 1059662");
+  // Only allow content process to embed an app when nested content
+  // process is enabled.
+  if (XRE_GetProcessType() != GeckoProcessType_Default &&
+      !(GetBoolAttr(nsGkAtoms::Remote) && NestedEnabled())){
+    NS_WARNING("Can't embed-apps. Embed-apps is restricted to in-proc apps "
+               "or content processes with nested pref enabled, see bug 1097479");
     return NS_OK;
   }
 
   nsAutoString appManifestURL;
   nsAutoString widgetManifestURL;
 
   GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
 
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -8,24 +8,26 @@
 #include "mozilla/dom/MediaKeySystemAccessBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentTypeParser.h"
 #ifdef MOZ_FMP4
 #include "MP4Decoder.h"
 #endif
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
+#include "WMFDecoderModule.h"
 #endif
 #include "nsContentCID.h"
 #include "nsServiceManagerUtils.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "VideoUtils.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/EMEUtils.h"
+#include "GMPUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
                                       mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
@@ -154,16 +156,23 @@ MediaKeySystemAccess::GetKeySystemStatus
        aKeySystem.EqualsLiteral("com.adobe.primetime"))) {
     // Win Vista and later only.
     if (!IsVistaOrLater()) {
       return MediaKeySystemStatus::Cdm_not_supported;
     }
     if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
       return MediaKeySystemStatus::Cdm_disabled;
     }
+    if ((!WMFDecoderModule::HasH264() || !WMFDecoderModule::HasAAC()) ||
+        !EMEVoucherFileExists()) {
+      // The system doesn't have the codecs that Adobe EME relies
+      // on installed, or doesn't have a voucher for the plugin-container.
+      // Adobe EME isn't going to work, so don't advertise that it will.
+      return MediaKeySystemStatus::Cdm_not_supported;
+    }
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, true);
   }
 #endif
 
   return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -389,17 +389,17 @@ GonkVideoDecoderManager::Output(int64_t 
       if (mDecoder->UpdateOutputBuffers()) {
         return Output(aStreamOffset, aOutData);
       }
       GVDM_LOG("Fails to update output buffers!");
       return NS_ERROR_FAILURE;
     }
     case -EAGAIN:
     {
-      GVDM_LOG("Need to try again!");
+//      GVDM_LOG("Need to try again!");
       return NS_ERROR_NOT_AVAILABLE;
     }
     case android::ERROR_END_OF_STREAM:
     {
       GVDM_LOG("Got the EOS frame!");
       nsRefPtr<VideoData> data;
       nsresult rv = CreateVideoData(aStreamOffset, getter_AddRefs(data));
       if (rv == NS_ERROR_NOT_AVAILABLE) {
@@ -487,29 +487,34 @@ GonkVideoDecoderManager::Flush()
 }
 
 void
 GonkVideoDecoderManager::codecReserved()
 {
   GVDM_LOG("codecReserved");
   sp<AMessage> format = new AMessage;
   sp<Surface> surface;
-
+  status_t rv = OK;
   // Fixed values
   GVDM_LOG("Configure mime type: %s, widht:%d, height:%d", mMimeType.get(), mVideoWidth, mVideoHeight);
   format->setString("mime", mMimeType.get());
   format->setInt32("width", mVideoWidth);
   format->setInt32("height", mVideoHeight);
   if (mNativeWindow != nullptr) {
     surface = new Surface(mNativeWindow->getBufferQueue());
   }
   mDecoder->configure(format, surface, nullptr, 0);
   mDecoder->Prepare();
-  status_t rv = mDecoder->Input(mCodecSpecificData->Elements(), mCodecSpecificData->Length(), 0,
-                                android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
+
+  if (mMimeType.EqualsLiteral("video/mp4v-es")) {
+    rv = mDecoder->Input(mCodecSpecificData->Elements(),
+                         mCodecSpecificData->Length(), 0,
+                         android::MediaCodec::BUFFER_FLAG_CODECCONFIG);
+  }
+
   if (rv != OK) {
     GVDM_LOG("Failed to configure codec!!!!");
     mReaderCallback->Error();
   }
 }
 
 void
 GonkVideoDecoderManager::codecCanceled()
copy from dom/media/gmp-plugin/Makefile.in
copy to dom/media/gmp-plugin-openh264/Makefile.in
--- a/dom/media/gmp-plugin/Makefile.in
+++ b/dom/media/gmp-plugin-openh264/Makefile.in
@@ -1,13 +1,12 @@
 #
 # 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/.
 
-INSTALL_TARGETS += FAKE_GMP_PLUGIN
-FAKE_GMP_PLUGIN_DEST = $(DEPTH)/dist/bin/gmp-fake/1.0
-FAKE_GMP_PLUGIN_FILES = \
-  $(SHARED_LIBRARY) \
-  $(srcdir)/fake.info \
-  $(srcdir)/fake.voucher
+INSTALL_TARGETS += FAKE_GMP_OPENH264_PLUGIN
+FAKE_GMP_OPENH264_PLUGIN_DEST = $(DEPTH)/dist/bin/gmp-fakeopenh264/1.0
+FAKE_GMP_OPENH264_PLUGIN_FILES = \
+  $(srcdir)/fakeopenh264.info \
+  $(srcdir)/fakeopenh264.voucher \
+  $(NULL)
 
-include $(topsrcdir)/config/rules.mk
copy from dom/media/gmp-plugin/fake.info
copy to dom/media/gmp-plugin-openh264/fakeopenh264.info
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin-openh264/fakeopenh264.info
@@ -1,5 +1,4 @@
-Name: fake
+Name: fakeopenh264
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v7[fake]
-Libraries: dxva2.dll
+APIs: encode-video[h264], decode-video[h264]
copy from dom/media/gmp-plugin/fake.voucher
copy to dom/media/gmp-plugin-openh264/fakeopenh264.voucher
--- a/dom/media/gmp-plugin/fake.voucher
+++ b/dom/media/gmp-plugin-openh264/fakeopenh264.voucher
@@ -1,1 +1,1 @@
-gmp-fake placeholder voucher
\ No newline at end of file
+gmp-fakeopenh264 placeholder voucher
copy from dom/media/gmp-plugin/moz.build
copy to dom/media/gmp-plugin-openh264/moz.build
--- a/dom/media/gmp-plugin/moz.build
+++ b/dom/media/gmp-plugin-openh264/moz.build
@@ -1,22 +1,22 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-NO_DIST_INSTALL = True
+# largely a copy of dom/media/gmp-fake/moz.build
+
+FINAL_TARGET = 'dist/bin/gmp-fakeopenh264/1.0'
 SOURCES += [
-        'gmp-fake.cpp',
-        'gmp-test-decryptor.cpp',
-        'gmp-test-storage.cpp',
+    '../gmp-plugin/gmp-fake.cpp',
 ]
 
-SharedLibrary("fake")
+SharedLibrary("fakeopenh264")
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'ole32',
     ]
 
 USE_STATIC_LIBS = True
 NO_VISIBILITY_FLAGS = True
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v7[fake]
+APIs: encode-video[h264:fake], decode-video[h264:fake], eme-decrypt-v7[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp-plugin/gmp-fake.cpp
+++ b/dom/media/gmp-plugin/gmp-fake.cpp
@@ -44,20 +44,22 @@
 #include <limits.h>
 
 #include "gmp-platform.h"
 #include "gmp-video-host.h"
 #include "gmp-video-encode.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-frame-i420.h"
 #include "gmp-video-frame-encoded.h"
+
+#if defined(GMP_FAKE_SUPPORT_DECRYPT)
 #include "gmp-decryption.h"
-
 #include "gmp-test-decryptor.h"
 #include "gmp-test-storage.h"
+#endif
 
 #if defined(_MSC_VER)
 #define PUBLIC_FUNC __declspec(dllexport)
 #else
 #define PUBLIC_FUNC
 #endif
 
 #define BIG_FRAME 10000
@@ -402,22 +404,24 @@ extern "C" {
   PUBLIC_FUNC GMPErr
   GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
     if (!strcmp (aApiName, GMP_API_VIDEO_DECODER)) {
       *aPluginApi = new FakeVideoDecoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, GMP_API_VIDEO_ENCODER)) {
       *aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
+#if defined(GMP_FAKE_SUPPORT_DECRYPT)
     } else if (!strcmp (aApiName, GMP_API_DECRYPTOR)) {
       *aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, GMP_API_ASYNC_SHUTDOWN)) {
       *aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
       return GMPNoErr;
+#endif
     }
     return GMPGenericErr;
   }
 
   PUBLIC_FUNC void
   GMPShutdown (void) {
     g_platform_api = NULL;
   }
--- a/dom/media/gmp-plugin/moz.build
+++ b/dom/media/gmp-plugin/moz.build
@@ -6,16 +6,18 @@
 
 NO_DIST_INSTALL = True
 SOURCES += [
         'gmp-fake.cpp',
         'gmp-test-decryptor.cpp',
         'gmp-test-storage.cpp',
 ]
 
+DEFINES['GMP_FAKE_SUPPORT_DECRYPT'] = True
+
 SharedLibrary("fake")
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'ole32',
     ]
 
 USE_STATIC_LIBS = True
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -50,16 +50,17 @@ extern PRLogModuleInfo* GetGMPLog();
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
   , mDeleteProcessOnlyOnUnload(false)
   , mAbnormalShutdownInProgress(false)
   , mIsBlockingDeletion(false)
+  , mCanDecrypt(false)
   , mGMPContentChildCount(0)
   , mAsyncShutdownRequired(false)
   , mAsyncShutdownInProgress(false)
 #ifdef PR_LOGGING
   , mChildPid(0)
 #endif
 {
   LOGD("GMPParent ctor");
@@ -751,41 +752,50 @@ GMPParent::ReadGMPMetaData()
         nsCCharSeparatedTokenizer tagTokens(ts, ':');
         while (tagTokens.hasMoreTokens()) {
           const nsDependentCSubstring tag(tagTokens.nextToken());
           cap->mAPITags.AppendElement(tag);
         }
       }
     }
 
+    if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR) ||
+        cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_COMPAT)) {
+      mCanDecrypt = true;
+
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
-    if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR) &&
-        !mozilla::SandboxInfo::Get().CanSandboxMedia()) {
-      printf_stderr("GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
-                    " but this system can't sandbox it; not loading.\n",
-                    mDisplayName.get());
-      delete cap;
-      return NS_ERROR_FAILURE;
+      if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) {
+        printf_stderr("GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
+                      " but this system can't sandbox it; not loading.\n",
+                      mDisplayName.get());
+        delete cap;
+        return NS_ERROR_FAILURE;
+      }
+#endif
     }
-#endif
 
     mCapabilities.AppendElement(cap);
   }
 
   if (mCapabilities.IsEmpty()) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 bool
 GMPParent::CanBeSharedCrossNodeIds() const
 {
-  return mNodeId.IsEmpty();
+  return mNodeId.IsEmpty() &&
+    // XXX bug 1159300 hack -- maybe remove after openh264 1.4
+    // We don't want to use CDM decoders for non-encrypted playback
+    // just yet; especially not for WebRTC. Don't allow CDMs to be used
+    // without a node ID.
+    !mCanDecrypt;
 }
 
 bool
 GMPParent::CanBeUsedFrom(const nsACString& aNodeId) const
 {
   return (mNodeId.IsEmpty() && State() == GMPStateNotLoaded) ||
          mNodeId == aNodeId;
 }
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -194,16 +194,18 @@ private:
   nsCString mVersion;
   nsCString mPluginId;
   nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
   GMPProcessParent* mProcess;
   bool mDeleteProcessOnlyOnUnload;
   bool mAbnormalShutdownInProgress;
   bool mIsBlockingDeletion;
 
+  bool mCanDecrypt;
+
   nsTArray<nsRefPtr<GMPTimerParent>> mTimers;
   nsTArray<nsRefPtr<GMPStorageParent>> mStorage;
   nsCOMPtr<nsIThread> mGMPThread;
   nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
   // NodeId the plugin is assigned to, or empty if the the plugin is not
   // assigned to a NodeId.
   nsAutoCString mNodeId;
   // This is used for GMP content in the parent, there may be more of these in
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=2 et :
  * 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 "GMPProcessParent.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsIFile.h"
+#include "GMPUtils.h"
 
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include <string>
 
 using std::vector;
 using std::string;
@@ -40,25 +39,23 @@ GMPProcessParent::GMPProcessParent(const
 GMPProcessParent::~GMPProcessParent()
 {
   MOZ_COUNT_DTOR(GMPProcessParent);
 }
 
 bool
 GMPProcessParent::Launch(int32_t aTimeoutMs)
 {
-  nsCOMPtr<nsIFile> greDir;
-  NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir));
-  if (!greDir) {
-    NS_WARNING("GMPProcessParent can't get NS_GRE_DIR");
+  nsCOMPtr<nsIFile> path;
+  if (!GetEMEVoucherPath(getter_AddRefs(path))) {
+    NS_WARNING("GMPProcessParent can't get EME voucher path!");
     return false;
   }
-  greDir->AppendNative(NS_LITERAL_CSTRING("voucher.bin"));
   nsAutoCString voucherPath;
-  greDir->GetNativePath(voucherPath);
+  path->GetNativePath(voucherPath);
 
   vector<string> args;
   args.push_back(mGMPPath);
   args.push_back(string(voucherPath.BeginReading(), voucherPath.EndReading()));
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   std::wstring wGMPPath = UTF8ToWide(mGMPPath.c_str());
   mAllowedFilesRead.push_back(wGMPPath + L"\\*");
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -668,31 +668,33 @@ GeckoMediaPluginServiceParent::ClonePlug
   MutexAutoLock lock(mMutex);
   mPlugins.AppendElement(gmp);
 
   return gmp.get();
 }
 
 class NotifyObserversTask final : public nsRunnable {
 public:
-  explicit NotifyObserversTask(const char* aTopic)
+  explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
     : mTopic(aTopic)
+    , mData(aData)
   {}
   NS_IMETHOD Run() override {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     MOZ_ASSERT(obsService);
     if (obsService) {
-      obsService->NotifyObservers(nullptr, mTopic, nullptr);
+      obsService->NotifyObservers(nullptr, mTopic, mData.get());
     }
     return NS_OK;
   }
 private:
   ~NotifyObserversTask() {}
   const char* mTopic;
+  const nsString mData;
 };
 
 void
 GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
 
@@ -732,46 +734,62 @@ GeckoMediaPluginServiceParent::RemoveOnG
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
+  // Plugin destruction can modify |mPlugins|. Put them aside for now and
+  // destroy them once we're done with |mPlugins|.
+  nsTArray<nsRefPtr<GMPParent>> deadPlugins;
+
+  bool inUse = false;
   MutexAutoLock lock(mMutex);
   for (size_t i = mPlugins.Length() - 1; i < mPlugins.Length(); i--) {
     nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
     bool equals;
     if (NS_FAILED(directory->Equals(pluginpath, &equals)) || !equals) {
       continue;
     }
 
     nsRefPtr<GMPParent> gmp = mPlugins[i];
     if (aDeleteFromDisk && gmp->State() != GMPStateNotLoaded) {
       // We have to wait for the child process to release its lib handle
       // before we can delete the GMP.
+      inUse = true;
       gmp->MarkForDeletion();
 
       if (!mPluginsWaitingForDeletion.Contains(aDirectory)) {
         mPluginsWaitingForDeletion.AppendElement(aDirectory);
       }
     }
 
     if (gmp->State() == GMPStateNotLoaded || !aCanDefer) {
       // GMP not in use or shutdown is being forced; can shut it down now.
-      gmp->AbortAsyncShutdown();
-      gmp->CloseActive(true);
+      deadPlugins.AppendElement(gmp);
       mPlugins.RemoveElementAt(i);
     }
   }
 
-  if (aDeleteFromDisk) {
+  {
+    MutexAutoUnlock unlock(mMutex);
+    for (auto& gmp : deadPlugins) {
+      gmp->AbortAsyncShutdown();
+      gmp->CloseActive(true);
+    }
+  }
+
+  if (aDeleteFromDisk && !inUse) {
     if (NS_SUCCEEDED(directory->Remove(true))) {
       mPluginsWaitingForDeletion.RemoveElement(aDirectory);
+      NS_DispatchToMainThread(new NotifyObserversTask("gmp-directory-deleted",
+                                                      nsString(aDirectory)),
+                              NS_DISPATCH_NORMAL);
     }
   }
 }
 
 // May remove when Bug 1043671 is fixed
 static void Dummy(nsRefPtr<GMPParent>& aOnDeathsDoor)
 {
   // exists solely to do nothing and let the Runnable kill the GMPParent
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GMPUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+
+bool
+GetEMEVoucherPath(nsIFile** aPath)
+{
+  nsCOMPtr<nsIFile> path;
+  NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(path));
+  if (!path) {
+    NS_WARNING("GetEMEVoucherPath can't get NS_GRE_DIR!");
+    return false;
+  }
+  path->AppendNative(NS_LITERAL_CSTRING("voucher.bin"));
+  path.forget(aPath);
+  return true;
+}
+
+bool
+EMEVoucherFileExists()
+{
+  nsCOMPtr<nsIFile> path;
+  bool exists;
+  return GetEMEVoucherPath(getter_AddRefs(path)) &&
+         NS_SUCCEEDED(path->Exists(&exists)) &&
+         exists;
+}
+
+} // namespace mozilla
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -16,11 +16,15 @@ struct DestroyPolicy
   void operator()(T* aGMPObject) const {
     aGMPObject->Destroy();
   }
 };
 
 template<typename T>
 using GMPUniquePtr = mozilla::UniquePtr<T, DestroyPolicy<T>>;
 
+bool GetEMEVoucherPath(nsIFile** aPath);
+
+bool EMEVoucherFileExists();
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -93,16 +93,17 @@ UNIFIED_SOURCES += [
     'GMPService.cpp',
     'GMPServiceChild.cpp',
     'GMPServiceParent.cpp',
     'GMPSharedMemManager.cpp',
     'GMPStorageChild.cpp',
     'GMPStorageParent.cpp',
     'GMPTimerChild.cpp',
     'GMPTimerParent.cpp',
+    'GMPUtils.cpp',
     'GMPVideoDecoderChild.cpp',
     'GMPVideoDecoderParent.cpp',
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
     'GMPVideoPlaneImpl.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/GMPTestMonitor.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadUtils.h"
+
+#ifndef __GMPTestMonitor_h__
+#define __GMPTestMonitor_h__
+
+class GMPTestMonitor
+{
+public:
+  GMPTestMonitor()
+    : mFinished(false)
+  {
+  }
+
+  void AwaitFinished()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    while (!mFinished) {
+      NS_ProcessNextEvent(nullptr, true);
+    }
+    mFinished = false;
+  }
+
+private:
+  void MarkFinished()
+  {
+    mFinished = true;
+  }
+
+public:
+  void SetFinished()
+  {
+    NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
+                                                          &GMPTestMonitor::MarkFinished));
+  }
+
+private:
+  bool mFinished;
+};
+
+#endif // __GMPTestMonitor_h__
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
+#include "GMPTestMonitor.h"
 #include "GMPVideoDecoderProxy.h"
 #include "GMPVideoEncoderProxy.h"
 #include "GMPDecryptorProxy.h"
 #include "GMPServiceParent.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Atomics.h"
@@ -23,50 +24,16 @@
 #include "mozilla/WindowsVersion.h"
 #endif
 
 using namespace std;
 
 using namespace mozilla;
 using namespace mozilla::gmp;
 
-class GMPTestMonitor
-{
-public:
-  GMPTestMonitor()
-    : mFinished(false)
-  {
-  }
-
-  void AwaitFinished()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    while (!mFinished) {
-      NS_ProcessNextEvent(nullptr, true);
-    }
-    mFinished = false;
-  }
-
-private:
-  void MarkFinished()
-  {
-    mFinished = true;
-  }
-
-public:
-  void SetFinished()
-  {
-    NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
-                                                          &GMPTestMonitor::MarkFinished));
-  }
-
-private:
-  bool mFinished;
-};
-
 struct GMPTestRunner
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner)
 
   void DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&));
   void RunTestGMPTestCodec1(GMPTestMonitor& aMonitor);
   void RunTestGMPTestCodec2(GMPTestMonitor& aMonitor);
   void RunTestGMPTestCodec3(GMPTestMonitor& aMonitor);
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestGMPRemoveAndDelete.cpp
@@ -0,0 +1,449 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GMPService.h"
+#include "GMPTestMonitor.h"
+#include "gmp-api/gmp-video-host.h"
+#include "gtest/gtest.h"
+#include "mozilla/Services.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIObserverService.h"
+
+#define GMP_DIR_NAME NS_LITERAL_STRING("gmp-fake")
+#define GMP_OLD_VERSION NS_LITERAL_STRING("1.0")
+#define GMP_NEW_VERSION NS_LITERAL_STRING("1.1")
+
+#define GMP_DELETED_TOPIC "gmp-directory-deleted"
+
+#define EXPECT_OK(X) EXPECT_TRUE(NS_SUCCEEDED(X))
+
+class GMPRemoveTest : public nsIObserver
+                    , public GMPVideoDecoderCallbackProxy
+{
+public:
+  GMPRemoveTest();
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  // Called when a GMP plugin directory has been successfully deleted.
+  // |aData| will contain the directory path.
+  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+                     const char16_t* aData) override;
+
+  // Create a new GMP plugin directory that we can trash and add it to the GMP
+  // service. Remove the original plugin directory. Original plugin directory
+  // gets re-added at destruction.
+  void Setup();
+
+  bool CreateVideoDecoder(nsCString aNodeId = EmptyCString());
+  void CloseVideoDecoder();
+
+  void DeletePluginDirectory(bool aCanDefer);
+
+  // Decode a dummy frame.
+  GMPErr Decode();
+
+  // Wait until TestMonitor has been signaled.
+  void Wait();
+
+  // Did we get a Terminated() callback from the plugin?
+  bool IsTerminated();
+
+  // From GMPVideoDecoderCallbackProxy
+  // Set mDecodeResult; unblock TestMonitor.
+  virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
+  virtual void Error(GMPErr aError) override;
+
+  // From GMPVideoDecoderCallbackProxy
+  // We expect this to be called when a plugin has been forcibly closed.
+  virtual void Terminated() override;
+
+  // Ignored GMPVideoDecoderCallbackProxy members
+  virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override {}
+  virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {}
+  virtual void InputDataExhausted() override {}
+  virtual void DrainComplete() override {}
+  virtual void ResetComplete() override {}
+
+private:
+  virtual ~GMPRemoveTest();
+
+  void gmp_Decode();
+  void gmp_GetVideoDecoder(nsCString aNodeId,
+                           GMPVideoDecoderProxy** aOutDecoder,
+                           GMPVideoHost** aOutHost);
+  void GeneratePlugin();
+
+  GMPTestMonitor mTestMonitor;
+  nsCOMPtr<nsIThread> mGMPThread;
+
+  bool mIsTerminated;
+
+  // Path to the cloned GMP we have created.
+  nsString mTmpPath;
+  nsCOMPtr<nsIFile> mTmpDir;
+
+  // Path to the original GMP. Store so that we can re-add it after we're done
+  // testing.
+  nsString mOriginalPath;
+
+  GMPVideoDecoderProxy* mDecoder;
+  GMPVideoHost* mHost;
+  GMPErr mDecodeResult;
+};
+
+/*
+ * Simple test that the plugin is deleted when forcibly removed and deleted.
+ */
+TEST(GeckoMediaPlugins, RemoveAndDeleteForcedSimple)
+{
+  nsRefPtr<GMPRemoveTest> test(new GMPRemoveTest());
+
+  test->Setup();
+  test->DeletePluginDirectory(false /* force immediate */);
+  test->Wait();
+}
+
+/*
+ * Simple test that the plugin is deleted when deferred deletion is allowed.
+ */
+TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredSimple)
+{
+  nsRefPtr<GMPRemoveTest> test(new GMPRemoveTest());
+
+  test->Setup();
+  test->DeletePluginDirectory(true /* can defer */);
+  test->Wait();
+}
+
+/*
+ * Test that the plugin is unavailable immediately after a forced
+ * RemoveAndDelete, and that the plugin is deleted afterwards.
+ */
+TEST(GeckoMediaPlugins, RemoveAndDeleteForcedInUse)
+{
+  nsRefPtr<GMPRemoveTest> test(new GMPRemoveTest());
+
+  test->Setup();
+  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
+
+  // Test that we can decode a frame.
+  GMPErr err = test->Decode();
+  EXPECT_EQ(err, GMPNoErr);
+
+  test->DeletePluginDirectory(false /* force immediate */);
+  test->Wait();
+
+  // Test that the VideoDecoder is no longer available.
+  EXPECT_FALSE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
+
+  // Test that we were notified of the plugin's destruction.
+  EXPECT_TRUE(test->IsTerminated());
+}
+
+/*
+ * Test that the plugin is still usable after a deferred RemoveAndDelete, and
+ * that the plugin is deleted afterwards.
+ */
+TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredInUse)
+{
+  nsRefPtr<GMPRemoveTest> test(new GMPRemoveTest());
+
+  test->Setup();
+  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
+
+  // Make sure decoding works before we do anything.
+  GMPErr err = test->Decode();
+  EXPECT_EQ(err, GMPNoErr);
+
+  test->DeletePluginDirectory(true /* can defer */);
+
+  // Test that decoding still works.
+  err = test->Decode();
+  EXPECT_EQ(err, GMPNoErr);
+
+  // Test that this origin is still able to fetch the video decoder.
+  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
+
+  test->CloseVideoDecoder();
+  test->Wait();
+}
+
+static StaticRefPtr<GeckoMediaPluginService> gService;
+static StaticRefPtr<GeckoMediaPluginServiceParent> gServiceParent;
+
+static GeckoMediaPluginService*
+GetService()
+{
+  if (!gService) {
+    nsRefPtr<GeckoMediaPluginService> service =
+      GeckoMediaPluginService::GetGeckoMediaPluginService();
+    gService = service;
+  }
+
+  return gService.get();
+}
+
+static GeckoMediaPluginServiceParent*
+GetServiceParent()
+{
+  if (!gServiceParent) {
+    nsRefPtr<GeckoMediaPluginServiceParent> parent =
+      GeckoMediaPluginServiceParent::GetSingleton();
+    gServiceParent = parent;
+  }
+
+  return gServiceParent.get();
+}
+
+NS_IMPL_ISUPPORTS(GMPRemoveTest, nsIObserver)
+
+GMPRemoveTest::GMPRemoveTest()
+  : mIsTerminated(false)
+  , mDecoder(nullptr)
+  , mHost(nullptr)
+{
+}
+
+GMPRemoveTest::~GMPRemoveTest()
+{
+  bool exists;
+  EXPECT_TRUE(NS_SUCCEEDED(mTmpDir->Exists(&exists)) && !exists);
+
+  EXPECT_OK(GetServiceParent()->AddPluginDirectory(mOriginalPath));
+}
+
+void
+GMPRemoveTest::Setup()
+{
+  GeneratePlugin();
+  EXPECT_OK(GetServiceParent()->RemovePluginDirectory(mOriginalPath));
+
+  GetServiceParent()->AddPluginDirectory(mTmpPath);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  obs->AddObserver(this, GMP_DELETED_TOPIC, false /* strong ref */);
+
+  GetService()->GetThread(getter_AddRefs(mGMPThread));
+}
+
+bool
+GMPRemoveTest::CreateVideoDecoder(nsCString aNodeId)
+{
+  GMPVideoHost* host;
+  GMPVideoDecoderProxy* decoder = nullptr;
+
+  mGMPThread->Dispatch(
+    NS_NewNonOwningRunnableMethodWithArgs<nsCString, GMPVideoDecoderProxy**, GMPVideoHost**>(
+      this, &GMPRemoveTest::gmp_GetVideoDecoder, aNodeId, &decoder, &host),
+    NS_DISPATCH_NORMAL);
+
+  mTestMonitor.AwaitFinished();
+
+  if (!decoder) {
+    return false;
+  }
+
+  GMPVideoCodec codec;
+  memset(&codec, 0, sizeof(codec));
+  codec.mGMPApiVersion = 33;
+
+  nsTArray<uint8_t> empty;
+  mGMPThread->Dispatch(
+    NS_NewNonOwningRunnableMethodWithArgs<const GMPVideoCodec&, const nsTArray<uint8_t>&, GMPVideoDecoderCallbackProxy*, int32_t>(
+      decoder, &GMPVideoDecoderProxy::InitDecode,
+      codec, empty, this, 1 /* core count */),
+    NS_DISPATCH_SYNC);
+
+  if (mDecoder) {
+    CloseVideoDecoder();
+  }
+
+  mDecoder = decoder;
+  mHost = host;
+
+  return true;
+}
+
+void
+GMPRemoveTest::gmp_GetVideoDecoder(nsCString aNodeId,
+                                   GMPVideoDecoderProxy** aOutDecoder,
+                                   GMPVideoHost** aOutHost)
+{
+  nsTArray<nsCString> tags;
+  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+  tags.AppendElement(NS_LITERAL_CSTRING("fake"));
+
+  class Callback : public GetGMPVideoDecoderCallback
+  {
+  public:
+    Callback(GMPTestMonitor* aMonitor, GMPVideoDecoderProxy** aDecoder, GMPVideoHost** aHost)
+      : mMonitor(aMonitor), mDecoder(aDecoder), mHost(aHost) { }
+    virtual void Done(GMPVideoDecoderProxy* aDecoder, GMPVideoHost* aHost) override {
+      *mDecoder = aDecoder;
+      *mHost = aHost;
+      mMonitor->SetFinished();
+    }
+  private:
+    GMPTestMonitor* mMonitor;
+    GMPVideoDecoderProxy** mDecoder;
+    GMPVideoHost** mHost;
+  };
+
+  UniquePtr<GetGMPVideoDecoderCallback>
+    cb(new Callback(&mTestMonitor, aOutDecoder, aOutHost));
+
+  if (NS_FAILED(GetService()->GetGMPVideoDecoder(&tags, aNodeId, Move(cb)))) {
+    mTestMonitor.SetFinished();
+  }
+}
+
+void
+GMPRemoveTest::CloseVideoDecoder()
+{
+  mGMPThread->Dispatch(
+    NS_NewNonOwningRunnableMethod(mDecoder, &GMPVideoDecoderProxy::Close),
+    NS_DISPATCH_SYNC);
+
+  mDecoder = nullptr;
+  mHost = nullptr;
+}
+
+void
+GMPRemoveTest::DeletePluginDirectory(bool aCanDefer)
+{
+  GetServiceParent()->RemoveAndDeletePluginDirectory(mTmpPath, aCanDefer);
+}
+
+GMPErr
+GMPRemoveTest::Decode()
+{
+  mGMPThread->Dispatch(
+    NS_NewNonOwningRunnableMethod(this, &GMPRemoveTest::gmp_Decode),
+    NS_DISPATCH_NORMAL);
+
+  mTestMonitor.AwaitFinished();
+  return mDecodeResult;
+}
+
+void
+GMPRemoveTest::gmp_Decode()
+{
+  // from gmp-fake.cpp
+  struct EncodedFrame {
+    uint32_t length_;
+    uint8_t h264_compat_;
+    uint32_t magic_;
+    uint32_t width_;
+    uint32_t height_;
+    uint8_t y_;
+    uint8_t u_;
+    uint8_t v_;
+    uint32_t timestamp_;
+  };
+
+  GMPVideoFrame* absFrame;
+  GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &absFrame);
+  EXPECT_EQ(err, GMPNoErr);
+
+  GMPUniquePtr<GMPVideoEncodedFrame>
+    frame(static_cast<GMPVideoEncodedFrame*>(absFrame));
+  err = frame->CreateEmptyFrame(sizeof(EncodedFrame) /* size */);
+  EXPECT_EQ(err, GMPNoErr);
+
+  EncodedFrame* frameData = reinterpret_cast<EncodedFrame*>(frame->Buffer());
+  frameData->magic_ = 0x4652414d;
+  frameData->width_ = frameData->height_ = 16;
+
+  nsTArray<uint8_t> empty;
+  nsresult rv = mDecoder->Decode(Move(frame), false /* aMissingFrames */, empty);
+  EXPECT_OK(rv);
+}
+
+void
+GMPRemoveTest::Wait()
+{
+  mTestMonitor.AwaitFinished();
+}
+
+bool
+GMPRemoveTest::IsTerminated()
+{
+  return mIsTerminated;
+}
+
+// nsIObserver
+NS_IMETHODIMP
+GMPRemoveTest::Observe(nsISupports* aSubject, const char* aTopic,
+                       const char16_t* aData)
+{
+  EXPECT_TRUE(!strcmp(GMP_DELETED_TOPIC, aTopic));
+
+  nsString data(aData);
+  if (mTmpPath.Equals(data)) {
+    mTestMonitor.SetFinished();
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->RemoveObserver(this, GMP_DELETED_TOPIC);
+  }
+
+  return NS_OK;
+}
+
+// GMPVideoDecoderCallbackProxy
+void
+GMPRemoveTest::Decoded(GMPVideoi420Frame* aDecodedFrame)
+{
+  aDecodedFrame->Destroy();
+  mDecodeResult = GMPNoErr;
+  mTestMonitor.SetFinished();
+}
+
+// GMPVideoDecoderCallbackProxy
+void
+GMPRemoveTest::Error(GMPErr aError)
+{
+  mDecodeResult = aError;
+  mTestMonitor.SetFinished();
+}
+
+// GMPVideoDecoderCallbackProxy
+void
+GMPRemoveTest::Terminated()
+{
+  mIsTerminated = true;
+}
+
+void
+GMPRemoveTest::GeneratePlugin()
+{
+  nsresult rv;
+  nsCOMPtr<nsIFile> gmpDir;
+  nsCOMPtr<nsIFile> origDir;
+  nsCOMPtr<nsIFile> tmpDir;
+
+  rv = NS_GetSpecialDirectory(NS_GRE_DIR,
+                              getter_AddRefs(gmpDir));
+  EXPECT_OK(rv);
+  rv = gmpDir->Append(GMP_DIR_NAME);
+  EXPECT_OK(rv);
+
+  rv = gmpDir->Clone(getter_AddRefs(origDir));
+  EXPECT_OK(rv);
+  rv = origDir->Append(GMP_OLD_VERSION);
+  EXPECT_OK(rv);
+
+  rv = origDir->CopyTo(gmpDir, GMP_NEW_VERSION);
+  EXPECT_OK(rv);
+
+  rv = gmpDir->Clone(getter_AddRefs(tmpDir));
+  EXPECT_OK(rv);
+  rv = tmpDir->Append(GMP_NEW_VERSION);
+  EXPECT_OK(rv);
+
+  EXPECT_OK(origDir->GetPath(mOriginalPath));
+  EXPECT_OK(tmpDir->GetPath(mTmpPath));
+  mTmpDir = tmpDir;
+}
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'MockMediaResource.cpp',
     'TestAudioCompactor.cpp',
     'TestGMPCrossOrigin.cpp',
+    'TestGMPRemoveAndDelete.cpp',
     'TestMP4Demuxer.cpp',
     'TestMP4Reader.cpp',
     'TestTrackEncoder.cpp',
     'TestVideoSegment.cpp',
     'TestWebMBuffered.cpp',
 ]
 
 if CONFIG['MOZ_EME']:
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -18,16 +18,17 @@ with Files('RTC*'):
 component_av = ('Core', 'WebRTC: Audio/Video')
 with Files('GetUserMedia*'):
     BUG_COMPONENT = component_av
 
 DIRS += [
     'encoder',
     'gmp',
     'gmp-plugin',
+    'gmp-plugin-openh264',
     'imagecapture',
     'mediasource',
     'ogg',
     'systemservices',
     'webaudio',
     'webrtc',
     'webspeech',
     'webvtt',
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1541,17 +1541,17 @@ PeerConnectionWrapper.prototype = {
    *        A promise that resolves when we're receiving the tone from |from|.
    */
   checkReceivingToneFrom : function(from) {
     var inputElem = from.localMediaElements[0];
 
     // As input we use the stream of |from|'s first available audio sender.
     var inputSenderTracks = from._pc.getSenders().map(sn => sn.track);
     var inputAudioStream = from._pc.getLocalStreams()
-      .find(s => s.getAudioTracks().some(t => inputSenderTracks.includes(t)));
+      .find(s => s.getAudioTracks().some(t => inputSenderTracks.some(t2 => t == t2)));
     var inputAnalyser = new AudioStreamAnalyser(inputAudioStream);
 
     // It would have been nice to have a working getReceivers() here, but until
     // we do, let's use what remote streams we have.
     var outputAudioStream = this._pc.getRemoteStreams()
       .find(s => s.getAudioTracks().length > 0);
     var outputAnalyser = new AudioStreamAnalyser(outputAudioStream);
 
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -11,19 +11,19 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/PodOperations.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode)
 
-class AnalyserNodeEngine : public AudioNodeEngine
+class AnalyserNodeEngine final : public AudioNodeEngine
 {
-  class TransferBuffer : public nsRunnable
+  class TransferBuffer final : public nsRunnable
   {
   public:
     TransferBuffer(AudioNodeStream* aStream,
                    const AudioChunk& aChunk)
       : mStream(aStream)
       , mChunk(aChunk)
     {
     }
--- a/dom/media/webaudio/AnalyserNode.h
+++ b/dom/media/webaudio/AnalyserNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "FFTBlock.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class AnalyserNode : public AudioNode
+class AnalyserNode final : public AudioNode
 {
 public:
   explicit AnalyserNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -48,21 +48,21 @@ NS_IMPL_ADDREF_INHERITED(AudioBufferSour
 NS_IMPL_RELEASE_INHERITED(AudioBufferSourceNode, AudioNode)
 
 /**
  * Media-thread playback engine for AudioBufferSourceNode.
  * Nothing is played until a non-null buffer has been set (via
  * AudioNodeStream::SetBuffer) and a non-zero mBufferEnd has been set (via
  * AudioNodeStream::SetInt32Parameter).
  */
-class AudioBufferSourceNodeEngine : public AudioNodeEngine
+class AudioBufferSourceNodeEngine final : public AudioNodeEngine
 {
 public:
-  explicit AudioBufferSourceNodeEngine(AudioNode* aNode,
-                                       AudioDestinationNode* aDestination) :
+  AudioBufferSourceNodeEngine(AudioNode* aNode,
+                              AudioDestinationNode* aDestination) :
     AudioNodeEngine(aNode),
     mStart(0.0), mBeginProcessing(0),
     mStop(STREAM_TIME_MAX),
     mResampler(nullptr), mRemainingResamplerTail(0),
     mBufferEnd(0),
     mLoopStart(0), mLoopEnd(0),
     mBufferSampleRate(0), mBufferPosition(0), mChannels(0),
     mDopplerShift(1.0f),
@@ -708,22 +708,22 @@ AudioBufferSourceNode::Stop(double aWhen
 
   ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
 }
 
 void
 AudioBufferSourceNode::NotifyMainThreadStateChanged()
 {
   if (mStream->IsFinished()) {
-    class EndedEventDispatcher : public nsRunnable
+    class EndedEventDispatcher final : public nsRunnable
     {
     public:
       explicit EndedEventDispatcher(AudioBufferSourceNode* aNode)
         : mNode(aNode) {}
-      NS_IMETHODIMP Run()
+      NS_IMETHODIMP Run() override
       {
         // If it's not safe to run scripts right now, schedule this to run later
         if (!nsContentUtils::IsSafeToRunScript()) {
           nsContentUtils::AddScriptRunner(this);
           return NS_OK;
         }
 
         mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
--- a/dom/media/webaudio/AudioBufferSourceNode.h
+++ b/dom/media/webaudio/AudioBufferSourceNode.h
@@ -10,18 +10,18 @@
 #include "AudioNode.h"
 #include "AudioBuffer.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioParam;
 
-class AudioBufferSourceNode : public AudioNode,
-                              public MainThreadMediaStreamListener
+class AudioBufferSourceNode final : public AudioNode,
+                                    public MainThreadMediaStreamListener
 {
 public:
   explicit AudioBufferSourceNode(AudioContext* aContext);
 
   virtual void DestroyMediaStream() override
   {
     if (mStream) {
       mStream->RemoveMainThreadListener(this);
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -25,17 +25,17 @@
 #include "nsWidgetsCID.h"
 #include "mozilla/dom/Promise.h"
 
 namespace mozilla {
 namespace dom {
 
 static uint8_t gWebAudioOutputKey;
 
-class OfflineDestinationNodeEngine : public AudioNodeEngine
+class OfflineDestinationNodeEngine final : public AudioNodeEngine
 {
 public:
   typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
 
   OfflineDestinationNodeEngine(AudioDestinationNode* aNode,
                                uint32_t aNumberOfChannels,
                                uint32_t aLength,
                                float aSampleRate)
@@ -130,17 +130,17 @@ public:
   class OnCompleteTask final : public nsRunnable
   {
   public:
     OnCompleteTask(AudioContext* aAudioContext, AudioBuffer* aRenderedBuffer)
       : mAudioContext(aAudioContext)
       , mRenderedBuffer(aRenderedBuffer)
     {}
 
-    NS_IMETHOD Run()
+    NS_IMETHOD Run() override
     {
       nsRefPtr<OfflineAudioCompletionEvent> event =
           new OfflineAudioCompletionEvent(mAudioContext, nullptr, nullptr);
       event->InitEvent(mRenderedBuffer);
       mAudioContext->DispatchTrustedEvent(event);
 
       return NS_OK;
     }
@@ -205,27 +205,27 @@ private:
   uint32_t mWriteIndex;
   uint32_t mNumberOfChannels;
   // How many frames the OfflineAudioContext intends to produce.
   uint32_t mLength;
   float mSampleRate;
   bool mBufferAllocated;
 };
 
-class InputMutedRunnable : public nsRunnable
+class InputMutedRunnable final : public nsRunnable
 {
 public:
   InputMutedRunnable(AudioNodeStream* aStream,
                      bool aInputMuted)
     : mStream(aStream)
     , mInputMuted(aInputMuted)
   {
   }
 
-  NS_IMETHOD Run()
+  NS_IMETHOD Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
     nsRefPtr<AudioNode> node = mStream->Engine()->NodeMainThread();
 
     if (node) {
       nsRefPtr<AudioDestinationNode> destinationNode =
         static_cast<AudioDestinationNode*>(node.get());
       destinationNode->InputMuted(mInputMuted);
@@ -233,17 +233,17 @@ public:
     return NS_OK;
   }
 
 private:
   nsRefPtr<AudioNodeStream> mStream;
   bool mInputMuted;
 };
 
-class DestinationNodeEngine : public AudioNodeEngine
+class DestinationNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit DestinationNodeEngine(AudioDestinationNode* aNode)
     : AudioNodeEngine(aNode)
     , mVolume(1.0f)
     , mLastInputMuted(false)
   {
     MOZ_ASSERT(aNode);
--- a/dom/media/webaudio/AudioEventTimeline.h
+++ b/dom/media/webaudio/AudioEventTimeline.h
@@ -16,18 +16,20 @@
 #include "math.h"
 #include "WebAudioUtils.h"
 
 namespace mozilla {
 
 namespace dom {
 
 // This is an internal helper class and should not be used outside of this header.
-struct AudioTimelineEvent {
-  enum Type : uint32_t {
+struct AudioTimelineEvent final
+{
+  enum Type : uint32_t
+  {
     SetValue,
     LinearRamp,
     ExponentialRamp,
     SetTarget,
     SetValueCurve
   };
 
   AudioTimelineEvent(Type aType, double aTime, float aValue, double aTimeConstant = 0.0,
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -320,17 +320,18 @@ AudioNode::Disconnect(uint32_t aOutput, 
     return;
   }
 
   // An upstream node may be starting to play on the graph thread, and the
   // engine for a downstream node may be sending a PlayingRefChangeHandler
   // ADDREF message to this (main) thread.  Wait for a round trip before
   // releasing nodes, to give engines receiving sound now time to keep their
   // nodes alive.
-  class RunnableRelease : public nsRunnable {
+  class RunnableRelease final : public nsRunnable
+  {
   public:
     explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
       : mNode(aNode) {}
 
     NS_IMETHODIMP Run() override
     {
       mNode = nullptr;
       return NS_OK;
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -69,17 +69,18 @@ public:
 
   // This should be idempotent (safe to call multiple times).
   virtual void DestroyMediaStream();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode,
                                            DOMEventTargetHelper)
 
-  virtual AudioBufferSourceNode* AsAudioBufferSourceNode() {
+  virtual AudioBufferSourceNode* AsAudioBufferSourceNode()
+  {
     return nullptr;
   }
 
   AudioContext* GetParentObject() const
   {
     return mContext;
   }
 
@@ -132,17 +133,18 @@ public:
     return mChannelInterpretation;
   }
   void SetChannelInterpretationValue(ChannelInterpretation aMode)
   {
     mChannelInterpretation = aMode;
     SendChannelMixingParametersToStream();
   }
 
-  struct InputNode {
+  struct InputNode final
+  {
     ~InputNode()
     {
       if (mStreamPort) {
         mStreamPort->Destroy();
       }
     }
 
     size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -22,27 +22,29 @@ class DelayNodeEngine;
 class AudioNodeStream;
 
 /**
  * This class holds onto a set of immutable channel buffers. The storage
  * for the buffers must be malloced, but the buffer pointers and the malloc
  * pointers can be different (e.g. if the buffers are contained inside
  * some malloced object).
  */
-class ThreadSharedFloatArrayBufferList : public ThreadSharedObject {
+class ThreadSharedFloatArrayBufferList final : public ThreadSharedObject
+{
 public:
   /**
    * Construct with null data.
    */
   explicit ThreadSharedFloatArrayBufferList(uint32_t aCount)
   {
     mContents.SetLength(aCount);
   }
 
-  struct Storage {
+  struct Storage final
+  {
     Storage() :
       mDataToFree(nullptr),
       mFree(nullptr),
       mSampleData(nullptr)
     {}
     ~Storage() {
       if (mFree) {
         mFree(mDataToFree);
@@ -229,17 +231,18 @@ AudioBlockPanStereoToStereo(const float 
  */
 float
 AudioBufferSumOfSquares(const float* aInput, uint32_t aLength);
 
 /**
  * All methods of this class and its subclasses are called on the
  * MediaStreamGraph thread.
  */
-class AudioNodeEngine {
+class AudioNodeEngine
+{
 public:
   // This should be compatible with AudioNodeStream::OutputChunks.
   typedef nsAutoTArray<AudioChunk, 1> OutputChunks;
 
   explicit AudioNodeEngine(dom::AudioNode* aNode)
     : mNode(aNode)
     , mNodeMutex("AudioNodeEngine::mNodeMutex")
     , mInputCount(aNode ? aNode->NumberOfInputs() : 1)
--- a/dom/media/webaudio/AudioNodeExternalInputStream.h
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.h
@@ -13,24 +13,27 @@
 namespace mozilla {
 
 /**
  * This is a MediaStream implementation that acts for a Web Audio node but
  * unlike other AudioNodeStreams, supports any kind of MediaStream as an
  * input --- handling any number of audio tracks and handling blocking of
  * the input MediaStream.
  */
-class AudioNodeExternalInputStream : public AudioNodeStream {
+class AudioNodeExternalInputStream final : public AudioNodeStream
+{
 public:
-  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate, uint32_t aContextId);
+  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate,
+                               uint32_t aContextId);
 protected:
   ~AudioNodeExternalInputStream();
 
 public:
-  virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
+  virtual void ProcessInput(GraphTime aFrom, GraphTime aTo,
+                            uint32_t aFlags) override;
 
 private:
   /**
    * Determines if this is enabled or not.  Disabled nodes produce silence.
    * This node becomes disabled if the document principal does not subsume the
    * DOMMediaStream principal.
    */
   bool IsEnabled();
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -90,23 +90,25 @@ AudioNodeStream::SizeOfAudioNodesIncludi
     mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
   }
 }
 
 void
 AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                                         double aStreamTime)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
             double aStreamTime)
       : ControlMessage(aStream), mStreamTime(aStreamTime),
-        mRelativeToStream(aRelativeToStream), mIndex(aIndex) {}
-    virtual void Run()
+        mRelativeToStream(aRelativeToStream), mIndex(aIndex)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->
           SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
     }
     double mStreamTime;
     MediaStream* mRelativeToStream;
     uint32_t mIndex;
   };
@@ -122,151 +124,163 @@ AudioNodeStream::SetStreamTimeParameterI
 {
   StreamTime ticks = TicksFromDestinationTime(aRelativeToStream, aStreamTime);
   mEngine->SetStreamTimeParameter(aIndex, ticks);
 }
 
 void
 AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, double aValue)
-      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
-    virtual void Run()
+      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetDoubleParameter(mIndex, mValue);
     }
     double mValue;
     uint32_t mIndex;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, int32_t aValue)
-      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
-    virtual void Run()
+      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetInt32Parameter(mIndex, mValue);
     }
     int32_t mValue;
     uint32_t mIndex;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
                                       const AudioParamTimeline& aValue)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex,
             const AudioParamTimeline& aValue)
       : ControlMessage(aStream),
         mValue(aValue),
         mSampleRate(aStream->SampleRate()),
-        mIndex(aIndex) {}
-    virtual void Run()
+        mIndex(aIndex)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetTimelineParameter(mIndex, mValue, mSampleRate);
     }
     AudioParamTimeline mValue;
     TrackRate mSampleRate;
     uint32_t mIndex;
   };
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, const ThreeDPoint& aValue)
-      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
-    virtual void Run()
+      : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetThreeDPointParameter(mIndex, mValue);
     }
     ThreeDPoint mValue;
     uint32_t mIndex;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream,
             already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
-      : ControlMessage(aStream), mBuffer(aBuffer) {}
-    virtual void Run()
+      : ControlMessage(aStream), mBuffer(aBuffer)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetBuffer(mBuffer.forget());
     }
     nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aBuffer));
 }
 
 void
 AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream,
             nsTArray<float>& aData)
       : ControlMessage(aStream)
     {
       mData.SwapElements(aData);
     }
-    virtual void Run()
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
     }
     nsTArray<float> mData;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aData));
 }
 
 void
 AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
                                             ChannelCountMode aChannelCountMode,
                                             ChannelInterpretation aChannelInterpretation)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream,
             uint32_t aNumberOfChannels,
             ChannelCountMode aChannelCountMode,
             ChannelInterpretation aChannelInterpretation)
       : ControlMessage(aStream),
         mNumberOfChannels(aNumberOfChannels),
         mChannelCountMode(aChannelCountMode),
         mChannelInterpretation(aChannelInterpretation)
     {}
-    virtual void Run()
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->
         SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
                                        mChannelInterpretation);
     }
     uint32_t mNumberOfChannels;
     ChannelCountMode mChannelCountMode;
     ChannelInterpretation mChannelInterpretation;
@@ -275,21 +289,23 @@ AudioNodeStream::SetChannelMixingParamet
   GraphImpl()->AppendMessage(new Message(this, aNumberOfChannels,
                                          aChannelCountMode,
                                          aChannelInterpretation));
 }
 
 void
 AudioNodeStream::SetPassThrough(bool aPassThrough)
 {
-  class Message : public ControlMessage {
+  class Message final : public ControlMessage
+  {
   public:
     Message(AudioNodeStream* aStream, bool aPassThrough)
-      : ControlMessage(aStream), mPassThrough(aPassThrough) {}
-    virtual void Run()
+      : ControlMessage(aStream), mPassThrough(aPassThrough)
+    {}
+    virtual void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
     }
     bool mPassThrough;
   };
 
   GraphImpl()->AppendMessage(new Message(this, aPassThrough));
 }
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -26,17 +26,18 @@ class AudioNodeEngine;
  * The start time of the AudioTrack is aligned to the start time of the
  * AudioContext's destination node stream, plus some multiple of BLOCK_SIZE
  * samples.
  *
  * An AudioNodeStream has an AudioNodeEngine plugged into it that does the
  * actual audio processing. AudioNodeStream contains the glue code that
  * integrates audio processing with the MediaStreamGraph.
  */
-class AudioNodeStream : public ProcessedMediaStream {
+class AudioNodeStream : public ProcessedMediaStream
+{
   typedef dom::ChannelCountMode ChannelCountMode;
   typedef dom::ChannelInterpretation ChannelInterpretation;
 
 public:
   typedef mozilla::dom::AudioContext AudioContext;
 
   enum { AUDIO_TRACK = 1 };
 
--- a/dom/media/webaudio/AudioProcessingEvent.h
+++ b/dom/media/webaudio/AudioProcessingEvent.h
@@ -9,17 +9,17 @@
 
 #include "AudioBuffer.h"
 #include "ScriptProcessorNode.h"
 #include "mozilla/dom/Event.h"
 
 namespace mozilla {
 namespace dom {
 
-class AudioProcessingEvent : public Event
+class AudioProcessingEvent final : public Event
 {
 public:
   AudioProcessingEvent(ScriptProcessorNode* aOwner,
                        nsPresContext* aPresContext,
                        WidgetEvent* aEvent);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_TO_EVENT
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -68,17 +68,17 @@ SetParamsOnBiquad(WebCore::Biquad& aBiqu
     aBiquad.setAllpassParams(normalizedFrequency, aQ);
     break;
   default:
     NS_NOTREACHED("We should never see the alternate names here");
     break;
   }
 }
 
-class BiquadFilterNodeEngine : public AudioNodeEngine
+class BiquadFilterNodeEngine final : public AudioNodeEngine
 {
 public:
   BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
     // Keep the default values in sync with the default values in
     // BiquadFilterNode::BiquadFilterNode
--- a/dom/media/webaudio/BiquadFilterNode.h
+++ b/dom/media/webaudio/BiquadFilterNode.h
@@ -11,17 +11,17 @@
 #include "AudioParam.h"
 #include "mozilla/dom/BiquadFilterNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class BiquadFilterNode : public AudioNode
+class BiquadFilterNode final : public AudioNode
 {
 public:
   explicit BiquadFilterNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BiquadFilterNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -13,17 +13,17 @@
 #include "mozilla/ReentrantMonitor.h"
 
 namespace mozilla {
 
 /**
  * This class provides a decoder object which decodes a media file that lives in
  * a memory buffer.
  */
-class BufferDecoder : public AbstractMediaDecoder
+class BufferDecoder final : public AbstractMediaDecoder
 {
 public:
   // This class holds a weak pointer to MediaResource.  It's the responsibility
   // of the caller to manage the memory of the MediaResource object.
   explicit BufferDecoder(MediaResource* aResource);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
--- a/dom/media/webaudio/ChannelMergerNode.cpp
+++ b/dom/media/webaudio/ChannelMergerNode.cpp
@@ -9,17 +9,17 @@
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS_INHERITED0(ChannelMergerNode, AudioNode)
 
-class ChannelMergerNodeEngine : public AudioNodeEngine
+class ChannelMergerNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit ChannelMergerNodeEngine(ChannelMergerNode* aNode)
     : AudioNodeEngine(aNode)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
--- a/dom/media/webaudio/ChannelMergerNode.h
+++ b/dom/media/webaudio/ChannelMergerNode.h
@@ -9,17 +9,17 @@
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class ChannelMergerNode : public AudioNode
+class ChannelMergerNode final : public AudioNode
 {
 public:
   ChannelMergerNode(AudioContext* aContext,
                     uint16_t aInputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/ChannelSplitterNode.cpp
+++ b/dom/media/webaudio/ChannelSplitterNode.cpp
@@ -9,17 +9,17 @@
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS_INHERITED0(ChannelSplitterNode, AudioNode)
 
-class ChannelSplitterNodeEngine : public AudioNodeEngine
+class ChannelSplitterNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit ChannelSplitterNodeEngine(ChannelSplitterNode* aNode)
     : AudioNodeEngine(aNode)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
--- a/dom/media/webaudio/ChannelSplitterNode.h
+++ b/dom/media/webaudio/ChannelSplitterNode.h
@@ -9,17 +9,17 @@
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class ChannelSplitterNode : public AudioNode
+class ChannelSplitterNode final : public AudioNode
 {
 public:
   ChannelSplitterNode(AudioContext* aContext,
                       uint16_t aOutputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -17,17 +17,17 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ConvolverNode, AudioNode, mBuffer)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ConvolverNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(ConvolverNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
 
-class ConvolverNodeEngine : public AudioNodeEngine
+class ConvolverNodeEngine final : public AudioNodeEngine
 {
   typedef PlayingRefChangeHandler PlayingRefChanged;
 public:
   ConvolverNodeEngine(AudioNode* aNode, bool aNormalize)
     : AudioNodeEngine(aNode)
     , mBufferLength(0)
     , mLeftOverData(INT32_MIN)
     , mSampleRate(0.0f)
--- a/dom/media/webaudio/ConvolverNode.h
+++ b/dom/media/webaudio/ConvolverNode.h
@@ -8,17 +8,17 @@
 #define ConvolverNode_h_
 
 #include "AudioNode.h"
 #include "AudioBuffer.h"
 
 namespace mozilla {
 namespace dom {
 
-class ConvolverNode : public AudioNode
+class ConvolverNode final : public AudioNode
 {
 public:
   explicit ConvolverNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ConvolverNode, AudioNode);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/DelayBuffer.h
+++ b/dom/media/webaudio/DelayBuffer.h
@@ -8,17 +8,18 @@
 #define DelayBuffer_h_
 
 #include "nsTArray.h"
 #include "AudioSegment.h"
 #include "mozilla/dom/AudioNodeBinding.h" // for ChannelInterpretation
 
 namespace mozilla {
 
-class DelayBuffer {
+class DelayBuffer final
+{
   typedef dom::ChannelInterpretation ChannelInterpretation;
 
 public:
   // See WebAudioUtils::ComputeSmoothingRate() for frame to frame exponential
   // |smoothingRate| multiplier.
   DelayBuffer(double aMaxDelayTicks, double aSmoothingRate)
     : mSmoothingRate(aSmoothingRate)
     , mCurrentDelay(-1.0)
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -20,17 +20,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Delay
                                    mDelay)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
 
-class DelayNodeEngine : public AudioNodeEngine
+class DelayNodeEngine final : public AudioNodeEngine
 {
   typedef PlayingRefChangeHandler PlayingRefChanged;
 public:
   DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
                   double aMaxDelayTicks)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
--- a/dom/media/webaudio/DelayNode.h
+++ b/dom/media/webaudio/DelayNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class DelayNode : public AudioNode
+class DelayNode final : public AudioNode
 {
 public:
   DelayNode(AudioContext* aContext, double aMaxDelay);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DelayNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/DynamicsCompressorNode.cpp
+++ b/dom/media/webaudio/DynamicsCompressorNode.cpp
@@ -25,17 +25,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Dynam
                                    mRelease)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode)
 
-class DynamicsCompressorNodeEngine : public AudioNodeEngine
+class DynamicsCompressorNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit DynamicsCompressorNodeEngine(AudioNode* aNode,
                                         AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
     // Keep the default value in sync with the default value in
@@ -146,26 +146,26 @@ public:
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
-    class Command : public nsRunnable
+    class Command final : public nsRunnable
     {
     public:
       Command(AudioNodeStream* aStream, float aReduction)
         : mStream(aStream)
         , mReduction(aReduction)
       {
       }
 
-      NS_IMETHODIMP Run()
+      NS_IMETHOD Run() override
       {
         nsRefPtr<DynamicsCompressorNode> node;
         {
           // No need to keep holding the lock for the whole duration of this
           // function, since we're holding a strong reference to it, so if
           // we can obtain the reference, we will hold the node alive in
           // this function.
           MutexAutoLock lock(mStream->Engine()->NodeMutex());
--- a/dom/media/webaudio/DynamicsCompressorNode.h
+++ b/dom/media/webaudio/DynamicsCompressorNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class DynamicsCompressorNode : public AudioNode
+class DynamicsCompressorNode final : public AudioNode
 {
 public:
   explicit DynamicsCompressorNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DynamicsCompressorNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/FFTBlock.h
+++ b/dom/media/webaudio/FFTBlock.h
@@ -11,17 +11,18 @@
 #include "AudioNodeEngine.h"
 #include "kiss_fft/kiss_fftr.h"
 
 namespace mozilla {
 
 // This class defines an FFT block, loosely modeled after Blink's FFTFrame
 // class to make sharing code with Blink easy.
 // Currently it's implemented on top of KissFFT on all platforms.
-class FFTBlock {
+class FFTBlock final
+{
 public:
   explicit FFTBlock(uint32_t aFFTSize)
     : mFFT(nullptr)
     , mIFFT(nullptr)
     , mFFTSize(aFFTSize)
   {
     MOZ_COUNT_CTOR(FFTBlock);
     mOutputBuffer.SetLength(aFFTSize / 2 + 1);
--- a/dom/media/webaudio/GainNode.cpp
+++ b/dom/media/webaudio/GainNode.cpp
@@ -18,17 +18,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(GainN
                                    mGain)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GainNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
 
-class GainNodeEngine : public AudioNodeEngine
+class GainNodeEngine final : public AudioNodeEngine
 {
 public:
   GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
     // Keep the default value in sync with the default value in GainNode::GainNode.
     , mGain(1.f)
--- a/dom/media/webaudio/GainNode.h
+++ b/dom/media/webaudio/GainNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "AudioParam.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class GainNode : public AudioNode
+class GainNode final : public AudioNode
 {
 public:
   explicit GainNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -45,17 +45,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WebAudioDecodeJob)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebAudioDecodeJob, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebAudioDecodeJob, Release)
 
 using namespace dom;
 
-class ReportResultTask : public nsRunnable
+class ReportResultTask final : public nsRunnable
 {
 public:
   ReportResultTask(WebAudioDecodeJob& aDecodeJob,
                    WebAudioDecodeJob::ResultFn aFunction,
                    WebAudioDecodeJob::ErrorCode aErrorCode)
     : mDecodeJob(aDecodeJob)
     , mFunction(aFunction)
     , mErrorCode(aErrorCode)
@@ -77,23 +77,24 @@ private:
   // Therefore, it is not safe to do anything fancy with it in this class.
   // Really, this class is only used because nsRunnableMethod doesn't support
   // methods accepting arguments.
   WebAudioDecodeJob& mDecodeJob;
   WebAudioDecodeJob::ResultFn mFunction;
   WebAudioDecodeJob::ErrorCode mErrorCode;
 };
 
-enum class PhaseEnum : int {
+enum class PhaseEnum : int
+{
   Decode,
   AllocateBuffer,
   Done
 };
 
-class MediaDecodeTask : public nsRunnable
+class MediaDecodeTask final : public nsRunnable
 {
 public:
   MediaDecodeTask(const char* aContentType, uint8_t* aBuffer,
                   uint32_t aLength,
                   WebAudioDecodeJob& aDecodeJob)
     : mContentType(aContentType)
     , mBuffer(aBuffer)
     , mLength(aLength)
@@ -209,17 +210,18 @@ MediaDecodeTask::CreateReader()
 
   if (!mDecoderReader->EnsureTaskQueue()) {
     return false;
   }
 
   return true;
 }
 
-class AutoResampler {
+class AutoResampler final
+{
 public:
   AutoResampler()
     : mResampler(nullptr)
   {}
   ~AutoResampler()
   {
     if (mResampler) {
       speex_resampler_destroy(mResampler);
--- a/dom/media/webaudio/MediaElementAudioSourceNode.h
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.h
@@ -7,17 +7,17 @@
 #ifndef MediaElementAudioSourceNode_h_
 #define MediaElementAudioSourceNode_h_
 
 #include "MediaStreamAudioSourceNode.h"
 
 namespace mozilla {
 namespace dom {
 
-class MediaElementAudioSourceNode : public MediaStreamAudioSourceNode
+class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode
 {
 public:
   MediaElementAudioSourceNode(AudioContext* aContext,
                               DOMMediaStream* aStream);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual const char* NodeType() const override
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.h
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.h
@@ -7,17 +7,17 @@
 #ifndef MediaStreamAudioDestinationNode_h_
 #define MediaStreamAudioDestinationNode_h_
 
 #include "AudioNode.h"
 
 namespace mozilla {
 namespace dom {
 
-class MediaStreamAudioDestinationNode : public AudioNode
+class MediaStreamAudioDestinationNode final : public AudioNode
 {
 public:
   explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioDestinationNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "DOMMediaStream.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 
 namespace dom {
 
-class MediaStreamAudioSourceNodeEngine : public AudioNodeEngine
+class MediaStreamAudioSourceNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit MediaStreamAudioSourceNodeEngine(AudioNode* aNode)
     : AudioNodeEngine(aNode), mEnabled(false) {}
 
   bool IsEnabled() const { return mEnabled; }
   enum Parameters {
     ENABLE
--- a/dom/media/webaudio/OfflineAudioCompletionEvent.h
+++ b/dom/media/webaudio/OfflineAudioCompletionEvent.h
@@ -10,17 +10,17 @@
 #include "AudioBuffer.h"
 #include "mozilla/dom/Event.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class OfflineAudioCompletionEvent : public Event
+class OfflineAudioCompletionEvent final : public Event
 {
 public:
   OfflineAudioCompletionEvent(AudioContext* aOwner,
                               nsPresContext* aPresContext,
                               WidgetEvent* aEvent);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_TO_EVENT
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -18,17 +18,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Oscil
                                    mPeriodicWave, mFrequency, mDetune)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
 
-class OscillatorNodeEngine : public AudioNodeEngine
+class OscillatorNodeEngine final : public AudioNodeEngine
 {
 public:
   OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
     , mStart(-1)
     , mStop(STREAM_TIME_MAX)
@@ -510,22 +510,22 @@ OscillatorNode::Stop(double aWhen, Error
   ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
                              Context(), std::max(0.0, aWhen));
 }
 
 void
 OscillatorNode::NotifyMainThreadStateChanged()
 {
   if (mStream->IsFinished()) {
-    class EndedEventDispatcher : public nsRunnable
+    class EndedEventDispatcher final : public nsRunnable
     {
     public:
       explicit EndedEventDispatcher(OscillatorNode* aNode)
         : mNode(aNode) {}
-      NS_IMETHODIMP Run()
+      NS_IMETHOD Run() override
       {
         // If it's not safe to run scripts right now, schedule this to run later
         if (!nsContentUtils::IsSafeToRunScript()) {
           nsContentUtils::AddScriptRunner(this);
           return NS_OK;
         }
 
         mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
--- a/dom/media/webaudio/OscillatorNode.h
+++ b/dom/media/webaudio/OscillatorNode.h
@@ -13,18 +13,18 @@
 #include "mozilla/dom/OscillatorNodeBinding.h"
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class OscillatorNode : public AudioNode,
-                       public MainThreadMediaStreamListener
+class OscillatorNode final : public AudioNode,
+                             public MainThreadMediaStreamListener
 {
 public:
   explicit OscillatorNode(AudioContext* aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -34,17 +34,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PannerNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(PannerNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(PannerNode, AudioNode)
 
-class PannerNodeEngine : public AudioNodeEngine
+class PannerNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit PannerNodeEngine(AudioNode* aNode)
     : AudioNodeEngine(aNode)
     // Please keep these default values consistent with PannerNode::PannerNode below.
     , mPanningModelFunction(&PannerNodeEngine::EqualPowerPanningFunction)
     , mDistanceModelFunction(&PannerNodeEngine::InverseGainFunction)
     , mPosition()
--- a/dom/media/webaudio/PannerNode.h
+++ b/dom/media/webaudio/PannerNode.h
@@ -16,18 +16,18 @@
 #include <set>
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 class AudioBufferSourceNode;
 
-class PannerNode : public AudioNode,
-                   public SupportsWeakPtr<PannerNode>
+class PannerNode final : public AudioNode,
+                         public SupportsWeakPtr<PannerNode>
 {
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PannerNode)
   explicit PannerNode(AudioContext* aContext);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void DestroyMediaStream() override;
--- a/dom/media/webaudio/PlayingRefChangeHandler.h
+++ b/dom/media/webaudio/PlayingRefChangeHandler.h
@@ -8,17 +8,17 @@
 #define PlayingRefChangeHandler_h__
 
 #include "nsThreadUtils.h"
 #include "AudioNodeStream.h"
 
 namespace mozilla {
 namespace dom {
 
-class PlayingRefChangeHandler : public nsRunnable
+class PlayingRefChangeHandler final : public nsRunnable
 {
 public:
   enum ChangeType { ADDREF, RELEASE };
   PlayingRefChangeHandler(AudioNodeStream* aStream, ChangeType aChange)
     : mStream(aStream)
     , mChange(aChange)
   {
   }
--- a/dom/media/webaudio/ReportDecodeResultTask.h
+++ b/dom/media/webaudio/ReportDecodeResultTask.h
@@ -7,17 +7,17 @@
 #ifndef ReportDecodeResultTask_h_
 #define ReportDecodeResultTask_h_
 
 #include "mozilla/Attributes.h"
 #include "MediaBufferDecoder.h"
 
 namespace mozilla {
 
-class ReportDecodeResultTask : public nsRunnable
+class ReportDecodeResultTask final : public nsRunnable
 {
 public:
   ReportDecodeResultTask(DecodeJob& aDecodeJob,
                          DecodeJob::ResultFn aFunction)
     : mDecodeJob(aDecodeJob)
     , mFunction(aFunction)
   {
     MOZ_ASSERT(aFunction);
--- a/dom/media/webaudio/ScriptProcessorNode.cpp
+++ b/dom/media/webaudio/ScriptProcessorNode.cpp
@@ -23,20 +23,20 @@ namespace dom {
 // The maximum latency, in seconds, that we can live with before dropping
 // buffers.
 static const float MAX_LATENCY_S = 0.5;
 
 NS_IMPL_ISUPPORTS_INHERITED0(ScriptProcessorNode, AudioNode)
 
 // This class manages a queue of output buffers shared between
 // the main thread and the Media Stream Graph thread.
-class SharedBuffers
+class SharedBuffers final
 {
 private:
-  class OutputQueue
+  class OutputQueue final
   {
   public:
     explicit OutputQueue(const char* aName)
       : mMutex(aName)
     {}
 
     size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     {
@@ -232,17 +232,17 @@ private:
   float mLatency;
   // This is the time at which we last produced a buffer, to detect if the main
   // thread has been blocked.
   TimeStamp mLastEventTime;
   // True if we should be dropping buffers.
   bool mDroppingBuffers;
 };
 
-class ScriptProcessorNodeEngine : public AudioNodeEngine
+class ScriptProcessorNodeEngine final : public AudioNodeEngine
 {
 public:
   typedef nsAutoTArray<nsAutoArrayPtr<float>, 2> InputChannels;
 
   ScriptProcessorNodeEngine(ScriptProcessorNode* aNode,
                             AudioDestinationNode* aDestination,
                             uint32_t aBufferSize,
                             uint32_t aNumberOfInputChannels)
@@ -356,17 +356,17 @@ private:
     // Add the duration of the current sample
     playbackTick += WEBAUDIO_BLOCK_SIZE;
     // Add the delay caused by the main thread
     playbackTick += mSharedBuffers->DelaySoFar();
     // Compute the playback time in the coordinate system of the destination
     double playbackTime =
       mSource->DestinationTimeFromTicks(mDestination, playbackTick);
 
-    class Command : public nsRunnable
+    class Command final : public nsRunnable
     {
     public:
       Command(AudioNodeStream* aStream,
               InputChannels& aInputChannels,
               double aPlaybackTime,
               bool aNullInput)
         : mStream(aStream)
         , mPlaybackTime(aPlaybackTime)
@@ -375,17 +375,17 @@ private:
         mInputChannels.SetLength(aInputChannels.Length());
         if (!aNullInput) {
           for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
             mInputChannels[i] = aInputChannels[i].forget();
           }
         }
       }
 
-      NS_IMETHODIMP Run()
+      NS_IMETHOD Run() override
       {
         nsRefPtr<ScriptProcessorNode> node = static_cast<ScriptProcessorNode*>
           (mStream->Engine()->NodeMainThread());
         if (!node) {
           return NS_OK;
         }
         AudioContext* context = node->Context();
         if (!context) {
--- a/dom/media/webaudio/ScriptProcessorNode.h
+++ b/dom/media/webaudio/ScriptProcessorNode.h
@@ -11,17 +11,17 @@
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 class SharedBuffers;
 
-class ScriptProcessorNode : public AudioNode
+class ScriptProcessorNode final : public AudioNode
 {
 public:
   ScriptProcessorNode(AudioContext* aContext,
                       uint32_t aBufferSize,
                       uint32_t aNumberOfInputChannels,
                       uint32_t aNumberOfOutputChannels);
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/media/webaudio/StereoPannerNode.cpp
+++ b/dom/media/webaudio/StereoPannerNode.cpp
@@ -22,17 +22,17 @@ using namespace std;
 NS_IMPL_CYCLE_COLLECTION_INHERITED(StereoPannerNode, AudioNode, mPan)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(StereoPannerNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(StereoPannerNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(StereoPannerNode, AudioNode)
 
-class StereoPannerNodeEngine : public AudioNodeEngine
+class StereoPannerNodeEngine final : public AudioNodeEngine
 {
 public:
   StereoPannerNodeEngine(AudioNode* aNode,
                          AudioDestinationNode* aDestination)
     : AudioNodeEngine(aNode)
     , mSource(nullptr)
     , mDestination(static_cast<AudioNodeStream*>(aDestination->Stream()))
     // Keep the default value in sync with the default value in
--- a/dom/media/webaudio/StereoPannerNode.h
+++ b/dom/media/webaudio/StereoPannerNode.h
@@ -10,17 +10,17 @@
 #include "AudioNode.h"
 #include "mozilla/dom/StereoPannerNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class StereoPannerNode : public AudioNode
+class StereoPannerNode final : public AudioNode
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(StereoPannerNode)
   explicit StereoPannerNode(AudioContext* aContext);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
--- a/dom/media/webaudio/ThreeDPoint.h
+++ b/dom/media/webaudio/ThreeDPoint.h
@@ -9,17 +9,18 @@
 
 #include <cmath>
 #include <algorithm>
 
 namespace mozilla {
 
 namespace dom {
 
-struct ThreeDPoint {
+struct ThreeDPoint final
+{
   ThreeDPoint()
     : x(0.)
     , y(0.)
     , z(0.)
   {
   }
   ThreeDPoint(double aX, double aY, double aZ)
     : x(aX)
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -43,17 +43,17 @@ static uint32_t ValueOf(OverSampleType a
   case OverSampleType::_2x:  return 2;
   case OverSampleType::_4x:  return 4;
   default:
     NS_NOTREACHED("We should never reach here");
     return 1;
   }
 }
 
-class Resampler
+class Resampler final
 {
 public:
   Resampler()
     : mType(OverSampleType::None)
     , mUpSampler(nullptr)
     , mDownSampler(nullptr)
     , mChannels(0)
     , mSampleRate(0)
@@ -156,17 +156,17 @@ private:
   OverSampleType mType;
   SpeexResamplerState* mUpSampler;
   SpeexResamplerState* mDownSampler;
   uint32_t mChannels;
   TrackRate mSampleRate;
   nsTArray<float> mBuffer;
 };
 
-class WaveShaperNodeEngine : public AudioNodeEngine
+class WaveShaperNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit WaveShaperNodeEngine(AudioNode* aNode)
     : AudioNodeEngine(aNode)
     , mType(OverSampleType::None)
   {
   }
 
--- a/dom/media/webaudio/WaveShaperNode.h
+++ b/dom/media/webaudio/WaveShaperNode.h
@@ -11,17 +11,17 @@
 #include "mozilla/dom/WaveShaperNodeBinding.h"
 #include "mozilla/dom/TypedArray.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
-class WaveShaperNode : public AudioNode
+class WaveShaperNode final : public AudioNode
 {
 public:
   explicit WaveShaperNode(AudioContext *aContext);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WaveShaperNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -127,17 +127,17 @@ var interfaceNamesInGlobalScope =
     {name: "MozAbortablePromise", pref: "dom.abortablepromise.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "AlarmsManager", pref: "dom.mozAlarms.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AnalyserNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Animation", pref: "dom.animations-api.core.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "AnimationEffectReadonly", pref: "dom.animations-api.core.enabled"},
+    {name: "AnimationEffectReadOnly", pref: "dom.animations-api.core.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AnimationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "AnimationTimeline", pref: "dom.animations-api.core.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Attr",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Audio",
@@ -646,17 +646,17 @@ var interfaceNamesInGlobalScope =
     {name: "InputPortManager", b2g: true, pref: "dom.inputport.enabled", permission: ["inputport"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "InstallTrigger", b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "KeyframeEffectReadonly", pref: "dom.animations-api.core.enabled"},
+    {name: "KeyframeEffectReadOnly", pref: "dom.animations-api.core.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "LocalMediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaDeviceInfo",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaDevices",
--- a/dom/webidl/Animation.webidl
+++ b/dom/webidl/Animation.webidl
@@ -11,17 +11,17 @@
  */
 
 enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" };
 
 [Func="nsDocument::IsWebAnimationsEnabled"]
 interface Animation {
   // Bug 1049975: Make 'effect' writeable
   [Pure]
-  readonly attribute AnimationEffectReadonly? effect;
+  readonly attribute AnimationEffectReadOnly? effect;
   readonly attribute AnimationTimeline timeline;
   [BinaryName="startTimeAsDouble"]
   attribute double? startTime;
   [SetterThrows, BinaryName="currentTimeAsDouble"]
   attribute double? currentTime;
 
            attribute double             playbackRate;
   [BinaryName="playStateFromJS"]
rename from dom/webidl/AnimationEffectReadonly.webidl
rename to dom/webidl/AnimationEffectReadOnly.webidl
--- a/dom/webidl/AnimationEffectReadonly.webidl
+++ b/dom/webidl/AnimationEffectReadOnly.webidl
@@ -6,13 +6,13 @@
  * The origin of this IDL file is
  * http://w3c.github.io/web-animations/#animationeffectreadonly
  *
  * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Func="nsDocument::IsWebAnimationsEnabled"]
-interface AnimationEffectReadonly {
+interface AnimationEffectReadOnly {
   // Not yet implemented:
-  // readonly attribute AnimationEffectTimingReadonly timing;
+  // readonly attribute AnimationEffectTimingReadOnly timing;
   // readonly attribute ComputedTimingProperties      computedTiming;
 };
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -7,17 +7,17 @@
  * http://w3c.github.io/web-animations/#the-keyframeeffect-interfaces
  *
  * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [HeaderFile="mozilla/dom/KeyframeEffect.h",
  Func="nsDocument::IsWebAnimationsEnabled"]
-interface KeyframeEffectReadonly : AnimationEffectReadonly {
+interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
   readonly attribute Element?  target;
   readonly attribute DOMString name;
   // Not yet implemented:
   // readonly attribute IterationCompositeOperation iterationComposite;
   // readonly attribute CompositeOperation          composite;
   // readonly attribute DOMString                   spacing;
   // KeyframeEffect             clone();
   // sequence<ComputedKeyframe> getFrames ();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -18,17 +18,17 @@ PREPROCESSED_WEBIDL_FILES = [
 WEBIDL_FILES = [
     'AbortablePromise.webidl',
     'AbstractWorker.webidl',
     'ActivityRequestHandler.webidl',
     'AlarmsManager.webidl',
     'AnalyserNode.webidl',
     'Animatable.webidl',
     'Animation.webidl',
-    'AnimationEffectReadonly.webidl',
+    'AnimationEffectReadOnly.webidl',
     'AnimationEvent.webidl',
     'AnimationTimeline.webidl',
     'AnonymousContent.webidl',
     'AppInfo.webidl',
     'AppNotificationServiceOptions.webidl',
     'Apps.webidl',
     'APZTestData.webidl',
     'ArchiveReader.webidl',
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -8,16 +8,17 @@
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIHttpChannel.h"
 #include "nsIInputStreamPump.h"
 #include "nsIIOService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsISerializable.h"
 #include "nsIStreamLoader.h"
 #include "nsIStreamListenerTee.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIURI.h"
 
 #include "jsapi.h"
 #include "nsError.h"
 #include "nsContentPolicyUtils.h"
@@ -415,16 +416,17 @@ private:
 
   ScriptLoadInfo& mLoadInfo;
   uint32_t mIndex;
   nsRefPtr<ScriptLoaderRunnable> mRunnable;
   bool mIsWorkerScript;
   bool mFailed;
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIURI> mBaseURI;
+  nsCString mSecurityInfo;
 };
 
 NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
 
 class CachePromiseHandler final : public PromiseNativeHandler
 {
 public:
   CachePromiseHandler(ScriptLoaderRunnable* aRunnable,
@@ -452,26 +454,28 @@ private:
 
   nsRefPtr<ScriptLoaderRunnable> mRunnable;
   ScriptLoadInfo& mLoadInfo;
   uint32_t mIndex;
 };
 
 class ScriptLoaderRunnable final : public WorkerFeature,
                                    public nsIRunnable,
-                                   public nsIStreamLoaderObserver
+                                   public nsIStreamLoaderObserver,
+                                   public nsIRequestObserver
 {
   friend class ScriptExecutorRunnable;
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
 
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   nsTArray<ScriptLoadInfo> mLoadInfos;
   nsRefPtr<CacheCreator> mCacheCreator;
+  nsCOMPtr<nsIInputStream> mReader;
   bool mIsMainScript;
   WorkerScriptType mWorkerScriptType;
   bool mCanceled;
   bool mCanceledMainThread;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
@@ -555,16 +559,89 @@ private:
     ScriptLoadInfo& loadInfo = mLoadInfos[index];
 
     nsresult rv = OnStreamCompleteInternal(aLoader, aContext, aStatus,
                                            aStringLen, aString, loadInfo);
     LoadingFinished(index, rv);
     return NS_OK;
   }
 
+  NS_IMETHOD
+  OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
+    MOZ_ASSERT(indexSupports, "This should never fail!");
+
+    uint32_t index = UINT32_MAX;
+    if (NS_FAILED(indexSupports->GetData(&index)) ||
+        index >= mLoadInfos.Length()) {
+      MOZ_CRASH("Bad index!");
+    }
+
+    ScriptLoadInfo& loadInfo = mLoadInfos[index];
+
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+    MOZ_ASSERT(channel == loadInfo.mChannel);
+
+    // We synthesize the result code, but its never exposed to content.
+    nsRefPtr<InternalResponse> ir =
+      new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
+    ir->SetBody(mReader);
+
+    // Set the security info of the channel on the response so that it's
+    // saved in the cache.
+    nsCOMPtr<nsISupports> infoObj;
+    channel->GetSecurityInfo(getter_AddRefs(infoObj));
+    if (infoObj) {
+      nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
+      if (serializable) {
+        ir->SetSecurityInfo(serializable);
+        MOZ_ASSERT(!ir->GetSecurityInfo().IsEmpty());
+      } else {
+        NS_WARNING("A non-serializable object was obtained from nsIChannel::GetSecurityInfo()!");
+      }
+    }
+
+    nsRefPtr<Response> response = new Response(mCacheCreator->Global(), ir);
+
+    RequestOrUSVString request;
+
+    MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
+    request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
+                                    loadInfo.mFullURL.Length());
+
+    ErrorResult error;
+    nsRefPtr<Promise> cachePromise =
+      mCacheCreator->Cache_()->Put(request, *response, error);
+    if (NS_WARN_IF(error.Failed())) {
+      nsresult rv = error.StealNSResult();
+      channel->Cancel(rv);
+      return rv;
+    }
+
+    nsRefPtr<CachePromiseHandler> promiseHandler =
+      new CachePromiseHandler(this, loadInfo, index);
+    cachePromise->AppendNativeHandler(promiseHandler);
+
+    loadInfo.mCachePromise.swap(cachePromise);
+    loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+                nsresult aStatusCode)
+  {
+    // Nothing to do here!
+    return NS_OK;
+  }
+
   virtual bool
   Notify(JSContext* aCx, Status aStatus) override
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (aStatus >= Terminating && !mCanceled) {
       mCanceled = true;
 
@@ -768,69 +845,39 @@ private:
     }
 
     if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
       rv = channel->AsyncOpen(loader, indexSupports);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else {
-      nsCOMPtr<nsIInputStream> reader;
       nsCOMPtr<nsIOutputStream> writer;
 
       // In case we return early.
       loadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
 
-      rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer), 0,
+      rv = NS_NewPipe(getter_AddRefs(mReader), getter_AddRefs(writer), 0,
                       UINT32_MAX, // unlimited size to avoid writer WOULD_BLOCK case
                       true, false); // non-blocking reader, blocking writer
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      // We synthesize the result code, but its never exposed to content.
-      nsRefPtr<InternalResponse> ir =
-        new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-      ir->SetBody(reader);
-
       nsCOMPtr<nsIStreamListenerTee> tee =
         do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
-      rv = tee->Init(loader, writer, nullptr);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = channel->AsyncOpen(tee, indexSupports);
+      rv = tee->Init(loader, writer, this);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      nsRefPtr<Response> response = new Response(mCacheCreator->Global(), ir);
-
-      RequestOrUSVString request;
-
-      MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
-      request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
-                                      loadInfo.mFullURL.Length());
-
-      ErrorResult error;
-      nsRefPtr<Promise> cachePromise =
-        mCacheCreator->Cache_()->Put(request, *response, error);
-      if (NS_WARN_IF(error.Failed())) {
-        nsresult rv = error.StealNSResult();
-        channel->Cancel(rv);
+      nsresult rv = channel->AsyncOpen(tee, indexSupports);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-
-      nsRefPtr<CachePromiseHandler> promiseHandler =
-        new CachePromiseHandler(this, loadInfo, aIndex);
-      cachePromise->AppendNativeHandler(promiseHandler);
-
-      loadInfo.mCachePromise.swap(cachePromise);
-      loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
     }
 
     loadInfo.mChannel.swap(channel);
 
     return NS_OK;
   }
 
   nsresult
@@ -912,16 +959,30 @@ private:
     }
 
     // Update the principal of the worker and its base URI if we just loaded the
     // worker's primary script.
     if (IsMainWorkerScript()) {
       // Take care of the base URI first.
       mWorkerPrivate->SetBaseURI(finalURI);
 
+      // Store the security info if needed.
+      if (mWorkerPrivate->IsServiceWorker()) {
+        nsCOMPtr<nsISupports> infoObj;
+        channel->GetSecurityInfo(getter_AddRefs(infoObj));
+        if (infoObj) {
+          nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
+          if (serializable) {
+            mWorkerPrivate->SetSecurityInfo(serializable);
+          } else {
+            NS_WARNING("A non-serializable object was obtained from nsIChannel::GetSecurityInfo()!");
+          }
+        }
+      }
+
       // Now to figure out which principal to give this worker.
       WorkerPrivate* parent = mWorkerPrivate->GetParent();
 
       NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
                    "Must have one of these!");
 
       nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
                                              mWorkerPrivate->GetPrincipal() :
@@ -985,17 +1046,17 @@ private:
     }
 
     DataReceived();
     return NS_OK;
   }
 
   void
   DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
-                        uint32_t aStringLen)
+                        uint32_t aStringLen, const nsCString& aSecurityInfo)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
     MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
 
     // May be null.
     nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
@@ -1013,16 +1074,17 @@ private:
       if (NS_SUCCEEDED(rv)) {
         mWorkerPrivate->SetBaseURI(finalURI);
       }
 
       nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
       MOZ_ASSERT(principal);
       nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
       MOZ_ASSERT(loadGroup);
+      mWorkerPrivate->SetSecurityInfo(aSecurityInfo);
       // Needed to initialize the principal info. This is fine because
       // the cache principal cannot change, unlike the channel principal.
       mWorkerPrivate->SetPrincipal(principal, loadGroup);
     }
 
     if (NS_SUCCEEDED(rv)) {
       DataReceived();
     }
@@ -1096,17 +1158,19 @@ private:
                                    firstIndex, lastIndex);
       if (!runnable->Dispatch(nullptr)) {
         MOZ_ASSERT(false, "This should never fail!");
       }
     }
   }
 };
 
-NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable, nsIStreamLoaderObserver)
+NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable,
+                                        nsIStreamLoaderObserver,
+                                        nsIRequestObserver)
 
 void
 CachePromiseHandler::ResolvedCallback(JSContext* aCx,
                                       JS::Handle<JS::Value> aValue)
 {
   AssertIsOnMainThread();
   // May already have been canceled by CacheScriptLoader::Fail from
   // CancelMainThread.
@@ -1364,20 +1428,21 @@ CacheScriptLoader::ResolvedCallback(JSCo
   nsresult rv = UNWRAP_OBJECT(Response, obj, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
   }
 
   nsCOMPtr<nsIInputStream> inputStream;
   response->GetBody(getter_AddRefs(inputStream));
+  mSecurityInfo = response->GetSecurityInfo();
 
   if (!inputStream) {
     mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
-    mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0);
+    mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mSecurityInfo);
     return;
   }
 
   MOZ_ASSERT(!mPump);
   rv = NS_NewInputStreamPump(getter_AddRefs(mPump), inputStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
@@ -1423,17 +1488,17 @@ CacheScriptLoader::OnStreamComplete(nsIS
 
   if (NS_FAILED(aStatus)) {
     Fail(aStatus);
     return NS_OK;
   }
 
   mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
 
-  mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen);
+  mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mSecurityInfo);
   return NS_OK;
 }
 
  class ChannelGetterRunnable final : public nsRunnable
 {
   WorkerPrivate* mParentWorker;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   const nsAString& mScriptURL;
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -13,16 +13,17 @@
 #include "nsContentUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsNetCID.h"
 #include "nsSerializationHelper.h"
 #include "nsQueryObject.h"
 
+#include "mozilla/Preferences.h"
 #include "mozilla/dom/FetchEventBinding.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/workers/bindings/ServiceWorker.h"
 
 #include "WorkerPrivate.h"
@@ -92,31 +93,40 @@ public:
     return NS_OK;
   }
 };
 
 class FinishResponse final : public nsRunnable
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
   nsRefPtr<InternalResponse> mInternalResponse;
+  nsCString mWorkerSecurityInfo;
 public:
   FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                 InternalResponse* aInternalResponse)
+                 InternalResponse* aInternalResponse,
+                 const nsCString& aWorkerSecurityInfo)
     : mChannel(aChannel)
     , mInternalResponse(aInternalResponse)
+    , mWorkerSecurityInfo(aWorkerSecurityInfo)
   {
   }
 
   NS_IMETHOD
   Run()
   {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsISupports> infoObj;
-    nsresult rv = NS_DeserializeObject(mInternalResponse->GetSecurityInfo(), getter_AddRefs(infoObj));
+    nsAutoCString securityInfo(mInternalResponse->GetSecurityInfo());
+    if (securityInfo.IsEmpty()) {
+      // We are dealing with a synthesized response here, so fall back to the
+      // security info for the worker script.
+      securityInfo = mWorkerSecurityInfo;
+    }
+    nsresult rv = NS_DeserializeObject(securityInfo, getter_AddRefs(infoObj));
     if (NS_SUCCEEDED(rv)) {
       rv = mChannel->SetSecurityInfo(infoObj);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     mChannel->SynthesizeStatus(mInternalResponse->GetStatus(), mInternalResponse->GetStatusText());
@@ -154,31 +164,36 @@ public:
 
   void CancelRequest();
 };
 
 struct RespondWithClosure
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   nsRefPtr<InternalResponse> mInternalResponse;
+  nsCString mWorkerSecurityInfo;
 
   RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     InternalResponse* aInternalResponse)
+                     InternalResponse* aInternalResponse,
+                     const nsCString& aWorkerSecurityInfo)
     : mInterceptedChannel(aChannel)
     , mInternalResponse(aInternalResponse)
+    , mWorkerSecurityInfo(aWorkerSecurityInfo)
   {
   }
 };
 
 void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
 {
   nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
   nsCOMPtr<nsIRunnable> event;
   if (NS_SUCCEEDED(aStatus)) {
-    event = new FinishResponse(data->mInterceptedChannel, data->mInternalResponse);
+    event = new FinishResponse(data->mInterceptedChannel,
+                               data->mInternalResponse,
+                               data->mWorkerSecurityInfo);
   } else {
     event = new CancelChannelRunnable(data->mInterceptedChannel);
   }
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
 }
 
 class MOZ_STACK_CLASS AutoCancel
 {
@@ -229,17 +244,22 @@ RespondWithHandler::ResolvedCallback(JSC
     return;
   }
 
   nsRefPtr<InternalResponse> ir = response->GetInternalResponse();
   if (NS_WARN_IF(!ir)) {
     return;
   }
 
-  nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir));
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  nsAutoPtr<RespondWithClosure> closure(
+      new RespondWithClosure(mInterceptedChannel, ir, worker->GetSecurityInfo()));
   nsCOMPtr<nsIInputStream> body;
   response->GetBody(getter_AddRefs(body));
   // Errors and redirects may not have a body.
   if (body) {
     response->SetBodyUsed();
 
     nsCOMPtr<nsIOutputStream> responseBody;
     rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -27,16 +27,17 @@
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIWorkerDebugger.h"
 #include "nsIXPConnect.h"
 #include "nsPerformance.h"
 #include "nsPIDOMWindow.h"
+#include "nsSerializationHelper.h"
 
 #include <algorithm>
 #include "jsfriendapi.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
@@ -4077,16 +4078,27 @@ WorkerPrivateParent<Derived>::SetPrincip
 
   mLoadInfo.mPrincipalInfo = new PrincipalInfo();
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
     PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
 }
 
 template <class Derived>
+void
+WorkerPrivateParent<Derived>::SetSecurityInfo(nsISerializable* aSerializable)
+{
+  MOZ_ASSERT(IsServiceWorker());
+  AssertIsOnMainThread();
+  nsAutoCString securityInfo;
+  NS_SerializeToString(aSerializable, securityInfo);
+  SetSecurityInfo(securityInfo);
+}
+
+template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
   if (mParent) {
     return mParent->GetJSContext();
   }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -32,16 +32,17 @@
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
 class nsIDocument;
 class nsIEventTarget;
 class nsIPrincipal;
 class nsIScriptContext;
+class nsISerializable;
 class nsIThread;
 class nsIThreadInternal;
 class nsITimer;
 class nsIURI;
 
 namespace JS {
 struct RuntimeStats;
 }
@@ -486,16 +487,35 @@ public:
   const nsString&
   ServiceWorkerCacheName() const
   {
     MOZ_ASSERT(IsServiceWorker());
     AssertIsOnMainThread();
     return mLoadInfo.mServiceWorkerCacheName;
   }
 
+  const nsCString&
+  GetSecurityInfo() const
+  {
+    MOZ_ASSERT(IsServiceWorker());
+    return mLoadInfo.mSecurityInfo;
+  }
+
+  void
+  SetSecurityInfo(const nsCString& aSecurityInfo)
+  {
+    MOZ_ASSERT(IsServiceWorker());
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mLoadInfo.mSecurityInfo.IsEmpty());
+    mLoadInfo.mSecurityInfo = aSecurityInfo;
+  }
+
+  void
+  SetSecurityInfo(nsISerializable* aSerializable);
+
   // This is used to handle importScripts(). When the worker is first loaded
   // and executed, it happens in a sync loop. At this point it sets
   // mLoadingWorkerScript to true. importScripts() calls that occur during the
   // execution run in nested sync loops and so this continues to return true,
   // leading to these scripts being cached offline.
   // mLoadingWorkerScript is set to false when the top level loop ends.
   // importScripts() in function calls or event handlers are always fetched
   // from the network.
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -238,16 +238,18 @@ struct WorkerLoadInfo
   // Only set if we have a custom overriden load group
   nsRefPtr<InterfaceRequestor> mInterfaceRequestor;
 
   nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
   nsCString mDomain;
 
   nsString mServiceWorkerCacheName;
 
+  nsCString mSecurityInfo;
+
   uint64_t mWindowID;
 
   bool mFromWindow;
   bool mEvalAllowed;
   bool mReportCSPViolations;
   bool mXHRParamsAllowed;
   bool mPrincipalIsSystem;
   bool mIsInPrivilegedApp;
--- a/dom/workers/test/serviceworkers/fetch/https/https_test.js
+++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js
@@ -2,10 +2,13 @@ self.addEventListener("install", functio
   event.waitUntil(caches.open("cache").then(function(cache) {
     return cache.add("index.html");
   }));
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index.html") >= 0) {
     event.respondWith(caches.match(event.request));
+  } else if (event.request.url.indexOf("synth.html") >= 0) {
+    event.respondWith(new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>',
+                                   {headers:{"Content-Type": "text/html"}}));
   }
 });
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -77,16 +77,17 @@ support-files =
   periodic/register.html
   periodic/unregister.html
 
 [test_unregister.html]
 [test_installation_simple.html]
 [test_fetch_event.html]
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
+[test_https_synth_fetch_from_cached_sw.html]
 [test_match_all.html]
 [test_match_all_advanced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
 [test_controller.html]
 [test_workerUnregister.html]
 [test_workerUpdate.html]
--- a/dom/workers/test/serviceworkers/test_https_fetch.html
+++ b/dom/workers/test/serviceworkers/test_https_fetch.html
@@ -26,16 +26,18 @@
       if (e.data.status == "ok") {
         ok(e.data.result, e.data.message);
       } else if (e.data.status == "registrationdone") {
         ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                            .getService(SpecialPowers.Ci.nsIIOService);
         ios.offline = true;
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/index.html";
       } else if (e.data.status == "done") {
+        iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html";
+      } else if (e.data.status == "done-synth") {
         ios.offline = false;
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/unregister.html";
       } else if (e.data.status == "unregistrationdone") {
         window.onmessage = null;
         ok(true, "Test finished successfully");
         SimpleTest.finish();
       }
     };
copy from dom/workers/test/serviceworkers/test_https_fetch.html
copy to dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
--- a/dom/workers/test/serviceworkers/test_https_fetch.html
+++ b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
@@ -1,22 +1,22 @@
 <!--
   Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Bug 1133763 - test fetch event in HTTPS origins</title>
+  <title>Bug 1156847 - test fetch event generating a synthesized response in HTTPS origins from a cached SW</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
-<div id="content" style="display: none">
+<div id="content" tyle="display: none">
 <iframe></iframe>
 </div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   var iframe;
   function runTest() {
     iframe = document.querySelector("iframe");
@@ -24,18 +24,31 @@
     var ios;
     window.onmessage = function(e) {
       if (e.data.status == "ok") {
         ok(e.data.result, e.data.message);
       } else if (e.data.status == "registrationdone") {
         ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                            .getService(SpecialPowers.Ci.nsIIOService);
         ios.offline = true;
-        iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/index.html";
-      } else if (e.data.status == "done") {
+
+        // In order to load synth.html from a cached service worker, we first
+        // remove the existing window that is keeping the service worker alive,
+        // and do a GC to ensure that the SW is destroyed.  This way, when we
+        // load synth.html for the second time, we will first recreate the
+        // service worker from the cache.  This is intended to test that we
+        // properly store and retrieve the security info from the cache.
+        iframe.parentNode.removeChild(iframe);
+        iframe = null;
+        SpecialPowers.exactGC(window, function() {
+          iframe = document.createElement("iframe");
+          iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html";
+          document.body.appendChild(iframe);
+        });
+      } else if (e.data.status == "done-synth") {
         ios.offline = false;
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/unregister.html";
       } else if (e.data.status == "unregistrationdone") {
         window.onmessage = null;
         ok(true, "Test finished successfully");
         SimpleTest.finish();
       }
     };
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -215,16 +215,19 @@ public:
 };
 
 class NoLog
 {
 public:
   NoLog() {}
   ~NoLog() {}
 
+  // No-op
+  MOZ_IMPLICIT NoLog(const NoLog&) {}
+
   template<typename T>
   NoLog &operator <<(const T &aLogText) { return *this; }
 };
 
 enum class LogOptions : int {
   NoNewline = 0x01,
   AutoPrefix = 0x02,
   AssertOnCall = 0x04
@@ -248,45 +251,31 @@ public:
     return (int(LogOptions::AutoPrefix) |
             (aWithAssert ? int(LogOptions::AssertOnCall) : 0));
   }
 
   // Note that we're calling BasicLogger::ShouldOutputMessage, rather than
   // Logger::ShouldOutputMessage.  Since we currently don't have a different
   // version of that method for different loggers, this is OK. Once we do,
   // change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
-  explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL))
-    : mOptions(aOptions)
-    , mLogIt(BasicLogger::ShouldOutputMessage(L))
-  {
-    if (mLogIt && AutoPrefix()) {
-      if (mOptions & int(LogOptions::AssertOnCall)) {
-        mMessage << "[GFX" << L << "]: ";
-      } else {
-        mMessage << "[GFX" << L << "-]: ";
-      }
-    }
+  explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL)) {
+    Init(aOptions, BasicLogger::ShouldOutputMessage(L));
   }
+
   ~Log() {
     Flush();
   }
 
   void Flush() {
     if (MOZ_LIKELY(!LogIt())) return;
 
     std::string str = mMessage.str();
     if (!str.empty()) {
       WriteLog(str);
     }
-    if (AutoPrefix()) {
-      mMessage.str("[GFX");
-      mMessage << L << "]: ";
-    } else {
-      mMessage.str("");
-    }
     mMessage.clear();
   }
 
   Log &operator <<(char aChar) {
     if (MOZ_UNLIKELY(LogIt())) {
       mMessage << aChar;
     }
     return *this;
@@ -473,18 +462,34 @@ public:
     }
     return *this;
   }
 
   inline bool LogIt() const { return mLogIt; }
   inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
   inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
 
+  // We do not want this version to do any work, and stringstream can't be
+  // copied anyway.  It does come in handy for the "Once" macro defined below.
+  MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false); }
 
 private:
+  // Initialization common to two constructors
+  void Init(int aOptions, bool aLogIt) {
+    mOptions = aOptions;
+    mLogIt = aLogIt;
+    if (mLogIt && AutoPrefix()) {
+      if (mOptions & int(LogOptions::AssertOnCall)) {
+        mMessage << "[GFX" << L << "]: ";
+      } else {
+        mMessage << "[GFX" << L << "-]: ";
+      }
+    }
+  }
+
   void WriteLog(const std::string &aString) {
     if (MOZ_UNLIKELY(LogIt())) {
       Logger::OutputMessage(aString, L, NoNewline());
       if (mOptions & int(LogOptions::AssertOnCall)) {
         MOZ_ASSERT(false, "An assert from the graphics logger");
       }
     }
   }
@@ -493,30 +498,50 @@ private:
   int mOptions;
   bool mLogIt;
 };
 
 typedef Log<LOG_DEBUG> DebugLog;
 typedef Log<LOG_WARNING> WarningLog;
 typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
 
+// Macro to glue names to get us less chance of name clashing.
+#if defined GFX_LOGGING_GLUE1 || defined GFX_LOGGING_GLUE
+#error "Clash of the macro GFX_LOGGING_GLUE1 or GFX_LOGGING_GLUE"
+#endif
+#define GFX_LOGGING_GLUE1(x, y)  x##y
+#define GFX_LOGGING_GLUE(x, y)   GFX_LOGGING_GLUE1(x, y)
+
+// This log goes into crash reports, use with care.
+#define gfxCriticalError mozilla::gfx::CriticalLog
+#define gfxCriticalErrorOnce static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxCriticalError
+
+// The "once" versions will only trigger the first time through. You can do this:
+// gfxCriticalErrorOnce() << "This message only shows up once;
+// instead of the usual:
+// static bool firstTime = true;
+// if (firstTime) {
+//   firstTime = false;
+//   gfxCriticalError() << "This message only shows up once;
+// }
 #ifdef GFX_LOG_DEBUG
 #define gfxDebug mozilla::gfx::DebugLog
+#define gfxDebugOnce static gfxDebug GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxDebug
 #else
 #define gfxDebug if (1) ; else mozilla::gfx::NoLog
+#define gfxDebugOnce if (1) ; else mozilla::gfx::NoLog
 #endif
 #ifdef GFX_LOG_WARNING
 #define gfxWarning mozilla::gfx::WarningLog
+#define gfxWarningOnce static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxWarning
 #else
 #define gfxWarning if (1) ; else mozilla::gfx::NoLog
+#define gfxWarningOnce if (1) ; else mozilla::gfx::NoLog
 #endif
 
-// This log goes into crash reports, use with care.
-#define gfxCriticalError mozilla::gfx::CriticalLog
-
 // See nsDebug.h and the NS_WARN_IF macro
 
 #ifdef __cplusplus
  // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds
 inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr,
                                 const char* aFile, int32_t aLine)
 {
   if (MOZ_UNLIKELY(aCondition)) {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -8,17 +8,17 @@
 #include <stdint.h>                     // for uint32_t
 #include "apz/src/AsyncPanZoomController.h"
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "LayerManagerComposite.h"      // for LayerManagerComposite, etc
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "gfxPoint.h"                   // for gfxPoint, gfxSize
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 #include "mozilla/WidgetUtils.h"        // for ComputeTransformForRotation
-#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadonly
+#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadOnly
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorParent.h" // for CompositorParent, etc
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
@@ -469,17 +469,17 @@ SampleAnimations(Layer* aLayer, TimeStam
     timing.mDirection = animation.direction();
     // Animations typically only run on the compositor during their active
     // interval but if we end up sampling them outside that range (for
     // example, while they are waiting to be removed) we currently just
     // assume that we should fill.
     timing.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH;
 
     ComputedTiming computedTiming =
-      dom::KeyframeEffectReadonly::GetComputedTimingAt(
+      dom::KeyframeEffectReadOnly::GetComputedTimingAt(
         Nullable<TimeDuration>(elapsedDuration), timing);
 
     MOZ_ASSERT(0.0 <= computedTiming.mTimeFraction &&
                computedTiming.mTimeFraction <= 1.0,
                "time fraction should be in [0-1]");
 
     int segmentIndex = 0;
     AnimationSegment* segment = animation.segments().Elements();
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -981,17 +981,18 @@ EnableScreenConfigurationNotifications()
 void
 DisableScreenConfigurationNotifications()
 {
 }
 
 void
 GetCurrentScreenConfiguration(hal::ScreenConfiguration* aScreenConfiguration)
 {
-  *aScreenConfiguration = nsScreenGonk::GetConfiguration();
+  nsRefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
+  *aScreenConfiguration = screen->GetConfiguration();
 }
 
 bool
 LockScreenOrientation(const dom::ScreenOrientation& aOrientation)
 {
   return OrientationObserver::GetInstance()->LockScreenOrientation(aOrientation);
 }
 
--- a/image/src/DynamicImage.cpp
+++ b/image/src/DynamicImage.cpp
@@ -19,22 +19,16 @@ using namespace mozilla::gfx;
 using mozilla::layers::LayerManager;
 using mozilla::layers::ImageContainer;
 
 namespace mozilla {
 namespace image {
 
 // Inherited methods from Image.
 
-nsresult
-DynamicImage::Init(const char* aMimeType, uint32_t aFlags)
-{
-  return NS_OK;
-}
-
 already_AddRefed<ProgressTracker>
 DynamicImage::GetProgressTracker()
 {
   return nullptr;
 }
 
 size_t
 DynamicImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
--- a/image/src/DynamicImage.h
+++ b/image/src/DynamicImage.h
@@ -26,18 +26,16 @@ public:
 
   explicit DynamicImage(gfxDrawable* aDrawable)
     : mDrawable(aDrawable)
   {
     MOZ_ASSERT(aDrawable, "Must have a gfxDrawable to wrap");
   }
 
   // Inherited methods from Image.
-  virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override;
-
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
   virtual size_t SizeOfSourceWithComputedFallback(
                                  MallocSizeOf aMallocSizeOf) const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual void IncrementAnimationConsumers() override;
   virtual void DecrementAnimationConsumers() override;
--- a/image/src/Image.cpp
+++ b/image/src/Image.cpp
@@ -58,16 +58,22 @@ ImageResource::ImageResource(ImageURL* a
   mInnerWindowId(0),
   mAnimationConsumers(0),
   mAnimationMode(kNormalAnimMode),
   mInitialized(false),
   mAnimating(false),
   mError(false)
 { }
 
+ImageResource::~ImageResource()
+{
+  // Ask our ProgressTracker to drop its weak reference to us.
+  mProgressTracker->ResetImage();
+}
+
 // Translates a mimetype into a concrete decoder
 Image::eDecoderType
 Image::GetDecoderType(const char* aMimeType)
 {
   // By default we don't know
   eDecoderType rv = eDecoderType_unknown;
 
   // PNG
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -168,25 +168,16 @@ public:
    */
   static const uint32_t INIT_FLAG_NONE                     = 0x0;
   static const uint32_t INIT_FLAG_DISCARDABLE              = 0x1;
   static const uint32_t INIT_FLAG_DECODE_ONLY_ON_DRAW      = 0x2;
   static const uint32_t INIT_FLAG_DECODE_IMMEDIATELY       = 0x4;
   static const uint32_t INIT_FLAG_TRANSIENT                = 0x8;
   static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE  = 0x10;
 
-  /**
-   * Creates a new image container.
-   *
-   * @param aMimeType The mimetype of the image.
-   * @param aFlags Initialization flags of the INIT_FLAG_* variety.
-   */
-  virtual nsresult Init(const char* aMimeType,
-                        uint32_t aFlags) = 0;
-
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() = 0;
   virtual void SetProgressTracker(ProgressTracker* aProgressTracker) {}
 
   /**
    * The size, in bytes, occupied by the compressed source data of the image.
    * If MallocSizeOf does not work on this platform, uses a fallback approach to
    * ensure that something reasonable is always returned.
    */
@@ -293,16 +284,17 @@ public:
   /*
    * Returns a non-AddRefed pointer to the URI associated with this image.
    * Illegal to use off-main-thread.
    */
   virtual ImageURL* GetURI() override { return mURI.get(); }
 
 protected:
   explicit ImageResource(ImageURL* aURI);
+  ~ImageResource();
 
   // Shared functionality for implementors of imgIContainer. Every
   // implementation of attribute animationMode should forward here.
   nsresult GetAnimationModeInternal(uint16_t* aAnimationMode);
   nsresult SetAnimationModeInternal(uint16_t aAnimationMode);
 
   /**
    * Helper for RequestRefresh.
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/Likely.h"
 
 #include "nsIHttpChannel.h"
 #include "nsIFileChannel.h"
 #include "nsIFile.h"
 #include "nsMimeTypes.h"
 #include "nsIRequest.h"
 
+#include "MultipartImage.h"
 #include "RasterImage.h"
 #include "VectorImage.h"
 #include "Image.h"
 #include "nsMediaFragmentURIParser.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 
 #include "ImageFactory.h"
@@ -144,22 +145,42 @@ BadImage(nsRefPtr<T>& image)
 
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateAnonymousImage(const nsCString& aMimeType)
 {
   nsresult rv;
 
   nsRefPtr<RasterImage> newImage = new RasterImage();
 
+  nsRefPtr<ProgressTracker> newTracker = new ProgressTracker();
+  newTracker->SetImage(newImage);
+  newImage->SetProgressTracker(newTracker);
+
   rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_NONE);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   return newImage.forget();
 }
 
+/* static */ already_AddRefed<MultipartImage>
+ImageFactory::CreateMultipartImage(Image* aFirstPart,
+                                   ProgressTracker* aProgressTracker)
+{
+  MOZ_ASSERT(aFirstPart);
+  MOZ_ASSERT(aProgressTracker);
+
+  nsRefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart);
+  aProgressTracker->SetImage(newImage);
+  newImage->SetProgressTracker(aProgressTracker);
+
+  newImage->Init();
+
+  return newImage.forget();
+}
+
 int32_t
 SaturateToInt32(int64_t val)
 {
   if (val > INT_MAX) {
     return INT_MAX;
   }
   if (val < INT_MIN) {
     return INT_MIN;
@@ -201,19 +222,23 @@ GetContentSize(nsIRequest* aRequest)
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateRasterImage(nsIRequest* aRequest,
                                 ProgressTracker* aProgressTracker,
                                 const nsCString& aMimeType,
                                 ImageURL* aURI,
                                 uint32_t aImageFlags,
                                 uint32_t aInnerWindowId)
 {
+  MOZ_ASSERT(aProgressTracker);
+
   nsresult rv;
 
-  nsRefPtr<RasterImage> newImage = new RasterImage(aProgressTracker, aURI);
+  nsRefPtr<RasterImage> newImage = new RasterImage(aURI);
+  aProgressTracker->SetImage(newImage);
+  newImage->SetProgressTracker(aProgressTracker);
 
   rv = newImage->Init(aMimeType.get(), aImageFlags);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   newImage->SetInnerWindowID(aInnerWindowId);
 
   uint32_t len = GetContentSize(aRequest);
 
@@ -263,19 +288,23 @@ ImageFactory::CreateRasterImage(nsIReque
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateVectorImage(nsIRequest* aRequest,
                                 ProgressTracker* aProgressTracker,
                                 const nsCString& aMimeType,
                                 ImageURL* aURI,
                                 uint32_t aImageFlags,
                                 uint32_t aInnerWindowId)
 {
+  MOZ_ASSERT(aProgressTracker);
+
   nsresult rv;
 
-  nsRefPtr<VectorImage> newImage = new VectorImage(aProgressTracker, aURI);
+  nsRefPtr<VectorImage> newImage = new VectorImage(aURI);
+  aProgressTracker->SetImage(newImage);
+  newImage->SetProgressTracker(aProgressTracker);
 
   rv = newImage->Init(aMimeType.get(), aImageFlags);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   newImage->SetInnerWindowID(aInnerWindowId);
 
   rv = newImage->OnStartRequest(aRequest, nullptr);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
--- a/image/src/ImageFactory.h
+++ b/image/src/ImageFactory.h
@@ -13,16 +13,17 @@
 class nsCString;
 class nsIRequest;
 
 namespace mozilla {
 namespace image {
 
 class Image;
 class ImageURL;
+class MultipartImage;
 class ProgressTracker;
 
 class ImageFactory
 {
 public:
   /**
    * Registers vars with Preferences. Should only be called on the main thread.
    */
@@ -49,16 +50,28 @@ public:
    * Creates a new image which isn't associated with a URI or loaded through
    * the usual image loading mechanism.
    *
    * @param aMimeType      The mimetype of the image.
    */
   static already_AddRefed<Image>
   CreateAnonymousImage(const nsCString& aMimeType);
 
+  /**
+   * Creates a new multipart/x-mixed-replace image wrapper, and initializes it
+   * with the first part. Subsequent parts should be passed to the existing
+   * MultipartImage via MultipartImage::BeginTransitionToPart().
+   *
+   * @param aFirstPart       An image containing the first part of the multipart
+   *                         stream.
+   * @param aProgressTracker A progress tracker for the multipart image.
+   */
+  static already_AddRefed<MultipartImage>
+  CreateMultipartImage(Image* aFirstPart, ProgressTracker* aProgressTracker);
+
 private:
   // Factory functions that create specific types of image containers.
   static already_AddRefed<Image>
   CreateRasterImage(nsIRequest* aRequest,
                     ProgressTracker* aProgressTracker,
                     const nsCString& aMimeType,
                     ImageURL* aURI,
                     uint32_t aImageFlags,
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -16,22 +16,16 @@ using gfx::DataSourceSurface;
 using gfx::SourceSurface;
 using layers::LayerManager;
 using layers::ImageContainer;
 
 namespace image {
 
 // Inherited methods from Image.
 
-nsresult
-ImageWrapper::Init(const char* aMimeType, uint32_t aFlags)
-{
-  return mInnerImage->Init(aMimeType, aFlags);
-}
-
 already_AddRefed<ProgressTracker>
 ImageWrapper::GetProgressTracker()
 {
   return mInnerImage->GetProgressTracker();
 }
 
 size_t
 ImageWrapper::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
--- a/image/src/ImageWrapper.h
+++ b/image/src/ImageWrapper.h
@@ -17,18 +17,16 @@ namespace image {
  */
 class ImageWrapper : public Image
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINER
 
   // Inherited methods from Image.
-  virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override;
-
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
 
   virtual size_t
     SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual void IncrementAnimationConsumers() override;
--- a/image/src/MultipartImage.cpp
+++ b/image/src/MultipartImage.cpp
@@ -98,33 +98,42 @@ private:
   nsRefPtr<Image> mImage;
 };
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // Implementation
 ///////////////////////////////////////////////////////////////////////////////
 
-MultipartImage::MultipartImage(Image* aImage, ProgressTracker* aTracker)
-  : ImageWrapper(aImage)
+MultipartImage::MultipartImage(Image* aFirstPart)
+  : ImageWrapper(aFirstPart)
   , mDeferNotifications(false)
 {
-  MOZ_ASSERT(aTracker);
-  mProgressTrackerInit = new ProgressTrackerInit(this, aTracker);
   mNextPartObserver = new NextPartObserver(this);
+}
+
+void
+MultipartImage::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
 
   // Start observing the first part.
   nsRefPtr<ProgressTracker> firstPartTracker =
     InnerImage()->GetProgressTracker();
   firstPartTracker->AddObserver(this);
   InnerImage()->RequestDecode();
   InnerImage()->IncrementAnimationConsumers();
 }
 
-MultipartImage::~MultipartImage() { }
+MultipartImage::~MultipartImage()
+{
+  // Ask our ProgressTracker to drop its weak reference to us.
+  mTracker->ResetImage();
+}
 
 NS_IMPL_QUERY_INTERFACE_INHERITED0(MultipartImage, ImageWrapper)
 NS_IMPL_ADDREF(MultipartImage)
 NS_IMPL_RELEASE(MultipartImage)
 
 void
 MultipartImage::BeginTransitionToPart(Image* aNextPart)
 {
@@ -141,32 +150,42 @@ MultipartImage::BeginTransitionToPart(Im
 
   // Start observing the next part; we'll complete the transition when
   // NextPartObserver calls FinishTransition.
   mNextPartObserver->BeginObserving(mNextPart);
   mNextPart->RequestDecode();
   mNextPart->IncrementAnimationConsumers();
 }
 
-void MultipartImage::FinishTransition()
+static Progress
+FilterProgress(Progress aProgress)
+{
+  // Filter out onload blocking notifications, since we don't want to block
+  // onload for multipart images.
+  return aProgress & ~(FLAG_ONLOAD_BLOCKED | FLAG_ONLOAD_UNBLOCKED);
+}
+
+void
+MultipartImage::FinishTransition()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mNextPart, "Should have a next part here");
 
   nsRefPtr<ProgressTracker> newCurrentPartTracker =
     mNextPart->GetProgressTracker();
   if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) {
     // This frame has an error; drop it.
     mNextPart = nullptr;
 
     // We still need to notify, though.
     mTracker->ResetForNewRequest();
     nsRefPtr<ProgressTracker> currentPartTracker =
       InnerImage()->GetProgressTracker();
-    mTracker->SyncNotifyProgress(currentPartTracker->GetProgress());
+    mTracker
+      ->SyncNotifyProgress(FilterProgress(currentPartTracker->GetProgress()));
 
     return;
   }
 
   // Stop observing the current part.
   {
     nsRefPtr<ProgressTracker> currentPartTracker =
       InnerImage()->GetProgressTracker();
@@ -176,18 +195,19 @@ void MultipartImage::FinishTransition()
   // Make the next part become the current part.
   mTracker->ResetForNewRequest();
   SetInnerImage(mNextPart);
   mNextPart = nullptr;
   newCurrentPartTracker->AddObserver(this);
 
   // Finally, send all the notifications for the new current part and send a
   // FRAME_UPDATE notification so that observers know to redraw.
-  mTracker->SyncNotifyProgress(newCurrentPartTracker->GetProgress(),
-                               GetMaxSizedIntRect());
+  mTracker
+    ->SyncNotifyProgress(FilterProgress(newCurrentPartTracker->GetProgress()),
+                         GetMaxSizedIntRect());
 }
 
 already_AddRefed<imgIContainer>
 MultipartImage::Unwrap()
 {
   // Although we wrap another image, we don't allow callers to unwrap as. As far
   // as external code is concerned, MultipartImage is atomic.
   nsCOMPtr<imgIContainer> image = this;
--- a/image/src/MultipartImage.h
+++ b/image/src/MultipartImage.h
@@ -22,18 +22,16 @@ class NextPartObserver;
 class MultipartImage
   : public ImageWrapper
   , public IProgressObserver
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(MultipartImage)
   NS_DECL_ISUPPORTS
 
-  MultipartImage(Image* aImage, ProgressTracker* aTracker);
-
   void BeginTransitionToPart(Image* aNextPart);
 
   // Overridden ImageWrapper methods:
   virtual already_AddRefed<imgIContainer> Unwrap() override;
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
   virtual void SetProgressTracker(ProgressTracker* aTracker) override;
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
@@ -68,22 +66,25 @@ public:
   // methods to do nothing.
   virtual void BlockOnload() override { }
   virtual void UnblockOnload() override { }
 
 protected:
   virtual ~MultipartImage();
 
 private:
+  friend class ImageFactory;
   friend class NextPartObserver;
 
+  explicit MultipartImage(Image* aFirstPart);
+  void Init();
+
   void FinishTransition();
 
   nsRefPtr<ProgressTracker> mTracker;
-  nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
   nsRefPtr<NextPartObserver> mNextPartObserver;
   nsRefPtr<Image> mNextPart;
   bool mDeferNotifications : 1;
 };
 
 } // namespace image
 } // namespace mozilla
 
--- a/image/src/ProgressTracker.cpp
+++ b/image/src/ProgressTracker.cpp
@@ -17,36 +17,16 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Services.h"
 
 using mozilla::WeakPtr;
 
 namespace mozilla {
 namespace image {
 
-ProgressTrackerInit::ProgressTrackerInit(Image* aImage,
-                                         ProgressTracker* aTracker)
-{
-  MOZ_ASSERT(aImage);
-
-  if (aTracker) {
-    mTracker = aTracker;
-  } else {
-    mTracker = new ProgressTracker();
-  }
-  mTracker->SetImage(aImage);
-  aImage->SetProgressTracker(mTracker);
-  MOZ_ASSERT(mTracker);
-}
-
-ProgressTrackerInit::~ProgressTrackerInit()
-{
-  mTracker->ResetImage();
-}
-
 static void
 CheckProgressConsistency(Progress aProgress)
 {
   // Check preconditions for every progress bit.
 
   if (aProgress & FLAG_SIZE_AVAILABLE) {
     // No preconditions.
   }
--- a/image/src/ProgressTracker.h
+++ b/image/src/ProgressTracker.h
@@ -153,32 +153,31 @@ public:
     return mObservers.Length();
   }
 
   // This is intentionally non-general because its sole purpose is to support
   // some obscure network priority logic in imgRequest. That stuff could
   // probably be improved, but it's too scary to mess with at the moment.
   bool FirstObserverIs(IProgressObserver* aObserver);
 
+  // Resets our weak reference to our image. Image subclasses should call this
+  // in their destructor.
+  void ResetImage();
+
 private:
   typedef nsTObserverArray<mozilla::WeakPtr<IProgressObserver>> ObserverArray;
   friend class AsyncNotifyRunnable;
   friend class AsyncNotifyCurrentStateRunnable;
-  friend class ProgressTrackerInit;
+  friend class ImageFactory;
 
   ProgressTracker(const ProgressTracker& aOther) = delete;
 
-  // This method should only be called once, and only on an ProgressTracker
-  // that was initialized without an image. ProgressTrackerInit automates this.
+  // Sets our weak reference to our image. Only ImageFactory should call this.
   void SetImage(Image* aImage);
 
-  // Resets our weak reference to our image, for when mImage is about to go out
-  // of scope.  ProgressTrackerInit automates this.
-  void ResetImage();
-
   // Send some notifications that would be necessary to make |aObserver| believe
   // the request is finished downloading and decoding.  We only send
   // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
   void EmulateRequestFinished(IProgressObserver* aObserver);
 
   // Main thread only because it deals with the observer service.
   void FireFailureNotification();
 
@@ -196,21 +195,12 @@ private:
   // List of observers attached to the image. Each observer represents a
   // consumer using the image. Array and/or individual elements should only be
   // accessed on the main thread.
   ObserverArray mObservers;
 
   Progress mProgress;
 };
 
-class ProgressTrackerInit
-{
-public:
-  ProgressTrackerInit(Image* aImage, ProgressTracker* aTracker);
-  ~ProgressTrackerInit();
-private:
-  ProgressTracker* mTracker;
-};
-
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_src_ProgressTracker_h
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -245,18 +245,17 @@ static nsCOMPtr<nsIThread> sScaleWorkerT
 #ifndef DEBUG
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
 #else
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
                   imgIContainerDebug)
 #endif
 
 //******************************************************************************
-RasterImage::RasterImage(ProgressTracker* aProgressTracker,
-                         ImageURL* aURI /* = nullptr */) :
+RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
   ImageResource(aURI), // invoke superclass's constructor
   mSize(0,0),
   mLockCount(0),
   mDecodeCount(0),
   mRequestedSampleSize(0),
   mLastImageContainerDrawResult(DrawResult::NOT_READY),
 #ifdef DEBUG
   mFramesNotified(0),
@@ -268,18 +267,16 @@ RasterImage::RasterImage(ProgressTracker
   mTransient(false),
   mDiscardable(false),
   mHasSourceData(false),
   mHasBeenDecoded(false),
   mPendingAnimation(false),
   mAnimationFinished(false),
   mWantFullDecode(false)
 {
-  mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker);
-
   Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 }
 
 //******************************************************************************
 RasterImage::~RasterImage()
 {
   // Make sure our SourceBuffer is marked as complete. This will ensure that any
   // outstanding decoders terminate.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -161,19 +161,16 @@ public:
 #ifdef DEBUG
   NS_DECL_IMGICONTAINERDEBUG
 #endif
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
 
   // Methods inherited from Image
-  nsresult Init(const char* aMimeType,
-                uint32_t aFlags) override;
-
   virtual void OnSurfaceDiscarded() override;
 
   // Raster-specific methods
   static NS_METHOD WriteToSourceBuffer(nsIInputStream* aIn, void* aClosure,
                                        const char* aFromRawSegment,
                                        uint32_t aToOffset, uint32_t aCount,
                                        uint32_t* aWriteCount);
 
@@ -283,16 +280,18 @@ public:
     nsCString spec;
     if (GetURI()) {
       GetURI()->GetSpec(spec);
     }
     return spec;
   }
 
 private:
+  nsresult Init(const char* aMimeType, uint32_t aFlags);
+
   DrawResult DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
                                           gfxContext* aContext,
                                           const nsIntSize& aSize,
                                           const ImageRegion& aRegion,
                                           GraphicsFilter aFilter,
                                           uint32_t aFlags);
 
   TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
@@ -420,19 +419,16 @@ private: // data
   bool                       mAnimationFinished:1;
 
   // Whether, once we are done doing a size decode, we should immediately kick
   // off a full decode.
   bool                       mWantFullDecode:1;
 
   TimeStamp mDrawStartTime;
 
-  // Initializes ProgressTracker and resets it on RasterImage destruction.
-  nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
-
 
   //////////////////////////////////////////////////////////////////////////////
   // Scaling.
   //////////////////////////////////////////////////////////////////////////////
 
   // Initiates an HQ scale for the given frame, if possible.
   void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
 
@@ -470,18 +466,17 @@ private: // data
 
     nsRefPtr<RasterImage> mImage;
   };
 
   // Helpers
   bool CanDiscard();
 
 protected:
-  explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,
-                       ImageURL* aURI = nullptr);
+  explicit RasterImage(ImageURL* aURI = nullptr);
 
   bool ShouldAnimate() override;
 
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP
 RasterImage::GetAnimationMode(uint16_t* aAnimationMode) {
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -322,28 +322,25 @@ SVGDrawingCallback::operator()(gfxContex
 NS_IMPL_ISUPPORTS(VectorImage,
                   imgIContainer,
                   nsIStreamListener,
                   nsIRequestObserver)
 
 //------------------------------------------------------------------------------
 // Constructor / Destructor
 
-VectorImage::VectorImage(ProgressTracker* aProgressTracker,
-                         ImageURL* aURI /* = nullptr */) :
+VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) :
   ImageResource(aURI), // invoke superclass's constructor
   mLockCount(0),
   mIsInitialized(false),
   mIsFullyLoaded(false),
   mIsDrawing(false),
   mHaveAnimations(false),
   mHasPendingInvalidation(false)
-{
-  mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker);
-}
+{ }
 
 VectorImage::~VectorImage()
 {
   CancelAllListeners();
   SurfaceCache::RemoveImage(ImageKey(this));
 }
 
 //------------------------------------------------------------------------------
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -29,19 +29,16 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_IMGICONTAINER
 
   // (no public constructor - use ImageFactory)
 
   // Methods inherited from Image
-  nsresult Init(const char* aMimeType,
-                uint32_t aFlags) override;
-
   virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf)
     const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
                                         nsIInputStream* aInStr,
@@ -67,28 +64,29 @@ public:
   // Callback for SVGParseCompleteListener.
   void OnSVGDocumentParsed();
 
   // Callbacks for SVGLoadEventListener.
   void OnSVGDocumentLoaded();
   void OnSVGDocumentError();
 
 protected:
-  explicit VectorImage(ProgressTracker* aProgressTracker = nullptr,
-                       ImageURL* aURI = nullptr);
+  explicit VectorImage(ImageURL* aURI = nullptr);
   virtual ~VectorImage();
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
   virtual bool     ShouldAnimate() override;
 
   void CreateSurfaceAndShow(const SVGDrawingParameters& aParams);
   void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
 
 private:
+  nsresult Init(const char* aMimeType, uint32_t aFlags);
+
   /**
    * In catastrophic circumstances like a GPU driver crash, we may lose our
    * surfaces even if they're locked. RecoverFromLossOfSurfaces discards all
    * existing surfaces, allowing us to recover.
    */
   void RecoverFromLossOfSurfaces();
 
   void CancelAllListeners();
@@ -107,19 +105,16 @@ private:
   bool           mIsFullyLoaded;          // Has the SVG document finished
                                           // loading?
   bool           mIsDrawing;              // Are we currently drawing?
   bool           mHaveAnimations;         // Is our SVG content SMIL-animated?
                                           // (Only set after mIsFullyLoaded.)
   bool           mHasPendingInvalidation; // Invalidate observers next refresh
                                           // driver tick.
 
-  // Initializes ProgressTracker and resets it on RasterImage destruction.
-  nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
-
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP VectorImage::GetAnimationMode(uint16_t* aAnimationMode) {
   return GetAnimationModeInternal(aAnimationMode);
 }
 
 inline NS_IMETHODIMP VectorImage::SetAnimationMode(uint16_t aAnimationMode) {
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -972,17 +972,18 @@ PrepareForNewPart(nsIRequest* aRequest, 
     nsRefPtr<Image> partImage =
       ImageFactory::CreateImage(aRequest, progressTracker, result.mContentType,
                                 aURI, /* aIsMultipart = */ true,
                                 aInnerWindowId);
 
     if (result.mIsFirstPart) {
       // First part for a multipart channel. Create the MultipartImage wrapper.
       MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet");
-      result.mImage = new MultipartImage(partImage, aProgressTracker);
+      result.mImage =
+        ImageFactory::CreateMultipartImage(partImage, aProgressTracker);
     } else {
       // Transition to the new part.
       auto multipartImage = static_cast<MultipartImage*>(aExistingImage);
       multipartImage->BeginTransitionToPart(partImage);
     }
   } else {
     MOZ_ASSERT(!aExistingImage, "New part for non-multipart channel?");
     MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet");
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1227,20 +1227,22 @@ if test "$GNU_CC"; then
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=parentheses"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-arith"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-sign"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-to-int-cast"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=return-type"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=sequence-point"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=switch"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=trigraphs"
+        _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=uninitialized"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=unknown-pragmas"
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=write-strings"
 
         MOZ_C_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_c_has_werror_non_literal_null_conversion)
+        MOZ_C_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_c_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-unused - lots of violations in third-party code
     # -Wno-inline-new-delete - we inline 'new' and 'delete' in mozalloc
     #
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-unused"
 
@@ -1330,23 +1332,25 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=overloaded-virtual"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=parentheses"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=reorder"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
+        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=uninitialized"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unknown-pragmas"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-value"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=write-strings"
 
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, conversion-null, ac_cxx_has_werror_conversion_null)
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_cxx_has_werror_non_literal_null_conversion)
+        MOZ_CXX_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_cxx_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
     #
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-invalid-offsetof"
 
     if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
@@ -2912,24 +2916,21 @@ fi
 
 dnl ========================================================
 dnl = Disable treating compiler warnings as errors
 dnl ========================================================
 if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
    WARNINGS_AS_ERRORS=''
 elif test "$GNU_CC"; then
     # Prevent the following GCC warnings from being treated as errors:
-    # -Wuninitialized - too many false positives
     # -Wmaybe-uninitialized - too many false positives
     # -Wdeprecated-declarations - we don't want our builds held hostage when a
     #   platform-specific API becomes deprecated.
     # -Wfree-nonheap-object - false positives during PGO
     # -Warray-bounds - false positives depending on optimization
-    MOZ_C_SUPPORTS_WARNING(-W, no-error=uninitialized, ac_c_has_noerror_uninitialized)
-    MOZ_CXX_SUPPORTS_WARNING(-W, no-error=uninitialized, ac_cxx_has_noerror_uninitialized)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=maybe-uninitialized, ac_c_has_noerror_maybe_uninitialized)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=maybe-uninitialized, ac_cxx_has_noerror_maybe_uninitialized)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=deprecated-declarations, ac_c_has_noerror_deprecated_declarations)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=deprecated-declarations, ac_cxx_has_noerror_deprecated_declarations)
     MOZ_C_SUPPORTS_WARNING(-W, no-error=array-bounds, ac_c_has_noerror_array_bounds)
     MOZ_CXX_SUPPORTS_WARNING(-W, no-error=array-bounds, ac_cxx_has_noerror_array_bounds)
 
     if test -n "$MOZ_PGO"; then
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -1,11 +1,14 @@
 #!/bin/bash
+
+# Note that the -x will be temporarily cancelled and reinstated below, so if
+# you want to eliminate this, you'll need to eliminate it there too.
+set -x
 set -e
-set -x
 
 DIR="$(dirname $0)"
 ABSDIR="$(cd $DIR; pwd)"
 SOURCE="$(cd $DIR/../../../..; pwd)"
 
 function usage() {
   echo "Usage: $0 [--dep] <variant>"
 }
@@ -138,17 +141,19 @@ RUN_JSTESTS=true
 
 PARENT=$$
 
 # Spawn off a child process, detached from any of our fds, that will kill us after a timeout.
 # To report the timeout, catch the signal in the parent before exiting.
 sh -c "sleep $TIMEOUT; kill -INT $PARENT" <&- >&- 2>&- &
 KILLER=$!
 disown %1
+set +x
 trap "echo 'TEST-UNEXPECTED-FAIL | autospider.sh $TIMEOUT timeout | ignore later failures' >&2; exit 1" INT
+set -x
 
 # If we do *not* hit that timeout, kill off the spawned process on a regular exit.
 trap "kill $KILLER" EXIT
 
 if [[ "$VARIANT" = "rootanalysis" ]]; then
     export JS_GC_ZEAL=7
 
 elif [[ "$VARIANT" = "compacting" ]]; then
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -2,16 +2,17 @@ asm.js/testParallelCompile.js
 auto-regress/bug653395.js
 auto-regress/bug675251.js
 baseline/bug847446.js
 baseline/bug852175.js
 basic/bug632964-regexp.js
 basic/bug677957-2.js
 basic/testBug614653.js
 basic/testBug686274.js
+basic/testManyVars.js
 basic/testTypedArrayInit.js
 gc/bug-1014972.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 self-test/assertDeepEq.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4799,17 +4799,16 @@ Parser<FullParseHandler>::forStatement()
          * Parse the rest of the for/in or for/of head.
          *
          * Here pn1 is everything to the left of 'in' or 'of'. At the end of
          * this block, pn1 is a decl or nullptr, pn2 is the assignment target
          * that receives the enumeration value each iteration, and pn3 is the
          * rhs of 'in'.
          */
         if (headKind == PNK_FOROF) {
-            forStmt.type = STMT_FOR_OF_LOOP;
             forStmt.type = (headKind == PNK_FOROF) ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
             if (isForEach) {
                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
                 return null();
             }
         } else {
             forStmt.type = STMT_FOR_IN_LOOP;
             iflags |= JSITER_ENUMERATE;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -661,16 +661,27 @@ template <typename S, typename T>
 void
 js::GCMarker::traverse(S source, T target)
 {
     MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(target),
                   runtime()->isAtomsZone(target->zone()) || target->zone() == source->zone());
     traverse(target);
 }
 
+namespace js {
+// Special-case JSObject->JSObject edges to check the compartment too.
+template <>
+void
+GCMarker::traverse(JSObject* source, JSObject* target)
+{
+    MOZ_ASSERT(target->compartment() == source->compartment());
+    traverse(target);
+}
+} // namespace js
+
 template <typename V, typename S> struct TraverseFunctor : public VoidDefaultAdaptor<V> {
     template <typename T> void operator()(T t, GCMarker* gcmarker, S s) {
         return gcmarker->traverse(s, t);
     }
 };
 
 template <typename S>
 void
@@ -770,16 +781,192 @@ js::GCMarker::eagerlyMarkChildren(Shape*
             traverse(shape, shape->getterObject());
         if (shape->hasSetterObject() && shape->setterObject()->isTenured())
             traverse(shape, shape->setterObject());
 
         shape = shape->previous();
     } while (shape && mark(shape));
 }
 
+void
+JSString::traceChildren(JSTracer* trc)
+{
+    if (hasBase())
+        traceBase(trc);
+    else if (isRope())
+        asRope().traceChildren(trc);
+}
+inline void
+GCMarker::eagerlyMarkChildren(JSString* str)
+{
+    if (str->isLinear())
+        eagerlyMarkChildren(&str->asLinear());
+    else
+        eagerlyMarkChildren(&str->asRope());
+}
+
+void
+JSString::traceBase(JSTracer* trc)
+{
+    MOZ_ASSERT(hasBase());
+    TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
+}
+inline void
+js::GCMarker::eagerlyMarkChildren(JSLinearString* linearStr)
+{
+    JS_COMPARTMENT_ASSERT(runtime(), linearStr);
+    MOZ_ASSERT(linearStr->isMarked());
+    MOZ_ASSERT(linearStr->JSString::isLinear());
+
+    // Use iterative marking to avoid blowing out the stack.
+    while (linearStr->hasBase()) {
+        linearStr = linearStr->base();
+        MOZ_ASSERT(linearStr->JSString::isLinear());
+        if (linearStr->isPermanentAtom())
+            break;
+        JS_COMPARTMENT_ASSERT(runtime(), linearStr);
+        if (!mark(static_cast<JSString*>(linearStr)))
+            break;
+    }
+}
+
+void
+JSRope::traceChildren(JSTracer* trc) {
+    js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
+    js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
+}
+inline void
+js::GCMarker::eagerlyMarkChildren(JSRope* rope)
+{
+    // This function tries to scan the whole rope tree using the marking stack
+    // as temporary storage. If that becomes full, the unscanned ropes are
+    // added to the delayed marking list. When the function returns, the
+    // marking stack is at the same depth as it was on entry. This way we avoid
+    // using tags when pushing ropes to the stack as ropes never leak to other
+    // users of the stack. This also assumes that a rope can only point to
+    // other ropes or linear strings, it cannot refer to GC things of other
+    // types.
+    ptrdiff_t savedPos = stack.position();
+    JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
+    while (true) {
+        JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
+        JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
+        JS_COMPARTMENT_ASSERT(runtime(), rope);
+        MOZ_ASSERT(rope->isMarked());
+        JSRope* next = nullptr;
+
+        JSString* right = rope->rightChild();
+        if (!right->isPermanentAtom() &&
+            mark(right))
+        {
+            if (right->isLinear())
+                eagerlyMarkChildren(&right->asLinear());
+            else
+                next = &right->asRope();
+        }
+
+        JSString* left = rope->leftChild();
+        if (!left->isPermanentAtom() &&
+            mark(left))
+        {
+            if (left->isLinear()) {
+                eagerlyMarkChildren(&left->asLinear());
+            } else {
+                // When both children are ropes, set aside the right one to
+                // scan it later.
+                if (next && !stack.push(reinterpret_cast<uintptr_t>(next)))
+                    delayMarkingChildren(next);
+                next = &left->asRope();
+            }
+        }
+        if (next) {
+            rope = next;
+        } else if (savedPos != stack.position()) {
+            MOZ_ASSERT(savedPos < stack.position());
+            rope = reinterpret_cast<JSRope*>(stack.pop());
+        } else {
+            break;
+        }
+    }
+    MOZ_ASSERT(savedPos == stack.position());
+}
+
+void
+js::ObjectGroup::traceChildren(JSTracer* trc)
+{
+    unsigned count = getPropertyCount();
+    for (unsigned i = 0; i < count; i++) {
+        if (ObjectGroup::Property* prop = getProperty(i))
+            TraceEdge(trc, &prop->id, "group_property");
+    }
+
+    if (proto().isObject())
+        TraceEdge(trc, &protoRaw(), "group_proto");
+
+    if (newScript())
+        newScript()->trace(trc);
+
+    if (maybePreliminaryObjects())
+        maybePreliminaryObjects()->trace(trc);
+
+    if (maybeUnboxedLayout())
+        unboxedLayout().trace(trc);
+
+    if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) {
+        TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group");
+        setOriginalUnboxedGroup(unboxedGroup);
+    }
+
+    if (JSObject* descr = maybeTypeDescr()) {
+        TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr");
+        setTypeDescr(&descr->as<TypeDescr>());
+    }
+
+    if (JSObject* fun = maybeInterpretedFunction()) {
+        TraceManuallyBarrieredEdge(trc, &fun, "group_function");
+        setInterpretedFunction(&fun->as<JSFunction>());
+    }
+}
+void
+js::GCMarker::lazilyMarkChildren(ObjectGroup* group)
+{
+    unsigned count = group->getPropertyCount();
+    for (unsigned i = 0; i < count; i++) {
+        if (ObjectGroup::Property* prop = group->getProperty(i))
+            traverse(group, prop->id.get());
+    }
+
+    if (group->proto().isObject())
+        traverse(group, group->proto().toObject());
+
+    group->compartment()->mark();
+
+    if (GlobalObject* global = group->compartment()->unsafeUnbarrieredMaybeGlobal())
+        traverse(group, static_cast<JSObject*>(global));
+
+    if (group->newScript())
+        group->newScript()->trace(this);
+
+    if (group->maybePreliminaryObjects())
+        group->maybePreliminaryObjects()->trace(this);
+
+    if (group->maybeUnboxedLayout())
+        group->unboxedLayout().trace(this);
+
+    if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
+        traverse(group, unboxedGroup);
+
+    if (TypeDescr* descr = group->maybeTypeDescr())
+        traverse(group, static_cast<JSObject*>(descr));
+
+    if (JSFunction* fun = group->maybeInterpretedFunction())
+        traverse(group, static_cast<JSObject*>(fun));
+}
+
+
 template <typename T>
 static inline void
 CheckIsMarkedThing(T* thingp)
 {
 #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
     static_assert(
             FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
             false, "Only the base cell layout types are allowed into marking/tracing internals");
@@ -1067,102 +1254,16 @@ gc::MarkValueForBarrier(JSTracer* trc, V
 void
 gc::MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name)
 {
     TraceManuallyBarrieredEdge(trc, idp, name);
 }
 
 /*** Push Mark Stack ***/
 
-static inline void
-ScanLinearString(GCMarker* gcmarker, JSLinearString* str)
-{
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), str);
-    MOZ_ASSERT(str->isMarked());
-
-    /*
-     * Add extra asserts to confirm the static type to detect incorrect string
-     * mutations.
-     */
-    MOZ_ASSERT(str->JSString::isLinear());
-    while (str->hasBase()) {
-        str = str->base();
-        MOZ_ASSERT(str->JSString::isLinear());
-        if (str->isPermanentAtom())
-            break;
-        JS_COMPARTMENT_ASSERT(gcmarker->runtime(), str);
-        if (!str->markIfUnmarked())
-            break;
-    }
-}
-
-/*
- * The function tries to scan the whole rope tree using the marking stack as
- * temporary storage. If that becomes full, the unscanned ropes are added to
- * the delayed marking list. When the function returns, the marking stack is
- * at the same depth as it was on entry. This way we avoid using tags when
- * pushing ropes to the stack as ropes never leaks to other users of the
- * stack. This also assumes that a rope can only point to other ropes or
- * linear strings, it cannot refer to GC things of other types.
- */
-static void
-ScanRope(GCMarker* gcmarker, JSRope* rope)
-{
-    ptrdiff_t savedPos = gcmarker->stack.position();
-    JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
-    for (;;) {
-        JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
-        JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
-        JS_COMPARTMENT_ASSERT(gcmarker->runtime(), rope);
-        MOZ_ASSERT(rope->isMarked());
-        JSRope* next = nullptr;
-
-        JSString* right = rope->rightChild();
-        if (!right->isPermanentAtom() && right->markIfUnmarked()) {
-            if (right->isLinear())
-                ScanLinearString(gcmarker, &right->asLinear());
-            else
-                next = &right->asRope();
-        }
-
-        JSString* left = rope->leftChild();
-        if (!left->isPermanentAtom() && left->markIfUnmarked()) {
-            if (left->isLinear()) {
-                ScanLinearString(gcmarker, &left->asLinear());
-            } else {
-                /*
-                 * When both children are ropes, set aside the right one to
-                 * scan it later.
-                 */
-                if (next && !gcmarker->stack.push(reinterpret_cast<uintptr_t>(next)))
-                    gcmarker->delayMarkingChildren(next);
-                next = &left->asRope();
-            }
-        }
-        if (next) {
-            rope = next;
-        } else if (savedPos != gcmarker->stack.position()) {
-            MOZ_ASSERT(savedPos < gcmarker->stack.position());
-            rope = reinterpret_cast<JSRope*>(gcmarker->stack.pop());
-        } else {
-            break;
-        }
-    }
-    MOZ_ASSERT(savedPos == gcmarker->stack.position());
- }
-
-inline void
-GCMarker::eagerlyMarkChildren(JSString* str)
-{
-    if (str->isLinear())
-        ScanLinearString(this, &str->asLinear());
-    else
-        ScanRope(this, &str->asRope());
-}
-
 /*
  * This function is used by the cycle collector to trace through a
  * shape. The cycle collector does not care about shapes or base
  * shapes, so those are not marked. Instead, any shapes or base shapes
  * that are encountered have their children marked. Stack space is
  * bounded.
  */
 void
@@ -1273,89 +1374,16 @@ gc::MarkCycleCollectorChildren(JSTracer*
     TraceChildren(&groupTracer, group, JSTRACE_OBJECT_GROUP);
 
     while (!groupTracer.worklist.empty()) {
         ObjectGroup* innerGroup = groupTracer.worklist.popCopy();
         TraceChildren(&groupTracer, innerGroup, JSTRACE_OBJECT_GROUP);
     }
 }
 
-static void
-ScanObjectGroup(GCMarker* gcmarker, ObjectGroup* group)
-{
-    unsigned count = group->getPropertyCount();
-    for (unsigned i = 0; i < count; i++) {
-        if (ObjectGroup::Property* prop = group->getProperty(i))
-            DoMarking(gcmarker, prop->id.get());
-    }
-
-    if (group->proto().isObject())
-        gcmarker->traverse(group->proto().toObject());
-
-    group->compartment()->mark();
-
-    if (GlobalObject* global = group->compartment()->unsafeUnbarrieredMaybeGlobal())
-        gcmarker->traverse(static_cast<JSObject*>(global));
-
-    if (group->newScript())
-        group->newScript()->trace(gcmarker);
-
-    if (group->maybePreliminaryObjects())
-        group->maybePreliminaryObjects()->trace(gcmarker);
-
-    if (group->maybeUnboxedLayout())
-        group->unboxedLayout().trace(gcmarker);
-
-    if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
-        gcmarker->traverse(unboxedGroup);
-
-    if (TypeDescr* descr = group->maybeTypeDescr())
-        gcmarker->traverse(static_cast<JSObject*>(descr));
-
-    if (JSFunction* fun = group->maybeInterpretedFunction())
-        gcmarker->traverse(static_cast<JSObject*>(fun));
-}
-
-void
-js::ObjectGroup::traceChildren(JSTracer* trc)
-{
-    unsigned count = getPropertyCount();
-    for (unsigned i = 0; i < count; i++) {
-        if (ObjectGroup::Property* prop = getProperty(i))
-            TraceEdge(trc, &prop->id, "group_property");
-    }
-
-    if (proto().isObject())
-        TraceEdge(trc, &protoRaw(), "group_proto");
-
-    if (newScript())
-        newScript()->trace(trc);
-
-    if (maybePreliminaryObjects())
-        maybePreliminaryObjects()->trace(trc);
-
-    if (maybeUnboxedLayout())
-        unboxedLayout().trace(trc);
-
-    if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) {
-        TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group");
-        setOriginalUnboxedGroup(unboxedGroup);
-    }
-
-    if (JSObject* descr = maybeTypeDescr()) {
-        TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr");
-        setTypeDescr(&descr->as<TypeDescr>());
-    }
-
-    if (JSObject* fun = maybeInterpretedFunction()) {
-        TraceManuallyBarrieredEdge(trc, &fun, "group_function");
-        setInterpretedFunction(&fun->as<JSFunction>());
-    }
-}
-
 template<typename T>
 static void
 PushArenaTyped(GCMarker* gcmarker, ArenaHeader* aheader)
 {
     for (ArenaCellIterUnderGC i(aheader); !i.done(); i.next())
         gcmarker->traverse(i.get<T>());
 }
 
@@ -1506,74 +1534,79 @@ GCMarker::restoreValueArray(NativeObject
             *vpp = *endp = vp;
         }
     }
 
     MOZ_ASSERT(*vpp <= *endp);
     return true;
 }
 
-void
-GCMarker::processMarkStackOther(uintptr_t tag, uintptr_t addr)
-{
-    if (tag == GroupTag) {
-        ScanObjectGroup(this, reinterpret_cast<ObjectGroup*>(addr));
-    } else if (tag == SavedValueArrayTag) {
-        MOZ_ASSERT(!(addr & CellMask));
-        NativeObject* obj = reinterpret_cast<NativeObject*>(addr);
-        HeapValue* vp;
-        HeapValue* end;
-        if (restoreValueArray(obj, (void**)&vp, (void**)&end))
-            pushValueArray(obj, vp, end);
-        else
-            repush(obj);
-    } else if (tag == JitCodeTag) {
-        reinterpret_cast<jit::JitCode*>(addr)->traceChildren(this);
-    }
-}
-
 inline void
 GCMarker::processMarkStackTop(SliceBudget& budget)
 {
     /*
      * The function uses explicit goto and implements the scanning of the
      * object directly. It allows to eliminate the tail recursion and
      * significantly improve the marking performance, see bug 641025.
      */
     HeapSlot* vp;
     HeapSlot* end;
     JSObject* obj;
 
     const int32_t* unboxedTraceList;
     uint8_t* unboxedMemory;
 
+    // Decode
     uintptr_t addr = stack.pop();
     uintptr_t tag = addr & StackTagMask;
     addr &= ~StackTagMask;
 
-    if (tag == ValueArrayTag) {
+    // Dispatch
+    switch (tag) {
+      case ValueArrayTag: {
         JS_STATIC_ASSERT(ValueArrayTag == 0);
         MOZ_ASSERT(!(addr & CellMask));
         obj = reinterpret_cast<JSObject*>(addr);
         uintptr_t addr2 = stack.pop();
         uintptr_t addr3 = stack.pop();
         MOZ_ASSERT(addr2 <= addr3);
         MOZ_ASSERT((addr3 - addr2) % sizeof(Value) == 0);
         vp = reinterpret_cast<HeapSlot*>(addr2);
         end = reinterpret_cast<HeapSlot*>(addr3);
         goto scan_value_array;
-    }
+      }
 
-    if (tag == ObjectTag) {
+      case ObjectTag: {
         obj = reinterpret_cast<JSObject*>(addr);
         JS_COMPARTMENT_ASSERT(runtime(), obj);
         goto scan_obj;
-    }
+      }
+
+      case GroupTag: {
+        return lazilyMarkChildren(reinterpret_cast<ObjectGroup*>(addr));
+      }
+
+      case JitCodeTag: {
+        return reinterpret_cast<jit::JitCode*>(addr)->traceChildren(this);
+      }
 
-    processMarkStackOther(tag, addr);
+      case SavedValueArrayTag: {
+        MOZ_ASSERT(!(addr & CellMask));
+        NativeObject* obj = reinterpret_cast<NativeObject*>(addr);
+        HeapValue* vp;
+        HeapValue* end;
+        if (restoreValueArray(obj, (void**)&vp, (void**)&end))
+            pushValueArray(obj, vp, end);
+        else
+            repush(obj);
+        return;
+      }
+
+      default: MOZ_CRASH("Invalid tag in mark stack");
+    }
     return;
 
   scan_value_array:
     MOZ_ASSERT(vp <= end);
     while (vp != end) {
         budget.step();
         if (budget.isOverBudget()) {
             pushValueArray(obj, vp, end);
@@ -1604,30 +1637,27 @@ GCMarker::processMarkStackTop(SliceBudge
             JSString* str = *reinterpret_cast<JSString**>(unboxedMemory + *unboxedTraceList);
             traverse(obj, str);
             unboxedTraceList++;
         }
         unboxedTraceList++;
         while (*unboxedTraceList != -1) {
             JSObject* obj2 = *reinterpret_cast<JSObject**>(unboxedMemory + *unboxedTraceList);
             MOZ_ASSERT_IF(obj2, obj->compartment() == obj2->compartment());
-            if (obj2 && mark(obj2))
-                repush(obj2);
+            if (obj2)
+                traverse(obj, obj2);
             unboxedTraceList++;
         }
         unboxedTraceList++;
         while (*unboxedTraceList != -1) {
             const Value& v = *reinterpret_cast<Value*>(unboxedMemory + *unboxedTraceList);
             if (v.isString()) {
                 traverse(obj, v.toString());
             } else if (v.isObject()) {
-                JSObject* obj2 = &v.toObject();
-                MOZ_ASSERT(obj->compartment() == obj2->compartment());
-                if (mark(obj2))
-                    repush(obj2);
+                traverse(obj, &v.toObject());
             } else if (v.isSymbol()) {
                 traverse(obj, v.toSymbol());
             }
             unboxedTraceList++;
         }
         return;
     }
 
@@ -1637,69 +1667,69 @@ GCMarker::processMarkStackTop(SliceBudge
 
         budget.step();
         if (budget.isOverBudget()) {
             repush(obj);
             return;
         }
 
         ObjectGroup* group = obj->groupFromGC();
-        traverse(group);
+        traverse(obj, group);
 
         /* Call the trace hook if necessary. */
         const Class* clasp = group->clasp();
         if (clasp->trace) {
             // Global objects all have the same trace hook. That hook is safe without barriers
             // if the global has no custom trace hook of its own, or has been moved to a different
             // compartment, and so can't have one.
             MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
                             (!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
                           clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
             if (clasp->trace == InlineTypedObject::obj_trace) {
                 Shape* shape = obj->as<InlineTypedObject>().shapeFromGC();
-                traverse(shape);
+                traverse(obj, shape);
                 TypeDescr* descr = &obj->as<InlineTypedObject>().typeDescr();
                 if (!descr->hasTraceList())
                     return;
                 unboxedTraceList = descr->traceList();
                 unboxedMemory = obj->as<InlineTypedObject>().inlineTypedMem();
                 goto scan_unboxed;
             }
             if (clasp == &UnboxedPlainObject::class_) {
                 JSObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
-                if (expando && mark(expando))
-                    repush(expando);
+                if (expando)
+                    traverse(obj, expando);
                 const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
                 unboxedTraceList = layout.traceList();
                 if (!unboxedTraceList)
                     return;
                 unboxedMemory = obj->as<UnboxedPlainObject>().data();
                 goto scan_unboxed;
             }
             clasp->trace(this, obj);
         }
 
         if (!clasp->isNative())
             return;
 
         NativeObject* nobj = &obj->as<NativeObject>();
 
         Shape* shape = nobj->lastProperty();
-        traverse(shape);
+        traverse(obj, shape);
 
         unsigned nslots = nobj->slotSpan();
 
         do {
             if (nobj->hasEmptyElements())
                 break;
 
             if (nobj->denseElementsAreCopyOnWrite()) {
                 JSObject* owner = nobj->getElementsHeader()->ownerObject();
                 if (owner != nobj) {
-                    traverse(owner);
+                    traverse(obj, owner);
                     break;
                 }
             }
 
             vp = nobj->getDenseElementsAllowCopyOnWrite();
             end = vp + nobj->getDenseInitializedLength();
             if (!nslots)
                 goto scan_value_array;
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -9,16 +9,18 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "gc/Heap.h"
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/TracingAPI.h"
 
+class JSLinearString;
+class JSRope;
 namespace js {
 class BaseShape;
 class GCMarker;
 class LazyScript;
 class NativeObject;
 class ObjectGroup;
 namespace gc {
 struct ArenaHeader;
@@ -240,19 +242,22 @@ class GCMarker : public JSTracer
     void repush(JSObject* obj) {
         MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
         pushTaggedPtr(ObjectTag, obj);
     }
 
     template <typename T> void markAndTraceChildren(T* thing);
     template <typename T> void markAndPush(StackTag tag, T* thing);
     template <typename T> void markAndScan(T* thing);
-    void eagerlyMarkChildren(Shape* shape);
+    void eagerlyMarkChildren(JSLinearString* str);
+    void eagerlyMarkChildren(JSRope* rope);
     void eagerlyMarkChildren(JSString* str);
     void eagerlyMarkChildren(LazyScript *thing);
+    void eagerlyMarkChildren(Shape* shape);
+    void lazilyMarkChildren(ObjectGroup* group);
 
     // We may not have concrete types yet, so this has to be out of the header.
     template <typename T>
     void dispatchToTraceChildren(T* thing);
 
     // Mark the given GC thing, but do not trace its children. Return true
     // if the thing became marked.
     template <typename T>
@@ -284,17 +289,16 @@ class GCMarker : public JSTracer
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
     bool restoreValueArray(NativeObject* obj, void** vpp, void** endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget& budget);
-    void processMarkStackOther(uintptr_t tag, uintptr_t addr);
 
     /* The color is only applied to objects and functions. */
     uint32_t color;
 
     /* Pointer to the top of the stack of arenas we are delaying marking on. */
     js::gc::ArenaHeader* unmarkedArenaStackTop;
 
     /* Count of arenas that are currently in the stack. */
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -115,27 +115,27 @@ class GlobalObject : public NativeObject
      * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
      * we won't expose GlobalObject, so just assert that the two values are
      * synchronized.
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
 
     enum WarnOnceFlag : int32_t {
-        WARN_WATCH_DEPRECATED = 0x00000001,
-        WARN_PROTO_SETTING_SLOW = 0x00000002,
-        WARN_STRING_CONTAINS_DEPRECATED = 0x00000004
+        WARN_WATCH_DEPRECATED                   = 0x00000001,
+        WARN_PROTO_SETTING_SLOW                 = 0x00000002,
+        WARN_STRING_CONTAINS_DEPRECATED         = 0x00000004
     };
 
     // Emit the specified warning if the given slot in |obj|'s global isn't
     // true, then set the slot to true.  Thus calling this method warns once
     // for each global object it's called on, and every other call does
     // nothing.
     static bool
-    warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag slot, unsigned errorNumber);
+    warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, unsigned errorNumber);
 
 
   public:
     void setThrowTypeError(JSFunction* fun) {
         MOZ_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
         setSlot(THROWTYPEERROR, ObjectValue(*fun));
     }
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -461,20 +461,17 @@ class JSString : public js::gc::TenuredC
     /* Only called by the GC for dependent or undepended strings. */
 
     inline bool hasBase() const {
         return d.u1.flags & HAS_BASE_BIT;
     }
 
     inline JSLinearString* base() const;
 
-    void traceBase(JSTracer* trc) {
-        MOZ_ASSERT(hasBase());
-        js::TraceManuallyBarrieredEdge(trc, &d.s.u3.base, "base");
-    }
+    void traceBase(JSTracer* trc);
 
     /* Only called by the GC for strings with the AllocKind::STRING kind. */
 
     inline void finalize(js::FreeOp* fop);
 
     /* Gets the number of bytes that the chars take on the heap. */
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
@@ -504,17 +501,17 @@ class JSString : public js::gc::TenuredC
     void dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const;
 
     template <typename CharT>
     static void dumpChars(const CharT* s, size_t len, FILE* fp=stderr);
 
     bool equals(const char* s);
 #endif
 
-    inline void traceChildren(JSTracer* trc);
+    void traceChildren(JSTracer* trc);
 
     static MOZ_ALWAYS_INLINE void readBarrier(JSString* thing) {
         if (thing->isPermanentAtom())
             return;
 
         TenuredCell::readBarrier(thing);
     }
 
@@ -573,20 +570,17 @@ class JSRope : public JSString
         return d.s.u2.left;
     }
 
     JSString* rightChild() const {
         MOZ_ASSERT(isRope());
         return d.s.u3.right;
     }
 
-    void traceChildren(JSTracer* trc) {
-        js::TraceManuallyBarrieredEdge(trc, &d.s.u2.left, "left child");
-        js::TraceManuallyBarrieredEdge(trc, &d.s.u3.right, "right child");
-    }
+    void traceChildren(JSTracer* trc);
 
     static size_t offsetOfLeft() {
         return offsetof(JSRope, d.s.u2.left);
     }
     static size_t offsetOfRight() {
         return offsetof(JSRope, d.s.u3.right);
     }
 
@@ -1267,25 +1261,16 @@ JSString::ensureFlat(js::ExclusiveContex
 inline JSLinearString*
 JSString::base() const
 {
     MOZ_ASSERT(hasBase());
     MOZ_ASSERT(!d.s.u3.base->isInline());
     return d.s.u3.base;
 }
 
-inline void
-JSString::traceChildren(JSTracer* trc)
-{
-    if (hasBase())
-        traceBase(trc);
-    else if (isRope())
-        asRope().traceChildren(trc);
-}
-
 template<>
 MOZ_ALWAYS_INLINE const char16_t*
 JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC& nogc) const
 {
     return nonInlineTwoByteChars(nogc);
 }
 
 template<>
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1389,28 +1389,16 @@ nsFrameConstructorSaveState::~nsFrameCon
 #endif
       }
     }
     NS_ASSERTION(!mItems->LastChild() || !mItems->LastChild()->GetNextSibling(),
                  "Something corrupted our list");
   }
 }
 
-static
-bool IsBorderCollapse(nsIFrame* aFrame)
-{
-  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
-    if (nsGkAtoms::tableFrame == frame->GetType()) {
-      return ((nsTableFrame*)frame)->IsBorderCollapse();
-    }
-  }
-  NS_ASSERTION(false, "program error");
-  return false;
-}
-
 /**
  * Moves aFrameList from aOldParent to aNewParent.  This updates the parent
  * pointer of the frames in the list, and reparents their views as needed.
  * nsFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its
  * ancestors as needed. Then it sets the list as the initial child list
  * on aNewParent, unless aNewParent either already has kids or has been
  * reflowed; in that case it appends the new frames.  Note that this
  * method differs from ReparentFrames in that it doesn't change the kids'
@@ -2182,32 +2170,34 @@ nsCSSFrameConstructor::ConstructTableCel
 {
   NS_PRECONDITION(aDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL,
                   "Unexpected call");
 
   nsIContent* const content = aItem.mContent;
   nsStyleContext* const styleContext = aItem.mStyleContext;
   const uint32_t nameSpaceID = aItem.mNameSpaceID;
 
-  bool borderCollapse = IsBorderCollapse(aParentFrame);
+  nsTableFrame* tableFrame =
+    static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
   nsContainerFrame* newFrame;
   // <mtable> is border separate in mathml.css and the MathML code doesn't implement
   // border collapse. For those users who style <mtable> with border collapse,
   // give them the default non-MathML table frames that understand border collapse.
   // This won't break us because MathML table frames are all subclasses of the default
   // table code, and so we can freely mix <mtable> with <mtr> or <tr>, <mtd> or <td>.
   // What will happen is just that non-MathML frames won't understand MathML attributes
   // and will therefore miss the special handling that the MathML code does.
-  if (kNameSpaceID_MathML == nameSpaceID && !borderCollapse)
-    newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext);
-  else
+  if (kNameSpaceID_MathML == nameSpaceID && !tableFrame->IsBorderCollapse()) {
+    newFrame = NS_NewMathMLmtdFrame(mPresShell, styleContext, tableFrame);
+  } else {
     // Warning: If you change this and add a wrapper frame around table cell
     // frames, make sure Bug 368554 doesn't regress!
     // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
-    newFrame = NS_NewTableCellFrame(mPresShell, styleContext, borderCollapse);
+    newFrame = NS_NewTableCellFrame(mPresShell, styleContext, tableFrame);
+  }
 
   // Initialize the table cell frame
   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
 
   // Resolve pseudo style and initialize the body cell frame
   nsRefPtr<nsStyleContext> innerPseudoStyle;
   innerPseudoStyle = mPresShell->StyleSet()->
     ResolveAnonymousBoxStyle(nsCSSAnonBoxes::cellContent, styleContext);
@@ -8694,18 +8684,20 @@ nsCSSFrameConstructor::CreateContinuingF
 
     rowFrame->SetInitialChildList(kPrincipalList, newChildList);
     newFrame = rowFrame;
 
   } else if (IS_TABLE_CELL(frameType)) {
     // Warning: If you change this and add a wrapper frame around table cell
     // frames, make sure Bug 368554 doesn't regress!
     // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp.
+    nsTableFrame* tableFrame =
+      static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame();
     nsTableCellFrame* cellFrame =
-      NS_NewTableCellFrame(shell, styleContext, IsBorderCollapse(aParentFrame));
+      NS_NewTableCellFrame(shell, styleContext, tableFrame);
 
     cellFrame->Init(content, aParentFrame, aFrame);
     if (cellFrame->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
       nsTableFrame::RegisterPositionedTablePart(cellFrame);
     }
 
     // Create a continuing area frame
     nsIFrame* blockFrame = aFrame->GetFirstPrincipalChild();
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -421,17 +421,17 @@ AddAnimationsForProperty(nsIFrame* aFram
              "inconsistent property flags");
 
   // Add from first to last (since last overrides)
   for (size_t animIdx = 0; animIdx < aAnimations.Length(); animIdx++) {
     dom::Animation* anim = aAnimations[animIdx];
     if (!anim->IsPlaying()) {
       continue;
     }
-    dom::KeyframeEffectReadonly* effect = anim->GetEffect();
+    dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
     MOZ_ASSERT(effect, "A playing animation should have an effect");
     const AnimationProperty* property =
       effect->GetAnimationOfProperty(aProperty);
     if (!property) {
       continue;
     }
 
     // Note that if mWinsInCascade on property was  false,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -500,17 +500,17 @@ GetMinAndMaxScaleForAnimationProperty(ns
                                       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()) {
       continue;
     }
-    dom::KeyframeEffectReadonly* effect = anim->GetEffect();
+    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];
           gfxSize from = GetScaleForValue(segment.mFromValue,
                                           aContent->GetPrimaryFrame());
           aMaxScale.width = std::max<float>(aMaxScale.width, from.width);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4877,22 +4877,22 @@ ScrollFrameHelper::SetScrollbarEnabled(n
 
 void
 ScrollFrameHelper::SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom,
                                          nscoord aSize)
 {
   DebugOnly<nsWeakPtr> weakShell(
     do_GetWeakReference(mOuter->PresContext()->PresShell()));
   // convert to pixels
-  int32_t pizelSize = nsPresContext::AppUnitsToIntCSSPixels(aSize);
+  int32_t pixelSize = nsPresContext::AppUnitsToIntCSSPixels(aSize);
 
   // only set the attribute if it changed.
 
   nsAutoString newValue;
-  newValue.AppendInt(pizelSize);
+  newValue.AppendInt(pixelSize);
 
   if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
     return;
 
   nsWeakFrame weakFrame(mOuter);
   nsCOMPtr<nsIContent> kungFuDeathGrip = aContent;
   aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, true);
   MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
--- a/layout/generic/nsHTMLParts.h
+++ b/layout/generic/nsHTMLParts.h
@@ -191,17 +191,17 @@ NS_NewTableColGroupFrame(nsIPresShell* a
 class nsTableRowFrame;
 nsTableRowFrame*
 NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 class nsTableRowGroupFrame;
 nsTableRowGroupFrame*
 NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 class nsTableCellFrame;
 nsTableCellFrame*
-NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsBorderCollapse);
+NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsTableFrame* aTableFrame);
 
 nsresult
 NS_NewHTMLContentSink(nsIHTMLContentSink** aInstancePtrResult,
                       nsIDocument* aDoc, nsIURI* aURL,
                       nsISupports* aContainer, // e.g. docshell
                       nsIChannel* aChannel);
 nsresult
 NS_NewHTMLFragmentContentSink(nsIFragmentContentSink** aInstancePtrResult);
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -1699,16 +1699,21 @@ nsMathMLChar::StretchInternal(nsPresCont
   // We did not find a size variant or a glyph assembly to stretch this
   // operator. Verify whether a font with an OpenType MATH table is available
   // and record missing math script otherwise.
   gfxMissingFontRecorder* MFR = aPresContext->MissingFontRecorder();
   if (MFR && !fm->GetThebesFontGroup()->GetFirstMathFont()) {
     MFR->RecordScript(MOZ_SCRIPT_MATHEMATICAL_NOTATION);
   }
 
+  // If the scale_stretchy_operators option is disabled, we are done.
+  if (!Preferences::GetBool("mathml.scale_stretchy_operators.enabled", true)) {
+    return NS_OK;
+  }
+  
   // stretchy character
   if (stretchy) {
     if (isVertical) {
       float scale =
         std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
       if (!largeop || scale > 1.0) {
         // make the character match the desired height.
--- a/layout/mathml/nsMathMLParts.h
+++ b/layout/mathml/nsMathMLParts.h
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsMathMLParts_h___
 #define nsMathMLParts_h___
 
 #include "nscore.h"
 #include "nsISupports.h"
 
+class nsTableFrame;
+
 // Factory methods for creating MathML objects
 nsIFrame* NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmrowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmspaceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
@@ -21,17 +23,17 @@ nsIFrame* NS_NewMathMLmfracFrame(nsIPres
 nsIFrame* NS_NewMathMLmsubFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmsupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmsubsupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmmultiscriptsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsContainerFrame* NS_NewMathMLmtableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsContainerFrame* NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsContainerFrame* NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
-nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsTableFrame* aTableFrame);
 nsContainerFrame* NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmsqrtFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame* NS_NewMathMLsemanticsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 nsContainerFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags);
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -225,17 +225,17 @@ ApplyBorderToStyle(const nsMathMLmtdFram
 }
 
 static nsMargin
 ComputeBorderOverflow(nsMathMLmtdFrame* aFrame, nsStyleBorder aStyleBorder)
 {
   nsMargin overflow;
   int32_t rowIndex;
   int32_t columnIndex;
-  nsTableFrame* table = nsTableFrame::GetTableFrame(aFrame);
+  nsTableFrame* table = aFrame->GetTableFrame();
   aFrame->GetCellIndexes(rowIndex, columnIndex);
   if (!columnIndex) {
     overflow.left = table->GetColSpacing(-1);
     overflow.right = table->GetColSpacing(0) / 2;
   } else if (columnIndex == table->GetColCount() - 1) {
     overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
     overflow.right =  table->GetColSpacing(columnIndex + 1);
   } else {
@@ -1102,19 +1102,20 @@ nsMathMLmtrFrame::AttributeChanged(int32
 
   return NS_OK;
 }
 
 // --------
 // implementation of nsMathMLmtdFrame
 
 nsContainerFrame*
-NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+                     nsTableFrame* aTableFrame)
 {
-  return new (aPresShell) nsMathMLmtdFrame(aContext);
+  return new (aPresShell) nsMathMLmtdFrame(aContext, aTableFrame);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
 
 nsMathMLmtdFrame::~nsMathMLmtdFrame()
 {
 }
 
--- a/layout/mathml/nsMathMLmtableFrame.h
+++ b/layout/mathml/nsMathMLmtableFrame.h
@@ -213,17 +213,17 @@ public:
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsTableRowFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
   // helper to restyle and reflow the table -- @see nsMathMLmtableFrame.
   void RestyleTable()
   {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+    nsTableFrame* tableFrame = GetTableFrame();
     if (tableFrame && tableFrame->IsFrameOfType(nsIFrame::eMathML)) {
       // relayout the table
       ((nsMathMLmtableFrame*)tableFrame)->RestyleTable();
     }
   }
 
 protected:
   explicit nsMathMLmtrFrame(nsStyleContext* aContext) : nsTableRowFrame(aContext) {}
@@ -233,17 +233,18 @@ protected:
 // --------------
 
 class nsMathMLmtdFrame : public nsTableCellFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsContainerFrame* NS_NewMathMLmtdFrame(nsIPresShell* aPresShell,
-                                                nsStyleContext* aContext);
+                                                nsStyleContext* aContext,
+                                                nsTableFrame* aTableFrame);
 
   // overloaded nsTableCellFrame methods
 
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
 
   virtual nsresult
@@ -263,17 +264,18 @@ public:
     return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
   virtual nsMargin* GetBorderWidth(nsMargin& aBorder) const override;
 
   virtual nsMargin GetBorderOverflow() override;
 
 protected:
-  explicit nsMathMLmtdFrame(nsStyleContext* aContext) : nsTableCellFrame(aContext) {}
+  nsMathMLmtdFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame)
+    : nsTableCellFrame(aContext, aTableFrame) {}
   virtual ~nsMathMLmtdFrame();
 }; // class nsMathMLmtdFrame
 
 // --------------
 
 class nsMathMLmtdInnerFrame : public nsBlockFrame,
                               public nsMathMLFrame {
 public:
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -19,8 +19,18 @@ HTTP(..) == marker-shadow.html marker-sh
 skip-if(Android||B2G) == clipped-elements.html clipped-elements-ref.html
 HTTP(..) == theme-overflow.html theme-overflow-ref.html
 skip-if(B2G||Mulet) HTTP(..) == table-cell.html table-cell-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Mulet) HTTP(..) == two-value-syntax.html two-value-syntax-ref.html # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
 skip-if(B2G||Mulet) HTTP(..) == single-value.html single-value-ref.html  # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy(1,702) skip-if(Android||B2G||Mulet) fuzzy-if(asyncPanZoom&&!layersGPUAccelerated,102,12352) HTTP(..) == xulscroll.html xulscroll-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP(..) == combobox-zoom.html combobox-zoom-ref.html
+
+# The vertical-text pref setting can be removed after bug 1138384 lands
+pref(layout.css.vertical-text.enabled,true) == vertical-decorations-1.html vertical-decorations-1-ref.html
+pref(layout.css.vertical-text.enabled,true) == vertical-decorations-2.html vertical-decorations-2-ref.html
+pref(layout.css.vertical-text.enabled,true) != vertical-decorations-1.html vertical-decorations-1-2-notref.html
+pref(layout.css.vertical-text.enabled,true) != vertical-decorations-2.html vertical-decorations-1-2-notref.html
+pref(layout.css.vertical-text.enabled,true) == vertical-decorations-3.html vertical-decorations-3-ref.html
+pref(layout.css.vertical-text.enabled,true) == vertical-decorations-4.html vertical-decorations-4-ref.html
+pref(layout.css.vertical-text.enabled,true) != vertical-decorations-3.html vertical-decorations-3-4-notref.html
+pref(layout.css.vertical-text.enabled,true) != vertical-decorations-4.html vertical-decorations-3-4-notref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-1-2-notref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vlr {
+  writing-mode: vertical-lr;
+  text-orientation: sideways-right;
+  height: 10ch;
+}
+</style>
+</head>
+
+<body>
+<div class="vlr">abcdefghijklm</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-1-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vlr {
+  writing-mode: vertical-lr;
+  text-orientation: sideways-right;
+  height: 10ch;
+}
+</style>
+</head>
+
+<body>
+<div class="vlr"><u>abcdefghi</u>…</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vlr {
+  writing-mode: vertical-lr;
+  text-orientation: sideways-right;
+  height: 10ch;
+}
+</style>
+</head>
+
+<body>
+<div class="vlr"><u>abcdefghijklm</u></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-2-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+span {
+  text-decoration: overline;
+}
+.vlr {
+  writing-mode: vertical-lr;
+  text-orientation: sideways-right;
+  height: 10ch;
+}
+</style>
+</head>
+
+<body>
+<div class="vlr"><span>abcdefghi</span>…</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+span {
+  text-decoration: overline;
+}
+.vlr {
+  writing-mode: vertical-lr;
+  text-orientation: sideways-right;
+  height: 10ch;
+}
+</style>
+</head>
+
+<body>
+<div class="vlr"><span>abcdefghijklm</span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-3-4-notref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vrl {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  height: 6em;
+}
+</style>
+</head>
+
+<body>
+<div class="vrl">你好吗?我很好!</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-3-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vrl {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  height: 6em;
+}
+</style>
+</head>
+
+<body>
+<div class="vrl"><u>你好吗?我</u>…</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-3.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+.vrl {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  height: 6em;
+}
+</style>
+</head>
+
+<body>
+<div class="vrl"><u>你好吗?我很好!</u></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-4-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+span {
+  text-decoration: overline;
+}
+.vrl {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  height: 6em;
+}
+</style>
+</head>
+
+<body>
+<div class="vrl"><span>你好吗?我</span>…</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/vertical-decorations-4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+div {
+  font: 25px monospace;
+  white-space: pre;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  border: 1px solid gray;
+  padding: 5px;
+  display: inline-block;
+  vertical-align: top;
+}
+span {
+  text-decoration: overline;
+}
+.vrl {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  height: 6em;
+}
+</style>
+</head>
+
+<body>
+<div class="vrl"><span>你好吗?我很好!</span></div>
+</body>
+</html>
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -25,17 +25,17 @@
 #include "RestyleManager.h"
 #include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 
 
 using mozilla::layers::Layer;
 using mozilla::dom::Animation;
-using mozilla::dom::KeyframeEffectReadonly;
+using mozilla::dom::KeyframeEffectReadOnly;
 
 namespace mozilla {
 
 /* static */ bool
 IsGeometricProperty(nsCSSProperty aProperty)
 {
   switch (aProperty) {
     case eCSSProperty_bottom:
@@ -596,17 +596,17 @@ AnimationCollection::CanPerformOnComposi
   }
 
   for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     const Animation* anim = mAnimations[animIdx];
     if (!anim->IsPlaying()) {
       continue;
     }
 
-    const KeyframeEffectReadonly* effect = anim->GetEffect();
+    const KeyframeEffectReadOnly* effect = anim->GetEffect();
     MOZ_ASSERT(effect, "A playing animation should have an effect");
 
     for (size_t propIdx = 0, propEnd = effect->Properties().Length();
          propIdx != propEnd; ++propIdx) {
       if (IsGeometricProperty(effect->Properties()[propIdx].mProperty)) {
         aFlags = CanAnimateFlags(aFlags | CanAnimate_HasGeometricProperty);
         break;
       }
@@ -615,17 +615,17 @@ AnimationCollection::CanPerformOnComposi
 
   bool existsProperty = false;
   for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     const Animation* anim = mAnimations[animIdx];
     if (!anim->IsPlaying()) {
       continue;
     }
 
-    const KeyframeEffectReadonly* effect = anim->GetEffect();
+    const KeyframeEffectReadOnly* effect = anim->GetEffect();
     MOZ_ASSERT(effect, "A playing animation should have an effect");
 
     existsProperty = existsProperty || effect->Properties().Length() > 0;
 
     for (size_t propIdx = 0, propEnd = effect->Properties().Length();
          propIdx != propEnd; ++propIdx) {
       const AnimationProperty& prop = effect->Properties()[propIdx];
       if (!CanAnimatePropertyOnCompositor(mElement,
@@ -668,17 +668,17 @@ AnimationCollection::PostUpdateLayerAnim
     }
   }
 }
 
 bool
 AnimationCollection::HasAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
-    const KeyframeEffectReadonly* effect = mAnimations[animIdx]->GetEffect();
+    const KeyframeEffectReadOnly* effect = mAnimations[animIdx]->GetEffect();
     if (effect && effect->HasAnimationOfProperty(aProperty) &&
         !effect->IsFinishedTransition()) {
       return true;
     }
   }
   return false;
 }
 
@@ -917,17 +917,17 @@ AnimationCollection::HasCurrentAnimation
 
 bool
 AnimationCollection::HasCurrentAnimationsForProperties(
                               const nsCSSProperty* aProperties,
                               size_t aPropertyCount) const
 {
   for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
     const Animation& anim = *mAnimations[animIdx];
-    const KeyframeEffectReadonly* effect = anim.GetEffect();
+    const KeyframeEffectReadOnly* effect = anim.GetEffect();
     if (effect &&
         effect->IsCurrent(anim) &&
         effect->HasAnimationOfProperties(aProperties, aPropertyCount)) {
       return true;
     }
   }
 
   return false;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -21,17 +21,17 @@
 #include "nsIDocument.h"
 #include "nsDOMMutationObserver.h"
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using mozilla::dom::Animation;
 using mozilla::dom::AnimationPlayState;
-using mozilla::dom::KeyframeEffectReadonly;
+using mozilla::dom::KeyframeEffectReadOnly;
 using mozilla::CSSAnimation;
 
 mozilla::dom::Promise*
 CSSAnimation::GetReady(ErrorResult& aRv)
 {
   FlushStyle();
   return Animation::GetReady(aRv);
 }
@@ -347,18 +347,18 @@ nsAnimationManager::CheckAnimationRule(n
           continue;
         }
 
         bool animationChanged = false;
 
         // Update the old from the new so we can keep the original object
         // identity (and any expando properties attached to it).
         if (oldAnim->GetEffect() && newAnim->GetEffect()) {
-          KeyframeEffectReadonly* oldEffect = oldAnim->GetEffect();
-          KeyframeEffectReadonly* newEffect = newAnim->GetEffect();
+          KeyframeEffectReadOnly* oldEffect = oldAnim->GetEffect();
+          KeyframeEffectReadOnly* newEffect = newAnim->GetEffect();
           animationChanged =
             oldEffect->Timing() != newEffect->Timing() ||
             oldEffect->Properties() != newEffect->Properties();
           oldEffect->Timing() = newEffect->Timing();
           oldEffect->Properties() = newEffect->Properties();
         }
 
         // Reset compositor state so animation will be re-synchronized.
@@ -521,18 +521,18 @@ nsAnimationManager::BuildAnimations(nsSt
     AnimationTiming timing;
     timing.mIterationDuration =
       TimeDuration::FromMilliseconds(src.GetDuration());
     timing.mDelay = TimeDuration::FromMilliseconds(src.GetDelay());
     timing.mIterationCount = src.GetIterationCount();
     timing.mDirection = src.GetDirection();
     timing.mFillMode = src.GetFillMode();
 
-    nsRefPtr<KeyframeEffectReadonly> destEffect =
-      new KeyframeEffectReadonly(mPresContext->Document(), aTarget,
+    nsRefPtr<KeyframeEffectReadOnly> destEffect =
+      new KeyframeEffectReadOnly(mPresContext->Document(), aTarget,
                                  aStyleContext->GetPseudoType(), timing,
                                  src.GetName());
     dest->SetEffect(destEffect);
 
     // Even in the case where we call PauseFromStyle below, we still need to
     // call PlayFromStyle first. This is because a newly-created animation is
     // idle and has no effect until it is played (or otherwise given a start
     // time).
@@ -749,17 +749,17 @@ nsAnimationManager::UpdateCascadeResults
   nsAutoTArray<nsCSSProperty, 2> propertiesToTrack;
 
   {
     nsCSSPropertySet propertiesToTrackAsSet;
 
     for (size_t animIdx = aElementAnimations->mAnimations.Length();
          animIdx-- != 0; ) {
       const Animation* anim = aElementAnimations->mAnimations[animIdx];
-      const KeyframeEffectReadonly* effect = anim->GetEffect();
+      const KeyframeEffectReadOnly* effect = anim->GetEffect();
       if (!effect) {
         continue;
       }
 
       for (size_t propIdx = 0, propEnd = effect->Properties().Length();
            propIdx != propEnd; ++propIdx) {
         const AnimationProperty& prop = effect->Properties()[propIdx];
         // We only bother setting mWinsInCascade for properties that we
@@ -797,17 +797,17 @@ nsAnimationManager::UpdateCascadeResults
    * currently in effect.
    */
 
   bool changed = false;
   for (size_t animIdx = aElementAnimations->mAnimations.Length();
        animIdx-- != 0; ) {
     CSSAnimation* anim =
       aElementAnimations->mAnimations[animIdx]->AsCSSAnimation();
-    KeyframeEffectReadonly* effect = anim->GetEffect();
+    KeyframeEffectReadOnly* effect = anim->GetEffect();
 
     anim->mInEffectForCascadeResults = anim->IsInEffect();
 
     if (!effect) {
       continue;
     }
 
     for (size_t propIdx = 0, propEnd = effect->Properties().Length();
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -29,29 +29,29 @@
 #include "nsStyleChangeList.h"
 #include "nsStyleSet.h"
 #include "RestyleManager.h"
 #include "nsDOMMutationObserver.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 using mozilla::dom::Animation;
-using mozilla::dom::KeyframeEffectReadonly;
+using mozilla::dom::KeyframeEffectReadOnly;
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 const nsString&
 ElementPropertyTransition::Name() const
 {
    if (!mName.Length()) {
      const_cast<ElementPropertyTransition*>(this)->mName =
        NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(TransitionProperty()));
    }
-   return dom::KeyframeEffectReadonly::Name();
+   return dom::KeyframeEffectReadOnly::Name();
 }
 
 double
 ElementPropertyTransition::CurrentValuePortion() const
 {
   // It would be easy enough to handle finished transitions by using a time
   // fraction of 1 but currently we should not be called for finished
   // transitions.
@@ -316,17 +316,17 @@ nsTransitionManager::StyleContextChanged
 
     AnimationPtrArray& animations = collection->mAnimations;
     size_t i = animations.Length();
     MOZ_ASSERT(i != 0, "empty transitions list?");
     StyleAnimationValue currentValue;
     do {
       --i;
       Animation* anim = animations[i];
-      dom::KeyframeEffectReadonly* effect = anim->GetEffect();
+      dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
       MOZ_ASSERT(effect && effect->Properties().Length() == 1,
                  "Should have one animation property for a transition");
       MOZ_ASSERT(effect && 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];
           // properties no longer in 'transition-property'
       if ((checkProperties &&
@@ -628,17 +628,17 @@ 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();
+    dom::KeyframeEffectReadOnly* effect = anim->GetEffect();
 
     if (!effect->IsFinishedTransition()) {
       continue;
     }
 
     MOZ_ASSERT(effect && effect->Properties().Length() == 1,
                "Should have one animation property for a transition");
     MOZ_ASSERT(effect && effect->Properties()[0].mSegments.Length() == 1,
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -24,23 +24,23 @@ struct StyleTransition;
 }
 
 /*****************************************************************************
  * Per-Element data                                                          *
  *****************************************************************************/
 
 namespace mozilla {
 
-struct ElementPropertyTransition : public dom::KeyframeEffectReadonly
+struct ElementPropertyTransition : public dom::KeyframeEffectReadOnly
 {
   ElementPropertyTransition(nsIDocument* aDocument,
                             dom::Element* aTarget,
                             nsCSSPseudoElements::Type aPseudoType,
                             const AnimationTiming &aTiming)
-    : dom::KeyframeEffectReadonly(aDocument, aTarget, aPseudoType,
+    : dom::KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
                                   aTiming, EmptyString())
   { }
 
   virtual ElementPropertyTransition* AsTransition() override { return this; }
   virtual const ElementPropertyTransition* AsTransition() const override { return this; }
 
   virtual const nsString& Name() const override;
 
--- a/layout/tables/BasicTableLayoutStrategy.cpp
+++ b/layout/tables/BasicTableLayoutStrategy.cpp
@@ -9,16 +9,17 @@
  * used for CSS2's 'table-layout: auto'.
  */
 
 #include "BasicTableLayoutStrategy.h"
 
 #include <algorithm>
 
 #include "nsTableFrame.h"
+#include "nsTableColFrame.h"
 #include "nsTableCellFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsGkAtoms.h"
 #include "SpanningCellSorter.h"
 #include "nsIContent.h"
 
 using namespace mozilla;
 using namespace mozilla::layout;
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -37,19 +37,20 @@
 //TABLECELL SELECTION
 #include "nsFrameSelection.h"
 #include "mozilla/LookAndFeel.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
-nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) :
-  nsContainerFrame(aContext)
-  , mDesiredSize(GetWritingMode())
+nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext,
+                                   nsTableFrame* aTableFrame)
+  : nsContainerFrame(aContext)
+  , mDesiredSize(aTableFrame->GetWritingMode())
 {
   mColIndex = 0;
   mPriorAvailWidth = 0;
 
   SetContentEmpty(false);
   SetHasPctOverHeight(false);
 }
 
@@ -126,17 +127,17 @@ nsTableCellFrame::NotifyPercentHeight(co
     // are based on the height of the cell, since its containing block
     // is the inner cell frame.
 
     // We'll only honor the percent height if sibling-cells/ancestors
     // have specified/pct height. (Also, siblings only count for this if
     // both this cell and the sibling cell span exactly 1 row.)
 
     if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) ||
-        (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 &&
+        (GetTableFrame()->GetEffectiveRowSpan(*this) == 1 &&
          (cellRS->parentReflowState->frame->GetStateBits() &
           NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) {
 
       for (const nsHTMLReflowState *rs = aReflowState.parentReflowState;
            rs != cellRS;
            rs = rs->parentReflowState) {
         rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
       }
@@ -215,30 +216,29 @@ nsTableCellFrame::AttributeChanged(int32
   // We need to recalculate in this case because of the nowrap quirk in
   // BasicTableLayoutStrategy
   if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
       PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
   }
   // let the table frame decide what to do
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-  tableFrame->AttributeChangedFor(this, mContent, aAttribute);
+  GetTableFrame()->AttributeChangedFor(this, mContent, aAttribute);
   return NS_OK;
 }
 
 /* virtual */ void
 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   if (tableFrame->IsBorderCollapse() &&
       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
     int32_t colIndex, rowIndex;
     GetColIndex(colIndex);
     GetRowIndex(rowIndex);
     // row span needs to be clamped as we do not create rows in the cellmap
     // which do not have cells originating in them
     nsIntRect damageArea(colIndex, rowIndex, GetColSpan(),
@@ -474,17 +474,17 @@ PaintTableCellSelection(nsIFrame* aFrame
 
 void
 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                    const nsRect&           aDirtyRect,
                                    const nsDisplayListSet& aLists)
 {
   DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
   if (IsVisibleInSelection(aBuilder)) {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+    nsTableFrame* tableFrame = GetTableFrame();
     int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ?
                                 StyleTableBorder()->mEmptyCells
                                 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
     // take account of 'empty-cells'
     if (StyleVisibility()->IsVisible() &&
         (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) {
       // display outset box-shadows if we need to.
       const nsStyleBorder* borderStyle = StyleBorder();
@@ -896,17 +896,17 @@ nsTableCellFrame::Reflow(nsPresContext* 
     availSize.height = 1;
 
   WritingMode wm = aReflowState.GetWritingMode();
   nsHTMLReflowMetrics kidSize(wm, aDesiredSize.mFlags);
   kidSize.ClearSize();
   SetPriorAvailWidth(aReflowState.AvailableWidth());
   nsIFrame* firstKid = mFrames.FirstChild();
   NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
 
   if (aReflowState.mFlags.mSpecialHeightReflow) {
     const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset);
     DISPLAY_REFLOW_CHANGE();
   }
   else if (aPresContext->IsPaginated()) {
     nscoord computedUnpaginatedHeight =
       CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this,
@@ -1065,22 +1065,22 @@ nsTableCellFrame::GetCellIndexes(int32_t
   }
   aColIndex = mColIndex;
   return  NS_OK;
 }
 
 nsTableCellFrame*
 NS_NewTableCellFrame(nsIPresShell*   aPresShell,
                      nsStyleContext* aContext,
-                     bool            aIsBorderCollapse)
+                     nsTableFrame* aTableFrame)
 {
-  if (aIsBorderCollapse)
-    return new (aPresShell) nsBCTableCellFrame(aContext);
+  if (aTableFrame->IsBorderCollapse())
+    return new (aPresShell) nsBCTableCellFrame(aContext, aTableFrame);
   else
-    return new (aPresShell) nsTableCellFrame(aContext);
+    return new (aPresShell) nsTableCellFrame(aContext, aTableFrame);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
 
 nsMargin*
 nsTableCellFrame::GetBorderWidth(nsMargin&  aBorder) const
 {
   aBorder = StyleBorder()->GetComputedBorder();
@@ -1098,18 +1098,19 @@ nsresult
 nsTableCellFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
 }
 #endif
 
 // nsBCTableCellFrame
 
-nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext)
-:nsTableCellFrame(aContext)
+nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext,
+                                       nsTableFrame* aTableFrame)
+  : nsTableCellFrame(aContext, aTableFrame)
 {
   mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0;
 }
 
 nsBCTableCellFrame::~nsBCTableCellFrame()
 {
 }
 
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -11,18 +11,17 @@
 #include "nsITableCellLayout.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsStyleContext.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
 #include "nsTArray.h"
-
-class nsTableFrame;
+#include "nsTableRowFrame.h"
 
 /**
  * nsTableCellFrame
  * data structure to maintain information about a single table cell's frame
  *
  * NOTE:  frames are not ref counted.  We expose addref and release here
  * so we can change that decsion in the future.  Users of nsITableCellLayout
  * should refcount correctly as if this object is being ref counted, though
@@ -38,19 +37,31 @@ class nsTableCellFrame : public nsContai
 
 public:
   NS_DECL_QUERYFRAME_TARGET(nsTableCellFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // default constructor supplied by the compiler
 
-  explicit nsTableCellFrame(nsStyleContext* aContext);
+  nsTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame);
   ~nsTableCellFrame();
 
+  nsTableRowFrame* GetTableRowFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableRowFrame);
+    return static_cast<nsTableRowFrame*>(parent);
+  }
+
+  nsTableFrame* GetTableFrame() const
+  {
+    return GetTableRowFrame()->GetTableFrame();
+  }
+
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
@@ -80,23 +91,16 @@ public:
   }
 
   virtual nsMargin GetUsedMargin() const override;
 
   virtual void NotifyPercentHeight(const nsHTMLReflowState& aReflowState) override;
 
   virtual bool NeedsToObserve(const nsHTMLReflowState& aReflowState) override;
 
-  /** instantiate a new instance of nsTableRowFrame.
-    * @param aPresShell the pres shell for this frame
-    *
-    * @return           the frame that was created
-    */
-  friend nsIFrame* NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
-
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   DrawResult PaintCellBackground(nsRenderingContext& aRenderingContext,
                                  const nsRect& aDirtyRect, nsPoint aPt,
                                  uint32_t aFlags);
 
@@ -121,16 +125,24 @@ public:
    * @see nsLayoutAtoms::tableCellFrame
    */
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
+  // Although the spec doesn't say that writing-mode is not applied to
+  // table-cells, we still override this method here because we want to
+  // make effective writing mode of table structure frames consistent
+  // within a table. The content inside table cells is reflowed by an
+  // anonymous block, hence their writing mode is not affected.
+  virtual mozilla::WritingMode GetWritingMode() const override
+    { return GetTableFrame()->GetWritingMode(); }
+
   void VerticallyAlignChild(nscoord aMaxAscent);
 
   /*
    * Get the value of vertical-align adjusted for CSS 2's rules for a
    * table cell, which means the result is always
    * NS_STYLE_VERTICAL_ALIGN_{TOP,MIDDLE,BOTTOM,BASELINE}.
    */
   virtual uint8_t GetVerticalAlign() const;
@@ -291,17 +303,17 @@ inline void nsTableCellFrame::SetHasPctO
 
 // nsBCTableCellFrame
 class nsBCTableCellFrame final : public nsTableCellFrame
 {
   typedef mozilla::image::DrawResult DrawResult;
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
-  explicit nsBCTableCellFrame(nsStyleContext* aContext);
+  nsBCTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame);
 
   ~nsBCTableCellFrame();
 
   virtual nsIAtom* GetType() const override;
 
   virtual nsMargin GetUsedBorder() const override;
   virtual bool GetBorderRadii(const nsSize& aFrameSize,
                               const nsSize& aBorderArea,
--- a/layout/tables/nsTableColFrame.cpp
+++ b/layout/tables/nsTableColFrame.cpp
@@ -54,17 +54,17 @@ nsTableColFrame::SetColType(nsTableColTy
 /* virtual */ void
 nsTableColFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsSplittableFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   if (tableFrame->IsBorderCollapse() &&
       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
     nsIntRect damageArea(GetColIndex(), 0, 1, tableFrame->GetRowCount());
     tableFrame->AddBCDamageArea(damageArea);
   }
 }
 
 void nsTableColFrame::SetContinuousBCBorderWidth(uint8_t     aForSide,
@@ -93,18 +93,17 @@ nsTableColFrame::Reflow(nsPresContext*  
 {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableColFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aDesiredSize.ClearSize();
   const nsStyleVisibility* colVis = StyleVisibility();
   bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
   if (collapseCol) {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-    tableFrame->SetNeedToCollapse(true);
+    GetTableFrame()->SetNeedToCollapse(true);
   }
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 }
 
 int32_t nsTableColFrame::GetSpan()
 {
   return StyleTable()->mSpan;
--- a/layout/tables/nsTableColFrame.h
+++ b/layout/tables/nsTableColFrame.h
@@ -5,23 +5,18 @@
 #ifndef nsTableColFrame_h__
 #define nsTableColFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "celldata.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsTArray.h"
-
-enum nsTableColType {
-  eColContent            = 0, // there is real col content associated
-  eColAnonymousCol       = 1, // the result of a span on a col
-  eColAnonymousColGroup  = 2, // the result of a span on a col group
-  eColAnonymousCell      = 3  // the result of a cell alone
-};
+#include "nsTableColGroupFrame.h"
+#include "mozilla/WritingModes.h"
 
 class nsTableColFrame : public nsSplittableFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   enum {eWIDTH_SOURCE_NONE          =0,   // no cell has contributed to the width style
         eWIDTH_SOURCE_CELL          =1,   // a cell specified a width
         eWIDTH_SOURCE_CELL_WITH_SPAN=2    // a cell implicitly specified a width via colspan
@@ -32,16 +27,29 @@ public:
 
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableColFrame* NS_NewTableColFrame(nsIPresShell* aPresShell,
                                               nsStyleContext*  aContext);
+
+  nsTableColGroupFrame* GetTableColGroupFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableColGroupFrame);
+    return static_cast<nsTableColGroupFrame*>(parent);
+  }
+
+  nsTableFrame* GetTableFrame() const
+  {
+    return GetTableColGroupFrame()->GetTableFrame();
+  }
+
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
   int32_t GetColIndex() const;
 
   void SetColIndex (int32_t aColIndex);
 
   nsTableColFrame* GetNextCol() const;
@@ -66,16 +74,19 @@ public:
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   virtual nsSplittableType GetSplittableType() const override;
 
+  virtual mozilla::WritingMode GetWritingMode() const override
+    { return GetTableFrame()->GetWritingMode(); }
+
   /** return the number of the columns the col represents.  always >= 1 */
   int32_t GetSpan();
 
   /** convenience method, calls into cellmap */
   int32_t Count() const;
 
   nscoord GetLeftBorderWidth();
   void    SetLeftBorderWidth(BCPixelSize aWidth);
--- a/layout/tables/nsTableColGroupFrame.cpp
+++ b/layout/tables/nsTableColGroupFrame.cpp
@@ -70,17 +70,17 @@ void nsTableColGroupFrame::ResetColIndic
 }
 
 
 nsresult
 nsTableColGroupFrame::AddColsToTable(int32_t                   aFirstColIndex,
                                      bool                      aResetSubsequentColIndices,
                                      const nsFrameList::Slice& aCols)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
 
   tableFrame->InvalidateFrameSubtree();
 
   // set the col indices of the col frames and and add col info to the table
   int32_t colIndex = aFirstColIndex;
   nsFrameList::Enumerator e(aCols);
   for (; !e.AtEnd(); e.Next()) {
     ((nsTableColFrame*)e.get())->SetColIndex(colIndex);
@@ -136,34 +136,33 @@ nsTableColGroupFrame::GetLastRealColGrou
 void
 nsTableColGroupFrame::SetInitialChildList(ChildListID     aListID,
                                           nsFrameList&    aChildList)
 {
   MOZ_ASSERT(mFrames.IsEmpty(),
              "unexpected second call to SetInitialChildList");
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
   if (aChildList.IsEmpty()) { 
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-    tableFrame->AppendAnonymousColFrames(this, GetSpan(), eColAnonymousColGroup, 
-                                         false);
+    GetTableFrame()->AppendAnonymousColFrames(this, GetSpan(),
+                                              eColAnonymousColGroup, false);
     return; 
   }
 
   mFrames.AppendFrames(this, aChildList);
 }
 
 /* virtual */ void
 nsTableColGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
      
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   if (tableFrame->IsBorderCollapse() &&
       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
     int32_t colCount = GetColCount();
     if (!colCount)
       return; // this is a degenerated colgroup 
     nsIntRect damageArea(GetFirstColumn()->GetColIndex(), 0, colCount,
                          tableFrame->GetRowCount());
     tableFrame->AddBCDamageArea(damageArea);
@@ -312,17 +311,17 @@ nsTableColGroupFrame::RemoveFrame(ChildL
         col = nextCol;
       }
     }
     
     int32_t colIndex = colFrame->GetColIndex();
     // The RemoveChild call handles calling FrameNeedsReflow on us.
     RemoveChild(*colFrame, true);
     
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+    nsTableFrame* tableFrame = GetTableFrame();
     tableFrame->RemoveCol(this, colIndex, true, true);
     if (mFrames.IsEmpty() && contentRemoval && 
         GetColType() == eColGroupContent) {
       tableFrame->AppendAnonymousColFrames(this, GetSpan(),
                                            eColAnonymousColGroup, true);
     }
   }
   else {
@@ -357,18 +356,17 @@ nsTableColGroupFrame::Reflow(nsPresConte
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_ASSERTION(nullptr!=mContent, "bad state -- null content for frame");
   
   const nsStyleVisibility* groupVis = StyleVisibility();
   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
   if (collapseGroup) {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-    tableFrame->SetNeedToCollapse(true);
+    GetTableFrame()->SetNeedToCollapse(true);
   }
   // for every content child that (is a column thingy and does not already have a frame)
   // create a frame and adjust it's style
   
   for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame;
        kidFrame = kidFrame->GetNextSibling()) {
     // Give the child frame a chance to reflow, even though we know it'll have 0 size
     nsHTMLReflowMetrics kidSize(aReflowState);
@@ -431,18 +429,18 @@ void nsTableColGroupFrame::SetContinuous
     default:
       NS_ERROR("invalid side arg");
   }
 }
 
 void nsTableColGroupFrame::GetContinuousBCBorderWidth(nsMargin& aBorder)
 {
   int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
-  nsTableFrame* table = nsTableFrame::GetTableFrame(this);
-  nsTableColFrame* col = table->GetColFrame(mStartColIndex + mColCount - 1);
+  nsTableColFrame* col = GetTableFrame()->
+    GetColFrame(mStartColIndex + mColCount - 1);
   col->GetContinuousBCBorderWidth(aBorder);
   aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips,
                                             mTopContBorderWidth);
   aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips,
                                             mBottomContBorderWidth);
 }
 
 /* ----- global methods ----- */
--- a/layout/tables/nsTableColGroupFrame.h
+++ b/layout/tables/nsTableColGroupFrame.h
@@ -3,26 +3,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsTableColGroupFrame_h__
 #define nsTableColGroupFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
-#include "nsTableColFrame.h"
-
-class nsTableFrame;
-class nsTableColFrame;
+#include "nsTableFrame.h"
+#include "mozilla/WritingModes.h"
 
-enum nsTableColGroupType {
-  eColGroupContent            = 0, // there is real col group content associated   
-  eColGroupAnonymousCol       = 1, // the result of a col
-  eColGroupAnonymousCell      = 2  // the result of a cell alone
-};
+class nsTableColFrame;
 
 /**
  * nsTableColGroupFrame
  * data structure to maintain information about a single table cell's frame
  *
  * @author  sclark
  */
 class nsTableColGroupFrame final : public nsContainerFrame
@@ -35,16 +29,23 @@ public:
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableColGroupFrame* NS_NewTableColGroupFrame(nsIPresShell* aPresShell,
                                                         nsStyleContext* aContext);
 
+  nsTableFrame* GetTableFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame);
+    return static_cast<nsTableFrame*>(parent);
+  }
+
   /**
    * ColGroups never paint anything, nor receive events.
    */
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override {}
 
   /** A colgroup can be caused by three things:
@@ -108,16 +109,19 @@ public:
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableColGroupFrame
    */
   virtual nsIAtom* GetType() const override;
 
+  virtual mozilla::WritingMode GetWritingMode() const override
+    { return GetTableFrame()->GetWritingMode(); }
+
   /** Add column frames to the table storages: colframe cache and cellmap
     * this doesn't change the mFrames of the colgroup frame.
     * @param aFirstColIndex - the index at which aFirstFrame should be inserted
     *                         into the colframe cache.
     * @param aResetSubsequentColIndices - the indices of the col frames
     *                                     after the insertion might need
     *                                     an update
     * @param aCols - an iterator that can be used to iterate over the col
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -7,18 +7,16 @@
 
 #include "mozilla/Attributes.h"
 #include "celldata.h"
 #include "imgIContainer.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
-#include "nsTableColFrame.h"
-#include "nsTableColGroupFrame.h"
 #include "nsCellMap.h"
 #include "nsGkAtoms.h"
 #include "nsDisplayList.h"
 
 class nsTableCellFrame;
 class nsTableCellMap;
 class nsTableColFrame;
 class nsTableRowGroupFrame;
@@ -95,16 +93,29 @@ private:
   nsDisplayTableItem*   mOldCurrentItem;
 #ifdef DEBUG
   nsDisplayTableItem*   mPushedItem;
 #endif
 };
 
 /* ============================================================================ */
 
+enum nsTableColGroupType {
+  eColGroupContent            = 0, // there is real col group content associated
+  eColGroupAnonymousCol       = 1, // the result of a col
+  eColGroupAnonymousCell      = 2  // the result of a cell alone
+};
+
+enum nsTableColType {
+  eColContent            = 0, // there is real col content associated
+  eColAnonymousCol       = 1, // the result of a span on a col
+  eColAnonymousColGroup  = 2, // the result of a span on a col group
+  eColAnonymousCell      = 3  // the result of a cell alone
+};
+
 /**
   * nsTableFrame maps the inner portion of a table (everything except captions.)
   * Used as a pseudo-frame within nsTableOuterFrame, it may also be used
   * stand-alone as the top-level frame.
   *
   * The principal child list contains row group frames. There is also an
   * additional child list, kColGroupList, which contains the col group frames.
   */
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -166,17 +166,17 @@ nsTableRowFrame::DestroyFrom(nsIFrame* a
 /* virtual */ void
 nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   if (tableFrame->IsBorderCollapse() &&
       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
     nsIntRect damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
     tableFrame->AddBCDamageArea(damageArea);
   }
 }
 
 void
@@ -184,17 +184,17 @@ nsTableRowFrame::AppendFrames(ChildListI
                               nsFrameList&    aFrameList)
 {
   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
 
   DrainSelfOverflowList(); // ensure the last frame is in mFrames
   const nsFrameList::Slice& newCells = mFrames.AppendFrames(nullptr, aFrameList);
 
   // Add the new cell frames to the table
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) {
     nsIFrame *childFrame = e.get();
     NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
     tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex());
   }
 
   PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                                                NS_FRAME_HAS_DIRTY_CHILDREN);
@@ -210,17 +210,17 @@ nsTableRowFrame::InsertFrames(ChildListI
   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
   DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
   //Insert Frames in the frame list
   const nsFrameList::Slice& newCells = mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
 
   // Get the table frame
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   nsIAtom* cellFrameType = tableFrame->IsBorderCollapse() ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame;
   nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType);
   nsTArray<nsTableCellFrame*> cellChildren;
   for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) {
     nsIFrame *childFrame = e.get();
     NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
     cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame));
   }
@@ -240,17 +240,17 @@ void
 nsTableRowFrame::RemoveFrame(ChildListID     aListID,
                              nsIFrame*       aOldFrame)
 {
   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
 
   MOZ_ASSERT((nsTableCellFrame*)do_QueryFrame(aOldFrame));
   nsTableCellFrame* cellFrame = static_cast<nsTableCellFrame*>(aOldFrame);
   // remove the cell from the cell map
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   tableFrame->RemoveCell(cellFrame, GetRowIndex());
 
   // Remove the frame and destroy it
   mFrames.DestroyFrame(aOldFrame);
 
   PresContext()->PresShell()->
     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                      NS_FRAME_HAS_DIRTY_CHILDREN);
@@ -311,17 +311,17 @@ nsTableRowFrame::GetFirstCell()
 
 /**
  * Post-reflow hook. This is where the table row does its post-processing
  */
 void
 nsTableRowFrame::DidResize()
 {
   // Resize and re-align the cell frames based on our row height
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   nsTableIterator iter(*this);
   nsIFrame* childFrame = iter.First();
 
   WritingMode wm = GetWritingMode();
   nsHTMLReflowMetrics desiredSize(wm);
   desiredSize.SetSize(wm, GetLogicalSize(wm));
   desiredSize.SetOverflowAreasToDesiredBounds();
 
@@ -477,17 +477,17 @@ nsTableRowFrame::UpdateHeight(nscoord   
       }
     }
   }
 }
 
 nscoord
 nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight())
                             ? 0 : aReflowState.ComputedHeight();
   ResetHeight(computedHeight);
 
   const nsStylePosition* position = StylePosition();
   if (position->mHeight.ConvertsToLength()) {
     SetFixedHeight(nsRuleNode::ComputeCoordPercentCalc(position->mHeight, 0));
   }
@@ -541,26 +541,24 @@ public:
                      nsRenderingContext* aCtx) override;
   NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND)
 };
 
 void
 nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder,
                                    nsRenderingContext* aCtx)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
-  TableBackgroundPainter painter(tableFrame,
+  auto rowFrame = static_cast<nsTableRowFrame*>(mFrame);
+  TableBackgroundPainter painter(rowFrame->GetTableFrame(),
                                  TableBackgroundPainter::eOrigin_TableRow,
                                  mFrame->PresContext(), *aCtx,
                                  mVisibleRect, ToReferenceFrame(),
                                  aBuilder->GetBackgroundPaintFlags());
 
-  DrawResult result =
-    painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
-
+  DrawResult result = painter.PaintRow(rowFrame);
   nsDisplayTableItemGeometry::UpdateDrawResult(this, result);
 }
 
 void
 nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists)
 {
@@ -606,18 +604,17 @@ nsresult
 nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame,
                                            nscoord&          aDesiredHeight)
 {
   nscoord specifiedHeight = 0;
 
   // Get the height specified in the style information
   const nsStylePosition* position = aCellFrame->StylePosition();
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-  int32_t rowSpan = tableFrame->GetEffectiveRowSpan(*aCellFrame);
+  int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame);
 
   switch (position->mHeight.GetUnit()) {
     case eStyleUnit_Calc: {
       if (position->mHeight.CalcHasPercent()) {
         // Treat this like "auto"
         break;
       }
       // Fall through to the coord case
@@ -1038,17 +1035,17 @@ nsTableRowFrame::Reflow(nsPresContext*  
                         nsHTMLReflowMetrics&     aDesiredSize,
                         const nsHTMLReflowState& aReflowState,
                         nsReflowStatus&          aStatus)
 {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   const nsStyleVisibility* rowVis = StyleVisibility();
   bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
   if (collapseRow) {
     tableFrame->SetNeedToCollapse(true);
   }
 
   // see if a special height reflow needs to occur due to having a pct height
   nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
@@ -1094,18 +1091,17 @@ nsTableRowFrame::ReflowCellFrame(nsPresC
                                  nscoord                  aAvailableHeight,
                                  nsReflowStatus&          aStatus)
 {
   // Reflow the cell frame with the specified height. Use the existing width
   nsRect cellRect = aCellFrame->GetRect();
   nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect();
 
   nsSize availSize(cellRect.width, aAvailableHeight);
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-  bool borderCollapse = tableFrame->IsBorderCollapse();
+  bool borderCollapse = GetTableFrame()->IsBorderCollapse();
   nsTableCellReflowState
     cellReflowState(aPresContext, aReflowState, aCellFrame,
                     LogicalSize(aCellFrame->GetWritingMode(),
                                 availSize),
                     nsHTMLReflowState::CALLER_WILL_INIT);
   InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState);
   cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage;
 
@@ -1139,18 +1135,18 @@ nsTableRowFrame::ReflowCellFrame(nsPresC
 nscoord
 nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
                                         nscoord aWidth,
                                         bool    aCollapseGroup,
                                         bool& aDidCollapse)
 {
   const nsStyleVisibility* rowVis = StyleVisibility();
   bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
-  nsTableFrame* tableFrame = static_cast<nsTableFrame*>(
-    nsTableFrame::GetTableFrame(this)->FirstInFlow());
+  nsTableFrame* tableFrame =
+    static_cast<nsTableFrame*>(GetTableFrame()->FirstInFlow());
   if (collapseRow) {
     tableFrame->SetNeedToCollapse(true);
   }
 
   if (aRowOffset != 0) {
     // We're moving, so invalidate our old position
     InvalidateFrameSubtree();
   }
--- a/layout/tables/nsTableRowFrame.h
+++ b/layout/tables/nsTableRowFrame.h
@@ -4,18 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsTableRowFrame_h__
 #define nsTableRowFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsTablePainter.h"
+#include "nsTableRowGroupFrame.h"
+#include "mozilla/WritingModes.h"
 
-class  nsTableFrame;
 class  nsTableCellFrame;
 struct nsTableCellReflowState;
 
 /**
  * nsTableRowFrame is the frame that maps table rows 
  * (HTML tag TR). This class cannot be reused
  * outside of an nsTableRowGroupFrame.  It assumes that its parent is an nsTableRowGroupFrame,  
  * and its children are nsTableCellFrames.
@@ -53,16 +54,28 @@ public:
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableRowFrame* NS_NewTableRowFrame(nsIPresShell* aPresShell,
                                               nsStyleContext* aContext);
 
+  nsTableRowGroupFrame* GetTableRowGroupFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableRowGroupFrame);
+    return static_cast<nsTableRowGroupFrame*>(parent);
+  }
+
+  nsTableFrame* GetTableFrame() const
+  {
+    return GetTableRowGroupFrame()->GetTableFrame();
+  }
+
   virtual nsMargin GetUsedMargin() const override;
   virtual nsMargin GetUsedBorder() const override;
   virtual nsMargin GetUsedPadding() const override;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
@@ -93,16 +106,19 @@ public:
    *
    * @see nsGkAtoms::tableRowFrame
    */
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
+
+  virtual mozilla::WritingMode GetWritingMode() const override
+    { return GetTableFrame()->GetWritingMode(); }
  
   void UpdateHeight(nscoord           aHeight,
                     nscoord           aAscent,
                     nscoord           aDescent,
                     nsTableFrame*     aTableFrame = nullptr,
                     nsTableCellFrame* aCellFrame  = nullptr);
 
   void ResetHeight(nscoord aRowStyleHeight);
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -69,18 +69,17 @@ int32_t nsTableRowGroupFrame::GetStartRo
   int32_t result = -1;
   if (mFrames.NotEmpty()) {
     NS_ASSERTION(mFrames.FirstChild()->GetType() == nsGkAtoms::tableRowFrame,
                  "Unexpected frame type");
     result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex();
   }
   // if the row group doesn't have any children, get it the hard way
   if (-1 == result) {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
-    return tableFrame->GetStartRowIndex(this);
+    return GetTableFrame()->GetStartRowIndex(this);
   }
 
   return result;
 }
 
 void  nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex,
                                              int32_t anAdjustment)
 {
@@ -152,26 +151,24 @@ public:
 
   NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND)
 };
 
 void
 nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
                                         nsRenderingContext* aCtx)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
-  TableBackgroundPainter painter(tableFrame,
+  auto rgFrame = static_cast<nsTableRowGroupFrame*>(mFrame);
+  TableBackgroundPainter painter(rgFrame->GetTableFrame(),
                                  TableBackgroundPainter::eOrigin_TableRowGroup,
                                  mFrame->PresContext(), *aCtx,
                                  mVisibleRect, ToReferenceFrame(),
                                  aBuilder->GetBackgroundPaintFlags());
 
-  DrawResult result =
-    painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
-
+  DrawResult result = painter.PaintRowGroup(rgFrame);
   nsDisplayTableItemGeometry::UpdateDrawResult(this, result);
 }
 
 // Handle the child-traversal part of DisplayGenericTablePart
 static void
 DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
             const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
 {
@@ -322,17 +319,17 @@ nsTableRowGroupFrame::ReflowChildren(nsP
                                      nsHTMLReflowMetrics&   aDesiredSize,
                                      nsRowGroupReflowState& aReflowState,
                                      nsReflowStatus&        aStatus,
                                      bool*                aPageBreakBeforeEnd)
 {
   if (aPageBreakBeforeEnd)
     *aPageBreakBeforeEnd = false;
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   const bool borderCollapse = tableFrame->IsBorderCollapse();
 
   // XXXldb Should we really be checking this rather than available height?
   // (Think about multi-column layout!)
   bool isPaginated = aPresContext->IsPaginated() &&
                        NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height;
 
   bool haveRow = false;
@@ -520,17 +517,17 @@ nsTableRowGroupFrame::DidResizeRows(nsHT
 // Actual row heights will be adjusted later if the table has a style height.
 // Even if rows don't change height, this method must be called to set the heights of each
 // cell in the row to the height of its row.
 void
 nsTableRowGroupFrame::CalculateRowHeights(nsPresContext*           aPresContext,
                                           nsHTMLReflowMetrics&     aDesiredSize,
                                           const nsHTMLReflowState& aReflowState)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   const bool isPaginated = aPresContext->IsPaginated();
 
   int32_t numEffCols = tableFrame->GetEffectiveColCount();
 
   int32_t startRowIndex = GetStartRowIndex();
   // find the row corresponding to the row index we just found
   nsTableRowFrame* startRowFrame = GetFirstRow();
 
@@ -799,17 +796,17 @@ nsTableRowGroupFrame::CalculateRowHeight
 
   aDesiredSize.Height() = rowGroupHeight; // Adjust our desired size
 }
 
 nscoord
 nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aYTotalOffset,
                                                   nscoord aWidth)
 {
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   const nsStyleVisibility* groupVis = StyleVisibility();
   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
   if (collapseGroup) {
     tableFrame->SetNeedToCollapse(true);
   }
 
   nsOverflowAreas overflow;
 
@@ -1289,17 +1286,17 @@ nsTableRowGroupFrame::Reflow(nsPresConte
   aStatus     = NS_FRAME_COMPLETE;
 
   // Row geometry may be going to change so we need to invalidate any row cursor.
   ClearRowCursor();
 
   // see if a special height reflow needs to occur due to having a pct height
   nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   nsRowGroupReflowState state(aReflowState, tableFrame);
   const nsStyleVisibility* groupVis = StyleVisibility();
   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
   if (collapseGroup) {
     tableFrame->SetNeedToCollapse(true);
   }
 
   // Check for an overflow list
@@ -1370,17 +1367,17 @@ nsTableRowGroupFrame::UpdateOverflow()
 /* virtual */ void
 nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
 
   if (!aOldStyleContext) //avoid this on init
     return;
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   if (tableFrame->IsBorderCollapse() &&
       tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
     nsIntRect damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
                          GetRowCount());
     tableFrame->AddBCDamageArea(damageArea);
   }
 }
 
@@ -1407,17 +1404,17 @@ nsTableRowGroupFrame::AppendFrames(Child
     }
   }
 
   int32_t rowIndex = GetRowCount();
   // Append the frames to the sibling chain
   mFrames.AppendFrames(nullptr, aFrameList);
 
   if (rows.Length() > 0) {
-    nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+    nsTableFrame* tableFrame = GetTableFrame();
     tableFrame->AppendRows(this, rowIndex, rows);
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
     tableFrame->SetGeometryDirty();
   }
 }
 
@@ -1430,17 +1427,17 @@ nsTableRowGroupFrame::InsertFrames(Child
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
 
   DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
   ClearRowCursor();
 
   // collect the new row frames in an array
   // XXXbz why are we doing the QI stuff?  There shouldn't be any non-rows here.
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   nsTArray<nsTableRowFrame*> rows;
   bool gotFirstRow = false;
   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
     nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
     NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
     if (rowFrame) {
       NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW ==
                      e.get()->StyleDisplay()->mDisplay,
@@ -1474,20 +1471,20 @@ nsTableRowGroupFrame::InsertFrames(Child
 void
 nsTableRowGroupFrame::RemoveFrame(ChildListID     aListID,
                                   nsIFrame*       aOldFrame)
 {
   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
 
   ClearRowCursor();
 
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   // XXX why are we doing the QI stuff?  There shouldn't be any non-rows here.
   nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame);
   if (rowFrame) {
+    nsTableFrame* tableFrame = GetTableFrame();
     // remove the rows from the table (and flag a rebalance)
     tableFrame->RemoveRows(*rowFrame, 1, true);
 
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN);
     tableFrame->SetGeometryDirty();
   }
@@ -1511,17 +1508,17 @@ nsTableRowGroupFrame::GetUsedPadding() c
 {
   return nsMargin(0,0,0,0);
 }
 
 nscoord
 nsTableRowGroupFrame::GetHeightBasis(const nsHTMLReflowState& aReflowState)
 {
   nscoord result = 0;
-  nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* tableFrame = GetTableFrame();
   int32_t startRowIndex = GetStartRowIndex();
   if ((aReflowState.ComputedHeight() > 0) && (aReflowState.ComputedHeight() < NS_UNCONSTRAINEDSIZE)) {
     nscoord cellSpacing = tableFrame->GetRowSpacing(startRowIndex,
                                                     std::max(startRowIndex,
                                                              startRowIndex + GetRowCount() - 1));
     result = aReflowState.ComputedHeight() - cellSpacing;
   }
   else {
@@ -1647,31 +1644,30 @@ int32_t
 nsTableRowGroupFrame::GetNumLines()
 {
   return GetRowCount();
 }
 
 bool
 nsTableRowGroupFrame::GetDirection()
 {
-  nsTableFrame* table = nsTableFrame::GetTableFrame(this);
   return (NS_STYLE_DIRECTION_RTL ==
-          table->StyleVisibility()->mDirection);
+          GetTableFrame()->StyleVisibility()->mDirection);
 }
 
 NS_IMETHODIMP
 nsTableRowGroupFrame::GetLine(int32_t    aLineNumber,
                               nsIFrame** aFirstFrameOnLine,
                               int32_t*   aNumFramesOnLine,
                               nsRect&    aLineBounds)
 {
   NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
   NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
 
-  nsTableFrame* table = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* table = GetTableFrame();
   nsTableCellMap* cellMap = table->GetCellMap();
 
   *aFirstFrameOnLine = nullptr;
   *aNumFramesOnLine = 0;
   aLineBounds.SetRect(0, 0, 0, 0);
 
   if ((aLineNumber < 0) || (aLineNumber >=  GetRowCount())) {
     return NS_OK;
@@ -1723,17 +1719,17 @@ nsTableRowGroupFrame::CheckLineOrder(int
 
 NS_IMETHODIMP
 nsTableRowGroupFrame::FindFrameAt(int32_t    aLineNumber,
                                   nsPoint    aPos,
                                   nsIFrame** aFrameFound,
                                   bool*    aPosIsBeforeFirstFrame,
                                   bool*    aPosIsAfterLastFrame)
 {
-  nsTableFrame* table = nsTableFrame::GetTableFrame(this);
+  nsTableFrame* table = GetTableFrame();
   nsTableCellMap* cellMap = table->GetCellMap();
 
   WritingMode wm = table->GetWritingMode();
   nscoord cw = table->GetRect().width;
   LogicalPoint pos(wm, aPos, cw);
 
   *aFrameFound = nullptr;
   *aPosIsBeforeFirstFrame = true;
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -7,18 +7,18 @@
 
 #include "mozilla/Attributes.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsIAtom.h"
 #include "nsILineIterator.h"
 #include "nsTablePainter.h"
 #include "nsTArray.h"
+#include "nsTableFrame.h"
 
-class nsTableFrame;
 class nsTableRowFrame;
 
 struct nsRowGroupReflowState {
   const nsHTMLReflowState& reflowState;  // Our reflow state
 
   nsTableFrame* tableFrame;
 
   // The available size (computed from the parent)
@@ -63,16 +63,23 @@ public:
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableRowGroupFrame* NS_NewTableRowGroupFrame(nsIPresShell* aPresShell,
                                                         nsStyleContext* aContext);
   virtual ~nsTableRowGroupFrame();
 
+  nsTableFrame* GetTableFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame);
+    return static_cast<nsTableFrame*>(parent);
+  }
+
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
@@ -113,16 +120,19 @@ public:
   virtual nsIAtom* GetType() const override;
 
   nsTableRowFrame* GetFirstRow();
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
+  virtual mozilla::WritingMode GetWritingMode() const override
+    { return GetTableFrame()->GetWritingMode(); }
+
   /** return the number of child rows (not necessarily == number of child frames) */
   int32_t GetRowCount();
 
   /** return the table-relative row index of the first row in this rowgroup.
     * if there are no rows, -1 is returned.
     */
   int32_t GetStartRowIndex();
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -127,17 +127,20 @@ pref("dom.indexedDB.logging.profiler-mar
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
 // Whether or not Shared Web Workers are enabled.
 pref("dom.workers.sharedWorkers.enabled", true);
 
-// WebSocket in workers are disabled by default.
+// Whether or not WebSockets in workers are enabled.
+// Note: we need this pref because WebSocket in Workers is a new implementation
+// and we want to be able to disable it quickly in case of regressions.
+// When this feature is stable enough we can get rid of this pref: Bug 1159792
 pref("dom.workers.websocket.enabled", true);
 
 // Service workers
 pref("dom.serviceWorkers.enabled", false);
 
 // Whether nonzero values can be returned from performance.timing.*
 pref("dom.enable_performance", true);
 
@@ -243,16 +246,19 @@ pref("browser.chrome.toolbar_style",    
 // if 0, no images are used for tab icons for image documents.
 pref("browser.chrome.image_icons.max_size", 1024);
 
 pref("browser.triple_click_selects_paragraph", true);
 
 // Print/Preview Shrink-To-Fit won't shrink below 20% for text-ish documents.
 pref("print.shrink-to-fit.scale-limit-percent", 20);
 
+// Enable scale transform for stretchy MathML operators. See bug 414277.
+pref("mathml.scale_stretchy_operators.enabled", true);
+
 // Media cache size in kilobytes
 pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 999999);
 // Stop reading ahead when our buffered data is this many seconds ahead
 // of the current playback position. This limit can stop us from using arbitrary
 // amounts of network bandwidth prefetching huge videos.
@@ -433,17 +439,17 @@ pref("media.webvtt.regions.enabled", fal
 
 // AudioTrack and VideoTrack support
 pref("media.track.enabled", false);
 
 // Whether to enable MediaSource support.
 // We want to enable on non-release  builds and on release windows and mac
 // but on release builds restrict to YouTube. We don't enable for other
 // configurations because code for those platforms isn't ready yet.
-#if defined(XP_WIN) || defined(XP_MACOSX)
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
 pref("media.mediasource.enabled", true);
 #else
 pref("media.mediasource.enabled", false);
 #endif
 
 #ifdef RELEASE_BUILD
 pref("media.mediasource.whitelist", true);
 #else
@@ -2378,17 +2384,17 @@ pref("plugin.persistentPermissionAlways.
 // before we consider it "hung".
 pref("dom.ipc.plugins.timeoutSecs", 45);
 // How long a plugin process will wait for a response from the parent
 // to a synchronous request before terminating itself. After this
 // point the child assumes the parent is hung. Currently disabled.
 pref("dom.ipc.plugins.parentTimeoutSecs", 0);
 // How long a plugin in e10s is allowed to process a synchronous IPC
 // message before we notify the chrome process of a hang.
-pref("dom.ipc.plugins.contentTimeoutSecs", 45);
+pref("dom.ipc.plugins.contentTimeoutSecs", 10);
 // How long a plugin launch is allowed to take before
 // we consider it failed.
 pref("dom.ipc.plugins.processLaunchTimeoutSecs", 45);
 #ifdef XP_WIN
 // How long a plugin is allowed to process a synchronous IPC message
 // before we display the plugin hang UI
 pref("dom.ipc.plugins.hangUITimeoutSecs", 11);
 // Minimum time that the plugin hang UI will be displayed
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -316,35 +316,44 @@ nsHttpChannel::Connect()
     // Even if we're in private browsing mode, we still enforce existing STS
     // data (it is read-only).
     // if the connection is not using SSL and either the exact host matches or
     // a superdomain wants to force HTTPS, do it.
     bool isHttps = false;
     rv = mURI->SchemeIs("https", &isHttps);
     NS_ENSURE_SUCCESS(rv,rv);
 
-    if (mAllowSTS && !isHttps) {
+    if (!isHttps) {
         // enforce Strict-Transport-Security
         nsISiteSecurityService* sss = gHttpHandler->GetSSService();
         NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
 
         bool isStsHost = false;
         uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
         rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags,
                               &isStsHost);
 
         // if the SSS check fails, it's likely because this load is on a
         // malformed URI or something else in the setup is wrong, so any error
         // should be reported.
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (isStsHost) {
             LOG(("nsHttpChannel::Connect() STS permissions found\n"));
-            return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
+            if (mAllowSTS) {
+                Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
+                return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
+            } else {
+                Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2);
+            }
+        } else {
+            Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1);
         }
+    } else {
+        Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
     }
 
     // ensure that we are using a valid hostname
     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
         return NS_ERROR_UNKNOWN_HOST;
 
     // Finalize ConnectionInfo flags before SpeculativeConnect
     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
@@ -2320,17 +2329,17 @@ nsHttpChannel::ProcessPartialContent()
          this, mTransaction.get(), cachedContentLength, entitySize,
          mResponseHead->PeekHeader(nsHttp::Content_Range)));
 
     if ((entitySize >= 0) && (cachedContentLength >= 0) &&
         (entitySize != cachedContentLength)) {
         LOG(("nsHttpChannel::ProcessPartialContent [this=%p] "
              "206 has different total entity size than the content length "
              "of the original partially cached entity.\n", this));
-        
+
         mCacheEntry->AsyncDoom(nullptr);
         Cancel(NS_ERROR_CORRUPTED_CONTENT);
         return CallOnStartRequest();
     }
 
     if (mConcurentCacheAccess) {
         // We started to read cached data sooner than its write has been done.
         // But the concurrent write has not finished completely, so we had to
@@ -2755,17 +2764,17 @@ nsHttpChannel::OpenCacheEntry(bool isHtt
         cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
     }
     else {
         cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY
                             | nsICacheStorage::CHECK_MULTITHREADED;
     }
 
     if (!mPostID && mApplicationCache) {
-        rv = cacheStorageService->AppCacheStorage(info, 
+        rv = cacheStorageService->AppCacheStorage(info,
             mApplicationCache,
             getter_AddRefs(cacheStorage));
     }
     else if (PossiblyIntercepted() || mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
         rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well...
             getter_AddRefs(cacheStorage));
     }
     else {
@@ -5806,17 +5815,17 @@ nsHttpChannel::OnDataAvailable(nsIReques
                         "http channel Listener OnDataAvailable contract violation"));
                     if (consoleService) {
                         consoleService->LogStringMessage(message.get());
                     }
                 }
             }
             mLogicalOffset += count;
         }
-        
+
         return rv;
     }
 
     return NS_ERROR_ABORT;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIThreadRetargetableRequest
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -671,17 +671,20 @@ class GTestCommands(MachCommandBase):
 
         # Use GTest environment variable to control test execution
         # For details see:
         # https://code.google.com/p/googletest/wiki/AdvancedGuide#Running_Test_Programs:_Advanced_Options
         gtest_env = {b'GTEST_FILTER': gtest_filter}
 
         xre_path = os.path.join(self.topobjdir, "dist", "bin")
         gtest_env["MOZ_XRE_DIR"] = xre_path
-        gtest_env["MOZ_GMP_PATH"] = os.path.join(xre_path, "gmp-fake", "1.0")
+        gtest_env["MOZ_GMP_PATH"] = os.pathsep.join(
+            os.path.join(xre_path, p, "1.0")
+            for p in ('gmp-fake', 'gmp-fakeopenh264')
+        )
 
         gtest_env[b"MOZ_RUN_GTEST"] = b"True"
 
         if shuffle:
             gtest_env[b"GTEST_SHUFFLE"] = b"True"
 
         if tbpl_parser:
             gtest_env[b"MOZ_TBPL_PARSER"] = b"True"
--- a/security/sandbox/win/wow_helper/Makefile.in
+++ b/security/sandbox/win/wow_helper/Makefile.in
@@ -35,8 +35,13 @@ LIB = $(call lazy,LIB,$$(shell python -c
 CXXFLAGS := $(filter-out -arch:IA32,$(CXXFLAGS))
 
 # OS_COMPILE_CXXFLAGS includes mozilla-config.h, which contains x86-specific
 # defines breaking the build.
 OS_COMPILE_CXXFLAGS :=
 
 # LNK1246: '/SAFESEH' not compatible with 'x64' target machine
 LDFLAGS := $(filter-out -SAFESEH,$(LDFLAGS))
+
+# When targetting x64, we need to specify a subsystem of at least 5.02, because
+# the 5.01 value we inherit from the x86 parts is silently ignored, making the
+# linker default to 6.00 (Vista) as of VS2013.
+WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,5.02
--- a/testing/gtest/rungtests.py
+++ b/testing/gtest/rungtests.py
@@ -62,17 +62,20 @@ class GTests(object):
         return result
 
     def build_core_environment(self, env = {}):
         """
         Add environment variables likely to be used across all platforms, including remote systems.
         """
         env["MOZILLA_FIVE_HOME"] = self.xre_path
         env["MOZ_XRE_DIR"] = self.xre_path
-        env["MOZ_GMP_PATH"] = os.path.join(self.xre_path, "gmp-fake", "1.0")
+        env["MOZ_GMP_PATH"] = os.pathsep.join(
+            os.path.join(self.xre_path, p, "1.0")
+            for p in ('gmp-fake', 'gmp-fakeopenh264')
+        )
         env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
         env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
         env["MOZ_CRASHREPORTER"] = "1"
         env["MOZ_RUN_GTEST"] = "1"
         # Normally we run with GTest default output, override this to use the TBPL test format.
         env["MOZ_TBPL_PARSER"] = "1"
 
         if not mozinfo.has_sandbox:
deleted file mode 100644
--- a/testing/marionette/client/marionette/tests/unit/test_submit.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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/.
-
-import time
-
-from marionette_driver.by import By
-from marionette_driver.errors import NoSuchElementException
-from marionette_driver.wait import Wait
-from marionette import MarionetteTestCase
-
-class TestSubmit(MarionetteTestCase):
-
-    def test_should_be_able_to_submit_forms(self):
-        test_html = self.marionette.absolute_url("formPage.html")
-        self.marionette.navigate(test_html)
-        self.marionette.find_element(By.NAME, "login").submit()
-        for x in xrange(1, 10):
-            try:
-                self.marionette.find_element(By.NAME, "login")
-                time.sleep(1)
-            except NoSuchElementException:
-                break
-
-        Wait(self.marionette, timeout=10, ignored_exceptions=NoSuchElementException)\
-            .until(lambda m: m.find_element(By.ID, 'email'))
-        title = self.marionette.title
-        count = 0
-        while self.marionette.title == '' and count <= 10:
-            time.sleep(1)
-            title = self.marionette.title
-            count = count + 1
-            if title != '':
-                break
-        self.assertEqual(title, "We Arrive Here")
-
-    def test_should_submit_a_form_when_any_input_element_within_that_form_is_submitted(self):
-        test_html = self.marionette.absolute_url("formPage.html")
-        self.marionette.navigate(test_html)
-        self.marionette.find_element(By.ID, "checky").submit()
-
-        for x in xrange(1, 10):
-            try:
-                self.marionette.find_element(By.ID, "checky")
-                time.sleep(1)
-            except NoSuchElementException:
-                break
-
-        Wait(self.marionette, ignored_exceptions=NoSuchElementException).until(
-            lambda m: m.find_element(By.ID, 'email'))
-        self.assertEqual(self.marionette.title, "We Arrive Here")
-
-    def test_should_submit_a_form_when_any_element_wihin_that_form_is_submitted(self):
-        test_html = self.marionette.absolute_url("formPage.html")
-        self.marionette.navigate(test_html)
-        self.marionette.find_element(By.XPATH, "//form/p").submit()
-
-        for x in xrange(1, 10):
-            try:
-                self.marionette.find_element(By.XPATH, "//form/p")
-                time.sleep(1)
-            except NoSuchElementException:
-                break
-
-        Wait(self.marionette, ignored_exceptions=NoSuchElementException).until(
-            lambda m: m.find_element(By.ID, 'email'))
-        self.assertEqual(self.marionette.title, "We Arrive Here")
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -127,17 +127,16 @@ b2g = false
 b2g = false
 [test_implicit_waits.py]
 [test_wait.py]
 [test_expected.py]
 [test_date_time_value.py]
 [test_getactiveframe_oop.py]
 disabled = "Bug 925688"
 b2g = false
-[test_submit.py]
 [test_chrome_async_finish.js]
 [test_screen_orientation.py]
 browser = false
 [test_errors.py]
 
 [test_execute_isolate.py]
 [test_click_scrolling.py]
 [test_profile_management.py]
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2083,35 +2083,16 @@ GeckoDriver.prototype.getElementValueOfC
     case Context.CONTENT:
       resp.value = yield this.listener.getElementValueOfCssProperty(
           {id: id, propertyName: prop});
       break;
   }
 };
 
 /**
- * Submit a form on a content page by either using form or element in
- * a form.
- *
- * @param {string} id
- *     Reference to the elemen that will be checked.
- */
-GeckoDriver.prototype.submitElement = function(cmd, resp) {
-  switch (this.context) {
-    case Context.CHROME:
-      throw new WebDriverError(
-          "Command 'submitElement' is not available in chrome context");
-
-    case Context.CONTENT:
-      yield this.listener.submitElement({id: cmd.parameters.id});
-      break;
-  }
-};
-
-/**
  * Check if element is enabled.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
  */
 GeckoDriver.prototype.isElementEnabled = function(cmd, resp) {
   let id = cmd.parameters.id;
 
@@ -2947,17 +2928,16 @@ GeckoDriver.prototype.commands = {
   "findElements": GeckoDriver.prototype.findElements,
   "findChildElements":GeckoDriver.prototype.findChildElements, // Needed for WebDriver compat
   "clickElement": GeckoDriver.prototype.clickElement,
   "getElementAttribute": GeckoDriver.prototype.getElementAttribute,
   "getElementText": GeckoDriver.prototype.getElementText,
   "getElementTagName": GeckoDriver.prototype.getElementTagName,
   "isElementDisplayed": GeckoDriver.prototype.isElementDisplayed,
   "getElementValueOfCssProperty": GeckoDriver.prototype.getElementValueOfCssProperty,
-  "submitElement": GeckoDriver.prototype.submitElement,
   "getElementSize": GeckoDriver.prototype.getElementSize,  //deprecated
   "getElementRect": GeckoDriver.prototype.getElementRect,
   "isElementEnabled": GeckoDriver.prototype.isElementEnabled,
   "isElementSelected": GeckoDriver.prototype.isElementSelected,
   "sendKeysToElement": GeckoDriver.prototype.sendKeysToElement,
   "getElementLocation": GeckoDriver.prototype.getElementLocation,  // deprecated
   "getElementPosition": GeckoDriver.prototype.getElementLocation,  // deprecated
   "clearElement": GeckoDriver.prototype.clearElement,
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -169,21 +169,16 @@ class HTMLElement(object):
         '''
         Gets the value of the specified CSS property name.
 
         :param property_name: Property name to get the value of.
         '''
         return self.marionette._send_message('getElementValueOfCssProperty', 'value',
                                              id=self.id,
                                              propertyName=property_name)
-    def submit(self):
-        '''
-        Submits if the element is a form or is within a form
-        '''
-        return self.marionette._send_message('submitElement', 'ok', id=self.id)
 
 class MouseButton(object):
     '''
     Enum-like class for mouse button constants
     '''
     LEFT = 0
     MIDDLE = 1
     RIGHT = 2
--- a/testing/marionette/driver/setup.py
+++ b/testing/marionette/driver/setup.py
@@ -1,11 +1,11 @@
 from setuptools import setup, find_packages
 
-version = '0.5'
+version = '0.6'
 
 # dependencies
 with open('requirements.txt') as f:
     deps = f.read().splitlines()
 
 setup(name='marionette_driver',
       version=version,
       description="Marionette Driver",
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -216,17 +216,16 @@ function startListeners() {
   addMessageListenerId("Marionette:findElementsContent", findElementsContent);
   addMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
   addMessageListenerId("Marionette:clickElement", clickElementFn);
   addMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
   addMessageListenerId("Marionette:getElementText", getElementTextFn);
   addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssProperty);
-  addMessageListenerId("Marionette:submitElement", submitElement);
   addMessageListenerId("Marionette:getElementSize", getElementSizeFn);  // deprecated
   addMessageListenerId("Marionette:getElementRect", getElementRectFn);
   addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   addMessageListenerId("Marionette:isElementSelected", isElementSelected);
   addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
   addMessageListenerId("Marionette:getElementLocation", getElementLocation); //deprecated
   addMessageListenerId("Marionette:clearElement", clearElement);
   addMessageListenerId("Marionette:switchToFrame", switchToFrame);
@@ -321,17 +320,16 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:findElementsContent", findElementsContent);
   removeMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
   removeMessageListenerId("Marionette:clickElement", clickElementFn);
   removeMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
   removeMessageListenerId("Marionette:getElementText", getElementTextFn);
   removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
   removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayed);
   removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssProperty);
-  removeMessageListenerId("Marionette:submitElement", submitElement);
   removeMessageListenerId("Marionette:getElementSize", getElementSizeFn); // deprecated
   removeMessageListenerId("Marionette:getElementRect", getElementRectFn);
   removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
   removeMessageListenerId("Marionette:isElementSelected", isElementSelected);
   removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
   removeMessageListenerId("Marionette:getElementLocation", getElementLocation);
   removeMessageListenerId("Marionette:clearElement", clearElement);
   removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
@@ -451,53 +449,54 @@ function checkForInterrupted() {
 
 /*
  * Marionette Methods
  */
 
 /**
  * Returns a content sandbox that can be used by the execute_foo functions.
  */
-function createExecuteContentSandbox(aWindow, timeout) {
-  let sandbox = new Cu.Sandbox(aWindow, {sandboxPrototype: aWindow});
+function createExecuteContentSandbox(win, timeout) {
+  let mn = new Marionette(
+      this,
+      win,
+      "content",
+      marionetteLogObj,
+      timeout,
+      heartbeatCallback,
+      marionetteTestName);
+  mn.runEmulatorCmd = (cmd, cb) => this.runEmulatorCmd(cmd, cb);
+  mn.runEmulatorShell = (args, cb) => this.runEmulatorShell(args, cb);
+
+  let sandbox = new Cu.Sandbox(win, {sandboxPrototype: win});
   sandbox.global = sandbox;
-  sandbox.window = aWindow;
+  sandbox.window = win;
   sandbox.document = sandbox.window.document;
   sandbox.navigator = sandbox.window.navigator;
   sandbox.testUtils = utils;
   sandbox.asyncTestCommandId = asyncTestCommandId;
+  sandbox.marionette = mn;
 
-  let marionette = new Marionette(this, aWindow, "content",
-                                  marionetteLogObj, timeout,
-                                  heartbeatCallback,
-                                  marionetteTestName);
-  marionette.runEmulatorCmd = (cmd, cb) => this.runEmulatorCmd(cmd, cb);
-  marionette.runEmulatorShell = (args, cb) => this.runEmulatorShell(args, cb);
-  sandbox.marionette = marionette;
-  marionette.exports.forEach(function(fn) {
-    try {
-      sandbox[fn] = marionette[fn].bind(marionette);
-    }
-    catch(e) {
-      sandbox[fn] = marionette[fn];
+  mn.exports.forEach(fn => {
+    if (typeof mn[fn] == "function") {
+      sandbox[fn] = mn[fn].bind(mn);
+    } else {
+      sandbox[fn] = mn[fn];
     }
   });
 
-  if (aWindow.wrappedJSObject.SpecialPowers != undefined) {
-    XPCOMUtils.defineLazyGetter(sandbox, 'SpecialPowers', function() {
-      return aWindow.wrappedJSObject.SpecialPowers;
-    });
+  let specialPowersFn;
+  if (typeof win.wrappedJSObject.SpecialPowers != "undefined") {
+    specialPowersFn = () => win.wrappedJSObject.SpecialPowers;
+  } else {
+    specialPowersFn = () => new SpecialPowers(win);
   }
-  else {
-    XPCOMUtils.defineLazyGetter(sandbox, 'SpecialPowers', function() {
-      return new SpecialPowers(aWindow);
-    });
-  }
+  XPCOMUtils.defineLazyGetter(sandbox, "SpecialPowers", specialPowersFn);
 
-  sandbox.asyncComplete = function(obj, id) {
+  sandbox.asyncComplete = (obj, id) => {
     if (id == asyncTestCommandId) {
       curFrame.removeEventListener("unload", onunload, false);
       curFrame.clearTimeout(asyncTestTimeoutId);
 
       if (inactivityTimeoutId != null) {
         curFrame.clearTimeout(inactivityTimeoutId);
       }
 
@@ -520,19 +519,19 @@ function createExecuteContentSandbox(aWi
       asyncTestTimeoutId = undefined;
       asyncTestCommandId = undefined;
       inactivityTimeoutId = null;
     }
   };
 
   sandbox.finish = function() {
     if (asyncTestRunning) {
-      sandbox.asyncComplete(marionette.generate_results(), sandbox.asyncTestCommandId);
+      sandbox.asyncComplete(mn.generate_results(), sandbox.asyncTestCommandId);
     } else {
-      return marionette.generate_results();
+      return mn.generate_results();
     }
   };
   sandbox.marionetteScriptFinished = val =>
       sandbox.asyncComplete(val, sandbox.asyncTestCommandId);
 
   return sandbox;
 }
 
@@ -1449,39 +1448,16 @@ function getElementValueOfCssProperty(ms
     sendResponse({value: curFrame.document.defaultView.getComputedStyle(el, null).getPropertyValue(propertyName)},
                  command_id);
   } catch (e) {
     sendError(e, command_id);
   }
 }
 
 /**
-  * Submit a form on a content page by either using form or element in a form
-  * @param object msg
-  *               'json' JSON object containing 'id' member of the element
-  */
-function submitElement (msg) {
-  let command_id = msg.json.command_id;
-  try {
-    let el = elementManager.getKnownElement(msg.json.id, curFrame);
-    while (el.parentNode != null && el.tagName.toLowerCase() != 'form') {
-      el = el.parentNode;
-    }
-    if (el.tagName && el.tagName.toLowerCase() == 'form') {
-      el.submit();
-      sendOk(command_id);
-    } else {
-      sendError(new NoSuchElementError("Element is not a form element or in a form"), command_id);
-    }
-  } catch (e) {
-    sendError(e, command_id);
-  }
-}
-
-/**
  * Get the size of the element.
  *
  * @param {WebElement} id
  *     Web element reference.
  *
  * @return {Object.<string, number>}
  *     The width/height dimensions of th element.
  */
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -81,16 +81,17 @@ TEST_HARNESS_PLUGINS := \
 endif
 
 # Rules for staging the necessary harness bits for a test package
 PKG_STAGE = $(DIST)/test-stage
 DIST_BIN = $(DIST)/bin
 
 GMP_TEST_PLUGIN_DIRS := \
   $(DIST_BIN)/gmp-fake \
+  $(DIST_BIN)/gmp-fakeopenh264 \
   $(DIST_BIN)/gmp-clearkey \
   $(NULL)
 
 $(_DEST_DIR):
 	$(NSINSTALL) -D $@
 
 # On Android only, include a release signed Robocop APK in the test package.
 ifeq ($(MOZ_BUILD_APP),mobile/android)
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -452,25 +452,24 @@ class MochitestRunner(MozbuildObject):
             if app_override == "dist":
                 options.app = self.get_binary_path(where='staged-package')
             elif app_override:
                 options.app = app_override
             if options.gmp_path is None:
                 # Need to fix the location of gmp_fake which might not be
                 # shipped in the binary
                 bin_path = self.get_binary_path()
-                options.gmp_path = os.path.join(
-                    os.path.dirname(bin_path),
-                    'gmp-fake',
-                    '1.0')
-                options.gmp_path += os.pathsep
-                options.gmp_path += os.path.join(
-                    os.path.dirname(bin_path),
-                    'gmp-clearkey',
-                    '0.1')
+                gmp_modules = (
+                    ('gmp-fake', '1.0'),
+                    ('gmp-clearkey', '0.1'),
+                    ('gmp-fakeopenh264', '1.0')
+                )
+                options.gmp_path = os.pathsep.join(
+                    os.path.join(os.path.dirname(bin_path), *p)
+                    for p in gmp_modules)
 
         logger_options = {
             key: value for key,
             value in vars(options).iteritems() if key.startswith('log')}
         runner = mochitest.Mochitest(logger_options)
         options = opts.verifyOptions(options, runner)
 
         if options is None:
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1446,16 +1446,17 @@ class Mochitest(MochitestUtilsMixin):
             options.xrePath,
             # For packaged builds, GMP plugins will get copied under
             # $profile/plugins.
             os.path.join(self.profile.profile, 'plugins'),
         ]
 
         gmp_subdirs = [
             os.path.join('gmp-fake', '1.0'),
+            os.path.join('gmp-fakeopenh264', '1.0'),
             os.path.join('gmp-clearkey', '0.1'),
         ]
 
         gmp_paths = [os.path.join(parent, sub)
                      for parent in gmp_parentdirs
                      for sub in gmp_subdirs
                      if os.path.isdir(os.path.join(parent, sub))]
 
--- a/testing/mozharness/mozharness.json
+++ b/testing/mozharness/mozharness.json
@@ -1,4 +1,4 @@
 {
     "repo": "https://hg.mozilla.org/build/mozharness",
-    "revision": "23dee28169d6"
+    "revision": "fa7ea67fc5a2"
 }
--- a/testing/web-platform/harness/wptrunner/executors/executormarionette.py
+++ b/testing/web-platform/harness/wptrunner/executors/executormarionette.py
@@ -152,30 +152,29 @@ class MarionetteProtocol(Protocol):
 
     def set_pref(self, name, value):
         self.logger.info("Setting pref %s (%s)" % (name, value))
         self.marionette.set_context(self.marionette.CONTEXT_CHROME)
         script = """
             let prefInterface = Components.classes["@mozilla.org/preferences-service;1"]
                                           .getService(Components.interfaces.nsIPrefBranch);
             let pref = '%s';
-            let value = '%s';
             let type = prefInterface.getPrefType(pref);
             switch(type) {
                 case prefInterface.PREF_STRING:
-                    prefInterface.setCharPref(pref, value);
+                    prefInterface.setCharPref(pref, '%s');
                     break;
                 case prefInterface.PREF_BOOL:
-                    prefInterface.setBoolPref(pref, value);
+                    prefInterface.setBoolPref(pref, %s);
                     break;
                 case prefInterface.PREF_INT:
-                    prefInterface.setIntPref(pref, value);
+                    prefInterface.setIntPref(pref, %s);
                     break;
             }
-            """ % (name, value)
+            """ % (name, value, value, value)
         self.marionette.execute_script(script)
         self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
 
     def clear_user_pref(self, name):
         self.logger.info("Clearing pref %s" % (name))
         self.marionette.set_context(self.marionette.CONTEXT_CHROME)
         script = """
             let prefInterface = Components.classes["@mozilla.org/preferences-service;1"]
--- a/testing/web-platform/harness/wptrunner/executors/executorservo.py
+++ b/testing/web-platform/harness/wptrunner/executors/executorservo.py
@@ -181,17 +181,18 @@ class ServoRefTestExecutor(ProcessTestEx
         os.rmdir(self.tempdir)
         ProcessTestExecutor.teardown(self)
 
     def screenshot(self, test):
         full_url = self.test_url(test)
 
         with TempFilename(self.tempdir) as output_path:
             self.command = [self.binary, "--cpu", "--hard-fail", "--exit",
-                            "--output=%s" % output_path, full_url]
+                            "-Z", "disable-text-aa", "--output=%s" % output_path,
+                            full_url]
 
             env = os.environ.copy()
             env["HOST_FILE"] = self.hosts_path
 
             self.proc = ProcessHandler(self.command,
                                        processOutputLine=[self.on_output],
                                        env=env)
 
--- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm
@@ -129,16 +129,20 @@ let NotificationTracker = {
     let watchers = this._watchers[component1];
     let index = watchers.lastIndexOf(watcher);
     if (index > -1) {
       watchers.splice(index, 1);
     }
 
     this._registered.delete(watcher);
   },
+
+  getCount(component1) {
+    return this.findPaths([component1]).length;
+  },
 };
 
 // This code registers an nsIContentPolicy in the child process. When
 // it runs, it notifies the parent that it needs to run its own
 // nsIContentPolicy list. If any policy in the parent rejects a
 // resource load, that answer is returned to the child.
 let ContentPolicyChild = {
   _classDescription: "Addon shim content policy",
@@ -538,9 +542,13 @@ let RemoteAddonsChild = {
     for (let shim of perTabShims) {
       try {
         shim.uninit();
       } catch(e) {
         Cu.reportError(e);
       }
     }
   },
+
+  get useSyncWebProgress() {
+    return NotificationTracker.getCount("web-progress") > 0;
+  },
 };
--- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm
@@ -5,16 +5,17 @@
 this.EXPORTED_SYMBOLS = ["RemoteAddonsParent"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/RemoteWebProgress.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
                                   "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Prefetcher",
                                   "resource://gre/modules/Prefetcher.jsm");
@@ -874,16 +875,80 @@ TabBrowserElementInterposition.getters.c
 TabBrowserElementInterposition.getters.contentDocument = function(addon, target) {
   CompatWarning.warn("Direct access to content objects will no longer work in the chrome process.",
                      addon, CompatWarning.warnings.content);
 
   let browser = target.selectedBrowser;
   return getContentDocument(addon, browser);
 };
 
+// This function returns a wrapper around an
+// nsIWebProgressListener. When the wrapper is invoked, it calls the
+// real listener but passes CPOWs for the nsIWebProgress and
+// nsIRequest arguments.
+let progressListeners = {global: new WeakMap(), tabs: new WeakMap()};
+function wrapProgressListener(kind, listener)
+{
+  if (progressListeners[kind].has(listener)) {
+    return progressListeners[kind].get(listener);
+  }
+
+  let ListenerHandler = {
+    get: function(target, name) {
+      if (name.startsWith("on")) {
+        return function(...args) {
+          listener[name].apply(listener, RemoteWebProgressManager.argumentsForAddonListener(kind, args));
+        };
+      }
+
+      return listener[name];
+    }
+  };
+  let listenerProxy = new Proxy(listener, ListenerHandler);
+
+  progressListeners[kind].set(listener, listenerProxy);
+  return listenerProxy;
+}
+
+TabBrowserElementInterposition.methods.addProgressListener = function(addon, target, listener) {
+  if (!target.ownerDocument.defaultView.gMultiProcessBrowser) {
+    return target.addProgressListener(listener);
+  }
+
+  NotificationTracker.add(["web-progress", addon]);
+  return target.addProgressListener(wrapProgressListener("global", listener));
+};
+
+TabBrowserElementInterposition.methods.removeProgressListener = function(addon, target, listener) {
+  if (!target.ownerDocument.defaultView.gMultiProcessBrowser) {
+    return target.removeProgressListener(listener);
+  }
+
+  NotificationTracker.remove(["web-progress", addon]);
+  return target.removeProgressListener(wrapProgressListener("global", listener));
+};
+
+TabBrowserElementInterposition.methods.addTabsProgressListener = function(addon, target, listener) {
+  if (!target.ownerDocument.defaultView.gMultiProcessBrowser) {
+    return target.addTabsProgressListener(listener);
+  }
+
+  NotificationTracker.add(["web-progress", addon]);
+  return target.addTabsProgressListener(wrapProgressListener("tabs", listener));
+};
+
+TabBrowserElementInterposition.methods.removeTabsProgressListener = function(addon, target, listener) {
+  if (!target.ownerDocument.defaultView.gMultiProcessBrowser) {
+    return target.removeTabsProgressListener(listener);
+  }
+
+  NotificationTracker.remove(["web-progress", addon]);
+  return target.removeTabsProgressListener(wrapProgressListener("tabs", listener));
+};
+
 let ChromeWindowInterposition = new Interposition("ChromeWindowInterposition",
                                                   EventTargetInterposition);
 
 // _content is for older add-ons like pinboard and all-in-one gestures
 // that should be using content instead.
 ChromeWindowInterposition.getters.content =
 ChromeWindowInterposition.getters._content = function(addon, target) {
   CompatWarning.warn("Direct access to content objects will no longer work in the chrome process.",
--- a/toolkit/components/addoncompat/tests/addon/bootstrap.js
+++ b/toolkit/components/addoncompat/tests/addon/bootstrap.js
@@ -489,32 +489,78 @@ function testAboutModuleRegistration()
         gBrowser.removeTab(newTab);
         unregisterModules();
         resolve();
       });
     });
   });
 }
 
+function testProgressListener()
+{
+  const url = baseURL + "browser_addonShims_testpage.html";
+
+  let sawGlobalLocChange = false;
+  let sawTabsLocChange = false;
+
+  let globalListener = {
+    onLocationChange: function(webProgress, request, uri) {
+      if (uri.spec == url) {
+        sawGlobalLocChange = true;
+        ok(request instanceof Ci.nsIHttpChannel, "Global listener channel is an HTTP channel");
+      }
+    },
+  };
+
+  let tabsListener = {
+    onLocationChange: function(browser, webProgress, request, uri) {
+      if (uri.spec == url) {
+        sawTabsLocChange = true;
+        ok(request instanceof Ci.nsIHttpChannel, "Tab listener channel is an HTTP channel");
+      }
+    },
+  };
+
+  gBrowser.addProgressListener(globalListener);
+  gBrowser.addTabsProgressListener(tabsListener);
+  info("Added progress listeners");
+
+  return new Promise(function(resolve, reject) {
+    let tab = gBrowser.addTab(url);
+    gBrowser.selectedTab = tab;
+    addLoadListener(tab.linkedBrowser, function handler() {
+      ok(sawGlobalLocChange, "Saw global onLocationChange");
+      ok(sawTabsLocChange, "Saw tabs onLocationChange");
+
+      gBrowser.removeTab(tab);
+      gBrowser.removeProgressListener(globalListener);
+      gBrowser.removeTabsProgressListener(tabsListener);
+      resolve();
+    });
+  });
+}
+
 function runTests(win, funcs)
 {
   ok = funcs.ok;
   is = funcs.is;
   info = funcs.info;
 
   gWin = win;
   gBrowser = win.gBrowser;
 
   return testContentWindow().
     then(testListeners).
     then(testCapturing).
     then(testObserver).
     then(testSandbox).
     then(testAddonContent).
-    then(testAboutModuleRegistration);
+    then(testAboutModuleRegistration).
+    then(testProgressListener).
+    then(Promise.resolve());
 }
 
 /*
  bootstrap.js API
 */
 
 function startup(aData, aReason)
 {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1196,16 +1196,22 @@
     "kind": "boolean",
     "description": "Whether a HTTP transaction routed via Alt-Svc was scheme=http"
   },
   "HTTP_WAP_CONTENT_TYPE_RECEIVED": {
     "expires_in_version": "40",
     "kind": "boolean",
     "description": "Whether a WAP content type response is served to the browser."
   },
+  "HTTP_SCHEME_UPGRADE": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 10,
+    "description": "Was the URL upgraded to HTTPS?  (0=already HTTPS, 1=no reason to upgrade, 2=STS upgrade blocked by pref, 3=upgraded with STS)"
+  },
   "SSL_HANDSHAKE_VERSION": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 16,
     "description": "SSL Version (1=tls1, 2=tls1.1, 3=tls1.2)"
   },
   "SSL_TIME_UNTIL_READY": {
     "expires_in_version": "never",
--- a/toolkit/content/aboutServiceWorkers.js
+++ b/toolkit/content/aboutServiceWorkers.js
@@ -127,17 +127,17 @@ function display(info) {
     error => {
       dump("about:serviceworkers - push registration failed\n");
     }
   );
 
   let updateButton = document.createElement("button");
   updateButton.appendChild(document.createTextNode(bundle.GetStringFromName('update')));
   updateButton.onclick = function() {
-    gSWM.update(info.scope);
+    gSWM.softUpdate(info.scope);
   };
   div.appendChild(updateButton);
 
   let unregisterButton = document.createElement("button");
   unregisterButton.appendChild(document.createTextNode(bundle.GetStringFromName('unregister')));
   div.appendChild(unregisterButton);
 
   let loadingMessage = document.createElement('span');
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -69,63 +69,73 @@ let WebProgressListener = {
     return {
       webProgress: aWebProgress || null,
       requestURI: this._requestSpec(aRequest, "URI"),
       originalRequestURI: this._requestSpec(aRequest, "originalURI"),
       documentContentType: content.document && content.document.contentType
     };
   },
 
-  _setupObjects: function setupObjects(aWebProgress) {
+  _setupObjects: function setupObjects(aWebProgress, aRequest) {
     let domWindow;
     try {
       domWindow = aWebProgress && aWebProgress.DOMWindow;
     } catch (e) {
       // If nsDocShell::Destroy has already been called, then we'll
       // get NS_NOINTERFACE when trying to get the DOM window. Ignore
       // that here.
       domWindow = null;
     }
 
     return {
       contentWindow: content,
       // DOMWindow is not necessarily the content-window with subframes.
-      DOMWindow: domWindow
+      DOMWindow: domWindow,
+      webProgress: aWebProgress,
+      request: aRequest,
     };
   },
 
+  _send(name, data, objects) {
+    if (RemoteAddonsChild.useSyncWebProgress) {
+      sendRpcMessage(name, data, objects);
+    } else {
+      sendAsyncMessage(name, data, objects);
+    }
+  },
+
   onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress);
+    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.stateFlags = aStateFlags;
     json.status = aStatus;
 
-    sendAsyncMessage("Content:StateChange", json, objects);
+    this._send("Content:StateChange", json, objects);
   },
 
   onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress);
+    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.curSelf = aCurSelf;
     json.maxSelf = aMaxSelf;
     json.curTotal = aCurTotal;
     json.maxTotal = aMaxTotal;
 
-    sendAsyncMessage("Content:ProgressChange", json, objects);
+    this._send("Content:ProgressChange", json, objects);
   },
 
   onProgressChange64: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
     this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
   },
 
   onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress);
+    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.location = aLocationURI ? aLocationURI.spec : "";
     json.flags = aFlags;
 
     // These properties can change even for a sub-frame navigation.
     let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
     json.canGoBack = webNav.canGoBack;
     json.canGoForward = webNav.canGoForward;
@@ -134,37 +144,37 @@ let WebProgressListener = {
       json.documentURI = content.document.documentURIObject.spec;
       json.title = content.document.title;
       json.charset = content.document.characterSet;
       json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
       json.principal = content.document.nodePrincipal;
       json.synthetic = content.document.mozSyntheticDocument;
     }
 
-    sendAsyncMessage("Content:LocationChange", json, objects);
+    this._send("Content:LocationChange", json, objects);
   },
 
   onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress);
+    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.status = aStatus;
     json.message = aMessage;
 
-    sendAsyncMessage("Content:StatusChange", json, objects);
+    this._send("Content:StatusChange", json, objects);
   },
 
   onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
     let json = this._setupJSON(aWebProgress, aRequest);
-    let objects = this._setupObjects(aWebProgress);
+    let objects = this._setupObjects(aWebProgress, aRequest);
 
     json.state = aState;
     json.status = SecurityUI.getSSLStatusAsString();
 
-    sendAsyncMessage("Content:SecurityChange", json, objects);
+    this._send("Content:SecurityChange", json, objects);
   },
 
   onRefreshAttempted: function onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
     return true;
   },
 
   QueryInterface: function QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
--- a/toolkit/modules/RemoteWebProgress.jsm
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -12,30 +12,35 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function newURI(spec)
 {
     return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
                                                     .newURI(spec, null, null);
 }
 
-function RemoteWebProgressRequest(spec, originalSpec)
+function RemoteWebProgressRequest(spec, originalSpec, requestCPOW)
 {
+  this.wrappedJSObject = this;
+
   this._uri = newURI(spec);
   this._originalURI = newURI(originalSpec);
+  this._requestCPOW = requestCPOW;
 }
 
 RemoteWebProgressRequest.prototype = {
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]),
 
   get URI() { return this._uri.clone(); },
   get originalURI() { return this._originalURI.clone(); }
 };
 
 function RemoteWebProgress(aManager, aIsTopLevel) {
+  this.wrappedJSObject = this;
+
   this._manager = aManager;
 
   this._isLoadingDocument = false;
   this._DOMWindow = null;
   this._DOMWindowID = 0;
   this._isTopLevel = aIsTopLevel;
   this._loadType = 0;
 }
@@ -70,16 +75,45 @@ RemoteWebProgress.prototype = {