merge autoland to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 30 May 2017 11:14:12 +0200
changeset 409296 0c712d76d598ec92a7adaaf7180fd4e559f9abc0
parent 409199 34ac1a5d6576d6775491c8a882710a1520551da6 (current diff)
parent 409295 f26f1c5652fca64191701d09fc49adf76e0c49a3 (diff)
child 409337 286f71223256cbb3a769432fd860f563c4886e81
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge autoland to mozilla-central a=merge
browser/themes/windows/caption-buttons.svg
intl/lwbrk/nsISemanticUnitScanner.idl
intl/lwbrk/nsSemanticUnitScanner.cpp
intl/lwbrk/nsSemanticUnitScanner.h
intl/unicharutil/nsCaseConversionImp2.cpp
intl/unicharutil/nsCaseConversionImp2.h
intl/unicharutil/nsCategoryImp.cpp
intl/unicharutil/nsCategoryImp.h
intl/unicharutil/nsICaseConversion.h
intl/unicharutil/nsIUGenCategory.h
intl/unicharutil/nsUnicharUtilCIID.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -116,17 +116,16 @@ devtools/client/storage/test/*.html
 !devtools/client/storage/test/storage-cookies.html
 !devtools/client/storage/test/storage-overflow.html
 !devtools/client/storage/test/storage-search.html
 !devtools/client/storage/test/storage-unsecured-iframe.html
 !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/net/**
 devtools/client/webconsole/test/**
-devtools/client/webconsole/console-output.js
 devtools/client/webconsole/hudservice.js
 devtools/client/webconsole/webconsole-connection-proxy.js
 devtools/client/webconsole/webconsole.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/tests/browser/storage-*.html
 !devtools/server/tests/browser/storage-unsecured-iframe.html
 devtools/server/tests/browser/stylesheets-nested-iframes.html
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1450,16 +1450,18 @@ pref("browser.translation.ui.show", fals
 // Allows to define the translation engine. Bing is default, Yandex may optionally switched on.
 pref("browser.translation.engine", "bing");
 
 // Telemetry settings.
 // Determines if Telemetry pings can be archived locally.
 pref("toolkit.telemetry.archive.enabled", true);
 // Enables sending the shutdown ping when Firefox shuts down.
 pref("toolkit.telemetry.shutdownPingSender.enabled", true);
+// Enables sending the 'new-profile' ping on new profiles.
+pref("toolkit.telemetry.newProfilePing.enabled", true);
 
 // Telemetry experiments settings.
 pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 // Whether experiments are supported by the current application profile.
 pref("experiments.supported", true);
 
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -6,16 +6,23 @@
  * Keeps thumbnails of open web pages up-to-date.
  */
 var gBrowserThumbnails = {
   /**
    * Pref that controls whether we can store SSL content on disk
    */
   PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
 
+  /**
+   * Pref that controls whether activity stream is enabled
+   */
+  PREF_ACTIVITY_STREAM_ENABLED: "browser.newtabpage.activity-stream.enabled",
+
+  _activityStreamEnabled: null,
+
   _captureDelayMS: 1000,
 
   /**
    * Used to keep track of disk_cache_ssl preference
    */
   _sslDiskCacheEnabled: null,
 
   /**
@@ -32,31 +39,36 @@ var gBrowserThumbnails = {
    * List of tab events we want to listen for.
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
     PageThumbs.addExpirationFilter(this);
     gBrowser.addTabsProgressListener(this);
     Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this);
+    Services.prefs.addObserver(this.PREF_ACTIVITY_STREAM_ENABLED, this);
 
     this._sslDiskCacheEnabled =
       Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+    this._activityStreamEnabled =
+      Services.prefs.getBoolPref(this.PREF_ACTIVITY_STREAM_ENABLED);
 
     this._tabEvents.forEach(function(aEvent) {
       gBrowser.tabContainer.addEventListener(aEvent, this);
     }, this);
 
     this._timeouts = new WeakMap();
   },
 
   uninit: function Thumbnails_uninit() {
     PageThumbs.removeExpirationFilter(this);
     gBrowser.removeTabsProgressListener(this);
     Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
+    Services.prefs.removeObserver(this.PREF_ACTIVITY_STREAM_ENABLED, this);
+
     if (this._topSiteURLsRefreshTimer) {
       this._topSiteURLsRefreshTimer.cancel();
       this._topSiteURLsRefreshTimer = null;
     }
 
     this._tabEvents.forEach(function(aEvent) {
       gBrowser.tabContainer.removeEventListener(aEvent, this);
     }, this);
@@ -74,56 +86,71 @@ var gBrowserThumbnails = {
         break;
       case "TabClose": {
         this._clearTimeout(aEvent.target.linkedBrowser);
         break;
       }
     }
   },
 
-  observe: function Thumbnails_observe() {
-    this._sslDiskCacheEnabled =
-      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+  observe: function Thumbnails_observe(subject, topic, data) {
+    switch (data) {
+      case this.PREF_DISK_CACHE_SSL:
+        this._sslDiskCacheEnabled =
+          Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+        break;
+      case this.PREF_ACTIVITY_STREAM_ENABLED:
+        this._activityStreamEnabled =
+          Services.prefs.getBoolPref(this.PREF_ACTIVITY_STREAM_ENABLED);
+        // Get the new top sites
+        XPCOMUtils.defineLazyGetter(this, "_topSiteURLs", getTopSiteURLs);
+        break;
+    }
   },
 
+  /**
+   * clearTopSiteURLCache is only ever called if we've created an nsITimer,
+   * which only happens if we've loaded the tiles top sites. Therefore we only
+   * need to clear the tiles top sites (and not activity stream's top sites)
+   */
   clearTopSiteURLCache: function Thumbnails_clearTopSiteURLCache() {
     if (this._topSiteURLsRefreshTimer) {
       this._topSiteURLsRefreshTimer.cancel();
       this._topSiteURLsRefreshTimer = null;
     }
     // Delete the defined property
     delete this._topSiteURLs;
-    XPCOMUtils.defineLazyGetter(this, "_topSiteURLs",
-                                Thumbnails_getTopSiteURLs);
+    XPCOMUtils.defineLazyGetter(this, "_topSiteURLs", getTopSiteURLs);
   },
 
   notify: function Thumbnails_notify(timer) {
     gBrowserThumbnails._topSiteURLsRefreshTimer = null;
     gBrowserThumbnails.clearTopSiteURLCache();
   },
 
-  filterForThumbnailExpiration:
-  function Thumbnails_filterForThumbnailExpiration(aCallback) {
-    aCallback(this._topSiteURLs);
+  async filterForThumbnailExpiration(aCallback) {
+    const topSites = await this._topSiteURLs;
+    aCallback(topSites);
   },
 
   /**
    * State change progress listener for all tabs.
    */
   onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
                                                    aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
       this._delayedCapture(aBrowser);
   },
 
-  _capture: function Thumbnails_capture(aBrowser) {
+  async _capture(aBrowser) {
     // Only capture about:newtab top sites.
+    const topSites = await this._topSiteURLs;
     if (!aBrowser.currentURI ||
-        this._topSiteURLs.indexOf(aBrowser.currentURI.spec) == -1)
+        topSites.indexOf(aBrowser.currentURI.spec) == -1)
       return;
     this._shouldCapture(aBrowser, function(aResult) {
       if (aResult) {
         PageThumbs.captureAndStoreIfStale(aBrowser);
       }
     });
   },
 
@@ -154,26 +181,30 @@ var gBrowserThumbnails = {
     if (this._timeouts.has(aBrowser)) {
       aBrowser.removeEventListener("scroll", this);
       clearTimeout(this._timeouts.get(aBrowser));
       this._timeouts.delete(aBrowser);
     }
   }
 };
 
-function Thumbnails_getTopSiteURLs() {
-  // The _topSiteURLs getter can be expensive to run, but its return value can
-  // change frequently on new profiles, so as a compromise we cache its return
-  // value as a lazy getter for 1 minute every time it's called.
-  gBrowserThumbnails._topSiteURLsRefreshTimer =
-    Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  gBrowserThumbnails._topSiteURLsRefreshTimer.initWithCallback(gBrowserThumbnails,
-                                                               60 * 1000,
-                                                               Ci.nsITimer.TYPE_ONE_SHOT);
-  return NewTabUtils.links.getLinks().reduce((urls, link) => {
-    if (link)
-      urls.push(link.url);
+async function getTopSiteURLs() {
+  let sites = [];
+  if (gBrowserThumbnails._activityStreamEnabled) {
+    sites = await NewTabUtils.activityStreamLinks.getTopSites();
+  } else {
+    // The _topSiteURLs getter can be expensive to run, but its return value can
+    // change frequently on new profiles, so as a compromise we cache its return
+    // value as a lazy getter for 1 minute every time it's called.
+    gBrowserThumbnails._topSiteURLsRefreshTimer =
+      Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    gBrowserThumbnails._topSiteURLsRefreshTimer.initWithCallback(gBrowserThumbnails,
+                                                                 60 * 1000,
+                                                                 Ci.nsITimer.TYPE_ONE_SHOT);
+    sites = NewTabUtils.links.getLinks();
+  }
+  return sites.reduce((urls, link) => {
+    if (link) urls.push(link.url);
     return urls;
   }, []);
 }
 
-XPCOMUtils.defineLazyGetter(gBrowserThumbnails, "_topSiteURLs",
-                            Thumbnails_getTopSiteURLs);
+XPCOMUtils.defineLazyGetter(gBrowserThumbnails, "_topSiteURLs", getTopSiteURLs);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1705,18 +1705,16 @@ var gBrowserInit = {
     gBrowser.tabContainer.addEventListener("TabSelect", function() {
       for (let panel of document.querySelectorAll("panel[tabspecific='true']")) {
         if (panel.state == "open") {
           panel.hidePopup();
         }
       }
     });
 
-    gPageActionButton.init();
-
     this.delayedStartupFinished = true;
 
     _resolveDelayedStartup();
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
     TelemetryTimestamps.add("delayedStartupFinished");
   },
 
   // Returns the URI(s) to load at startup.
@@ -7737,22 +7735,16 @@ var gPageActionButton = {
     return this.panel = document.getElementById("page-action-panel");
   },
 
   get sendToDeviceBody() {
     delete this.sendToDeviceBody;
     return this.sendToDeviceBody = document.getElementById("page-action-sendToDeviceView-body");
   },
 
-  init() {
-    if (getBoolPref("browser.photon.structure.enabled")) {
-      this.button.hidden = false;
-    }
-  },
-
   onEvent(event) {
     event.stopPropagation();
 
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN)) {
       return; // Left click, space or enter only
     }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -316,16 +316,18 @@
       <toolbarbutton id="sidebar-switcher-tabs"
                      label="&syncedTabs.sidebar.label;"
                      class="subviewbutton subviewbutton-iconic"
                      observes="viewTabsSidebar"
                      oncommand="SidebarUI.show('viewTabsSidebar');">
          <observes element="viewTabsSidebar" attribute="checked"/>
        </toolbarbutton>
       <toolbarseparator/>
+      <vbox id="sidebar-extensions"></vbox>
+      <toolbarseparator/>
       <toolbarbutton id="sidebar-reverse-position"
                      class="subviewbutton"
                      oncommand="SidebarUI.reversePosition()"/>
       <toolbarseparator/>
       <toolbarbutton label="&sidebarMenuClose.label;"
                      class="subviewbutton"
                      oncommand="SidebarUI.hide()"/>
     </panel>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2846,16 +2846,23 @@
                 // cancels the operation.  We are finished here in both cases.
                 this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
                 return false;
               }
 
               newTab = true;
             }
 
+            // Mute audio immediately to improve perceived speed of tab closure.
+            if (aTab.hasAttribute("soundplaying")) {
+              // Don't persist the muted state as this wasn't a user action.
+              // This lets undo-close-tab return it to an unmuted state.
+              aTab.linkedBrowser.mute(true);
+            }
+
             aTab.closing = true;
             this._removingTabs.push(aTab);
             this._visibleTabs = null; // invalidate cache
 
             // Invalidate hovered tab state tracking for this closing tab.
             if (this.tabContainer._hoveredTab == aTab)
               aTab._mouseleave();
 
@@ -6016,44 +6023,62 @@
         <body><![CDATA[
           if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
             this.visible = window.toolbar.visible;
           else
             this.visible = true;
         ]]></body>
       </method>
 
+      <field name="_closeButtonsUpdatePending">false</field>
       <method name="adjustTabstrip">
         <body><![CDATA[
-          // If we're overflowing, tab widths don't change anymore, so we can
-          // return early to avoid flushing layout.
+          if (this._closeButtonsUpdatePending) {
+            return;
+          }
+          this._closeButtonsUpdatePending = true;
+
+          // If we're overflowing, tabs are at their minimum widths.
           if (this.getAttribute("overflow") == "true") {
-            this.setAttribute("closebuttons", "activetab");
+            window.requestAnimationFrame(() => {
+              this._closeButtonsUpdatePending = false;
+              this.setAttribute("closebuttons", "activetab");
+            });
             return;
           }
 
-          let numTabs = this.childNodes.length -
-                        this.tabbrowser._removingTabs.length;
-          if (numTabs > 2) {
-            // This is an optimization to avoid layout flushes by calling
-            // getBoundingClientRect() when we just opened a second tab. In
-            // this case it's highly unlikely that the tab width is smaller
-            // than mTabClipWidth and the tab close button obscures too much
-            // of the tab's label. In the edge case of the window being too
-            // narrow (or if tabClipWidth has been set to a way higher value),
-            // we'll correct the 'closebuttons' attribute after the tabopen
-            // animation has finished.
-
-            let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
-            if (tab && tab.getBoundingClientRect().width <= this.mTabClipWidth) {
-              this.setAttribute("closebuttons", "activetab");
-              return;
-            }
-          }
-          this.removeAttribute("closebuttons");
+          // Wait until after the next paint to get current layout data from
+          // getBoundsWithoutFlushing.
+          window.requestAnimationFrame(() => {
+            window.requestAnimationFrame(() => {
+              this._closeButtonsUpdatePending = false;
+
+              // The scrollbox may have started overflowing since we checked
+              // overflow earlier, so check again.
+              if (this.getAttribute("overflow") == "true") {
+                this.setAttribute("closebuttons", "activetab");
+                return;
+              }
+
+              // Check if tab widths are below the threshold where we want to
+              // remove close buttons from background tabs so that people don't
+              // accidentally close tabs by selecting them.
+              let rect = ele => {
+                return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils)
+                             .getBoundsWithoutFlushing(ele);
+              };
+              let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
+              if (tab && rect(tab).width <= this.mTabClipWidth) {
+                this.setAttribute("closebuttons", "activetab");
+              } else {
+                this.removeAttribute("closebuttons");
+              }
+            });
+          });
         ]]></body>
       </method>
 
       <method name="_handleTabSelect">
         <parameter name="aSmoothScroll"/>
         <body><![CDATA[
           if (this.getAttribute("overflow") == "true")
             this.mTabstrip.ensureElementIsVisible(this.selectedItem, aSmoothScroll);
@@ -7522,41 +7547,44 @@
         <body><![CDATA[
           switch (aEvent.type) {
             case "TabAttrModified":
               this._tabOnAttrModified(aEvent);
               break;
             case "TabClose":
               this._tabOnTabClose(aEvent);
               break;
-            case "scroll":
-              this._updateTabsVisibilityStatus();
-              break;
           }
         ]]></body>
       </method>
 
       <method name="_updateTabsVisibilityStatus">
         <body><![CDATA[
           var tabContainer = gBrowser.tabContainer;
           // We don't want menu item decoration unless there is overflow.
-          if (tabContainer.getAttribute("overflow") != "true")
+          if (tabContainer.getAttribute("overflow") != "true") {
             return;
-
-          var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
-          for (var i = 0; i < this.childNodes.length; i++) {
-            let curTab = this.childNodes[i].tab;
-            if (!curTab) // "Undo close tab", menuseparator, or entries put here by addons.
+          }
+
+          let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIDOMWindowUtils);
+          let tabstripRect = windowUtils.getBoundsWithoutFlushing(tabContainer.mTabstrip);
+          for (let menuitem of this.childNodes) {
+            let curTab = menuitem.tab;
+            if (!curTab) {
+              // "Undo close tab", menuseparator, or entries put here by addons.
               continue;
-            let curTabBO = curTab.boxObject;
-            if (curTabBO.screenX >= tabstripBO.screenX &&
-                curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
-              this.childNodes[i].setAttribute("tabIsVisible", "true");
-            else
-              this.childNodes[i].removeAttribute("tabIsVisible");
+            }
+            let curTabRect = windowUtils.getBoundsWithoutFlushing(curTab);
+            if (curTabRect.left >= tabstripRect.left &&
+                curTabRect.right <= tabstripRect.right) {
+              menuitem.setAttribute("tabIsVisible", "true");
+            } else {
+              menuitem.removeAttribute("tabIsVisible");
+            }
           }
         ]]></body>
       </method>
 
       <method name="_createTabMenuItem">
         <parameter name="aTab"/>
         <body><![CDATA[
           var menuItem = document.createElementNS(
@@ -7645,17 +7673,16 @@
           document.getElementById("alltabs_undoCloseTab").disabled =
             SessionStore.getClosedTabCount(window) == 0;
 
           var tabcontainer = gBrowser.tabContainer;
 
           // Listen for changes in the tab bar.
           tabcontainer.addEventListener("TabAttrModified", this);
           tabcontainer.addEventListener("TabClose", this);
-          tabcontainer.mTabstrip.addEventListener("scroll", this);
 
           let tabs = gBrowser.visibleTabs;
           for (var i = 0; i < tabs.length; i++) {
             if (!tabs[i].pinned)
               this._createTabMenuItem(tabs[i]);
           }
           this._updateTabsVisibilityStatus();
         }
@@ -7674,17 +7701,16 @@
             menuItem.tab.mCorrespondingMenuitem = null;
             this.removeChild(menuItem);
           }
           if (menuItem.hasAttribute("usercontextid")) {
             this.removeChild(menuItem);
           }
         }
         var tabcontainer = gBrowser.tabContainer;
-        tabcontainer.mTabstrip.removeEventListener("scroll", this);
         tabcontainer.removeEventListener("TabAttrModified", this);
         tabcontainer.removeEventListener("TabClose", this);
       ]]></handler>
 
       <handler event="DOMMenuItemActive">
       <![CDATA[
         var tab = event.target.tab;
         if (tab) {
@@ -7732,19 +7758,30 @@
 
       <property name="label">
         <setter><![CDATA[
           if (!this.label) {
             this.removeAttribute("mirror");
             this.removeAttribute("sizelimit");
           }
 
-          this.style.minWidth = this.getAttribute("type") == "status" &&
-                                this.getAttribute("previoustype") == "status"
-                                  ? getComputedStyle(this).width : "";
+          if (this.getAttribute("type") == "status" &&
+              this.getAttribute("previoustype") == "status") {
+            // Before updating the label, set the panel's current width as its
+            // min-width to let the panel grow but not shrink and prevent
+            // unnecessary flicker while loading pages. We only care about the
+            // panel's width once it has been painted, so we can do this
+            // without flushing layout.
+            this.style.minWidth =
+              window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindowUtils)
+                    .getBoundsWithoutFlushing(this).width + "px";
+          } else {
+            this.style.minWidth = "";
+          }
 
           if (val) {
             this.setAttribute("label", val);
             this.removeAttribute("inactive");
             this._mouseTargetRect = null;
             MousePosTracker.addListener(this);
           } else {
             this.setAttribute("inactive", "true");
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -89,16 +89,17 @@ tags = blocklist
 [browser_pluginnotification.js]
 tags = blocklist
 [browser_plugin_reloading.js]
 tags = blocklist
 [browser_blocklist_content.js]
 skip-if = !e10s
 tags = blocklist
 [browser_enable_DRM_prompt.js]
+[browser_private_browsing_eme_persistent_state.js]
 [browser_globalplugin_crashinfobar.js]
 skip-if = !crashreporter
 [browser_pluginCrashCommentAndURL.js]
 skip-if = !crashreporter
 [browser_pageInfo_plugins.js]
 [browser_pluginCrashReportNonDeterminism.js]
 skip-if = !crashreporter || os == 'linux' # Bug 1152811
 [browser_private_clicktoplay.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js
@@ -0,0 +1,44 @@
+/* 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/. */
+
+/*
+ * This test ensures that navigator.requestMediaKeySystemAccess() requests
+ * to run EME with persistent state are rejected in private browsing windows.
+ * Bug 1334111.
+ */
+
+const TEST_URL =
+  getRootDirectory(gTestPath).replace("chrome://mochitests/content",
+  "https://example.com") + "empty_file.html";
+
+async function isEmePersistentStateSupported(mode) {
+  let win = await BrowserTestUtils.openNewBrowserWindow(mode);
+  let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+  let persistentStateSupported = await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+    try {
+      let config = [{
+        initDataTypes: ["webm"],
+        videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}],
+        persistentState: "required",
+      }];
+      await content.navigator.requestMediaKeySystemAccess("org.w3.clearkey", config);
+    } catch (ex) {
+      return false;
+    }
+    return true;
+  });
+
+  await BrowserTestUtils.closeWindow(win);
+
+  return persistentStateSupported;
+}
+
+add_task(async function test() {
+  is(await isEmePersistentStateSupported({private: true}),
+     false,
+     "EME persistentState should *NOT* be supported in private browsing window.");
+  is(await isEmePersistentStateSupported({private: false}),
+     true,
+     "EME persistentState *SHOULD* be supported in non private browsing window.");
+});
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -719,18 +719,23 @@ function openPreferences(paneID, extraAr
   let histogram = Services.telemetry.getHistogramById("FX_PREFERENCES_OPENED_VIA");
   if (extraArgs && extraArgs.origin) {
     histogram.add(extraArgs.origin);
   } else {
     histogram.add("other");
   }
   function switchToAdvancedSubPane(doc) {
     if (extraArgs && extraArgs["advancedTab"]) {
+      // After the Preferences reorg works in Bug 1335907, no more advancedPrefs element.
+      // The old Preference is pref-off behind `browser.preferences.useOldOrganization` on Nightly.
+      // During the transition between the old and new Preferences, should do checking before proceeding.
       let advancedPaneTabs = doc.getElementById("advancedPrefs");
-      advancedPaneTabs.selectedTab = doc.getElementById(extraArgs["advancedTab"]);
+      if (advancedPaneTabs) {
+        advancedPaneTabs.selectedTab = doc.getElementById(extraArgs["advancedTab"]);
+      }
     }
   }
 
   // This function is duplicated from preferences.js.
   function internalPrefCategoryNameToFriendlyName(aName) {
     return (aName || "").replace(/^pane./, function(toReplace) { return toReplace[4].toLowerCase(); });
   }
 
--- a/browser/base/content/webext-panels.xul
+++ b/browser/base/content/webext-panels.xul
@@ -1,16 +1,17 @@
 <?xml version="1.0"?>
 
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 <!DOCTYPE page [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 <!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
 %textcontextDTD;
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -37,16 +37,17 @@ this.sidebarAction = class extends Exten
 
     let options = extension.manifest.sidebar_action;
 
     // Add the extension to the sidebar menu.  The sidebar widget will copy
     // from that when it is viewed, so we shouldn't need to update that.
     let widgetId = makeWidgetId(extension.id);
     this.id = `${widgetId}-sidebar-action`;
     this.menuId = `menu_${this.id}`;
+    this.buttonId = `button_${this.id}`;
 
     // We default browser_style to true because this is a new API and
     // we therefore don't need to worry about breaking existing add-ons.
     this.browserStyle = options.browser_style || options.browser_style === null;
 
     this.defaults = {
       enabled: true,
       title: options.default_title || extension.name,
@@ -85,16 +86,20 @@ this.sidebarAction = class extends Exten
       let {document, SidebarUI} = window;
       if (SidebarUI.currentID === this.id) {
         SidebarUI.hide();
       }
       let menu = document.getElementById(this.menuId);
       if (menu) {
         menu.remove();
       }
+      let button = document.getElementById(this.buttonId);
+      if (button) {
+        button.remove();
+      }
       let broadcaster = document.getElementById(this.id);
       if (broadcaster) {
         broadcaster.remove();
       }
     }
     windowTracker.removeOpenListener(this.windowOpenListener);
   }
 
@@ -147,27 +152,35 @@ this.sidebarAction = class extends Exten
     broadcaster.setAttribute("autoCheck", "false");
     broadcaster.setAttribute("type", "checkbox");
     broadcaster.setAttribute("group", "sidebar");
     broadcaster.setAttribute("label", details.title);
     broadcaster.setAttribute("sidebarurl", this.sidebarUrl(details.panel));
 
     // oncommand gets attached to menuitem, so we use the observes attribute to
     // get the command id we pass to SidebarUI.
-    broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");
+    broadcaster.setAttribute("oncommand", "SidebarUI.show(this.getAttribute('observes'))");
 
+    // Insert a menuitem for View->Show Sidebars.
     let menuitem = document.createElementNS(XUL_NS, "menuitem");
     menuitem.setAttribute("id", this.menuId);
     menuitem.setAttribute("observes", this.id);
     menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
+    this.setMenuIcon(menuitem, details);
 
-    this.setMenuIcon(menuitem, details);
+    // Insert a toolbarbutton for the sidebar dropdown selector.
+    let toolbarbutton = document.createElementNS(XUL_NS, "toolbarbutton");
+    toolbarbutton.setAttribute("id", this.buttonId);
+    toolbarbutton.setAttribute("observes", this.id);
+    toolbarbutton.setAttribute("class", "subviewbutton subviewbutton-iconic webextension-menuitem");
+    this.setMenuIcon(toolbarbutton, details);
 
     document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
     document.getElementById("viewSidebarMenu").appendChild(menuitem);
+    document.getElementById("sidebar-extensions").appendChild(toolbarbutton);
 
     return menuitem;
   }
 
   setMenuIcon(menuitem, details) {
     let getIcon = size => IconDetails.escapeUrl(
       IconDetails.getPreferredIcon(details.icon, this.extension, size).icon);
 
@@ -202,16 +215,19 @@ this.sidebarAction = class extends Exten
     let url = this.sidebarUrl(tabData.panel);
     let urlChanged = url !== broadcaster.getAttribute("sidebarurl");
     if (urlChanged) {
       broadcaster.setAttribute("sidebarurl", url);
     }
 
     this.setMenuIcon(menu, tabData);
 
+    let button = document.getElementById(this.buttonId);
+    this.setMenuIcon(button, tabData);
+
     // Update the sidebar if this extension is the current sidebar.
     if (SidebarUI.currentID === this.id) {
       SidebarUI.title = title;
       if (SidebarUI.isOpen && urlChanged) {
         SidebarUI.show(this.id);
       }
     }
   }
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
@@ -33,31 +33,33 @@ let extData = {
 add_task(async function sidebar_windows() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   await extension.startup();
   // Test sidebar is opened on install
   await extension.awaitMessage("sidebar");
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
   // Check that the menuitem has our image styling.
   let elements = document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
+  // ui is in flux, at time of writing we potentially have 3 menuitems, later
+  // it may be two or one, just make sure one is there.
+  ok(elements.length > 0, "have a menuitem");
   let style = elements[0].getAttribute("style");
   ok(style.includes("webextension-menuitem-image"), "this menu has style");
 
   let secondSidebar = extension.awaitMessage("sidebar");
 
   // SidebarUI relies on window.opener being set, which is normal behavior when
   // using menu or key commands to open a new browser window.
   let win = await BrowserTestUtils.openNewBrowserWindow({opener: window});
 
   await secondSidebar;
   ok(!win.document.getElementById("sidebar-box").hidden, "sidebar box is visible in second window");
   // Check that the menuitem has our image styling.
   elements = win.document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
+  ok(elements.length > 0, "have a menuitem");
   style = elements[0].getAttribute("style");
   ok(style.includes("webextension-menuitem-image"), "this menu has style");
 
   await extension.unload();
   await BrowserTestUtils.closeWindow(win);
 
   // Move toolbar button back to customization.
   CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
--- a/browser/components/preferences/in-content-old/advanced.js
+++ b/browser/components/preferences/in-content-old/advanced.js
@@ -253,26 +253,18 @@ var gAdvancedPane = {
    */
   writeEnableOCSP() {
     var checkbox = document.getElementById("enableOCSP");
     var defaults = Services.prefs.getDefaultBranch(null);
     var defaultValue = defaults.getIntPref("security.OCSP.enabled");
     return checkbox.checked ? defaultValue : 0;
   },
 
-  /**
-   * When the user toggles the layers.acceleration.disabled pref,
-   * sync its new value to the gfx.direct2d.disabled pref too.
-   */
   updateHardwareAcceleration() {
-    if (AppConstants.platform == "win") {
-      var fromPref = document.getElementById("layers.acceleration.disabled");
-      var toPref = document.getElementById("gfx.direct2d.disabled");
-      toPref.value = fromPref.value;
-    }
+    // Placeholder for restart
   },
 
   // DATA CHOICES TAB
 
   /**
    * Set up or hide the Learn More links for various data collection options
    */
   _setupLearnMoreLink(pref, element) {
--- a/browser/components/preferences/in-content-old/advanced.xul
+++ b/browser/components/preferences/in-content-old/advanced.xul
@@ -29,22 +29,16 @@
 #endif
 
   <preference id="general.autoScroll"
               name="general.autoScroll"
               type="bool"/>
   <preference id="general.smoothScroll"
               name="general.smoothScroll"
               type="bool"/>
-#ifdef XP_WIN
-  <preference id="gfx.direct2d.disabled"
-              name="gfx.direct2d.disabled"
-              type="bool"
-              inverted="true"/>
-#endif
   <preference id="layout.spellcheckDefault"
               name="layout.spellcheckDefault"
               type="int"/>
 
 #ifdef MOZ_TELEMETRY_REPORTING
   <preference id="toolkit.telemetry.enabled"
               name="toolkit.telemetry.enabled"
               type="bool"/>
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -648,26 +648,18 @@ var gMainPane = {
     if (AppConstants.platform == "win") {
       let minVersion = Services.prefs.getBoolPref("ui.osk.require_win10") ? 10 : 6.2;
       if (Services.vc.compare(Services.sysinfo.getProperty("version"), minVersion) >= 0) {
         document.getElementById("useOnScreenKeyboard").hidden = false;
       }
     }
   },
 
-  /**
-   * When the user toggles the layers.acceleration.disabled pref,
-   * sync its new value to the gfx.direct2d.disabled pref too.
-   */
   updateHardwareAcceleration() {
-    if (AppConstants.platform == "win") {
-      var fromPref = document.getElementById("layers.acceleration.disabled");
-      var toPref = document.getElementById("gfx.direct2d.disabled");
-      toPref.value = fromPref.value;
-    }
+    // Placeholder for restart on change
   },
 
   // FONTS
 
   /**
    * Populates the default font list in UI.
    */
   _rebuildFonts() {
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -174,22 +174,16 @@
      - set to true to enable finer page scrolling than line-by-line on page-up,
        page-down, and other such page movements -->
   <preference id="general.autoScroll"
               name="general.autoScroll"
               type="bool"/>
   <preference id="general.smoothScroll"
               name="general.smoothScroll"
               type="bool"/>
-#ifdef XP_WIN
-  <preference id="gfx.direct2d.disabled"
-              name="gfx.direct2d.disabled"
-              type="bool"
-              inverted="true"/>
-#endif
   <preference id="layout.spellcheckDefault"
               name="layout.spellcheckDefault"
               type="int"/>
 
 #ifdef MOZ_TELEMETRY_REPORTING
   <preference id="toolkit.telemetry.enabled"
               name="toolkit.telemetry.enabled"
               type="bool"/>
--- a/browser/components/preferences/in-content/tests/browser_layersacceleration.js
+++ b/browser/components/preferences/in-content/tests/browser_layersacceleration.js
@@ -8,23 +8,15 @@ add_task(async function() {
   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
 
   let doc = gBrowser.contentDocument;
   let checkbox = doc.querySelector("#allowHWAccel");
   is(!checkbox.checked,
      Services.prefs.getBoolPref("layers.acceleration.disabled"),
      "checkbox should represent inverted pref value before clicking on checkbox");
 
-  if (AppConstants.platform == "win") {
-    is(Services.prefs.getBoolPref("gfx.direct2d.disabled"), false, "direct2d pref should be set to false");
-  }
-
   checkbox.click();
 
   is(!checkbox.checked,
      Services.prefs.getBoolPref("layers.acceleration.disabled"),
      "checkbox should represent inverted pref value after clicking on checkbox");
-  if (AppConstants.platform == "win") {
-    is(Services.prefs.getBoolPref("gfx.direct2d.disabled"), true, "direct2d pref should be set to true");
-  }
-
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -176,18 +176,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "GlobalState",
   "resource:///modules/sessionstore/GlobalState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
   "resource:///modules/sessionstore/PrivacyFilter.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RunState",
   "resource:///modules/sessionstore/RunState.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
-  "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DevToolsShim",
+  "chrome://devtools-shim/content/DevToolsShim.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
   "resource:///modules/sessionstore/SessionSaver.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionCookies",
   "resource:///modules/sessionstore/SessionCookies.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
   "resource://gre/modules/Timer.jsm");
@@ -2752,17 +2752,17 @@ var SessionStoreInternal = {
     // Merge closed windows from this session with ones from last session
     if (lastSessionState._closedWindows) {
       this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
       this._capClosedWindows();
       this._closedObjectsChanged = true;
     }
 
     if (lastSessionState.scratchpads) {
-      ScratchpadManager.restoreSession(lastSessionState.scratchpads);
+      DevToolsShim.restoreScratchpadSession(lastSessionState.scratchpads);
     }
 
     // Set data that persists between sessions
     this._recentCrashes = lastSessionState.session &&
                           lastSessionState.session.recentCrashes || 0;
 
     // Update the session start time using the restored session state.
     this._updateSessionStartTime(lastSessionState);
@@ -3120,22 +3120,19 @@ var SessionStoreInternal = {
       _closedWindows: lastClosedWindowsCopy,
       session,
       global: this._globalState.getState()
     };
 
     // Collect and store session cookies.
     state.cookies = SessionCookies.collect();
 
-    if (Cu.isModuleLoaded("resource://devtools/client/scratchpad/scratchpad-manager.jsm")) {
-      // get open Scratchpad window states too
-      let scratchpads = ScratchpadManager.getSessionState();
-      if (scratchpads && scratchpads.length) {
-        state.scratchpads = scratchpads;
-      }
+    let scratchpads = DevToolsShim.getOpenedScratchpads();
+    if (scratchpads && scratchpads.length) {
+      state.scratchpads = scratchpads;
     }
 
     // Persist the last session if we deferred restoring it
     if (LastSession.canRestore) {
       state.lastSessionState = LastSession.getState();
     }
 
     // If we were called by the SessionSaver and started with only a private
@@ -3467,17 +3464,17 @@ var SessionStoreInternal = {
           this.windowToFocus = window;
         }
       }
     }
 
     this.restoreWindow(aWindow, root.windows[0], aOptions);
 
     if (aState.scratchpads) {
-      ScratchpadManager.restoreSession(aState.scratchpads);
+      DevToolsShim.restoreScratchpadSession(aState.scratchpads);
     }
   },
 
   /**
    * Manage history restoration for a window
    * @param aWindow
    *        Window to restore the tabs into
    * @param aTabs
--- a/browser/components/uitour/UITour-lib.js
+++ b/browser/components/uitour/UITour-lib.js
@@ -37,18 +37,18 @@ if (typeof Mozilla == "undefined") {
       themeIntervalId = null;
     }
   }
 
   function _sendEvent(action, data) {
     var event = new CustomEvent("mozUITour", {
       bubbles: true,
       detail: {
-	action,
-	data: data || {}
+        action,
+        data: data || {}
       }
     });
 
     document.dispatchEvent(event);
   }
 
   function _generateCallbackID() {
     return Math.random().toString(36).replace(/[^a-z]+/g, "");
@@ -741,26 +741,44 @@ if (typeof Mozilla == "undefined") {
    * UITour APIs.
    */
   Mozilla.UITour.toggleReaderMode = function() {
     _sendEvent("toggleReaderMode");
   };
 
   /**
    * @param {String} pane - Pane to open/switch the preferences to.
-   * Valid values match fragments on about:preferences and are subject to change e.g.:<ul>
+   * Valid values match fragments on about:preferences and are subject to change e.g.:
+   * <ul>
+   * For the old Preferences
    * <li>general
    * <li>search
    * <li>content
    * <li>applications
    * <li>privacy
    * <li>security
    * <li>sync
    * <li>advanced
    * </ul>
+   *
+   * <ul>
+   * For the new Preferences
+   * <li>general
+   * <li>applications
+   * <li>sync
+   * <li>privacy
+   * <li>advanced
+   * </ul>
+   *
+   * The mapping between the old and the new Preferences:
+   * To open to the options of sending telemetry, health report, crach reports,
+   * that is, the advanced pane > dataChoicesTab on the old and the privcacy pane > reports on the new.
+   * Please call `Mozilla.UITour.openPreferences("privacy-reports")`.
+   * UITour would do route mapping automatically.
+   *
    * @since 42
    */
   Mozilla.UITour.openPreferences = function(pane) {
     _sendEvent("openPreferences", {
       pane
     });
   };
 
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -531,17 +531,31 @@ this.UITour = {
       }
 
       case "openPreferences": {
         if (typeof data.pane != "string" && typeof data.pane != "undefined") {
           log.warn("openPreferences: Invalid pane specified");
           return false;
         }
 
-        window.openPreferences(data.pane, { origin: "UITour" });
+        let paneID = data.pane;
+        let extraArgs = { origin: "UITour" };
+        if (Services.prefs.getBoolPref("browser.preferences.useOldOrganization", true)) {
+          // We are heading to the old Preferences so
+          // let's map the new one to the old one if the `paneID` was for the new Preferences.
+          // Currently only the old advanced pane > dataChoicesTab has the mapping need,
+          // so here only do mapping for it right now.
+          // We could add another mapping when there is need.
+          if (paneID == "privacy-reports") {
+            paneID = "advanced";
+            extraArgs.advancedTab = "dataChoicesTab";
+          }
+        }
+
+        window.openPreferences(paneID, extraArgs);
         break;
       }
 
       case "showFirefoxAccounts": {
         // 'signup' is the only action that makes sense currently, so we don't
         // accept arbitrary actions just to be safe...
         let p = new URLSearchParams("action=signup&entrypoint=uitour");
         // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
--- a/browser/components/uitour/test/browser_openPreferences.js
+++ b/browser/components/uitour/test/browser_openPreferences.js
@@ -29,8 +29,40 @@ add_UITour_task(async function test_open
 });
 
 add_UITour_task(async function test_openPrivacyPreferences() {
   let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
   await gContentAPI.openPreferences("privacy");
   let tab = await promiseTabOpened;
   await BrowserTestUtils.removeTab(tab);
 });
+
+add_UITour_task(async function test_openOldDataChoicesTab() {
+  if (!AppConstants.MOZ_DATA_REPORTING) {
+    return;
+  }
+  await SpecialPowers.pushPrefEnv({set: [["browser.preferences.useOldOrganization", true]]});
+  let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#advanced");
+  await gContentAPI.openPreferences("privacy-reports");
+  let tab = await promiseTabOpened;
+  await BrowserTestUtils.waitForEvent(gBrowser.selectedBrowser, "Initialized");
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let selectedTab = doc.getElementById("advancedPrefs").selectedTab;
+  is(selectedTab.id, "dataChoicesTab", "Should open to the dataChoicesTab in the old Preferences");
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_UITour_task(async function test_openPrivacyReports() {
+  if (!AppConstants.MOZ_TELEMETRY_REPORTING &&
+      !(AppConstants.MOZ_DATA_REPORTING && AppConstants.MOZ_CRASHREPORTER)) {
+    return;
+  }
+  await SpecialPowers.pushPrefEnv({set: [["browser.preferences.useOldOrganization", false]]});
+  let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy-reports");
+  await gContentAPI.openPreferences("privacy-reports");
+  let tab = await promiseTabOpened;
+  await BrowserTestUtils.waitForEvent(gBrowser.selectedBrowser, "Initialized");
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let reports = doc.querySelector("groupbox[data-subcategory='reports']");
+  is(doc.location.hash, "#privacy", "Should not display the reports subcategory in the location hash.");
+  is(reports.hidden, false, "Should open to the reports subcategory in the privacy pane in the new Preferences.");
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -254,17 +254,16 @@
 @RESPATH@/browser/components/jsinspector.xpt
 @RESPATH@/components/layout_base.xpt
 #ifdef NS_PRINTING
 @RESPATH@/components/layout_printing.xpt
 #endif
 @RESPATH@/components/layout_xul_tree.xpt
 @RESPATH@/components/layout_xul.xpt
 @RESPATH@/components/locale.xpt
-@RESPATH@/components/lwbrk.xpt
 #ifdef MOZ_GECKO_PROFILER
 @RESPATH@/components/memory_profiler.xpt
 #endif
 @RESPATH@/browser/components/migration.xpt
 @RESPATH@/components/mimetype.xpt
 @RESPATH@/components/mozfind.xpt
 #ifdef ENABLE_INTL_API
 @RESPATH@/components/mozintl.xpt
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -254,13 +254,14 @@ removeContainerMsg=If you remove this Co
 
 removeContainerOkButton=Remove this Container
 removeContainerButton2=Don’t remove this Container
 
 # Search Results Pane
 # LOCALIZATION NOTE %S will be replaced by the word being searched
 searchResults.sorryMessageWin=Sorry! There are no results in Options for “%S”.
 searchResults.sorryMessageUnix=Sorry! There are no results in Preferences for “%S”.
-# LOCALIZATION NOTE %S gets replaced with the browser name
-searchResults.needHelp2=Need help? Visit <html:a id="need-help-link" target="_blank" href="%S">%S Support</html:a>
+# LOCALIZATION NOTE (searchResults.needHelp2): %1$S is a link to SUMO, %2$S is
+# the browser name
+searchResults.needHelp2=Need help? Visit <html:a id="need-help-link" target="_blank" href="%1$S">%2$S Support</html:a>
 
 # LOCALIZATION NOTE %S is the default value of the `dom.ipc.processCount` pref.
 defaultContentProcessCount=%S (default)
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -18,27 +18,27 @@
 :root {
 %ifdef MOZ_PHOTON_THEME
   --toolbarbutton-border-radius: 2px;
 %else
   --toolbarbutton-border-radius: 1px;
 
   --backbutton-background: rgba(255,255,255,.15);
   --backbutton-border-color: var(--urlbar-border-color-hover);
-%endif
-
-  --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px);
 
   --toolbarbutton-hover-background: rgba(255,255,255,.5) linear-gradient(rgba(255,255,255,.5), transparent);
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.25);
   --toolbarbutton-hover-boxshadow: none;
 
   --toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.4));
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 1px 1px rgba(0,0,0,.1) inset, 0 0 1px rgba(0,0,0,.3) inset;
+%endif
+
+  --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px);
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(200,200,200,.5);
 
   --panel-separator-color: ThreeDShadow;
   --arrowpanel-dimmed: hsla(0,0%,80%,.3);
   --arrowpanel-dimmed-further: hsla(0,0%,80%,.45);
   --arrowpanel-dimmed-even-further: hsla(0,0%,80%,.8);
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -26,26 +26,26 @@
   --toolbarbutton-border-radius: 3px;
 
   --toolbarbutton-hover-background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
   --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
 
   --backbutton-border-color: rgba(0,0,0,0.2);
   --backbutton-background: linear-gradient(rgba(255,255,255,0.9),
                                            rgba(255,255,255,0.7)) repeat-x;
-%endif
 
   --toolbarbutton-hover-bordercolor: hsla(0,0%,0%,.2);
   --toolbarbutton-hover-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                    0 1px 0 hsla(0,0%,100%,.5) inset;
 
   --toolbarbutton-active-bordercolor: hsla(0,0%,0%,.3);
   --toolbarbutton-active-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                     0 1px 0 hsla(0,0%,0%,.05) inset,
                                     0 1px 1px hsla(0,0%,0%,.2) inset;
+%endif
 
   --toolbarbutton-checkedhover-backgroundcolor: hsla(0,0%,0%,.09);
 
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0, 11px, 14px, 0);
   --urlbar-dropmarker-active-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
--- a/browser/themes/shared/sidebar.inc.css
+++ b/browser/themes/shared/sidebar.inc.css
@@ -84,16 +84,20 @@
 #sidebar-close:hover {
   background: var(--header-background-color-hover);
 }
 
 #sidebarMenu-popup .subviewbutton {
   min-width: 190px;
 }
 
+#sidebar-extensions:empty + toolbarseparator {
+  display: none;
+}
+
 %ifndef XP_MACOSX
 /* Allow room for the checkbox drawn as a background image at the start of the toolbarbutton */
 #sidebarMenu-popup .subviewbutton-iconic > .toolbarbutton-icon {
   padding-inline-start: 16px;
 }
 #sidebarMenu-popup .subviewbutton-iconic > .toolbarbutton-text {
   padding-inline-start: 0;
 }
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -33,23 +33,22 @@
 :root[uidensity=touch] {
   --toolbarbutton-inner-padding: 9px;
 }
 
 toolbar:-moz-lwtheme {
 %ifndef MOZ_PHOTON_THEME
   --toolbarbutton-hover-background: rgba(255,255,255,.25);
   --toolbarbutton-active-background: rgba(70%,70%,70%,.25);
-%endif
 
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
 
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 0 2px rgba(0,0,0,.6) inset;
-
+%endif
   --toolbarbutton-checkedhover-backgroundcolor: rgba(85%,85%,85%,.25);
 }
 
 %ifdef MOZ_PHOTON_THEME
 toolbar[brighttext] {
   --backbutton-background: hsla(240,5%,5%,.1);
 }
 %endif
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -45,87 +45,97 @@
    * the titlebar */
   #browser {
     -moz-appearance: -moz-win-exclude-glass;
   }
 
   @media not all and (-moz-os-version: windows-win7) {
     @media not all and (-moz-os-version: windows-win8) {
       @media (-moz-windows-default-theme) {
-        #main-window {
+        :root:not(:-moz-lwtheme) {
+%ifdef MOZ_PHOTON_THEME
+          background-color: hsl(235,33%,19%);
+          --titlebar-text-color: hsl(240,9%,98%);
+%else
           background-color: hsl(0, 0%, 78%);
+%endif
         }
 
         :root[tabsintitlebar] .tab-label:-moz-window-inactive {
           /* Calculated to match the opacity change of Windows Explorer
              titlebar text change for inactive windows. */
           opacity: .6;
         }
       }
 
       @media (-moz-windows-default-theme: 0) {
-        #main-window {
+        :root {
           background-color: transparent;
         }
       }
 
       #titlebar-buttonbox,
       .titlebar-button {
         -moz-appearance: none !important;
       }
 
       .titlebar-button {
         border: none;
         margin: 0 !important;
         padding: 10px 17px;
+        -moz-context-properties: stroke;
+%ifdef MOZ_PHOTON_THEME
+        stroke: white;
+%else
+        stroke: black;
+%endif
       }
 
-      #main-window[sizemode=maximized] .titlebar-button {
+      :root[sizemode=maximized] .titlebar-button {
         padding-top: 8px;
         padding-bottom: 8px;
       }
 
       .titlebar-button > .toolbarbutton-icon {
         width: 12px;
         height: 12px;
       }
 
       #titlebar-min {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
+        list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
       }
 
       #titlebar-max {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize);
+        list-style-image: url(chrome://browser/skin/window-controls/maximize.svg);
       }
 
-      #main-window[sizemode="maximized"] #titlebar-max {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+      :root[sizemode="maximized"] #titlebar-max {
+        list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
       }
 
       #titlebar-close {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
-      }
-      #titlebar-close:hover {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+        list-style-image: url(chrome://browser/skin/window-controls/close.svg);
       }
 
+      .titlebar-button:-moz-lwtheme {
+        -moz-context-properties: unset;
+      }
       #titlebar-min:-moz-lwtheme {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-themes);
+        list-style-image: url(chrome://browser/skin/window-controls/minimize-themes.svg);
       }
       #titlebar-max:-moz-lwtheme {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-themes);
+        list-style-image: url(chrome://browser/skin/window-controls/maximize-themes.svg);
       }
-      #main-window[sizemode="maximized"] #titlebar-max:-moz-lwtheme {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-themes);
+      :root[sizemode="maximized"] #titlebar-max:-moz-lwtheme {
+        list-style-image: url(chrome://browser/skin/window-controls/restore-themes.svg);
       }
       #titlebar-close:-moz-lwtheme {
-        list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-themes);
+        list-style-image: url(chrome://browser/skin/window-controls/close-themes.svg);
       }
 
-
       /* the 12px image renders a 10px icon, and the 10px upscaled gets rounded to 12.5, which
        * rounds up to 13px, which makes the icon one pixel too big on 1.25dppx. Fix: */
       @media (min-resolution: 1.20dppx) and (max-resolution: 1.45dppx) {
         .titlebar-button > .toolbarbutton-icon {
           width: 11.5px;
           height: 11.5px;
         }
       }
@@ -163,70 +173,82 @@
          * impossible to know by how much. */
         .titlebar-button > .toolbarbutton-icon {
           width: 10.8px;
           height: 10.8px;
         }
       }
 
       @media (-moz-windows-default-theme) {
+%ifdef MOZ_PHOTON_THEME
         .titlebar-button:hover {
-          background-color: hsla(0, 0%, 0%, .12);
+          background-color: hsla(0,0%,100%,.12);
+        }
+        .titlebar-button:hover:active {
+          background-color: hsla(0,0%,100%,.22);
+        }
+        .titlebar-button:-moz-lwtheme-darktext:hover {
+          background-color: hsla(0,0%,0%,.12);
+        }
+        .titlebar-button:-moz-lwtheme-darktext:hover:active {
+          background-color: hsla(0,0%,0%,.22);
         }
-
+%else
+        .titlebar-button:hover {
+          background-color: hsla(0,0%,0%,.12);
+        }
         .titlebar-button:hover:active {
-          background-color: hsla(0, 0%, 0%, .22);
+          background-color: hsla(0,0%,0%,.22);
+        }
+        .titlebar-button:-moz-lwtheme-brighttext:hover {
+          background-color: hsla(0,0%,100%,.12);
         }
+        .titlebar-button:-moz-lwtheme-brighttext:hover:active {
+          background-color: hsla(0,0%,100%,.22);
+        }
+%endif
 
         .titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
           opacity: 0.5;
         }
 
         #titlebar-close:hover {
-          background-color: hsl(355, 86%, 49%);
+          stroke: white;
+          background-color: hsl(355,86%,49%);
         }
 
         #titlebar-close:hover:active {
-          background-color: hsl(355, 82%, 69%);
+          background-color: hsl(355,82%,69%);
         }
       }
+
       @media (-moz-windows-default-theme: 0) {
         .titlebar-button {
           background-color: -moz-field;
+          stroke: ButtonText;
         }
         .titlebar-button:hover {
           background-color: Highlight;
+          stroke: HighlightText;
         }
 
         #titlebar-min {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast);
-        }
-        #titlebar-min:hover {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast-hover);
+          list-style-image: url(chrome://browser/skin/window-controls/minimize-highcontrast.svg);
         }
 
         #titlebar-max {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast);
-        }
-        #titlebar-max:hover {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast-hover);
+          list-style-image: url(chrome://browser/skin/window-controls/maximize-highcontrast.svg);
         }
 
-        #main-window[sizemode="maximized"] #titlebar-max {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast);
-        }
-        #main-window[sizemode="maximized"] #titlebar-max:hover {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast-hover);
+        :root[sizemode="maximized"] #titlebar-max {
+          list-style-image: url(chrome://browser/skin/window-controls/restore-highcontrast.svg);
         }
 
         #titlebar-close {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast);
-        }
-        #titlebar-close:hover {
-          list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast-hover);
+          list-style-image: url(chrome://browser/skin/window-controls/close-highcontrast.svg);
         }
       }
     }
   }
 
   @media (-moz-os-version: windows-win7),
          (-moz-os-version: windows-win8) {
     #main-window[sizemode="maximized"] #titlebar-buttonbox {
@@ -259,39 +281,33 @@
     /* Artificially draw window borders that are covered by lwtheme, see bug 591930.
      * Borders for win7 are below, win10 doesn't need them. */
     #main-window[sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
       border-top: 1px solid @toolbarShadowColor@;
     }
   }
 
   @media (-moz-windows-default-theme) {
-    #toolbar-menubar:not(:-moz-lwtheme),
-    #TabsToolbar:not(:-moz-lwtheme) {
-      color: black;
-    }
-
     #main-menubar > menu:not(:-moz-lwtheme) {
       color: inherit;
     }
 
     /* Use a different color only on Windows 8 and higher for inactive windows.
      * On Win 7, the menubar fog disappears for inactive windows, and renders gray
      * illegible.
      */
     @media not all and (-moz-os-version: windows-win7) {
       #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive {
         color: ThreeDShadow;
       }
     }
   }
 
-  #main-window[darkwindowframe="true"] #toolbar-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive),
-  #main-window[darkwindowframe="true"] #TabsToolbar:not(:-moz-lwtheme):not(:-moz-window-inactive) {
-    color: white;
+  :root[darkwindowframe="true"]:not(:-moz-lwtheme):not(:-moz-window-inactive) {
+    --titlebar-text-color: white;
   }
 
   /* Show borders on Win 7 & 8, but not on 10 and later: */
   @media (-moz-os-version: windows-win7),
          (-moz-os-version: windows-win8) {
     /* Vertical toolbar border */
     #main-window:not([customizing])[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
     #main-window:not([customizing])[sizemode=normal] #navigator-toolbox:-moz-lwtheme,
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -10,37 +10,39 @@
 
 %include ../shared/browser.inc
 %include windowsShared.inc
 %define toolbarShadowColor hsla(209,67%,12%,0.35)
 
 %include ../shared/browser.inc.css
 
 :root {
+  --titlebar-text-color: inherit;
+
   --space-above-tabbar: 15px;
 
   --toolbarbutton-vertical-text-padding: calc(var(--toolbarbutton-inner-padding) - 1px);
 
 %ifdef MOZ_PHOTON_THEME
   --toolbarbutton-border-radius: 2px;
 %else
   --toolbarbutton-border-radius: 1px;
 
   --toolbarbutton-hover-background: rgba(0,0,0,.1);
   --toolbarbutton-active-background: rgba(0,0,0,.15);
 
   --backbutton-border-color: var(--urlbar-border-color-hover);
   --backbutton-background: rgba(255,255,255,.15);
-%endif
 
   --toolbarbutton-hover-bordercolor: rgba(0,0,0,.2);
   --toolbarbutton-hover-boxshadow: none;
 
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 0 0 1px rgba(0,0,0,.15) inset;
+%endif
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(0,0,0,.1);
 
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
   --urlbar-dropmarker-hover-region: rect(0px, 22px, 14px, 11px);
   --urlbar-dropmarker-active-region: rect(0px, 33px, 14px, 22px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
@@ -108,22 +110,23 @@ toolbar[brighttext] {
 #navigator-toolbox::after {
   content: "";
   display: -moz-box;
   -moz-box-ordinal-group: 101; /* tabs toolbar is 100 */
   border-bottom: 1px solid ThreeDShadow;
 }
 
 @media (-moz-windows-default-theme) {
+%ifndef MOZ_PHOTON_THEME
   @media (-moz-os-version: windows-win7) {
     #navigator-toolbox::after {
       border-bottom-color: #aabccf;
     }
   }
-
+%endif
   @media (-moz-os-version: windows-win8),
          (-moz-os-version: windows-win10) {
     #navigator-toolbox::after {
       border-bottom-color: #c2c2c2;
     }
   }
 }
 
@@ -158,29 +161,32 @@ toolbar[brighttext] {
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar)[collapsed=true] {
   min-height: 0.1px;
   max-height: 0;
   transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
 }
 
+#toolbar-menubar,
+#TabsToolbar {
+  color: var(--titlebar-text-color);
+}
+
 @media (-moz-windows-compositor: 0),
        (-moz-windows-default-theme: 0) {
   /* Please keep the menu text colors in this media block in sync with
    * compacttheme.css, minus the :not(:-moz-lwtheme) condition - see Bug 1165718.
    */
-  #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme),
-  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme) {
-    color: CaptionText;
+  :root[tabsintitlebar]:not([inFullscreen]):not(:-moz-lwtheme) {
+    --titlebar-text-color: CaptionText;
   }
 
-  #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive,
-  #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive {
-    color: InactiveCaptionText;
+  :root[tabsintitlebar]:not([inFullscreen]):not(:-moz-lwtheme):-moz-window-inactive {
+    --titlebar-text-color: InactiveCaptionText;
   }
 }
 
 @media (-moz-windows-compositor: 0) {
   #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
     visibility: hidden;
   }
 
@@ -465,17 +471,17 @@ menuitem.bookmark-item {
   opacity: 0.7;
 }
 
 
 %include ../shared/bookmarked-notification.inc.css
 %include ../shared/toolbarbuttons.inc.css
 %include ../shared/toolbarbutton-icons.inc.css
 %include ../shared/menupanel.inc.css
-
+%ifndef MOZ_PHOTON_THEME
 @media (-moz-os-version: windows-win7) {
   :root {
     --toolbarbutton-hover-background: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
     --toolbarbutton-hover-bordercolor: hsla(210,54%,20%,.15) hsla(210,54%,20%,.2) hsla(210,54%,20%,.25);
     --toolbarbutton-hover-boxshadow: 0 1px hsla(0,0%,100%,.3) inset,
                                      0 1px hsla(210,54%,20%,.03),
                                      0 0 2px hsla(210,54%,20%,.1);
 
@@ -485,16 +491,17 @@ menuitem.bookmark-item {
                                       0 0 1px hsla(210,54%,20%,.2) inset,
 /* allows keyhole-forward-clip-path to be used for non-hover as well as hover: */
                                       0 1px 0 hsla(210,54%,20%,0),
                                       0 0 2px hsla(210,54%,20%,0);
 
     --toolbarbutton-checkedhover-backgroundcolor: rgba(90%,90%,90%,.4);
   }
 }
+%endif
 
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-back.png") !important;
 }
 
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(rtl) {
@@ -505,75 +512,81 @@ menuitem.bookmark-item {
 
 #minimize-button,
 #restore-button,
 #close-button {
   -moz-appearance: none;
   border: none;
   margin: 0 !important;
   padding: 6px 12px;
+  -moz-context-properties: stroke;
+  stroke: currentColor;
+  color: inherit;
 }
 
 #minimize-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
+  list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
 }
 
 #restore-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+  list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
 }
 
 #minimize-button:hover,
 #restore-button:hover {
-  background-color: hsla(0, 0%, 0%, .12);
+  background-color: hsla(0,0%,0%,.12);
 }
 
 #minimize-button:hover:active,
 #restore-button:hover:active {
-  background-color: hsla(0, 0%, 0%, .22);
+  background-color: hsla(0,0%,0%,.22);
+}
+
+#TabsToolbar[brighttext] > #window-controls > #minimize-button:hover,
+#TabsToolbar[brighttext] > #window-controls > #restore-button:hover {
+  background-color: hsla(0,0%,100%,.12);
+}
+
+#TabsToolbar[brighttext] > #window-controls > #minimize-button:hover:active,
+#TabsToolbar[brighttext] > #window-controls > #restore-button:hover:active {
+  background-color: hsla(0,0%,100%,.22);
 }
 
 #close-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
+  list-style-image: url(chrome://browser/skin/window-controls/close.svg);
 }
 
 #close-button:hover {
   background-color: hsl(355, 86%, 49%);
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+  stroke: white;
 }
 
 #close-button:hover:active {
   background-color: hsl(355, 82%, 69%);
 }
 
-toolbar[brighttext] #minimize-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-white);
-}
-
-toolbar[brighttext] #restore-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-white);
-}
-
-toolbar[brighttext] #close-button {
-  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
-}
-
 @media (-moz-os-version: windows-win7) {
   #window-controls {
+    -moz-box-align: start;
     margin-inline-start: 4px;
   }
 
   #minimize-button,
   #restore-button,
   #close-button {
+    -moz-appearance: none;
+    border-style: none;
+    margin: 0;
     /* Important to ensure this applies even on toolbar[brighttext] */
     list-style-image: url("chrome://global/skin/icons/windowControls.png") !important;
     /* Also override background color to a avoid hover background styling
      * leaking through around the image. */
     background-color: transparent !important;
     padding: 0;
+    -moz-context-properties: unset;
   }
 
   #minimize-button {
     -moz-image-region: rect(0, 16px, 16px, 0);
   }
 
   #minimize-button:hover {
     -moz-image-region: rect(16px, 16px, 32px, 0);
@@ -604,30 +617,16 @@ toolbar[brighttext] #close-button {
 
   #close-button:hover {
     -moz-image-region: rect(16px, 48px, 32px, 32px);
   }
 
   #close-button:hover:active {
     -moz-image-region: rect(32px, 48px, 48px, 32px);
   }
-}
-
-@media (-moz-os-version: windows-win7) {
-  #window-controls {
-    -moz-box-align: start;
-  }
-
-  #minimize-button,
-  #restore-button,
-  #close-button {
-    -moz-appearance: none;
-    border-style: none;
-    margin: 0;
-  }
 
   #close-button {
     -moz-image-region: rect(0, 49px, 16px, 32px);
   }
 
   #close-button:hover {
     -moz-image-region: rect(16px, 49px, 32px, 32px);
   }
deleted file mode 100644
--- a/browser/themes/windows/caption-buttons.svg
+++ /dev/null
@@ -1,100 +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/. -->
-<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-  <style>
-    g:not(:target) {
-      display: none;
-    }
-    use:target > g {
-      display: initial;
-    }
-
-    g {
-      stroke: ButtonText;
-      stroke-width: 0.9px;
-      fill: none;
-    }
-    g:not([id|="close"]) {
-      shape-rendering: crispEdges;
-    }
-
-    .highcontrast {
-      stroke-width: 1.9px;
-    }
-    .highcontrast-hover > g {
-      stroke: HighlightText;
-    }
-    .white > g {
-      stroke: #fff;
-    }
-    .themes {
-      stroke: #fff;
-      stroke-width: 1.9px;
-    }
-
-    .outer-stroke {
-      stroke: #000;
-      stroke-width: 3.6;
-      opacity: .75;
-    }
-    .restore-background-window {
-      stroke-width: .9;
-    }
-  </style>
-  <g id="close">
-    <path d="M1,1 l 10,10 M1,11 l 10,-10"/>
-  </g>
-  <g id="maximize">
-    <rect x="1.5" y="1.5" width="9" height="9"/>
-  </g>
-  <g id="minimize">
-    <line x1="1" y1="5.5" x2="11" y2="5.5"/>
-  </g>
-  <g id="restore">
-    <rect x="1.5" y="3.5" width="7" height="7"/>
-    <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
-  </g>
-
-  <use id="close-white" class="white" xlink:href="#close"/>
-  <use id="maximize-white" class="white" xlink:href="#maximize"/>
-  <use id="minimize-white" class="white" xlink:href="#minimize"/>
-  <use id="restore-white" class="white" xlink:href="#restore"/>
-
-  <g id="close-highcontrast" class="highcontrast">
-    <path d="M1,1 l 10,10 M1,11 l 10,-10"/>
-  </g>
-  <g id="maximize-highcontrast" class="highcontrast">
-    <rect x="2" y="2" width="8" height="8"/>
-  </g>
-  <g id="minimize-highcontrast" class="highcontrast">
-    <line x1="1" y1="6" x2="11" y2="6"/>
-  </g>
-  <g id="restore-highcontrast" class="highcontrast">
-    <rect x="2" y="4" width="6" height="6"/>
-    <polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
-  </g>
-
-  <use id="close-highcontrast-hover" class="highcontrast-hover" xlink:href="#close-highcontrast"/>
-  <use id="maximize-highcontrast-hover" class="highcontrast-hover" xlink:href="#maximize-highcontrast"/>
-  <use id="minimize-highcontrast-hover" class="highcontrast-hover" xlink:href="#minimize-highcontrast"/>
-  <use id="restore-highcontrast-hover" class="highcontrast-hover" xlink:href="#restore-highcontrast"/>
-
-  <g id="close-themes" class="themes">
-    <path d="M1,1 l 10,10 M1,11 l 10,-10" class="outer-stroke" />
-    <path d="M1.75,1.75 l 8.5,8.5 M1.75,10.25 l 8.5,-8.5"/>
-  </g>
-  <g id="maximize-themes" class="themes">
-    <rect x="2" y="2" width="8" height="8" class="outer-stroke"/>
-    <rect x="2" y="2" width="8" height="8"/>
-  </g>
-  <g id="minimize-themes" class="themes">
-    <line x1="0" y1="6" x2="12" y2="6" class="outer-stroke"/>
-    <line x1="1" y1="6" x2="11" y2="6"/>
-  </g>
-  <g id="restore-themes" class="themes">
-    <path d="M2,4 l 6,0 l 0,6 l -6,0z M2.5,1.5 l 8,0 l 0,8" class="outer-stroke"/>
-    <rect x="2" y="4" width="6" height="6"/>
-    <polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
-  </g>
-</svg>
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -140,24 +140,22 @@
 
   #main-window[sizemode="maximized"] #main-menubar > menu:not(:-moz-window-inactive) {
     color: inherit;
   }
 
   /* Use proper menu text styling in Win7 classic mode (copied from browser.css) */
   @media (-moz-windows-compositor: 0),
          (-moz-windows-default-theme: 0) {
-    #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar,
-    #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar {
-      color: CaptionText;
+    :root[tabsintitlebar]:not([inFullscreen]) {
+      --titlebar-text-color: CaptionText;
     }
 
-    #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:-moz-window-inactive,
-    #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:-moz-window-inactive {
-      color: InactiveCaptionText;
+    :root[tabsintitlebar]:not([inFullscreen]):-moz-window-inactive {
+      --titlebar-text-color: InactiveCaptionText;
     }
 
     #main-window[tabsintitlebar] #main-menubar > menu {
       color: inherit;
     }
   }
 
   /* Use less opacity than normal since this is very dark, and on top of the default toolbar color */
@@ -219,45 +217,31 @@
 
 @media (-moz-os-version: windows-win10) {
   /* Always keep draggable space on the sides of tabs since there is no top margin on Win10 */
   #main-window .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox {
     padding-left: 15px;
     padding-right: 15px;
   }
 
-  /* Force white caption buttons for the dark theme on Windows 10 */
-  #titlebar-min:-moz-lwtheme-brighttext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-white);
+  .titlebar-button:-moz-lwtheme {
+    -moz-context-properties: stroke;
+    stroke: currentColor;
   }
-  #titlebar-max:-moz-lwtheme-brighttext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-white);
-  }
-  #main-window[sizemode="maximized"] #titlebar-max:-moz-lwtheme-brighttext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-white);
-  }
-  #titlebar-close:-moz-lwtheme-brighttext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+  #titlebar-min:-moz-lwtheme {
+    list-style-image: url(chrome://browser/skin/window-controls/minimize.svg);
   }
-
-  /* ... and normal ones for the light theme on Windows 10 */
-  #titlebar-min:-moz-lwtheme-darktext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
-  }
-  #titlebar-max:-moz-lwtheme-darktext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize);
+  #titlebar-max:-moz-lwtheme {
+    list-style-image: url(chrome://browser/skin/window-controls/maximize.svg);
   }
-  #main-window[sizemode="maximized"] #titlebar-max:-moz-lwtheme-darktext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
+  :root[sizemode="maximized"] #titlebar-max:-moz-lwtheme {
+    list-style-image: url(chrome://browser/skin/window-controls/restore.svg);
   }
-  #titlebar-close:-moz-lwtheme-darktext {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
-  }
-  #titlebar-close:-moz-lwtheme-darktext:hover {
-    list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+  #titlebar-close:-moz-lwtheme {
+    list-style-image: url(chrome://browser/skin/window-controls/close.svg);
   }
 }
 
 .ac-type-icon {
   /* Left-align the type icon in awesomebar popup results with the icon in the
      urlbar. */
   margin-inline-start: 13px;
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -5,17 +5,16 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-  skin/classic/browser/caption-buttons.svg
   skin/classic/browser/Info.png
   skin/classic/browser/livemark-folder.png
   skin/classic/browser/menu-back.png
   skin/classic/browser/menu-forward.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
@@ -32,16 +31,26 @@ browser.jar:
   skin/classic/browser/privatebrowsing-mask-titlebar-win7-tall.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/reload-stop-go@2x.png
   skin/classic/browser/reload-stop-go-win7.png
   skin/classic/browser/reload-stop-go-win7@2x.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
+  skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
+  skin/classic/browser/sync-horizontalbar.png
+  skin/classic/browser/sync-horizontalbar@2x.png
+  skin/classic/browser/sync-horizontalbar-win7.png
+  skin/classic/browser/sync-horizontalbar-win7@2x.png
+  skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
+  skin/classic/browser/syncProgress-horizontalbar.png
+  skin/classic/browser/syncProgress-horizontalbar@2x.png
+  skin/classic/browser/syncProgress-horizontalbar-win7.png
+  skin/classic/browser/syncProgress-horizontalbar-win7@2x.png
   skin/classic/browser/toolbarbutton-dropdown-arrow-win7.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker@2x.png
   skin/classic/browser/urlbar-history-dropmarker-win7.png
   skin/classic/browser/urlbar-history-dropmarker-win7@2x.png
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
@@ -117,26 +126,28 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-selected-end.svg         (tab-selected-end.svg)
   skin/classic/browser/tabbrowser/tab-selected-start.svg       (tab-selected-start.svg)
 
   skin/classic/browser/tabbrowser/tab-stroke-end.png           (tabbrowser/tab-stroke-end.png)
   skin/classic/browser/tabbrowser/tab-stroke-end@2x.png        (tabbrowser/tab-stroke-end@2x.png)
   skin/classic/browser/tabbrowser/tab-stroke-start.png         (tabbrowser/tab-stroke-start.png)
   skin/classic/browser/tabbrowser/tab-stroke-start@2x.png      (tabbrowser/tab-stroke-start@2x.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png         (tabbrowser/tabDragIndicator.png)
-  skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
-  skin/classic/browser/sync-horizontalbar.png
-  skin/classic/browser/sync-horizontalbar@2x.png
-  skin/classic/browser/sync-horizontalbar-win7.png
-  skin/classic/browser/sync-horizontalbar-win7@2x.png
-  skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
-  skin/classic/browser/syncProgress-horizontalbar.png
-  skin/classic/browser/syncProgress-horizontalbar@2x.png
-  skin/classic/browser/syncProgress-horizontalbar-win7.png
-  skin/classic/browser/syncProgress-horizontalbar-win7@2x.png
+  skin/classic/browser/window-controls/close.svg                 (window-controls/close.svg)
+  skin/classic/browser/window-controls/close-highcontrast.svg    (window-controls/close-highcontrast.svg)
+  skin/classic/browser/window-controls/close-themes.svg          (window-controls/close-themes.svg)
+  skin/classic/browser/window-controls/maximize.svg              (window-controls/maximize.svg)
+  skin/classic/browser/window-controls/maximize-highcontrast.svg (window-controls/maximize-highcontrast.svg)
+  skin/classic/browser/window-controls/maximize-themes.svg       (window-controls/maximize-themes.svg)
+  skin/classic/browser/window-controls/minimize.svg              (window-controls/minimize.svg)
+  skin/classic/browser/window-controls/minimize-highcontrast.svg (window-controls/minimize-highcontrast.svg)
+  skin/classic/browser/window-controls/minimize-themes.svg       (window-controls/minimize-themes.svg)
+  skin/classic/browser/window-controls/restore.svg               (window-controls/restore.svg)
+  skin/classic/browser/window-controls/restore-highcontrast.svg  (window-controls/restore-highcontrast.svg)
+  skin/classic/browser/window-controls/restore-themes.svg        (window-controls/restore-themes.svg)
 
 #ifdef E10S_TESTING_ONLY
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
 #endif
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/page-livemarks.png                   chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/audioFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/close-highcontrast.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <path stroke="context-stroke" stroke-width="1.9" fill="none" d="M1,1 l 10,10 M1,11 l 10,-10"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/close-themes.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <path stroke="black" stroke-width="3.6" stroke-opacity=".75" d="M1,1 l 10,10 M1,11 l 10,-10"/>
+  <path stroke="white" stroke-width="1.9" d="M1.75,1.75 l 8.5,8.5 M1.75,10.25 l 8.5,-8.5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/close.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <path stroke="context-stroke" stroke-width=".9" fill="none" d="M1,1 l 10,10 M1,11 l 10,-10"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/maximize-highcontrast.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">
+  <rect stroke="context-stroke" stroke-width="1.9" fill="none" x="2" y="2" width="8" height="8"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/maximize-themes.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges" fill="none">
+  <rect stroke="black" stroke-width="3.6" stroke-opacity=".75" x="2" y="2" width="8" height="8"/>
+  <rect stroke="white" stroke-width="1.9" x="2" y="2" width="8" height="8"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/maximize.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">
+  <rect stroke="context-stroke" stroke-width=".9" fill="none" x="1.5" y="1.5" width="9" height="9"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/minimize-highcontrast.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <line stroke="context-stroke" stroke-width="1.9" fill="none" shape-rendering="crispEdges" x1="1" y1="6" x2="11" y2="6"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/minimize-themes.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">
+  <line stroke="black" stroke-width="3.6" stroke-opacity=".75" x1="0" y1="6" x2="12" y2="6"/>
+  <line stroke="white" stroke-width="1.9" x1="1" y1="6" x2="11" y2="6"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/minimize.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
+  <line stroke="context-stroke" stroke-width=".9" fill="none" shape-rendering="crispEdges" x1="1" y1="5.5" x2="11" y2="5.5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/restore-highcontrast.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" stroke="context-stroke" stroke-width="1.9" fill="none" shape-rendering="crispEdges">
+  <rect x="2" y="4" width="6" height="6"/>
+  <polyline points="3.5,1.5 10.5,1.5 10.5,8.5" stroke-width=".9"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/restore-themes.svg
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges" fill="none" stroke="white">
+  <path stroke="black" stroke-width="3.6" stroke-opacity=".75" d="M2,4 l 6,0 l 0,6 l -6,0z M2.5,1.5 l 8,0 l 0,8"/>
+  <rect stroke-width="1.9" x="2" y="4" width="6" height="6"/>
+  <polyline stroke-width=".9" points="3.5,1.5 10.5,1.5 10.5,8.5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/window-controls/restore.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" stroke="context-stroke" stroke-width=".9" fill="none" shape-rendering="crispEdges">
+  <rect x="1.5" y="3.5" width="7" height="7"/>
+  <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
+</svg>
--- a/build/moz.configure/keyfiles.configure
+++ b/build/moz.configure/keyfiles.configure
@@ -1,23 +1,23 @@
 # -*- Mode: python; 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/.
 
 
 @template
-def keyfile(desc, help=None, callback=lambda x: x):
+def keyfile(desc, default=None, help=None, callback=lambda x: x):
     help = help or ('Use the secret key contained in the given keyfile '
                     'for %s requests' % desc)
     name = desc.lower().replace(' ', '-')
     no_key = callback('no-%s-key' % name)
 
-    option('--with-%s-keyfile' % name, nargs=1, help=help)
+    option('--with-%s-keyfile' % name, nargs=1, default=default, help=help)
 
     @depends('--with-%s-keyfile' % name)
     @checking('for the %s key' % desc, lambda x: x and x is not no_key)
     @imports(_from='__builtin__', _import='open')
     @imports(_from='__builtin__', _import='IOError')
     def keyfile(value):
         if value:
             try:
@@ -29,36 +29,37 @@ def keyfile(desc, help=None, callback=la
             except IOError as e:
                 raise FatalCheckError("'%s': %s." % (value[0], e.strerror))
         return no_key
 
     return keyfile
 
 
 @template
-def simple_keyfile(desc):
-    value = keyfile(desc)
+def simple_keyfile(desc, default=None):
+    value = keyfile(desc, default=default)
     set_config('MOZ_%s_KEY' % desc.upper().replace(' ', '_'), value)
 
 
 @template
-def id_and_secret_keyfile(desc):
+def id_and_secret_keyfile(desc, default=None):
     def id_and_secret(value):
         if value.startswith('no-') and value.endswith('-key'):
             id = value[:-3] + 'clientid'
             secret = value
         elif ' ' in value:
             id, secret = value.split(' ', 1)
         else:
             raise FatalCheckError('%s key file has an invalid format.' % desc)
         return namespace(
             id=id,
             secret=secret,
         )
 
     content = keyfile(desc, help='Use the client id and secret key contained '
                                  'in the given keyfile for %s requests' % desc,
+                      default=default,
                       callback=id_and_secret)
 
 
     name = desc.upper().replace(' ', '_')
     set_config('MOZ_%s_CLIENTID' % name, content.id)
     set_config('MOZ_%s_KEY' % name, content.secret)
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -9,16 +9,17 @@ const Services = require("Services");
 
 const {DevToolsShim} = Cu.import("chrome://devtools-shim/content/DevToolsShim.jsm", {});
 
 // Load gDevToolsBrowser toolbox lazily as they need gDevTools to be fully initialized
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "Toolbox", "devtools/client/framework/toolbox", true);
 loader.lazyRequireGetter(this, "ToolboxHostManager", "devtools/client/framework/toolbox-host-manager", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
+loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
   require("devtools/client/definitions");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {JsonView} = require("devtools/client/jsonview/main");
 const AboutDevTools = require("devtools/client/framework/about-devtools-toolbox");
 const {Task} = require("devtools/shared/task");
 const {getTheme, setTheme, addThemeObserver, removeThemeObserver} =
@@ -390,16 +391,37 @@ DevTools.prototype = {
         definitions.push(definition);
       }
     }
 
     return definitions.sort(this.ordinalSort);
   },
 
   /**
+   * Get the array of currently opened scratchpad windows.
+   *
+   * @return {Array} array of currently opened scratchpad windows.
+   *         Empty array if the scratchpad manager is not loaded.
+   */
+  getOpenedScratchpads: function () {
+    // Check if the module is loaded to avoid loading ScratchpadManager for no reason.
+    if (!Cu.isModuleLoaded("resource://devtools/client/scratchpad/scratchpad-manager.jsm")) {
+      return [];
+    }
+    return ScratchpadManager.getSessionState();
+  },
+
+  /**
+   * Restore the provided array of scratchpad window states.
+   */
+  restoreScratchpadSession: function (scratchpads) {
+    ScratchpadManager.restoreSession(scratchpads);
+  },
+
+  /**
    * Show a Toolbox for a target (either by creating a new one, or if a toolbox
    * already exists for the target, by bring to the front the existing one)
    * If |toolId| is specified then the displayed toolbox will have the
    * specified tool selected.
    * If |hostType| is specified then the toolbox will be displayed using the
    * specified HostType.
    *
    * @param {Target} target
@@ -535,19 +557,24 @@ DevTools.prototype = {
     }
 
     JsonView.destroy();
 
     gDevTools.unregisterDefaults();
 
     removeThemeObserver(this._onThemeChanged);
 
-    // Notify the DevToolsShim that DevTools are no longer available, particularly if the
-    // destroy was caused by disabling/removing the DevTools add-on.
-    DevToolsShim.unregister();
+    // Do not unregister devtools from the DevToolsShim if the destroy is caused by an
+    // application shutdown. For instance SessionStore needs to save the Scratchpad
+    // manager state on shutdown.
+    if (!shuttingDown) {
+      // Notify the DevToolsShim that DevTools are no longer available, particularly if
+      // the destroy was caused by disabling/removing the DevTools add-on.
+      DevToolsShim.unregister();
+    }
 
     // Cleaning down the toolboxes: i.e.
     //   for (let [target, toolbox] of this._toolboxes) toolbox.destroy();
     // Is taken care of by the gDevToolsBrowser.forgetBrowserWindow
   },
 
   /**
    * Iterator that yields each of the toolboxes.
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -2,16 +2,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 devtools.jar:
 %   content devtools %content/
     content/shared/vendor/d3.js (shared/vendor/d3.js)
     content/shared/vendor/dagre-d3.js (shared/vendor/dagre-d3.js)
     content/shared/widgets/widgets.css (shared/widgets/widgets.css)
+    content/netmonitor/src/assets/styles/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
     content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
     content/projecteditor/chrome/content/projecteditor.xul (projecteditor/chrome/content/projecteditor.xul)
     content/projecteditor/lib/helpers/readdir.js (projecteditor/lib/helpers/readdir.js)
     content/netmonitor/index.html (netmonitor/index.html)
     content/webconsole/webconsole.xul (webconsole/webconsole.xul)
     content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul)
     content/scratchpad/scratchpad.js (scratchpad/scratchpad.js)
     content/shared/splitview.css (shared/splitview.css)
@@ -151,17 +152,16 @@ devtools.jar:
     skin/images/breakpoint.svg (themes/images/breakpoint.svg)
     skin/webconsole.css (themes/webconsole.css)
     skin/images/webconsole.svg (themes/images/webconsole.svg)
     skin/images/breadcrumbs-scrollbutton.png (themes/images/breadcrumbs-scrollbutton.png)
     skin/images/breadcrumbs-scrollbutton@2x.png (themes/images/breadcrumbs-scrollbutton@2x.png)
     skin/animationinspector.css (themes/animationinspector.css)
     skin/canvasdebugger.css (themes/canvasdebugger.css)
     skin/debugger.css (themes/debugger.css)
-    skin/netmonitor.css (netmonitor/src/assets/styles/netmonitor.css)
     skin/performance.css (themes/performance.css)
     skin/memory.css (themes/memory.css)
     skin/scratchpad.css (themes/scratchpad.css)
     skin/shadereditor.css (themes/shadereditor.css)
     skin/storage.css (themes/storage.css)
     skin/splitview.css (themes/splitview.css)
     skin/styleeditor.css (themes/styleeditor.css)
     skin/webaudioeditor.css (themes/webaudioeditor.css)
--- a/devtools/client/netmonitor/index.html
+++ b/devtools/client/netmonitor/index.html
@@ -1,17 +1,17 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 <html dir="">
   <head>
     <link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
-    <link rel="stylesheet" href="chrome://devtools/skin/netmonitor.css"/>
+    <link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/netmonitor.css"/>
     <script src="chrome://devtools/content/shared/theme-switching.js"></script>
   </head>
   <body class="theme-sidebar" role="application">
     <div id="mount"></div>
     <script>
       "use strict";
 
       const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -1,29 +1,28 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=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/. */
 
 "use strict";
 
-const {Ci, Cu} = require("chrome");
+const {Ci} = require("chrome");
 
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 
 const { extend } = require("sdk/core/heritage");
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const {Task} = require("devtools/shared/task");
 const l10n = require("devtools/client/webconsole/webconsole-l10n");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const {PluralForm} = require("devtools/shared/plural-form");
 
@@ -113,17 +112,21 @@ const CONSOLE_API_LEVELS_TO_SEVERITIES =
 // Array of known message source URLs we need to hide from output.
 const IGNORED_SOURCE_URLS = ["debugger eval code"];
 
 // The maximum length of strings to be displayed by the Web Console.
 const MAX_LONG_STRING_LENGTH = 200000;
 
 // Regular expression that matches the allowed CSS property names when using
 // the `window.console` API.
-const RE_ALLOWED_STYLES = /^(?:-moz-)?(?:background|border|box|clear|color|cursor|display|float|font|line|margin|padding|text|transition|outline|white-space|word|writing|(?:min-|max-)?width|(?:min-|max-)?height)/;
+const RE_ALLOWED_STYLES = new RegExp(["^(?:-moz-)?(?:background|border|box|clear|" +
+                                      "color|cursor|display|float|font|line|margin|" +
+                                      "padding|text|transition|outline|white-space|" +
+                                      "word|writing|(?:min-|max-)?width|" +
+                                      "(?:min-|max-)?height)"]);
 
 // Regular expressions to search and replace with 'notallowed' in the styles
 // given to the `window.console` API methods.
 const RE_CLEANUP_STYLES = [
   // url(), -moz-element()
   /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
 
   // various URL protocols
@@ -145,18 +148,17 @@ const TABLE_COLUMN_MAX_ITEMS = 10;
  *        The console output owner. This usually the WebConsoleFrame instance.
  *        Any other object can be used, as long as it has the following
  *        properties and methods:
  *          - window
  *          - document
  *          - outputMessage(category, methodOrNode[, methodArguments])
  *            TODO: this is needed temporarily, until bug 778766 is fixed.
  */
-function ConsoleOutput(owner)
-{
+function ConsoleOutput(owner) {
   this.owner = owner;
   this._onFlushOutputMessage = this._onFlushOutputMessage.bind(this);
 }
 
 ConsoleOutput.prototype = {
   _dummyElement: null,
 
   /**
@@ -201,30 +203,28 @@ ConsoleOutput.prototype = {
 
   /**
    * Release an actor.
    *
    * @private
    * @param string actorId
    *        The actor ID you want to release.
    */
-  _releaseObject: function (actorId)
-  {
+  _releaseObject: function (actorId) {
     this.owner._releaseObject(actorId);
   },
 
   /**
    * Add a message to output.
    *
    * @param object ...args
    *        Any number of Message objects.
    * @return this
    */
-  addMessage: function (...args)
-  {
+  addMessage: function (...args) {
     for (let msg of args) {
       msg.init(this);
       this.owner.outputMessage(msg._categoryCompat, this._onFlushOutputMessage,
                                [msg]);
     }
     return this;
   },
 
@@ -237,33 +237,31 @@ ConsoleOutput.prototype = {
    * TODO: remove this method once bug 778766 is fixed.
    *
    * @private
    * @param object message
    *        The message object to render.
    * @return DOMElement
    *         The message DOM element that can be added to the console output.
    */
-  _onFlushOutputMessage: function (message)
-  {
+  _onFlushOutputMessage: function (message) {
     return message.render().element;
   },
 
   /**
    * Get an array of selected messages. This list is based on the text selection
    * start and end points.
    *
    * @param number [limit]
    *        Optional limit of selected messages you want. If no value is given,
    *        all of the selected messages are returned.
    * @return array
    *         Array of DOM elements for each message that is currently selected.
    */
-  getSelectedMessages: function (limit)
-  {
+  getSelectedMessages: function (limit) {
     let selection = this.window.getSelection();
     if (selection.isCollapsed) {
       return [];
     }
 
     if (selection.containsNode(this.element, true)) {
       return Array.slice(this.element.children);
     }
@@ -298,81 +296,75 @@ ConsoleOutput.prototype = {
   /**
    * Find the DOM element of a message for any given descendant.
    *
    * @param DOMElement elem
    *        The element to start the search from.
    * @return DOMElement|null
    *         The DOM element of the message, if any.
    */
-  getMessageForElement: function (elem)
-  {
+  getMessageForElement: function (elem) {
     while (elem && elem.parentNode) {
       if (elem.classList && elem.classList.contains("message")) {
         return elem;
       }
       elem = elem.parentNode;
     }
     return null;
   },
 
   /**
    * Select all messages.
    */
-  selectAllMessages: function ()
-  {
+  selectAllMessages: function () {
     let selection = this.window.getSelection();
     selection.removeAllRanges();
     let range = this.document.createRange();
     range.selectNodeContents(this.element);
     selection.addRange(range);
   },
 
   /**
    * Add a message to the selection.
    *
    * @param DOMElement elem
    *        The message element to select.
    */
-  selectMessage: function (elem)
-  {
+  selectMessage: function (elem) {
     let selection = this.window.getSelection();
     selection.removeAllRanges();
     let range = this.document.createRange();
     range.selectNodeContents(elem);
     selection.addRange(range);
   },
 
   /**
    * Open an URL in a new tab.
    * @see WebConsole.openLink() in hudservice.js
    */
-  openLink: function ()
-  {
+  openLink: function () {
     this.owner.owner.openLink.apply(this.owner.owner, arguments);
   },
 
   openLocationInDebugger: function ({url, line}) {
     return this.owner.owner.viewSourceInDebugger(url, line);
   },
 
   /**
    * Open the variables view to inspect an object actor.
    * @see JSTerm.openVariablesView() in webconsole.js
    */
-  openVariablesView: function ()
-  {
+  openVariablesView: function () {
     this.owner.jsterm.openVariablesView.apply(this.owner.jsterm, arguments);
   },
 
   /**
    * Destroy this ConsoleOutput instance.
    */
-  destroy: function ()
-  {
+  destroy: function () {
     this._dummyElement = null;
     this.owner = null;
   },
 }; // ConsoleOutput.prototype
 
 /**
  * Message objects container.
  * @type object
@@ -380,18 +372,17 @@ ConsoleOutput.prototype = {
 var Messages = {};
 
 /**
  * The BaseMessage object is used for all types of messages. Every kind of
  * message should use this object as its base.
  *
  * @constructor
  */
-Messages.BaseMessage = function ()
-{
+Messages.BaseMessage = function () {
   this.widgets = new Set();
   this._onClickAnchor = this._onClickAnchor.bind(this);
   this._repeatID = { uid: gSequenceId() };
   this.textContent = "";
 };
 
 Messages.BaseMessage.prototype = {
   /**
@@ -466,58 +457,54 @@ Messages.BaseMessage.prototype = {
    * Initialize the message.
    *
    * @param object output
    *        The ConsoleOutput owner.
    * @param object [parent=null]
    *        Optional: a different message object that owns this instance.
    * @return this
    */
-  init: function (output, parent = null)
-  {
+  init: function (output, parent = null) {
     this.output = output;
     this.parent = parent;
     return this;
   },
 
   /**
    * Non-unique ID for this message object used for tracking duplicate messages.
    * Different message kinds can identify themselves based their own criteria.
    *
    * @return string
    */
-  getRepeatID: function ()
-  {
+  getRepeatID: function () {
     return JSON.stringify(this._repeatID);
   },
 
   /**
    * Render the message. After this method is invoked the |element| property
    * will point to the DOM element of this message.
    * @return this
    */
-  render: function ()
-  {
+  render: function () {
     if (!this.element) {
       this.element = this._renderCompat();
     }
     return this;
   },
 
   /**
    * Prepare the message container for the Web Console, such that it is
    * compatible with the current implementation.
    * TODO: remove this once bug 778766 is fixed.
    *
    * @private
    * @return Element
    *         The DOM element that wraps the message.
    */
-  _renderCompat: function ()
-  {
+  _renderCompat: function () {
     let doc = this.output.document;
     let container = doc.createElementNS(XHTML_NS, "div");
     container.id = "console-msg-" + gSequenceId();
     container.className = "message";
     if (this.category == "input") {
       // Assistive technology tools shouldn't echo input to the user,
       // as the user knows what they've just typed.
       container.setAttribute("aria-live", "off");
@@ -539,41 +526,38 @@ Messages.BaseMessage.prototype = {
    *
    * @private
    * @param Element element
    *        The DOM element to which you want to add a click event handler.
    * @param function [callback=this._onClickAnchor]
    *        Optional click event handler. The default event handler is
    *        |this._onClickAnchor|.
    */
-  _addLinkCallback: function (element, callback = this._onClickAnchor)
-  {
+  _addLinkCallback: function (element, callback = this._onClickAnchor) {
     // This is going into the WebConsoleFrame object instance that owns
     // the ConsoleOutput object. The WebConsoleFrame owner is the WebConsole
     // object instance from hudservice.js.
     // TODO: move _addMessageLinkCallback() into ConsoleOutput once bug 778766
     // is fixed.
     this.output.owner._addMessageLinkCallback(element, callback);
   },
 
   /**
    * The default |click| event handler for links in the output. This function
    * opens the anchor's link in a new tab.
    *
    * @private
    * @param Event event
    *        The DOM event that invoked this function.
    */
-  _onClickAnchor: function (event)
-  {
+  _onClickAnchor: function (event) {
     this.output.openLink(event.target.href);
   },
 
-  destroy: function ()
-  {
+  destroy: function () {
     // Destroy all widgets that have registered themselves in this.widgets
     for (let widget of this.widgets) {
       widget.destroy();
     }
     this.widgets.clear();
   }
 };
 
@@ -816,18 +800,17 @@ Messages.Simple.prototype = extend(Messa
   },
 
   get _filterKeyCompat() {
     return this._categoryCompat !== null && this._severityCompat !== null ?
            COMPAT.PREFERENCE_KEYS[this._categoryCompat][this._severityCompat] :
            null;
   },
 
-  init: function ()
-  {
+  init: function () {
     Messages.BaseMessage.prototype.init.apply(this, arguments);
     this._groupDepthCompat = this.output.owner.groupDepth;
     this._initRepeatID();
     return this;
   },
 
   /**
    * Tells if the message can be expanded/collapsed.
@@ -838,18 +821,17 @@ Messages.Simple.prototype = extend(Messa
   /**
    * Getter that tells if this message is collapsed - no details are shown.
    * @type boolean
    */
   get collapsed() {
     return this.collapsible && this.element && !this.element.hasAttribute("open");
   },
 
-  _initRepeatID: function ()
-  {
+  _initRepeatID: function () {
     if (!this._filterDuplicates) {
       return;
     }
 
     // Add the properties we care about for identifying duplicate messages.
     let rid = this._repeatID;
     delete rid.uid;
 
@@ -860,29 +842,27 @@ Messages.Simple.prototype = extend(Messa
     rid.location = this.location;
     rid.link = this._link;
     rid.linkCallback = this._linkCallback + "";
     rid.className = this._className;
     rid.groupDepth = this._groupDepthCompat;
     rid.textContent = "";
   },
 
-  getRepeatID: function ()
-  {
+  getRepeatID: function () {
     // No point in returning a string that includes other properties when there
     // is a unique ID.
     if (this._repeatID.uid) {
       return JSON.stringify({ uid: this._repeatID.uid });
     }
 
     return JSON.stringify(this._repeatID);
   },
 
-  render: function ()
-  {
+  render: function () {
     if (this.element) {
       return this;
     }
 
     let timestamp = new Widgets.MessageTimestamp(this, this.timestamp).render();
 
     let icon = this.document.createElementNS(XHTML_NS, "span");
     icon.className = "icon";
@@ -946,18 +926,17 @@ Messages.Simple.prototype = extend(Messa
     return this;
   },
 
   /**
    * Render the message body DOM element.
    * @private
    * @return Element
    */
-  _renderBody: function ()
-  {
+  _renderBody: function () {
     let bodyWrapper = this.document.createElementNS(XHTML_NS, "span");
     bodyWrapper.className = "message-body-wrapper";
 
     let bodyFlex = this.document.createElementNS(XHTML_NS, "span");
     bodyFlex.className = "message-flex-body";
     bodyWrapper.appendChild(bodyFlex);
 
     let body = this.document.createElementNS(XHTML_NS, "span");
@@ -1009,18 +988,17 @@ Messages.Simple.prototype = extend(Messa
     return bodyWrapper;
   },
 
   /**
    * Render the repeat bubble DOM element part of the message.
    * @private
    * @return Element
    */
-  _renderRepeatNode: function ()
-  {
+  _renderRepeatNode: function () {
     if (!this._filterDuplicates) {
       return null;
     }
 
     let repeatNode = this.document.createElementNS(XHTML_NS, "span");
     repeatNode.setAttribute("value", "1");
     repeatNode.className = "message-repeats";
     repeatNode.textContent = 1;
@@ -1028,18 +1006,17 @@ Messages.Simple.prototype = extend(Messa
     return repeatNode;
   },
 
   /**
    * Render the message source location DOM element.
    * @private
    * @return Element
    */
-  _renderLocation: function ()
-  {
+  _renderLocation: function () {
     if (!this.location) {
       return null;
     }
 
     let {url, line, column} = this.location;
     if (IGNORED_SOURCE_URLS.indexOf(url) != -1) {
       return null;
     }
@@ -1053,55 +1030,51 @@ Messages.Simple.prototype = extend(Messa
    * The click event handler for the message expander arrow element. This method
    * toggles the display of message details.
    *
    * @private
    * @param nsIDOMEvent ev
    *        The DOM event object.
    * @see this.toggleDetails()
    */
-  _onClickCollapsible: function (ev)
-  {
+  _onClickCollapsible: function (ev) {
     ev.preventDefault();
     this.toggleDetails();
   },
 
   /**
    * Expand/collapse message details.
    */
-  toggleDetails: function ()
-  {
+  toggleDetails: function () {
     let twisty = this.element.querySelector(".theme-twisty");
     if (this.element.hasAttribute("open")) {
       this.element.removeAttribute("open");
       twisty.removeAttribute("open");
     } else {
       this.element.setAttribute("open", true);
       twisty.setAttribute("open", true);
     }
   },
 }); // Messages.Simple.prototype
 
-
 /**
  * The Extended message.
  *
  * @constructor
  * @extends Messages.Simple
  * @param array messagePieces
  *        The message to display given as an array of elements. Each array
  *        element can be a DOM node, function, ObjectActor, LongString or
  *        a string.
  * @param object [options]
  *        Options for rendering this message:
  *        - quoteStrings: boolean that tells if you want strings to be wrapped
  *        in quotes or not.
  */
-Messages.Extended = function (messagePieces, options = {})
-{
+Messages.Extended = function (messagePieces, options = {}) {
   Messages.Simple.call(this, null, options);
 
   this._messagePieces = messagePieces;
 
   if ("quoteStrings" in options) {
     this._quoteStrings = options.quoteStrings;
   }
 
@@ -1120,32 +1093,30 @@ Messages.Extended.prototype = extend(Mes
 
   /**
    * Boolean that tells if the strings displayed in this message are wrapped.
    * @private
    * @type boolean
    */
   _quoteStrings: true,
 
-  getRepeatID: function ()
-  {
+  getRepeatID: function () {
     if (this._repeatID.uid) {
       return JSON.stringify({ uid: this._repeatID.uid });
     }
 
     // Sets are not stringified correctly. Temporarily switching to an array.
     let actors = this._repeatID.actors;
     this._repeatID.actors = [...actors];
     let result = JSON.stringify(this._repeatID);
     this._repeatID.actors = actors;
     return result;
   },
 
-  render: function ()
-  {
+  render: function () {
     let result = this.document.createDocumentFragment();
 
     for (let i = 0; i < this._messagePieces.length; i++) {
       let separator = i > 0 ? this._renderBodyPieceSeparator() : null;
       if (separator) {
         result.appendChild(separator);
       }
 
@@ -1159,29 +1130,30 @@ Messages.Extended.prototype = extend(Mes
   },
 
   /**
    * Render the separator between the pieces of the message.
    *
    * @private
    * @return Element
    */
-  _renderBodyPieceSeparator: function () { return null; },
+  _renderBodyPieceSeparator: function () {
+    return null;
+  },
 
   /**
    * Render one piece/element of the message array.
    *
    * @private
    * @param mixed piece
    *        Message element to display - this can be a LongString, ObjectActor,
    *        DOM node or a function to invoke.
    * @return Element
    */
-  _renderBodyPiece: function (piece, options = {})
-  {
+  _renderBodyPiece: function (piece, options = {}) {
     if (piece instanceof Ci.nsIDOMNode) {
       return piece;
     }
     if (typeof piece == "function") {
       return piece(this);
     }
 
     return this._renderValueGrip(piece, options);
@@ -1202,18 +1174,17 @@ Messages.Extended.prototype = extend(Mes
    *        grip. This is typically set to true when the object needs to be
    *        displayed in an array preview, or as a property value in object
    *        previews, etc.
    *        - shorten - boolean that tells the renderer to display a truncated
    *        grip.
    * @return DOMElement
    *         The DOM element that displays the given grip.
    */
-  _renderValueGrip: function (grip, options = {})
-  {
+  _renderValueGrip: function (grip, options = {}) {
     let isPrimitive = VariablesView.isPrimitive({ value: grip });
     let isActorGrip = WebConsoleUtils.isActorGrip(grip);
     let noStringQuotes = !this._quoteStrings;
     if ("noStringQuotes" in options) {
       noStringQuotes = options.noStringQuotes;
     }
 
     if (isActorGrip) {
@@ -1230,17 +1201,17 @@ Messages.Extended.prototype = extend(Mes
 
     let unshortenedGrip = grip;
     if (options.shorten) {
       grip = this.shortenValueGrip(grip);
     }
 
     let result = this.document.createElementNS(XHTML_NS, "span");
     if (isPrimitive) {
-      if (Widgets.URLString.prototype.containsURL.call(Widgets.URLString.prototype, grip)) {
+      if (Widgets.URLString.prototype.containsURL(grip)) {
         let widget = new Widgets.URLString(this, grip, unshortenedGrip).render();
         return widget.element;
       }
 
       let className = this.getClassNameForValueGrip(grip);
       if (className) {
         result.className = className;
       }
@@ -1261,18 +1232,17 @@ Messages.Extended.prototype = extend(Mes
    *
    * @param object grip
    *        Value grip from the server.
    * @return object
    *        Possible values of object:
    *        - A shortened string, if original grip was of string type.
    *        - The unmodified input grip, if it wasn't of string type.
    */
-  shortenValueGrip: function (grip)
-  {
+  shortenValueGrip: function (grip) {
     let shortVal = grip;
     if (typeof (grip) == "string") {
       shortVal = grip.replace(/(\r\n|\n|\r)/gm, " ");
       if (shortVal.length > MAX_STRING_GRIP_LENGTH) {
         shortVal = shortVal.substring(0, MAX_STRING_GRIP_LENGTH - 1) + ELLIPSIS;
       }
     }
 
@@ -1282,18 +1252,17 @@ Messages.Extended.prototype = extend(Mes
   /**
    * Get a CodeMirror-compatible class name for a given value grip.
    *
    * @param object grip
    *        Value grip from the server.
    * @return string
    *         The class name for the grip.
    */
-  getClassNameForValueGrip: function (grip)
-  {
+  getClassNameForValueGrip: function (grip) {
     let map = {
       "number": "cm-number",
       "longstring": "console-string",
       "string": "console-string",
       "regexp": "cm-string-2",
       "boolean": "cm-atom",
       "-infinity": "cm-atom",
       "infinity": "cm-atom",
@@ -1320,52 +1289,48 @@ Messages.Extended.prototype = extend(Mes
    * @param object objectActor
    *        The ObjectActor to display.
    * @param object options
    *        Options to use for displaying the ObjectActor.
    * @see this._renderValueGrip for the available options.
    * @return DOMElement
    *         The DOM element that displays the object actor.
    */
-  _renderObjectActor: function (objectActor, options = {})
-  {
-    let widget = Widgets.ObjectRenderers.byClass[objectActor.class];
+  _renderObjectActor: function (objectActor, options = {}) {
+    let Widget = Widgets.ObjectRenderers.byClass[objectActor.class];
 
     let { preview } = objectActor;
-    if ((!widget || (widget.canRender && !widget.canRender(objectActor)))
+    if ((!Widget || (Widget.canRender && !Widget.canRender(objectActor)))
         && preview
         && preview.kind) {
-      widget = Widgets.ObjectRenderers.byKind[preview.kind];
+      Widget = Widgets.ObjectRenderers.byKind[preview.kind];
     }
 
-    if (!widget || (widget.canRender && !widget.canRender(objectActor))) {
-      widget = Widgets.JSObject;
+    if (!Widget || (Widget.canRender && !Widget.canRender(objectActor))) {
+      Widget = Widgets.JSObject;
     }
 
-    let instance = new widget(this, objectActor, options).render();
+    let instance = new Widget(this, objectActor, options).render();
     return instance.element;
   },
 }); // Messages.Extended.prototype
 
-
-
 /**
  * The JavaScriptEvalOutput message.
  *
  * @constructor
  * @extends Messages.Extended
  * @param object evalResponse
  *        The evaluation response packet received from the server.
  * @param string [errorMessage]
  *        Optional error message to display.
  * @param string [errorDocLink]
  * Optional error doc URL to link to.
  */
-Messages.JavaScriptEvalOutput = function (evalResponse, errorMessage, errorDocLink)
-{
+Messages.JavaScriptEvalOutput = function (evalResponse, errorMessage, errorDocLink) {
   let severity = "log", msg, quoteStrings = true;
 
   // Store also the response packet from the back end. It might
   // be useful to extensions customizing the console output.
   this.response = evalResponse;
 
   if (typeof (errorMessage) !== "undefined") {
     severity = "error";
@@ -1396,18 +1361,17 @@ Messages.JavaScriptEvalOutput.prototype 
 /**
  * The ConsoleGeneric message is used for console API calls.
  *
  * @constructor
  * @extends Messages.Extended
  * @param object packet
  *        The Console API call packet received from the server.
  */
-Messages.ConsoleGeneric = function (packet)
-{
+Messages.ConsoleGeneric = function (packet) {
   let options = {
     className: "cm-s-mozilla",
     timestamp: packet.timeStamp,
     category: packet.category || "webdev",
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     prefix: packet.prefix,
     private: packet.private,
     filterDuplicates: true,
@@ -1436,36 +1400,33 @@ Messages.ConsoleGeneric = function (pack
   this._repeatID.styles = packet.styles;
   this.stack = this._repeatID.stacktrace = packet.stacktrace;
   this._styles = packet.styles || [];
 };
 
 Messages.ConsoleGeneric.prototype = extend(Messages.Extended.prototype, {
   _styles: null,
 
-  _renderBodyPieceSeparator: function ()
-  {
+  _renderBodyPieceSeparator: function () {
     return this.document.createTextNode(" ");
   },
 
-  render: function ()
-  {
+  render: function () {
     let result = this.document.createDocumentFragment();
     this._renderBodyPieces(result);
 
     this._message = result;
     this._stacktrace = null;
 
     Messages.Simple.prototype.render.call(this);
 
     return this;
   },
 
-  _renderBodyPieces: function (container)
-  {
+  _renderBodyPieces: function (container) {
     let lastStyle = null;
     let stylePieces = this._styles.length > 0 ? this._styles.length : 1;
 
     for (let i = 0; i < this._messagePieces.length; i++) {
       // Pieces with an associated style definition come from "%c" formatting.
       // For body pieces beyond that, add a separator before each one.
       if (i >= stylePieces) {
         container.appendChild(this._renderBodyPieceSeparator());
@@ -1480,18 +1441,17 @@ Messages.ConsoleGeneric.prototype = exte
 
       container.appendChild(this._renderBodyPiece(piece, lastStyle));
     }
 
     this._messagePieces = null;
     this._styles = null;
   },
 
-  _renderBodyPiece: function (piece, style)
-  {
+  _renderBodyPiece: function (piece, style) {
     // Skip quotes for top-level strings.
     let options = { noStringQuotes: true };
     let elem = Messages.Extended.prototype._renderBodyPiece.call(this, piece, options);
     let result = elem;
 
     if (style) {
       if (elem.nodeType == nodeConstants.ELEMENT_NODE) {
         elem.style = style;
@@ -1514,18 +1474,17 @@ Messages.ConsoleGeneric.prototype = exte
    * - only some of the properties are allowed, based on a whitelist. See
    *   RE_ALLOWED_STYLES.
    *
    * @param string style
    *        The style string to cleanup.
    * @return string
    *         The style value after cleanup.
    */
-  cleanupStyle: function (style)
-  {
+  cleanupStyle: function (style) {
     for (let r of RE_CLEANUP_STYLES) {
       style = style.replace(r, "notallowed");
     }
 
     let dummy = this.output._dummyElement;
     if (!dummy) {
       dummy = this.output._dummyElement =
         this.document.createElementNS(XHTML_NS, "div");
@@ -1555,18 +1514,17 @@ Messages.ConsoleGeneric.prototype = exte
 /**
  * The ConsoleTrace message is used for console.trace() calls.
  *
  * @constructor
  * @extends Messages.Simple
  * @param object packet
  *        The Console API call packet received from the server.
  */
-Messages.ConsoleTrace = function (packet)
-{
+Messages.ConsoleTrace = function (packet) {
   let options = {
     className: "cm-s-mozilla",
     timestamp: packet.timeStamp,
     category: packet.category || "webdev",
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     private: packet.private,
     filterDuplicates: true,
     location: {
@@ -1596,18 +1554,17 @@ Messages.ConsoleTrace.prototype = extend
    * method. This array is cleared when the message is initialized, and
    * associated actors are released.
    *
    * @private
    * @type array
    */
   _arguments: null,
 
-  init: function ()
-  {
+  init: function () {
     let result = Messages.Simple.prototype.init.apply(this, arguments);
 
     // We ignore console.trace() arguments. Release object actors.
     if (Array.isArray(this._arguments)) {
       for (let arg of this._arguments) {
         if (WebConsoleUtils.isActorGrip(arg)) {
           this.output._releaseObject(arg.actor);
         }
@@ -1662,18 +1619,17 @@ Messages.ConsoleTrace.prototype = extend
 /**
  * The ConsoleTable message is used for console.table() calls.
  *
  * @constructor
  * @extends Messages.Extended
  * @param object packet
  *        The Console API call packet received from the server.
  */
-Messages.ConsoleTable = function (packet)
-{
+Messages.ConsoleTable = function (packet) {
   let options = {
     className: "cm-s-mozilla",
     timestamp: packet.timeStamp,
     category: packet.category || "webdev",
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     private: packet.private,
     filterDuplicates: false,
     location: {
@@ -1721,18 +1677,17 @@ Messages.ConsoleTable.prototype = extend
    * A promise that resolves when the table data is ready or null if invalid
    * arguments are provided.
    *
    * @private
    * @type promise|null
    */
   _populatePromise: null,
 
-  init: function ()
-  {
+  init: function () {
     let result = Messages.Extended.prototype.init.apply(this, arguments);
     this._data = [];
     this._columns = {};
 
     this._populatePromise = this._populateTableData();
 
     return result;
   },
@@ -1741,18 +1696,17 @@ Messages.ConsoleTable.prototype = extend
    * Sets the key value pair of the id and display name for the columns in the
    * table.
    *
    * @private
    * @param array|string columns
    *        Either a string or array containing the names for the columns in
    *        the output table.
    */
-  _setColumns: function (columns)
-  {
+  _setColumns: function (columns) {
     if (columns.class == "Array") {
       let items = columns.preview.items;
 
       for (let item of items) {
         if (typeof item == "string") {
           this._columns[item] = item;
         }
       }
@@ -1764,67 +1718,66 @@ Messages.ConsoleTable.prototype = extend
   /**
    * Retrieves the table data and columns from the arguments received from the
    * server.
    *
    * @return Promise|null
    *         Returns a promise that resolves when the table data is ready or
    *         null if the arguments are invalid.
    */
-  _populateTableData: function ()
-  {
+  _populateTableData: function () {
     let deferred = promise.defer();
 
     if (this._arguments.length <= 0) {
-      return;
+      return deferred.reject();
     }
 
     let data = this._arguments[0];
     if (data.class != "Array" && data.class != "Object" &&
         data.class != "Map" && data.class != "Set" &&
         data.class != "WeakMap" && data.class != "WeakSet") {
-      return;
+      return deferred.reject();
     }
 
     let hasColumnsArg = false;
     if (this._arguments.length > 1) {
       if (data.class == "Object" || data.class == "Array") {
-        this._columns["_index"] = l10n.getStr("table.index");
+        this._columns._index = l10n.getStr("table.index");
       } else {
-        this._columns["_index"] = l10n.getStr("table.iterationIndex");
+        this._columns._index = l10n.getStr("table.iterationIndex");
       }
 
       this._setColumns(this._arguments[1]);
       hasColumnsArg = true;
     }
 
     if (data.class == "Object" || data.class == "Array") {
       // Get the object properties, and parse the key and value properties into
       // the table data and columns.
       this.client = new ObjectClient(this.output.owner.jsterm.hud.proxy.client,
           data);
-      this.client.getPrototypeAndProperties(aResponse => {
-        let {ownProperties} = aResponse;
+      this.client.getPrototypeAndProperties(response => {
+        let {ownProperties} = response;
         let rowCount = 0;
         let columnCount = 0;
 
         for (let index of Object.keys(ownProperties || {})) {
           // Avoid outputting the length property if the data argument provided
           // is an array
           if (data.class == "Array" && index == "length") {
             continue;
           }
 
           if (!hasColumnsArg) {
-            this._columns["_index"] = l10n.getStr("table.index");
+            this._columns._index = l10n.getStr("table.index");
           }
 
           if (data.class == "Array") {
-            if (index == parseInt(index)) {
-              index = parseInt(index);
+            if (index == parseInt(index, 10)) {
+              index = parseInt(index, 10);
             }
           }
 
           let property = ownProperties[index].value;
           let item = { _index: index };
 
           if (property.class == "Object" || property.class == "Array") {
             let {preview} = property;
@@ -1839,39 +1792,39 @@ Messages.ConsoleTable.prototype = extend
 
               if (!hasColumnsArg && !(key in this._columns) &&
                   (++columnCount <= TABLE_COLUMN_MAX_ITEMS)) {
                 this._columns[key] = key;
               }
             }
           } else {
             // Display the value for any non-object data input.
-            item["_value"] = this._renderValueGrip(property, { concise: true });
+            item._value = this._renderValueGrip(property, { concise: true });
 
             if (!hasColumnsArg && !("_value" in this._columns)) {
-              this._columns["_value"] = l10n.getStr("table.value");
+              this._columns._value = l10n.getStr("table.value");
             }
           }
 
           this._data.push(item);
 
           if (++rowCount == TABLE_ROW_MAX_ITEMS) {
             break;
           }
         }
 
         deferred.resolve();
       });
     } else if (data.class == "Map" || data.class == "WeakMap") {
       let entries = data.preview.entries;
 
       if (!hasColumnsArg) {
-        this._columns["_index"] = l10n.getStr("table.iterationIndex");
-        this._columns["_key"] = l10n.getStr("table.key");
-        this._columns["_value"] = l10n.getStr("table.value");
+        this._columns._index = l10n.getStr("table.iterationIndex");
+        this._columns._key = l10n.getStr("table.key");
+        this._columns._value = l10n.getStr("table.value");
       }
 
       let rowCount = 0;
       for (let [key, value] of entries) {
         let item = {
           _index: rowCount,
           _key: this._renderValueGrip(key, { concise: true }),
           _value: this._renderValueGrip(value, { concise: true })
@@ -1884,42 +1837,41 @@ Messages.ConsoleTable.prototype = extend
         }
       }
 
       deferred.resolve();
     } else if (data.class == "Set" || data.class == "WeakSet") {
       let entries = data.preview.items;
 
       if (!hasColumnsArg) {
-        this._columns["_index"] = l10n.getStr("table.iterationIndex");
-        this._columns["_value"] = l10n.getStr("table.value");
+        this._columns._index = l10n.getStr("table.iterationIndex");
+        this._columns._value = l10n.getStr("table.value");
       }
 
       let rowCount = 0;
       for (let entry of entries) {
         let item = {
-          _index : rowCount,
+          _index: rowCount,
           _value: this._renderValueGrip(entry, { concise: true })
         };
 
         this._data.push(item);
 
         if (++rowCount == TABLE_ROW_MAX_ITEMS) {
           break;
         }
       }
 
       deferred.resolve();
     }
 
     return deferred.promise;
   },
 
-  render: function ()
-  {
+  render: function () {
     this._attachment = this._renderTable();
     Messages.Extended.prototype.render.apply(this, arguments);
     this.element.setAttribute("open", true);
     return this;
   },
 
   _renderMessage: function () {
     let cmvar = this.document.createElementNS(XHTML_NS, "span");
@@ -1978,18 +1930,17 @@ var Widgets = {};
 
 /**
  * The base widget class.
  *
  * @constructor
  * @param object message
  *        The owning message.
  */
-Widgets.BaseWidget = function (message)
-{
+Widgets.BaseWidget = function (message) {
   this.message = message;
 };
 
 Widgets.BaseWidget.prototype = {
   /**
    * The owning message object.
    * @type object
    */
@@ -2052,18 +2003,17 @@ Widgets.BaseWidget.prototype = {
    *        of the new DOM element. Otherwise, the value becomes the
    *        .textContent of the new DOM element.
    * @param string [textContent]
    *        If this argument is provided the value is used as the textContent of
    *        the new DOM element.
    * @return DOMElement
    *         The new DOM element.
    */
-  el: function (tagNameIdAndClasses)
-  {
+  el: function (tagNameIdAndClasses) {
     let attrs, text;
     if (typeof arguments[1] == "object") {
       attrs = arguments[1];
       text = arguments[2];
     } else {
       text = arguments[1];
     }
 
@@ -2094,72 +2044,67 @@ Widgets.BaseWidget.prototype = {
  * The timestamp widget.
  *
  * @constructor
  * @param object message
  *        The owning message.
  * @param number timestamp
  *        The UNIX timestamp to display.
  */
-Widgets.MessageTimestamp = function (message, timestamp)
-{
+Widgets.MessageTimestamp = function (message, timestamp) {
   Widgets.BaseWidget.call(this, message);
   this.timestamp = timestamp;
 };
 
 Widgets.MessageTimestamp.prototype = extend(Widgets.BaseWidget.prototype, {
   /**
    * The UNIX timestamp.
    * @type number
    */
   timestamp: 0,
 
-  render: function ()
-  {
+  render: function () {
     if (this.element) {
       return this;
     }
 
     this.element = this.document.createElementNS(XHTML_NS, "span");
     this.element.className = "timestamp devtools-monospace";
     this.element.textContent = l10n.timestampString(this.timestamp) + " ";
 
     return this;
   },
 }); // Widgets.MessageTimestamp.prototype
 
-
 /**
  * The URLString widget, for rendering strings where at least one token is a
  * URL.
  *
  * @constructor
  * @param object message
  *        The owning message.
  * @param string str
  *        The string, which contains at least one valid URL.
  * @param string unshortenedStr
  *        The unshortened form of the string, if it was shortened.
  */
-Widgets.URLString = function (message, str, unshortenedStr)
-{
+Widgets.URLString = function (message, str, unshortenedStr) {
   Widgets.BaseWidget.call(this, message);
   this.str = str;
   this.unshortenedStr = unshortenedStr;
 };
 
 Widgets.URLString.prototype = extend(Widgets.BaseWidget.prototype, {
   /**
    * The string to format, which contains at least one valid URL.
    * @type string
    */
   str: "",
 
-  render: function ()
-  {
+  render: function () {
     if (this.element) {
       return this;
     }
 
     // The rendered URLString will be a <span> containing a number of text
     // <spans> for non-URL tokens and <a>'s for URL tokens.
     this.element = this.el("span", {
       class: "console-string"
@@ -2183,32 +2128,32 @@ Widgets.URLString.prototype = extend(Wid
         }
         this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
         textStart = tokenStart + token.length;
         this.element.appendChild(this._renderURL(token, unshortenedToken));
       }
     }
 
     // Clean up any non-URL text at the end of the source string.
-    this.element.appendChild(this._renderText(this.str.slice(textStart, this.str.length)));
+    const rendered = this._renderText(this.str.slice(textStart, this.str.length));
+    this.element.appendChild(rendered);
     this.element.appendChild(this._renderText("\""));
 
     return this;
   },
 
   /**
    * Determines whether a grip is a string containing a URL.
    *
    * @param string grip
    *        The grip, which may contain a URL.
    * @return boolean
    *         Whether the grip is a string containing a URL.
    */
-  containsURL: function (grip)
-  {
+  containsURL: function (grip) {
     if (typeof grip != "string") {
       return false;
     }
 
     let tokens = grip.split(/\s+/);
     return tokens.some(this._isURL);
   },
 
@@ -2237,18 +2182,17 @@ Widgets.URLString.prototype = extend(Wid
    *
    * @param string url
    *        The string to be rendered as a url.
    * @param string fullUrl
    *        The unshortened form of the URL, if it was shortened.
    * @return DOMElement
    *         An element containing the rendered string.
    */
-  _renderURL: function (url, fullUrl)
-  {
+  _renderURL: function (url, fullUrl) {
     let unshortened = fullUrl || url;
     let result = this.el("a", {
       class: "url",
       title: unshortened,
       href: unshortened,
       draggable: false
     }, url);
     this.message._addLinkCallback(result);
@@ -2268,76 +2212,70 @@ Widgets.URLString.prototype = extend(Wid
  *        The owning message.
  * @param object objectActor
  *        The ObjectActor to display.
  * @param object [options]
  *        Options for displaying the given ObjectActor. See
  *        Messages.Extended.prototype._renderValueGrip for the available
  *        options.
  */
-Widgets.JSObject = function (message, objectActor, options = {})
-{
+Widgets.JSObject = function (message, objectActor, options = {}) {
   Widgets.BaseWidget.call(this, message);
   this.objectActor = objectActor;
   this.options = options;
   this._onClick = this._onClick.bind(this);
 };
 
 Widgets.JSObject.prototype = extend(Widgets.BaseWidget.prototype, {
   /**
    * The ObjectActor displayed by the widget.
    * @type object
    */
   objectActor: null,
 
-  render: function ()
-  {
+  render: function () {
     if (!this.element) {
       this._render();
     }
 
     return this;
   },
 
-  _render: function ()
-  {
+  _render: function () {
     let str = VariablesView.getString(this.objectActor, this.options);
     let className = this.message.getClassNameForValueGrip(this.objectActor);
     if (!className && this.objectActor.class == "Object") {
       className = "cm-variable";
     }
 
     this.element = this._anchor(str, { className: className });
   },
 
   /**
    * Render a concise representation of an object.
    */
-  _renderConciseObject: function ()
-  {
+  _renderConciseObject: function () {
     this.element = this._anchor(this.objectActor.class,
                                 { className: "cm-variable" });
   },
 
   /**
    * Render the `<class> { ` prefix of an object.
    */
-  _renderObjectPrefix: function ()
-  {
+  _renderObjectPrefix: function () {
     let { kind } = this.objectActor.preview;
     this.element = this.el("span.kind-" + kind);
     this._anchor(this.objectActor.class, { className: "cm-variable" });
     this._text(" { ");
   },
 
   /**
    * Render the ` }` suffix of an object.
    */
-  _renderObjectSuffix: function ()
-  {
+  _renderObjectSuffix: function () {
     this._text(" }");
   },
 
   /**
    * Render an object property.
    *
    * @param String key
    *        The property name.
@@ -2347,57 +2285,63 @@ Widgets.JSObject.prototype = extend(Widg
    *        The container node to render to.
    * @param Boolean needsComma
    *        True if there was another property before this one and we need to
    *        separate them with a comma.
    * @param Boolean valueIsText
    *        Add the value as is, don't treat it as a grip and pass it to
    *        `_renderValueGrip`.
    */
-  _renderObjectProperty: function (key, value, container, needsComma, valueIsText = false)
-  {
+  _renderObjectProperty: function (
+    key,
+    value,
+    container,
+    needsComma,
+    valueIsText = false
+  ) {
     if (needsComma) {
       this._text(", ");
     }
 
     container.appendChild(this.el("span.cm-property", key));
     this._text(": ");
 
     if (valueIsText) {
       this._text(value);
     } else {
-      let valueElem = this.message._renderValueGrip(value, { concise: true, shorten: true });
+      let valueElem = this.message._renderValueGrip(value, {
+        concise: true,
+        shorten: true
+      });
       container.appendChild(valueElem);
     }
   },
 
   /**
    * Render this object's properties.
    *
    * @param nsIDOMNode container
    *        The container node to render to.
    * @param Boolean needsComma
    *        True if there was another property before this one and we need to
    *        separate them with a comma.
    */
-  _renderObjectProperties: function (container, needsComma)
-  {
+  _renderObjectProperties: function (container, needsComma) {
     let { preview } = this.objectActor;
     let { ownProperties, safeGetterValues } = preview;
 
     let shown = 0;
 
     let getValue = desc => {
       if (desc.get) {
         return "Getter";
       } else if (desc.set) {
         return "Setter";
-      } else {
-        return desc.value;
       }
+      return desc.value;
     };
 
     for (let key of Object.keys(ownProperties || {})) {
       this._renderObjectProperty(key, getValue(ownProperties[key]), container,
                                  shown > 0 || needsComma,
                                  ownProperties[key].get || ownProperties[key].set);
       shown++;
     }
@@ -2435,18 +2379,17 @@ Widgets.JSObject.prototype = extend(Widg
    *        on the anchor open the link in a new tab.
    *        - appendTo (DOMElement): append the element to the given DOM
    *        element. If not provided, the anchor is appended to |this.element|
    *        if it is available. If |appendTo| is provided and if it is a falsy
    *        value, the anchor is not appended to any element.
    * @return DOMElement
    *         The DOM element of the new anchor.
    */
-  _anchor: function (text, options = {})
-  {
+  _anchor: function (text, options = {}) {
     if (!options.onClick) {
       // If the anchor has an URL, open it in a new tab. If not, show the
       // current object actor.
       options.onClick = options.href ? this._onClickAnchor : this._onClick;
     }
 
     options.onContextMenu = options.onContextMenu || this._onContextMenu;
 
@@ -2464,27 +2407,25 @@ Widgets.JSObject.prototype = extend(Widg
       options.appendTo.appendChild(anchor);
     } else if (!("appendTo" in options) && this.element) {
       this.element.appendChild(anchor);
     }
 
     return anchor;
   },
 
-  openObjectInVariablesView: function ()
-  {
+  openObjectInVariablesView: function () {
     this.output.openVariablesView({
       label: VariablesView.getString(this.objectActor, { concise: true }),
       objectActor: this.objectActor,
       autofocus: true,
     });
   },
 
-  storeObjectInWindow: function ()
-  {
+  storeObjectInWindow: function () {
     let evalString = `{ let i = 0;
       while (this.hasOwnProperty("temp" + i) && i < 1000) {
         i++;
       }
       this["temp" + i] = _self;
       "temp" + i;
     }`;
     let options = {
@@ -2496,18 +2437,17 @@ Widgets.JSObject.prototype = extend(Widg
       this.output.owner.jsterm.setInputValue(res.result);
     });
   },
 
   /**
    * The click event handler for objects shown inline.
    * @private
    */
-  _onClick: function ()
-  {
+  _onClick: function () {
     this.openObjectInVariablesView();
   },
 
   _onContextMenu: function (ev) {
     // TODO offer a nice API for the context menu.
     // Probably worth to take a look at Firebug's way
     // https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
     let doc = ev.target.ownerDocument;
@@ -2543,18 +2483,17 @@ Widgets.JSObject.prototype = extend(Widg
    *
    * @private
    * @param string str
    *        String to add.
    * @param DOMElement [target = this.element]
    *        Optional DOM element to append the string to. The default is
    *        this.element.
    */
-  _text: function (str, target = this.element)
-  {
+  _text: function (str, target = this.element) {
     target.appendChild(this.document.createTextNode(str));
   },
 }); // Widgets.JSObject.prototype
 
 Widgets.ObjectRenderers = {};
 Widgets.ObjectRenderers.byKind = {};
 Widgets.ObjectRenderers.byClass = {};
 
@@ -2579,18 +2518,17 @@ Widgets.ObjectRenderers.byClass = {};
  *        - initialize (function, optional): the constructor of the renderer
  *        widget. This function is invoked with the following arguments: the
  *        owner message object instance, the object actor grip to display, and
  *        an options object. See Messages.Extended.prototype._renderValueGrip()
  *        for details about the options object.
  *        - render (function, required): the method that displays the given
  *        object actor.
  */
-Widgets.ObjectRenderers.add = function (obj)
-{
+Widgets.ObjectRenderers.add = function (obj) {
   let extendObj = obj.extends || Widgets.JSObject;
 
   let constructor = function () {
     if (obj.initialize) {
       obj.initialize.apply(this, arguments);
     } else {
       extendObj.apply(this, arguments);
     }
@@ -2616,25 +2554,23 @@ Widgets.ObjectRenderers.add = function (
   } else if (obj.byKind) {
     Widgets.ObjectRenderers.byKind[obj.byKind] = constructor;
   } else {
     throw new Error("You are adding an object renderer without any byClass or " +
                     "byKind property.");
   }
 };
 
-
 /**
  * The widget used for displaying Date objects.
  */
 Widgets.ObjectRenderers.add({
   byClass: "Date",
 
-  render: function ()
-  {
+  render: function () {
     let {preview} = this.objectActor;
     this.element = this.el("span.class-" + this.objectActor.class);
 
     let anchorText = this.objectActor.class;
     let anchorClass = "cm-variable";
     if (preview && "timestamp" in preview && typeof preview.timestamp != "number") {
       anchorText = new Date(preview.timestamp).toString(); // invalid date
       anchorClass = "";
@@ -2654,27 +2590,24 @@ Widgets.ObjectRenderers.add({
 });
 
 /**
  * The widget used for displaying Function objects.
  */
 Widgets.ObjectRenderers.add({
   byClass: "Function",
 
-  render: function ()
-  {
+  render: function () {
     let grip = this.objectActor;
     this.element = this.el("span.class-" + this.objectActor.class);
 
     // TODO: Bug 948484 - support arrow functions and ES6 generators
     let name = grip.userDisplayName || grip.displayName || grip.name || "";
     name = VariablesView.getString(name, { noStringQuotes: true });
 
-    let str = this.options.concise ? name || "function " : "function " + name;
-
     if (this.options.concise) {
       this._anchor(name || "function", {
         className: name ? "cm-variable" : "cm-keyword",
       });
       if (!name) {
         this._text(" ");
       }
     } else if (name) {
@@ -2715,18 +2648,17 @@ Widgets.ObjectRenderers.add({
 }); // Widgets.ObjectRenderers.byClass.Function
 
 /**
  * The widget used for displaying ArrayLike objects.
  */
 Widgets.ObjectRenderers.add({
   byKind: "ArrayLike",
 
-  render: function ()
-  {
+  render: function () {
     let {preview} = this.objectActor;
     let {items} = preview;
     this.element = this.el("span.kind-" + preview.kind);
 
     this._anchor(this.objectActor.class, { className: "cm-variable" });
 
     if (!items || this.options.concise) {
       this._text("[");
@@ -2740,18 +2672,17 @@ Widgets.ObjectRenderers.add({
     let isFirst = true;
     let emptySlots = 0;
     // A helper that renders a comma between items if isFirst == false.
     let renderSeparator = () => !isFirst && this._text(", ");
 
     for (let item of items) {
       if (item === null) {
         emptySlots++;
-      }
-      else {
+      } else {
         renderSeparator();
         isFirst = false;
 
         if (emptySlots) {
           this._renderEmptySlots(emptySlots);
           emptySlots = 0;
         }
 
@@ -2770,37 +2701,38 @@ Widgets.ObjectRenderers.add({
       this._text(", ");
 
       let n = preview.length - shown;
       let str = VariablesView.stringifiers._getNMoreString(n);
       this._anchor(str);
     }
 
     this._text(" ]");
+
+    return this;
   },
 
-  _renderEmptySlots: function (aNumSlots, aAppendComma = true) {
+  _renderEmptySlots: function (numSlots, appendComma = true) {
     let slotLabel = l10n.getStr("emptySlotLabel");
-    let slotText = PluralForm.get(aNumSlots, slotLabel);
-    this._text("<" + slotText.replace("#1", aNumSlots) + ">");
-    if (aAppendComma) {
+    let slotText = PluralForm.get(numSlots, slotLabel);
+    this._text("<" + slotText.replace("#1", numSlots) + ">");
+    if (appendComma) {
       this._text(", ");
     }
   },
 
 }); // Widgets.ObjectRenderers.byKind.ArrayLike
 
 /**
  * The widget used for displaying MapLike objects.
  */
 Widgets.ObjectRenderers.add({
   byKind: "MapLike",
 
-  render: function ()
-  {
+  render: function () {
     let {preview} = this.objectActor;
     let {entries} = preview;
 
     let container = this.element = this.el("span.kind-" + preview.kind);
     this._anchor(this.objectActor.class, { className: "cm-variable" });
 
     if (!entries || this.options.concise) {
       if (typeof preview.size == "number") {
@@ -2853,24 +2785,22 @@ Widgets.ObjectRenderers.add({
 }); // Widgets.ObjectRenderers.byKind.MapLike
 
 /**
  * The widget used for displaying objects with a URL.
  */
 Widgets.ObjectRenderers.add({
   byKind: "ObjectWithURL",
 
-  render: function ()
-  {
+  render: function () {
     this.element = this._renderElement(this.objectActor,
                                        this.objectActor.preview.url);
   },
 
-  _renderElement: function (objectActor, url)
-  {
+  _renderElement: function (objectActor, url) {
     let container = this.el("span.kind-" + objectActor.preview.kind);
 
     this._anchor(objectActor.class, {
       className: "cm-variable",
       appendTo: container,
     });
 
     if (!VariablesView.isFalsy({ value: url })) {
@@ -2884,18 +2814,17 @@ Widgets.ObjectRenderers.add({
 }); // Widgets.ObjectRenderers.byKind.ObjectWithURL
 
 /**
  * The widget used for displaying objects with a string next to them.
  */
 Widgets.ObjectRenderers.add({
   byKind: "ObjectWithText",
 
-  render: function ()
-  {
+  render: function () {
     let {preview} = this.objectActor;
     this.element = this.el("span.kind-" + preview.kind);
 
     this._anchor(this.objectActor.class, { className: "cm-variable" });
 
     if (!this.options.concise) {
       this._text(" ");
       this.element.appendChild(this.el("span.theme-fg-color6",
@@ -2905,18 +2834,17 @@ Widgets.ObjectRenderers.add({
 });
 
 /**
  * The widget used for displaying DOM event previews.
  */
 Widgets.ObjectRenderers.add({
   byKind: "DOMEvent",
 
-  render: function ()
-  {
+  render: function () {
     let {preview} = this.objectActor;
 
     let container = this.element = this.el("span.kind-" + preview.kind);
 
     this._anchor(preview.type || this.objectActor.class,
                  { className: "cm-variable" });
 
     if (this.options.concise) {
@@ -2987,24 +2915,24 @@ Widgets.ObjectRenderers.add({
       case nodeConstants.DOCUMENT_FRAGMENT_NODE:
       case nodeConstants.ELEMENT_NODE:
         return true;
       default:
         return false;
     }
   },
 
-  render: function ()
-  {
-    switch (this.objectActor.preview.nodeType) {
+  render: function () {
+    const {preview} = this.objectActor;
+
+    switch (preview.nodeType) {
       case nodeConstants.DOCUMENT_NODE:
         this._renderDocumentNode();
         break;
       case nodeConstants.ATTRIBUTE_NODE: {
-        let {preview} = this.objectActor;
         this.element = this.el("span.attributeNode.kind-" + preview.kind);
         let attr = this._renderAttributeNode(preview.nodeName, preview.value, true);
         this.element.appendChild(attr);
         break;
       }
       case nodeConstants.TEXT_NODE:
         this._renderTextNode();
         break;
@@ -3017,69 +2945,64 @@ Widgets.ObjectRenderers.add({
       case nodeConstants.ELEMENT_NODE:
         this._renderElementNode();
         break;
       default:
         throw new Error("Unsupported nodeType: " + preview.nodeType);
     }
   },
 
-  _renderDocumentNode: function ()
-  {
+  _renderDocumentNode: function () {
     let fn =
       Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
     this.element = fn.call(this, this.objectActor,
                            this.objectActor.preview.location);
     this.element.classList.add("documentNode");
   },
 
-  _renderAttributeNode: function (nodeName, nodeValue, addLink)
-  {
+  _renderAttributeNode: function (nodeName, nodeValue, addLink) {
     let value = VariablesView.getString(nodeValue, { noStringQuotes: true });
 
     let fragment = this.document.createDocumentFragment();
     if (addLink) {
       this._anchor(nodeName, { className: "cm-attribute", appendTo: fragment });
     } else {
       fragment.appendChild(this.el("span.cm-attribute", nodeName));
     }
 
     this._text("=\"", fragment);
     fragment.appendChild(this.el("span.theme-fg-color6", escapeHTML(value)));
     this._text("\"", fragment);
 
     return fragment;
   },
 
-  _renderTextNode: function ()
-  {
+  _renderTextNode: function () {
     let {preview} = this.objectActor;
     this.element = this.el("span.textNode.kind-" + preview.kind);
 
     this._anchor(preview.nodeName, { className: "cm-variable" });
     this._text(" ");
 
     let text = VariablesView.getString(preview.textContent);
     this.element.appendChild(this.el("span.console-string", text));
   },
 
-  _renderCommentNode: function ()
-  {
+  _renderCommentNode: function () {
     let {preview} = this.objectActor;
     let comment = "<!-- " + VariablesView.getString(preview.textContent, {
       noStringQuotes: true,
     }) + " -->";
 
     this.element = this._anchor(comment, {
       className: "kind-" + preview.kind + " commentNode cm-comment",
     });
   },
 
-  _renderDocumentFragmentNode: function ()
-  {
+  _renderDocumentFragmentNode: function () {
     let {preview} = this.objectActor;
     let {childNodes} = preview;
     let container = this.element = this.el("span.documentFragmentNode.kind-" +
                                            preview.kind);
 
     this._anchor(this.objectActor.class, { className: "cm-variable" });
 
     if (!childNodes || this.options.concise) {
@@ -3108,38 +3031,38 @@ Widgets.ObjectRenderers.add({
       let n = preview.childNodesLength - shown;
       let str = VariablesView.stringifiers._getNMoreString(n);
       this._anchor(str);
     }
 
     this._text(" ]");
   },
 
-  _renderElementNode: function ()
-  {
-    let doc = this.document;
+  _renderElementNode: function () {
     let {attributes, nodeName} = this.objectActor.preview;
 
-    this.element = this.el("span." + "kind-" + this.objectActor.preview.kind + ".elementNode");
+    this.element = this.el("span." + "kind-" + this.objectActor.preview.kind +
+                           ".elementNode");
 
     this._text("<");
     let openTag = this.el("span.cm-tag");
     this.element.appendChild(openTag);
 
     let tagName = this._anchor(nodeName, {
       className: "cm-tag",
       appendTo: openTag
     });
 
     if (this.options.concise) {
       if (attributes.id) {
         tagName.appendChild(this.el("span.cm-attribute", "#" + attributes.id));
       }
       if (attributes.class) {
-        tagName.appendChild(this.el("span.cm-attribute", "." + attributes.class.split(/\s+/g).join(".")));
+        const joinedClasses = "." + attributes.class.split(/\s+/g).join(".");
+        tagName.appendChild(this.el("span.cm-attribute", joinedClasses));
       }
     } else {
       for (let name of Object.keys(attributes)) {
         let attr = this._renderAttributeNode(" " + name, attributes[name]);
         this.element.appendChild(attr);
       }
     }
 
@@ -3157,18 +3080,17 @@ Widgets.ObjectRenderers.add({
    * will attach mouseover/out event listeners to do so, and the inspector icon
    * to open the node in the inspector.
    * @return a promise that resolves when the node has been linked to the
    * inspector, or rejects if it wasn't (either if no toolbox could be found to
    * access the inspector, or if the node isn't present in the inspector, i.e.
    * if the node is in a DocumentFragment or not part of the tree, or not of
    * type nodeConstants.ELEMENT_NODE).
    */
-  linkToInspector: Task.async(function* ()
-  {
+  linkToInspector: Task.async(function* () {
     if (this._linkedToInspector) {
       return;
     }
 
     // Checking the node type
     if (this.objectActor.preview.nodeType !== nodeConstants.ELEMENT_NODE) {
       throw new Error("The object cannot be linked to the inspector as it " +
         "isn't an element node");
@@ -3179,17 +3101,18 @@ Widgets.ObjectRenderers.add({
     this.toolbox = gDevTools.getToolbox(target);
     if (!this.toolbox) {
       // In cases like the browser console, there is no toolbox.
       return;
     }
 
     // Checking that the inspector supports the node
     yield this.toolbox.initInspector();
-    this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this.objectActor.actor);
+    this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(
+      this.objectActor.actor);
     if (!this._nodeFront) {
       throw new Error("The object cannot be linked to the inspector, the " +
         "corresponding nodeFront could not be found");
     }
 
     // At this stage, the message may have been cleared already
     if (!this.document) {
       throw new Error("The object cannot be linked to the inspector, the " +
@@ -3214,68 +3137,66 @@ Widgets.ObjectRenderers.add({
     this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
   }),
 
   /**
    * Highlight the DOMNode corresponding to the ObjectActor in the page.
    * @return a promise that resolves when the node has been highlighted, or
    * rejects if the node cannot be highlighted (detached from the DOM)
    */
-  highlightDomNode: Task.async(function* ()
-  {
+  highlightDomNode: Task.async(function* () {
     yield this.linkToInspector();
     let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
     if (isAttached) {
       yield this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
     } else {
-      throw null;
+      throw new Error("Node is not attached.");
     }
   }),
 
   /**
    * Unhighlight a previously highlit node
    * @see highlightDomNode
    * @return a promise that resolves when the highlighter has been hidden
    */
-  unhighlightDomNode: function ()
-  {
+  unhighlightDomNode: function () {
     return this.linkToInspector().then(() => {
       return this.toolbox.highlighterUtils.unhighlight();
     }).then(null, e => console.error(e));
   },
 
   /**
    * Open the DOMNode corresponding to the ObjectActor in the inspector panel
    * @return a promise that resolves when the inspector has been switched to
    * and the node has been selected, or rejects if the node cannot be selected
    * (detached from the DOM). Note that in any case, the inspector panel will
    * be switched to.
    */
-  openNodeInInspector: Task.async(function* ()
-  {
+  openNodeInInspector: Task.async(function* () {
     yield this.linkToInspector();
     yield this.toolbox.selectTool("inspector");
 
     let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
     if (isAttached) {
       let onReady = promise.defer();
       this.toolbox.inspector.once("inspector-updated", onReady.resolve);
       yield this.toolbox.selection.setNodeFront(this._nodeFront, "console");
       yield onReady.promise;
     } else {
-      throw null;
+      throw new Error("Node is not attached.");
     }
   }),
 
-  destroy: function ()
-  {
+  destroy: function () {
     if (this.toolbox && this._nodeFront) {
       this.element.removeEventListener("mouseover", this.highlightDomNode);
       this.element.removeEventListener("mouseout", this.unhighlightDomNode);
-      this._openInspectorNode.removeEventListener("mousedown", this.openNodeInInspector, true);
+      this._openInspectorNode.removeEventListener("mousedown",
+                                                  this.openNodeInInspector,
+                                                  true);
 
       if (this._linkedToInspector) {
         this.unhighlightDomNode().then(() => {
           this.toolbox = null;
           this._nodeFront = null;
         });
       } else {
         this.toolbox = null;
@@ -3286,18 +3207,17 @@ Widgets.ObjectRenderers.add({
 }); // Widgets.ObjectRenderers.byKind.DOMNode
 
 /**
  * The widget user for displaying Promise objects.
  */
 Widgets.ObjectRenderers.add({
   byClass: "Promise",
 
-  render: function ()
-  {
+  render: function () {
     let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
     if ((!ownProperties && !safeGetterValues) || this.options.concise) {
       this._renderConciseObject();
       return;
     }
 
     this._renderObjectPrefix();
     let container = this.element;
@@ -3370,18 +3290,17 @@ Widgets.ObjectRenderers.add({
 });
 
 /**
  * The widget used for displaying generic JS object previews.
  */
 Widgets.ObjectRenderers.add({
   byKind: "Object",
 
-  render: function ()
-  {
+  render: function () {
     let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
     if ((!ownProperties && !safeGetterValues) || this.options.concise) {
       this._renderConciseObject();
       return;
     }
 
     this._renderObjectPrefix();
     this._renderObjectProperties(this.element, false);
@@ -3395,36 +3314,34 @@ Widgets.ObjectRenderers.add({
  * @constructor
  * @param object message
  *        The owning message.
  * @param object longStringActor
  *        The LongStringActor to display.
  * @param object options
  *        Options, such as noStringQuotes
  */
-Widgets.LongString = function (message, longStringActor, options)
-{
+Widgets.LongString = function (message, longStringActor, options) {
   Widgets.BaseWidget.call(this, message);
   this.longStringActor = longStringActor;
   this.noStringQuotes = (options && "noStringQuotes" in options) ?
     options.noStringQuotes : !this.message._quoteStrings;
 
   this._onClick = this._onClick.bind(this);
   this._onSubstring = this._onSubstring.bind(this);
 };
 
 Widgets.LongString.prototype = extend(Widgets.BaseWidget.prototype, {
   /**
    * The LongStringActor displayed by the widget.
    * @type object
    */
   longStringActor: null,
 
-  render: function ()
-  {
+  render: function () {
     if (this.element) {
       return this;
     }
 
     let result = this.element = this.document.createElementNS(XHTML_NS, "span");
     result.className = "longString console-string";
     this._renderString(this.longStringActor.initial);
     result.appendChild(this._renderEllipsis());
@@ -3433,64 +3350,60 @@ Widgets.LongString.prototype = extend(Wi
   },
 
   /**
    * Render the long string in the widget element.
    * @private
    * @param string str
    *        The string to display.
    */
-  _renderString: function (str)
-  {
+  _renderString: function (str) {
     this.element.textContent = VariablesView.getString(str, {
       noStringQuotes: this.noStringQuotes,
       noEllipsis: true,
     });
   },
 
   /**
    * Render the anchor ellipsis that allows the user to expand the long string.
    *
    * @private
    * @return Element
    */
-  _renderEllipsis: function ()
-  {
+  _renderEllipsis: function () {
     let ellipsis = this.document.createElementNS(XHTML_NS, "a");
     ellipsis.className = "longStringEllipsis";
     ellipsis.textContent = l10n.getStr("longStringEllipsis");
     ellipsis.href = "#";
     ellipsis.draggable = false;
     this.message._addLinkCallback(ellipsis, this._onClick);
 
     return ellipsis;
   },
 
   /**
    * The click event handler for the ellipsis shown after the short string. This
    * function expands the element to show the full string.
    * @private
    */
-  _onClick: function ()
-  {
+  _onClick: function () {
     let longString = this.output.webConsoleClient.longString(this.longStringActor);
     let toIndex = Math.min(longString.length, MAX_LONG_STRING_LENGTH);
 
     longString.substring(longString.initial.length, toIndex, this._onSubstring);
   },
 
   /**
    * The longString substring response callback.
    *
    * @private
    * @param object response
    *        Response packet.
    */
-  _onSubstring: function (response)
-  {
+  _onSubstring: function (response) {
     if (response.error) {
       console.error("LongString substring failure: " + response.error);
       return;
     }
 
     this.element.lastChild.remove();
     this.element.classList.remove("longString");
 
@@ -3507,27 +3420,25 @@ Widgets.LongString.prototype = extend(Wi
       this._logWarningAboutStringTooLong();
     }
   },
 
   /**
    * Inform user that the string he tries to view is too long.
    * @private
    */
-  _logWarningAboutStringTooLong: function ()
-  {
+  _logWarningAboutStringTooLong: function () {
     let msg = new Messages.Simple(l10n.getStr("longStringTooLong"), {
       category: "output",
       severity: "warning",
     });
     this.output.addMessage(msg);
   },
 }); // Widgets.LongString.prototype
 
-
 /**
  * The stacktrace widget.
  *
  * @constructor
  * @extends Widgets.BaseWidget
  * @param object message
  *        The owning message.
  * @param array stacktrace
@@ -3576,18 +3487,17 @@ Widgets.Stacktrace.prototype = extend(Wi
  * @param object message
  *        The owning message.
  * @param array data
  *        Array of objects that holds the data to log in the table.
  * @param object columns
  *        Object containing the key value pair of the id and display name for
  *        the columns in the table.
  */
-Widgets.Table = function (message, data, columns)
-{
+Widgets.Table = function (message, data, columns) {
   Widgets.BaseWidget.call(this, message);
   this.data = data;
   this.columns = columns;
 };
 
 Widgets.Table.prototype = extend(Widgets.BaseWidget.prototype, {
   /**
    * Array of objects that holds the data to output in the table.
@@ -3620,17 +3530,16 @@ Widgets.Table.prototype = extend(Widgets
     for (let row of this.data) {
       this.table.push(row);
     }
 
     return this;
   }
 }); // Widgets.Table.prototype
 
-function gSequenceId()
-{
+function gSequenceId() {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
 
 exports.ConsoleOutput = ConsoleOutput;
 exports.Messages = Messages;
 exports.Widgets = Widgets;
--- a/devtools/shim/DevToolsShim.jsm
+++ b/devtools/shim/DevToolsShim.jsm
@@ -148,16 +148,40 @@ this.DevToolsShim = {
   unregisterTheme: function (theme) {
     if (this.isInstalled()) {
       this.gDevTools.unregisterTheme(theme);
     } else {
       removeItem(this.themes, t => t === theme);
     }
   },
 
+  /**
+   * Called from SessionStore.jsm in mozilla-central when saving the current state.
+   *
+   * @return {Array} array of currently opened scratchpad windows. Empty array if devtools
+   *         are not installed
+   */
+  getOpenedScratchpads: function () {
+    if (!this.isInstalled()) {
+      return [];
+    }
+    return this.gDevTools.getOpenedScratchpads();
+  },
+
+  /**
+   * Called from SessionStore.jsm in mozilla-central when restoring a state that contained
+   * opened scratchpad windows.
+   */
+  restoreScratchpadSession: function (scratchpads) {
+    if (!this.isInstalled()) {
+      return;
+    }
+    this.gDevTools.restoreScratchpadSession(scratchpads);
+  },
+
   _onDevToolsRegistered: function () {
     // Register all pending event listeners on the real gDevTools object.
     for (let [event, listener] of this.listeners) {
       this.gDevTools.on(event, listener);
     }
 
     for (let tool of this.tools) {
       this.gDevTools.registerTool(tool);
--- a/devtools/shim/tests/unit/test_devtools_shim.js
+++ b/devtools/shim/tests/unit/test_devtools_shim.js
@@ -17,16 +17,18 @@ function createMockDevTools() {
   let methods = [
     "on",
     "off",
     "registerTool",
     "registerTheme",
     "unregisterTool",
     "unregisterTheme",
     "emit",
+    "getOpenedScratchpads",
+    "restoreScratchpadSession",
   ];
 
   let mock = {
     callLog: {}
   };
 
   for (let method of methods) {
     // Create a stub for method, that only pushes its arguments in the inner callLog
@@ -183,16 +185,40 @@ function test_events() {
   DevToolsShim.register(mock);
   checkCalls(mock, "emit", 1, ["devtools-registered"]);
 
   // Check emit is called once with the devtools-unregistered event.
   DevToolsShim.unregister();
   checkCalls(mock, "emit", 2, ["devtools-unregistered"]);
 }
 
+function test_scratchpad_apis() {
+  ok(!DevToolsShim.isInstalled(), "DevTools are not installed");
+
+  // Check that restoreScratchpadSession doesn't crash.
+  DevToolsShim.restoreScratchpadSession([{}]);
+
+  let scratchpads = DevToolsShim.getOpenedScratchpads();
+  equal(scratchpads.length, 0,
+      "getOpenedScratchpads returns [] when DevTools are not installed");
+
+  let mock = createMockDevTools();
+  DevToolsShim.register(mock);
+
+  // Check that calls to restoreScratchpadSession are not held.
+  checkCalls(mock, "restoreScratchpadSession", 0);
+
+  DevToolsShim.getOpenedScratchpads();
+  checkCalls(mock, "getOpenedScratchpads", 1, []);
+
+  let scratchpadSessions = [{}];
+  DevToolsShim.restoreScratchpadSession(scratchpadSessions);
+  checkCalls(mock, "restoreScratchpadSession", 1, [scratchpadSessions]);
+}
+
 function run_test() {
   test_register_unregister();
   DevToolsShim.unregister();
 
   test_on_is_forwarded_to_devtools();
   DevToolsShim.unregister();
 
   test_off_called_before_registering_devtools();
@@ -202,10 +228,13 @@ function run_test() {
   DevToolsShim.unregister();
 
   test_registering_tool();
   DevToolsShim.unregister();
 
   test_registering_theme();
   DevToolsShim.unregister();
 
+  test_scratchpad_apis();
+  DevToolsShim.unregister();
+
   test_events();
 }
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -54,16 +54,17 @@ support-files =
   mozilla/file_set-easing.html
   style/file_animation-seeking-with-current-time.html
   style/file_animation-seeking-with-start-time.html
   style/file_animation-setting-effect.html
   style/file_animation-setting-spacing.html
   style/file_composite.html
   style/file_missing-keyframe.html
   style/file_missing-keyframe-on-compositor.html
+  ../../../layout/style/test/property_database.js
   testcommon.js
 
 [css-animations/test_animations-dynamic-changes.html]
 [css-animations/test_animation-cancel.html]
 [css-animations/test_animation-computed-timing.html]
 [css-animations/test_animation-currenttime.html]
 [css-animations/test_animation-finish.html]
 [css-animations/test_animation-finished.html]
@@ -101,16 +102,17 @@ support-files =
 [document-timeline/test_request_animation_frame.html]
 [mozilla/test_cubic_bezier_limits.html]
 [mozilla/test_deferred_start.html]
 [mozilla/test_disable_animations_api_core.html]
 [mozilla/test_disabled_properties.html]
 [mozilla/test_discrete-animations.html]
 [mozilla/test_document-timeline-origin-time-range.html]
 [mozilla/test_hide_and_show.html]
+[mozilla/test_moz-prefixed-properties.html]
 [mozilla/test_set-easing.html]
 [mozilla/test_spacing_property_order.html]
 [mozilla/test_transform_limits.html]
 [mozilla/test_transition_finish_on_compositor.html]
 skip-if = toolkit == 'android'
 [mozilla/test_underlying-discrete-value.html]
 [style/test_animation-seeking-with-current-time.html]
 [style/test_animation-seeking-with-start-time.html]
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/mozilla/test_moz-prefixed-properties.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test animations of all properties that have -moz prefix</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="../testcommon.js"></script>
+  <script src="../property_database.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+"use strict";
+
+const testcases = [
+  {
+    property: "-moz-box-align"
+  },
+  {
+    property: "-moz-box-direction"
+  },
+  {
+    property: "-moz-box-orient",
+    expectedValueMap: {
+      "block-axis": "vertical",
+      "inline-axis": "horizontal",
+    }
+  },
+  {
+    property: "-moz-box-pack"
+  },
+  {
+    property: "-moz-float-edge"
+  },
+  {
+    property: "-moz-orient"
+  },
+  {
+    property: "-moz-osx-font-smoothing",
+    pref: "layout.css.osx-font-smoothing.enabled"
+  },
+  {
+    property: "-moz-user-focus"
+  },
+  {
+    property: "-moz-user-input"
+  },
+  {
+    property: "-moz-user-modify"
+  },
+  {
+    property: "-moz-window-dragging"
+  },
+];
+
+testcases.forEach(testcase => {
+  if (testcase.pref && !IsCSSPropertyPrefEnabled(testcase.pref)) {
+    return;
+  }
+
+  const property = gCSSProperties[testcase.property];
+  const values = property.initial_values.concat(property.other_values);
+  values.forEach(value => {
+    test(function(t) {
+      const container = addDiv(t);
+      const target = document.createElement("div");
+      container.appendChild(target);
+
+      container.style[property.domProp] = value;
+
+      const animation =
+        target.animate({ [property.domProp]: [value, "inherit"] },
+                       { duration: 1000, delay: -500 } );
+
+      const expectedValue =
+        testcase.expectedValueMap && testcase.expectedValueMap[value]
+        ? testcase.expectedValueMap[value] : value;
+      assert_equals(getComputedStyle(target)[property.domProp], expectedValue,
+                    `Computed style shoud be "${ expectedValue }"`);
+    }, `Test inherit value for "${ testcase.property }" `
+       + `(Parent element style is "${ value }")`);
+  });
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1899,114 +1899,25 @@ Navigator::GetUserAgent(nsPIDOMWindowInn
       return rv;
     }
     CopyASCIItoUTF16(userAgent, aUserAgent);
   }
   return NS_OK;
 }
 
 static nsCString
-ToCString(const nsString& aString)
-{
-  nsCString str("'");
-  str.Append(NS_ConvertUTF16toUTF8(aString));
-  str.AppendLiteral("'");
-  return str;
-}
-
-static nsCString
-ToCString(const MediaKeysRequirement aValue)
-{
-  nsCString str("'");
-  str.Append(nsDependentCString(MediaKeysRequirementValues::strings[static_cast<uint32_t>(aValue)].value));
-  str.AppendLiteral("'");
-  return str;
-}
-
-static nsCString
-ToCString(const MediaKeySystemMediaCapability& aValue)
-{
-  nsCString str;
-  str.AppendLiteral("{contentType=");
-  str.Append(ToCString(aValue.mContentType));
-  str.AppendLiteral(", robustness=");
-  str.Append(ToCString(aValue.mRobustness));
-  str.AppendLiteral("}");
-  return str;
-}
-
-template<class Type>
-static nsCString
-ToCString(const Sequence<Type>& aSequence)
-{
-  nsCString str;
-  str.AppendLiteral("[");
-  for (size_t i = 0; i < aSequence.Length(); i++) {
-    if (i != 0) {
-      str.AppendLiteral(",");
-    }
-    str.Append(ToCString(aSequence[i]));
-  }
-  str.AppendLiteral("]");
-  return str;
-}
-
-template<class Type>
-static nsCString
-ToCString(const Optional<Sequence<Type>>& aOptional)
-{
-  nsCString str;
-  if (aOptional.WasPassed()) {
-    str.Append(ToCString(aOptional.Value()));
-  } else {
-    str.AppendLiteral("[]");
-  }
-  return str;
-}
-
-static nsCString
-ToCString(const MediaKeySystemConfiguration& aConfig)
-{
-  nsCString str;
-  str.AppendLiteral("{label=");
-  str.Append(ToCString(aConfig.mLabel));
-
-  str.AppendLiteral(", initDataTypes=");
-  str.Append(ToCString(aConfig.mInitDataTypes));
-
-  str.AppendLiteral(", audioCapabilities=");
-  str.Append(ToCString(aConfig.mAudioCapabilities));
-
-  str.AppendLiteral(", videoCapabilities=");
-  str.Append(ToCString(aConfig.mVideoCapabilities));
-
-  str.AppendLiteral(", distinctiveIdentifier=");
-  str.Append(ToCString(aConfig.mDistinctiveIdentifier));
-
-  str.AppendLiteral(", persistentState=");
-  str.Append(ToCString(aConfig.mPersistentState));
-
-  str.AppendLiteral(", sessionTypes=");
-  str.Append(ToCString(aConfig.mSessionTypes));
-
-  str.AppendLiteral("}");
-
-  return str;
-}
-
-static nsCString
 RequestKeySystemAccessLogString(
   const nsAString& aKeySystem,
   const Sequence<MediaKeySystemConfiguration>& aConfigs,
   bool aIsSecureContext)
 {
   nsCString str;
   str.AppendPrintf("Navigator::RequestMediaKeySystemAccess(keySystem='%s' options=",
                    NS_ConvertUTF16toUTF8(aKeySystem).get());
-  str.Append(ToCString(aConfigs));
+  str.Append(MediaKeySystemAccess::ToCString(aConfigs));
   str.AppendLiteral(") secureContext=");
   str.AppendInt(aIsSecureContext);
   return str;
 }
 
 already_AddRefed<Promise>
 Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
                                        const Sequence<MediaKeySystemConfiguration>& aConfigs,
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -194,17 +194,16 @@
 #include "nsSandboxFlags.h"
 #include "nsScriptSecurityManager.h"
 #include "nsSerializationHelper.h"
 #include "nsStreamUtils.h"
 #include "nsTextEditorState.h"
 #include "nsTextFragment.h"
 #include "nsTextNode.h"
 #include "nsThreadUtils.h"
-#include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsViewManager.h"
 #include "nsViewportInfo.h"
 #include "nsWidgetsCID.h"
 #include "nsIWindowProvider.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsXULPopupManager.h"
 #include "xpcprivate.h" // nsXPConnect
@@ -1754,19 +1753,19 @@ nsContentUtils::IsFirstLetterPunctuation
     }
   }
   return false;
 }
 
 // static
 bool nsContentUtils::IsAlphanumeric(uint32_t aChar)
 {
-  nsIUGenCategory::nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
-
-  return (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kNumber);
+  nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
+
+  return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
 }
 
 // static
 bool nsContentUtils::IsAlphanumericAt(const nsTextFragment* aFrag, uint32_t aOffset)
 {
   char16_t h = aFrag->CharAt(aOffset);
   if (!IS_SURROGATE(h)) {
     return IsAlphanumeric(h);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4029,16 +4029,28 @@ nsDOMWindowUtils::GetElementsRestyled(ui
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *aResult = presContext->ElementsRestyledCount();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetRestyleGeneration(uint64_t* aResult)
+{
+  nsPresContext* presContext = GetPresContext();
+  if (!presContext) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  *aResult = presContext->GetRestyleGeneration();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetFramesConstructed(uint64_t* aResult)
 {
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *aResult = presContext->FramesConstructedCount();
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -208,20 +208,16 @@ DOMInterfaces = {
     'headerFile': 'nsCSSRules.h',
 },
 
 'CSSGroupingRule': {
     'concrete': False,
     'nativeType': 'mozilla::css::GroupRule',
 },
 
-'CSSImportRule': {
-    'nativeType': 'mozilla::css::ImportRule',
-},
-
 'CSSLexer': {
     'wrapperCache': False
 },
 
 'CSSPrimitiveValue': {
     'nativeType': 'nsROCSSPrimitiveValue',
 },
 
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -2551,17 +2551,17 @@ Migrate(mozIStorageConnection* aConn)
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
         if (shouldRewrite) {
           rewriteSchema = true;
         }
         break;
       }
     }
 
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     int32_t lastVersion = currentVersion;
 #endif
     rv = aConn->GetSchemaVersion(&currentVersion);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion);
   }
 
   // Don't release assert this since people do sometimes share profiles
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -1520,17 +1520,17 @@ Manager::AddRefCacheId(CacheId aCacheId)
 }
 
 void
 Manager::ReleaseCacheId(CacheId aCacheId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
     if (mCacheIdRefs[i].mCacheId == aCacheId) {
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       uint32_t oldRef = mCacheIdRefs[i].mCount;
 #endif
       mCacheIdRefs[i].mCount -= 1;
       MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount < oldRef);
       if (mCacheIdRefs[i].mCount == 0) {
         bool orphaned = mCacheIdRefs[i].mOrphaned;
         mCacheIdRefs.RemoveElementAt(i);
         RefPtr<Context> context = mContext;
@@ -1571,17 +1571,17 @@ Manager::AddRefBodyId(const nsID& aBodyI
 }
 
 void
 Manager::ReleaseBodyId(const nsID& aBodyId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
     if (mBodyIdRefs[i].mBodyId == aBodyId) {
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       uint32_t oldRef = mBodyIdRefs[i].mCount;
 #endif
       mBodyIdRefs[i].mCount -= 1;
       MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount < oldRef);
       if (mBodyIdRefs[i].mCount < 1) {
         bool orphaned = mBodyIdRefs[i].mOrphaned;
         mBodyIdRefs.RemoveElementAt(i);
         RefPtr<Context> context = mContext;
--- a/dom/cache/StreamControl.cpp
+++ b/dom/cache/StreamControl.cpp
@@ -40,26 +40,26 @@ StreamControl::~StreamControl()
   // owning thread only, but can't call virtual AssertOwningThread in destructor
   MOZ_DIAGNOSTIC_ASSERT(mReadStreamList.IsEmpty());
 }
 
 void
 StreamControl::CloseReadStreams(const nsID& aId)
 {
   AssertOwningThread();
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   uint32_t closedCount = 0;
 #endif
 
   ReadStreamList::ForwardIterator iter(mReadStreamList);
   while (iter.HasMore()) {
     RefPtr<ReadStream::Controllable> stream = iter.GetNext();
     if (stream->MatchId(aId)) {
       stream->CloseStream();
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       closedCount += 1;
 #endif
     }
   }
 
   MOZ_DIAGNOSTIC_ASSERT(closedCount > 0);
 }
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1882,16 +1882,23 @@ interface nsIDOMWindowUtils : nsISupport
    * than once (e.g., for an inline that contains blocks).  This also
    * counts restyling of pseudo-elements and anonymous boxes.
    *
    * May throw NS_ERROR_NOT_AVAILABLE.
    */
   readonly attribute unsigned long long elementsRestyled;
 
   /**
+   * Restyle generation for the current document.
+   *
+   * May throw NS_ERROR_NOT_AVAILABLE.
+   */
+  readonly attribute unsigned long long restyleGeneration;
+
+  /**
    * Number of frames constructed (excluding breaking) for the curent
    * document.
    *
    * May throw NS_ERROR_NOT_AVAILABLE.
    */
   readonly attribute unsigned long long framesConstructed;
 
   /**
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -45,23 +45,28 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Me
                                       mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
+static nsCString
+ToCString(const MediaKeySystemConfiguration& aConfig);
+
 MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindowInner* aParent,
                                            const nsAString& aKeySystem,
                                            const MediaKeySystemConfiguration& aConfig)
   : mParent(aParent)
   , mKeySystem(aKeySystem)
   , mConfig(aConfig)
 {
+  EME_LOG("Created MediaKeySystemAccess for keysystem=%s config=%s",
+          NS_ConvertUTF16toUTF8(mKeySystem).get(), mozilla::dom::ToCString(mConfig).get());
 }
 
 MediaKeySystemAccess::~MediaKeySystemAccess()
 {
 }
 
 JSObject*
 MediaKeySystemAccess::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
@@ -816,17 +821,18 @@ UnboxSessionTypes(const Optional<Sequenc
   return sessionTypes;
 }
 
 // 3.1.2.2 Get Supported Configuration and Consent
 static bool
 GetSupportedConfig(const KeySystemConfig& aKeySystem,
                    const MediaKeySystemConfiguration& aCandidate,
                    MediaKeySystemConfiguration& aOutConfig,
-                   DecoderDoctorDiagnostics* aDiagnostics)
+                   DecoderDoctorDiagnostics* aDiagnostics,
+                   bool aInPrivateBrowsing)
 {
   // Let accumulated configuration be a new MediaKeySystemConfiguration dictionary.
   MediaKeySystemConfiguration config;
   // Set the label member of accumulated configuration to equal the label member of
   // candidate configuration.
   config.mLabel = aCandidate.mLabel;
   // If the initDataTypes member of candidate configuration is non-empty, run the
   // following steps:
@@ -869,16 +875,24 @@ GetSupportedConfig(const KeySystemConfig
                         aKeySystem.mPersistentState,
                         config.mPersistentState)) {
     EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
             "persistentState requirement not satisfied.",
             NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
     return false;
   }
 
+  if (config.mPersistentState == MediaKeysRequirement::Required &&
+      aInPrivateBrowsing) {
+    EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; "
+            "persistentState requested in Private Browsing window.",
+            NS_ConvertUTF16toUTF8(aCandidate.mLabel).get());
+    return false;
+  }
+
   Sequence<nsString> sessionTypes(UnboxSessionTypes(aCandidate.mSessionTypes));
   if (sessionTypes.IsEmpty()) {
     // Malloc failure.
     return false;
   }
 
   // For each value in session types:
   for (const auto& sessionTypeString : sessionTypes) {
@@ -1039,30 +1053,33 @@ GetSupportedConfig(const KeySystemConfig
   // Return accumulated configuration.
   aOutConfig = config;
 
   return true;
 }
 
 /* static */
 bool
-MediaKeySystemAccess::GetSupportedConfig(const nsAString& aKeySystem,
-                                         const Sequence<MediaKeySystemConfiguration>& aConfigs,
-                                         MediaKeySystemConfiguration& aOutConfig,
-                                         DecoderDoctorDiagnostics* aDiagnostics)
+MediaKeySystemAccess::GetSupportedConfig(
+  const nsAString& aKeySystem,
+  const Sequence<MediaKeySystemConfiguration>& aConfigs,
+  MediaKeySystemConfiguration& aOutConfig,
+  DecoderDoctorDiagnostics* aDiagnostics,
+  bool aIsPrivateBrowsing)
 {
   KeySystemConfig implementation;
   if (!GetKeySystemConfig(aKeySystem, implementation)) {
     return false;
   }
   for (const MediaKeySystemConfiguration& candidate : aConfigs) {
     if (mozilla::dom::GetSupportedConfig(implementation,
                                          candidate,
                                          aOutConfig,
-                                         aDiagnostics)) {
+                                         aDiagnostics,
+                                         aIsPrivateBrowsing)) {
       return true;
     }
   }
 
   return false;
 }
 
 
@@ -1079,10 +1096,108 @@ MediaKeySystemAccess::NotifyObservers(ns
   data.ToJSON(json);
   EME_LOG("MediaKeySystemAccess::NotifyObservers() %s", NS_ConvertUTF16toUTF8(json).get());
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(aWindow, "mediakeys-request", json.get());
   }
 }
 
+static nsCString
+ToCString(const nsString& aString)
+{
+  nsCString str("'");
+  str.Append(NS_ConvertUTF16toUTF8(aString));
+  str.AppendLiteral("'");
+  return str;
+}
+
+static nsCString
+ToCString(const MediaKeysRequirement aValue)
+{
+  nsCString str("'");
+  str.Append(nsDependentCString(
+    MediaKeysRequirementValues::strings[static_cast<uint32_t>(aValue)].value));
+  str.AppendLiteral("'");
+  return str;
+}
+
+static nsCString
+ToCString(const MediaKeySystemMediaCapability& aValue)
+{
+  nsCString str;
+  str.AppendLiteral("{contentType=");
+  str.Append(ToCString(aValue.mContentType));
+  str.AppendLiteral(", robustness=");
+  str.Append(ToCString(aValue.mRobustness));
+  str.AppendLiteral("}");
+  return str;
+}
+
+template<class Type>
+static nsCString
+ToCString(const Sequence<Type>& aSequence)
+{
+  nsCString str;
+  str.AppendLiteral("[");
+  for (size_t i = 0; i < aSequence.Length(); i++) {
+    if (i != 0) {
+      str.AppendLiteral(",");
+    }
+    str.Append(ToCString(aSequence[i]));
+  }
+  str.AppendLiteral("]");
+  return str;
+}
+
+template<class Type>
+static nsCString
+ToCString(const Optional<Sequence<Type>>& aOptional)
+{
+  nsCString str;
+  if (aOptional.WasPassed()) {
+    str.Append(ToCString(aOptional.Value()));
+  } else {
+    str.AppendLiteral("[]");
+  }
+  return str;
+}
+
+static nsCString
+ToCString(const MediaKeySystemConfiguration& aConfig)
+{
+  nsCString str;
+  str.AppendLiteral("{label=");
+  str.Append(ToCString(aConfig.mLabel));
+
+  str.AppendLiteral(", initDataTypes=");
+  str.Append(ToCString(aConfig.mInitDataTypes));
+
+  str.AppendLiteral(", audioCapabilities=");
+  str.Append(ToCString(aConfig.mAudioCapabilities));
+
+  str.AppendLiteral(", videoCapabilities=");
+  str.Append(ToCString(aConfig.mVideoCapabilities));
+
+  str.AppendLiteral(", distinctiveIdentifier=");
+  str.Append(ToCString(aConfig.mDistinctiveIdentifier));
+
+  str.AppendLiteral(", persistentState=");
+  str.Append(ToCString(aConfig.mPersistentState));
+
+  str.AppendLiteral(", sessionTypes=");
+  str.Append(ToCString(aConfig.mSessionTypes));
+
+  str.AppendLiteral("}");
+
+  return str;
+}
+
+/* static */
+nsCString
+MediaKeySystemAccess::ToCString(
+  const Sequence<MediaKeySystemConfiguration>& aConfig)
+{
+  return mozilla::dom::ToCString(aConfig);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySystemAccess.h
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -56,24 +56,29 @@ public:
   static bool IsSupported(const nsAString& aKeySystem,
                           const Sequence<MediaKeySystemConfiguration>& aConfigs,
                           DecoderDoctorDiagnostics* aDiagnostics);
 
   static void NotifyObservers(nsPIDOMWindowInner* aWindow,
                               const nsAString& aKeySystem,
                               MediaKeySystemStatus aStatus);
 
-  static bool GetSupportedConfig(const nsAString& aKeySystem,
-                                 const Sequence<MediaKeySystemConfiguration>& aConfigs,
-                                 MediaKeySystemConfiguration& aOutConfig,
-                                 DecoderDoctorDiagnostics* aDiagnostics);
+  static bool GetSupportedConfig(
+    const nsAString& aKeySystem,
+    const Sequence<MediaKeySystemConfiguration>& aConfigs,
+    MediaKeySystemConfiguration& aOutConfig,
+    DecoderDoctorDiagnostics* aDiagnostics,
+    bool aIsPrivateBrowsing);
 
   static bool KeySystemSupportsInitDataType(const nsAString& aKeySystem,
                                             const nsAString& aInitDataType);
 
+  static nsCString ToCString(
+    const Sequence<MediaKeySystemConfiguration>& aConfig);
+
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   const nsString mKeySystem;
   const MediaKeySystemConfiguration mConfig;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -160,18 +160,22 @@ MediaKeySystemAccessManager::Request(Det
     // Failed due to user disabling something, send a notification to
     // chrome, so we can show some UI to explain how the user can rectify
     // the situation.
     MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, status);
     aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR, message);
     return;
   }
 
+  bool isPrivateBrowsing =
+    mWindow->GetExtantDoc() &&
+    mWindow->GetExtantDoc()->NodePrincipal()->GetPrivateBrowsingId() > 0;
   MediaKeySystemConfiguration config;
-  if (MediaKeySystemAccess::GetSupportedConfig(aKeySystem, aConfigs, config, &diagnostics)) {
+  if (MediaKeySystemAccess::GetSupportedConfig(
+        aKeySystem, aConfigs, config, &diagnostics, isPrivateBrowsing)) {
     RefPtr<MediaKeySystemAccess> access(
       new MediaKeySystemAccess(mWindow, aKeySystem, config));
     aPromise->MaybeResolve(access);
     diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
                                           aKeySystem, true, __func__);
     return;
   }
   // Not to inform user, because nothing to do if the corresponding keySystem
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -33,31 +33,31 @@ static mozilla::LazyLogModule sGetUserMe
 #define LOG(args) MOZ_LOG(sGetUserMediaLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 
 // statics from AudioInputCubeb
 nsTArray<int>* AudioInputCubeb::mDeviceIndexes;
 int AudioInputCubeb::mDefaultDevice = -1;
 nsTArray<nsCString>* AudioInputCubeb::mDeviceNames;
-cubeb_device_collection* AudioInputCubeb::mDevices = nullptr;
+cubeb_device_collection AudioInputCubeb::mDevices = { nullptr, 0 };
 bool AudioInputCubeb::mAnyInUse = false;
 StaticMutex AudioInputCubeb::sMutex;
 
 // AudioDeviceID is an annoying opaque value that's really a string
 // pointer, and is freed when the cubeb_device_collection is destroyed
 
 void AudioInputCubeb::UpdateDeviceList()
 {
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     return;
   }
 
-  cubeb_device_collection *devices = nullptr;
+  cubeb_device_collection devices = { nullptr, 0 };
 
   if (CUBEB_OK != cubeb_enumerate_devices(cubebContext,
                                           CUBEB_DEVICE_TYPE_INPUT,
                                           &devices)) {
     return;
   }
 
   for (auto& device_index : (*mDeviceIndexes)) {
@@ -66,49 +66,47 @@ void AudioInputCubeb::UpdateDeviceList()
   // We keep all the device names, but wipe the mappings and rebuild them
 
   // Calculate translation from existing mDevices to new devices. Note we
   // never end up with less devices than before, since people have
   // stashed indexes.
   // For some reason the "fake" device for automation is marked as DISABLED,
   // so white-list it.
   mDefaultDevice = -1;
-  for (uint32_t i = 0; i < devices->count; i++) {
+  for (uint32_t i = 0; i < devices.count; i++) {
     LOG(("Cubeb device %u: type 0x%x, state 0x%x, name %s, id %p",
-         i, devices->device[i]->type, devices->device[i]->state,
-         devices->device[i]->friendly_name, devices->device[i]->device_id));
-    if (devices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
-        (devices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED ||
-         (devices->device[i]->state == CUBEB_DEVICE_STATE_DISABLED &&
-          devices->device[i]->friendly_name &&
-          strcmp(devices->device[i]->friendly_name, "Sine source at 440 Hz") == 0)))
+         i, devices.device[i].type, devices.device[i].state,
+         devices.device[i].friendly_name, devices.device[i].device_id));
+    if (devices.device[i].type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
+        (devices.device[i].state == CUBEB_DEVICE_STATE_ENABLED ||
+         (devices.device[i].state == CUBEB_DEVICE_STATE_DISABLED &&
+          devices.device[i].friendly_name &&
+          strcmp(devices.device[i].friendly_name, "Sine source at 440 Hz") == 0)))
     {
-      auto j = mDeviceNames->IndexOf(devices->device[i]->device_id);
+      auto j = mDeviceNames->IndexOf(devices.device[i].device_id);
       if (j != nsTArray<nsCString>::NoIndex) {
         // match! update the mapping
         (*mDeviceIndexes)[j] = i;
       } else {
         // new device, add to the array
         mDeviceIndexes->AppendElement(i);
-        mDeviceNames->AppendElement(devices->device[i]->device_id);
+        mDeviceNames->AppendElement(devices.device[i].device_id);
         j = mDeviceIndexes->Length()-1;
       }
-      if (devices->device[i]->preferred & CUBEB_DEVICE_PREF_VOICE) {
+      if (devices.device[i].preferred & CUBEB_DEVICE_PREF_VOICE) {
         // There can be only one... we hope
         NS_ASSERTION(mDefaultDevice == -1, "multiple default cubeb input devices!");
         mDefaultDevice = j;
       }
     }
   }
   LOG(("Cubeb default input device %d", mDefaultDevice));
   StaticMutexAutoLock lock(sMutex);
   // swap state
-  if (mDevices) {
-    cubeb_device_collection_destroy(cubebContext, mDevices);
-  }
+  cubeb_device_collection_destroy(cubebContext, &mDevices);
   mDevices = devices;
 }
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
   : mMutex("mozilla::MediaEngineWebRTC"),
     mVoiceEngine(nullptr),
     mAudioInput(nullptr),
     mFullDuplex(aPrefs.mFullDuplex),
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -174,21 +174,17 @@ public:
       mDeviceIndexes = new nsTArray<int>;
       mDeviceNames = new nsTArray<nsCString>;
       mDefaultDevice = -1;
     }
   }
 
   static void CleanupGlobalData()
   {
-    if (mDevices) {
-      // This doesn't require anything more than support for free()
-      cubeb_device_collection_destroy(CubebUtils::GetCubebContext(), mDevices);
-      mDevices = nullptr;
-    }
+    cubeb_device_collection_destroy(CubebUtils::GetCubebContext(), &mDevices);
     delete mDeviceIndexes;
     mDeviceIndexes = nullptr;
     delete mDeviceNames;
     mDeviceNames = nullptr;
   }
 
   int GetNumOfRecordingDevices(int& aDevices)
   {
@@ -229,36 +225,36 @@ public:
     // Assert sMutex is held
     sMutex.AssertCurrentThreadOwns();
 #ifdef MOZ_WIDGET_ANDROID
     aID = nullptr;
     return true;
 #else
     int dev_index = DeviceIndex(aDeviceIndex);
     if (dev_index != -1) {
-      aID = mDevices->device[dev_index]->devid;
+      aID = mDevices.device[dev_index].devid;
       return true;
     }
     return false;
 #endif
   }
 
   int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128],
                              char aStrGuidUTF8[128])
   {
 #ifdef MOZ_WIDGET_ANDROID
     aStrNameUTF8[0] = '\0';
     aStrGuidUTF8[0] = '\0';
 #else
     int32_t devindex = DeviceIndex(aIndex);
-    if (!mDevices || devindex < 0) {
+    if (mDevices.count == 0 || devindex < 0) {
       return 1;
     }
     SprintfLiteral(aStrNameUTF8, "%s%s", aIndex == -1 ? "default: " : "",
-		   mDevices->device[devindex]->friendly_name);
+                   mDevices.device[devindex].friendly_name);
     aStrGuidUTF8[0] = '\0';
 #endif
     return 0;
   }
 
   int GetRecordingDeviceStatus(bool& aIsAvailable)
   {
     // With cubeb, we only expose devices of type CUBEB_DEVICE_TYPE_INPUT,
@@ -266,19 +262,19 @@ public:
     aIsAvailable = true;
     return 0;
   }
 
   void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener)
   {
 #ifdef MOZ_WIDGET_ANDROID
     // OpenSL ES does not support enumerating devices.
-    MOZ_ASSERT(!mDevices);
+    MOZ_ASSERT(mDevices.count == 0);
 #else
-    MOZ_ASSERT(mDevices);
+    MOZ_ASSERT(mDevices.count > 0);
 #endif
 
     if (mInUseCount == 0) {
       ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoEXMedia;
       ptrVoEXMedia = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
       if (ptrVoEXMedia) {
         ptrVoEXMedia->SetExternalRecordingStatus(true);
       }
@@ -320,17 +316,17 @@ private:
   // updated on each re-enumeration.
   int mSelectedDevice;
   uint32_t mInUseCount;
 
   // pointers to avoid static constructors
   static nsTArray<int>* mDeviceIndexes;
   static int mDefaultDevice; // -1 == not set
   static nsTArray<nsCString>* mDeviceNames;
-  static cubeb_device_collection *mDevices;
+  static cubeb_device_collection mDevices;
   static bool mAnyInUse;
   static StaticMutex sMutex;
 };
 
 class AudioInputWebRTC final : public AudioInput
 {
 public:
   explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {}
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1234,17 +1234,17 @@ private:
                                    loadInfo.mScriptTextLength);
     if (NS_SUCCEEDED(rv) && IsMainWorkerScript()) {
       nsCOMPtr<nsIURI> finalURI;
       rv = NS_NewURI(getter_AddRefs(finalURI), loadInfo.mFullURL, nullptr, nullptr);
       if (NS_SUCCEEDED(rv)) {
         mWorkerPrivate->SetBaseURI(finalURI);
       }
 
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
       MOZ_DIAGNOSTIC_ASSERT(principal);
 
       bool equal = false;
       MOZ_ALWAYS_SUCCEEDS(responsePrincipal->Equals(principal, &equal));
       MOZ_DIAGNOSTIC_ASSERT(equal);
 
       nsCOMPtr<nsIContentSecurityPolicy> csp;
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1800,17 +1800,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
   }
 
   nsContentUtils::StorageAccess access =
     nsContentUtils::StorageAllowedForPrincipal(info.mPrincipal);
   info.mStorageAllowed = access > nsContentUtils::StorageAccess::ePrivateBrowsing;
   info.mOriginAttributes = mInfo->GetOriginAttributes();
 
   // Verify that we don't have any CSP on pristine principal.
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   Unused << info.mPrincipal->GetCsp(getter_AddRefs(csp));
   MOZ_DIAGNOSTIC_ASSERT(!csp);
 #endif
 
   // Default CSP permissions for now.  These will be overrided if necessary
   // based on the script CSP headers during load in ScriptLoader.
   info.mEvalAllowed = true;
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -657,17 +657,17 @@ public:
   }
 
   nsresult
   SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
 
   nsresult
   SetPrincipalFromChannel(nsIChannel* aChannel);
 
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   bool
   FinalChannelPrincipalIsValid(nsIChannel* aChannel);
 
   bool
   PrincipalURIMatchesScriptURL();
 #endif
 
   bool
@@ -908,17 +908,17 @@ public:
   AssertIsOnParentThread() const
   { }
 
   void
   AssertInnerWindowIsCorrect() const
   { }
 #endif
 
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   bool
   PrincipalIsValid() const;
 #endif
 };
 
 class WorkerDebugger : public nsIWorkerDebugger {
   friend class ::ReportDebuggerErrorRunnable;
   friend class ::PostDebuggerMessageRunnable;
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -285,17 +285,17 @@ struct WorkerLoadInfo
   nsresult
   GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
                                       nsIPrincipal** aPrincipalOut,
                                       nsILoadGroup** aLoadGroupOut);
 
   nsresult
   SetPrincipalFromChannel(nsIChannel* aChannel);
 
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   bool
   FinalChannelPrincipalIsValid(nsIChannel* aChannel);
 
   bool
   PrincipalIsValid() const;
 
   bool
   PrincipalURIMatchesScriptURL();
--- a/dom/workers/test/gtest/TestReadWrite.cpp
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -21,17 +21,17 @@
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 class ServiceWorkerRegistrarTest : public ServiceWorkerRegistrar
 {
 public:
   ServiceWorkerRegistrarTest()
   {
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                        getter_AddRefs(mProfileDir));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
 #else
     NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                            getter_AddRefs(mProfileDir));
 #endif
     MOZ_DIAGNOSTIC_ASSERT(mProfileDir);
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -65,17 +65,16 @@
 #include "nsIDOMEvent.h"                // for nsIDOMEvent
 #include "nsIDOMEventListener.h"        // for nsIDOMEventListener
 #include "nsIDOMEventTarget.h"          // for nsIDOMEventTarget
 #include "nsIDOMHTMLElement.h"          // for nsIDOMHTMLElement
 #include "nsIDOMMozNamedAttrMap.h"      // for nsIDOMMozNamedAttrMap
 #include "nsIDOMMouseEvent.h"           // for nsIDOMMouseEvent
 #include "nsIDOMNode.h"                 // for nsIDOMNode, etc.
 #include "nsIDOMNodeList.h"             // for nsIDOMNodeList
-#include "nsIDOMText.h"                 // for nsIDOMText
 #include "nsIDocument.h"                // for nsIDocument
 #include "nsIDocumentStateListener.h"   // for nsIDocumentStateListener
 #include "nsIEditActionListener.h"      // for nsIEditActionListener
 #include "nsIEditorObserver.h"          // for nsIEditorObserver
 #include "nsIEditorSpellCheck.h"        // for nsIEditorSpellCheck
 #include "nsIFrame.h"                   // for nsIFrame
 #include "nsIHTMLDocument.h"            // for nsIHTMLDocument
 #include "nsIInlineSpellChecker.h"      // for nsIInlineSpellChecker, etc.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -36,17 +36,16 @@
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsID.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
-#include "nsIDOMText.h"
 #include "nsIFrame.h"
 #include "nsIHTMLAbsPosEditor.h"
 #include "nsIHTMLDocument.h"
 #include "nsINode.h"
 #include "nsLiteralString.h"
 #include "nsRange.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
@@ -5590,20 +5589,19 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
     if (mHTMLEditor->IsVisBreak(nextNode)) {
       break;
     }
 
     // Check for newlines in pre-formatted text nodes.
     bool isPRE;
     mHTMLEditor->IsPreformatted(nextNode->AsDOMNode(), &isPRE);
     if (isPRE) {
-      nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(nextNode);
-      if (textNode) {
+      if (EditorBase::IsTextNode(nextNode)) {
         nsAutoString tempString;
-        textNode->GetData(tempString);
+        nextNode->GetAsText()->GetData(tempString);
         int32_t newlinePos = tempString.FindChar(nsCRT::LF);
         if (newlinePos >= 0) {
           if (static_cast<uint32_t>(newlinePos) + 1 == tempString.Length()) {
             // No need for special processing if the newline is at the end.
             break;
           }
           *outNode = nextNode->AsDOMNode();
           *outOffset = newlinePos + 1;
@@ -6450,49 +6448,45 @@ HTMLEditRules::ReturnInParagraph(Selecti
   nsCOMPtr<nsIDOMNode> selNode = aNode;
   int32_t selOffset = aOffset;
 
   NS_ENSURE_STATE(mHTMLEditor);
   if (aNode == aPara && doesCRCreateNewP) {
     // we are at the edges of the block, newBRneeded not needed!
     sibling = node->AsContent();
   } else if (EditorBase::IsTextNode(aNode)) {
-    nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
-    uint32_t strLength;
-    nsresult rv = textNode->GetLength(&strLength);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     // at beginning of text node?
     if (!aOffset) {
       // is there a BR prior to it?
       NS_ENSURE_STATE(mHTMLEditor);
       sibling = mHTMLEditor->GetPriorHTMLSibling(node);
       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
           TextEditUtils::HasMozAttr(GetAsDOMNode(sibling))) {
         NS_ENSURE_STATE(mHTMLEditor);
         newBRneeded = true;
       }
-    } else if (aOffset == (int32_t)strLength) {
+    } else if (aOffset == static_cast<int32_t>(node->Length())) {
       // we're at the end of text node...
       // is there a BR after to it?
       NS_ENSURE_STATE(mHTMLEditor);
       sibling = mHTMLEditor->GetNextHTMLSibling(node);
       if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) ||
           TextEditUtils::HasMozAttr(GetAsDOMNode(sibling))) {
         NS_ENSURE_STATE(mHTMLEditor);
         newBRneeded = true;
         offset++;
       }
     } else {
       if (doesCRCreateNewP) {
         nsCOMPtr<nsIDOMNode> tmp;
         if (NS_WARN_IF(!mHTMLEditor)) {
           return NS_ERROR_UNEXPECTED;
         }
-        rv = mHTMLEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
+        nsresult rv =
+          mHTMLEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
         NS_ENSURE_SUCCESS(rv, rv);
         selNode = tmp;
       }
 
       newBRneeded = true;
       offset++;
     }
   } else {
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -16,17 +16,16 @@
 #include "HTMLEditorEventListener.h"
 #include "HTMLEditRules.h"
 #include "HTMLEditUtils.h"
 #include "HTMLURIRefObject.h"
 #include "StyleSheetTransactions.h"
 #include "TextEditUtils.h"
 #include "TypeInState.h"
 
-#include "nsIDOMText.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMAttr.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsISelectionController.h"
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -23,17 +23,16 @@
 #include "nsIContent.h"
 #include "nsID.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMNode.h"
-#include "nsIDOMText.h"
 #include "nsIDocument.h"
 #include "nsIEditor.h"
 #include "nsIHTMLObjectResizer.h"
 #include "nsIPresShell.h"
 #include "nsISupportsUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
@@ -621,18 +620,16 @@ HTMLEditor::SetResizeIncrements(int32_t 
 }
 
 nsresult
 HTMLEditor::SetResizingInfoPosition(int32_t aX,
                                     int32_t aY,
                                     int32_t aW,
                                     int32_t aH)
 {
-  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
-
   // Determine the position of the resizing info box based upon the new
   // position and size of the element (aX, aY, aW, aH), and which
   // resizer is the "activated handle".  For example, place the resizing
   // info box at the bottom-right corner of the new element, if the element
   // is being resized by the bottom-right resizer.
   int32_t infoXPosition;
   int32_t infoYPosition;
 
@@ -691,22 +688,25 @@ HTMLEditor::SetResizingInfoPosition(int3
   diffWidthStr.AppendInt(diffWidth);
   diffHeightStr.AppendInt(diffHeight);
 
   nsAutoString info(widthStr + NS_LITERAL_STRING(" x ") + heightStr +
                     NS_LITERAL_STRING(" (") + diffWidthStr +
                     NS_LITERAL_STRING(", ") + diffHeightStr +
                     NS_LITERAL_STRING(")"));
 
-  nsCOMPtr<nsIDOMText> nodeAsText;
-  nsresult rv = domdoc->CreateTextNode(info, getter_AddRefs(nodeAsText));
-  NS_ENSURE_SUCCESS(rv, rv);
-  textInfo = do_QueryInterface(nodeAsText);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+  textInfo = doc->CreateTextNode(info);
+  if (NS_WARN_IF(!textInfo)) {
+    return NS_ERROR_FAILURE;
+  }
   mResizingInfo->AppendChild(*textInfo, erv);
-  NS_ENSURE_TRUE(!erv.Failed(), erv.StealNSResult());
+  if (NS_WARN_IF(erv.Failed())) {
+    return erv.StealNSResult();
+  }
 
   return mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
 }
 
 nsresult
 HTMLEditor::SetShadowPosition(Element* aShadow,
                               Element* aOriginalObject,
                               int32_t aOriginalObjectX,
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -23,17 +23,16 @@
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeFilter.h"
-#include "nsIDOMText.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISupportsBase.h"
 #include "nsLiteralString.h"
 #include "nsTextNode.h"
 #include "nsUnicharUtils.h"
 #include "nsIHTMLCollection.h"
@@ -440,52 +439,52 @@ TextEditRules::CollapseSelectionToTraili
   // editor is reframed, this may be called by AfterEdit().
   if (!aSelection->RangeCount()) {
     mTextEditor->EndOfDocument();
   }
 
   // if we are at the end of the textarea, we need to set the
   // selection to stick to the mozBR at the end of the textarea.
   int32_t selOffset;
-  nsCOMPtr<nsIDOMNode> selNode;
+  nsCOMPtr<nsINode> selNode;
   nsresult rv =
     EditorBase::GetStartNodeAndOffset(aSelection,
                                       getter_AddRefs(selNode), &selOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-  nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
-  if (!nodeAsText) {
+  if (!EditorBase::IsTextNode(selNode)) {
     return NS_OK; // Nothing to do if we're not at a text node.
   }
 
-  uint32_t length;
-  rv = nodeAsText->GetLength(&length);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // nothing to do if we're not at the end of the text node
-  if (selOffset != int32_t(length)) {
+  if (selOffset != static_cast<int32_t>(selNode->Length())) {
     return NS_OK;
   }
 
   int32_t parentOffset;
-  nsCOMPtr<nsIDOMNode> parentNode =
+  nsINode* parentNode =
     EditorBase::GetNodeLocation(selNode, &parentOffset);
 
   NS_ENSURE_STATE(mTextEditor);
-  nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mTextEditor->GetRoot());
-  NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
+  nsINode* root = mTextEditor->GetRoot();
+  if (NS_WARN_IF(!root)) {
+    return NS_ERROR_NULL_POINTER;
+  }
   if (parentNode != root) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIDOMNode> nextNode = mTextEditor->GetChildAt(parentNode,
-                                                          parentOffset + 1);
+  nsINode* nextNode = parentNode->GetChildAt(parentOffset + 1);
   if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
     rv = aSelection->Collapse(parentNode, parentOffset + 1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 static inline already_AddRefed<nsINode>
 GetTextNode(Selection* selection)
 {
   int32_t selOffset;
@@ -1040,36 +1039,37 @@ TextEditRules::WillDeleteSelection(Selec
   return NS_OK;
 }
 
 nsresult
 TextEditRules::DidDeleteSelection(Selection* aSelection,
                                   nsIEditor::EDirection aCollapsedAction,
                                   nsresult aResult)
 {
-  nsCOMPtr<nsIDOMNode> startNode;
+  nsCOMPtr<nsINode> startNode;
   int32_t startOffset;
   nsresult rv =
     EditorBase::GetStartNodeAndOffset(aSelection,
                                       getter_AddRefs(startNode), &startOffset);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (NS_WARN_IF(!startNode)) {
+    return NS_ERROR_FAILURE;
+  }
 
   // delete empty text nodes at selection
   if (EditorBase::IsTextNode(startNode)) {
-    nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
-    uint32_t strLength;
-    rv = textNode->GetLength(&strLength);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     // are we in an empty text node?
-    if (!strLength) {
+    if (!startNode->Length()) {
       NS_ENSURE_STATE(mTextEditor);
       rv = mTextEditor->DeleteNode(startNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
   if (mDidExplicitlySetInterline) {
     return NS_OK;
   }
   // We prevent the caret from sticking on the left of prior BR
   // (i.e. the end of previous line) after this deletion.  Bug 92124
   return aSelection->SetInterlinePosition(true);
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
@@ -62,17 +62,16 @@
 #include "nsXPIDLString.h"
 #include "nsIObserverService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIFile.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozISpellI18NManager.h"
-#include "nsUnicharUtilCIID.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozInlineSpellChecker.h"
 #include "mozilla/Services.h"
 #include <stdlib.h>
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "mozilla/dom/EncodingUtils.h"
--- a/extensions/spellcheck/src/mozEnglishWordUtils.cpp
+++ b/extensions/spellcheck/src/mozEnglishWordUtils.cpp
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozEnglishWordUtils.h"
 #include "nsReadableUtils.h"
 #include "nsIServiceManager.h"
 #include "nsUnicharUtils.h"
-#include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsCRT.h"
 #include "mozilla/Likely.h"
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozEnglishWordUtils)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozEnglishWordUtils)
 
 NS_INTERFACE_MAP_BEGIN(mozEnglishWordUtils)
@@ -129,17 +128,17 @@ NS_IMETHODIMP mozEnglishWordUtils::GetRo
     }
   return NS_OK;
 }
 
 // This needs vast improvement
 bool mozEnglishWordUtils::ucIsAlpha(char16_t aChar)
 {
   // XXX we have to fix callers to handle the full Unicode range
-  return nsIUGenCategory::kLetter == mozilla::unicode::GetGenCategory(aChar);
+  return nsUGenCategory::kLetter == mozilla::unicode::GetGenCategory(aChar);
 }
 
 NS_IMETHODIMP mozEnglishWordUtils::FindNextWord(const char16_t *word, uint32_t length, uint32_t offset, int32_t *begin, int32_t *end)
 {
   const char16_t *p = word + offset;
   const char16_t *endbuf = word + length;
   const char16_t *startWord=p;
   if(p<endbuf){
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
@@ -7,17 +7,16 @@
 #include "nsDebug.h"
 #include "nsIAtom.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMRange.h"
 #include "nsIEditor.h"
 #include "nsIDOMNode.h"
-#include "nsUnicharUtilCIID.h"
 #include "nsUnicodeProperties.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIContent.h"
 #include "nsTextFragment.h"
 #include "mozilla/dom/Element.h"
 #include "nsRange.h"
 #include "nsContentUtils.h"
 #include "nsIFrame.h"
@@ -846,19 +845,19 @@ WordSplitState::ClassifyCharacter(int32_
 {
   NS_ASSERTION(aIndex >= 0 && aIndex <= int32_t(mDOMWordText.Length()),
                "Index out of range");
   if (aIndex == int32_t(mDOMWordText.Length()))
     return CHAR_CLASS_SEPARATOR;
 
   // this will classify the character, we want to treat "ignorable" characters
   // such as soft hyphens, and also ZWJ and ZWNJ as word characters.
-  nsIUGenCategory::nsUGenCategory
-    charCategory = mozilla::unicode::GetGenCategory(mDOMWordText[aIndex]);
-  if (charCategory == nsIUGenCategory::kLetter ||
+  nsUGenCategory charCategory =
+    mozilla::unicode::GetGenCategory(mDOMWordText[aIndex]);
+  if (charCategory == nsUGenCategory::kLetter ||
       IsIgnorableCharacter(mDOMWordText[aIndex]) ||
       mDOMWordText[aIndex] == 0x200C /* ZWNJ */ ||
       mDOMWordText[aIndex] == 0x200D /* ZWJ */)
     return CHAR_CLASS_WORD;
 
   // If conditional punctuation is surrounded immediately on both sides by word
   // characters it also counts as a word character.
   if (IsConditionalPunctuation(mDOMWordText[aIndex])) {
@@ -897,20 +896,20 @@ WordSplitState::ClassifyCharacter(int32_
   if (aIndex > 0 &&
       mDOMWordText[aIndex] == '.' &&
       mDOMWordText[aIndex - 1] != '.' &&
       ClassifyCharacter(aIndex - 1, false) != CHAR_CLASS_WORD) {
     return CHAR_CLASS_WORD;
   }
 
   // all other punctuation
-  if (charCategory == nsIUGenCategory::kSeparator ||
-      charCategory == nsIUGenCategory::kOther ||
-      charCategory == nsIUGenCategory::kPunctuation ||
-      charCategory == nsIUGenCategory::kSymbol) {
+  if (charCategory == nsUGenCategory::kSeparator ||
+      charCategory == nsUGenCategory::kOther ||
+      charCategory == nsUGenCategory::kPunctuation ||
+      charCategory == nsUGenCategory::kSymbol) {
     // Don't break on hyphens, as hunspell handles them on its own.
     if (aIndex > 0 &&
         mDOMWordText[aIndex] == '-' &&
         mDOMWordText[aIndex - 1] != '-' &&
         ClassifyCharacter(aIndex - 1, false) == CHAR_CLASS_WORD) {
       // A hyphen is only meaningful as a separator inside a word
       // if the previous and next characters are a word character.
       if (aIndex == int32_t(mDOMWordText.Length()) - 1)
@@ -1028,17 +1027,17 @@ WordSplitState::IsSpecialWord()
 
 bool
 WordSplitState::ShouldSkipWord(int32_t aStart, int32_t aLength)
 {
   int32_t last = aStart + aLength;
 
   // check to see if the word contains a digit
   for (int32_t i = aStart; i < last; i ++) {
-    if (unicode::GetGenCategory(mDOMWordText[i]) == nsIUGenCategory::kNumber) {
+    if (unicode::GetGenCategory(mDOMWordText[i]) == nsUGenCategory::kNumber) {
       return true;
     }
   }
 
   // not special
   return false;
 }
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -21,19 +21,19 @@
 #include "nsGkAtoms.h"
 
 #include "gfxTypes.h"
 #include "gfxContext.h"
 #include "gfxFontMissingGlyphs.h"
 #include "gfxGraphiteShaper.h"
 #include "gfxHarfBuzzShaper.h"
 #include "gfxUserFontSet.h"
-#include "nsIUGenCategory.h"
 #include "nsSpecialCasingData.h"
 #include "nsTextRunTransformations.h"
+#include "nsUGenCategory.h"
 #include "nsUnicodeProperties.h"
 #include "nsStyleConsts.h"
 #include "mozilla/AppUnits.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
@@ -694,17 +694,17 @@ bool
 gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
 {
     if (IsIgnorable(aCh)) {
         // There are a few default-ignorables of Letter category (currently,
         // just the Hangul filler characters) that we'd better not discard
         // if they're followed by additional characters in the same cluster.
         // Some fonts use them to carry the width of a whole cluster of
         // combining jamos; see bug 1238243.
-        if (GetGenCategory(aCh) == nsIUGenCategory::kLetter &&
+        if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
             aIndex + 1 < GetLength() &&
             !GetCharacterGlyphs()[aIndex + 1].IsClusterStart()) {
             return false;
         }
         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
         details->mGlyphID = aCh;
         details->mAdvance = 0;
         details->mXOffset = 0;
--- a/image/test/reftest/jpeg/reftest.list
+++ b/image/test/reftest/jpeg/reftest.list
@@ -46,9 +46,9 @@ random-if(Android) == jpg-srgb-icc.jpg j
 # \r\n
 # <contents of blue.jpg> (no newline)
 # --BOUNDARYOMG--\r\n
 # 
 # (The boundary is arbitrary, and just has to be defined as something that
 # won't be in the text of the contents themselves. --$(boundary)\r\n means
 # "Here is the beginning of a boundary," and --$(boundary)-- means "All done
 # sending you parts.")
-HTTP == webcam-simulacrum.mjpg blue.jpg
+random-if(stylo||styloVsGecko) HTTP == webcam-simulacrum.mjpg blue.jpg # bug 1368589 for stylo
--- a/intl/build/nsI18nModule.cpp
+++ b/intl/build/nsI18nModule.cpp
@@ -5,76 +5,60 @@
 
 #include "mozilla/ModuleUtils.h"
 
 // lwbrk
 #include "nsLWBrkCIID.h"
 #include "nsJISx4051LineBreaker.h"
 #include "nsSampleWordBreaker.h"
 
-#include "nsSemanticUnitScanner.h"
-
 // unicharutil
-#include "nsCategoryImp.h"
-#include "nsUnicharUtilCIID.h"
-#include "nsCaseConversionImp2.h"
 #include "nsEntityConverter.h"
 #include "nsSaveAsCharset.h"
 #include "nsUnicodeNormalizer.h"
 
 // string bundles (intl)
 #include "nsStringBundleService.h"
 #include "nsStringBundleTextOverride.h"
 
 // locale
 #include "nsLocaleConstructors.h"
 
 // uconv
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsJISx4051LineBreaker)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSampleWordBreaker)
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsSemanticUnitScanner)
-
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleTextOverride, Init)
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsCaseConversionImp2)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsCategoryImp,
-                                         nsCategoryImp::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsEntityConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSaveAsCharset)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeNormalizer)
 
 NS_DEFINE_NAMED_CID(MOZ_LOCALESERVICE_CID);
 NS_DEFINE_NAMED_CID(MOZ_OSPREFERENCES_CID);
 NS_DEFINE_NAMED_CID(NS_LBRK_CID);
 NS_DEFINE_NAMED_CID(NS_WBRK_CID);
-NS_DEFINE_NAMED_CID(NS_SEMANTICUNITSCANNER_CID);
-NS_DEFINE_NAMED_CID(NS_UNICHARUTIL_CID);
-NS_DEFINE_NAMED_CID(NS_UNICHARCATEGORY_CID);
 NS_DEFINE_NAMED_CID(NS_ENTITYCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_SAVEASCHARSET_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODE_NORMALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_STRINGBUNDLESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STRINGBUNDLETEXTOVERRIDE_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_COLLATIONFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_SCRIPTABLEDATEFORMAT_CID);
 NS_DEFINE_NAMED_CID(NS_PLATFORMCHARSET_CID);
 NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
 
 static const mozilla::Module::CIDEntry kIntlCIDs[] = {
     { &kMOZ_LOCALESERVICE_CID, false, nullptr, mozilla::intl::LocaleServiceConstructor },
     { &kMOZ_OSPREFERENCES_CID, false, nullptr, mozilla::intl::OSPreferencesConstructor },
     { &kNS_LBRK_CID, false, nullptr, nsJISx4051LineBreakerConstructor },
     { &kNS_WBRK_CID, false, nullptr, nsSampleWordBreakerConstructor },
-    { &kNS_SEMANTICUNITSCANNER_CID, false, nullptr, nsSemanticUnitScannerConstructor },
-    { &kNS_UNICHARUTIL_CID, false, nullptr, nsCaseConversionImp2Constructor },
-    { &kNS_UNICHARCATEGORY_CID, false, nullptr, nsCategoryImpConstructor },
     { &kNS_ENTITYCONVERTER_CID, false, nullptr, nsEntityConverterConstructor },
     { &kNS_SAVEASCHARSET_CID, false, nullptr, nsSaveAsCharsetConstructor },
     { &kNS_UNICODE_NORMALIZER_CID, false, nullptr, nsUnicodeNormalizerConstructor },
     { &kNS_STRINGBUNDLESERVICE_CID, false, nullptr, nsStringBundleServiceConstructor },
     { &kNS_STRINGBUNDLETEXTOVERRIDE_CID, false, nullptr, nsStringBundleTextOverrideConstructor },
     { &kNS_LOCALESERVICE_CID, false, nullptr, CreateLocaleService },
     { &kNS_COLLATIONFACTORY_CID, false, nullptr, nsCollationFactoryConstructor },
     { &kNS_SCRIPTABLEDATEFORMAT_CID, false, nullptr, NS_NewScriptableDateFormat },
@@ -83,19 +67,16 @@ static const mozilla::Module::CIDEntry k
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kIntlContracts[] = {
     { MOZ_LOCALESERVICE_CONTRACTID, &kMOZ_LOCALESERVICE_CID },
     { MOZ_OSPREFERENCES_CONTRACTID, &kMOZ_OSPREFERENCES_CID },
     { NS_LBRK_CONTRACTID, &kNS_LBRK_CID },
     { NS_WBRK_CONTRACTID, &kNS_WBRK_CID },
-    { NS_SEMANTICUNITSCANNER_CONTRACTID, &kNS_SEMANTICUNITSCANNER_CID },
-    { NS_UNICHARUTIL_CONTRACTID, &kNS_UNICHARUTIL_CID },
-    { NS_UNICHARCATEGORY_CONTRACTID, &kNS_UNICHARCATEGORY_CID },
     { NS_ENTITYCONVERTER_CONTRACTID, &kNS_ENTITYCONVERTER_CID },
     { NS_SAVEASCHARSET_CONTRACTID, &kNS_SAVEASCHARSET_CID },
     { NS_UNICODE_NORMALIZER_CONTRACTID, &kNS_UNICODE_NORMALIZER_CID },
     { NS_STRINGBUNDLE_CONTRACTID, &kNS_STRINGBUNDLESERVICE_CID },
     { NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID, &kNS_STRINGBUNDLETEXTOVERRIDE_CID },
     { NS_LOCALESERVICE_CONTRACTID, &kNS_LOCALESERVICE_CID },
     { NS_COLLATIONFACTORY_CONTRACTID, &kNS_COLLATIONFACTORY_CID },
     { NS_SCRIPTABLEDATEFORMAT_CONTRACTID, &kNS_SCRIPTABLEDATEFORMAT_CID },
--- a/intl/hyphenation/glue/nsHyphenator.cpp
+++ b/intl/hyphenation/glue/nsHyphenator.cpp
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHyphenator.h"
 #include "nsIFile.h"
 #include "nsUTF8Utils.h"
 #include "nsUnicodeProperties.h"
-#include "nsUnicharUtilCIID.h"
 #include "nsIURI.h"
 
 #include "hyphen.h"
 
 nsHyphenator::nsHyphenator(nsIURI *aURI)
   : mDict(nullptr)
 {
   nsCString uriSpec;
@@ -61,18 +60,18 @@ nsHyphenator::Hyphenate(const nsAString&
       if (i + 1 < aString.Length() && NS_IS_LOW_SURROGATE(aString[i+1])) {
         ch = SURROGATE_TO_UCS4(ch, aString[i+1]);
         chLen = 2;
       } else {
         NS_WARNING("unpaired surrogate found during hyphenation");
       }
     }
 
-    nsIUGenCategory::nsUGenCategory cat = mozilla::unicode::GetGenCategory(ch);
-    if (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kMark) {
+    nsUGenCategory cat = mozilla::unicode::GetGenCategory(ch);
+    if (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kMark) {
       if (!inWord) {
         inWord = true;
         wordStart = i;
       }
       wordLimit = i + chLen;
       if (i + chLen < aString.Length()) {
         continue;
       }
--- a/intl/lwbrk/moz.build
+++ b/intl/lwbrk/moz.build
@@ -1,32 +1,25 @@
 # -*- Mode: python; 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/.
 
 TEST_DIRS += ['gtest']
 
-XPIDL_SOURCES += [
-    'nsISemanticUnitScanner.idl',
-]
-
-XPIDL_MODULE = 'lwbrk'
-
 EXPORTS += [
     'nsILineBreaker.h',
     'nsIWordBreaker.h',
     'nsLWBrkCIID.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsJISx4051LineBreaker.cpp',
     'nsSampleWordBreaker.cpp',
-    'nsSemanticUnitScanner.cpp',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     SOURCES += [
         'nsPangoBreaker.cpp',
     ]
     CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
deleted file mode 100644
--- a/intl/lwbrk/nsISemanticUnitScanner.idl
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-%{C++
-// {ADF42751-1CEF-4ad2-AA8E-BCB849D8D31F}
-#define NS_SEMANTICUNITSCANNER_CID { 0xadf42751, 0x1cef, 0x4ad2, { 0xaa, 0x8e, 0xbc, 0xb8, 0x49, 0xd8, 0xd3, 0x1f}}
-#define NS_SEMANTICUNITSCANNER_CONTRACTID "@mozilla.org/intl/semanticunitscanner;1"
-%}
-
-/**
- * Provides a language independent way to break UNICODE
- * text into meaningful semantic units (e.g. words).
- */
-[scriptable, uuid(9f620be4-e535-11d6-b254-00039310a47a)]
-interface nsISemanticUnitScanner : nsISupports {
-    /**
-     * start()
-     *
-     * Starts up the semantic unit scanner with an optional
-     * character set, which acts as a hint to optimize the heuristics
-     * used to determine the language(s) of the processed text.
-     *
-     * @param characterSet the character set the text was originally
-     *                     encoded in (can be NULL)
-     */
-    void start(in string characterSet);
-
-    /**
-     * next()
-     * Get the begin / end offset of the next unit in the current text
-     *
-     * @param text the text to be scanned
-     * @param length the number of characters in the text to be processed
-     * @param pos the current position
-     * @param isLastBuffer, the buffer is the last one
-     * @param begin the begin offset of the next unit 
-     * @param begin the end offset of the next unit 
-     * @return has more unit in the current text
-     */
-    boolean next(in wstring text, in long length, in long pos, 
-              in boolean isLastBuffer,
-              out long begin, out long end );
-
-};
--- a/intl/lwbrk/nsJISx4051LineBreaker.cpp
+++ b/intl/lwbrk/nsJISx4051LineBreaker.cpp
@@ -660,17 +660,17 @@ public:
     if (mHasCJKChar)
       return false;
     uint32_t index = mIndex + aOffset;
 
     // If the character at index is a letter (rather than various punctuation
     // characters, etc) then we want a shorter "conservative" range
     uint32_t conservativeRangeStart, conservativeRangeEnd;
     if (index < mLength &&
-        nsIUGenCategory::kLetter ==
+        nsUGenCategory::kLetter ==
           (mText ? GetGenCategory(mText[index])
                  : GetGenCategory(GetUnicodeCharAt(index)))) {
       // Primarily for hyphenated word prefixes/suffixes; we add 1 to Start
       // to get more balanced behavior (if we break off a 2-letter prefix,
       // that means the break will actually be three letters from start of
       // word, to include the hyphen; whereas a 2-letter suffix will be
       // broken only two letters from end of word).
       conservativeRangeEnd = CONSERVATIVE_RANGE_LETTER;
deleted file mode 100644
--- a/intl/lwbrk/nsSemanticUnitScanner.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsSemanticUnitScanner.h"
-
-NS_IMPL_ISUPPORTS_INHERITED(nsSemanticUnitScanner, nsSampleWordBreaker, nsISemanticUnitScanner)
-
-nsSemanticUnitScanner::nsSemanticUnitScanner() : nsSampleWordBreaker()
-{
-  /* member initializers and constructor code */
-}
-
-nsSemanticUnitScanner::~nsSemanticUnitScanner()
-{
-  /* destructor code */
-}
-
-
-NS_IMETHODIMP nsSemanticUnitScanner::Start(const char *characterSet)
-{
-    // do nothing for now.
-    return NS_OK;
-}
-
-NS_IMETHODIMP nsSemanticUnitScanner::Next(const char16_t *text, int32_t length, int32_t pos, bool isLastBuffer, int32_t *begin, int32_t *end, bool *_retval)
-{
-    // xxx need to bullet proff and check input pointer 
-    //  make sure begin, end and _retval is not nullptr here
-
-    // if we reach the end, just return
-    if (pos >= length) {
-       *begin = pos;
-       *end = pos;
-       *_retval = false;
-       return NS_OK;
-    }
-
-    uint8_t char_class = nsSampleWordBreaker::GetClass(text[pos]);
-
-    // if we are in chinese mode, return one han letter at a time
-    // we should not do this if we are in Japanese or Korean mode
-    if (kWbClassHanLetter == char_class) {
-       *begin = pos;
-       *end = pos+1;
-       *_retval = true;
-       return NS_OK;
-    }
-
-    int32_t next;
-    // find the next "word"
-    next = NextWord(text, (uint32_t) length, (uint32_t) pos);
-
-    // if we don't have enough text to make decision, return 
-    if (next == NS_WORDBREAKER_NEED_MORE_TEXT) {
-       *begin = pos;
-       *end = isLastBuffer ? length : pos;
-       *_retval = isLastBuffer;
-       return NS_OK;
-    } 
-    
-    // if what we got is space or punct, look at the next break
-    if ((char_class == kWbClassSpace) || (char_class == kWbClassPunct)) {
-        // if the next "word" is not letters, 
-        // call itself recursively with the new pos
-        return Next(text, length, next, isLastBuffer, begin, end, _retval);
-    }
-
-    // for the rest, return 
-    *begin = pos;
-    *end = next;
-    *_retval = true;
-    return NS_OK;
-}
-
deleted file mode 100644
--- a/intl/lwbrk/nsSemanticUnitScanner.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsSemanticUnitScanner_h__
-#define nsSemanticUnitScanner_h__
-
-#include "nsSampleWordBreaker.h"
-#include "nsISemanticUnitScanner.h"
-
-
-class nsSemanticUnitScanner : public nsISemanticUnitScanner
-                            , public nsSampleWordBreaker
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSISEMANTICUNITSCANNER
-
-  nsSemanticUnitScanner();
-
-private:
-  virtual ~nsSemanticUnitScanner();
-  /* additional members */
-};
-
-#endif
--- a/intl/unicharutil/moz.build
+++ b/intl/unicharutil/moz.build
@@ -11,25 +11,21 @@ XPIDL_SOURCES += [
     'nsIEntityConverter.idl',
     'nsISaveAsCharset.idl',
     'nsIUnicodeNormalizer.idl',
 ]
 
 XPIDL_MODULE = 'unicharutil'
 
 EXPORTS += [
-    'nsICaseConversion.h',
-    'nsIUGenCategory.h',
-    'nsUnicharUtilCIID.h',
+    'nsUGenCategory.h',
     'nsUnicodeNormalizer.h',
 ]
 
 UNIFIED_SOURCES += [
-    'nsCaseConversionImp2.cpp',
-    'nsCategoryImp.cpp',
     'nsEntityConverter.cpp',
     'nsSaveAsCharset.cpp',
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     UNIFIED_SOURCES += [
         'nsUnicodeNormalizer_ICU.cpp',
     ]
deleted file mode 100644
--- a/intl/unicharutil/nsCaseConversionImp2.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsCaseConversionImp2.h"
-#include "nsUnicharUtils.h"
-
-NS_IMETHODIMP_(MozExternalRefCountType) nsCaseConversionImp2::AddRef(void)
-{
-  return (MozExternalRefCountType)1;
-}
-
-NS_IMETHODIMP_(MozExternalRefCountType) nsCaseConversionImp2::Release(void)
-{
-  return (MozExternalRefCountType)1;
-}
-
-NS_IMPL_QUERY_INTERFACE(nsCaseConversionImp2, nsICaseConversion)
-
-NS_IMETHODIMP nsCaseConversionImp2::ToUpper(char16_t aChar, char16_t* aReturn)
-{
-  *aReturn = ToUpperCase(aChar);
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsCaseConversionImp2::ToLower(char16_t aChar, char16_t* aReturn)
-{
-  *aReturn = ToLowerCase(aChar);
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsCaseConversionImp2::ToTitle(char16_t aChar, char16_t* aReturn)
-{
-  *aReturn = ToTitleCase(aChar);
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsCaseConversionImp2::ToUpper(const char16_t* anArray,
-                                         char16_t* aReturn,
-                                         uint32_t aLen)
-{
-  ToUpperCase(anArray, aReturn, aLen);
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsCaseConversionImp2::ToLower(const char16_t* anArray,
-                                         char16_t* aReturn,
-                                         uint32_t aLen)
-{
-  ToLowerCase(anArray, aReturn, aLen);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsCaseConversionImp2::CaseInsensitiveCompare(const char16_t *aLeft,
-                                             const char16_t *aRight,
-                                             uint32_t aCount, int32_t* aResult)
-{
-  *aResult = ::CaseInsensitiveCompare(aLeft, aRight, aCount);
-  return NS_OK;
-}
deleted file mode 100644
--- a/intl/unicharutil/nsCaseConversionImp2.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsCaseConversionImp2_h__
-#define nsCaseConversionImp2_h__
-
-#include "nscore.h"
-#include "nsISupports.h"
-
-#include "nsICaseConversion.h"
-
-class nsCaseConversionImp2 : public nsICaseConversion { 
-  NS_DECL_THREADSAFE_ISUPPORTS 
-
-public:
-  virtual ~nsCaseConversionImp2() { }
-
-  static nsCaseConversionImp2* GetInstance();
-
-  NS_IMETHOD ToUpper(char16_t aChar, char16_t* aReturn) override;
-
-  NS_IMETHOD ToLower(char16_t aChar, char16_t* aReturn) override;
-
-  NS_IMETHOD ToTitle(char16_t aChar, char16_t* aReturn) override;
-
-  NS_IMETHOD ToUpper(const char16_t* anArray, char16_t* aReturn, uint32_t aLen) override;
-
-  NS_IMETHOD ToLower(const char16_t* anArray, char16_t* aReturn, uint32_t aLen) override;
-
-  NS_IMETHOD CaseInsensitiveCompare(const char16_t* aLeft, const char16_t* aRight, uint32_t aLength, int32_t *aResult) override;
-};
-
-#endif
deleted file mode 100644
--- a/intl/unicharutil/nsCategoryImp.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nscore.h"
-#include "nsISupports.h"
-#include "nsCategoryImp.h"
-#include "nsUnicodeProperties.h"
-
-NS_IMPL_QUERY_INTERFACE(nsCategoryImp, nsIUGenCategory)
-
-NS_IMETHODIMP_(MozExternalRefCountType) nsCategoryImp::AddRef(void)
-{
-  return MozExternalRefCountType(1);
-}
-
-NS_IMETHODIMP_(MozExternalRefCountType) nsCategoryImp::Release(void)
-{
-  return MozExternalRefCountType(1);
-}
-
-nsCategoryImp* nsCategoryImp::GetInstance()
-{
-  static nsCategoryImp categoryImp;
-  return &categoryImp;
-}
-
-nsIUGenCategory::nsUGenCategory nsCategoryImp::Get(uint32_t aChar)
-{
-  return mozilla::unicode::GetGenCategory(aChar);
-}
deleted file mode 100644
--- a/intl/unicharutil/nsCategoryImp.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsCategoryImp_h__
-#define nsCategoryImp_h__
-
-#include "nsIUGenCategory.h"
-
-class nsCategoryImp : public nsIUGenCategory {
-   NS_DECL_THREADSAFE_ISUPPORTS
-   
-public:
-   static nsCategoryImp* GetInstance();
-    
-   /**
-    * Give a Unichar, return a nsUGenCategory
-    */
-   virtual nsUGenCategory Get(uint32_t aChar) override;
-};
-
-#endif  /* nsCategoryImp_h__ */
deleted file mode 100644
--- a/intl/unicharutil/nsICaseConversion.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsICaseConversion_h__
-#define nsICaseConversion_h__
-
-
-#include "nsISupports.h"
-#include "nscore.h"
-
-// {07D3D8E0-9614-11d2-B3AD-00805F8A6670}
-#define NS_ICASECONVERSION_IID \
-{ 0x7d3d8e0, 0x9614, 0x11d2, \
-    { 0xb3, 0xad, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
-
-class nsICaseConversion : public nsISupports {
-
-public: 
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICASECONVERSION_IID)
-
-  // Convert one Unicode character into upper case
-  NS_IMETHOD ToUpper( char16_t aChar, char16_t* aReturn) = 0;
-
-  // Convert one Unicode character into lower case
-  NS_IMETHOD ToLower( char16_t aChar, char16_t* aReturn) = 0;
-
-  // Convert one Unicode character into title case
-  NS_IMETHOD ToTitle( char16_t aChar, char16_t* aReturn) = 0;
-
-  // Convert an array of Unicode characters into upper case
-  NS_IMETHOD ToUpper( const char16_t* anArray, char16_t* aReturn, uint32_t aLen) = 0;
-
-  // Convert an array of Unicode characters into lower case
-  NS_IMETHOD ToLower( const char16_t* anArray, char16_t* aReturn, uint32_t aLen) = 0;
-
-  // case-insensitive char16_t* comparison - aResult returns similar
-  // to strcasecmp
-  NS_IMETHOD CaseInsensitiveCompare(const char16_t* aLeft, const char16_t* aRight, uint32_t aLength, int32_t* aResult) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsICaseConversion, NS_ICASECONVERSION_IID)
-
-#endif  /* nsICaseConversion_h__ */
deleted file mode 100644
--- a/intl/unicharutil/nsIUGenCategory.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsIUGenCategory_h__
-#define nsIUGenCategory_h__
-
-
-#include "nsISupports.h"
-#include "nscore.h"
-
-// {671fea05-fcee-4b1c-82a3-6eb03eda8ddc}
-#define NS_IUGENCATEGORY_IID \
-{ 0x671fea05, 0xfcee, 0x4b1c, \
-    { 0x82, 0xa3, 0x6e, 0xb0, 0x3e, 0xda, 0x8d, 0xdc } }
-
-
-class nsIUGenCategory : public nsISupports {
-
-public: 
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IUGENCATEGORY_IID)
-
-   /**
-    *  Read http://unicode.org/reports/tr44/#General_Category_Values
-    *  for the detailed definition of the following categories
-    */
-   typedef enum {
-     kUndefined    = 0,
-     kMark         = 1, // Mn, Mc, and Me
-     kNumber       = 2, // Nd, Nl, and No 
-     kSeparator    = 3, // Zs, Zl, and Zp
-     kOther        = 4, // Cc, Cf, Cs, Co, and Cn
-     kLetter       = 5, // Lu, Ll, Lt, Lm, and Lo
-     kPunctuation  = 6, // Pc, Pd, Ps, Pe, Pi, Pf, and Po
-     kSymbol       = 7  // Sm, Sc, Sk, and So
-   } nsUGenCategory;
-
-   /**
-    * Give a Unichar, return a nsUGenCategory
-    */
-   virtual nsUGenCategory Get(uint32_t aChar) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIUGenCategory, NS_IUGENCATEGORY_IID)
-
-#endif  /* nsIUGenCategory_h__ */
new file mode 100644
--- /dev/null
+++ b/intl/unicharutil/nsUGenCategory.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsUGenCategory_h
+#define nsUGenCategory_h
+
+/**
+ *  Read http://unicode.org/reports/tr44/#General_Category_Values
+ *  for the detailed definition of the following categories
+ */
+enum class nsUGenCategory {
+  kUndefined    = 0,
+  kMark         = 1, // Mn, Mc, and Me
+  kNumber       = 2, // Nd, Nl, and No
+  kSeparator    = 3, // Zs, Zl, and Zp
+  kOther        = 4, // Cc, Cf, Cs, Co, and Cn
+  kLetter       = 5, // Lu, Ll, Lt, Lm, and Lo
+  kPunctuation  = 6, // Pc, Pd, Ps, Pe, Pi, Pf, and Po
+  kSymbol       = 7  // Sm, Sc, Sk, and So
+};
+
+#endif // nsUGenCategory_h
deleted file mode 100644
--- a/intl/unicharutil/nsUnicharUtilCIID.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsUnicharUtilCIID_h__
-#define nsUnicharUtilCIID_h__
-
-
-#include "nsISupports.h"
-#include "nscore.h"
-
-#define NS_UNICHARUTIL_CID \
-{ 0xcc10c750, 0x9ec3, 0x11d2, \
-  { 0xb3, 0xae, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
-
-#define NS_UNICHARUTIL_CONTRACTID "@mozilla.org/intl/unicharutil;1"
-
-#define NS_UNICHARCATEGORY_CID \
-{ 0x748a1132, 0x671a, 0x409a, \
-  { 0x8d, 0x1d, 0xf1, 0xcd, 0xf6, 0xb3, 0xa6, 0xb4 } }
-
-#define NS_UNICHARCATEGORY_CONTRACTID "@mozilla.org/intl/unicharcategory;1"
-
-#endif
--- a/intl/unicharutil/util/IrishCasing.cpp
+++ b/intl/unicharutil/util/IrishCasing.cpp
@@ -211,17 +211,17 @@ const uint8_t IrishCasing::sUcClasses[26
 uint8_t
 IrishCasing::GetClass(uint32_t aCh)
 {
   using mozilla::unicode::GetGenCategory;
   if (aCh >= 'a' && aCh <= 'z') {
     return sLcClasses[aCh - 'a'];
   } else if (aCh >= 'A' && aCh <= 'Z') {
     return sUcClasses[aCh - 'A'];
-  } else if (GetGenCategory(aCh) == nsIUGenCategory::kLetter) {
+  } else if (GetGenCategory(aCh) == nsUGenCategory::kLetter) {
     if (aCh == a_ACUTE || aCh == e_ACUTE || aCh == i_ACUTE ||
         aCh == o_ACUTE || aCh == u_ACUTE) {
       return kClass_vowel;
     } else if (aCh == A_ACUTE || aCh == E_ACUTE || aCh == I_ACUTE ||
                aCh == O_ACUTE || aCh == U_ACUTE) {
       return kClass_Vowel;
     } else {
       return kClass_letter;
--- a/intl/unicharutil/util/nsUnicodeProperties.cpp
+++ b/intl/unicharutil/util/nsUnicodeProperties.cpp
@@ -93,51 +93,51 @@ that the Plane 0 pages are always the fi
 array.
 
 The division of the remaining 16 bits into Page and Char fields is
 adjusted for each property (by experiment using the generation tool)
 to provide the most compact storage, depending on the distribution
 of values.
 */
 
-const nsIUGenCategory::nsUGenCategory sDetailedToGeneralCategory[] = {
+const nsUGenCategory sDetailedToGeneralCategory[] = {
   /*
    * The order here corresponds to the HB_UNICODE_GENERAL_CATEGORY_* constants
    * of the hb_unicode_general_category_t enum in gfx/harfbuzz/src/hb-unicode.h.
    */
-  /* CONTROL */             nsIUGenCategory::kOther,
-  /* FORMAT */              nsIUGenCategory::kOther,
-  /* UNASSIGNED */          nsIUGenCategory::kOther,
-  /* PRIVATE_USE */         nsIUGenCategory::kOther,
-  /* SURROGATE */           nsIUGenCategory::kOther,
-  /* LOWERCASE_LETTER */    nsIUGenCategory::kLetter,
-  /* MODIFIER_LETTER */     nsIUGenCategory::kLetter,
-  /* OTHER_LETTER */        nsIUGenCategory::kLetter,
-  /* TITLECASE_LETTER */    nsIUGenCategory::kLetter,
-  /* UPPERCASE_LETTER */    nsIUGenCategory::kLetter,
-  /* COMBINING_MARK */      nsIUGenCategory::kMark,
-  /* ENCLOSING_MARK */      nsIUGenCategory::kMark,
-  /* NON_SPACING_MARK */    nsIUGenCategory::kMark,
-  /* DECIMAL_NUMBER */      nsIUGenCategory::kNumber,
-  /* LETTER_NUMBER */       nsIUGenCategory::kNumber,
-  /* OTHER_NUMBER */        nsIUGenCategory::kNumber,
-  /* CONNECT_PUNCTUATION */ nsIUGenCategory::kPunctuation,
-  /* DASH_PUNCTUATION */    nsIUGenCategory::kPunctuation,
-  /* CLOSE_PUNCTUATION */   nsIUGenCategory::kPunctuation,
-  /* FINAL_PUNCTUATION */   nsIUGenCategory::kPunctuation,
-  /* INITIAL_PUNCTUATION */ nsIUGenCategory::kPunctuation,
-  /* OTHER_PUNCTUATION */   nsIUGenCategory::kPunctuation,
-  /* OPEN_PUNCTUATION */    nsIUGenCategory::kPunctuation,
-  /* CURRENCY_SYMBOL */     nsIUGenCategory::kSymbol,
-  /* MODIFIER_SYMBOL */     nsIUGenCategory::kSymbol,
-  /* MATH_SYMBOL */         nsIUGenCategory::kSymbol,
-  /* OTHER_SYMBOL */        nsIUGenCategory::kSymbol,
-  /* LINE_SEPARATOR */      nsIUGenCategory::kSeparator,
-  /* PARAGRAPH_SEPARATOR */ nsIUGenCategory::kSeparator,
-  /* SPACE_SEPARATOR */     nsIUGenCategory::kSeparator
+  /* CONTROL */             nsUGenCategory::kOther,
+  /* FORMAT */              nsUGenCategory::kOther,
+  /* UNASSIGNED */          nsUGenCategory::kOther,
+  /* PRIVATE_USE */         nsUGenCategory::kOther,
+  /* SURROGATE */           nsUGenCategory::kOther,
+  /* LOWERCASE_LETTER */    nsUGenCategory::kLetter,
+  /* MODIFIER_LETTER */     nsUGenCategory::kLetter,
+  /* OTHER_LETTER */        nsUGenCategory::kLetter,
+  /* TITLECASE_LETTER */    nsUGenCategory::kLetter,
+  /* UPPERCASE_LETTER */    nsUGenCategory::kLetter,
+  /* COMBINING_MARK */      nsUGenCategory::kMark,
+  /* ENCLOSING_MARK */      nsUGenCategory::kMark,
+  /* NON_SPACING_MARK */    nsUGenCategory::kMark,
+  /* DECIMAL_NUMBER */      nsUGenCategory::kNumber,
+  /* LETTER_NUMBER */       nsUGenCategory::kNumber,
+  /* OTHER_NUMBER */        nsUGenCategory::kNumber,
+  /* CONNECT_PUNCTUATION */ nsUGenCategory::kPunctuation,
+  /* DASH_PUNCTUATION */    nsUGenCategory::kPunctuation,
+  /* CLOSE_PUNCTUATION */   nsUGenCategory::kPunctuation,
+  /* FINAL_PUNCTUATION */   nsUGenCategory::kPunctuation,
+  /* INITIAL_PUNCTUATION */ nsUGenCategory::kPunctuation,
+  /* OTHER_PUNCTUATION */   nsUGenCategory::kPunctuation,
+  /* OPEN_PUNCTUATION */    nsUGenCategory::kPunctuation,
+  /* CURRENCY_SYMBOL */     nsUGenCategory::kSymbol,
+  /* MODIFIER_SYMBOL */     nsUGenCategory::kSymbol,
+  /* MATH_SYMBOL */         nsUGenCategory::kSymbol,
+  /* OTHER_SYMBOL */        nsUGenCategory::kSymbol,
+  /* LINE_SEPARATOR */      nsUGenCategory::kSeparator,
+  /* PARAGRAPH_SEPARATOR */ nsUGenCategory::kSeparator,
+  /* SPACE_SEPARATOR */     nsUGenCategory::kSeparator
 };
 
 #ifdef ENABLE_INTL_API
 const hb_unicode_general_category_t sICUtoHBcategory[U_CHAR_CATEGORY_COUNT] = {
   HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, // U_GENERAL_OTHER_TYPES = 0,
   HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, // U_UPPERCASE_LETTER = 1,
   HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, // U_LOWERCASE_LETTER = 2,
   HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, // U_TITLECASE_LETTER = 3,
--- a/intl/unicharutil/util/nsUnicodeProperties.h
+++ b/intl/unicharutil/util/nsUnicodeProperties.h
@@ -3,32 +3,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_UNICODEPROPERTIES_H
 #define NS_UNICODEPROPERTIES_H
 
 #include "nsBidiUtils.h"
-#include "nsIUGenCategory.h"
+#include "nsUGenCategory.h"
 #include "nsUnicodeScriptCodes.h"
 #include "harfbuzz/hb.h"
 
 #if ENABLE_INTL_API
 #include "unicode/uchar.h"
 #include "unicode/uscript.h"
 #endif
 
 const nsCharProps2& GetCharProps2(uint32_t aCh);
 
 namespace mozilla {
 
 namespace unicode {
 
-extern const nsIUGenCategory::nsUGenCategory sDetailedToGeneralCategory[];
+extern const nsUGenCategory sDetailedToGeneralCategory[];
 
 /* This MUST match the values assigned by genUnicodePropertyData.pl! */
 enum VerticalOrientation {
   VERTICAL_ORIENTATION_U  = 0,
   VERTICAL_ORIENTATION_R  = 1,
   VERTICAL_ORIENTATION_Tu = 2,
   VERTICAL_ORIENTATION_Tr = 3
 };
@@ -229,18 +229,18 @@ bool IsEastAsianWidthFWH(uint32_t aCh);
 // Return whether the char is default-ignorable.
 inline bool IsDefaultIgnorable(uint32_t aCh)
 {
   return GetCharProps2(aCh).mDefaultIgnorable;
 }
 
 #endif // !ENABLE_INTL_API
 
-// returns the simplified Gen Category as defined in nsIUGenCategory
-inline nsIUGenCategory::nsUGenCategory GetGenCategory(uint32_t aCh) {
+// returns the simplified Gen Category as defined in nsUGenCategory
+inline nsUGenCategory GetGenCategory(uint32_t aCh) {
   return sDetailedToGeneralCategory[GetGeneralCategory(aCh)];
 }
 
 inline VerticalOrientation GetVerticalOrientation(uint32_t aCh) {
   return VerticalOrientation(GetCharProps2(aCh).mVertOrient);
 }
 
 inline IdentifierType GetIdentifierType(uint32_t aCh) {
--- a/layout/base/GeckoRestyleManager.h
+++ b/layout/base/GeckoRestyleManager.h
@@ -268,22 +268,16 @@ public:
    *                        descendants.
    * @param aRestyleHintData: Additional data to go with aRestyleHint.
    */
   void PostRestyleEvent(Element* aElement,
                         nsRestyleHint aRestyleHint,
                         nsChangeHint aMinChangeHint,
                         const RestyleHintData* aRestyleHintData = nullptr);
 
-  void PostRestyleEventForCSSRuleChanges(Element* aElement,
-                                         nsRestyleHint aRestyleHint,
-                                         nsChangeHint aMinChangeHint) {
-    PostRestyleEvent(aElement, aRestyleHint, aMinChangeHint);
-  }
-
 public:
   /**
    * Asynchronously clear style data from the root frame downwards and ensure
    * it will all be rebuilt. This is safe to call anytime; it will schedule
    * a restyle and take effect next time style changes are flushed.
    * This method is used to recompute the style data when some change happens
    * outside of any style rules, like a color preference change or a change
    * in a system font size, or to fix things up when an optimization in the
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -161,19 +161,16 @@ public:
   // the container is null, no work is needed.
   void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
 
   MOZ_DECL_STYLO_METHODS(GeckoRestyleManager, ServoRestyleManager)
 
   inline void PostRestyleEvent(dom::Element* aElement,
                                nsRestyleHint aRestyleHint,
                                nsChangeHint aMinChangeHint);
-  inline void PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
-                                                 nsRestyleHint aRestyleHint,
-                                                 nsChangeHint aMinChangeHint);
   inline void RebuildAllStyleData(nsChangeHint aExtraHint,
                                   nsRestyleHint aRestyleHint);
   inline void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                            nsRestyleHint aRestyleHint);
   inline void ProcessPendingRestyles();
   inline void ContentStateChanged(nsIContent* aContent,
                                   EventStates aStateMask);
   inline void AttributeWillChange(dom::Element* aElement,
--- a/layout/base/RestyleManagerInlines.h
+++ b/layout/base/RestyleManagerInlines.h
@@ -20,25 +20,16 @@ void
 RestyleManager::PostRestyleEvent(dom::Element* aElement,
                                  nsRestyleHint aRestyleHint,
                                  nsChangeHint aMinChangeHint)
 {
   MOZ_STYLO_FORWARD(PostRestyleEvent, (aElement, aRestyleHint, aMinChangeHint));
 }
 
 void
-RestyleManager::PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
-                                                   nsRestyleHint aRestyleHint,
-                                                   nsChangeHint aMinChangeHint)
-{
-  MOZ_STYLO_FORWARD(PostRestyleEventForCSSRuleChanges,
-                    (aElement, aRestyleHint, aMinChangeHint));
-}
-
-void
 RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
                                     nsRestyleHint aRestyleHint)
 {
   MOZ_STYLO_FORWARD(RebuildAllStyleData, (aExtraHint, aRestyleHint));
 }
 
 void
 RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -62,24 +62,20 @@ ServoRestyleManager::PostRestyleEvent(El
   if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
     mHaveNonAnimationRestyles = true;
   }
 
   Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
 }
 
 void
-ServoRestyleManager::PostRestyleEventForCSSRuleChanges(
-  Element* aElement,
-  nsRestyleHint aRestyleHint,
-  nsChangeHint aMinChangeHint)
+ServoRestyleManager::PostRestyleEventForCSSRuleChanges()
 {
   mRestyleForCSSRuleChanges = true;
-
-  PostRestyleEvent(aElement, aRestyleHint, aMinChangeHint);
+  mPresContext->PresShell()->EnsureStyleFlush();
 }
 
 /* static */ void
 ServoRestyleManager::PostRestyleEventForAnimations(Element* aElement,
                                                    nsRestyleHint aRestyleHint)
 {
   Servo_NoteExplicitHints(aElement, aRestyleHint, nsChangeHint(0));
 }
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -38,20 +38,17 @@ public:
   typedef ServoElementSnapshotTable SnapshotTable;
   typedef RestyleManager base_type;
 
   explicit ServoRestyleManager(nsPresContext* aPresContext);
 
   void PostRestyleEvent(dom::Element* aElement,
                         nsRestyleHint aRestyleHint,
                         nsChangeHint aMinChangeHint);
-  void PostRestyleEventForLazyConstruction();
-  void PostRestyleEventForCSSRuleChanges(dom::Element* aElement,
-                                         nsRestyleHint aRestyleHint,
-                                         nsChangeHint aMinChangeHint);
+  void PostRestyleEventForCSSRuleChanges();
   void RebuildAllStyleData(nsChangeHint aExtraHint,
                            nsRestyleHint aRestyleHint);
   void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                     nsRestyleHint aRestyleHint);
   void ProcessPendingRestyles();
 
   void UpdateOnlyAnimationStyles();
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1890,19 +1890,19 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
   // Update any popups that may need to be moved or hidden due to their
   // anchor changing.
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm) {
     pm->UpdatePopupPositions(this);
   }
 #endif
 
-  nsCOMArray<nsIDocument> documents;
+  AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
   CollectDocuments(mPresContext->Document(), &documents);
-  for (int32_t i = 0; i < documents.Count(); ++i) {
+  for (uint32_t i = 0; i < documents.Length(); ++i) {
     nsIDocument* doc = documents[i];
     doc->UpdateIntersectionObservations();
     doc->ScheduleIntersectionObserverNotification();
   }
 
   /*
    * Perform notification to imgIRequests subscribed to listen
    * for refresh events.
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -334,26 +334,37 @@ private:
   private:
     Selection *mSelection;
     SelectionRegion mRegion;
     nsIPresShell::ScrollAxis mVerticalScroll;
     nsIPresShell::ScrollAxis mHorizontalScroll;
     int32_t mFlags;
   };
 
-  void setAnchorFocusRange(int32_t aIndex); // pass in index into mRanges;
-                                            // negative value clears
-                                            // mAnchorFocusRange
+  /**
+   * Set mAnchorFocusRange to mRanges[aIndex] if aIndex is a valid index.
+   * Set mAnchorFocusRange to nullptr if aIndex is negative.
+   * Otherwise, i.e., if aIndex is positive but out of bounds of mRanges, do
+   * nothing.
+   */
+  void SetAnchorFocusRange(int32_t aIndex);
   void SelectFramesForContent(nsIContent* aContent, bool aSelected);
-  nsresult     SelectAllFramesForContent(nsIContentIterator *aInnerIter,
-                               nsIContent *aContent,
-                               bool aSelected);
-  nsresult     selectFrames(nsPresContext* aPresContext, nsRange *aRange, bool aSelect);
-  nsresult     getTableCellLocationFromRange(nsRange *aRange, int32_t *aSelectionType, int32_t *aRow, int32_t *aCol);
-  nsresult     addTableCellRange(nsRange *aRange, bool *aDidAddRange, int32_t *aOutIndex);
+  nsresult SelectAllFramesForContent(nsIContentIterator* aInnerIter,
+                                     nsIContent *aContent,
+                                     bool aSelected);
+  nsresult SelectFrames(nsPresContext* aPresContext,
+                        nsRange* aRange,
+                        bool aSelect);
+  nsresult GetTableCellLocationFromRange(nsRange* aRange,
+                                         int32_t* aSelectionType,
+                                         int32_t* aRow,
+                                         int32_t* aCol);
+  nsresult AddTableCellRange(nsRange* aRange,
+                             bool* aDidAddRange,
+                             int32_t* aOutIndex);
 
   nsresult FindInsertionPoint(
       nsTArray<RangeData>* aElementArray,
       nsINode* aPointNode, int32_t aPointOffset,
       nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
       int32_t* aPoint);
   bool EqualsRangeAtPoint(nsINode* aBeginNode, int32_t aBeginOffset,
                             nsINode* aEndNode, int32_t aEndOffset,
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -515,17 +515,17 @@ struct MOZ_RAII AutoPrepareFocusRange
 
     // Remove all generated ranges (including the old mAnchorFocusRange).
     RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
     size_t i = len;
     while (i--) {
       range = aSelection->mRanges[i].mRange;
       if (range->IsGenerated()) {
         range->SetSelection(nullptr);
-        aSelection->selectFrames(presContext, range, false);
+        aSelection->SelectFrames(presContext, range, false);
         aSelection->mRanges.RemoveElementAt(i);
       }
     }
     if (aSelection->mFrameSelection) {
       aSelection->mFrameSelection->InvalidateDesiredPos();
     }
   }
 
@@ -3216,17 +3216,17 @@ nsFrameSelection::SelectCellElement(nsIC
 
   // Get child offset
   int32_t offset = parent->IndexOf(aCellElement);
 
   return CreateAndAddRange(parent, offset);
 }
 
 nsresult
-Selection::getTableCellLocationFromRange(nsRange* aRange,
+Selection::GetTableCellLocationFromRange(nsRange* aRange,
                                          int32_t* aSelectionType,
                                          int32_t* aRow, int32_t* aCol)
 {
   if (!aRange || !aSelectionType || !aRow || !aCol)
     return NS_ERROR_NULL_POINTER;
 
   *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
   *aRow = 0;
@@ -3260,17 +3260,17 @@ Selection::getTableCellLocationFromRange
     return result;
   if (!cellLayout)
     return NS_ERROR_FAILURE;
 
   return cellLayout->GetCellIndexes(*aRow, *aCol);
 }
 
 nsresult
-Selection::addTableCellRange(nsRange* aRange, bool* aDidAddRange,
+Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
                              int32_t* aOutIndex)
 {  
   if (!aDidAddRange || !aOutIndex)
     return NS_ERROR_NULL_POINTER;
 
   *aDidAddRange = false;
   *aOutIndex = -1;
 
@@ -3279,17 +3279,17 @@ Selection::addTableCellRange(nsRange* aR
 
   if (!aRange)
     return NS_ERROR_NULL_POINTER;
 
   nsresult result;
 
   // Get if we are adding a cell selection and the row, col of cell if we are
   int32_t newRow, newCol, tableMode;
-  result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
+  result = GetTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
   if (NS_FAILED(result)) return result;
   
   // If not adding a cell range, we are done here
   if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
   {
     mFrameSelection->mSelectingTableCellMode = tableMode;
     // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
     return NS_OK;
@@ -3506,17 +3506,17 @@ Selection::Selection(nsFrameSelection* a
   , mUserInitiated(false)
   , mCalledByJS(false)
   , mSelectionChangeBlockerCount(0)
 {
 }
 
 Selection::~Selection()
 {
-  setAnchorFocusRange(-1);
+  SetAnchorFocusRange(-1);
 
   uint32_t count = mRanges.Length();
   for (uint32_t i = 0; i < count; ++i) {
     mRanges[i].mRange->SetSelection(nullptr);
   }
 
   if (mAutoScrollTimer) {
     mAutoScrollTimer->Stop();
@@ -3639,17 +3639,17 @@ Selection::GetFocusNode()
 NS_IMETHODIMP
 Selection::GetFocusOffset(int32_t* aFocusOffset)
 {
   *aFocusOffset = static_cast<int32_t>(FocusOffset());
   return NS_OK;
 }
 
 void
-Selection::setAnchorFocusRange(int32_t indx)
+Selection::SetAnchorFocusRange(int32_t indx)
 {
   if (indx >= (int32_t)mRanges.Length())
     return;
   if (indx < 0) //release all
   {
     mAnchorFocusRange = nullptr;
   }
   else{
@@ -4098,21 +4098,21 @@ Selection::RemoveCollapsedRanges()
     }
   }
   return NS_OK;
 }
 
 nsresult
 Selection::Clear(nsPresContext* aPresContext)
 {
-  setAnchorFocusRange(-1);
+  SetAnchorFocusRange(-1);
 
   for (uint32_t i = 0; i < mRanges.Length(); ++i) {
     mRanges[i].mRange->SetSelection(nullptr);
-    selectFrames(aPresContext, mRanges[i].mRange, false);
+    SelectFrames(aPresContext, mRanges[i].mRange, false);
   }
   mRanges.Clear();
 
   // Reset direction so for more dependable table selection range handling
   SetDirection(eDirNext);
 
   // If this was an ATTENTION selection, change it back to normal now
   if (mFrameSelection &&
@@ -4497,59 +4497,59 @@ Selection::SelectAllFramesForContent(nsI
   }
 
   if (NS_WARN_IF(NS_FAILED(aInnerIter->Init(aContent)))) {
     return NS_ERROR_FAILURE;
   }
 
   for (; !aInnerIter->IsDone(); aInnerIter->Next()) {
     nsINode* node = aInnerIter->GetCurrentNode();
-    // Detect the bug of content iterator, but shouldn't cause a crash in
-    // release builds.
     MOZ_ASSERT(node);
-    nsIContent* innercontent =
-      node && node->IsContent() ? node->AsContent() : nullptr;
+    nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr;
     SelectFramesForContent(innercontent, aSelected);
   }
 
   return NS_OK;
 }
 
 /**
  * The idea of this helper method is to select or deselect "top to bottom",
  * traversing through the frames
  */
 nsresult
-Selection::selectFrames(nsPresContext* aPresContext, nsRange* aRange,
+Selection::SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
                         bool aSelect)
 {
   if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
     // nothing to do
     return NS_OK;
   }
-  MOZ_ASSERT(aRange);
+  MOZ_ASSERT(aRange && aRange->IsPositioned());
 
   if (mFrameSelection->GetTableCellSelection()) {
     nsINode* node = aRange->GetCommonAncestor();
     nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
                                 : aPresContext->FrameManager()->GetRootFrame();
     if (frame) {
       frame->InvalidateFrameSubtree();
     }
     return NS_OK;
   }
 
 
   // Loop through the content iterator for each content node; for each text
   // node, call SetSelected on it:
   nsINode* startNode = aRange->GetStartParent();
   nsIContent* startContent =
-    startNode && startNode->IsContent() ? startNode->AsContent() : nullptr;
+    startNode->IsContent() ? startNode->AsContent() : nullptr;
   if (!startContent) {
     // Don't warn, bug 1055722
+    // XXX The range can start from a document node and such range can be
+    //     added to Selection with JS.  Therefore, even in such cases,
+    //     shouldn't we handle selection in the range?
     return NS_ERROR_UNEXPECTED;
   }
 
   // We must call first one explicitly
   bool isFirstContentTextNode = startContent->IsNodeOfType(nsINode::eTEXT);
   nsINode* endNode = aRange->GetEndParent();
   if (isFirstContentTextNode) {
     nsIFrame* frame = startContent->GetPrimaryFrame();
@@ -4586,28 +4586,28 @@ Selection::selectFrames(nsPresContext* a
   nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
   iter->Init(aRange);
   if (isFirstContentTextNode && !iter->IsDone()) {
     iter->Next(); // first content has already been handled.
   }
   nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
   for (; !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
-    // Detect the bug of content iterator, but shouldn't cause a crash in
-    // release builds.
     MOZ_ASSERT(node);
-    nsIContent* content =
-      node && node->IsContent() ? node->AsContent() : nullptr;
+    nsIContent* content = node->IsContent() ? node->AsContent() : nullptr;
     SelectAllFramesForContent(inneriter, content, aSelect);
   }
 
   // We must now do the last one if it is not the same as the first
   if (endNode != startNode) {
     nsIContent* endContent =
-      endNode && endNode->IsContent() ? endNode->AsContent() : nullptr;
+      endNode->IsContent() ? endNode->AsContent() : nullptr;
+    // XXX The range can end at a document node and such range can be
+    //     added to Selection with JS.  Therefore, even in such cases,
+    //     shouldn't we handle selection in the range?
     if (NS_WARN_IF(!endContent)) {
       return NS_ERROR_UNEXPECTED;
     }
     if (endContent->IsNodeOfType(nsINode::eTEXT)) {
       nsIFrame* frame = endContent->GetPrimaryFrame();
       // The frame could be an SVG text frame, in which case we'll ignore it.
       if (frame && frame->IsTextFrame()) {
         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
@@ -4739,17 +4739,17 @@ Selection::Repaint(nsPresContext* aPresC
 
   if (arrCount < 1)
     return NS_OK;
 
   int32_t i;
   
   for (i = 0; i < arrCount; i++)
   {
-    nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
+    nsresult rv = SelectFrames(aPresContext, mRanges[i].mRange, true);
 
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   return NS_OK;
 }
@@ -5045,17 +5045,17 @@ Selection::AddRangeInternal(nsRange& aRa
     // associated with context object. Otherwise, this method must do nothing."
     return;
   }
 
   // This inserts a table cell range in proper document order
   // and returns NS_OK if range doesn't contain just one table cell
   bool didAddRange;
   int32_t rangeIndex;
-  nsresult result = addTableCellRange(&aRange, &didAddRange, &rangeIndex);
+  nsresult result = AddTableCellRange(&aRange, &didAddRange, &rangeIndex);
   if (NS_FAILED(result)) {
     aRv.Throw(result);
     return;
   }
 
   if (!didAddRange) {
     result = AddItem(&aRange, &rangeIndex);
     if (NS_FAILED(result)) {
@@ -5063,25 +5063,25 @@ Selection::AddRangeInternal(nsRange& aRa
       return;
     }
   }
 
   if (rangeIndex < 0) {
     return;
   }
 
-  setAnchorFocusRange(rangeIndex);
+  SetAnchorFocusRange(rangeIndex);
   
   // Make sure the caret appears on the next line, if at a newline
   if (mSelectionType == SelectionType::eNormal) {
     SetInterlinePosition(true);
   }
 
   RefPtr<nsPresContext>  presContext = GetPresContext();
-  selectFrames(presContext, &aRange, true);
+  SelectFrames(presContext, &aRange, true);
 
   if (!mFrameSelection)
     return;//nothing to do
 
   // Be aware, this instance may be destroyed after this call.
   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   result = frameSelection->NotifySelectionListeners(GetType());
@@ -5142,35 +5142,35 @@ Selection::RemoveRange(nsRange& aRange, 
   } else {
     // For non-text nodes, the given offsets should be sufficient.
     beginOffset = aRange.StartOffset();
     endOffset = aRange.EndOffset();
   }
 
   // clear the selected bit from the removed range's frames
   RefPtr<nsPresContext>  presContext = GetPresContext();
-  selectFrames(presContext, &aRange, false);
+  SelectFrames(presContext, &aRange, false);
 
   // add back the selected bit for each range touching our nodes
   nsTArray<nsRange*> affectedRanges;
   rv = GetRangesForIntervalArray(beginNode, beginOffset,
                                  endNode, endOffset,
                                  true, &affectedRanges);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return;
   }
   for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
-    selectFrames(presContext, affectedRanges[i], true);
+    SelectFrames(presContext, affectedRanges[i], true);
   }
 
   int32_t cnt = mRanges.Length();
   if (&aRange == mAnchorFocusRange) {
     // Reset anchor to LAST range or clear it if there are no ranges.
-    setAnchorFocusRange(cnt - 1);
+    SetAnchorFocusRange(cnt - 1);
 
     // When the selection is user-created it makes sense to scroll the range
     // into view. The spell-check selection, however, is created and destroyed
     // in the background. We don't want to scroll in this case or the view
     // might appear to be moving randomly (bug 337871).
     if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
       ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
     }
@@ -5298,18 +5298,18 @@ Selection::Collapse(nsINode& aParentNode
 #endif
 
   int32_t rangeIndex = -1;
   result = AddItem(range, &rangeIndex);
   if (NS_FAILED(result)) {
     aRv.Throw(result);
     return;
   }
-  setAnchorFocusRange(0);
-  selectFrames(presContext, range, true);
+  SetAnchorFocusRange(0);
+  SelectFrames(presContext, range, true);
 
   // Be aware, this instance may be destroyed after this call.
   // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
   result = frameSelection->NotifySelectionListeners(GetType());
   if (NS_FAILED(result)) {
     aRv.Throw(result);
   }
 }
@@ -5495,30 +5495,30 @@ Selection::SetAnchorFocusToRange(nsRange
   nsresult res = RemoveItem(mAnchorFocusRange);
   if (NS_FAILED(res))
     return res;
 
   int32_t aOutIndex = -1;
   res = AddItem(aRange, &aOutIndex, !collapsed);
   if (NS_FAILED(res))
     return res;
-  setAnchorFocusRange(aOutIndex);
+  SetAnchorFocusRange(aOutIndex);
 
   return NS_OK;
 }
 
 void
 Selection::ReplaceAnchorFocusRange(nsRange* aRange)
 {
   NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
   RefPtr<nsPresContext> presContext = GetPresContext();
   if (presContext) {
-    selectFrames(presContext, mAnchorFocusRange, false);
+    SelectFrames(presContext, mAnchorFocusRange, false);
     SetAnchorFocusToRange(aRange);
-    selectFrames(presContext, mAnchorFocusRange, true);
+    SelectFrames(presContext, mAnchorFocusRange, true);
   }
 }
 
 void
 Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
 {
   if (aDirection == mDirection) {
     return;
@@ -5530,21 +5530,21 @@ Selection::AdjustAnchorFocusForMultiRang
   }
 
   nsRange* firstRange = GetRangeAt(0);
   nsRange* lastRange = GetRangeAt(RangeCount() - 1);
 
   if (mDirection == eDirPrevious) {
     firstRange->SetIsGenerated(false);
     lastRange->SetIsGenerated(true);
-    setAnchorFocusRange(0);
+    SetAnchorFocusRange(0);
   } else { // aDir == eDirNext
     firstRange->SetIsGenerated(true);
     lastRange->SetIsGenerated(false);
-    setAnchorFocusRange(RangeCount() - 1);
+    SetAnchorFocusRange(RangeCount() - 1);
   }
 }
 
 /*
 Notes which might come in handy for extend:
 
 We can tell the direction of the selection by asking for the anchors selection
 if the begin is less than the end then we know the selection is to the "right".
@@ -5669,17 +5669,17 @@ Selection::Extend(nsINode& aParentNode, 
   int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
                                                   &aParentNode, aOffset,
                                                   &disconnected);
 
   // If the points are disconnected, the range will be collapsed below,
   // resulting in a range that selects nothing.
   if (shouldClearRange) {
     // Repaint the current range with the selection removed.
-    selectFrames(presContext, range, false);
+    SelectFrames(presContext, range, false);
   }
 
   RefPtr<nsRange> difRange = new nsRange(&aParentNode);
   if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
     //select from 1 to 2 unless they are collapsed
     range->SetEnd(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
@@ -5689,31 +5689,31 @@ Selection::Extend(nsINode& aParentNode, 
     nsresult tmp = difRange->SetStart(focusNode, focusOffset);
     if (NS_FAILED(tmp)) {
       res = tmp;
     }
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
-    selectFrames(presContext, difRange , true);
+    SelectFrames(presContext, difRange , true);
     res = SetAnchorFocusToRange(range);
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
   }
   else if (result1 == 0 && result3 > 0){//2, a1
     //select from 2 to 1a
     SetDirection(eDirPrevious);
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
     }
-    selectFrames(presContext, range, true);
+    SelectFrames(presContext, range, true);
     res = SetAnchorFocusToRange(range);
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
   }
   else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
     //deselect from 2 to 1
@@ -5731,19 +5731,20 @@ Selection::Extend(nsINode& aParentNode, 
     if (aRv.Failed()) {
       return;
     }
     res = SetAnchorFocusToRange(range);
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
-    selectFrames(presContext, difRange, false); // deselect now
+    SelectFrames(presContext, difRange, false); // deselect now
     difRange->SetEnd(range->GetEndParent(), range->EndOffset());
-    selectFrames(presContext, difRange, true); // must reselect last node maybe more
+    SelectFrames(presContext, difRange, true); // must reselect last node
+                                               // maybe more
   }
   else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
     if (GetDirection() == eDirPrevious){
       res = range->SetStart(endNode, endOffset);
       if (NS_FAILED(res)) {
         aRv.Throw(res);
         return;
       }
@@ -5764,28 +5765,28 @@ Selection::Extend(nsINode& aParentNode, 
         return;
       }
       res = SetAnchorFocusToRange(range);
       if (NS_FAILED(res)) {
         aRv.Throw(res);
         return;
       }
       //deselect from 1 to a
-      selectFrames(presContext, difRange , false);
+      SelectFrames(presContext, difRange , false);
     }
     else
     {
       res = SetAnchorFocusToRange(range);
       if (NS_FAILED(res)) {
         aRv.Throw(res);
         return;
       }
     }
     //select from a to 2
-    selectFrames(presContext, range , true);
+    SelectFrames(presContext, range , true);
   }
   else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
     //deselect from 1 to 2
     difRange->SetEnd(aParentNode, aOffset, aRv);
     res = difRange->SetStart(focusNode, focusOffset);
     if (aRv.Failed()) {
       return;
     }
@@ -5799,19 +5800,19 @@ Selection::Extend(nsINode& aParentNode, 
       return;
     }
 
     res = SetAnchorFocusToRange(range);
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
-    selectFrames(presContext, difRange , false);
+    SelectFrames(presContext, difRange , false);
     difRange->SetStart(range->GetStartParent(), range->StartOffset());
-    selectFrames(presContext, difRange, true);//must reselect last node
+    SelectFrames(presContext, difRange, true); // must reselect last node
   }
   else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
     if (GetDirection() == eDirNext){
       range->SetEnd(startNode, startOffset);
     }
     SetDirection(eDirPrevious);
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
@@ -5827,28 +5828,28 @@ Selection::Extend(nsINode& aParentNode, 
       tmp = SetAnchorFocusToRange(range);
       if (NS_FAILED(tmp)) {
         res = tmp;
       }
       if (NS_FAILED(res)) {
         aRv.Throw(res);
         return;
       }
-      selectFrames(presContext, difRange, false);
+      SelectFrames(presContext, difRange, false);
     }
     else
     {
       res = SetAnchorFocusToRange(range);
       if (NS_FAILED(res)) {
         aRv.Throw(res);
         return;
       }
     }
     //select from 2 to a
-    selectFrames(presContext, range , true);
+    SelectFrames(presContext, range , true);
   }
   else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
     //select from 2 to 1
     range->SetStart(aParentNode, aOffset, aRv);
     if (aRv.Failed()) {
       return;
     }
     SetDirection(eDirPrevious);
@@ -5857,29 +5858,29 @@ Selection::Extend(nsINode& aParentNode, 
     if (NS_FAILED(tmp)) {
       res = tmp;
     }
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
 
-    selectFrames(presContext, difRange, true);
+    SelectFrames(presContext, difRange, true);
     res = SetAnchorFocusToRange(range);
     if (NS_FAILED(res)) {
       aRv.Throw(res);
       return;
     }
   }
 
   if (mRanges.Length() > 1) {
     for (size_t i = 0; i < mRanges.Length(); ++i) {
       nsRange* range = mRanges[i].mRange;
       MOZ_ASSERT(range->IsInSelection());
-      selectFrames(presContext, range, range->IsInSelection());
+      SelectFrames(presContext, range, range->IsInSelection());
     }
   }
 
   DEBUG_OUT_RANGE(range);
 #ifdef DEBUG_SELECTION
   if (GetDirection() != oldDirection) {
     printf("    direction changed to %s\n",
            GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -302,17 +302,17 @@ nsCaseTransformTextRunFactory::Transform
 
   bool mergeNeeded = false;
 
   bool capitalizeDutchIJ = false;
   bool prevIsLetter = false;
   bool ntPrefix = false; // true immediately after a word-initial 'n' or 't'
                          // when doing Irish lowercasing
   uint32_t sigmaIndex = uint32_t(-1);
-  nsIUGenCategory::nsUGenCategory cat;
+  nsUGenCategory cat;
 
   uint8_t style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : 0;
   bool forceNonFullWidth = false;
   const nsIAtom* lang = aLanguage;
 
   LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang);
   mozilla::GreekCasing::State greekState;
   mozilla::IrishCasing::State irishState;
@@ -372,17 +372,17 @@ nsCaseTransformTextRunFactory::Transform
           sigmaIndex = uint32_t(-1);
           break;
         }
       }
 
       cat = mozilla::unicode::GetGenCategory(ch);
 
       if (languageSpecificCasing == eLSCB_Irish &&
-          cat == nsIUGenCategory::kLetter) {
+          cat == nsUGenCategory::kLetter) {
         // See bug 1018805 for Irish lowercasing requirements
         if (!prevIsLetter && (ch == 'n' || ch == 't')) {
           ntPrefix = true;
         } else {
           if (ntPrefix && mozilla::IrishCasing::IsUpperVowel(ch)) {
             aConvertedString.Append('-');
             ++extraChars;
           }
@@ -409,17 +409,17 @@ nsCaseTransformTextRunFactory::Transform
       // was a letter, CAPITAL SIGMA maps to FINAL SIGMA and we record the
       // position in the converted string; if we then encounter another letter,
       // that FINAL SIGMA is replaced with a standard SMALL SIGMA.
 
       // If sigmaIndex is not -1, it marks where we have provisionally mapped
       // a CAPITAL SIGMA to FINAL SIGMA; if we now find another letter, we
       // need to change it to SMALL SIGMA.
       if (sigmaIndex != uint32_t(-1)) {
-        if (cat == nsIUGenCategory::kLetter) {
+        if (cat == nsUGenCategory::kLetter) {
           aConvertedString.SetCharAt(GREEK_SMALL_LETTER_SIGMA, sigmaIndex);
         }
       }
 
       if (ch == GREEK_CAPITAL_LETTER_SIGMA) {
         // If preceding char was a letter, map to FINAL instead of SMALL,
         // and note where it occurred by setting sigmaIndex; we'll change it
         // to standard SMALL SIGMA later if another letter follows
@@ -434,18 +434,18 @@ nsCaseTransformTextRunFactory::Transform
         }
         prevIsLetter = true;
         break;
       }
 
       // ignore diacritics for the purpose of contextual sigma mapping;
       // otherwise, reset prevIsLetter appropriately and clear the
       // sigmaIndex marker
-      if (cat != nsIUGenCategory::kMark) {
-        prevIsLetter = (cat == nsIUGenCategory::kLetter);
+      if (cat != nsUGenCategory::kMark) {
+        prevIsLetter = (cat == nsUGenCategory::kLetter);
         sigmaIndex = uint32_t(-1);
       }
 
       mcm = mozilla::unicode::SpecialLower(ch);
       if (mcm) {
         int j = 0;
         while (j < 2 && mcm->mMappedChars[j + 1]) {
           aConvertedString.Append(mcm->mMappedChars[j]);
--- a/layout/reftests/font-face/reftest.list
+++ b/layout/reftests/font-face/reftest.list
@@ -86,17 +86,17 @@ random-if(cocoaWidget) skip-if(styloVsGe
 skip-if(styloVsGecko) HTTP(..) == insert-rule-1a.html insert-rule-1-ref.html
 skip-if(styloVsGecko) HTTP(..) == insert-rule-1b.html insert-rule-1-ref.html
 skip-if(styloVsGecko) HTTP(..) == delete-rule-1.html delete-rule-1-ref.html
 random-if(styloVsGecko) HTTP(..) == media-query-add-1.html media-query-add-1-ref.html
 random-if(styloVsGecko) HTTP(..) == media-query-remove-1.html media-query-remove-1-ref.html
 HTTP(..) != media-query-add-1-ref.html media-query-remove-1-ref.html
 
 HTTP(..) == ahem-metrics-1.html ahem-metrics-1-ref.html
-skip-if(styloVsGecko) fails-if(stylo) HTTP(..) == ex-unit-1.html ex-unit-1-ref.html
+skip-if(styloVsGecko) HTTP(..) == ex-unit-1.html ex-unit-1-ref.html
 skip-if(styloVsGecko||stylo) HTTP(..) == ex-unit-1-dynamic.html ex-unit-1-ref.html
 
 == local-1.html local-1-ref.html
 == local-styled-1.html local-styled-1-ref.html
 
 HTTP(..) == synthetic-weight-style.html synthetic-weight-style-ref.html
 HTTP(..) == synthetic-variations.html synthetic-variations-ref.html
 
--- a/layout/reftests/font-loading-api/reftest.list
+++ b/layout/reftests/font-loading-api/reftest.list
@@ -1,11 +1,11 @@
 default-preferences pref(layout.css.font-loading-api.enabled,true)
 
 HTTP(..) == dynamic-insert-1.html dynamic-insert-1-ref.html
 HTTP(..) == dynamic-remove-1.html dynamic-remove-1-ref.html
-fails-if(styloVsGecko||stylo) HTTP(..) == ex-unit-1.html ../font-face/ex-unit-1-ref.html
+HTTP(..) == ex-unit-1.html ../font-face/ex-unit-1-ref.html
 fuzzy-if(skiaContent,2,10) HTTP(..) == multiple-sets-1.html multiple-sets-1-ref.html
 HTTP(..) == name-collision.html ../font-face/name-collision-ref.html
 HTTP(..) == order-1.html ../font-face/order-1-ref.html
 HTTP(..) == src-list-1.html ../font-face/src-list-1-ref.html
 HTTP(..) == src-list-2.html ../font-face/src-list-2-ref.html
 HTTP(..) == src-list-data-1.html ../font-face/src-list-data-ref.html
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -284,17 +284,17 @@ fuzzy(1,5000) == mask-opacity-01.svg mas
 != mask-root-svg.svg about:blank
 == mask-transformed-01.svg mask-transformed-01-ref.svg
 == mask-transformed-02.svg pass.svg
 == mask-transformed-child-01.svg mask-transformed-child-01-ref.svg
 # fuzzy because of the differences between clipPath and mask clipping
 fuzzy-if(d2d||skiaContent,1,6400) == mask-type-01.svg mask-type-01-ref.svg
 fuzzy-if(d2d||skiaContent,1,6400) == mask-type-02.svg mask-type-01-ref.svg
 fuzzy-if(d2d||skiaContent,1,6400) == mask-type-03.svg mask-type-01-ref.svg
-fuzzy-if(d2d||skiaContent,1,6400) fails-if(styloVsGecko||stylo) == mask-type-04.svg mask-type-01-ref.svg
+fuzzy-if(d2d||skiaContent,1,6400) == mask-type-04.svg mask-type-01-ref.svg
 == mask-use-element-01.svg pass.svg
 
 != nested-mask-mode.svg about:blank
 == nested-viewBox-01.svg pass.svg
 fuzzy-if(skiaContent,3,448000) == nesting-invalid-01.svg nesting-invalid-01-ref.svg
 
 fuzzy-if(d2d&&/^Windows\x20NT\x20(6\.1|10\.0)/.test(http.oscpu),63,168) fuzzy-if(cocoaWidget,1,122) fuzzy-if(skiaContent,2,1000) == non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg # bug 1074161 for Win7 and OSX 10.8
 fuzzy-if(gtkWidget,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) fuzzy-if(Android,9,586) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg
--- a/layout/reftests/tab-size/reftest.list
+++ b/layout/reftests/tab-size/reftest.list
@@ -4,9 +4,9 @@
 == tab-size-4-span.html       spaces-4.html
 == tab-size-4-spanoffset.html spaces-4-offset.html
 == tab-size-4-multiple.html   spaces-4-multiple.html
 == tab-size-1.html            spaces-1.html
 == tab-size-0.html            spaces-0.html
 == tab-size-negative.html     tab-size-initial.html
 == tab-size-change-1a.html     tab-size-change-1-ref.html
 == tab-size-change-1b.html     tab-size-change-1-ref.html
-fuzzy-if(winWidget,100,1000) fails-if(styloVsGecko||stylo) == tab-size-length.html       tab-size-length-ref.html
+fuzzy-if(winWidget,100,1000) == tab-size-length.html       tab-size-length-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSImportRule.cpp
@@ -0,0 +1,54 @@
+/* -*- 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 "mozilla/dom/CSSImportRule.h"
+
+#include "mozilla/dom/CSSImportRuleBinding.h"
+#include "mozilla/dom/MediaList.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ADDREF_INHERITED(CSSImportRule, css::Rule)
+NS_IMPL_RELEASE_INHERITED(CSSImportRule, css::Rule)
+
+// QueryInterface implementation for CSSImportRule
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CSSImportRule)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSImportRule)
+NS_INTERFACE_MAP_END_INHERITING(css::Rule)
+
+bool
+CSSImportRule::IsCCLeaf() const
+{
+  // We're not a leaf.
+  return false;
+}
+
+NS_IMETHODIMP
+CSSImportRule::GetMedia(nsIDOMMediaList** aMedia)
+{
+  NS_ENSURE_ARG_POINTER(aMedia);
+  NS_ADDREF(*aMedia = Media());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CSSImportRule::GetStyleSheet(nsIDOMCSSStyleSheet** aStyleSheet)
+{
+  NS_ENSURE_ARG_POINTER(aStyleSheet);
+  NS_IF_ADDREF(*aStyleSheet = GetStyleSheet());
+  return NS_OK;
+}
+
+/* virtual */ JSObject*
+CSSImportRule::WrapObject(JSContext* aCx,
+                          JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSImportRuleBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/CSSImportRule.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CSSImportRule_h
+#define mozilla_dom_CSSImportRule_h
+
+#include "mozilla/css/Rule.h"
+#include "nsIDOMCSSImportRule.h"
+
+namespace mozilla {
+namespace dom {
+
+class CSSImportRule : public css::Rule
+                    , public nsIDOMCSSImportRule
+{
+protected:
+  using Rule::Rule;
+  virtual ~CSSImportRule() {}
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  bool IsCCLeaf() const final;
+
+  int32_t GetType() const final { return css::Rule::IMPORT_RULE; }
+  using Rule::GetType;
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+    const override = 0;
+
+  // nsIDOMCSSImportRule interface
+  NS_IMETHOD GetMedia(nsIDOMMediaList** aMedia) final;
+  NS_IMETHOD GetStyleSheet(nsIDOMCSSStyleSheet** aStyleSheet) final;
+
+  // WebIDL interface
+  uint16_t Type() const final { return nsIDOMCSSRule::IMPORT_RULE; }
+  // The XPCOM GetHref is fine, since it never fails.
+  virtual dom::MediaList* Media() const = 0;
+  virtual StyleSheet* GetStyleSheet() const = 0;
+
+  JSObject* WrapObject(JSContext* aCx,
+                       JS::Handle<JSObject*> aGivenProto) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CSSImportRule_h
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -322,48 +322,45 @@ CSSStyleSheetInner::SizeOfIncludingThis(
 
 // -------------------------------
 // CSS Style Sheet
 //
 
 CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode,
                              CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy)
   : StyleSheet(StyleBackendType::Gecko, aParsingMode),
-    mOwnerRule(nullptr),
     mInRuleProcessorCache(false),
     mScopeElement(nullptr),
     mRuleProcessors(nullptr)
 {
   mInner = new CSSStyleSheetInner(aCORSMode, aReferrerPolicy,
                                   SRIMetadata());
   mInner->AddSheet(this);
 }
 
 CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode,
                              CORSMode aCORSMode,
                              ReferrerPolicy aReferrerPolicy,
                              const SRIMetadata& aIntegrity)
   : StyleSheet(StyleBackendType::Gecko, aParsingMode),
-    mOwnerRule(nullptr),
     mInRuleProcessorCache(false),
     mScopeElement(nullptr),
     mRuleProcessors(nullptr)
 {
   mInner = new CSSStyleSheetInner(aCORSMode, aReferrerPolicy,
                                   aIntegrity);
   mInner->AddSheet(this);
 }
 
 CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
                              CSSStyleSheet* aParentToUse,
-                             css::ImportRule* aOwnerRuleToUse,
+                             dom::CSSImportRule* aOwnerRuleToUse,
                              nsIDocument* aDocumentToUse,
                              nsINode* aOwningNodeToUse)
-  : StyleSheet(aCopy, aDocumentToUse, aOwningNodeToUse),
-    mOwnerRule(aOwnerRuleToUse),
+  : StyleSheet(aCopy, aOwnerRuleToUse, aDocumentToUse, aOwningNodeToUse),
     mInRuleProcessorCache(false),
     mScopeElement(nullptr),
     mRuleProcessors(nullptr)
 {
   mParent = aParentToUse;
 }
 
 CSSStyleSheet::~CSSStyleSheet()
@@ -565,17 +562,17 @@ CSSStyleSheet::GetStyleRuleAt(int32_t aI
 {
   // Important: If this function is ever made scriptable, we must add
   // a security check here. See GetCssRules below for an example.
   return Inner()->mOrderedRules.SafeObjectAt(aIndex);
 }
 
 already_AddRefed<StyleSheet>
 CSSStyleSheet::Clone(StyleSheet* aCloneParent,
-                     css::ImportRule* aCloneOwnerRule,
+                     dom::CSSImportRule* aCloneOwnerRule,
                      nsIDocument* aCloneDocument,
                      nsINode* aCloneOwningNode) const
 {
   RefPtr<StyleSheet> clone = new CSSStyleSheet(*this,
     static_cast<CSSStyleSheet*>(aCloneParent),
     aCloneOwnerRule,
     aCloneDocument,
     aCloneOwningNode);
@@ -596,18 +593,18 @@ CSSStyleSheet::List(FILE* out, int32_t a
 {
   StyleSheet::List(out, aIndent);
 
   fprintf_stderr(out, "%s", "Rules in source order:\n");
   ListRules(Inner()->mOrderedRules, out, aIndent);
 }
 #endif
 
-void 
-CSSStyleSheet::ClearRuleCascadesInternal()
+void
+CSSStyleSheet::ClearRuleCascades()
 {
   // We might be in ClearRuleCascadesInternal because we had a modification
   // to the sheet that resulted in an nsCSSSelector being destroyed.
   // Tell the RestyleManager for each document we're used in
   // so that they can drop any nsCSSSelector pointers (used for
   // eRestyle_SomeDescendants) in their mPendingRestyles.
   for (StyleSetHandle setHandle : mStyleSets) {
     setHandle->AsGecko()->ClearSelectors();
@@ -623,16 +620,20 @@ CSSStyleSheet::ClearRuleCascadesInternal
         // RuleProcessorCache entries that contain this sheet, as the
         // list of @-moz-document rules might have changed.
         RuleProcessorCache::RemoveSheet(this);
         removedSheetFromRuleProcessorCache = true;
       }
       (*iter)->ClearRuleCascades();
     }
   }
+
+  if (mParent) {
+    static_cast<CSSStyleSheet*>(mParent)->ClearRuleCascades();
+  }
 }
 
 void
 CSSStyleSheet::DidDirty()
 {
   MOZ_ASSERT(!mInner->mComplete || mDirty,
              "caller must have called WillDirty()");
   ClearRuleCascades();
@@ -651,42 +652,25 @@ CSSStyleSheet::RegisterNamespaceRule(css
 }
 
 void
 CSSStyleSheet::SetScopeElement(dom::Element* aScopeElement)
 {
   mScopeElement = aScopeElement;
 }
 
-css::Rule*
-CSSStyleSheet::GetDOMOwnerRule() const
-{
-  return mOwnerRule;
-}
-
 CSSRuleList*
 CSSStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
 {
   if (!mRuleCollection) {
     mRuleCollection = new CSSRuleListImpl(this);
   }
   return mRuleCollection;
 }
 
-static bool
-RuleHasPendingChildSheet(css::Rule *cssRule)
-{
-  nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
-  NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
-  nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
-  importRule->GetStyleSheet(getter_AddRefs(childSheet));
-  RefPtr<CSSStyleSheet> cssSheet = do_QueryObject(childSheet);
-  return cssSheet != nullptr && !cssSheet->IsComplete();
-}
-
 uint32_t
 CSSStyleSheet::InsertRuleInternal(const nsAString& aRule,
                                   uint32_t aIndex,
                                   ErrorResult& aRv)
 {
   MOZ_ASSERT(mInner->mComplete);
 
   WillDirty();
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -108,27 +108,22 @@ public:
 #endif
 
   // XXX do these belong here or are they generic?
   void AppendStyleRule(css::Rule* aRule);
 
   int32_t StyleRuleCount() const;
   css::Rule* GetStyleRuleAt(int32_t aIndex) const;
 
-  void SetOwnerRule(css::ImportRule* aOwnerRule) { mOwnerRule = aOwnerRule; /* Not ref counted */ }
-  css::ImportRule* GetOwnerRule() const { return mOwnerRule; }
-  // Workaround overloaded-virtual warning in GCC.
-  using StyleSheet::GetOwnerRule;
-
   nsXMLNameSpaceMap* GetNameSpaceMap() const {
     return Inner()->mNameSpaceMap;
   }
 
-  virtual already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
-    css::ImportRule* aCloneOwnerRule,
+  already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
+    dom::CSSImportRule* aCloneOwnerRule,
     nsIDocument* aCloneDocument,
     nsINode* aCloneOwningNode) const final;
 
   void SetModifiedByChildRule() {
     NS_ASSERTION(mDirty,
                  "sheet must be marked dirty before handing out child rules");
     DidDirty();
   }
@@ -152,38 +147,32 @@ public:
   static bool RebuildChildList(css::Rule* aRule,
                                ChildSheetListBuilder* aBuilder);
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   dom::Element* GetScopeElement() const { return mScopeElement; }
   void SetScopeElement(dom::Element* aScopeElement);
 
-  // WebIDL CSSStyleSheet API
-  // Can't be inline because we can't include ImportRule here.  And can't be
-  // called GetOwnerRule because that would be ambiguous with the ImportRule
-  // version.
-  css::Rule* GetDOMOwnerRule() const final;
-
   void DidDirty() override;
 
 private:
   CSSStyleSheet(const CSSStyleSheet& aCopy,
                 CSSStyleSheet* aParentToUse,
-                css::ImportRule* aOwnerRuleToUse,
+                dom::CSSImportRule* aOwnerRuleToUse,
                 nsIDocument* aDocumentToUse,
                 nsINode* aOwningNodeToUse);
 
   CSSStyleSheet(const CSSStyleSheet& aCopy) = delete;
   CSSStyleSheet& operator=(const CSSStyleSheet& aCopy) = delete;
 
 protected:
   virtual ~CSSStyleSheet();
 
-  void ClearRuleCascadesInternal() override;
+  void ClearRuleCascades();
 
   // Add the namespace mapping from this @namespace rule to our namespace map
   nsresult RegisterNamespaceRule(css::Rule* aRule);
 
   // Drop our reference to mRuleCollection
   void DropRuleCollection();
 
   CSSStyleSheetInner* Inner() const
@@ -203,18 +192,16 @@ protected:
                               uint32_t aIndex, ErrorResult& aRv);
   void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv);
   nsresult InsertRuleIntoGroupInternal(const nsAString& aRule,
                                        css::GroupRule* aGroup,
                                        uint32_t aIndex);
 
   void EnabledStateChangedInternal();
 
-  css::ImportRule*      mOwnerRule; // weak ref
-
   RefPtr<CSSRuleListImpl> mRuleCollection;
   bool                  mInRuleProcessorCache;
   RefPtr<dom::Element> mScopeElement;
 
   AutoTArray<nsCSSRuleProcessor*, 8>* mRuleProcessors;
 
   friend class mozilla::StyleSheet;
   friend class ::nsCSSRuleProcessor;
--- a/layout/style/CounterStyleManager.h
+++ b/layout/style/CounterStyleManager.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_CounterStyleManager_h_
 #define mozilla_CounterStyleManager_h_
 
+#include "nsIAtom.h"
 #include "nsStringFwd.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 
 #include "nsStyleConsts.h"
 
 #include "mozilla/Attributes.h"
 
--- a/layout/style/GroupRule.h
+++ b/layout/style/GroupRule.h
@@ -74,17 +74,17 @@ struct GeckoGroupRuleRules
 
   IncrementalClearCOMRuleArray mRules;
   RefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
 };
 
 struct ServoGroupRuleRules
 {
   explicit ServoGroupRuleRules(already_AddRefed<ServoCssRules> aRawRules)
-    : mRuleList(new ServoCSSRuleList(Move(aRawRules))) {}
+    : mRuleList(new ServoCSSRuleList(Move(aRawRules), nullptr)) {}
   ServoGroupRuleRules(ServoGroupRuleRules&& aOther)
     : mRuleList(Move(aOther.mRuleList)) {}
   ServoGroupRuleRules(const ServoGroupRuleRules& aCopy) {
     // Do we ever clone Servo rules?
     MOZ_ASSERT_UNREACHABLE("stylo: Cloning GroupRule not implemented");
   }
 
   void SetParentRule(GroupRule* aParentRule) {
--- a/layout/style/ImportRule.h
+++ b/layout/style/ImportRule.h
@@ -6,74 +6,65 @@
 /* class for CSS @import rules */
 
 #ifndef mozilla_css_ImportRule_h__
 #define mozilla_css_ImportRule_h__
 
 #include "mozilla/Attributes.h"
 
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/css/Rule.h"
-#include "nsIDOMCSSImportRule.h"
+#include "mozilla/dom/CSSImportRule.h"
 
 class nsMediaList;
 class nsString;
 
 namespace mozilla {
 
 class CSSStyleSheet;
 class StyleSheet;
 
 namespace dom {
 class MediaList;
 }
 
 namespace css {
 
-class ImportRule final : public Rule,
-                         public nsIDOMCSSImportRule
+class ImportRule final : public dom::CSSImportRule
 {
 public:
   ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
              uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   // for |Clone|
   ImportRule(const ImportRule& aCopy);
   ~ImportRule();
 public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ImportRule, Rule)
   NS_DECL_ISUPPORTS_INHERITED
-  virtual bool IsCCLeaf() const override;
 
-  using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
+  // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
+  using dom::CSSImportRule::GetStyleSheet;
 
   // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-  virtual int32_t GetType() const override;
-  using Rule::GetType;
   virtual already_AddRefed<Rule> Clone() const override;
 
   void SetSheet(CSSStyleSheet*);
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
-
   // nsIDOMCSSImportRule interface
-  NS_DECL_NSIDOMCSSIMPORTRULE
+  NS_IMETHOD GetHref(nsAString& aHref) final;
 
   // WebIDL interface
-  uint16_t Type() const override;
   void GetCssTextImpl(nsAString& aCssText) const override;
-  // The XPCOM GetHref is fine, since it never fails.
-  dom::MediaList* Media() const;
-  StyleSheet* GetStyleSheet() const;
+  dom::MediaList* Media() const final;
+  StyleSheet* GetStyleSheet() const final;
 
 private:
   nsString  mURLSpec;
   RefPtr<nsMediaList> mMedia;
   RefPtr<CSSStyleSheet> mChildSheet;
 };
 
 } // namespace css
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -1417,17 +1417,17 @@ Loader::InsertChildSheet(StyleSheet* aSh
     // cloned off of top-level sheets which were disabled
     aSheet->AsGecko()->SetEnabled(true);
     aGeckoParentRule->SetSheet(aSheet->AsGecko()); // This sets the ownerRule on the sheet
   } else {
     if (!aSheet->AsServo()->RawSheet()) {
       aSheet->AsServo()->SetSheetForImport(aServoChildSheet);
     }
   }
-  aParentSheet->AppendStyleSheet(aSheet);
+  aParentSheet->PrependStyleSheet(aSheet);
 
   LOG(("  Inserting into parent sheet"));
   return NS_OK;
 }
 
 /**
  * LoadSheet handles the actual load of a sheet.  If the load is
  * supposed to be synchronous it just opens a channel synchronously
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -28,19 +28,16 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_From
                    mozilla::css::Loader* loader,
                    mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
                    mozilla::css::SheetParsingMode parsing_mode,
                    const RawServoMediaList* media_list,
                    RawGeckoURLExtraData* extra_data,
                    uint32_t line_number_offset,
                    nsCompatibility quirks_mode)
-SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet,
-                   RawServoStyleSheetStrong,
-                   const RawServoImportRuleBorrowed import_rule)
 SERVO_BINDING_FUNC(Servo_StyleSheet_ClearAndUpdate,
                    void,
                    RawServoStyleSheetBorrowed stylesheet,
                    mozilla::css::Loader* loader,
                    mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
                    RawGeckoURLExtraData* extra_data,
                    uint32_t line_number_offset)
@@ -67,17 +64,18 @@ SERVO_BINDING_FUNC(Servo_StyleSet_Prepen
 SERVO_BINDING_FUNC(Servo_StyleSet_RemoveStyleSheet, void,
                    RawServoStyleSetBorrowed set,
                    uint64_t unique_id)
 SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void,
                    RawServoStyleSetBorrowed set,
                    RawServoStyleSheetBorrowed sheet,
                    uint64_t unique_id,
                    uint64_t before_unique_id)
-SERVO_BINDING_FUNC(Servo_StyleSet_FlushStyleSheets, void, RawServoStyleSetBorrowed set)
+SERVO_BINDING_FUNC(Servo_StyleSet_FlushStyleSheets, void, RawServoStyleSetBorrowed set,
+                   RawGeckoElementBorrowedOrNull doc_elem)
 SERVO_BINDING_FUNC(Servo_StyleSet_NoteStyleSheetsChanged, void,
                    RawServoStyleSetBorrowed set, bool author_style_disabled)
 SERVO_BINDING_FUNC(Servo_StyleSet_GetKeyframesForName, bool,
                    RawServoStyleSetBorrowed set,
                    const nsACString* property,
                    nsTimingFunctionBorrowed timing_function,
                    ServoComputedValuesBorrowed computed_values,
                    RawGeckoKeyframeListBorrowedMut keyframe_list)
@@ -117,16 +115,17 @@ SERVO_BINDING_FUNC(Servo_CssRules_Delete
                      ServoCssRulesBorrowed rules, uint32_t index, \
                      uint32_t* line, uint32_t* column) \
   BASIC_RULE_FUNCS_WITHOUT_GETTER(type_##Rule)
 #define GROUP_RULE_FUNCS(type_) \
   BASIC_RULE_FUNCS(type_) \
   SERVO_BINDING_FUNC(Servo_##type_##Rule_GetRules, ServoCssRulesStrong, \
                      RawServo##type_##RuleBorrowed rule)
 BASIC_RULE_FUNCS(Style)
+BASIC_RULE_FUNCS(Import)
 BASIC_RULE_FUNCS_WITHOUT_GETTER(Keyframe)
 BASIC_RULE_FUNCS(Keyframes)
 GROUP_RULE_FUNCS(Media)
 BASIC_RULE_FUNCS(Namespace)
 BASIC_RULE_FUNCS(Page)
 GROUP_RULE_FUNCS(Supports)
 GROUP_RULE_FUNCS(Document)
 #undef GROUP_RULE_FUNCS
@@ -138,16 +137,20 @@ SERVO_BINDING_FUNC(Servo_CssRules_GetCou
                    ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoStyleRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
                    RawServoStyleRuleBorrowed rule,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
                    RawServoStyleRuleBorrowed rule, nsAString* result)
+SERVO_BINDING_FUNC(Servo_ImportRule_GetHref, void,
+                   RawServoImportRuleBorrowed rule, nsAString* result)
+SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet, const RawServoStyleSheet*,
+                   RawServoImportRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_Keyframe_GetKeyText, void,
                    RawServoKeyframeBorrowed keyframe, nsAString* result)
 // Returns whether it successfully changes the key text.
 SERVO_BINDING_FUNC(Servo_Keyframe_SetKeyText, bool,
                    RawServoKeyframeBorrowed keyframe, const nsACString* text)
 SERVO_BINDING_FUNC(Servo_Keyframe_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoKeyframeBorrowed keyframe)
 SERVO_BINDING_FUNC(Servo_Keyframe_SetStyle, void,
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -3,36 +3,65 @@
 /* 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/. */
 
 /* representation of CSSRuleList for stylo */
 
 #include "mozilla/ServoCSSRuleList.h"
 
+#include "mozilla/IntegerRange.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoDocumentRule.h"
+#include "mozilla/ServoImportRule.h"
 #include "mozilla/ServoKeyframesRule.h"
 #include "mozilla/ServoMediaRule.h"
 #include "mozilla/ServoNamespaceRule.h"
 #include "mozilla/ServoPageRule.h"
 #include "mozilla/ServoStyleRule.h"
+#include "mozilla/ServoStyleSheet.h"
 #include "mozilla/ServoSupportsRule.h"
 #include "nsCSSCounterStyleRule.h"
 #include "nsCSSFontFaceRule.h"
 
 namespace mozilla {
 
-ServoCSSRuleList::ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules)
-  : mRawRules(aRawRules)
+ServoCSSRuleList::ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules,
+                                   ServoStyleSheet* aDirectOwnerStyleSheet)
+  : mStyleSheet(aDirectOwnerStyleSheet)
+  , mRawRules(aRawRules)
 {
   Servo_CssRules_ListTypes(mRawRules, &mRules);
-  // XXX We may want to eagerly create object for import rule, so that
-  //     we don't lose the reference to child stylesheet when our own
-  //     stylesheet goes away.
+  // Only top level rule list can have @import rules.
+  if (aDirectOwnerStyleSheet) {
+    nsDataHashtable<nsPtrHashKey<const RawServoStyleSheet>,
+                    ServoStyleSheet*> stylesheets;
+    aDirectOwnerStyleSheet->EnumerateChildSheets(
+      [&stylesheets](StyleSheet* child) {
+        ServoStyleSheet* servoSheet = child->AsServo();
+        const RawServoStyleSheet* rawSheet = servoSheet->RawSheet();
+        MOZ_ASSERT(!stylesheets.Get(rawSheet, nullptr),
+                   "Multiple child sheets with same raw sheet?");
+        stylesheets.Put(rawSheet, servoSheet);
+      });
+    for (auto i : IntegerRange(mRules.Length())) {
+      if (mRules[i] != nsIDOMCSSRule::IMPORT_RULE) {
+        // Only @charset can be put before @import rule, but @charset
+        // rules don't have corresponding object, so if a rule is not
+        // @import rule, there is definitely no @import rule after it.
+        break;
+      }
+      ConstructImportRule(i, [&stylesheets](const RawServoStyleSheet* raw) {
+        // Child sheets may not correctly cloned for stylo, which is
+        // bug 1367213. That makes it possible to fail to get a style
+        // sheet for the raw sheet here.
+        return stylesheets.GetAndRemove(raw).valueOr(nullptr);
+      });
+    }
+  }
 }
 
 // QueryInterface implementation for ServoCSSRuleList
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServoCSSRuleList)
 NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList)
 
 NS_IMPL_ADDREF_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
 NS_IMPL_RELEASE_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
@@ -112,16 +141,22 @@ ServoCSSRuleList::GetRule(uint32_t aInde
       case nsIDOMCSSRule::FONT_FACE_RULE: {
         ruleObj = Servo_CssRules_GetFontFaceRuleAt(mRawRules, aIndex);
         break;
       }
       case nsIDOMCSSRule::COUNTER_STYLE_RULE: {
         ruleObj = Servo_CssRules_GetCounterStyleRuleAt(mRawRules, aIndex);
         break;
       }
+      case nsIDOMCSSRule::IMPORT_RULE:
+        // Currently ConstructImportRule may fail to construct an import
+        // rule eagerly. See comment in that function. This should be
+        // converted into an assertion when those bugs get fixed.
+        NS_WARNING("stylo: this @import rule was not constructed");
+        return nullptr;
       case nsIDOMCSSRule::KEYFRAME_RULE:
         MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
         return nullptr;
       default:
         NS_WARNING("stylo: not implemented yet");
         return nullptr;
     }
     ruleObj->SetStyleSheet(mStyleSheet);
@@ -175,33 +210,82 @@ ServoCSSRuleList::DropAllRules()
 void
 ServoCSSRuleList::DropReference()
 {
   mStyleSheet = nullptr;
   mParentRule = nullptr;
   DropAllRules();
 }
 
+template<typename ChildSheetGetter>
+void
+ServoCSSRuleList::ConstructImportRule(uint32_t aIndex, ChildSheetGetter aGetter)
+{
+  MOZ_ASSERT(mRules[aIndex] == nsIDOMCSSRule::IMPORT_RULE);
+
+  uint32_t line, column;
+  RefPtr<RawServoImportRule> rawRule =
+    Servo_CssRules_GetImportRuleAt(mRawRules, aIndex,
+                                   &line, &column).Consume();
+  const RawServoStyleSheet*
+    rawChildSheet = Servo_ImportRule_GetSheet(rawRule);
+  ServoStyleSheet* childSheet = aGetter(rawChildSheet);
+  if (!childSheet) {
+    // There are cases that we cannot get the child sheet currently.
+    // See comments in callsites of this function. This should become
+    // an assertion after bug 1367213 and bug 1368381 get fixed.
+    NS_WARNING("stylo: fail to get child sheet for @import rule");
+    return;
+  }
+  RefPtr<ServoImportRule>
+    ruleObj = new ServoImportRule(Move(rawRule), childSheet, line, column);
+  MOZ_ASSERT(!childSheet->GetOwnerRule(),
+             "Child sheet is already owned by another rule?");
+  MOZ_ASSERT(childSheet->GetParentSheet() == mStyleSheet,
+             "Not a child sheet of the owner of the rule?");
+  childSheet->SetOwnerRule(ruleObj);
+  mRules[aIndex] = CastToUint(ruleObj.forget().take());
+}
+
 nsresult
 ServoCSSRuleList::InsertRule(const nsAString& aRule, uint32_t aIndex)
 {
   MOZ_ASSERT(mStyleSheet, "Caller must ensure that "
              "the list is not unlinked from stylesheet");
   NS_ConvertUTF16toUTF8 rule(aRule);
   bool nested = !!mParentRule;
   css::Loader* loader = nullptr;
   if (nsIDocument* doc = mStyleSheet->GetAssociatedDocument()) {
     loader = doc->CSSLoader();
   }
   uint16_t type;
   nsresult rv = Servo_CssRules_InsertRule(mRawRules, mStyleSheet->RawSheet(),
                                           &rule, aIndex, nested,
                                           loader, mStyleSheet, &type);
-  if (!NS_FAILED(rv)) {
-    mRules.InsertElementAt(aIndex, type);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  mRules.InsertElementAt(aIndex, type);
+  if (type == nsIDOMCSSRule::IMPORT_RULE) {
+    MOZ_ASSERT(!nested, "@import rule cannot be nested");
+    ConstructImportRule(aIndex, [this](const RawServoStyleSheet* raw) {
+      StyleSheet* sheet = mStyleSheet->GetMostRecentlyAddedChildSheet();
+      MOZ_ASSERT(sheet, "Should have at least one "
+                 "child stylesheet after inserting @import rule");
+      ServoStyleSheet* servoSheet = sheet->AsServo();
+      // This should always be that case, but currently ServoStyleSheet
+      // may be reused and the reused stylesheet doesn't refer to the
+      // right raw sheet, which is bug 1368381. This should be converted
+      // to an assertion after that bug gets fixed.
+      if (servoSheet->RawSheet() == raw) {
+        NS_WARNING("New child sheet should always be prepended to the list");
+        return static_cast<ServoStyleSheet*>(nullptr);
+      }
+      return servoSheet;
+    });
   }
   return rv;
 }
 
 nsresult
 ServoCSSRuleList::DeleteRule(uint32_t aIndex)
 {
   nsresult rv = Servo_CssRules_DeleteRule(mRawRules, aIndex);
@@ -232,17 +316,17 @@ ServoCSSRuleList::FillStyleRuleHashtable
     uint16_t type = GetRuleType(i);
     if (type == nsIDOMCSSRule::STYLE_RULE) {
       ServoStyleRule* castedRule = static_cast<ServoStyleRule*>(GetRule(i));
       RawServoStyleRule* rawRule = castedRule->Raw();
       aTable.Put(rawRule, castedRule);
     } else if (type == nsIDOMCSSRule::MEDIA_RULE ||
                type == nsIDOMCSSRule::SUPPORTS_RULE ||
                type == nsIDOMCSSRule::DOCUMENT_RULE) {
-      css::GroupRule* castedRule = static_cast<css::GroupRule*>(GetRule(i));
+      auto castedRule = static_cast<css::GroupRule*>(GetRule(i));
 
       // Call this method recursively on the ServoCSSRuleList in the rule.
       ServoCSSRuleList* castedRuleList = static_cast<ServoCSSRuleList*>(
         castedRule->CssRules());
       castedRuleList->FillStyleRuleHashtable(aTable);
     }
   }
 }
--- a/layout/style/ServoCSSRuleList.h
+++ b/layout/style/ServoCSSRuleList.h
@@ -20,17 +20,22 @@ class ServoStyleSheet;
 namespace css {
 class GroupRule;
 class Rule;
 } // namespace css
 
 class ServoCSSRuleList final : public dom::CSSRuleList
 {
 public:
-  explicit ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules);
+  // @param aDirectOwnerStyleSheet should be set to the owner stylesheet
+  // if this rule list is owned directly by a stylesheet, which means it
+  // is a top level CSSRuleList. If it's owned by a group rule, nullptr.
+  // If this param is set, the caller doesn't need to call SetStyleSheet.
+  ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules,
+                   ServoStyleSheet* aDirectOwnerStyleSheet);
   css::GroupRule* GetParentRule() const { return mParentRule; }
   void SetParentRule(css::GroupRule* aParentRule);
   void SetStyleSheet(StyleSheet* aSheet);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoCSSRuleList, dom::CSSRuleList)
 
   ServoStyleSheet* GetParentObject() final { return mStyleSheet; }
@@ -65,16 +70,19 @@ private:
     return reinterpret_cast<css::Rule*>(aInt);
   }
 
   template<typename Func>
   void EnumerateInstantiatedRules(Func aCallback);
 
   void DropAllRules();
 
+  template<typename ChildSheetGetter>
+  inline void ConstructImportRule(uint32_t aIndex, ChildSheetGetter aGetter);
+
   // mStyleSheet may be nullptr when it drops the reference to us.
   ServoStyleSheet* mStyleSheet = nullptr;
   // mParentRule is nullptr if it isn't a nested rule list.
   css::GroupRule* mParentRule = nullptr;
   RefPtr<ServoCssRules> mRawRules;
   // Array stores either a number indicating rule type, or a pointer to
   // css::Rule. If the value is less than kMaxRuleType, the given rule
   // instance has not been constructed, and the value means the type
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoImportRule.cpp
@@ -0,0 +1,97 @@
+/* -*- 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/. */
+
+/* representation of CSSImportRule for stylo */
+
+#include "mozilla/ServoImportRule.h"
+
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoStyleSheet.h"
+
+namespace mozilla {
+
+ServoImportRule::ServoImportRule(RefPtr<RawServoImportRule> aRawRule,
+                                 ServoStyleSheet* aSheet,
+                                 uint32_t aLine, uint32_t aColumn)
+  : CSSImportRule(aLine, aColumn)
+  , mRawRule(Move(aRawRule))
+  , mChildSheet(aSheet)
+{
+}
+
+ServoImportRule::~ServoImportRule()
+{
+  if (mChildSheet) {
+    mChildSheet->SetOwnerRule(nullptr);
+  }
+}
+
+NS_IMPL_ADDREF_INHERITED(ServoImportRule, dom::CSSImportRule)
+NS_IMPL_RELEASE_INHERITED(ServoImportRule, dom::CSSImportRule)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServoImportRule,
+                                   dom::CSSImportRule, mChildSheet)
+
+// QueryInterface implementation for ServoImportRule
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServoImportRule)
+NS_INTERFACE_MAP_END_INHERITING(dom::CSSImportRule)
+
+/* virtual */ already_AddRefed<css::Rule>
+ServoImportRule::Clone() const
+{
+  // Rule::Clone is only used when CSSStyleSheetInner is cloned in
+  // preparation of being mutated. However, ServoStyleSheet never clones
+  // anything, so this method should never be called.
+  MOZ_ASSERT_UNREACHABLE("Shouldn't be cloning ServoSupportsRule");
+  return nullptr;
+}
+
+#ifdef DEBUG
+/* virtual */ void
+ServoImportRule::List(FILE* out, int32_t aIndent) const
+{
+  nsAutoCString str;
+  for (int32_t i = 0; i < aIndent; i++) {
+    str.AppendLiteral("  ");
+  }
+  Servo_ImportRule_Debug(mRawRule, &str);
+  fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+dom::MediaList*
+ServoImportRule::Media() const
+{
+  return mChildSheet->Media();
+}
+
+StyleSheet*
+ServoImportRule::GetStyleSheet() const
+{
+  return mChildSheet;
+}
+
+NS_IMETHODIMP
+ServoImportRule::GetHref(nsAString& aHref)
+{
+  Servo_ImportRule_GetHref(mRawRule, &aHref);
+  return NS_OK;
+}
+
+/* virtual */ void
+ServoImportRule::GetCssTextImpl(nsAString& aCssText) const
+{
+  Servo_ImportRule_GetCssText(mRawRule, &aCssText);
+}
+
+/* virtual */ size_t
+ServoImportRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  // TODO Implement this!
+  return aMallocSizeOf(this);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoImportRule.h
@@ -0,0 +1,56 @@
+/* -*- 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/. */
+
+/* representation of CSSImportRule for stylo */
+
+#ifndef mozilla_ServoImportRule_h
+#define mozilla_ServoImportRule_h
+
+#include "mozilla/dom/CSSImportRule.h"
+#include "mozilla/ServoBindingTypes.h"
+
+namespace mozilla {
+
+class ServoStyleSheet;
+class ServoMediaList;
+
+class ServoImportRule final : public dom::CSSImportRule
+{
+public:
+  ServoImportRule(RefPtr<RawServoImportRule> aRawRule,
+                  ServoStyleSheet* aSheet,
+                  uint32_t aLine, uint32_t aColumn);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServoImportRule, dom::CSSImportRule)
+
+  // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
+  using dom::CSSImportRule::GetStyleSheet;
+
+#ifdef DEBUG
+  void List(FILE* out = stdout, int32_t aIndent = 0) const final;
+#endif
+  already_AddRefed<css::Rule> Clone() const final;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
+
+  // nsIDOMCSSImportRule interface
+  NS_IMETHOD GetHref(nsAString& aHref) final;
+
+  // WebIDL interface
+  void GetCssTextImpl(nsAString& aCssText) const override;
+  dom::MediaList* Media() const final;
+  StyleSheet* GetStyleSheet() const final;
+
+private:
+  ~ServoImportRule();
+
+  RefPtr<RawServoImportRule> mRawRule;
+  RefPtr<ServoStyleSheet> mChildSheet;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ServoImportRule_h
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -125,20 +125,18 @@ ServoStyleSet::Shutdown()
   // starts going away.
   ClearNonInheritingStyleContexts();
   mRawSet = nullptr;
 }
 
 void
 ServoStyleSet::InvalidateStyleForCSSRuleChanges()
 {
-  if (Element* root = mPresContext->Document()->GetRootElement()) {
-    mPresContext->RestyleManager()->PostRestyleEventForCSSRuleChanges(
-        root, eRestyle_Subtree, nsChangeHint(0));
-  }
+  MOZ_ASSERT(StylistNeedsUpdate());
+  mPresContext->RestyleManager()->AsServo()->PostRestyleEventForCSSRuleChanges();
 }
 
 size_t
 ServoStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   // Measurement of the following members may be added later if DMD finds it is
@@ -162,27 +160,17 @@ ServoStyleSet::GetAuthorStyleDisabled() 
 nsresult
 ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
 {
   if (mAuthorStyleDisabled == aStyleDisabled) {
     return NS_OK;
   }
 
   mAuthorStyleDisabled = aStyleDisabled;
-
-  // If we've just disabled, we have to note the stylesheets have changed and
-  // call flush directly, since the PresShell won't.
-  if (mAuthorStyleDisabled) {
-    NoteStyleSheetsChanged();
-  }
-  // If we've just enabled, then PresShell will trigger the notification and
-  // later flush when the stylesheet objects are enabled in JS.
-  //
-  // TODO(emilio): Users can have JS disabled, can't they? Will that affect that
-  // notification on content documents?
+  ForceAllStyleDirty();
 
   return NS_OK;
 }
 
 void
 ServoStyleSet::BeginUpdate()
 {
 }
@@ -970,22 +958,43 @@ ServoStyleSet::StyleSubtreeForReconstruc
   DebugOnly<bool> postTraversalRequired =
     PrepareAndTraverseSubtree(aRoot,
                               TraversalRootBehavior::Normal,
                               TraversalRestyleBehavior::ForReconstruct);
   MOZ_ASSERT(!postTraversalRequired);
 }
 
 void
-ServoStyleSet::NoteStyleSheetsChanged()
+ServoStyleSet::ForceAllStyleDirty()
 {
   SetStylistStyleSheetsDirty();
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get(), mAuthorStyleDisabled);
 }
 
+void
+ServoStyleSet::RecordStyleSheetChange(
+    ServoStyleSheet* aSheet,
+    StyleSheet::ChangeType aChangeType)
+{
+  SetStylistStyleSheetsDirty();
+  switch (aChangeType) {
+    case StyleSheet::ChangeType::RuleAdded:
+    case StyleSheet::ChangeType::RuleRemoved:
+    case StyleSheet::ChangeType::RuleChanged:
+      // FIXME(emilio): We can presumably do better in a bunch of these.
+      return ForceAllStyleDirty();
+    case StyleSheet::ChangeType::ApplicableStateChanged:
+    case StyleSheet::ChangeType::Added:
+    case StyleSheet::ChangeType::Removed:
+      // Do nothing, we've already recorded the change in the
+      // Append/Remove/Replace methods, etc, and will act consequently.
+      return;
+  }
+}
+
 #ifdef DEBUG
 void
 ServoStyleSet::AssertTreeIsClean()
 {
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
     Servo_AssertTreeIsClean(root);
   }
@@ -1192,17 +1201,18 @@ ServoStyleSet::ResolveForDeclarations(
 
 void
 ServoStyleSet::UpdateStylist()
 {
   MOZ_ASSERT(StylistNeedsUpdate());
   if (mStylistState == StylistState::FullyDirty) {
     RebuildData();
   } else {
-    Servo_StyleSet_FlushStyleSheets(mRawSet.get());
+    Element* root = mPresContext->Document()->GetDocumentElement();
+    Servo_StyleSet_FlushStyleSheets(mRawSet.get(), root);
   }
   mStylistState = StylistState::NotDirty;
 }
 
 void
 ServoStyleSet::PrependSheetOfType(SheetType aType,
                                   ServoStyleSheet* aSheet)
 {
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -100,28 +100,22 @@ public:
 
   ServoStyleSet();
   ~ServoStyleSet();
 
   void Init(nsPresContext* aPresContext);
   void BeginShutdown();
   void Shutdown();
 
-  void RecordStyleSheetChange(mozilla::ServoStyleSheet*, StyleSheet::ChangeType)
-  {
-    // TODO(emilio): Record which kind of changes have we handled, and act
-    // properly in InvalidateStyleForCSSRuleChanges instead of invalidating the
-    // whole document.
-    NoteStyleSheetsChanged();
-  }
+  void RecordStyleSheetChange(mozilla::ServoStyleSheet*, StyleSheet::ChangeType);
 
   void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) {
     // FIXME(emilio): When we properly support shadow dom we'll need to do
     // better.
-    NoteStyleSheetsChanged();
+    ForceAllStyleDirty();
   }
 
   bool StyleSheetsHaveChanged() const
   {
     return StylistNeedsUpdate();
   }
 
   void InvalidateStyleForCSSRuleChanges();
@@ -298,17 +292,17 @@ public:
    */
   void StyleSubtreeForReconstruct(dom::Element* aRoot);
 
   /**
    * Records that the contents of style sheets have changed since the last
    * restyle.  Calling this will ensure that the Stylist rebuilds its
    * selector maps.
    */
-  void NoteStyleSheetsChanged();
+  void ForceAllStyleDirty();
 
   /**
    * Helper for correctly calling RebuildStylist without paying the cost of an
    * extra function call in the common no-rebuild-needed case.
    */
   void UpdateStylistIfNeeded()
   {
     if (StylistNeedsUpdate()) {
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -76,20 +76,20 @@ ServoStyleSheet::ServoStyleSheet(css::Sh
   : StyleSheet(StyleBackendType::Servo, aParsingMode)
 {
   mInner = new ServoStyleSheetInner(aCORSMode, aReferrerPolicy, aIntegrity);
   mInner->AddSheet(this);
 }
 
 ServoStyleSheet::ServoStyleSheet(const ServoStyleSheet& aCopy,
                                  ServoStyleSheet* aParentToUse,
-                                 css::ImportRule* aOwnerRuleToUse,
+                                 dom::CSSImportRule* aOwnerRuleToUse,
                                  nsIDocument* aDocumentToUse,
                                  nsINode* aOwningNodeToUse)
-  : StyleSheet(aCopy, aDocumentToUse, aOwningNodeToUse)
+  : StyleSheet(aCopy, aOwnerRuleToUse, aDocumentToUse, aOwningNodeToUse)
 {
   mParent = aParentToUse;
 }
 
 ServoStyleSheet::~ServoStyleSheet()
 {
   UnparentChildren();
 
@@ -193,79 +193,63 @@ void
 ServoStyleSheet::DropRuleList()
 {
   if (mRuleList) {
     mRuleList->DropReference();
     mRuleList = nullptr;
   }
 }
 
-css::Rule*
-ServoStyleSheet::GetDOMOwnerRule() const
-{
-  NS_ERROR("stylo: Don't know how to get DOM owner rule for ServoStyleSheet");
-  return nullptr;
-}
-
 already_AddRefed<StyleSheet>
 ServoStyleSheet::Clone(StyleSheet* aCloneParent,
-                       css::ImportRule* aCloneOwnerRule,
+                       dom::CSSImportRule* aCloneOwnerRule,
                        nsIDocument* aCloneDocument,
                        nsINode* aCloneOwningNode) const
 {
   RefPtr<StyleSheet> clone = new ServoStyleSheet(*this,
     static_cast<ServoStyleSheet*>(aCloneParent),
     aCloneOwnerRule,
     aCloneDocument,
     aCloneOwningNode);
   return clone.forget();
 }
 
-void
-ServoStyleSheet::ClearRuleCascadesInternal()
-{
-  for (StyleSetHandle& setHandle : mStyleSets) {
-    setHandle->AsServo()->NoteStyleSheetsChanged();
-    setHandle->AsServo()->UpdateStylistIfNeeded();
-  }
-}
-
 CSSRuleList*
 ServoStyleSheet::GetCssRulesInternal(ErrorResult& aRv)
 {
   if (!mRuleList) {
     EnsureUniqueInner();
 
     RefPtr<ServoCssRules> rawRules =
       Servo_StyleSheet_GetRules(Inner()->mSheet).Consume();
     MOZ_ASSERT(rawRules);
-    mRuleList = new ServoCSSRuleList(rawRules.forget());
-    mRuleList->SetStyleSheet(this);
+    mRuleList = new ServoCSSRuleList(rawRules.forget(), this);
   }
   return mRuleList;
 }
 
 uint32_t
 ServoStyleSheet::InsertRuleInternal(const nsAString& aRule,
                                     uint32_t aIndex, ErrorResult& aRv)
 {
   // Ensure mRuleList is constructed.
   GetCssRulesInternal(aRv);
 
   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
   aRv = mRuleList->InsertRule(aRule, aIndex);
   if (aRv.Failed()) {
     return 0;
   }
-  // XXX If the inserted rule is an import rule, we should only notify
-  // the document if its associated child stylesheet has been loaded.
   if (mDocument) {
-    // XXX We may not want to get the rule when stylesheet change event
-    // is not enabled.
-    mDocument->StyleRuleAdded(this, mRuleList->GetRule(aIndex));
+    if (mRuleList->GetRuleType(aIndex) != css::Rule::IMPORT_RULE ||
+        !RuleHasPendingChildSheet(mRuleList->GetRule(aIndex))) {
+      // XXX We may not want to get the rule when stylesheet change event
+      // is not enabled.
+      mDocument->StyleRuleAdded(this, mRuleList->GetRule(aIndex));
+    }
   }
   return aIndex;
 }
 
 void
 ServoStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
 {
   // Ensure mRuleList is constructed.
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -98,38 +98,30 @@ public:
   }
   void SetSheetForImport(const RawServoStyleSheet* aSheet) {
     MOZ_ASSERT(!Inner()->mSheet);
     Inner()->mSheet = aSheet;
   }
 
   URLExtraData* URLData() const { return Inner()->mURLData; }
 
-  // WebIDL CSSStyleSheet API
-  // Can't be inline because we can't include ImportRule here.  And can't be
-  // called GetOwnerRule because that would be ambiguous with the ImportRule
-  // version.
-  css::Rule* GetDOMOwnerRule() const final;
-
   void DidDirty() override {}
 
-  virtual already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
-    css::ImportRule* aCloneOwnerRule,
+  already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
+    dom::CSSImportRule* aCloneOwnerRule,
     nsIDocument* aCloneDocument,
     nsINode* aCloneOwningNode) const final;
 
   // nsICSSLoaderObserver interface
   NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate,
                               nsresult aStatus) final;
 
 protected:
   virtual ~ServoStyleSheet();
 
-  void ClearRuleCascadesInternal() override;
-
   ServoStyleSheetInner* Inner() const
   {
     return static_cast<ServoStyleSheetInner*>(mInner);
   }
 
   // Internal methods which do not have security check and completeness check.
   dom::CSSRuleList* GetCssRulesInternal(ErrorResult& aRv);
   uint32_t InsertRuleInternal(const nsAString& aRule,
@@ -141,17 +133,17 @@ protected:
 
   void EnabledStateChangedInternal() {}
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
 private:
   ServoStyleSheet(const ServoStyleSheet& aCopy,
                   ServoStyleSheet* aParentToUse,
-                  css::ImportRule* aOwnerRuleToUse,
+                  dom::CSSImportRule* aOwnerRuleToUse,
                   nsIDocument* aDocumentToUse,
                   nsINode* aOwningNodeToUse);
 
   void DropRuleList();
 
   RefPtr<ServoCSSRuleList> mRuleList;
 
   friend class StyleSheet;
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -1,48 +1,52 @@
 /* -*- 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 "mozilla/StyleSheet.h"
 
+#include "mozilla/dom/CSSImportRule.h"
 #include "mozilla/dom/CSSRuleList.h"
 #include "mozilla/dom/MediaList.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/CSSStyleSheet.h"
 
 #include "mozAutoDocUpdate.h"
 #include "NullPrincipal.h"
 
 namespace mozilla {
 
 StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode)
   : mParent(nullptr)
   , mDocument(nullptr)
   , mOwningNode(nullptr)
+  , mOwnerRule(nullptr)
   , mParsingMode(aParsingMode)
   , mType(aType)
   , mDisabled(false)
   , mDocumentAssociationMode(NotOwnedByDocument)
   , mInner(nullptr)
   , mDirty(false)
 {
 }
 
 StyleSheet::StyleSheet(const StyleSheet& aCopy,
+                       dom::CSSImportRule* aOwnerRuleToUse,
                        nsIDocument* aDocumentToUse,
                        nsINode* aOwningNodeToUse)
   : mParent(nullptr)
   , mTitle(aCopy.mTitle)
   , mDocument(aDocumentToUse)
   , mOwningNode(aOwningNodeToUse)
+  , mOwnerRule(aOwnerRuleToUse)
   , mParsingMode(aCopy.mParsingMode)
   , mType(aCopy.mType)
   , mDisabled(aCopy.mDisabled)
     // We only use this constructor during cloning.  It's the cloner's
     // responsibility to notify us if we end up being owned by a document.
   , mDocumentAssociationMode(NotOwnedByDocument)
   , mInner(aCopy.mInner) // Shallow copy, but concrete subclasses will fix up.
   , mDirty(aCopy.mDirty)
@@ -122,25 +126,16 @@ StyleSheet::TraverseInner(nsCycleCollect
   StyleSheet* childSheet = GetFirstChild();
   while (childSheet) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
     cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, childSheet));
     childSheet = childSheet->mNext;
   }
 }
 
-void
-StyleSheet::ClearRuleCascades()
-{
-  ClearRuleCascadesInternal();
-  if (mParent) {
-    mParent->ClearRuleCascades();
-  }
-}
-
 // QueryInterface implementation for StyleSheet
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
 NS_INTERFACE_MAP_END
 
@@ -443,17 +438,24 @@ StyleSheet::EnsureUniqueInner()
   }
 
   StyleSheetInfo* clone = mInner->CloneFor(this);
   MOZ_ASSERT(clone);
   mInner->RemoveSheet(this);
   mInner = clone;
 
   // Ensure we're using the new rules.
-  ClearRuleCascades();
+  //
+  // NOTE: In Servo, all kind of changes that change the set of selectors or
+  // rules we match are covered by the PresShell notifications. In Gecko that's
+  // true too, but this is probably needed because selectors are not refcounted
+  // and can become stale.
+  if (CSSStyleSheet* geckoSheet = GetAsGecko()) {
+    geckoSheet->ClearRuleCascades();
+  }
 
   // let our containing style sets know that if we call
   // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
   // document
   for (StyleSetHandle& setHandle : mStyleSets) {
     setHandle->SetNeedsRestyleAfterEnsureUniqueInner();
   }
 }
@@ -479,16 +481,22 @@ StyleSheet::GetCssRules(nsIPrincipal& aS
                         ErrorResult& aRv)
 {
   if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return nullptr;
   }
   FORWARD_INTERNAL(GetCssRulesInternal, (aRv))
 }
 
+css::Rule*
+StyleSheet::GetDOMOwnerRule() const
+{
+  return mOwnerRule;
+}
+
 uint32_t
 StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
                        nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
   if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
     return 0;
   }
@@ -682,26 +690,23 @@ StyleSheet::SetAssociatedDocument(nsIDoc
 
 void
 StyleSheet::ClearAssociatedDocument()
 {
   SetAssociatedDocument(nullptr, NotOwnedByDocument);
 }
 
 void
-StyleSheet::AppendStyleSheet(StyleSheet* aSheet)
+StyleSheet::PrependStyleSheet(StyleSheet* aSheet)
 {
   NS_PRECONDITION(nullptr != aSheet, "null arg");
 
   WillDirty();
-  RefPtr<StyleSheet>* tail = &SheetInfo().mFirstChild;
-  while (*tail) {
-    tail = &(*tail)->mNext;
-  }
-  *tail = aSheet;
+  aSheet->mNext = SheetInfo().mFirstChild;
+  SheetInfo().mFirstChild = aSheet;
 
   // This is not reference counted. Our parent tells us when
   // it's going away.
   aSheet->mParent = this;
   aSheet->SetAssociatedDocument(mDocument, mDocumentAssociationMode);
   DidDirty();
 }
 
@@ -789,9 +794,20 @@ StyleSheet::Media()
 // nsWrapperCache
 
 JSObject*
 StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
 }
 
+/* static */ bool
+StyleSheet::RuleHasPendingChildSheet(css::Rule* aRule)
+{
+  MOZ_ASSERT(aRule->GetType() == css::Rule::IMPORT_RULE);
+  auto rule = static_cast<dom::CSSImportRule*>(aRule);
+  if (StyleSheet* childSheet = rule->GetStyleSheet()) {
+    return !childSheet->IsComplete();
+  }
+  return false;
+}
+
 } // namespace mozilla
--- a/layout/style/StyleSheet.h
+++ b/layout/style/StyleSheet.h
@@ -27,37 +27,38 @@ namespace mozilla {
 
 class CSSStyleSheet;
 class ServoStyleSheet;
 class StyleSetHandle;
 struct StyleSheetInfo;
 struct CSSStyleSheetInner;
 
 namespace dom {
+class CSSImportRule;
 class CSSRuleList;
 class MediaList;
 class SRIMetadata;
 } // namespace dom
 
 namespace css {
 class GroupRule;
-class ImportRule;
 class Rule;
 }
 
 /**
  * Superclass for data common to CSSStyleSheet and ServoStyleSheet.
  */
 class StyleSheet : public nsIDOMCSSStyleSheet
                  , public nsICSSLoaderObserver
                  , public nsWrapperCache
 {
 protected:
   StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode);
   StyleSheet(const StyleSheet& aCopy,
+             dom::CSSImportRule* aOwnerRuleToUse,
              nsIDocument* aDocumentToUse,
              nsINode* aOwningNodeToUse);
   virtual ~StyleSheet();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(StyleSheet,
                                                          nsIDOMCSSStyleSheet)
@@ -125,17 +126,17 @@ public:
    * should never be inserted into a style set.  A sheet may not be
    * applicable for a variety of reasons including being disabled and
    * being incomplete.
    */
   inline bool IsApplicable() const;
   inline bool HasRules() const;
 
   virtual already_AddRefed<StyleSheet> Clone(StyleSheet* aCloneParent,
-                                             css::ImportRule* aCloneOwnerRule,
+                                             dom::CSSImportRule* aCloneOwnerRule,
                                              nsIDocument* aCloneDocument,
                                              nsINode* aCloneOwningNode) const = 0;
 
   bool IsModified() const { return mDirty; }
 
   void EnsureUniqueInner();
 
   // Append all of this sheet's child sheets to aArray.
@@ -156,19 +157,29 @@ public:
   }
   // aDocument must not be null.
   void SetAssociatedDocument(nsIDocument* aDocument,
                              DocumentAssociationMode aMode);
   void ClearAssociatedDocument();
   nsINode* GetOwnerNode() const { return mOwningNode; }
   inline StyleSheet* GetParentSheet() const { return mParent; }
 
-  void AppendStyleSheet(StyleSheet* aSheet);
+  void SetOwnerRule(dom::CSSImportRule* aOwnerRule) {
+    mOwnerRule = aOwnerRule; /* Not ref counted */
+  }
+  dom::CSSImportRule* GetOwnerRule() const { return mOwnerRule; }
+
+  void PrependStyleSheet(StyleSheet* aSheet);
 
   StyleSheet* GetFirstChild() const;
+  StyleSheet* GetMostRecentlyAddedChildSheet() const {
+    // New child sheet can only be prepended into the linked list of
+    // child sheets, so the most recently added one is always the first.
+    return GetFirstChild();
+  }
 
   // Principal() never returns a null pointer.
   inline nsIPrincipal* Principal() const;
   /**
    * SetPrincipal should be called on all sheets before parsing into them.
    * This can only be called once with a non-null principal.  Calling this with
    * a null pointer is allowed and is treated as a no-op.
    */
@@ -195,17 +206,20 @@ public:
   // GetOwnerNode is defined above.
   inline StyleSheet* GetParentStyleSheet() const;
   // The XPCOM GetTitle is fine for WebIDL.
   dom::MediaList* Media();
   bool Disabled() const { return mDisabled; }
   // The XPCOM SetDisabled is fine for WebIDL.
 
   // WebIDL CSSStyleSheet API
-  virtual css::Rule* GetDOMOwnerRule() const = 0;
+  // Can't be inline because we can't include ImportRule here.  And can't be
+  // called GetOwnerRule because that would be ambiguous with the ImportRule
+  // version.
+  css::Rule* GetDOMOwnerRule() const;
   dom::CSSRuleList* GetCssRules(nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aRv);
   uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
                       nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv);
   void DeleteRule(uint32_t aIndex,
                   nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv);
@@ -239,16 +253,23 @@ public:
 
   void AddStyleSet(const StyleSetHandle& aStyleSet);
   void DropStyleSet(const StyleSetHandle& aStyleSet);
 
   nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
   nsresult InsertRuleIntoGroup(const nsAString& aRule,
                                css::GroupRule* aGroup, uint32_t aIndex);
 
+  template<typename Func>
+  void EnumerateChildSheets(Func aCallback) {
+    for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
+      aCallback(child);
+    }
+  }
+
 private:
   // Get a handle to the various stylesheet bits which live on the 'inner' for
   // gecko stylesheets and live on the StyleSheet for Servo stylesheets.
   inline StyleSheetInfo& SheetInfo();
   inline const StyleSheetInfo& SheetInfo() const;
 
   // Check if the rules are available for read and write.
   // It does the security check as well as whether the rules have been
@@ -283,24 +304,25 @@ protected:
   // Called from SetEnabled when the enabled state changed.
   void EnabledStateChanged();
 
   // Unlink our inner, if needed, for cycle collection
   virtual void UnlinkInner();
   // Traverse our inner, if needed, for cycle collection
   virtual void TraverseInner(nsCycleCollectionTraversalCallback &);
 
-  void ClearRuleCascades();
-  virtual void ClearRuleCascadesInternal() {}
+  // Return whether the given @import rule has pending child sheet.
+  static bool RuleHasPendingChildSheet(css::Rule* aRule);
 
   StyleSheet*           mParent;    // weak ref
 
   nsString              mTitle;
   nsIDocument*          mDocument; // weak ref; parents maintain this for their children
   nsINode*              mOwningNode; // weak ref
+  dom::CSSImportRule*   mOwnerRule; // weak ref
 
   RefPtr<dom::MediaList> mMedia;
 
   RefPtr<StyleSheet> mNext;
 
   // mParsingMode controls access to nonstandard style constructs that
   // are not safe for use on the public Web but necessary in UA sheets
   // and/or useful in user sheets.
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -102,16 +102,17 @@ EXPORTS.mozilla += [
     'ServoBindingList.h',
     'ServoBindings.h',
     'ServoBindingTypes.h',
     'ServoCSSRuleList.h',
     'ServoDeclarationBlock.h',
     'ServoDocumentRule.h',
     'ServoElementSnapshot.h',
     'ServoElementSnapshotTable.h',
+    'ServoImportRule.h',
     'ServoKeyframeRule.h',
     'ServoKeyframesRule.h',
     'ServoMediaList.h',
     'ServoMediaRule.h',
     'ServoNamespaceRule.h',
     'ServoPageRule.h',
     'ServoPropPrefList.h',
     'ServoSpecifiedValues.h',
@@ -131,16 +132,17 @@ EXPORTS.mozilla += [
     'StyleSheet.h',
     'StyleSheetInfo.h',
     'StyleSheetInlines.h',
     'URLExtraData.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'CSS.h',
+    'CSSImportRule.h',
     'CSSKeyframeRule.h',
     'CSSKeyframesRule.h',
     'CSSLexer.h',
     'CSSMediaRule.h',
     'CSSMozDocumentRule.h',
     'CSSNamespaceRule.h',
     'CSSPageRule.h',
     'CSSRuleList.h',
@@ -167,16 +169,17 @@ EXPORTS.mozilla.css += [
     'URLMatchingFunction.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnimationCollection.cpp',
     'BindingStyleRule.cpp',
     'CounterStyleManager.cpp',
     'CSS.cpp',
+    'CSSImportRule.cpp',
     'CSSKeyframeRule.cpp',
     'CSSKeyframesRule.cpp',
     'CSSLexer.cpp',
     'CSSMediaRule.cpp',
     'CSSMozDocumentRule.cpp',
     'CSSPageRule.cpp',
     'CSSRuleList.cpp',
     'CSSStyleSheet.cpp',
@@ -236,16 +239,17 @@ UNIFIED_SOURCES += [
     'PreloadedStyleSheet.cpp',
     'RuleNodeCacheConditions.cpp',
     'RuleProcessorCache.cpp',
     'ServoBindings.cpp',
     'ServoCSSRuleList.cpp',
     'ServoDeclarationBlock.cpp',
     'ServoDocumentRule.cpp',
     'ServoElementSnapshot.cpp',
+    'ServoImportRule.cpp',
     'ServoKeyframeRule.cpp',
     'ServoKeyframesRule.cpp',
     'ServoMediaList.cpp',
     'ServoMediaRule.cpp',
     'ServoNamespaceRule.cpp',
     'ServoPageRule.cpp',
     'ServoSpecifiedValues.cpp',
     'ServoStyleRule.cpp',
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -31,17 +31,16 @@
 
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsStyleUtil.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "nsCSSParser.h"
 #include "nsDOMClassInfoID.h"
 #include "mozilla/dom/CSSStyleDeclarationBinding.h"
-#include "mozilla/dom/CSSImportRuleBinding.h"
 #include "mozilla/dom/CSSFontFaceRuleBinding.h"
 #include "mozilla/dom/CSSFontFeatureValuesRuleBinding.h"
 #include "mozilla/dom/CSSCounterStyleRuleBinding.h"
 #include "StyleRule.h"
 #include "nsFont.h"
 #include "nsIURI.h"
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
@@ -168,28 +167,28 @@ Rule::GetParentRule() const
 }
 
 // -------------------------------------------
 // ImportRule
 //
 
 ImportRule::ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
                        uint32_t aLineNumber, uint32_t aColumnNumber)
-  : Rule(aLineNumber, aColumnNumber)
+  : CSSImportRule(aLineNumber, aColumnNumber)
   , mURLSpec(aURLSpec)
   , mMedia(aMedia)
 {
   MOZ_ASSERT(aMedia);
   // XXXbz This is really silly.... the mMedia here will be replaced
   // with itself if we manage to load a sheet.  Which should really
   // never fail nowadays, in sane cases.
 }
 
 ImportRule::ImportRule(const ImportRule& aCopy)
-  : Rule(aCopy),
+  : CSSImportRule(aCopy),
     mURLSpec(aCopy.mURLSpec)
 {
   // Whether or not an @import rule has a null sheet is a permanent
   // property of that @import rule, since it is null only if the target
   // sheet failed security checks.
   if (aCopy.mChildSheet) {
     RefPtr<StyleSheet> sheet =
       aCopy.mChildSheet->Clone(nullptr, this, nullptr, nullptr);
@@ -204,32 +203,24 @@ ImportRule::ImportRule(const ImportRule&
 
 ImportRule::~ImportRule()
 {
   if (mChildSheet) {
     mChildSheet->SetOwnerRule(nullptr);
   }
 }
 
-NS_IMPL_ADDREF_INHERITED(ImportRule, Rule)
-NS_IMPL_RELEASE_INHERITED(ImportRule, Rule)
-
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ImportRule, Rule, mMedia, mChildSheet)
-
-bool
-ImportRule::IsCCLeaf() const
-{
-  // We're not a leaf.
-  return false;
-}
+NS_IMPL_ADDREF_INHERITED(ImportRule, CSSImportRule)
+NS_IMPL_RELEASE_INHERITED(ImportRule, CSSImportRule)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ImportRule, CSSImportRule, mMedia, mChildSheet)
 
 // QueryInterface implementation for ImportRule
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ImportRule)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMCSSImportRule)
-NS_INTERFACE_MAP_END_INHERITING(Rule)
+NS_INTERFACE_MAP_END_INHERITING(CSSImportRule)
 
 #ifdef DEBUG
 /* virtual */ void
 ImportRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   // Indent
   for (int32_t indent = aIndent; --indent >= 0; ) {
@@ -243,22 +234,16 @@ ImportRule::List(FILE* out, int32_t aInd
   nsAutoString mediaText;
   mMedia->GetText(mediaText);
   AppendUTF16toUTF8(mediaText, str);
   str.AppendLiteral("\n");
   fprintf_stderr(out, "%s", str.get());
 }
 #endif
 
-/* virtual */ int32_t
-ImportRule::GetType() const
-{
-  return Rule::IMPORT_RULE;
-}
-
 /* virtual */ already_AddRefed<Rule>
 ImportRule::Clone() const
 {
   RefPtr<Rule> clone = new ImportRule(*this);
   return clone.forget();
 }
 
 void
@@ -269,22 +254,16 @@ ImportRule::SetSheet(CSSStyleSheet* aShe
   // set the new sheet
   mChildSheet = aSheet;
   aSheet->SetOwnerRule(this);
 
   // set our medialist to be the same as the sheet's medialist
   mMedia = static_cast<nsMediaList*>(mChildSheet->Media());
 }
 
-uint16_t
-ImportRule::Type() const
-{
-  return nsIDOMCSSRule::IMPORT_RULE;
-}
-
 void
 ImportRule::GetCssTextImpl(nsAString& aCssText) const
 {
   aCssText.AssignLiteral("@import url(");
   nsStyleUtil::AppendEscapedCSSString(mURLSpec, aCssText);
   aCssText.Append(')');
   if (mMedia) {
     nsAutoString mediaText;
@@ -311,55 +290,30 @@ ImportRule::GetStyleSheet() const
 
 NS_IMETHODIMP
 ImportRule::GetHref(nsAString & aHref)
 {
   aHref = mURLSpec;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-ImportRule::GetMedia(nsIDOMMediaList * *aMedia)
-{
-  NS_ENSURE_ARG_POINTER(aMedia);
-
-  NS_ADDREF(*aMedia = mMedia);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImportRule::GetStyleSheet(nsIDOMCSSStyleSheet * *aStyleSheet)
-{
-  NS_ENSURE_ARG_POINTER(aStyleSheet);
-
-  NS_IF_ADDREF(*aStyleSheet = mChildSheet);
-  return NS_OK;
-}
-
 /* virtual */ size_t
 ImportRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mURLSpec
   //
   // The following members are not measured:
   // - mMedia, because it is measured via CSSStyleSheet::mMedia
   // - mChildSheet, because it is measured via CSSStyleSheetInner::mSheets
 }
 
-/* virtual */ JSObject*
-ImportRule::WrapObject(JSContext* aCx,
-                       JS::Handle<JSObject*> aGivenProto)
-{
-  return CSSImportRuleBinding::Wrap(aCx, this, aGivenProto);
-}
-
 // -------------------------------------------
 // nsICSSMediaRule
 //
 MediaRule::MediaRule(uint32_t aLineNumber, uint32_t aColumnNumber)
   : CSSMediaRule(aLineNumber, aColumnNumber)
 {
 }
 
--- a/layout/style/nsRuleData.cpp
+++ b/layout/style/nsRuleData.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsRuleData.h"
 
+#include "nsAttrValueInlines.h"
 #include "nsCSSParser.h"
 #include "mozilla/Poison.h"
 #include <stdint.h>
 
 using namespace mozilla;
 
 inline size_t
 nsRuleData::GetPoisonOffset()
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2404,35 +2404,35 @@ nsStyleSet::InvalidateStyleForCSSRuleCha
 {
   MOZ_ASSERT_IF(mStylesHaveChanged, mChangedScopeStyleRoots.IsEmpty());
 
   AutoTArray<RefPtr<mozilla::dom::Element>, 1> scopeRoots;
   mChangedScopeStyleRoots.SwapElements(scopeRoots);
   mStylesHaveChanged = false;
 
   nsPresContext* presContext = PresContext();
-  RestyleManager* restyleManager = presContext->RestyleManager();
+  RestyleManager* restyleManager = presContext->RestyleManager()->AsGecko();
   Element* root = presContext->Document()->GetRootElement();
   if (!root) {
     // No content to restyle
     return;
   }
 
   if (scopeRoots.IsEmpty()) {
     // If scopeRoots is empty, we know that mStylesHaveChanged was true at
     // the beginning of this function, and that we need to restyle the whole
     // document.
-    restyleManager->PostRestyleEventForCSSRuleChanges(root,
-                                                      eRestyle_Subtree,
-                                                      nsChangeHint(0));
+    restyleManager->PostRestyleEvent(root,
+                                     eRestyle_Subtree,
+                                     nsChangeHint(0));
   } else {
     for (Element* scopeRoot : scopeRoots) {
-      restyleManager->PostRestyleEventForCSSRuleChanges(scopeRoot,
-                                                        eRestyle_Subtree,
-                                                        nsChangeHint(0));
+      restyleManager->PostRestyleEvent(scopeRoot,
+                                       eRestyle_Subtree,
+                                       nsChangeHint(0));
     }
   }
 }
 
 void
 nsStyleSet::GCRuleTrees()
 {
   MOZ_ASSERT(!mInReconstruct);
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -268,16 +268,18 @@ skip-if = toolkit == 'android' #bug 7752
 [test_shorthand_property_getters.html]
 [test_specified_value_serialization.html]
 [test_style_attr_listener.html]
 [test_style_attribute_quirks.html]
 [test_style_attribute_standards.html]
 [test_style_struct_copy_constructors.html]
 [test_stylesheet_clone_font_face.html]
 skip-if = stylo # bug 1367523
+[test_stylesheet_additions.html]
+skip-if = !stylo
 [test_supports_rules.html]
 [test_system_font_serialization.html]
 [test_text_decoration_shorthands.html]
 [test_transitions_and_reframes.html]
 [test_transitions_and_restyles.html]
 [test_transitions_and_zoom.html]
 [test_transitions_cancel_near_end.html]
 [test_transitions_computed_values.html]
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -35,21 +35,16 @@ to mochitest command.
     * test_webkit_device_pixel_ratio.html [3]
   * test_media_queries_dynamic.html `restyle count`: support elementsRestyled [6]
   * test_media_queries_dynamic_xbl.html: xbl support bug 1290276 [2]
 * Animation support:
   * OMTA
     * test_animations_omta.html: bug 1361938, bug 1340005 [*]
   * SMIL Animation
     * test_restyles_in_smil_animation.html [2]
-* CSSOM support:
-  * \@import bug 1352968
-    * test_bug221428.html [1]
-    * test_css_eof_handling.html [1]
-* test_bug397427.html: @import issue bug 1331291 and CSSOM support of @import [1]
 * console support bug 1352669
   * test_bug413958.html `monitorConsole` [3]
   * test_parser_diagnostics_unprintables.html [550]
 * Transition support:
   * test_transitions.html: pseudo elements [4]
   * test_transitions_and_reframes.html `pseudo-element`: bug 1366422 [4]
   * Events:
     * test_animations_event_order.html [2]
@@ -134,18 +129,16 @@ to mochitest command.
   * ... `mathml-links.html` [2]
   * ... `caret-color-on-visited-1.html` [2]
 
 ## Assertions
 
 ## Need Gecko change
 
 * Servo is correct but Gecko is wrong
-  * flex-basis should be 0px when omitted in flex shorthand bug 1331530
-    * test_flexbox_flex_shorthand.html `flex-basis` [10]
   * Gecko rejects calc() in -webkit-gradient bug 1363349
     * test_property_syntax_errors.html `-webkit-gradient` [20]
 * test_property_syntax_errors.html `linear-gradient(0,`: unitless zero as degree bug 1363292 [10]
 * test_specified_value_serialization.html `-webkit-radial-gradient`: bug 1367299 [1]
 * test_variables.html `var(--var6)`: irrelevant test for stylo bug 1367306 [1]
 * Difference in rect serialization bug 1367028
   * test_shorthand_property_getters.html `5 5 5 5` [1]
 
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_stylesheet_additions.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+  Test for bug 1273303: Stylesheet additions and removals known to not
+  affect the document don't trigger restyles
+</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<div class="classScope">
+  <div></div>
+</div>
+<div id="idScope">
+  <div></div>
+</div>
+<!--
+  We do it this way, using `disabled`, because appending stylesheets to the
+  document marks a restyle as needed, so we can't accurately measure whether
+  we've restyled or not.
+-->
+<style id="target" disabled></style>
+<script>
+SimpleTest.waitForExplicitFinish();
+const utils = SpecialPowers.getDOMWindowUtils(window);
+const TESTS = [
+  { selector: ".nonexistentClassScope", restyle: false },
+  { selector: ".nonexistentClassScope + div", restyle: true },
+  { selector: ".nonexistentClassScope div + div", restyle: false },
+  { selector: ".classScope", restyle: true },
+  { selector: ".classScope div", restyle: true },
+  { selector: "#idScope", restyle: true },
+  { selector: "#nonexistentIdScope", restyle: false },
+  { selector: "#nonexistentIdScope div + bar", restyle: false },
+];
+
+for (const test of TESTS) {
+  // TODO(emilio): We don't as of right now, but we could also scan the
+  // stylesheet when removed if needed and test this too.
+  target.innerHTML = test.selector + " { color: green; }";
+  target.disabled = true;
+  document.body.offsetWidth;
+  const prevGeneration = utils.restyleGeneration;
+  target.disabled = false; // Make the stylesheet effective.
+  let element = document.querySelector(test.selector);
+  if (element) {
+    is(test.restyle, true, "How could we not expect a restyle?");
+    is(getComputedStyle(element).color, "rgb(0, 128, 0)",
+       "Element style should've changed appropriately");
+  }
+  document.body.offsetWidth;
+  is(test.restyle, utils.restyleGeneration != prevGeneration,
+     `Stylesheet addition with ${test.selector} should ${test.restyle ? "have" : "not have"} caused a restyle`);
+}
+
+SimpleTest.finish();
+</script>
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 96cdb173f86dfc86cbd21d097b24ec1e256d69fc (2017-05-22 11:55:15 +0300)
+The git commit ID used was 087dc942a9a3bb5cbb88e3763cde7fe709db99e9 (2017-05-29 15:51:19 +1200)
--- a/media/libcubeb/gtest/common.h
+++ b/media/libcubeb/gtest/common.h
@@ -65,47 +65,69 @@ layout_info const layout_infos[CUBEB_LAY
   { "3f2",            5,  CUBEB_LAYOUT_3F2 },
   { "3f2 lfe",        6,  CUBEB_LAYOUT_3F2_LFE },
   { "3f3r lfe",       7,  CUBEB_LAYOUT_3F3R_LFE },
   { "3f4 lfe",        8,  CUBEB_LAYOUT_3F4_LFE }
 };
 
 int has_available_input_device(cubeb * ctx)
 {
-  cubeb_device_collection * devices;
+  cubeb_device_collection devices;
   int input_device_available = 0;
   int r;
   /* Bail out early if the host does not have input devices. */
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &devices);
   if (r != CUBEB_OK) {
     fprintf(stderr, "error enumerating devices.");
     return 0;
   }
 
-  if (devices->count == 0) {
+  if (devices.count == 0) {
     fprintf(stderr, "no input device available, skipping test.\n");
-    cubeb_device_collection_destroy(ctx, devices);
+    cubeb_device_collection_destroy(ctx, &devices);
     return 0;
   }
 
-  for (uint32_t i = 0; i < devices->count; i++) {
-    input_device_available |= (devices->device[i]->state ==
+  for (uint32_t i = 0; i < devices.count; i++) {
+    input_device_available |= (devices.device[i].state ==
                                CUBEB_DEVICE_STATE_ENABLED);
   }
 
   if (!input_device_available) {
     fprintf(stderr, "there are input devices, but they are not "
         "available, skipping\n");
   }
 
-  cubeb_device_collection_destroy(ctx, devices);
+  cubeb_device_collection_destroy(ctx, &devices);
   return !!input_device_available;
 }
 
 void print_log(const char * msg, ...)
 {
   va_list args;
   va_start(args, msg);
   vprintf(msg, args);
   va_end(args);
 }
 
+/** Initialize cubeb with backend override.
+ *  Create call cubeb_init passing value for CUBEB_BACKEND env var as
+ *  override. */
+int common_init(cubeb ** ctx, char const * ctx_name)
+{
+  int r;
+  char const * backend;
+  char const * ctx_backend;
+
+  backend = getenv("CUBEB_BACKEND");
+  r = cubeb_init(ctx, ctx_name, backend);
+  if (r == CUBEB_OK && backend) {
+    ctx_backend = cubeb_get_backend_id(*ctx);
+    if (strcmp(backend, ctx_backend) != 0) {
+      fprintf(stderr, "Requested backend `%s', got `%s'\n",
+              backend, ctx_backend);
+    }
+  }
+
+  return r;
+}
+
 #endif /* TEST_COMMON */
--- a/media/libcubeb/gtest/test_audio.cpp
+++ b/media/libcubeb/gtest/test_audio.cpp
@@ -95,17 +95,17 @@ int supports_channel_count(string backen
 }
 
 int run_test(int num_channels, layout_info layout, int sampling_rate, int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
 
-  r = cubeb_init(&ctx, "Cubeb audio test: channels", NULL);
+  r = common_init(&ctx, "Cubeb audio test: channels");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     return r;
   }
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   const char * backend_id = cubeb_get_backend_id(ctx);
@@ -145,17 +145,17 @@ int run_test(int num_channels, layout_in
 }
 
 int run_panning_volume_test(int is_float)
 {
   int r = CUBEB_OK;
 
   cubeb *ctx = NULL;
 
-  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
+  r = common_init(&ctx, "Cubeb audio test");
   if (r != CUBEB_OK) {
     fprintf(stderr, "Error initializing cubeb library\n");
     return r;
   }
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
--- a/media/libcubeb/gtest/test_devices.cpp
+++ b/media/libcubeb/gtest/test_devices.cpp
@@ -8,16 +8,17 @@
 /* libcubeb enumerate device test/example.
  * Prints out a list of devices enumerated. */
 #include "gtest/gtest.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <memory>
 #include "cubeb/cubeb.h"
+#include "common.h"
 
 static void
 print_device_info(cubeb_device_info * info, FILE * f)
 {
   char devfmts[64] = "";
   const char * devtype, * devstate, * devdeffmt;
 
   switch (info->type) {
@@ -96,48 +97,71 @@ print_device_info(cubeb_device_info * in
 }
 
 static void
 print_device_collection(cubeb_device_collection * collection, FILE * f)
 {
   uint32_t i;
 
   for (i = 0; i < collection->count; i++)
-    print_device_info(collection->device[i], f);
+    print_device_info(&collection->device[i], f);
+}
+
+TEST(cubeb, destroy_default_collection)
+{
+  int r;
+  cubeb * ctx = NULL;
+  cubeb_device_collection collection{ nullptr, 0 };
+
+  r = common_init(&ctx, "Cubeb audio test");
+  ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
+
+  std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
+    cleanup_cubeb_at_exit(ctx, cubeb_destroy);
+
+  ASSERT_EQ(collection.device, nullptr);
+  ASSERT_EQ(collection.count, (size_t) 0);
+
+  r = cubeb_device_collection_destroy(ctx, &collection);
+  if (r != CUBEB_ERROR_NOT_SUPPORTED) {
+    ASSERT_EQ(r, CUBEB_OK);
+    ASSERT_EQ(collection.device, nullptr);
+    ASSERT_EQ(collection.count, (size_t) 0);
+  }
 }
 
 TEST(cubeb, enumerate_devices)
 {
   int r;
   cubeb * ctx = NULL;
-  cubeb_device_collection * collection = NULL;
+  cubeb_device_collection collection;
 
-  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
+  r = common_init(&ctx, "Cubeb audio test");
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   fprintf(stdout, "Enumerating input devices for backend %s\n",
       cubeb_get_backend_id(ctx));
 
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection);
   if (r == CUBEB_ERROR_NOT_SUPPORTED) {
     fprintf(stderr, "Device enumeration not supported"
                     " for this backend, skipping this test.\n");
     r = CUBEB_OK;
   }
   ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
 
-  fprintf(stdout, "Found %u input devices\n", collection->count);
-  print_device_collection(collection, stdout);
-  cubeb_device_collection_destroy(ctx, collection);
+  fprintf(stdout, "Found %zu input devices\n", collection.count);
+  print_device_collection(&collection, stdout);
+  cubeb_device_collection_destroy(ctx, &collection);
 
   fprintf(stdout, "Enumerating output devices for backend %s\n",
           cubeb_get_backend_id(ctx));
 
   r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
   ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
 
-  fprintf(stdout, "Found %u output devices\n", collection->count);
-  print_device_collection(collection, stdout);
-  cubeb_device_collection_destroy(ctx, collection);
+  fprintf(stdout, "Found %zu output devices\n", collection.count);
+  print_device_collection(&collection, stdout);
+  cubeb_device_collection_destroy(ctx, &collection);
 }
--- a/media/libcubeb/gtest/test_duplex.cpp
+++ b/media/libcubeb/gtest/test_duplex.cpp
@@ -78,17 +78,17 @@ TEST(cubeb, duplex)
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params input_params;
   cubeb_stream_params output_params;
   int r;
   user_state_duplex stream_state = { false };
   uint32_t latency_frames = 0;
 
-  r = cubeb_init(&ctx, "Cubeb duplex example", NULL);
+  r = common_init(&ctx, "Cubeb duplex example");
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   /* This test needs an available input device, skip it if this host does not
    * have one. */
   if (!has_available_input_device(ctx)) {
--- a/media/libcubeb/gtest/test_latency.cpp
+++ b/media/libcubeb/gtest/test_latency.cpp
@@ -1,23 +1,23 @@
 #include "gtest/gtest.h"
 #include <stdlib.h>
 #include <memory>
 #include "cubeb/cubeb.h"
+#include "common.h"
 
 TEST(cubeb, latency)
 {
   cubeb * ctx = NULL;
   int r;
   uint32_t max_channels;
   uint32_t preferred_rate;
   uint32_t latency_frames;
-  cubeb_channel_layout layout;
 
-  r = cubeb_init(&ctx, "Cubeb audio test", NULL);
+  r = common_init(&ctx, "Cubeb audio test");
   ASSERT_EQ(r, CUBEB_OK);
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   r = cubeb_get_max_channel_count(ctx, &max_channels);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
@@ -25,25 +25,21 @@ TEST(cubeb, latency)
   }
 
   r = cubeb_get_preferred_sample_rate(ctx, &preferred_rate);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
     ASSERT_GT(preferred_rate, 0u);
   }
 
-  r = cubeb_get_preferred_channel_layout(ctx, &layout);
-  ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED ||
-              (r == CUBEB_ERROR && layout == CUBEB_LAYOUT_UNDEFINED));
-
   cubeb_stream_params params = {
     CUBEB_SAMPLE_FLOAT32NE,
     preferred_rate,
     max_channels,
-    (r == CUBEB_OK) ? layout : CUBEB_LAYOUT_UNDEFINED
+    CUBEB_LAYOUT_UNDEFINED
 #if defined(__ANDROID__)
     , CUBEB_STREAM_TYPE_MUSIC
 #endif
   };
   r = cubeb_get_min_latency(ctx, params, &latency_frames);
   ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
   if (r == CUBEB_OK) {
     ASSERT_GT(latency_frames, 0u);
--- a/media/libcubeb/gtest/test_mixer.cpp
+++ b/media/libcubeb/gtest/test_mixer.cpp
@@ -83,16 +83,17 @@ audio_input audio_inputs[CUBEB_LAYOUT_MA
   { CUBEB_LAYOUT_2F2,           { L, R, LS, RS } },
   { CUBEB_LAYOUT_2F2_LFE,       { L, R, LFE, LS, RS } },
   { CUBEB_LAYOUT_3F2,           { L, R, C, LS, RS } },
   { CUBEB_LAYOUT_3F2_LFE,       { L, R, C, LFE, LS, RS } },
   { CUBEB_LAYOUT_3F3R_LFE,      { L, R, C, LFE, RC, LS, RS } },
   { CUBEB_LAYOUT_3F4_LFE,       { L, R, C, LFE, RLS, RRS, LS, RS } }
 };
 
+// The test cases must be aligned with cubeb_downmix.
 void
 downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
 {
   if (in_layout == CUBEB_LAYOUT_UNDEFINED) {
     return; // Only possible output layout would be UNDEFINED.
   }
 
   cubeb_stream_params in_params = {
@@ -121,29 +122,36 @@ downmix_test(float const * data, cubeb_c
   if (!cubeb_should_downmix(&in_params, &out_params)) {
     return;
   }
 
   fprintf(stderr, "Downmix from %s to %s\n", layout_infos[in_layout].name, layout_infos[out_layout].name);
 
   unsigned int const inframes = 10;
   vector<float> in(in_params.channels * inframes);
+#if defined(__APPLE__)
+  // The mixed buffer size doesn't be changed based on the channel layout set on OSX.
+  // Please see the comment above downmix_3f2 in cubeb_mixer.cpp.
+  vector<float> out(in_params.channels * inframes);
+#else
+  // In normal case, the mixed buffer size is based on the mixing channel layout.
   vector<float> out(out_params.channels * inframes);
+#endif
 
   for (unsigned int offset = 0 ; offset < inframes * in_params.channels ; offset += in_params.channels) {
     for (unsigned int i = 0 ; i < in_params.channels ; ++i) {
       in[offset + i] = data[i];
     }
   }
 
   // Create a mixer for downmix only.
   std::unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)>
     mixer(cubeb_mixer_create(in_params.format, CUBEB_MIXER_DIRECTION_DOWNMIX), cubeb_mixer_destroy);
 
-  cubeb_mixer_mix(mixer.get(), in.data(), inframes, out.data(), &in_params, &out_params);
+  cubeb_mixer_mix(mixer.get(), inframes, in.data(), in.size(), out.data(), out.size(), &in_params, &out_params);
 
   uint32_t in_layout_mask = 0;
   for (unsigned int i = 0 ; i < in_params.channels; ++i) {
     in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i];
   }
 
   uint32_t out_layout_mask = 0;
   for (unsigned int i = 0 ; out_layout != CUBEB_LAYOUT_UNDEFINED && i < out_params.channels; ++i) {
@@ -152,32 +160,37 @@ downmix_test(float const * data, cubeb_c
 
   for (unsigned int i = 0 ; i < inframes * out_params.channels ; ++i) {
     unsigned int index = i % out_params.channels;
 
     // downmix_3f2
     if ((in_layout == CUBEB_LAYOUT_3F2 || in_layout == CUBEB_LAYOUT_3F2_LFE) &&
         out_layout >= CUBEB_LAYOUT_MONO && out_layout <= CUBEB_LAYOUT_2F2_LFE) {
       auto & downmix_results = DOWNMIX_3F2_RESULTS[in_layout - CUBEB_LAYOUT_3F2][out_layout - CUBEB_LAYOUT_MONO];
-      fprintf(stderr, "[3f2] Expect: %lf, Get: %lf\n", downmix_results[index], out[index]);
-      ASSERT_EQ(downmix_results[index], out[index]);
+      fprintf(stderr, "[3f2] Expect: %lf, Get: %lf\n", downmix_results[index], out[i]);
+      ASSERT_EQ(downmix_results[index], out[i]);
       continue;
     }
 
+#if defined(__APPLE__)
+    // We only support downmix for audio 5.1 on OS X currently.
+    return;
+#endif
+
     // mix_remap
     if (out_layout_mask & in_layout_mask) {
       uint32_t mask = 1 << CHANNEL_INDEX_TO_ORDER[out_layout][index];
-      fprintf(stderr, "[map channels] Expect: %lf, Get: %lf\n", (mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[index]);
-      ASSERT_EQ((mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[index]);
+      fprintf(stderr, "[map channels] Expect: %lf, Get: %lf\n", (mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[i]);
+      ASSERT_EQ((mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[i]);
       continue;
     }
 
     // downmix_fallback
-    fprintf(stderr, "[fallback] Expect: %lf, Get: %lf\n", audio_inputs[in_layout].data[index], out[index]);
-    ASSERT_EQ(audio_inputs[in_layout].data[index], out[index]);
+    fprintf(stderr, "[fallback] Expect: %lf, Get: %lf\n", audio_inputs[in_layout].data[index], out[i]);
+    ASSERT_EQ(audio_inputs[in_layout].data[index], out[i]);
   }
 }
 
 TEST(cubeb, mixer)
 {
   for (auto audio_input : audio_inputs) {
     for (auto audio_output : layout_infos) {
       downmix_test(audio_input.data, audio_input.layout, audio_output.layout);
--- a/media/libcubeb/gtest/test_overload_callback.cpp
+++ b/media/libcubeb/gtest/test_overload_callback.cpp
@@ -53,17 +53,17 @@ void state_cb(cubeb_stream * stream, voi
 TEST(cubeb, overload_callback)
 {
   cubeb * ctx;
   cubeb_stream * stream;
   cubeb_stream_params output_params;
   int r;
   uint32_t latency_frames = 0;
 
-  r = cubeb_init(&ctx, "Cubeb callback overload", NULL);
+  r = common_init(&ctx, "Cubeb callback overload");
   ASSERT_EQ(r, CUBEB_OK);
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   output_params.format = STREAM_FORMAT;
   output_params.rate = 48000;
   output_params.channels = 2;
--- a/media/libcubeb/gtest/test_record.cpp
+++ b/media/libcubeb/gtest/test_record.cpp
@@ -72,17 +72,17 @@ TEST(cubeb, record)
     fprintf(stderr, "Set log callback failed\n");
   }
   cubeb *ctx;
   cubeb_stream *stream;
   cubeb_stream_params params;
   int r;
   user_state_record stream_state = { false };
 
-  r = cubeb_init(&ctx, "Cubeb record example", NULL);
+  r = common_init(&ctx, "Cubeb record example");
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
 
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   /* This test needs an available input device, skip it if this host does not
    * have one. */
   if (!has_available_input_device(ctx)) {
--- a/media/libcubeb/gtest/test_sanity.cpp
+++ b/media/libcubeb/gtest/test_sanity.cpp
@@ -72,17 +72,17 @@ test_state_callback(cubeb_stream * /*stm
 }
 
 TEST(cubeb, init_destroy_context)
 {
   int r;
   cubeb * ctx;
   char const* backend_id;
 
-  r = cubeb_init(&ctx, "test_sanity", NULL);
+  r = common_init(&ctx, "test_sanity");
   ASSERT_EQ(r, CUBEB_OK);
   ASSERT_NE(ctx, nullptr);
 
   backend_id = cubeb_get_backend_id(ctx);
   ASSERT_TRUE(backend_id);
 
   fprintf(stderr, "Backend: %s\n", backend_id);
 
@@ -93,35 +93,36 @@ TEST(cubeb, init_destroy_multiple_contex
 {
   size_t i;
   int r;
   cubeb * ctx[4];
   int order[4] = {2, 0, 3, 1};
   ASSERT_EQ(ARRAY_LENGTH(ctx), ARRAY_LENGTH(order));
 
   for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
-    r = cubeb_init(&ctx[i], NULL, NULL);
+    r = common_init(&ctx[i], NULL);