Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 22 Jun 2017 14:10:19 -0700
changeset 599255 b1b9129838ade91684574f42219b2010928d7db4
parent 599254 4c6668cbaccb1c207ced3bba18b81cf2be8ca495 (current diff)
parent 598989 023b96f48df893f82ced2ad2f659ac4f2b4d303a (diff)
child 599256 eb4d90270b1b4dce965eb4a1960cc4fc44cbbbb5
child 599264 2976c2ef3cc681bf66f4db5aa477ed1152b86dd4
child 599308 c5020fd10d8e17b17b424255cfa25080f51e2df8
child 599309 a6e94c5e14764f2144d992e87fb56a795e3100ed
child 599310 970519ce94f93e83d32ac350e478568383ccfcc0
child 599314 695861c12add90118489d73555f80a3b300041df
child 599330 3e49ea2d21713420e3f84c2265cbbfc0a68a1766
child 599336 df43822c20812c111d83f89ff438f25374687b24
child 599347 c3c919bbfebc61d8aa2992251b3bbbc5b6fb4677
child 599356 b10184ab7dcd8cef626a80512e1dbb2ebe2e00c7
child 599359 e5a6f733faf04e4ae4bc207d134d2f735b72754c
child 599361 9100e0ff26f1b62b7ba6d949614f53797448512c
child 599370 35542582c7c41ad620b0c054dd2bf551f9691bcc
child 599375 fd8cfc37bf642f3cd7bfc9f8a5a76e1ce2fa2900
child 599378 9a03f8a0de900e195e30bbf72aa4281297e74935
child 599380 1a9cfd0976ab70b11b049ec8e7402776827b881b
child 599411 d3a049266adc8f61470e9ec518d2386fd96e94ef
child 599412 63a63e56db037c889c762484f4c8b8cb26c9c915
child 599437 dcdf4a846f7b007526aa626db24598942f13f01d
child 599443 7d7d3cccd9e141d8a01429d9680ddcfb6550f064
child 599456 499bd54a9beb05011164f3dfc6b93f20af3681ad
child 599457 fc9457ab35b4e1763f5fd021b90a9f8d760ed025
child 599478 e4c14f0e0654fbabec9a21770bb647b365c7acfc
child 599481 a6159f65b703e78af22b5dab14f4d55205b65701
child 599484 b84bf22baf805e6104cbec09055c74575c455cb2
child 599492 1872f6b5c20b754bfb27c870545aa0a7b6e0eeeb
child 599515 d1de7370bd3ca50a734f8e27052d53fc5a9b6c29
child 599591 1472270cccc1fc96c025e779d0457f580850b025
child 599671 2a16f48d5eb0b8f3303d1a3d1383f1964d4eddd6
child 600381 c113c8ee691b2aad58c819d699b9a7711159fa3a
child 600383 0a216557eec1523318cd9b6b0da6074ee33b12aa
child 600386 69ca0f722b19318a14f09a865bc2985f5f736283
push id65462
push usergsquelart@mozilla.com
push dateThu, 22 Jun 2017 21:58:15 +0000
reviewersmerge
milestone56.0a1
Merge autoland to central, a=merge MozReview-Commit-ID: DBM5ExEXtYD
browser/components/tests/startupRecorder.js
layout/generic/nsFrame.cpp
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -15,16 +15,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContextMenu",
   "resource://gre/modules/LoginManagerContextMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
   "resource://gre/modules/WebNavigationFrames.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
   "resource://gre/modules/ContextualIdentityService.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DevToolsShim",
+  "chrome://devtools-shim/content/DevToolsShim.jsm");
 
 var gContextMenuContentData = null;
 
 function openContextMenu(aMessage) {
   let data = aMessage.data;
   let browser = aMessage.target;
 
   let spellInfo = data.spellInfo;
@@ -299,17 +301,21 @@ nsContextMenu.prototype = {
                   this.isContentSelected);
     this.showItem("context-viewpartialsource-mathml",
                   this.onMathML && !this.isContentSelected);
 
     var shouldShow = !(this.isContentSelected ||
                        this.onImage || this.onCanvas ||
                        this.onVideo || this.onAudio ||
                        this.onLink || this.onTextInput);
-    var showInspect = this.inTabBrowser && gPrefService.getBoolPref("devtools.inspector.enabled");
+
+    var showInspect = DevToolsShim.isInstalled() &&
+                      this.inTabBrowser &&
+                      gPrefService.getBoolPref("devtools.inspector.enabled", false);
+
     this.showItem("context-viewsource", shouldShow);
     this.showItem("context-viewinfo", shouldShow);
     // The page info is broken for WebExtension popups, as the browser is
     // destroyed when the popup is closed.
     this.setItemAttr("context-viewinfo", "disabled", this.webExtBrowserType === "popup");
     this.showItem("inspect-separator", showInspect);
     this.showItem("context-inspect", showInspect);
 
@@ -626,20 +632,17 @@ nsContextMenu.prototype = {
     gSync.initPageContextMenu(this);
   },
 
   openPasswordManager() {
     LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
   },
 
   inspectNode() {
-    let gBrowser = this.browser.ownerGlobal.gBrowser;
-    let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    let { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
-    return gDevToolsBrowser.inspectNode(gBrowser.selectedTab, this.targetSelectors);
+    return DevToolsShim.inspectNode(gBrowser.selectedTab, this.targetSelectors);
   },
 
   /**
    * Set various context menu attributes based on the state of the world.
    * Note: If the context menu is on a remote process the supplied parameters
    * will be overwritten with data from gContextMenuContentData.
    *
    * @param {Object} aNode The node that this menu is being opened on.
--- a/browser/base/content/test/newtab/browser_newtab_focus.js
+++ b/browser/base/content/test/newtab/browser_newtab_focus.js
@@ -2,47 +2,80 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * These tests make sure that focusing the 'New Tab Page' works as expected.
  */
 add_task(async function() {
   await pushPrefs(["accessibility.tabfocus", 7]);
 
+  // When the onboarding component is enabled, it would inject extra tour notification into
+  // the newtab page so there would be 2 more notification close button and action button
+  let onbardingEnabled = AppConstants.NIGHTLY_BUILD && Services.prefs.getBoolPref("browser.onboarding.enabled");
+
   // Focus count in new tab page.
   // 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search
   // bar; search button; and toggle button. Additionaly there may or may not be
   // a scroll bar caused by fix to 1180387, which will eat an extra focus
   let FOCUS_COUNT = 30;
 
   // Create a new tab page.
   await setLinks("0,1,2,3,4,5,6,7,8");
   setPinnedLinks("");
 
-  await addNewTabPageTab();
+  let tab = await addNewTabPageTab();
+  if (onbardingEnabled) {
+    FOCUS_COUNT += 2;
+    await promiseTourNotificationOpened(tab.linkedBrowser);
+  }
   gURLBar.focus();
-
   // Count the focus with the enabled page.
   countFocus(FOCUS_COUNT);
-
   // Disable page and count the focus with the disabled page.
   NewTabUtils.allPages.enabled = false;
 
-  countFocus(4);
+  let expectedCount = 4;
+  if (onbardingEnabled) {
+    expectedCount += 2;
+  }
+  countFocus(expectedCount);
 
   NewTabUtils.allPages.enabled = true;
 });
 
 /**
  * Focus the urlbar and count how many focus stops to return again to the urlbar.
  */
 function countFocus(aExpectedCount) {
   let focusCount = 0;
   do {
     EventUtils.synthesizeKey("VK_TAB", {});
     if (document.activeElement == gBrowser.selectedBrowser) {
       focusCount++;
     }
   } while (document.activeElement != gURLBar.inputField);
-
   ok(focusCount == aExpectedCount || focusCount == (aExpectedCount + 1),
      "Validate focus count in the new tab page.");
 }
+
+/**
+ * Wait for the onboarding tour notification opens
+ */
+function promiseTourNotificationOpened(browser) {
+  let condition = () => {
+    return ContentTask.spawn(browser, {}, function() {
+      return new Promise(resolve => {
+        let bar = content.document.querySelector("#onboarding-notification-bar");
+        if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
+          resolve(true);
+          return;
+        }
+        resolve(false);
+      });
+    })
+  };
+  return BrowserTestUtils.waitForCondition(
+    condition,
+    "Should open tour notification",
+    100,
+    30
+  );
+}
--- a/browser/extensions/onboarding/bootstrap.js
+++ b/browser/extensions/onboarding/bootstrap.js
@@ -6,17 +6,18 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 
 const PREF_WHITELIST = [
   "browser.onboarding.enabled",
   "browser.onboarding.hidden",
-  "browser.onboarding.notification.finished"
+  "browser.onboarding.notification.finished",
+  "browser.onboarding.notification.lastPrompted"
 ];
 
 /**
  * Set pref. Why no `getPrefs` function is due to the priviledge level.
  * We cannot set prefs inside a framescript but can read.
  * For simplicity and effeciency, we still read prefs inside the framescript.
  *
  * @param {Array} prefs the array of prefs to set.
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -32,30 +32,32 @@
   offset-inline-start: 30px;
   background: url("img/overlay-icon.svg") no-repeat;
 }
 
 #onboarding-overlay-dialog {
   display: none;
 }
 
-#onboarding-overlay-close-btn {
+#onboarding-overlay-close-btn,
+#onboarding-notification-close-btn {
   position: absolute;
   top: 15px;
   offset-inline-end: 15px;
   cursor: pointer;
   width: 16px;
   height: 16px;
   background-image: url(chrome://browser/skin/sidebar/close.svg);
   background-position: center center;
   background-repeat: no-repeat;
   padding: 12px;
 }
 
-#onboarding-overlay-close-btn:hover {
+#onboarding-overlay-close-btn:hover,
+#onboarding-notification-close-btn:hover {
   background-color: rgba(204, 204, 204, 0.6);
 }
 
 #onboarding-overlay.onboarding-opened > #onboarding-overlay-dialog {
   width: 960px;
   height: 510px;
   background: #f5f5f7;
   border: 1px solid rgba(9, 6, 13, 0.1); /* #09060D, 0.1 opacity */
@@ -231,47 +233,150 @@
 }
 
 /* Tour Icons */
 #onboarding-tour-search {
   background-image: url("img/icons_search.svg");
 }
 
 #onboarding-tour-search.onboarding-active,
-#onboarding-tour-search:hover {
+#onboarding-tour-search:hover,
+#onboarding-notification-bar[data-target-tour-id=onboarding-tour-search] #onboarding-notification-tour-icon {
   background-image: url("img/icons_search-colored.svg");
 }
 
 #onboarding-tour-private-browsing {
   background-image: url("img/icons_private.svg");
 }
 
 #onboarding-tour-private-browsing.onboarding-active,
-#onboarding-tour-private-browsing:hover {
+#onboarding-tour-private-browsing:hover,
+#onboarding-notification-bar[data-target-tour-id=onboarding-tour-private-browsing] #onboarding-notification-tour-icon {
   background-image: url("img/icons_private-colored.svg");
 }
 
 #onboarding-tour-addons {
   background-image: url("img/icons_addons.svg");
 }
 
 #onboarding-tour-addons.onboarding-active,
-#onboarding-tour-addons:hover {
+#onboarding-tour-addons:hover,
+#onboarding-notification-bar[data-target-tour-id=onboarding-tour-addons] #onboarding-notification-tour-icon {
   background-image: url("img/icons_addons-colored.svg");
 }
 
 #onboarding-tour-customize {
   background-image: url("img/icons_customize.svg");
 }
 
 #onboarding-tour-customize.onboarding-active,
-#onboarding-tour-customize:hover {
+#onboarding-tour-customize:hover,
+#onboarding-notification-bar[data-target-tour-id=onboarding-tour-customize] #onboarding-notification-tour-icon {
   background-image: url("img/icons_customize-colored.svg");
 }
 
 #onboarding-tour-default-browser {
   background-image: url("img/icons_default.svg");
 }
 
 #onboarding-tour-default-browser.onboarding-active,
-#onboarding-tour-default-browser:hover {
+#onboarding-tour-default-browser:hover,
+#onboarding-notification-bar[data-target-tour-id=onboarding-tour-default-browser] #onboarding-notification-tour-icon {
   background-image: url("img/icons_default-colored.svg");
 }
+
+
+/* Tour Notifications */
+#onboarding-notification-bar {
+  position: fixed;
+  z-index: 998; /* We want this always under #onboarding-overlay */
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 122px;
+  min-width: 1060px;
+  background: rgba(255, 255, 255, 0.97);
+  border-top: 2px solid #e9e9e9;
+  transition: transform 0.8s;
+  transform: translateY(122px);
+}
+
+#onboarding-notification-bar.onboarding-opened {
+  transform: translateY(0px);
+}
+
+#onboarding-notification-icon {
+  height: 36px;
+  background: url("img/overlay-icon.svg") no-repeat;
+  background-size: 36px;
+  background-position: 34px;
+  padding-inline-start: 190px;
+  position: absolute;
+  offset-block-start: 50%;
+  transform: translateY(-50%);
+}
+
+#onboarding-notification-icon::after {
+  --height: 22px;
+  content: attr(data-tooltip);
+  background: #5ce6e6;
+  position: absolute;
+  top: 0;
+  offset-inline-start: 68px;
+  color: #10404a;
+  font-size: 12px;
+  min-height: var(--height);
+  line-height: var(--height);
+  border-radius: calc(var(--height) / 2);
+  border: 1px solid #fff;
+  padding: 0 10px;
+  text-align: center;
+}
+
+#onboarding-notification-close-btn {
+  background-color: rgba(255, 255, 255, 0.97);
+  border: none;
+  position: absolute;
+  offset-block-start: 50%;
+  offset-inline-end: 34px;
+  transform: translateY(-50%);
+}
+
+#onboarding-notification-message-section {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  position: absolute;
+  offset-block-start: 50%;
+  offset-inline-start: 50%;
+  transform: translate(-50%, -50%);
+}
+
+#onboarding-notification-body {
+  width: 420px;
+  margin: 0 15px;
+  color: #0c0c0d;;
+  display: inline-block;
+}
+
+#onboarding-notification-body * {
+  font-size: 13px
+}
+
+#onboarding-notification-tour-title {
+  margin: 0;
+}
+
+#onboarding-notification-tour-icon {
+  width: 64px;
+  height: 64px;
+  background-repeat: no-repeat;
+}
+
+#onboarding-notification-action-btn {
+  background: #0d96ff;
+  border: none;
+  border-radius: 3px;
+  padding: 10px 20px;
+  font-size: 14px;
+  color: #fff;
+  box-shadow: 0 1px 0 rgba(0,0,0,0.23);
+}
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -22,28 +22,40 @@ const BRAND_SHORT_NAME = Services.string
 
 /**
  * Add any number of tours, following the format
  * {
  *   // The unique tour id
  *   id: "onboarding-tour-addons",
  *   // The string id of tour name which would be displayed on the navigation bar
  *   tourNameId: "onboarding.tour-addon",
+ *   // The method returing strings used on tour notification
+ *   getNotificationStrings(bundle):
+ *     - title: // The string of tour notification title
+ *     - message: // The string of tour notification message
+ *     - button: // The string of tour notification action button title
  *   // Return a div appended with elements for this tours.
  *   // Each tour should contain the following 3 sections in the div:
  *   // .onboarding-tour-description, .onboarding-tour-content, .onboarding-tour-button.
  *   // Add no-button css class in the div if this tour does not need a button.
  *   // The overlay layout will responsively position and distribute space for these 3 sections based on viewport size
  *   getPage() {},
  * },
  **/
 var onboardingTours = [
   {
     id: "onboarding-tour-private-browsing",
     tourNameId: "onboarding.tour-private-browsing",
+    getNotificationStrings(bundle) {
+      return {
+        title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-private-browsing.title"),
+        message: bundle.GetStringFromName("onboarding.notification.onboarding-tour-private-browsing.message"),
+        button: bundle.GetStringFromName("onboarding.button.learnMore"),
+      };
+    },
     getPage(win) {
       let div = win.document.createElement("div");
       div.innerHTML = `
         <section class="onboarding-tour-description">
           <h1 data-l10n-id="onboarding.tour-private-browsing.title"></h1>
           <p data-l10n-id="onboarding.tour-private-browsing.description"></p>
         </section>
         <section class="onboarding-tour-content">
@@ -54,16 +66,23 @@ var onboardingTours = [
         </aside>
       `;
       return div;
     },
   },
   {
     id: "onboarding-tour-addons",
     tourNameId: "onboarding.tour-addons",
+    getNotificationStrings(bundle) {
+      return {
+        title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-addons.title"),
+        message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-addons.message", [BRAND_SHORT_NAME], 1),
+        button: bundle.GetStringFromName("onboarding.button.learnMore"),
+      };
+    },
     getPage(win) {
       let div = win.document.createElement("div");
       div.innerHTML = `
         <section class="onboarding-tour-description">
           <h1 data-l10n-id="onboarding.tour-addons.title"></h1>
           <p data-l10n-id="onboarding.tour-addons.description"></p>
         </section>
         <section class="onboarding-tour-content">
@@ -74,16 +93,23 @@ var onboardingTours = [
         </aside>
       `;
       return div;
     },
   },
   {
     id: "onboarding-tour-customize",
     tourNameId: "onboarding.tour-customize",
+    getNotificationStrings(bundle) {
+      return {
+        title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-customize.title"),
+        message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-customize.message", [BRAND_SHORT_NAME], 1),
+        button: bundle.GetStringFromName("onboarding.button.learnMore"),
+      };
+    },
     getPage(win) {
       let div = win.document.createElement("div");
       div.innerHTML = `
         <section class="onboarding-tour-description">
           <h1 data-l10n-id="onboarding.tour-customize.title"></h1>
           <p data-l10n-id="onboarding.tour-customize.description"></p>
         </section>
         <section class="onboarding-tour-content">
@@ -94,16 +120,23 @@ var onboardingTours = [
         </aside>
       `;
       return div;
     },
   },
   {
     id: "onboarding-tour-search",
     tourNameId: "onboarding.tour-search",
+    getNotificationStrings(bundle) {
+      return {
+        title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-search.title"),
+        message: bundle.GetStringFromName("onboarding.notification.onboarding-tour-search.message"),
+        button: bundle.GetStringFromName("onboarding.button.learnMore"),
+      };
+    },
     getPage(win) {
       let div = win.document.createElement("div");
       div.innerHTML = `
         <section class="onboarding-tour-description">
           <h1 data-l10n-id="onboarding.tour-search.title"></h1>
           <p data-l10n-id="onboarding.tour-search.description"></p>
         </section>
         <section class="onboarding-tour-content">
@@ -114,16 +147,23 @@ var onboardingTours = [
         </aside>
       `;
       return div;
     },
   },
   {
     id: "onboarding-tour-default-browser",
     tourNameId: "onboarding.tour-default-browser",
+    getNotificationStrings(bundle) {
+      return {
+        title: bundle.formatStringFromName("onboarding.notification.onboarding-tour-default-browser.title", [BRAND_SHORT_NAME], 1),
+        message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-default-browser.message", [BRAND_SHORT_NAME], 1),
+        button: bundle.GetStringFromName("onboarding.button.learnMore"),
+      };
+    },
     getPage(win) {
       let div = win.document.createElement("div");
       let defaultBrowserButtonId = win.matchMedia("(-moz-os-version: windows-win7)").matches ?
         "onboarding.tour-default-browser.win7.button" : "onboarding.tour-default-browser.button";
       div.innerHTML = `
         <section class="onboarding-tour-description">
           <h1 data-l10n-id="onboarding.tour-default-browser.title"></h1>
           <p data-l10n-id="onboarding.tour-default-browser.description"></p>
@@ -142,41 +182,61 @@ var onboardingTours = [
 
 /**
  * The script won't be initialized if we turned off onboarding by
  * setting "browser.onboarding.enabled" to false.
  */
 class Onboarding {
   constructor(contentWindow) {
     this.init(contentWindow);
-    this._bundle = Services.strings.createBundle(BUNDLE_URI);
   }
 
   async init(contentWindow) {
     this._window = contentWindow;
     this._tourItems = [];
     this._tourPages = [];
     // We want to create and append elements after CSS is loaded so
     // no flash of style changes and no additional reflow.
     await this._loadCSS();
+    this._bundle = Services.strings.createBundle(BUNDLE_URI);
+
     this._overlayIcon = this._renderOverlayIcon();
     this._overlay = this._renderOverlay();
     this._window.document.body.appendChild(this._overlayIcon);
     this._window.document.body.appendChild(this._overlay);
 
     this._loadJS(UITOUR_JS_URI);
     this._loadJS(TOUR_AGENT_JS_URI);
 
     this._overlayIcon.addEventListener("click", this);
     this._overlay.addEventListener("click", this);
     // Destroy on unload. This is to ensure we remove all the stuff we left.
     // No any leak out there.
     this._window.addEventListener("unload", () => this.destroy());
 
     this._initPrefObserver();
+    this._initNotification();
+  }
+
+  _initNotification() {
+    let doc = this._window.document;
+    if (doc.hidden) {
+      // When the preloaded-browser feature is on,
+      // it would preload an hidden about:newtab in the background.
+      // We don't wnat to show notification in that hidden state.
+      let onVisible = () => {
+        if (!doc.hidden) {
+          doc.removeEventListener("visibilitychange", onVisible);
+          this.showNotification();
+        }
+      };
+      doc.addEventListener("visibilitychange", onVisible);
+    } else {
+      this.showNotification();
+    }
   }
 
   _initPrefObserver() {
     if (this._prefsObserved) {
       return;
     }
 
     this._prefsObserved = new Map();
@@ -214,58 +274,172 @@ class Onboarding {
       case "onboarding-overlay-icon":
       case "onboarding-overlay-close-btn":
       // If the clicking target is directly on the outer-most overlay,
       // that means clicking outside the tour content area.
       // Let's toggle the overlay.
       case "onboarding-overlay":
         this.toggleOverlay();
         break;
+
+      case "onboarding-notification-close-btn":
+        this.hideNotification();
+        break;
+
+      case "onboarding-notification-action-btn":
+        let tourId = this._notificationBar.dataset.targetTourId;
+        this.toggleOverlay();
+        this.gotoPage(tourId);
+        break;
     }
     if (evt.target.classList.contains("onboarding-tour-item")) {
       this.gotoPage(evt.target.id);
     }
   }
 
   destroy() {
     this._clearPrefObserver();
     this._overlayIcon.remove();
     this._overlay.remove();
+    if (this._notificationBar) {
+      this._notificationBar.remove();
+    }
   }
 
   toggleOverlay() {
     if (this._tourItems.length == 0) {
       // Lazy loading until first toggle.
       this._loadTours(onboardingTours);
     }
 
-    this._overlay.classList.toggle("opened");
+    this.hideNotification();
+    this._overlay.classList.toggle("onboarding-opened");
+
     let hiddenCheckbox = this._window.document.getElementById("onboarding-tour-hidden-checkbox");
     if (hiddenCheckbox.checked) {
       this.hide();
-      return;
     }
-
-    this._overlay.classList.toggle("onboarding-opened");
   }
 
   gotoPage(tourId) {
     let targetPageId = `${tourId}-page`;
     for (let page of this._tourPages) {
       page.style.display = page.id != targetPageId ? "none" : "";
     }
     for (let li of this._tourItems) {
       if (li.id == tourId) {
         li.classList.add("onboarding-active");
       } else {
         li.classList.remove("onboarding-active");
       }
     }
   }
 
+  isTourCompleted(tourId) {
+    return Preferences.get(`browser.onboarding.tour.${tourId}.completed`, false);
+  }
+
+  showNotification() {
+    if (Preferences.get("browser.onboarding.notification.finished", false)) {
+      return;
+    }
+
+    // Pick out the next target tour to show
+    let targetTour = null;
+
+    // Take the last tour as the default last prompted
+    // so below would start from the 1st one if found no the last prompted from the pref.
+    let lastPromptedId = onboardingTours[onboardingTours.length - 1].id;
+    lastPromptedId = Preferences.get("browser.onboarding.notification.lastPrompted", lastPromptedId);
+
+    let lastTourIndex = onboardingTours.findIndex(tour => tour.id == lastPromptedId);
+    if (lastTourIndex < 0) {
+      // Couldn't find the tour.
+      // This could be because the pref was manually modified into unknown value
+      // or the tour version has been updated so have an new tours set.
+      // Take the last tour as the last prompted so would start from the 1st one below.
+      lastTourIndex = onboardingTours.length - 1;
+    }
+
+    // Form tours to notify into the order we want.
+    // For example, There are tour #0 ~ #5 and the #3 is the last prompted.
+    // This would form [#4, #5, #0, #1, #2, #3].
+    // So the 1st met incomplete tour in #4 ~ #2 would be the one to show.
+    // Or #3 would be the one to show if #4 ~ #2 are all completed.
+    let toursToNotify = [ ...onboardingTours.slice(lastTourIndex + 1), ...onboardingTours.slice(0, lastTourIndex + 1) ];
+    targetTour = toursToNotify.find(tour => !this.isTourCompleted(tour.id));
+
+
+    if (!targetTour) {
+      this.sendMessageToChrome("set-prefs", [{
+        name: "browser.onboarding.notification.finished",
+        value: true
+      }]);
+      return;
+    }
+
+    // Show the target tour notification
+    this._notificationBar = this._renderNotificationBar();
+    this._notificationBar.addEventListener("click", this);
+    this._window.document.body.appendChild(this._notificationBar);
+
+    this._notificationBar.dataset.targetTourId = targetTour.id;
+    let notificationStrings = targetTour.getNotificationStrings(this._bundle);
+    let actionBtn = this._notificationBar.querySelector("#onboarding-notification-action-btn");
+    actionBtn.textContent = notificationStrings.button;
+    let tourTitle = this._notificationBar.querySelector("#onboarding-notification-tour-title");
+    tourTitle.textContent = notificationStrings.title;
+    let tourMessage = this._notificationBar.querySelector("#onboarding-notification-tour-message");
+    tourMessage.textContent = notificationStrings.message;
+
+    this._notificationBar.addEventListener("transitionend", () => {
+      this._notificationBar.dataset.cssTransition = "end";
+    }, { once: true });
+    this._window.requestAnimationFrame(() => {
+      // Request the 2nd animation frame.
+      // This is to make sure the appending operation above and the css operation happen
+      // in the different layout tick so as to make sure the transition happens.
+      this._window.requestAnimationFrame(() => this._notificationBar.classList.add("onboarding-opened"));
+    });
+
+    this.sendMessageToChrome("set-prefs", [{
+      name: "browser.onboarding.notification.lastPrompted",
+      value: targetTour.id
+    }]);
+  }
+
+  hideNotification() {
+    if (this._notificationBar) {
+      this._notificationBar.classList.remove("onboarding-opened");
+      delete this._notificationBar.dataset.cssTransition;
+    }
+  }
+
+  _renderNotificationBar() {
+    let div = this._window.document.createElement("div");
+    div.id = "onboarding-notification-bar";
+    // Here we use `innerHTML` is for more friendly reading.
+    // The security should be fine because this is not from an external input.
+    div.innerHTML = `
+      <div id="onboarding-notification-icon"></div>
+      <section id="onboarding-notification-message-section">
+        <div id="onboarding-notification-tour-icon"></div>
+        <div id="onboarding-notification-body">
+          <h6 id="onboarding-notification-tour-title"></h6>
+          <span id="onboarding-notification-tour-message"></span>
+        </div>
+        <button id="onboarding-notification-action-btn"></button>
+      </section>
+      <button id="onboarding-notification-close-btn"></button>
+    `;
+    let toolTip = this._bundle.formatStringFromName("onboarding.notification-icon-tool-tip", [BRAND_SHORT_NAME], 1);
+    div.querySelector("#onboarding-notification-icon").setAttribute("data-tooltip", toolTip);
+    return div;
+  }
+
   hide() {
     this.sendMessageToChrome("set-prefs", [
       {
         name: "browser.onboarding.hidden",
         value: true
       },
       {
         name: "browser.onboarding.notification.finished",
@@ -274,17 +448,16 @@ class Onboarding {
     ]);
   }
 
   _renderOverlay() {
     let div = this._window.document.createElement("div");
     div.id = "onboarding-overlay";
     // Here we use `innerHTML` is for more friendly reading.
     // The security should be fine because this is not from an external input.
-    // We're not shipping yet so l10n strings is going to be closed for now.
     div.innerHTML = `
       <div id="onboarding-overlay-dialog">
         <span id="onboarding-overlay-close-btn"></span>
         <header id="onboarding-header"></header>
         <nav>
           <ul id="onboarding-tour-list"></ul>
         </nav>
         <footer id="onboarding-footer">
--- a/browser/extensions/onboarding/locales/en-US/onboarding.properties
+++ b/browser/extensions/onboarding/locales/en-US/onboarding.properties
@@ -1,45 +1,64 @@
 # 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/.
-# LOCALIZATION NOTE(onboarding.tour-title): This string will be used in the overlay title. %S is brandShortName
+# LOCALIZATION NOTE(onboarding.overlay-title): This string will be used in the overlay title. %S is brandShortName
 onboarding.overlay-title=Getting started with %S
+
 onboarding.tour-search=One-Click Search
 onboarding.tour-search.title=Find the needle or the haystack.
-
 # LOCALIZATION NOTE (onboarding.tour-search.description): If Amazon is not part
 # of the default searchplugins for your locale, you can replace it with another
 # ecommerce website (if you're shipping one), but not with a general purpose
 # search engine (Google, Bing, Yandex, etc.). Alternatively, only reference
 # Wikipedia and drop Amazon from the text.
 onboarding.tour-search.description=Having a default search engine doesn’t mean it’s the only one you use. Pick a search engine or a site, like Amazon or Wikipedia, to search on the fly.
 onboarding.tour-search.button=Open One-Click Search
+onboarding.notification.onboarding-tour-search.title=Find it faster.
+onboarding.notification.onboarding-tour-search.message=Access all of your favorite search engines with a click. Search the whole Web or just one website right from the search box.
+
 onboarding.tour-private-browsing=Private Browsing
 onboarding.tour-private-browsing.title=A little privacy goes a long way.
-
 # LOCALIZATION NOTE(onboarding.tour-private-browsing.description): %S is brandShortName.
 onboarding.tour-private-browsing.description=Browse the internet without saving your searches or the sites you visited. When your session ends, the cookies disappear from %S like they were never there.
 onboarding.tour-private-browsing.button=Show Private Browsing in Menu
-onboarding.hidden-checkbox-label=Hide the tour
+onboarding.notification.onboarding-tour-private-browsing.title=Browse by yourself.
+onboarding.notification.onboarding-tour-private-browsing.message=There’s no reason to share your online life with trackers every time you browse. Want to keep something to yourself? Use Private Browsing with Tracking Protection.
+
 onboarding.tour-addons=Add-ons
 onboarding.tour-addons.title=Add more functionality.
+onboarding.notification.onboarding-tour-addons.title=Get more done.
+# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-addons.message): %S is brandShortName.
+onboarding.notification.onboarding-tour-addons.message=Add-ons are small apps you can add to %S that do lots of things — from managing to-do lists, to downloading videos, to changing the look of your browser.
 
 # LOCALIZATION NOTE(onboarding.tour-addons.description): This string will be used in the add-on tour description. %1$S is brandShortName
 onboarding.tour-addons.description=Add-ons expand %1$S’s built-in features, so %1$S works the way you do. Compare prices, check the weather or express your personality with a custom theme.
 onboarding.tour-addons.button=Show Add-ons in Menu
 onboarding.tour-customize=Customize
 onboarding.tour-customize.title=Do things your way.
-
 # LOCALIZATION NOTE(onboarding.tour-customize.description): This string will be used in the customize tour description. %S is brandShortName
 onboarding.tour-customize.description=Drag, drop, and reorder %S’s toolbar and menu to fit your needs. You can even select a compact theme to give websites more room.
 onboarding.tour-customize.button=Show Customize in Menu
+onboarding.notification.onboarding-tour-customize.title=Rearrange your toolbar.
+# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-customize.message): %S is brandShortName.
+onboarding.notification.onboarding-tour-customize.message=Put the tools you use most right at your fingertips. Add more options to your toolbar. Or select a theme to make %S reflect your personality.
+
 onboarding.tour-default-browser=Default Browser
 onboarding.tour-default-browser.title=We’re there for you.
-
 # LOCALIZATION NOTE(onboarding.tour-default-browser.description): This string will be used in the default browser tour description. %1$S is brandShortName
 onboarding.tour-default-browser.description=Love %1$S? Set it as your default browser. Then when you open a link from another application, %1$S has you covered.
-
 # LOCALIZATION NOTE(onboarding.tour-default-browser.button): Label for a button to open the OS default browser settings where it's not possible to set the default browser directly. (OSX, Linux, Windows 8 and higher)
 onboarding.tour-default-browser.button=Open Default Browser Settings
-
 # LOCALIZATION NOTE(onboarding.tour-default-browser.win7.button): Label for a button to directly set the default browser (Windows 7). %S is brandShortName
 onboarding.tour-default-browser.win7.button=Make %S Your Default Browser
+# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-default-browser.title): %S is brandShortName.
+onboarding.notification.onboarding-tour-default-browser.title=Make %S your go-to browser.
+# LOCALIZATION NOTE(onboarding.notification.onboarding-tour-default-browser.message): %1$S is brandShortName
+onboarding.notification.onboarding-tour-default-browser.message=It doesn’t take much to get the most from %1$S. Just set %1$S as your default browser and put control, customization, and protection on autopilot.
+
+onboarding.hidden-checkbox-label=Hide the tour
+
+#LOCALIZATION NOTE(onboarding.button.learnMore): this string is used as a button label, displayed near the message, and shared across all the onboarding notifications.
+onboarding.button.learnMore=Learn More
+
+# LOCALIZATION NOTE(onboarding.notification-icon-tool-tip): %S is brandShortName.
+onboarding.notification-icon-tool-tip=New to %S?
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_onboarding_hide_tours.js]
+[browser_onboarding_notification.js]
--- a/browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_hide_tours.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const ABOUT_HOME_URL = "about:home";
-const ABOUT_NEWTAB_URL = "about:newtab";
-
 function assertOnboardingDestroyed(browser) {
   return ContentTask.spawn(browser, {}, function() {
     let expectedRemovals = [
       "#onboarding-overlay",
       "#onboarding-overlay-icon"
     ];
     for (let selector of expectedRemovals) {
       let removal = content.document.querySelector(selector);
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification.js
@@ -0,0 +1,109 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_show_tour_notifications_in_order() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  let tab = null;
+  let targetTourId = null;
+  let reloadPromise = null;
+  let expectedPrefUpdate = null;
+  for (let i = 0; i < tourIds.length; ++i) {
+    expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.lastPrompted", tourIds[i]);
+    if (tab) {
+      reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+      tab.linkedBrowser.reload();
+      await reloadPromise;
+    } else {
+      tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+      await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+    }
+    await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+    await promiseTourNotificationOpened(tab.linkedBrowser);
+    targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+    is(targetTourId, tourIds[i], "Should show tour notifications in order");
+    await expectedPrefUpdate;
+  }
+
+  expectedPrefUpdate = promisePrefUpdated("browser.onboarding.notification.lastPrompted", tourIds[0]);
+  reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  tab.linkedBrowser.reload();
+  await reloadPromise;
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[0], "Should loop back to the 1st tour notification after showing all notifications");
+  await expectedPrefUpdate;
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_open_target_tour_from_notification() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-notification-action-btn", {}, tab.linkedBrowser);
+  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+  let { activeNavItemId, activePageId } = await getCurrentActiveTour(tab.linkedBrowser);
+
+  is(targetTourId, activeNavItemId, "Should navigate to the target tour item.");
+  is(`${targetTourId}-page`, activePageId, "Should display the target tour page.");
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_not_show_notification_for_completed_tour() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  // Make only the last tour uncompleted
+  let lastTourId = tourIds[tourIds.length - 1];
+  for (let id of tourIds) {
+    if (id != lastTourId) {
+      setTourCompletedState(id, true);
+    }
+  }
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, lastTourId, "Should not show notification for completed tour");
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_skip_notification_for_completed_tour() {
+  resetOnboardingDefaultState();
+  await SpecialPowers.pushPrefEnv({set: [["browser.onboarding.enabled", true]]});
+
+  let tourIds = TOUR_IDs;
+  // Make only 2nd tour completed
+  await setTourCompletedState(tourIds[1], true);
+
+  // Test show notification for the 1st tour
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  await BrowserTestUtils.loadURI(tab.linkedBrowser, ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[0], "Should show notification for incompleted tour");
+
+  // Test skip the 2nd tour and show notification for the 3rd tour
+  let reloadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  tab.linkedBrowser.reload();
+  await reloadPromise;
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is(targetTourId, tourIds[2], "Should skip notification for the completed 2nd tour");
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/extensions/onboarding/test/browser/head.js
+++ b/browser/extensions/onboarding/test/browser/head.js
@@ -1,12 +1,35 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/Preferences.jsm");
+let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
+
+const ABOUT_HOME_URL = "about:home";
+const ABOUT_NEWTAB_URL = "about:newtab";
+const TOUR_IDs = [
+  "onboarding-tour-private-browsing",
+  "onboarding-tour-addons",
+  "onboarding-tour-customize",
+  "onboarding-tour-search",
+  "onboarding-tour-default-browser",
+];
+
+function resetOnboardingDefaultState() {
+  // All the prefs should be reset to the default states
+  // and no need to revert back so we don't use `SpecialPowers.pushPrefEnv` here.
+  Preferences.set("browser.onboarding.hidden", false);
+  Preferences.set("browser.onboarding.notification.finished", false);
+  Preferences.set("browser.onboarding.notification.lastPrompted", "");
+  TOUR_IDs.forEach(id => Preferences.set(`browser.onboarding.tour.${id}.completed`, false));
+}
+
+function setTourCompletedState(tourId, state) {
+  Preferences.set(`browser.onboarding.tour.${tourId}.completed`, state);
+}
 
 function promiseOnboardingOverlayLoaded(browser) {
   // The onboarding overlay is init inside window.requestIdleCallback, not immediately,
   // so we use check conditions here.
   let condition = () => {
     return ContentTask.spawn(browser, {}, function() {
       return new Promise(resolve => {
         let doc = content && content.document;
@@ -26,17 +49,17 @@ function promiseOnboardingOverlayLoaded(
   );
 }
 
 function promiseOnboardingOverlayOpened(browser) {
   let condition = () => {
     return ContentTask.spawn(browser, {}, function() {
       return new Promise(resolve => {
         let overlay = content.document.querySelector("#onboarding-overlay");
-        if (overlay.classList.contains("opened")) {
+        if (overlay.classList.contains("onboarding-opened")) {
           resolve(true);
           return;
         }
         resolve(false);
       });
     })
   };
   return BrowserTestUtils.waitForCondition(
@@ -52,8 +75,59 @@ function promisePrefUpdated(name, expect
     let onUpdate = actualValue => {
       Preferences.ignore(name, onUpdate);
       is(expectedValue, actualValue, `Should update the pref of ${name}`);
       resolve();
     };
     Preferences.observe(name, onUpdate);
   });
 }
+
+function promiseTourNotificationOpened(browser) {
+  let condition = () => {
+    return ContentTask.spawn(browser, {}, function() {
+      return new Promise(resolve => {
+        let bar = content.document.querySelector("#onboarding-notification-bar");
+        if (bar && bar.classList.contains("onboarding-opened") && bar.dataset.cssTransition == "end") {
+          resolve(true);
+          return;
+        }
+        resolve(false);
+      });
+    })
+  };
+  return BrowserTestUtils.waitForCondition(
+    condition,
+    "Should open tour notification",
+    100,
+    30
+  );
+}
+
+function getCurrentNotificationTargetTourId(browser) {
+  return ContentTask.spawn(browser, {}, function() {
+    let bar = content.document.querySelector("#onboarding-notification-bar");
+    return bar ? bar.dataset.targetTourId : null;
+  });
+}
+
+function getCurrentActiveTour(browser) {
+  return ContentTask.spawn(browser, {}, function() {
+    let list = content.document.querySelector("#onboarding-tour-list");
+    let items = list.querySelectorAll(".onboarding-tour-item");
+    let activeNavItemId = null;
+    for (let item of items) {
+      if (item.classList.contains("onboarding-active")) {
+        activeNavItemId = item.id;
+        break;
+      }
+    }
+    let activePageId = null;
+    let pages = content.document.querySelectorAll(".onboarding-tour-page");
+    for (let page of pages) {
+      if (page.style.display != "none") {
+        activePageId = page.id;
+        break;
+      }
+    }
+    return { activeNavItemId, activePageId };
+  });
+}
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -302,51 +302,16 @@ var gDevToolsBrowser = exports.gDevTools
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.focus();
     } else {
       Services.ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
     }
   },
 
-  async inspectNode(tab, nodeSelectors) {
-    let target = TargetFactory.forTab(tab);
-
-    let toolbox = await gDevTools.showToolbox(target, "inspector");
-    let inspector = toolbox.getCurrentPanel();
-
-    // new-node-front tells us when the node has been selected, whether the
-    // browser is remote or not.
-    let onNewNode = inspector.selection.once("new-node-front");
-
-    // Evaluate the cross iframes query selectors
-    async function querySelectors(nodeFront) {
-      let selector = nodeSelectors.pop();
-      if (!selector) {
-        return nodeFront;
-      }
-      nodeFront = await inspector.walker.querySelector(nodeFront, selector);
-      if (nodeSelectors.length > 0) {
-        let { nodes } = await inspector.walker.children(nodeFront);
-        // This is the NodeFront for the document node inside the iframe
-        nodeFront = nodes[0];
-      }
-      return querySelectors(nodeFront);
-    }
-    let nodeFront = await inspector.walker.getRootNode();
-    nodeFront = await querySelectors(nodeFront);
-    // Select the final node
-    inspector.selection.setNodeFront(nodeFront, "browser-context-menu");
-
-    await onNewNode;
-    // Now that the node has been selected, wait until the inspector is
-    // fully updated.
-    await inspector.once("inspector-updated");
-  },
-
   _getContentProcessTarget(processId) {
     // Create a DebuggerServer in order to connect locally to it
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.allowChromeProcess = true;
 
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -570,16 +570,63 @@ DevTools.prototype = {
    *
    * Create a BrowserToolbox process linked to the provided addon id.
    */
   initBrowserToolboxProcessForAddon: function (addonID) {
     BrowserToolboxProcess.init({ addonID });
   },
 
   /**
+   * Called from the DevToolsShim, used by nsContextMenu.js.
+   *
+   * @param {XULTab} tab
+   *        The browser tab on which inspect node was used.
+   * @param {Array} selectors
+   *        An array of CSS selectors to find the target node. Several selectors can be
+   *        needed if the element is nested in frames and not directly in the root
+   *        document.
+   * @return {Promise} a promise that resolves when the node is selected in the inspector
+   *         markup view.
+   */
+  async inspectNode(tab, nodeSelectors) {
+    let target = TargetFactory.forTab(tab);
+
+    let toolbox = await gDevTools.showToolbox(target, "inspector");
+    let inspector = toolbox.getCurrentPanel();
+
+    // new-node-front tells us when the node has been selected, whether the
+    // browser is remote or not.
+    let onNewNode = inspector.selection.once("new-node-front");
+
+    // Evaluate the cross iframes query selectors
+    async function querySelectors(nodeFront) {
+      let selector = nodeSelectors.pop();
+      if (!selector) {
+        return nodeFront;
+      }
+      nodeFront = await inspector.walker.querySelector(nodeFront, selector);
+      if (nodeSelectors.length > 0) {
+        let { nodes } = await inspector.walker.children(nodeFront);
+        // This is the NodeFront for the document node inside the iframe
+        nodeFront = nodes[0];
+      }
+      return querySelectors(nodeFront);
+    }
+    let nodeFront = await inspector.walker.getRootNode();
+    nodeFront = await querySelectors(nodeFront);
+    // Select the final node
+    inspector.selection.setNodeFront(nodeFront, "browser-context-menu");
+
+    await onNewNode;
+    // Now that the node has been selected, wait until the inspector is
+    // fully updated.
+    await inspector.once("inspector-updated");
+  },
+
+  /**
    * Either the SDK Loader has been destroyed by the add-on contribution
    * workflow, or firefox is shutting down.
 
    * @param {boolean} shuttingDown
    *        True if firefox is currently shutting down. We may prevent doing
    *        some cleanups to speed it up. Otherwise everything need to be
    *        cleaned up in order to be able to load devtools again.
    */
--- a/devtools/client/webconsole/webpack.config.js
+++ b/devtools/client/webconsole/webpack.config.js
@@ -65,16 +65,17 @@ webpackConfig.resolve = {
     "devtools/shared/l10n": path.join(__dirname, "../../shared/l10n"),
 
     "devtools/client/framework/devtools": path.join(__dirname, "../../client/shims/devtools"),
     "devtools/client/framework/menu": "devtools-modules/client/framework/menu",
     "devtools/client/framework/menu-item": path.join(__dirname, "../../client/framework/menu-item"),
 
     "devtools/client/shared/components/reps/reps": path.join(__dirname, "../../client/shared/components/reps/reps"),
     "devtools/client/shared/redux/middleware/thunk": path.join(__dirname, "../../client/shared/redux/middleware/thunk"),
+    "devtools/client/shared/redux/middleware/debounce": path.join(__dirname, "../../client/shared/redux/middleware/debounce"),
     "devtools/client/shared/components/stack-trace": path.join(__dirname, "../../client/shared/components/stack-trace"),
     "devtools/client/shared/source-utils": path.join(__dirname, "../../client/shared/source-utils"),
     "devtools/client/shared/components/frame": path.join(__dirname, "../../client/shared/components/frame"),
 
     "devtools/shared/defer": path.join(__dirname, "../../shared/defer"),
     "devtools/shared/event-emitter": "devtools-modules/shared/event-emitter",
     "devtools/shared/client/main": path.join(__dirname, "new-console-output/test/fixtures/ObjectClient"),
     "devtools/shared/platform/clipboard": path.join(__dirname, "../../shared/platform/content/clipboard"),
--- a/devtools/shim/DevToolsShim.jsm
+++ b/devtools/shim/DevToolsShim.jsm
@@ -172,16 +172,36 @@ this.DevToolsShim = {
    */
   restoreScratchpadSession: function (scratchpads) {
     if (!this.isInstalled()) {
       return;
     }
     this.gDevTools.restoreScratchpadSession(scratchpads);
   },
 
+  /**
+   * Called from nsContextMenu.js in mozilla-central when using the Inspect Element
+   * context menu item.
+   *
+   * @param {XULTab} tab
+   *        The browser tab on which inspect node was used.
+   * @param {Array} selectors
+   *        An array of CSS selectors to find the target node. Several selectors can be
+   *        needed if the element is nested in frames and not directly in the root
+   *        document.
+   * @return {Promise} a promise that resolves when the node is selected in the inspector
+   *         markup view or that resolves immediately if DevTools are not installed.
+   */
+  inspectNode: function (tab, selectors) {
+    if (!this.isInstalled()) {
+      return Promise.resolve();
+    }
+    return this.gDevTools.inspectNode(tab, selectors);
+  },
+
   _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/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -62,16 +62,17 @@
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/TextEditor.h"
 #include "mozilla/TextEvents.h"
 #include "nsNodeUtils.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsDocument.h"
 #include "nsAttrValueOrString.h"
 #include "nsAttrValueInlines.h"
 #include "nsCSSPseudoElements.h"
 #ifdef MOZ_XUL
@@ -98,17 +99,16 @@
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "nsNodeInfoManager.h"
 #include "nsICategoryManager.h"
 #include "nsIDOMDocumentType.h"
 #include "nsGenericHTMLElement.h"
-#include "nsIEditor.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIControllers.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
@@ -3937,18 +3937,18 @@ Element::InsertAdjacentElement(const nsA
 void
 Element::InsertAdjacentText(
   const nsAString& aWhere, const nsAString& aData, ErrorResult& aError)
 {
   RefPtr<nsTextNode> textNode = OwnerDoc()->CreateTextNode(aData);
   InsertAdjacent(aWhere, textNode, aError);
 }
 
-nsIEditor*
-Element::GetEditorInternal()
+TextEditor*
+Element::GetTextEditorInternal()
 {
   nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
   return textCtrl ? textCtrl->GetTextEditor() : nullptr;
 }
 
 nsresult
 Element::SetBoolAttr(nsIAtom* aAttr, bool aValue)
 {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -57,16 +57,17 @@ class nsFocusManager;
 class nsGlobalWindow;
 class nsICSSDeclaration;
 class nsISMILAttr;
 class nsDocument;
 class nsDOMStringMap;
 
 namespace mozilla {
 class DeclarationBlock;
+class TextEditor;
 namespace dom {
   struct AnimationFilter;
   struct ScrollIntoViewOptions;
   struct ScrollToOptions;
   class DOMIntersectionObserver;
   class DOMMatrixReadOnly;
   class ElementOrCSSPseudoElement;
   class UnrestrictedDoubleOrKeyframeAnimationOptions;
@@ -1260,19 +1261,19 @@ public:
    */
   static CORSMode AttrValueToCORSMode(const nsAttrValue* aValue);
 
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) final override;
 
   nsINode* GetScopeChainParent() const override;
 
   /**
-   * Locate an nsIEditor rooted at this content node, if there is one.
+   * Locate a TextEditor rooted at this content node, if there is one.
    */
-  nsIEditor* GetEditorInternal();
+  mozilla::TextEditor* GetTextEditorInternal();
 
   /**
    * Helper method for NS_IMPL_BOOL_ATTR macro.
    * Gets value of boolean attribute. Only works for attributes in null
    * namespace.
    *
    * @param aAttr    name of attribute.
    * @param aValue   Boolean value of attribute.
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -19,16 +19,17 @@
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/ServoRestyleManager.h"
+#include "mozilla/TextEditor.h"
 #include "mozilla/URLExtraData.h"
 #include "mozilla/dom/Attr.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIAtom.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "mozilla/dom/Event.h"
 #include "nsIDocumentInlines.h"
 #include "nsIDocumentEncoder.h"
@@ -88,17 +89,16 @@
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "js/GCAPI.h"
 
 #include "nsNodeInfoManager.h"
 #include "nsICategoryManager.h"
 #include "nsGenericHTMLElement.h"
-#include "nsIEditor.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIControllers.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "ChildIterator.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsRuleProcessorData.h"
@@ -2284,18 +2284,18 @@ FragmentOrElement::GetMarkup(bool aInclu
                    // Don't do linebreaking that's not present in
                    // the source
                    nsIDocumentEncoder::OutputRaw |
                    // Only check for mozdirty when necessary (bug 599983)
                    nsIDocumentEncoder::OutputIgnoreMozDirty;
 
   if (IsEditable()) {
     nsCOMPtr<Element> elem = do_QueryInterface(this);
-    nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
-    if (editor && editor->OutputsMozDirty()) {
+    TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
+    if (textEditor && textEditor->OutputsMozDirty()) {
       flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
     }
   }
 
   DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   if (aIncludeSelf) {
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -17,16 +17,17 @@
 #include "mozilla/MemoryReporting.h"
 #include "nsAttrAndChildArray.h"          // member
 #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
 #include "nsIContent.h"                   // base class
 #include "nsIWeakReference.h"             // base class
 #include "nsNodeUtils.h"                  // class member nsNodeUtils::CloneNodeImpl
 #include "nsIHTMLCollection.h"
 #include "nsDataHashtable.h"
+#include "nsXBLBinding.h"
 
 class ContentUnbinder;
 class nsContentList;
 class nsLabelsNodeList;
 class nsDOMAttributeMap;
 class nsDOMTokenList;
 class nsIControllers;
 class nsICSSDeclaration;
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -8,18 +8,18 @@
 #define mozilla_dom_shadowroot_h__
 
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "mozilla/StyleSheet.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContentInlines.h"
+#include "nsIdentifierMapEntry.h"
 #include "nsTHashtable.h"
-#include "nsDocument.h"
 
 class nsIAtom;
 class nsIContent;
 class nsXBLPrototypeBinding;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -79,16 +79,17 @@ EXPORTS += [
     'nsGkAtomList.h',
     'nsGkAtoms.h',
     'nsIAnimationObserver.h',
     'nsIAttribute.h',
     'nsIContent.h',
     'nsIContentInlines.h',
     'nsIContentIterator.h',
     'nsIContentSerializer.h',
+    'nsIdentifierMapEntry.h',
     'nsIDocument.h',
     'nsIDocumentInlines.h',
     'nsIDocumentObserver.h',
     'nsIDOMClassInfo.h',
     'nsIGlobalObject.h',
     'nsImageLoadingContent.h',
     'nsIMutationObserver.h',
     'nsINode.h',
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -14,16 +14,17 @@
 #include "nsIDocument.h"
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsWeakReference.h"
 #include "nsWeakPtr.h"
 #include "nsTArray.h"
+#include "nsIdentifierMapEntry.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsStubDocumentObserver.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIContent.h"
 #include "nsIPrincipal.h"
 #include "nsIParser.h"
 #include "nsBindingManager.h"
@@ -125,224 +126,16 @@ public:
   // request in a subdocument in different process, whereupon the caller
   // need to send some notification itself with the real origin.
   bool mShouldNotifyNewOrigin = true;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-/**
- * Right now our identifier map entries contain information for 'name'
- * and 'id' mappings of a given string. This is so that
- * nsHTMLDocument::ResolveName only has to do one hash lookup instead
- * of two. It's not clear whether this still matters for performance.
- *
- * We also store the document.all result list here. This is mainly so that
- * when all elements with the given ID are removed and we remove
- * the ID's nsIdentifierMapEntry, the document.all result is released too.
- * Perhaps the document.all results should have their own hashtable
- * in nsHTMLDocument.
- */
-class nsIdentifierMapEntry : public PLDHashEntryHdr
-{
-public:
-  struct AtomOrString
-  {
-    MOZ_IMPLICIT AtomOrString(nsIAtom* aAtom) : mAtom(aAtom) {}
-    MOZ_IMPLICIT AtomOrString(const nsAString& aString) : mString(aString) {}
-    AtomOrString(const AtomOrString& aOther)
-      : mAtom(aOther.mAtom)
-      , mString(aOther.mString)
-    {
-    }
-
-    AtomOrString(AtomOrString&& aOther)
-      : mAtom(aOther.mAtom.forget())
-      , mString(aOther.mString)
-    {
-    }
-
-    nsCOMPtr<nsIAtom> mAtom;
-    const nsString mString;
-  };
-
-  typedef const AtomOrString& KeyType;
-  typedef const AtomOrString* KeyTypePointer;
-
-  typedef mozilla::dom::Element Element;
-  typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
-
-  explicit nsIdentifierMapEntry(const AtomOrString& aKey)
-    : mKey(aKey)
-  {
-  }
-  explicit nsIdentifierMapEntry(const AtomOrString* aKey)
-    : mKey(aKey ? *aKey : nullptr)
-  {
-  }
-  nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther) :
-    mKey(mozilla::Move(aOther.GetKey())),
-    mIdContentList(mozilla::Move(aOther.mIdContentList)),
-    mNameContentList(aOther.mNameContentList.forget()),
-    mChangeCallbacks(aOther.mChangeCallbacks.forget()),
-    mImageElement(aOther.mImageElement.forget())
-  {
-  }
-  ~nsIdentifierMapEntry();
-
-  KeyType GetKey() const { return mKey; }
-
-  nsString GetKeyAsString() const
-  {
-    if (mKey.mAtom) {
-      return nsAtomString(mKey.mAtom);
-    }
-
-    return mKey.mString;
-  }
-
-  bool KeyEquals(const KeyTypePointer aOtherKey) const
-  {
-    if (mKey.mAtom) {
-      if (aOtherKey->mAtom) {
-        return mKey.mAtom == aOtherKey->mAtom;
-      }
-
-      return mKey.mAtom->Equals(aOtherKey->mString);
-    }
-
-    if (aOtherKey->mAtom) {
-      return aOtherKey->mAtom->Equals(mKey.mString);
-    }
-
-    return mKey.mString.Equals(aOtherKey->mString);
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
-
-  static PLDHashNumber HashKey(const KeyTypePointer aKey)
-  {
-    return aKey->mAtom ?
-      aKey->mAtom->hash() : mozilla::HashString(aKey->mString);
-  }
-
-  enum { ALLOW_MEMMOVE = false };
-
-  void AddNameElement(nsINode* aDocument, Element* aElement);
-  void RemoveNameElement(Element* aElement);
-  bool IsEmpty();
-  nsBaseContentList* GetNameContentList() {
-    return mNameContentList;
-  }
-  bool HasNameElement() const {
-    return mNameContentList && mNameContentList->Length() != 0;
-  }
-
-  /**
-   * Returns the element if we know the element associated with this
-   * id. Otherwise returns null.
-   */
-  Element* GetIdElement();
-  /**
-   * Returns the list of all elements associated with this id.
-   */
-  const nsTArray<Element*>& GetIdElements() const {
-    return mIdContentList;
-  }
-  /**
-   * If this entry has a non-null image element set (using SetImageElement),
-   * the image element will be returned, otherwise the same as GetIdElement().
-   */
-  Element* GetImageIdElement();
-  /**
-   * Append all the elements with this id to aElements
-   */
-  void AppendAllIdContent(nsCOMArray<nsIContent>* aElements);
-  /**
-   * This can fire ID change callbacks.
-   * @return true if the content could be added, false if we failed due
-   * to OOM.
-   */
-  bool AddIdElement(Element* aElement);
-  /**
-   * This can fire ID change callbacks.
-   */
-  void RemoveIdElement(Element* aElement);
-  /**
-   * Set the image element override for this ID. This will be returned by
-   * GetIdElement(true) if non-null.
-   */
-  void SetImageElement(Element* aElement);
-  bool HasIdElementExposedAsHTMLDocumentProperty();
-
-  bool HasContentChangeCallback() { return mChangeCallbacks != nullptr; }
-  void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
-                                void* aData, bool aForImage);
-  void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
-                                void* aData, bool aForImage);
-
-  void Traverse(nsCycleCollectionTraversalCallback* aCallback);
-
-  struct ChangeCallback {
-    nsIDocument::IDTargetObserver mCallback;
-    void* mData;
-    bool mForImage;
-  };
-
-  struct ChangeCallbackEntry : public PLDHashEntryHdr {
-    typedef const ChangeCallback KeyType;
-    typedef const ChangeCallback* KeyTypePointer;
-
-    explicit ChangeCallbackEntry(const ChangeCallback* aKey) :
-      mKey(*aKey) { }
-    ChangeCallbackEntry(const ChangeCallbackEntry& toCopy) :
-      mKey(toCopy.mKey) { }
-
-    KeyType GetKey() const { return mKey; }
-    bool KeyEquals(KeyTypePointer aKey) const {
-      return aKey->mCallback == mKey.mCallback &&
-             aKey->mData == mKey.mData &&
-             aKey->mForImage == mKey.mForImage;
-    }
-
-    static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
-    static PLDHashNumber HashKey(KeyTypePointer aKey)
-    {
-      return mozilla::HashGeneric(aKey->mCallback, aKey->mData);
-    }
-    enum { ALLOW_MEMMOVE = true };
-
-    ChangeCallback mKey;
-  };
-
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-private:
-  nsIdentifierMapEntry(const nsIdentifierMapEntry& aOther) = delete;
-  nsIdentifierMapEntry& operator=(const nsIdentifierMapEntry& aOther) = delete;
-
-  void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
-                           bool aImageOnly = false);
-
-  AtomOrString mKey;
-  // empty if there are no elements with this ID.
-  // The elements are stored as weak pointers.
-  AutoTArray<Element*, 1> mIdContentList;
-  RefPtr<nsBaseContentList> mNameContentList;
-  nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
-  RefPtr<Element> mImageElement;
-};
-
-namespace mozilla {
-namespace dom {
-
-} // namespace dom
-} // namespace mozilla
-
 class nsDocHeaderData
 {
 public:
   nsDocHeaderData(nsIAtom* aField, const nsAString& aData)
     : mField(aField), mData(aData), mNext(nullptr)
   {
   }
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/TextEditor.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsAttrValueOrString.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
@@ -225,34 +226,39 @@ static nsIContent* GetEditorRootContent(
 {
   nsCOMPtr<nsIDOMElement> rootElement;
   aEditor->GetRootElement(getter_AddRefs(rootElement));
   nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
   return rootContent;
 }
 
 nsIContent*
-nsINode::GetTextEditorRootContent(nsIEditor** aEditor)
+nsINode::GetTextEditorRootContent(TextEditor** aTextEditor)
 {
-  if (aEditor)
-    *aEditor = nullptr;
+  if (aTextEditor) {
+    *aTextEditor = nullptr;
+  }
   for (nsINode* node = this; node; node = node->GetParentNode()) {
     if (!node->IsElement() ||
         !node->IsHTMLElement())
       continue;
 
-    nsCOMPtr<nsIEditor> editor =
-      static_cast<nsGenericHTMLElement*>(node)->GetEditorInternal();
-    if (!editor)
+    RefPtr<TextEditor> textEditor =
+      static_cast<nsGenericHTMLElement*>(node)->GetTextEditorInternal();
+    if (!textEditor) {
       continue;
-
-    nsIContent* rootContent = GetEditorRootContent(editor);
-    if (aEditor)
-      editor.swap(*aEditor);
-    return rootContent;
+    }
+
+    MOZ_ASSERT(!textEditor->AsHTMLEditor(),
+               "If it were an HTML editor, needs to use GetRootElement()");
+    Element* rootElement = textEditor->GetRoot();
+    if (aTextEditor) {
+      textEditor.forget(aTextEditor);
+    }
+    return rootElement;
   }
   return nullptr;
 }
 
 nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
 {
   if (aOptions.mComposed) {
     if (IsInComposedDoc() && GetComposedDoc()) {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -36,30 +36,30 @@ class nsAttrAndChildArray;
 class nsChildContentList;
 struct nsCSSSelectorList;
 class nsDOMAttributeMap;
 class nsIAnimationObserver;
 class nsIContent;
 class nsIDocument;
 class nsIDOMElement;
 class nsIDOMNodeList;
-class nsIEditor;
 class nsIFrame;
 class nsIMutationObserver;
 class nsINode;
 class nsINodeList;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
 class nsNodeWeakReference;
 class nsDOMMutationObserver;
 
 namespace mozilla {
 class EventListenerManager;
+class TextEditor;
 namespace dom {
 /**
  * @return true if aChar is what the WHATWG defines as a 'ascii whitespace'.
  * https://infra.spec.whatwg.org/#ascii-whitespace
  */
 inline bool IsSpaceCharacter(char16_t aChar) {
   return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' ||
          aChar == '\f';
@@ -1237,17 +1237,18 @@ public:
            IsCommonAncestorForRangeInSelection();
   }
 
   /**
    * Get the root content of an editor. So, this node must be a descendant of
    * an editor. Note that this should be only used for getting input or textarea
    * editor's root content. This method doesn't support HTML editors.
    */
-  nsIContent* GetTextEditorRootContent(nsIEditor** aEditor = nullptr);
+  nsIContent* GetTextEditorRootContent(
+                mozilla::TextEditor** aTextEditor = nullptr);
 
   /**
    * Get the nearest selection root, ie. the node that will be selected if the
    * user does "Select All" while the focus is in this node. Note that if this
    * node is not in an editor, the result comes from the nsFrameSelection that
    * is related to aPresShell, so the result might not be the ancestor of this
    * node. Be aware that if this node and the computed selection limiter are
    * not in same subtree, this returns the root content of the closeset subtree.
copy from dom/base/nsDocument.h
copy to dom/base/nsIdentifierMapEntry.h
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsIdentifierMapEntry.h
@@ -3,137 +3,35 @@
 /* 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/. */
 
 /*
  * Base class for all our document implementations.
  */
 
-#ifndef nsDocument_h___
-#define nsDocument_h___
-
-#include "nsIDocument.h"
+#ifndef nsIdentifierMapEntry_h
+#define nsIdentifierMapEntry_h
 
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsCRT.h"
-#include "nsWeakReference.h"
-#include "nsWeakPtr.h"
-#include "nsTArray.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMDocumentXBL.h"
-#include "nsStubDocumentObserver.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIContent.h"
-#include "nsIPrincipal.h"
-#include "nsIParser.h"
-#include "nsBindingManager.h"
-#include "nsInterfaceHashtable.h"
-#include "nsJSThingHashtable.h"
-#include "nsIScriptObjectPrincipal.h"
-#include "nsIURI.h"
-#include "nsIRadioGroupContainer.h"
-#include "nsILayoutHistoryState.h"
-#include "nsIRequest.h"
-#include "nsILoadGroup.h"
-#include "nsTObserverArray.h"
-#include "nsStubMutationObserver.h"
-#include "nsIChannel.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsContentList.h"
-#include "nsGkAtoms.h"
-#include "nsIApplicationCache.h"
-#include "nsIApplicationCacheContainer.h"
-#include "mozilla/StyleSetHandle.h"
 #include "PLDHashTable.h"
-#include "nsAttrAndChildArray.h"
-#include "nsDOMAttributeMap.h"
-#include "nsIContentViewer.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsILoadContext.h"
-#include "nsIProgressEventSink.h"
-#include "nsISecurityEventSink.h"
-#include "nsIChannelEventSink.h"
-#include "imgIRequest.h"
-#include "mozilla/EventListenerManager.h"
-#include "mozilla/EventStates.h"
+
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/PendingAnimationTracker.h"
-#include "mozilla/dom/DOMImplementation.h"
-#include "mozilla/dom/ScriptLoader.h"
-#include "mozilla/dom/StyleSheetList.h"
-#include "nsDataHashtable.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/Attributes.h"
-#include "nsIDOMXPathEvaluator.h"
-#include "jsfriendapi.h"
-#include "mozilla/LinkedList.h"
-#include "CustomElementRegistry.h"
-#include "mozilla/dom/Performance.h"
-#include "mozilla/Maybe.h"
-
-#define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
-#define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
-#define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
-#define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
-
-
-class nsDOMStyleSheetSetList;
-class nsDocument;
-class nsIRadioVisitor;
-class nsIFormControl;
-struct nsRadioGroupStruct;
-class nsOnloadBlocker;
-class nsUnblockOnloadEvent;
-class nsDOMNavigationTiming;
-class nsWindowSizes;
-class nsHtml5TreeOpExecutor;
-class nsDocumentOnStack;
-class nsISecurityConsoleMessage;
-class nsPIBoxObject;
+#include "mozilla/Move.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/net/ReferrerPolicy.h"
 
-namespace mozilla {
-class EventChainPreVisitor;
-namespace dom {
-class BoxObject;
-class ImageTracker;
-struct LifecycleCallbacks;
-class CallbackFunction;
-class DOMIntersectionObserver;
-class Performance;
-
-struct FullscreenRequest : public LinkedListElement<FullscreenRequest>
-{
-  explicit FullscreenRequest(Element* aElement);
-  FullscreenRequest(const FullscreenRequest&) = delete;
-  ~FullscreenRequest();
-
-  Element* GetElement() const { return mElement; }
-  nsDocument* GetDocument() const { return mDocument; }
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsContentList.h"
+#include "nsIAtom.h"
+#include "nsIDocument.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
 
-private:
-  RefPtr<Element> mElement;
-  RefPtr<nsDocument> mDocument;
-
-public:
-  // This value should be true if the fullscreen request is
-  // originated from chrome code.
-  bool mIsCallerChrome = false;
-  // This value denotes whether we should trigger a NewOrigin event if
-  // requesting fullscreen in its document causes the origin which is
-  // fullscreen to change. We may want *not* to trigger that event if
-  // we're calling RequestFullScreen() as part of a continuation of a
-  // request in a subdocument in different process, whereupon the caller
-  // need to send some notification itself with the real origin.
-  bool mShouldNotifyNewOrigin = true;
-};
-
-} // namespace dom
-} // namespace mozilla
+class nsIContent;
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
  *
  * We also store the document.all result list here. This is mainly so that
@@ -327,1327 +225,9 @@ private:
   // empty if there are no elements with this ID.
   // The elements are stored as weak pointers.
   AutoTArray<Element*, 1> mIdContentList;
   RefPtr<nsBaseContentList> mNameContentList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
   RefPtr<Element> mImageElement;
 };
 
-namespace mozilla {
-namespace dom {
-
-} // namespace dom
-} // namespace mozilla
-
-class nsDocHeaderData
-{
-public:
-  nsDocHeaderData(nsIAtom* aField, const nsAString& aData)
-    : mField(aField), mData(aData), mNext(nullptr)
-  {
-  }
-
-  ~nsDocHeaderData(void)
-  {
-    delete mNext;
-  }
-
-  nsCOMPtr<nsIAtom> mField;
-  nsString          mData;
-  nsDocHeaderData*  mNext;
-};
-
-class nsDOMStyleSheetList : public mozilla::dom::StyleSheetList,
-                            public nsStubDocumentObserver
-{
-public:
-  explicit nsDOMStyleSheetList(nsIDocument* aDocument);
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // nsIDocumentObserver
-  NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
-  NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
-
-  // nsIMutationObserver
-  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
-
-  virtual nsINode* GetParentObject() const override
-  {
-    return mDocument;
-  }
-
-  uint32_t Length() override;
-  mozilla::StyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) override;
-
-protected:
-  virtual ~nsDOMStyleSheetList();
-
-  int32_t       mLength;
-  nsIDocument*  mDocument;
-};
-
-class nsOnloadBlocker final : public nsIRequest
-{
-public:
-  nsOnloadBlocker() {}
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIREQUEST
-
-private:
-  ~nsOnloadBlocker() {}
-};
-
-class nsExternalResourceMap
-{
-public:
-  typedef nsIDocument::ExternalResourceLoad ExternalResourceLoad;
-  nsExternalResourceMap();
-
-  /**
-   * Request an external resource document.  This does exactly what
-   * nsIDocument::RequestExternalResource is documented to do.
-   */
-  nsIDocument* RequestResource(nsIURI* aURI,
-                               nsINode* aRequestingNode,
-                               nsDocument* aDisplayDocument,
-                               ExternalResourceLoad** aPendingLoad);
-
-  /**
-   * Enumerate the resource documents.  See
-   * nsIDocument::EnumerateExternalResources.
-   */
-  void EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, void* aData);
-
-  /**
-   * Traverse ourselves for cycle-collection
-   */
-  void Traverse(nsCycleCollectionTraversalCallback* aCallback) const;
-
-  /**
-   * Shut ourselves down (used for cycle-collection unlink), as well
-   * as for document destruction.
-   */
-  void Shutdown()
-  {
-    mPendingLoads.Clear();
-    mMap.Clear();
-    mHaveShutDown = true;
-  }
-
-  bool HaveShutDown() const
-  {
-    return mHaveShutDown;
-  }
-
-  // Needs to be public so we can traverse them sanely
-  struct ExternalResource
-  {
-    ~ExternalResource();
-    nsCOMPtr<nsIDocument> mDocument;
-    nsCOMPtr<nsIContentViewer> mViewer;
-    nsCOMPtr<nsILoadGroup> mLoadGroup;
-  };
-
-  // Hide all our viewers
-  void HideViewers();
-
-  // Show all our viewers
-  void ShowViewers();
-
-protected:
-  class PendingLoad : public ExternalResourceLoad,
-                      public nsIStreamListener
-  {
-    ~PendingLoad() {}
-
-  public:
-    explicit PendingLoad(nsDocument* aDisplayDocument) :
-      mDisplayDocument(aDisplayDocument)
-    {}
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSISTREAMLISTENER
-    NS_DECL_NSIREQUESTOBSERVER
-
-    /**
-     * Start aURI loading.  This will perform the necessary security checks and
-     * so forth.
-     */
-    nsresult StartLoad(nsIURI* aURI, nsINode* aRequestingNode);
-
-    /**
-     * Set up an nsIContentViewer based on aRequest.  This is guaranteed to
-     * put null in *aViewer and *aLoadGroup on all failures.
-     */
-    nsresult SetupViewer(nsIRequest* aRequest, nsIContentViewer** aViewer,
-                         nsILoadGroup** aLoadGroup);
-
-  private:
-    RefPtr<nsDocument> mDisplayDocument;
-    nsCOMPtr<nsIStreamListener> mTargetListener;
-    nsCOMPtr<nsIURI> mURI;
-  };
-  friend class PendingLoad;
-
-  class LoadgroupCallbacks final : public nsIInterfaceRequestor
-  {
-    ~LoadgroupCallbacks() {}
-  public:
-    explicit LoadgroupCallbacks(nsIInterfaceRequestor* aOtherCallbacks)
-      : mCallbacks(aOtherCallbacks)
-    {}
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIINTERFACEREQUESTOR
-  private:
-    // The only reason it's safe to hold a strong ref here without leaking is
-    // that the notificationCallbacks on a loadgroup aren't the docshell itself
-    // but a shim that holds a weak reference to the docshell.
-    nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
-
-    // Use shims for interfaces that docshell implements directly so that we
-    // don't hand out references to the docshell.  The shims should all allow
-    // getInterface back on us, but other than that each one should only
-    // implement one interface.
-
-    // XXXbz I wish we could just derive the _allcaps thing from _i
-#define DECL_SHIM(_i, _allcaps)                                              \
-    class _i##Shim final : public nsIInterfaceRequestor,                     \
-                           public _i                                         \
-    {                                                                        \
-      ~_i##Shim() {}                                                         \
-    public:                                                                  \
-      _i##Shim(nsIInterfaceRequestor* aIfreq, _i* aRealPtr)                  \
-        : mIfReq(aIfreq), mRealPtr(aRealPtr)                                 \
-      {                                                                      \
-        NS_ASSERTION(mIfReq, "Expected non-null here");                      \
-        NS_ASSERTION(mRealPtr, "Expected non-null here");                    \
-      }                                                                      \
-      NS_DECL_ISUPPORTS                                                      \
-      NS_FORWARD_NSIINTERFACEREQUESTOR(mIfReq->)                             \
-      NS_FORWARD_##_allcaps(mRealPtr->)                                      \
-    private:                                                                 \
-      nsCOMPtr<nsIInterfaceRequestor> mIfReq;                                \
-      nsCOMPtr<_i> mRealPtr;                                                 \
-    };
-
-    DECL_SHIM(nsILoadContext, NSILOADCONTEXT)
-    DECL_SHIM(nsIProgressEventSink, NSIPROGRESSEVENTSINK)
-    DECL_SHIM(nsIChannelEventSink, NSICHANNELEVENTSINK)
-    DECL_SHIM(nsISecurityEventSink, NSISECURITYEVENTSINK)
-    DECL_SHIM(nsIApplicationCacheContainer, NSIAPPLICATIONCACHECONTAINER)
-#undef DECL_SHIM
-  };
-
-  /**
-   * Add an ExternalResource for aURI.  aViewer and aLoadGroup might be null
-   * when this is called if the URI didn't result in an XML document.  This
-   * function makes sure to remove the pending load for aURI, if any, from our
-   * hashtable, and to notify its observers, if any.
-   */
-  nsresult AddExternalResource(nsIURI* aURI, nsIContentViewer* aViewer,
-                               nsILoadGroup* aLoadGroup,
-                               nsIDocument* aDisplayDocument);
-
-  nsClassHashtable<nsURIHashKey, ExternalResource> mMap;
-  nsRefPtrHashtable<nsURIHashKey, PendingLoad> mPendingLoads;
-  bool mHaveShutDown;
-};
-
-// Base class for our document implementations.
-class nsDocument : public nsIDocument,
-                   public nsIDOMDocument,
-                   public nsIDOMDocumentXBL,
-                   public nsSupportsWeakReference,
-                   public nsIScriptObjectPrincipal,
-                   public nsIRadioGroupContainer,
-                   public nsIApplicationCacheContainer,
-                   public nsStubMutationObserver,
-                   public nsIObserver,
-                   public nsIDOMXPathEvaluator
-{
-  friend class nsIDocument;
-
-public:
-  typedef mozilla::dom::Element Element;
-  using nsIDocument::GetElementsByTagName;
-  typedef mozilla::net::ReferrerPolicy ReferrerPolicy;
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-
-  NS_DECL_SIZEOF_EXCLUDING_THIS
-
-  virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup) override;
-  virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
-                          nsIPrincipal* aPrincipal) override;
-
-  already_AddRefed<nsIPrincipal> MaybeDowngradePrincipal(nsIPrincipal* aPrincipal);
-
-  // StartDocumentLoad is pure virtual so that subclasses must override it.
-  // The nsDocument StartDocumentLoad does some setup, but does NOT set
-  // *aDocListener; this is the job of subclasses.
-  virtual nsresult StartDocumentLoad(const char* aCommand,
-                                     nsIChannel* aChannel,
-                                     nsILoadGroup* aLoadGroup,
-                                     nsISupports* aContainer,
-                                     nsIStreamListener **aDocListener,
-                                     bool aReset = true,
-                                     nsIContentSink* aContentSink = nullptr) override = 0;
-
-  virtual void StopDocumentLoad() override;
-
-  virtual void NotifyPossibleTitleChange(bool aBoundTitleElement) override;
-
-  virtual void SetDocumentURI(nsIURI* aURI) override;
-
-  virtual void SetChromeXHRDocURI(nsIURI* aURI) override;
-
-  virtual void SetChromeXHRDocBaseURI(nsIURI* aURI) override;
-
-  virtual void ApplySettingsFromCSP(bool aSpeculative) override;
-
-  virtual already_AddRefed<nsIParser> CreatorParserOrNull() override;
-
-  /**
-   * Set the principal responsible for this document.
-   */
-  virtual void SetPrincipal(nsIPrincipal *aPrincipal) override;
-
-  /**
-   * Get the Content-Type of this document.
-   */
-  // NS_IMETHOD GetContentType(nsAString& aContentType);
-  // Already declared in nsIDOMDocument
-
-  /**
-   * Set the Content-Type of this document.
-   */
-  virtual void SetContentType(const nsAString& aContentType) override;
-
-  virtual void SetBaseURI(nsIURI* aURI) override;
-
-  /**
-   * Get/Set the base target of a link in a document.
-   */
-  virtual void GetBaseTarget(nsAString &aBaseTarget) override;
-
-  /**
-   * Return a standard name for the document's character set. This will
-   * trigger a startDocumentLoad if necessary to answer the question.
-   */
-  virtual void SetDocumentCharacterSet(const nsACString& aCharSetID) override;
-
-  /**
-   * Add an observer that gets notified whenever the charset changes.
-   */
-  virtual nsresult AddCharSetObserver(nsIObserver* aObserver) override;
-
-  /**
-   * Remove a charset observer.
-   */
-  virtual void RemoveCharSetObserver(nsIObserver* aObserver) override;
-
-  virtual Element* AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
-                                       void* aData, bool aForImage) override;
-  virtual void RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
-                                      void* aData, bool aForImage) override;
-
-  /**
-   * Access HTTP header data (this may also get set from other sources, like
-   * HTML META tags).
-   */
-  virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const override;
-  virtual void SetHeaderData(nsIAtom* aheaderField,
-                             const nsAString& aData) override;
-
-  /**
-   * Create a new presentation shell that will use aContext for
-   * its presentation context (presentation contexts <b>must not</b> be
-   * shared among multiple presentation shells).
-   */
-  already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
-                                             nsViewManager* aViewManager,
-                                             mozilla::StyleSetHandle aStyleSet)
-    final;
-  virtual void DeleteShell() override;
-
-  virtual bool GetAllowPlugins() override;
-
-  static bool IsElementAnimateEnabled(JSContext* aCx, JSObject* aObject);
-  static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
-  virtual mozilla::dom::DocumentTimeline* Timeline() override;
-  virtual void GetAnimations(
-      nsTArray<RefPtr<mozilla::dom::Animation>>& aAnimations) override;
-  mozilla::LinkedList<mozilla::dom::DocumentTimeline>& Timelines() override
-  {
-    return mTimelines;
-  }
-
-  virtual nsresult SetSubDocumentFor(Element* aContent,
-                                     nsIDocument* aSubDoc) override;
-  virtual nsIDocument* GetSubDocumentFor(nsIContent* aContent) const override;
-  virtual Element* FindContentForSubDocument(nsIDocument *aDocument) const override;
-  virtual Element* GetRootElementInternal() const override;
-
-  virtual void EnsureOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet) override;
-
-  /**
-   * Get the (document) style sheets owned by this document.
-   * These are ordered, highest priority last
-   */
-  virtual int32_t GetNumberOfStyleSheets() const override;
-  virtual mozilla::StyleSheet* GetStyleSheetAt(int32_t aIndex) const override;
-  virtual int32_t GetIndexOfStyleSheet(
-      const mozilla::StyleSheet* aSheet) const override;
-  virtual void AddStyleSheet(mozilla::StyleSheet* aSheet) override;
-  virtual void RemoveStyleSheet(mozilla::StyleSheet* aSheet) override;
-
-  virtual void UpdateStyleSheets(
-      nsTArray<RefPtr<mozilla::StyleSheet>>& aOldSheets,
-      nsTArray<RefPtr<mozilla::StyleSheet>>& aNewSheets) override;
-  virtual void AddStyleSheetToStyleSets(mozilla::StyleSheet* aSheet);
-  virtual void RemoveStyleSheetFromStyleSets(mozilla::StyleSheet* aSheet);
-
-  virtual void InsertStyleSheetAt(mozilla::StyleSheet* aSheet,
-                                  int32_t aIndex) override;
-  virtual void SetStyleSheetApplicableState(mozilla::StyleSheet* aSheet,
-                                            bool aApplicable) override;
-
-  virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType,
-                                            nsIURI* aSheetURI) override;
-  virtual nsresult AddAdditionalStyleSheet(additionalSheetType aType,
-                                           mozilla::StyleSheet* aSheet) override;
-  virtual void RemoveAdditionalStyleSheet(additionalSheetType aType,
-                                          nsIURI* sheetURI) override;
-  virtual mozilla::StyleSheet* GetFirstAdditionalAuthorSheet() override;
-
-  virtual nsIChannel* GetChannel() const override {
-    return mChannel;
-  }
-
-  virtual nsIChannel* GetFailedChannel() const override {
-    return mFailedChannel;
-  }
-  virtual void SetFailedChannel(nsIChannel* aChannel) override {
-    mFailedChannel = aChannel;
-  }
-
-  virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject) override;
-
-  virtual void SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) override;
-
-  virtual nsIGlobalObject* GetScopeObject() const override;
-  void SetScopeObject(nsIGlobalObject* aGlobal) override;
-  /**
-   * Get the script loader for this document
-   */
-  virtual mozilla::dom::ScriptLoader* ScriptLoader() override;
-
-  /**
-   * Add/Remove an element to the document's id and name hashes
-   */
-  virtual void AddToIdTable(Element* aElement, nsIAtom* aId) override;
-  virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId) override;
-  virtual void AddToNameTable(Element* aElement, nsIAtom* aName) override;
-  virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) override;
-
-  /**
-   * Add a new observer of document change notifications. Whenever
-   * content is changed, appended, inserted or removed the observers are
-   * informed.
-   */
-  virtual void AddObserver(nsIDocumentObserver* aObserver) override;
-
-  /**
-   * Remove an observer of document change notifications. This will
-   * return false if the observer cannot be found.
-   */
-  virtual bool RemoveObserver(nsIDocumentObserver* aObserver) override;
-
-  // Observation hooks used to propagate notifications to document
-  // observers.
-  virtual void BeginUpdate(nsUpdateType aUpdateType) override;
-  virtual void EndUpdate(nsUpdateType aUpdateType) override;
-  virtual void BeginLoad() override;
-  virtual void EndLoad() override;
-
-  virtual void SetReadyStateInternal(ReadyState rs) override;
-
-  virtual void ContentStateChanged(nsIContent* aContent,
-                                   mozilla::EventStates aStateMask)
-                                     override;
-  virtual void DocumentStatesChanged(
-                 mozilla::EventStates aStateMask) override;
-
-  virtual void StyleRuleChanged(mozilla::StyleSheet* aStyleSheet,
-                                mozilla::css::Rule* aStyleRule) override;
-  virtual void StyleRuleAdded(mozilla::StyleSheet* aStyleSheet,
-                              mozilla::css::Rule* aStyleRule) override;
-  virtual void StyleRuleRemoved(mozilla::StyleSheet* aStyleSheet,
-                                mozilla::css::Rule* aStyleRule) override;
-
-  virtual void FlushPendingNotifications(mozilla::FlushType aType) override;
-  virtual void FlushExternalResources(mozilla::FlushType aType) override;
-  virtual void SetXMLDeclaration(const char16_t *aVersion,
-                                 const char16_t *aEncoding,
-                                 const int32_t aStandalone) override;
-  virtual void GetXMLDeclaration(nsAString& aVersion,
-                                 nsAString& aEncoding,
-                                 nsAString& Standalone) override;
-  virtual bool IsScriptEnabled() override;
-
-  virtual void OnPageShow(bool aPersisted, mozilla::dom::EventTarget* aDispatchStartTarget) override;
-  virtual void OnPageHide(bool aPersisted, mozilla::dom::EventTarget* aDispatchStartTarget) override;
-
-  virtual void WillDispatchMutationEvent(nsINode* aTarget) override;
-  virtual void MutationEventDispatched(nsINode* aTarget) override;
-
-  // nsINode
-  virtual bool IsNodeOfType(uint32_t aFlags) const override;
-  virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
-  virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
-  virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
-  virtual uint32_t GetChildCount() const override;
-  virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
-                                 bool aNotify) override;
-  virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
-  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
-                         bool aPreallocateChildren) const override
-  {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
-  // nsIRadioGroupContainer
-  NS_IMETHOD WalkRadioGroup(const nsAString& aName,
-                            nsIRadioVisitor* aVisitor,
-                            bool aFlushContent) override;
-  virtual void
-    SetCurrentRadioButton(const nsAString& aName,
-                          mozilla::dom::HTMLInputElement* aRadio) override;
-  virtual mozilla::dom::HTMLInputElement*
-    GetCurrentRadioButton(const nsAString& aName) override;
-  NS_IMETHOD
-    GetNextRadioButton(const nsAString& aName,
-                       const bool aPrevious,
-                       mozilla::dom::HTMLInputElement*  aFocusedRadio,
-                       mozilla::dom::HTMLInputElement** aRadioOut) override;
-  virtual void AddToRadioGroup(const nsAString& aName,
-                               nsIFormControl* aRadio) override;
-  virtual void RemoveFromRadioGroup(const nsAString& aName,
-                                    nsIFormControl* aRadio) override;
-  virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override;
-  virtual void RadioRequiredWillChange(const nsAString& aName,
-                                       bool aRequiredAdded) override;
-  virtual bool GetValueMissingState(const nsAString& aName) const override;
-  virtual void SetValueMissingState(const nsAString& aName, bool aValue) override;
-
-  // for radio group
-  nsRadioGroupStruct* GetRadioGroup(const nsAString& aName) const;
-  nsRadioGroupStruct* GetOrCreateRadioGroup(const nsAString& aName);
-
-  virtual nsViewportInfo GetViewportInfo(const mozilla::ScreenIntSize& aDisplaySize) override;
-
-  enum class UseCounterReportKind {
-    // Flush the document's use counters only; the use counters for any
-    // external resource documents will be flushed when the external
-    // resource documents themselves are destroyed.
-    eDefault,
-
-    // Flush use counters for the document and for its external resource
-    // documents. (Should only be necessary for tests, where we need
-    // flushing to happen synchronously and deterministically.)
-    eIncludeExternalResources,
-  };
-
-  void ReportUseCounters(UseCounterReportKind aKind = UseCounterReportKind::eDefault);
-
-  virtual void AddIntersectionObserver(
-    mozilla::dom::DOMIntersectionObserver* aObserver) override;
-  virtual void RemoveIntersectionObserver(
-    mozilla::dom::DOMIntersectionObserver* aObserver) override;
-  virtual void UpdateIntersectionObservations() override;
-  virtual void ScheduleIntersectionObserverNotification() override;
-  virtual void NotifyIntersectionObservers() override;
-
-  virtual void NotifyLayerManagerRecreated() override;
-
-  virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
-  virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
-  virtual void ResolveScheduledSVGPresAttrs() override;
-
-private:
-  void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
-  void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
-
-public:
-  // nsIDOMNode
-  NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE
-
-  // nsIDOMDocument
-  NS_DECL_NSIDOMDOCUMENT
-
-  // nsIDOMDocumentXBL
-  NS_DECL_NSIDOMDOCUMENTXBL
-
-  // nsIDOMEventTarget
-  virtual nsresult GetEventTargetParent(
-                     mozilla::EventChainPreVisitor& aVisitor) override;
-  virtual mozilla::EventListenerManager*
-    GetOrCreateListenerManager() override;
-  virtual mozilla::EventListenerManager*
-    GetExistingListenerManager() const override;
-
-  // nsIScriptObjectPrincipal
-  virtual nsIPrincipal* GetPrincipal() override;
-
-  // nsIApplicationCacheContainer
-  NS_DECL_NSIAPPLICATIONCACHECONTAINER
-
-  // nsIObserver
-  NS_DECL_NSIOBSERVER
-
-  NS_DECL_NSIDOMXPATHEVALUATOR
-
-  virtual nsresult Init();
-
-  virtual already_AddRefed<Element> CreateElem(const nsAString& aName,
-                                               nsIAtom* aPrefix,
-                                               int32_t aNamespaceID,
-                                               const nsAString* aIs = nullptr) override;
-
-  virtual void Sanitize() override;
-
-  virtual void EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
-                                                 void *aData) override;
-
-  virtual bool CanSavePresentation(nsIRequest *aNewRequest) override;
-  virtual void Destroy() override;
-  virtual void RemovedFromDocShell() override;
-  virtual already_AddRefed<nsILayoutHistoryState> GetLayoutHistoryState() const override;
-
-  virtual void BlockOnload() override;
-  virtual void UnblockOnload(bool aFireSync) override;
-
-  virtual void AddStyleRelevantLink(mozilla::dom::Link* aLink) override;
-  virtual void ForgetLink(mozilla::dom::Link* aLink) override;
-
-  virtual void ClearBoxObjectFor(nsIContent* aContent) override;
-
-  virtual already_AddRefed<mozilla::dom::BoxObject>
-  GetBoxObjectFor(mozilla::dom::Element* aElement,
-                  mozilla::ErrorResult& aRv) override;
-
-  virtual Element*
-    GetAnonymousElementByAttribute(nsIContent* aElement,
-                                   nsIAtom* aAttrName,
-                                   const nsAString& aAttrValue) const override;
-
-  virtual Element* ElementFromPointHelper(float aX, float aY,
-                                          bool aIgnoreRootScrollFrame,
-                                          bool aFlushLayout) override;
-
-  virtual void ElementsFromPointHelper(float aX, float aY,
-                                       uint32_t aFlags,
-                                       nsTArray<RefPtr<mozilla::dom::Element>>& aElements) override;
-
-  virtual nsresult NodesFromRectHelper(float aX, float aY,
-                                                   float aTopSize, float aRightSize,
-                                                   float aBottomSize, float aLeftSize,
-                                                   bool aIgnoreRootScrollFrame,
-                                                   bool aFlushLayout,
-                                                   nsIDOMNodeList** aReturn) override;
-
-  virtual void FlushSkinBindings() override;
-
-  virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) override;
-  virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) override;
-  virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) override;
-  virtual nsIDocument*
-    RequestExternalResource(nsIURI* aURI,
-                            nsINode* aRequestingNode,
-                            ExternalResourceLoad** aPendingLoad) override;
-  virtual void
-    EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData) override;
-
-  // Returns our (lazily-initialized) animation controller.
-  // If HasAnimationController is true, this is guaranteed to return non-null.
-  nsSMILAnimationController* GetAnimationController() override;
-
-  virtual mozilla::PendingAnimationTracker*
-  GetPendingAnimationTracker() final override
-  {
-    return mPendingAnimationTracker;
-  }
-
-  virtual mozilla::PendingAnimationTracker*
-  GetOrCreatePendingAnimationTracker() override;
-
-  virtual void SuppressEventHandling(uint32_t aIncrease) override;
-
-  virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) override;
-
-  void DecreaseEventSuppression() {
-    MOZ_ASSERT(mEventsSuppressed);
-    --mEventsSuppressed;
-    UpdateFrameRequestCallbackSchedulingState();
-  }
-
-  virtual nsIDocument* GetTemplateContentsOwner() override;
-
-  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
-                                                                   nsIDocument)
-
-  void DoNotifyPossibleTitleChange();
-
-  nsExternalResourceMap& ExternalResourceMap()
-  {
-    return mExternalResourceMap;
-  }
-
-  void SetLoadedAsData(bool aLoadedAsData) { mLoadedAsData = aLoadedAsData; }
-  void SetLoadedAsInteractiveData(bool aLoadedAsInteractiveData)
-  {
-    mLoadedAsInteractiveData = aLoadedAsInteractiveData;
-  }
-
-  nsresult CloneDocHelper(nsDocument* clone, bool aPreallocateChildren) const;
-
-  void MaybeInitializeFinalizeFrameLoaders();
-
-  void MaybeEndOutermostXBLUpdate();
-
-  virtual void PreloadPictureOpened() override;
-  virtual void PreloadPictureClosed() override;
-
-  virtual void
-    PreloadPictureImageSource(const nsAString& aSrcsetAttr,
-                              const nsAString& aSizesAttr,
-                              const nsAString& aTypeAttr,
-                              const nsAString& aMediaAttr) override;
-
-  virtual already_AddRefed<nsIURI>
-    ResolvePreloadImage(nsIURI *aBaseURI,
-                        const nsAString& aSrcAttr,
-                        const nsAString& aSrcsetAttr,
-                        const nsAString& aSizesAttr) override;
-
-  virtual void MaybePreLoadImage(nsIURI* uri,
-                                 const nsAString &aCrossOriginAttr,
-                                 ReferrerPolicy aReferrerPolicy) override;
-  virtual void ForgetImagePreload(nsIURI* aURI) override;
-
-  virtual void MaybePreconnect(nsIURI* uri,
-                               mozilla::CORSMode aCORSMode) override;
-
-  virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
-                            const nsAString& aCrossOriginAttr,
-                            ReferrerPolicy aReferrerPolicy,
-                            const nsAString& aIntegrity) override;
-
-  virtual nsresult LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
-                                       RefPtr<mozilla::StyleSheet>* aSheet) override;
-
-  virtual nsISupports* GetCurrentContentSink() override;
-
-  virtual mozilla::EventStates GetDocumentState() final;
-  // GetDocumentState() mutates the state due to lazy resolution;
-  // and can't be used during parallel traversal. Use this instead,
-  // and ensure GetDocumentState() has been called first.
-  // This will assert if the state is stale.
-  virtual mozilla::EventStates ThreadSafeGetDocumentState() const final;
-
-  // Only BlockOnload should call this!
-  void AsyncBlockOnload();
-
-  virtual void SetScrollToRef(nsIURI *aDocumentURI) override;
-  virtual void ScrollToRef() override;
-  virtual void ResetScrolledToRefAlready() override;
-  virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) override;
-
-  virtual Element *GetElementById(const nsAString& aElementId) override;
-  virtual const nsTArray<Element*>* GetAllElementsForId(const nsAString& aElementId) const override;
-
-  virtual Element *LookupImageElement(const nsAString& aElementId) override;
-  virtual void MozSetImageElement(const nsAString& aImageElementId,
-                                  Element* aElement) override;
-
-  // AddPlugin adds a plugin-related element to mPlugins when the element is
-  // added to the tree.
-  virtual nsresult AddPlugin(nsIObjectLoadingContent* aPlugin) override;
-  // RemovePlugin removes a plugin-related element to mPlugins when the
-  // element is removed from the tree.
-  virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin) override;
-  // GetPlugins returns the plugin-related elements from
-  // the frame and any subframes.
-  virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) override;
-
-  // Adds an element to mResponsiveContent when the element is
-  // added to the tree.
-  virtual nsresult AddResponsiveContent(nsIContent* aContent) override;
-  // Removes an element from mResponsiveContent when the element is
-  // removed from the tree.
-  virtual void RemoveResponsiveContent(nsIContent* aContent) override;
-  // Notifies any responsive content added by AddResponsiveContent upon media
-  // features values changing.
-  virtual void NotifyMediaFeatureValuesChanged() override;
-
-  virtual nsresult GetStateObject(nsIVariant** aResult) override;
-
-  virtual nsDOMNavigationTiming* GetNavigationTiming() const override;
-  virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming) override;
-
-  virtual Element* FindImageMap(const nsAString& aNormalizedMapName) override;
-
-  virtual nsTArray<Element*> GetFullscreenStack() const override;
-  virtual void AsyncRequestFullScreen(
-    mozilla::UniquePtr<FullscreenRequest>&& aRequest) override;
-  virtual void RestorePreviousFullScreenState() override;
-  virtual bool IsFullscreenLeaf() override;
-  virtual nsresult
-    RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement) override;
-
-  virtual nsresult RemoteFrameFullscreenReverted() override;
-  virtual nsIDocument* GetFullscreenRoot() override;
-  virtual void SetFullscreenRoot(nsIDocument* aRoot) override;
-
-  // Returns the size of the mBlockedTrackingNodes array. (nsIDocument.h)
-  //
-  // This array contains nodes that have been blocked to prevent
-  // user tracking. They most likely have had their nsIChannel
-  // canceled by the URL classifier (Safebrowsing).
-  //
-  // A script can subsequently use GetBlockedTrackingNodes()
-  // to get a list of references to these nodes.
-  //
-  // Note:
-  // This expresses how many tracking nodes have been blocked for this
-  // document since its beginning, not how many of them are still around
-  // in the DOM tree. Weak references to blocked nodes are added in the
-  // mBlockedTrackingNodesArray but they are not removed when those nodes
-  // are removed from the tree or even garbage collected.
-  long BlockedTrackingNodeCount() const;
-
-  //
-  // Returns strong references to mBlockedTrackingNodes. (nsIDocument.h)
-  //
-  // This array contains nodes that have been blocked to prevent
-  // user tracking. They most likely have had their nsIChannel
-  // canceled by the URL classifier (Safebrowsing).
-  //
-  already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
-
-  static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
-
-  // Do the "fullscreen element ready check" from the fullscreen spec.
-  // It returns true if the given element is allowed to go into fullscreen.
-  bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
-
-  // This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
-  // to move this document into full-screen mode if allowed.
-  void RequestFullScreen(mozilla::UniquePtr<FullscreenRequest>&& aRequest);
-
-  // Removes all elements from the full-screen stack, removing full-scren
-  // styles from the top element in the stack.
-  void CleanupFullscreenState();
-
-  // Pushes aElement onto the full-screen stack, and removes full-screen styles
-  // from the former full-screen stack top, and its ancestors, and applies the
-  // styles to aElement. aElement becomes the new "full-screen element".
-  bool FullScreenStackPush(Element* aElement);
-
-  // Remove the top element from the full-screen stack. Removes the full-screen
-  // styles from the former top element, and applies them to the new top
-  // element, if there is one.
-  void FullScreenStackPop();
-
-  // Returns the top element from the full-screen stack.
-  Element* FullScreenStackTop();
-
-  // DOM-exposed fullscreen API
-  bool FullscreenEnabled(mozilla::dom::CallerType aCallerType) override;
-  Element* GetFullscreenElement() override;
-
-  void RequestPointerLock(Element* aElement,
-                          mozilla::dom::CallerType aCallerType) override;
-  bool SetPointerLock(Element* aElement, int aCursorStyle);
-  static void UnlockPointer(nsIDocument* aDoc = nullptr);
-
-  void SetCurrentOrientation(mozilla::dom::OrientationType aType,
-                             uint16_t aAngle) override;
-  uint16_t CurrentOrientationAngle() const override;
-  mozilla::dom::OrientationType CurrentOrientationType() const override;
-  void SetOrientationPendingPromise(mozilla::dom::Promise* aPromise) override;
-  mozilla::dom::Promise* GetOrientationPendingPromise() const override;
-
-  // This method may fire a DOM event; if it does so it will happen
-  // synchronously.
-  void UpdateVisibilityState();
-  // Posts an event to call UpdateVisibilityState
-  virtual void PostVisibilityUpdateEvent() override;
-
-  // Since we wouldn't automatically play media from non-visited page, we need
-  // to notify window when the page was first visited.
-  void MaybeActiveMediaComponents();
-
-  virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const override;
-  // DocAddSizeOfIncludingThis is inherited from nsIDocument.
-
-  virtual nsIDOMNode* AsDOMNode() override { return this; }
-
-  // WebIDL bits
-  virtual mozilla::dom::DOMImplementation*
-    GetImplementation(mozilla::ErrorResult& rv) override;
-  virtual void
-    RegisterElement(JSContext* aCx, const nsAString& aName,
-                    const mozilla::dom::ElementRegistrationOptions& aOptions,
-                    JS::MutableHandle<JSObject*> aRetval,
-                    mozilla::ErrorResult& rv) override;
-  virtual mozilla::dom::StyleSheetList* StyleSheets() override;
-  virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) override;
-  virtual void GetLastStyleSheetSet(nsString& aSheetSet) override;
-  virtual mozilla::dom::DOMStringList* StyleSheetSets() override;
-  virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) override;
-  virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
-                                                  const mozilla::dom::ElementCreationOptionsOrString& aOptions,
-                                                  ErrorResult& rv) override;
-  virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
-                                                    const nsAString& aQualifiedName,
-                                                    const mozilla::dom::ElementCreationOptionsOrString& aOptions,
-                                                    mozilla::ErrorResult& rv) override;
-
-  virtual void UnblockDOMContentLoaded() override;
-
-protected:
-  friend class nsNodeUtils;
-  friend class nsDocumentOnStack;
-
-  void IncreaseStackRefCnt()
-  {
-    ++mStackRefCnt;
-  }
-
-  void DecreaseStackRefCnt()
-  {
-    if (--mStackRefCnt == 0 && mNeedsReleaseAfterStackRefCntRelease) {
-      mNeedsReleaseAfterStackRefCntRelease = false;
-      NS_RELEASE_THIS();
-    }
-  }
-
-  /**
-   * Check that aId is not empty and log a message to the console
-   * service if it is.
-   * @returns true if aId looks correct, false otherwise.
-   */
-  inline bool CheckGetElementByIdArg(const nsAString& aId)
-  {
-    if (aId.IsEmpty()) {
-      ReportEmptyGetElementByIdArg();
-      return false;
-    }
-    return true;
-  }
-
-  void ReportEmptyGetElementByIdArg();
-
-  void DispatchContentLoadedEvents();
-
-  void RetrieveRelevantHeaders(nsIChannel *aChannel);
-
-  void TryChannelCharset(nsIChannel *aChannel,
-                         int32_t& aCharsetSource,
-                         nsACString& aCharset,
-                         nsHtml5TreeOpExecutor* aExecutor);
-
-  // Call this before the document does something that will unbind all content.
-  // That will stop us from doing a lot of work as each element is removed.
-  void DestroyElementMaps();
-
-  // Refreshes the hrefs of all the links in the document.
-  void RefreshLinkHrefs();
-
-  nsIContent* GetFirstBaseNodeWithHref();
-  nsresult SetFirstBaseNodeWithHref(nsIContent *node);
-
-  /**
-   * Returns the title element of the document as defined by the HTML
-   * specification, or null if there isn't one.  For documents whose root
-   * element is an <svg:svg>, this is the first <svg:title> element that's a
-   * child of the root.  For other documents, it's the first HTML title element
-   * in the document.
-   */
-  Element* GetTitleElement();
-
-public:
-  // Get our title
-  virtual void GetTitle(nsString& aTitle) override;
-  // Set our title
-  virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv) override;
-
-  js::ExpandoAndGeneration mExpandoAndGeneration;
-
-  bool ContainsEMEContent();
-
-  bool ContainsMSEContent();
-
-protected:
-  void RemoveDocStyleSheetsFromStyleSets();
-  void RemoveStyleSheetsFromStyleSets(
-      const nsTArray<RefPtr<mozilla::StyleSheet>>& aSheets,
-      mozilla::SheetType aType);
-  void ResetStylesheetsToURI(nsIURI* aURI);
-  void FillStyleSet(mozilla::StyleSetHandle aStyleSet);
-
-  // Return whether all the presshells for this document are safe to flush
-  bool IsSafeToFlush() const;
-
-  void DispatchPageTransition(mozilla::dom::EventTarget* aDispatchTarget,
-                              const nsAString& aType,
-                              bool aPersisted);
-
-  virtual nsPIDOMWindowOuter* GetWindowInternal() const override;
-  virtual nsIScriptGlobalObject* GetScriptHandlingObjectInternal() const override;
-  virtual bool InternalAllowXULXBL() override;
-
-  void UpdateScreenOrientation();
-
-  virtual mozilla::dom::FlashClassification DocumentFlashClassification() override;
-  virtual bool IsThirdParty() override;
-
-#define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_)                        \
-  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, nsIDocumentObserver, \
-                                           func_, params_);
-
-#ifdef DEBUG
-  void VerifyRootContentState();
-#endif
-
-  explicit nsDocument(const char* aContentType);
-  virtual ~nsDocument();
-
-  void EnsureOnloadBlocker();
-
-  void NotifyStyleSheetApplicableStateChanged();
-
-  // Apply the fullscreen state to the document, and trigger related
-  // events. It returns false if the fullscreen element ready check
-  // fails and nothing gets changed.
-  bool ApplyFullscreen(const FullscreenRequest& aRequest);
-
-  // Retrieves the classification of the Flash plugins in the document based on
-  // the classification lists.
-  mozilla::dom::FlashClassification PrincipalFlashClassification();
-
-  // Attempts to determine the Flash classification of this page based on the
-  // the classification lists and the classification of parent documents.
-  mozilla::dom::FlashClassification ComputeFlashClassification();
-
-  nsTArray<nsIObserver*> mCharSetObservers;
-
-  PLDHashTable *mSubDocuments;
-
-  // Array of owning references to all children
-  nsAttrAndChildArray mChildren;
-
-  // Pointer to our parser if we're currently in the process of being
-  // parsed into.
-  nsCOMPtr<nsIParser> mParser;
-
-  // Weak reference to our sink for in case we no longer have a parser.  This
-  // will allow us to flush out any pending stuff from the sink even if
-  // EndLoad() has already happened.
-  nsWeakPtr mWeakSink;
-
-  nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets;
-  nsTArray<RefPtr<mozilla::StyleSheet>> mOnDemandBuiltInUASheets;
-  nsTArray<RefPtr<mozilla::StyleSheet>> mAdditionalSheets[AdditionalSheetTypeCount];
-
-  // Array of observers
-  nsTObserverArray<nsIDocumentObserver*> mObservers;
-
-  // Array of intersection observers
-  nsTHashtable<nsPtrHashKey<mozilla::dom::DOMIntersectionObserver>>
-    mIntersectionObservers;
-
-  // Tracker for animations that are waiting to start.
-  // nullptr until GetOrCreatePendingAnimationTracker is called.
-  RefPtr<mozilla::PendingAnimationTracker> mPendingAnimationTracker;
-
-  // Weak reference to the scope object (aka the script global object)
-  // that, unlike mScriptGlobalObject, is never unset once set. This
-  // is a weak reference to avoid leaks due to circular references.
-  nsWeakPtr mScopeObject;
-
-  // Stack of full-screen elements. When we request full-screen we push the
-  // full-screen element onto this stack, and when we cancel full-screen we
-  // pop one off this stack, restoring the previous full-screen state
-  nsTArray<nsWeakPtr> mFullScreenStack;
-
-  // The root of the doc tree in which this document is in. This is only
-  // non-null when this document is in fullscreen mode.
-  nsWeakPtr mFullscreenRoot;
-
-  mozilla::dom::FlashClassification mFlashClassification;
-  // Do not use this value directly. Call the |IsThirdParty()| method, which
-  // caches its result here.
-  mozilla::Maybe<bool> mIsThirdParty;
-private:
-  void UpdatePossiblyStaleDocumentState();
-  static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
-
-public:
-  virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
-    GetCustomElementRegistry() override;
-
-  // Check whether web components are enabled for the global of aObject.
-  static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
-  // Check whether web components are enabled for the global of the document
-  // this nodeinfo comes from.
-  static bool IsWebComponentsEnabled(mozilla::dom::NodeInfo* aNodeInfo);
-
-  RefPtr<mozilla::EventListenerManager> mListenerManager;
-  RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
-  RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
-  RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;
-  nsDocHeaderData* mHeaderData;
-  /* mIdentifierMap works as follows for IDs:
-   * 1) Attribute changes affect the table immediately (removing and adding
-   *    entries as needed).
-   * 2) Removals from the DOM affect the table immediately
-   * 3) Additions to the DOM always update existing entries for names, and add
-   *    new ones for IDs.
-   */
-  nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
-
-  nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
-
-  // Recorded time of change to 'loading' state.
-  mozilla::TimeStamp mLoadingTimeStamp;
-
-  // True if the document has been detached from its content viewer.
-  bool mIsGoingAway:1;
-  // True if the document is being destroyed.
-  bool mInDestructor:1;
-
-  // True if this document has ever had an HTML or SVG <title> element
-  // bound to it
-  bool mMayHaveTitleElement:1;
-
-  bool mHasWarnedAboutBoxObjects:1;
-
-  bool mDelayFrameLoaderInitialization:1;
-
-  bool mSynchronousDOMContentLoaded:1;
-
-  bool mInXBLUpdate:1;
-
-  // Parser aborted. True if the parser of this document was forcibly
-  // terminated instead of letting it finish at its own pace.
-  bool mParserAborted:1;
-
-  friend class nsCallRequestFullScreen;
-
-  // ScreenOrientation "pending promise" as described by
-  // http://www.w3.org/TR/screen-orientation/
-  RefPtr<mozilla::dom::Promise> mOrientationPendingPromise;
-
-  uint16_t mCurrentOrientationAngle;
-  mozilla::dom::OrientationType mCurrentOrientationType;
-
-  // Keeps track of whether we have a pending
-  // 'style-sheet-applicable-state-changed' notification.
-  bool mSSApplicableStateNotificationPending:1;
-
-  // Whether we have reported use counters for this document with Telemetry yet.
-  // Normally this is only done at document destruction time, but for image
-  // documents (SVG documents) that are not guaranteed to be destroyed, we
-  // report use counters when the image cache no longer has any imgRequestProxys
-  // pointing to them.  We track whether we ever reported use counters so
-  // that we only report them once for the document.
-  bool mReportedUseCounters:1;
-
-  // Whether we have filled our pres shell's style set with the document's
-  // additional sheets and sheets from the nsStyleSheetService.
-  bool mStyleSetFilled:1;
-
-  uint8_t mPendingFullscreenRequests;
-
-  uint8_t mXMLDeclarationBits;
-
-  nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject> *mBoxObjectTable;
-
-  // A document "without a browsing context" that owns the content of
-  // HTMLTemplateElement.
-  nsCOMPtr<nsIDocument> mTemplateContentsOwner;
-
-  // Our update nesting level
-  uint32_t mUpdateNestLevel;
-
-  // The application cache that this document is associated with, if
-  // any.  This can change during the lifetime of the document.
-  nsCOMPtr<nsIApplicationCache> mApplicationCache;
-
-  nsCOMPtr<nsIContent> mFirstBaseNodeWithHref;
-
-  mozilla::EventStates mDocumentState;
-  mozilla::EventStates mGotDocumentState;
-
-  RefPtr<nsDOMNavigationTiming> mTiming;
-private:
-  friend class nsUnblockOnloadEvent;
-  // Recomputes the visibility state but doesn't set the new value.
-  mozilla::dom::VisibilityState GetVisibilityState() const;
-  void NotifyStyleSheetAdded(mozilla::StyleSheet* aSheet, bool aDocumentSheet);
-  void NotifyStyleSheetRemoved(mozilla::StyleSheet* aSheet, bool aDocumentSheet);
-
-  void PostUnblockOnloadEvent();
-  void DoUnblockOnload();
-
-  nsresult InitCSP(nsIChannel* aChannel);
-
-  /**
-   * Find the (non-anonymous) content in this document for aFrame. It will
-   * be aFrame's content node if that content is in this document and not
-   * anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
-   * element containing the subdocument containing aFrame, and/or find the
-   * nearest non-anonymous ancestor in this document.
-   * Returns null if there is no such element.
-   */
-  nsIContent* GetContentInThisDocument(nsIFrame* aFrame) const;
-
-  // Just like EnableStyleSheetsForSet, but doesn't check whether
-  // aSheetSet is null and allows the caller to control whether to set
-  // aSheetSet as the preferred set in the CSSLoader.
-  void EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
-                                       bool aUpdateCSSLoader);
-
-  void ClearAllBoxObjects();
-
-  // Returns true if the scheme for the url for this document is "about"
-  bool IsAboutPage() const;
-
-  // These are not implemented and not supported.
-  nsDocument(const nsDocument& aOther);
-  nsDocument& operator=(const nsDocument& aOther);
-
-  // The layout history state that should be used by nodes in this
-  // document.  We only actually store a pointer to it when:
-  // 1)  We have no script global object.
-  // 2)  We haven't had Destroy() called on us yet.
-  nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
-
-  // Currently active onload blockers
-  uint32_t mOnloadBlockCount;
-  // Onload blockers which haven't been activated yet
-  uint32_t mAsyncOnloadBlockCount;
-  nsCOMPtr<nsIRequest> mOnloadBlocker;
-
-  // A hashtable of styled links keyed by address pointer.
-  nsTHashtable<nsPtrHashKey<mozilla::dom::Link> > mStyledLinks;
-#ifdef DEBUG
-  // Indicates whether mStyledLinks was cleared or not.  This is used to track
-  // state so we can provide useful assertions to consumers of ForgetLink and
-  // AddStyleRelevantLink.
-  bool mStyledLinksCleared;
-#endif
-
-  // A set of responsive images keyed by address pointer.
-  nsTHashtable< nsPtrHashKey<nsIContent> > mResponsiveContent;
-
-  // Member to store out last-selected stylesheet set.
-  nsString mLastStyleSheetSet;
-
-  nsTArray<RefPtr<nsFrameLoader> > mInitializableFrameLoaders;
-  nsTArray<nsCOMPtr<nsIRunnable> > mFrameLoaderFinalizers;
-  RefPtr<nsRunnableMethod<nsDocument> > mFrameLoaderRunner;
-
-  nsCOMPtr<nsIRunnable> mMaybeEndOutermostXBLUpdateRunner;
-
-  nsRevocableEventPtr<nsRunnableMethod<nsDocument, void, false> >
-    mPendingTitleChangeEvent;
-
-  nsExternalResourceMap mExternalResourceMap;
-
-  // All images in process of being preloaded.  This is a hashtable so
-  // we can remove them as the real image loads start; that way we
-  // make sure to not keep the image load going when no one cares
-  // about it anymore.
-  nsRefPtrHashtable<nsURIHashKey, imgIRequest> mPreloadingImages;
-
-  // A list of preconnects initiated by the preloader. This prevents
-  // the same uri from being used more than once, and allows the dom
-  // builder to not repeat the work of the preloader.
-  nsDataHashtable< nsURIHashKey, bool> mPreloadedPreconnects;
-
-  // Current depth of picture elements from parser
-  int32_t mPreloadPictureDepth;
-
-  // Set if we've found a URL for the current picture
-  nsString mPreloadPictureFoundSource;
-
-  RefPtr<mozilla::dom::DOMImplementation> mDOMImplementation;
-
-  RefPtr<nsContentList> mImageMaps;
-
-  nsCString mScrollToRef;
-  uint8_t mScrolledToRefAlready : 1;
-  uint8_t mChangeScrollPosWhenScrollingToRef : 1;
-
-  // Tracking for plugins in the document.
-  nsTHashtable< nsPtrHashKey<nsIObjectLoadingContent> > mPlugins;
-
-  RefPtr<mozilla::dom::DocumentTimeline> mDocumentTimeline;
-  mozilla::LinkedList<mozilla::dom::DocumentTimeline> mTimelines;
-
-  enum ViewportType {
-    DisplayWidthHeight,
-    Specified,
-    Unknown
-  };
-
-  ViewportType mViewportType;
-
-  // These member variables cache information about the viewport so we don't have to
-  // recalculate it each time.
-  bool mValidWidth, mValidHeight;
-  mozilla::LayoutDeviceToScreenScale mScaleMinFloat;
-  mozilla::LayoutDeviceToScreenScale mScaleMaxFloat;
-  mozilla::LayoutDeviceToScreenScale mScaleFloat;
-  mozilla::CSSToLayoutDeviceScale mPixelRatio;
-  bool mAutoSize, mAllowZoom, mAllowDoubleTapZoom, mValidScaleFloat, mValidMaxScale, mScaleStrEmpty, mWidthStrEmpty;
-  mozilla::CSSSize mViewportSize;
-
-  nsrefcnt mStackRefCnt;
-  bool mNeedsReleaseAfterStackRefCntRelease;
-
-  // Set to true when the document is possibly controlled by the ServiceWorker.
-  // Used to prevent multiple requests to ServiceWorkerManager.
-  bool mMaybeServiceWorkerControlled;
-
-  // We lazily calculate declaration blocks for SVG elements
-  // with mapped attributes in Servo mode. This list contains all elements which
-  // need lazy resolution
-  nsTHashtable<nsPtrHashKey<nsSVGElement>> mLazySVGPresElements;
-
-#ifdef DEBUG
-public:
-  bool mWillReparent;
-#endif
-};
-
-class nsDocumentOnStack
-{
-public:
-  explicit nsDocumentOnStack(nsDocument* aDoc) : mDoc(aDoc)
-  {
-    mDoc->IncreaseStackRefCnt();
-  }
-  ~nsDocumentOnStack()
-  {
-    mDoc->DecreaseStackRefCnt();
-  }
-private:
-  nsDocument* mDoc;
-};
-
-#endif /* nsDocument_h___ */
+#endif // #ifndef nsIdentifierMapEntry_h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -130,33 +130,33 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IM
 
   tmp->NotifyIMEOfBlur();
   tmp->UnregisterObservers();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootContent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditableNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentObserver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndOfAddedTextCache.mContainerNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode)
 
   tmp->mIMENotificationRequests = nullptr;
   tmp->mESM = nullptr;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
     mStartOfRemovingTextRangeCache.mContainerNode)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
@@ -192,17 +192,17 @@ IMEContentObserver::IMEContentObserver()
   mTextChangeData.Test();
 #endif
 }
 
 void
 IMEContentObserver::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent,
-                         nsIEditor* aEditor)
+                         EditorBase* aEditorBase)
 {
   State state = GetState();
   if (NS_WARN_IF(state == eState_Observing)) {
     return; // Nothing to do.
   }
 
   bool firstInitialization = state != eState_StoppedObserving;
   if (!firstInitialization) {
@@ -219,17 +219,17 @@ IMEContentObserver::Init(nsIWidget* aWid
   mIMENotificationRequests = &mWidget->IMENotificationRequestsRef();
 
   if (aWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
     if (!InitWithPlugin(aPresContext, aContent)) {
       Clear();
       return;
     }
   } else {
-    if (!InitWithEditor(aPresContext, aContent, aEditor)) {
+    if (!InitWithEditor(aPresContext, aContent, aEditorBase)) {
       Clear();
       return;
     }
   }
 
   if (firstInitialization) {
     // Now, try to send NOTIFY_IME_OF_FOCUS to IME via the widget.
     MaybeNotifyIMEOfFocusSet();
@@ -284,28 +284,28 @@ IMEContentObserver::OnIMEReceivedFocus()
   // Some change events may wait to notify IME because this was being
   // initialized.  It is the time to flush them.
   FlushMergeableNotifications();
 }
 
 bool
 IMEContentObserver::InitWithEditor(nsPresContext* aPresContext,
                                    nsIContent* aContent,
-                                   nsIEditor* aEditor)
+                                   EditorBase* aEditorBase)
 {
-  MOZ_ASSERT(aEditor);
+  MOZ_ASSERT(aEditorBase);
 
   mEditableNode =
     IMEStateManager::GetRootEditableNode(aPresContext, aContent);
   if (NS_WARN_IF(!mEditableNode)) {
     return false;
   }
 
-  mEditor = aEditor;
-  if (NS_WARN_IF(!mEditor)) {
+  mEditorBase = aEditorBase;
+  if (NS_WARN_IF(!mEditorBase)) {
     return false;
   }
 
   nsIPresShell* presShell = aPresContext->PresShell();
 
   // get selection and root content
   nsCOMPtr<nsISelectionController> selCon;
   if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
@@ -384,17 +384,17 @@ IMEContentObserver::InitWithPlugin(nsPre
     return false;
   }
   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                        getter_AddRefs(mSelection));
   if (NS_WARN_IF(!mSelection)) {
     return false;
   }
 
-  mEditor = nullptr;
+  mEditorBase = nullptr;
   mEditableNode = aContent;
   mRootContent = aContent;
   // Should be safe to clear mDocumentObserver here even though it *might*
   // grab this instance because this is called by Init() and the callers of
   // it and MaybeReinitialize() grabs this instance with local RefPtr.
   // So, this won't cause refcount of this instance become 0.
   mDocumentObserver = nullptr;
 
@@ -406,23 +406,23 @@ IMEContentObserver::InitWithPlugin(nsPre
   MOZ_ASSERT(WasInitializedWithPlugin());
 
   return true;
 }
 
 bool
 IMEContentObserver::WasInitializedWithPlugin() const
 {
-  return mDocShell && !mEditor;
+  return mDocShell && !mEditorBase;
 }
 
 void
 IMEContentObserver::Clear()
 {
-  mEditor = nullptr;
+  mEditorBase = nullptr;
   mSelection = nullptr;
   mEditableNode = nullptr;
   mRootContent = nullptr;
   mDocShell = nullptr;
   // Should be safe to clear mDocumentObserver here even though it grabs
   // this instance in most cases because this is called by Init() or Destroy().
   // The callers of Init() grab this instance with local RefPtr.
   // The caller of Destroy() also grabs this instance with local RefPtr.
@@ -445,18 +445,18 @@ IMEContentObserver::ObserveEditableNode(
   if (!mIMEHasFocus) {
     MOZ_ASSERT(!mWidget || mNeedsToNotifyIMEOfFocusSet ||
                mSendingNotification == NOTIFY_IME_OF_FOCUS,
                "Wow, OnIMEReceivedFocus() won't be called?");
     return;
   }
 
   mIsObserving = true;
-  if (mEditor) {
-    mEditor->AddEditorObserver(this);
+  if (mEditorBase) {
+    mEditorBase->AddEditorObserver(this);
   }
 
   if (!WasInitializedWithPlugin()) {
     // Add selection change listener only when this starts to observe
     // non-plugin content since we cannot detect selection changes in
     // plugins.
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     NS_ENSURE_TRUE_VOID(selPrivate);
@@ -524,18 +524,18 @@ IMEContentObserver::NotifyIMEOfBlur()
 void
 IMEContentObserver::UnregisterObservers()
 {
   if (!mIsObserving) {
     return;
   }
   mIsObserving = false;
 
-  if (mEditor) {
-    mEditor->RemoveEditorObserver(this);
+  if (mEditorBase) {
+    mEditorBase->RemoveEditorObserver(this);
   }
 
   if (mSelection) {
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     if (selPrivate) {
       selPrivate->RemoveSelectionListener(this);
     }
     mSelectionData.Clear();
@@ -592,24 +592,24 @@ IMEContentObserver::DisconnectFromEventS
 {
   mESM = nullptr;
 }
 
 bool
 IMEContentObserver::MaybeReinitialize(nsIWidget* aWidget,
                                       nsPresContext* aPresContext,
                                       nsIContent* aContent,
-                                      nsIEditor* aEditor)
+                                      EditorBase* aEditorBase)
 {
   if (!IsObservingContent(aPresContext, aContent)) {
     return false;
   }
 
   if (GetState() == eState_StoppedObserving) {
-    Init(aWidget, aPresContext, aContent, aEditor);
+    Init(aWidget, aPresContext, aContent, aEditorBase);
   }
   return IsManaging(aPresContext, aContent);
 }
 
 bool
 IMEContentObserver::IsManaging(nsPresContext* aPresContext,
                                nsIContent* aContent) const
 {
@@ -675,25 +675,20 @@ IMEContentObserver::IsEditorHandlingEven
 
 bool
 IMEContentObserver::IsEditorComposing() const
 {
   // Note that don't use TextComposition here. The important thing is,
   // whether the editor already started to handle composition because
   // web contents can change selection, text content and/or something from
   // compositionstart event listener which is run before EditorBase handles it.
-  if (NS_WARN_IF(!mEditor)) {
+  if (NS_WARN_IF(!mEditorBase)) {
     return false;
   }
-  bool isComposing = false;
-  nsresult rv = mEditor->GetComposing(&isComposing);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-  return isComposing;
+  return mEditorBase->IsIMEComposing();
 }
 
 nsresult
 IMEContentObserver::GetSelectionAndRoot(nsISelection** aSelection,
                                         nsIContent** aRootContent) const
 {
   if (!mEditableNode || !mSelection) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -1671,19 +1666,17 @@ IMEContentObserver::IsSafeToNotifyIME() 
 
   // If it's in reflow, we should wait to finish the reflow.
   // FYI: This should be called again from Reflow() or ReflowInterruptible().
   if (IsReflowLocked()) {
     return false;
   }
 
   // If we're in handling an edit action, this method will be called later.
-  bool isInEditAction = false;
-  if (mEditor && NS_SUCCEEDED(mEditor->GetIsInEditAction(&isInEditAction)) &&
-      isInEditAction) {
+  if (mEditorBase && mEditorBase->IsInEditAction()) {
     return false;
   }
 
   return true;
 }
 
 void
 IMEContentObserver::FlushMergeableNotifications()
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -3,20 +3,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_IMEContentObserver_h_
 #define mozilla_IMEContentObserver_h_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/EditorBase.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h" // XXX Why does only this need to be included here?
-#include "nsIEditor.h"
 #include "nsIEditorObserver.h"
 #include "nsIReflowObserver.h"
 #include "nsISelectionListener.h"
 #include "nsIScrollObserver.h"
 #include "nsIWidget.h"
 #include "nsStubDocumentObserver.h"
 #include "nsStubMutationObserver.h"
 #include "nsThreadUtils.h"
@@ -81,24 +81,24 @@ public:
    * won't be released during calling this.
    *
    * @param aWidget         The widget which can access native IME.
    * @param aPresContext    The PresContext which has aContent.
    * @param aContent        An editable element or a plugin host element which
    *                        user may use IME in.
    *                        Or nullptr if this will observe design mode
    *                        document.
-   * @param aEditor         When aContent is an editable element or nullptr,
+   * @param aEditorBase     When aContent is an editable element or nullptr,
    *                        non-nullptr referring an editor instance which
    *                        manages aContent.
    *                        Otherwise, i.e., this will observe a plugin content,
    *                        should be nullptr.
    */
   void Init(nsIWidget* aWidget, nsPresContext* aPresContext,
-            nsIContent* aContent, nsIEditor* aEditor);
+            nsIContent* aContent, EditorBase* aEditorBase);
 
   /**
    * Destroy() finalizes the instance, i.e., stops observing contents and
    * clearing the members.
    * Be aware, callers of this method need to guarantee that the instance
    * won't be released during calling this.
    */
   void Destroy();
@@ -124,29 +124,32 @@ public:
    * won't be released during calling this.
    *
    * @return            Returns true if the instance is managing the content.
    *                    Otherwise, false.
    */
   bool MaybeReinitialize(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent,
-                         nsIEditor* aEditor);
+                         EditorBase* aEditorBase);
 
   bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent) const;
   bool IsManaging(const TextComposition* aTextComposition) const;
   bool WasInitializedWithPlugin() const;
+  bool WasInitializedWith(const EditorBase& aEditorBase) const
+  {
+    return mEditorBase == &aEditorBase;
+  }
   bool IsEditorHandlingEventForComposition() const;
   bool KeepAliveDuringDeactive() const
   {
     return mIMENotificationRequests &&
            mIMENotificationRequests->WantDuringDeactive();
   }
   nsIWidget* GetWidget() const { return mWidget; }
-  nsIEditor* GetEditor() const { return mEditor; }
   void SuppressNotifyingIME();
   void UnsuppressNotifyingIME();
   nsPresContext* GetPresContext() const;
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
                                nsIContent** aRoot) const;
 
   /**
    * TryToFlushPendingNotifications() should be called when pending events
@@ -166,19 +169,19 @@ private:
   enum State {
     eState_NotObserving,
     eState_Initializing,
     eState_StoppedObserving,
     eState_Observing
   };
   State GetState() const;
   bool InitWithEditor(nsPresContext* aPresContext, nsIContent* aContent,
-                      nsIEditor* aEditor);
+                      EditorBase* aEditorBase);
   bool InitWithPlugin(nsPresContext* aPresContext, nsIContent* aContent);
-  bool IsInitializedWithPlugin() const { return !mEditor; }
+  bool IsInitializedWithPlugin() const { return !mEditorBase; }
   void OnIMEReceivedFocus();
   void Clear();
   bool IsObservingContent(nsPresContext* aPresContext,
                           nsIContent* aContent) const;
   bool IsReflowLocked() const;
   bool IsSafeToNotifyIME() const;
   bool IsEditorComposing() const;
 
@@ -302,17 +305,17 @@ private:
   // mFocusedWidget has the editor observed by the instance.  E.g., if the
   // focused editor is in XUL panel, this should be the widget of the panel.
   // On the other hand, mWidget is its parent which handles IME.
   nsCOMPtr<nsIWidget> mFocusedWidget;
   nsCOMPtr<nsISelection> mSelection;
   nsCOMPtr<nsIContent> mRootContent;
   nsCOMPtr<nsINode> mEditableNode;
   nsCOMPtr<nsIDocShell> mDocShell;
-  nsCOMPtr<nsIEditor> mEditor;
+  RefPtr<EditorBase> mEditorBase;
 
   /**
    * Helper classes to notify IME.
    */
 
   class AChangeEvent: public Runnable
   {
   protected:
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -703,22 +703,22 @@ IMEStateManager::OnClickInEditor(nsPresC
   IMEState newState = GetNewIMEState(aPresContext, aContent);
   SetIMEState(newState, aContent, widget, action);
 }
 
 // static
 void
 IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
-                                 nsIEditor* aEditor)
+                                 EditorBase& aEditorBase)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, aEditor=0x%p), "
+    ("OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, aEditorBase=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p",
-     aPresContext, aContent, aEditor, sPresContext.get(), sContent.get(),
+     aPresContext, aContent, &aEditorBase, sPresContext.get(), sContent.get(),
      sActiveIMEContentObserver.get()));
 
   if (sPresContext != aPresContext || sContent != aContent) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnFocusInEditor(), "
        "an editor not managed by ISM gets focus"));
     return;
   }
@@ -730,55 +730,55 @@ IMEStateManager::OnFocusInEditor(nsPresC
       MOZ_LOG(sISMLog, LogLevel::Debug,
         ("  OnFocusInEditor(), "
          "the editor is already being managed by sActiveIMEContentObserver"));
       return;
     }
     DestroyIMEContentObserver();
   }
 
-  CreateIMEContentObserver(aEditor);
+  CreateIMEContentObserver(&aEditorBase);
 
   // Let's flush the focus notification now.
   if (sActiveIMEContentObserver) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnFocusInEditor(), new IMEContentObserver is "
        "created, trying to flush pending notifications..."));
     sActiveIMEContentObserver->TryToFlushPendingNotifications();
   }
 }
 
 // static
 void
-IMEStateManager::OnEditorInitialized(nsIEditor* aEditor)
+IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase)
 {
   if (!sActiveIMEContentObserver ||
-      sActiveIMEContentObserver->GetEditor() != aEditor) {
+      !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
     return;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("OnEditorInitialized(aEditor=0x%p)",
-     aEditor));
+    ("OnEditorInitialized(aEditorBase=0x%p)",
+     &aEditorBase));
 
   sActiveIMEContentObserver->UnsuppressNotifyingIME();
 }
 
 // static
 void
-IMEStateManager::OnEditorDestroying(nsIEditor* aEditor)
+IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase)
 {
   if (!sActiveIMEContentObserver ||
-      sActiveIMEContentObserver->GetEditor() != aEditor) {
+      !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
     return;
   }
 
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("OnEditorDestroying(aEditor=0x%p)",
-     aEditor));
+    ("OnEditorDestroying(aEditorBase=0x%p)",
+     &aEditorBase));
 
   // The IMEContentObserver shouldn't notify IME of anything until reframing
   // is finished.
   sActiveIMEContentObserver->SuppressNotifyingIME();
 }
 
 // static
 void
@@ -1664,24 +1664,24 @@ IMEStateManager::DestroyIMEContentObserv
      "the active IMEContentObserver..."));
   RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
   sActiveIMEContentObserver = nullptr;
   tsm->Destroy();
 }
 
 // static
 void
-IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
+IMEStateManager::CreateIMEContentObserver(EditorBase* aEditorBase)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("CreateIMEContentObserver(aEditor=0x%p), "
+    ("CreateIMEContentObserver(aEditorBase=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
      "sActiveIMEContentObserver=0x%p, "
      "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
-     aEditor, sPresContext.get(), sContent.get(),
+     aEditorBase, sPresContext.get(), sContent.get(),
      sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
      sActiveIMEContentObserver.get(),
      GetBoolName(sActiveIMEContentObserver ?
        sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
 
   if (NS_WARN_IF(sActiveIMEContentObserver)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  CreateIMEContentObserver(), FAILED due to "
@@ -1720,17 +1720,17 @@ IMEStateManager::CreateIMEContentObserve
     ("  CreateIMEContentObserver() is creating an "
      "IMEContentObserver instance..."));
   sActiveIMEContentObserver = new IMEContentObserver();
 
   // IMEContentObserver::Init() might create another IMEContentObserver
   // instance.  So, sActiveIMEContentObserver would be replaced with new one.
   // We should hold the current instance here.
   RefPtr<IMEContentObserver> activeIMEContentObserver(sActiveIMEContentObserver);
-  activeIMEContentObserver->Init(widget, sPresContext, sContent, aEditor);
+  activeIMEContentObserver->Init(widget, sPresContext, sContent, aEditorBase);
 }
 
 // static
 nsresult
 IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
                                           nsIContent** aRootContent)
 {
   if (!sActiveIMEContentObserver) {
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -154,24 +154,24 @@ public:
 
   // This method is called when editor actually gets focus.
   // aContent must be:
   //   If the editor is for <input> or <textarea>, the element.
   //   If the editor is for contenteditable, the active editinghost.
   //   If the editor is for designMode, nullptr.
   static void OnFocusInEditor(nsPresContext* aPresContext,
                               nsIContent* aContent,
-                              nsIEditor* aEditor);
+                              EditorBase& aEditorBase);
 
   // This method is called when the editor is initialized.
-  static void OnEditorInitialized(nsIEditor* aEditor);
+  static void OnEditorInitialized(EditorBase& aEditorBase);
 
   // This method is called when the editor is (might be temporarily) being
   // destroyed.
-  static void OnEditorDestroying(nsIEditor* aEditor);
+  static void OnEditorDestroying(EditorBase& aEditorBase);
 
   /**
    * All composition events must be dispatched via DispatchCompositionEvent()
    * for storing the composition target and ensuring a set of composition
    * events must be fired the stored target.  If the stored composition event
    * target is destroying, this removes the stored composition automatically.
    */
   static void DispatchCompositionEvent(
@@ -251,17 +251,17 @@ protected:
                           InputContextAction aAction);
   static void SetInputContext(nsIWidget* aWidget,
                               const InputContext& aInputContext,
                               const InputContextAction& aAction);
   static IMEState GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static void EnsureTextCompositionArray();
-  static void CreateIMEContentObserver(nsIEditor* aEditor);
+  static void CreateIMEContentObserver(EditorBase* aEditorBase);
   static void DestroyIMEContentObserver();
 
   static bool IsEditable(nsINode* node);
 
   static bool IsIMEObserverNeeded(const IMEState& aState);
 
   static nsIContent* GetRootContent(nsPresContext* aPresContext);
 
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -4,20 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "IMEStateManager.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
-#include "nsIEditor.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozilla/AutoRestore.h"
+#include "mozilla/EditorBase.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/TabParent.h"
@@ -668,48 +668,49 @@ TextComposition::OnEditorDestroyed()
 void
 TextComposition::EditorDidHandleCompositionChangeEvent()
 {
   mString = mLastData;
   mIsEditorHandlingEvent = false;
 }
 
 void
-TextComposition::StartHandlingComposition(nsIEditor* aEditor)
+TextComposition::StartHandlingComposition(EditorBase* aEditorBase)
 {
   MOZ_RELEASE_ASSERT(!mTabParent);
 
   MOZ_ASSERT(!HasEditor(), "There is a handling editor already");
-  mEditorWeak = do_GetWeakReference(aEditor);
+  mEditorBaseWeak = do_GetWeakReference(static_cast<nsIEditor*>(aEditorBase));
 }
 
 void
-TextComposition::EndHandlingComposition(nsIEditor* aEditor)
+TextComposition::EndHandlingComposition(EditorBase* aEditorBase)
 {
   MOZ_RELEASE_ASSERT(!mTabParent);
 
 #ifdef DEBUG
-  nsCOMPtr<nsIEditor> editor = GetEditor();
-  MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?");
+  RefPtr<EditorBase> editorBase = GetEditorBase();
+  MOZ_ASSERT(editorBase == aEditorBase,
+             "Another editor handled the composition?");
 #endif // #ifdef DEBUG
-  mEditorWeak = nullptr;
+  mEditorBaseWeak = nullptr;
 }
 
-already_AddRefed<nsIEditor>
-TextComposition::GetEditor() const
+already_AddRefed<EditorBase>
+TextComposition::GetEditorBase() const
 {
-  nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorWeak);
-  return editor.forget();
+  nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorBaseWeak);
+  RefPtr<EditorBase> editorBase = static_cast<EditorBase*>(editor.get());
+  return editorBase.forget();
 }
 
 bool
 TextComposition::HasEditor() const
 {
-  nsCOMPtr<nsIEditor> editor = GetEditor();
-  return !!editor;
+  return mEditorBaseWeak && mEditorBaseWeak->IsAlive();
 }
 
 /******************************************************************************
  * TextComposition::CompositionEventDispatcher
  ******************************************************************************/
 
 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
                                                TextComposition* aComposition,
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -14,20 +14,19 @@
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsPresContext.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextRange.h"
 #include "mozilla/dom/TabParent.h"
 
-class nsIEditor;
-
 namespace mozilla {
 
+class EditorBase;
 class EventDispatchingCallback;
 class IMEStateManager;
 
 /**
  * TextComposition represents a text composition.  This class stores the
  * composition event target and its presContext.  At dispatching the event via
  * this class, the instances use the stored event target.
  */
@@ -132,18 +131,18 @@ public:
   {
     return mIsEditorHandlingEvent;
   }
 
   /**
    * StartHandlingComposition() and EndHandlingComposition() are called by
    * editor when it holds a TextComposition instance and release it.
    */
-  void StartHandlingComposition(nsIEditor* aEditor);
-  void EndHandlingComposition(nsIEditor* aEditor);
+  void StartHandlingComposition(EditorBase* aEditorBase);
+  void EndHandlingComposition(EditorBase* aEditorBase);
 
   /**
    * OnEditorDestroyed() is called when the editor is destroyed but there is
    * active composition.
    */
   void OnEditorDestroyed();
 
   /**
@@ -198,18 +197,19 @@ private:
   // Same as mRange, but mRange will have old data during compositionupdate.
   // So this will be valied during compositionupdate.
   RefPtr<TextRangeArray> mLastRanges;
 
   // mNativeContext stores a opaque pointer.  This works as the "ID" for this
   // composition.  Don't access the instance, it may not be available.
   widget::NativeIMEContext mNativeContext;
 
-  // mEditorWeak is a weak reference to the focused editor handling composition.
-  nsWeakPtr mEditorWeak;
+  // mEditorBaseWeak is a weak reference to the focused editor handling
+  // composition.
+  nsWeakPtr mEditorBaseWeak;
 
   // mLastData stores the data attribute of the latest composition event (except
   // the compositionstart event).
   nsString mLastData;
 
   // mString stores the composition text which has been handled by the focused
   // editor.
   nsString mString;
@@ -278,23 +278,23 @@ private:
     , mWasNativeCompositionEndEventDiscarded(false)
     , mAllowControlCharacters(false)
     , mWasCompositionStringEmpty(true)
     , mHasDispatchedCompositionEvents(false)
   {}
   TextComposition(const TextComposition& aOther);
 
   /**
-   * GetEditor() returns nsIEditor pointer of mEditorWeak.
+   * GetEditorBase() returns EditorBase pointer of mEditorBaseWeak.
    */
-  already_AddRefed<nsIEditor> GetEditor() const;
+  already_AddRefed<EditorBase> GetEditorBase() const;
 
   /**
-   * HasEditor() returns true if mEditorWeak holds nsIEditor instance which is
-   * alive.  Otherwise, false.
+   * HasEditor() returns true if mEditorBaseWeak holds EditorBase instance
+   * which is alive.  Otherwise, false.
    */
   bool HasEditor() const;
 
   /**
    * EditorWillHandleCompositionChangeEvent() must be called before the focused
    * editor handles the compositionchange event.
    */
   void EditorWillHandleCompositionChangeEvent(
--- a/dom/html/HTMLBodyElement.cpp
+++ b/dom/html/HTMLBodyElement.cpp
@@ -2,16 +2,17 @@
 /* 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 "HTMLBodyElement.h"
 #include "mozilla/dom/HTMLBodyElementBinding.h"
 #include "mozilla/GenericSpecifiedValuesInlines.h"
+#include "mozilla/TextEditor.h"
 #include "nsAttrValueInlines.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsIEditor.h"
@@ -389,19 +390,19 @@ HTMLBodyElement::IsAttributeMapped(const
   };
 
   return FindAttributeDependence(aAttribute, map);
 }
 
 already_AddRefed<nsIEditor>
 HTMLBodyElement::GetAssociatedEditor()
 {
-  nsCOMPtr<nsIEditor> editor = GetEditorInternal();
-  if (editor) {
-    return editor.forget();
+  RefPtr<TextEditor> textEditor = GetTextEditorInternal();
+  if (textEditor) {
+    return textEditor.forget();
   }
 
   // Make sure this is the actual body of the document
   if (!IsCurrentBodyElement()) {
     return nullptr;
   }
 
   // For designmode, try to get document's editor
@@ -410,16 +411,17 @@ HTMLBodyElement::GetAssociatedEditor()
     return nullptr;
   }
 
   nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
   if (!docShell) {
     return nullptr;
   }
 
+  nsCOMPtr<nsIEditor> editor;
   docShell->GetEditor(getter_AddRefs(editor));
   return editor.forget();
 }
 
 bool
 HTMLBodyElement::IsEventAttributeName(nsIAtom *aName)
 {
   return nsContentUtils::IsEventAttributeName(aName,
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -65,16 +65,17 @@
 #include "nsVariant.h"
 
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/GenericSpecifiedValuesInlines.h"
 #include "mozilla/InternalMutationEvent.h"
+#include "mozilla/TextEditor.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 
 #include <algorithm>
 
 // input type=radio
 #include "nsIRadioGroupContainer.h"
 
@@ -2613,27 +2614,33 @@ HTMLInputElement::SetUserInput(const nsA
   }
 
   return NS_OK;
 }
 
 nsIEditor*
 HTMLInputElement::GetEditor()
 {
+  return GetTextEditorFromState();
+}
+
+TextEditor*
+HTMLInputElement::GetTextEditorFromState()
+{
   nsTextEditorState* state = GetEditorState();
   if (state) {
-    return state->GetEditor();
+    return state->GetTextEditor();
   }
   return nullptr;
 }
 
-NS_IMETHODIMP_(nsIEditor*)
+NS_IMETHODIMP_(TextEditor*)
 HTMLInputElement::GetTextEditor()
 {
-  return GetEditor();
+  return GetTextEditorFromState();
 }
 
 NS_IMETHODIMP_(nsISelectionController*)
 HTMLInputElement::GetSelectionController()
 {
   nsTextEditorState* state = GetEditorState();
   if (state) {
     return state->GetSelectionController();
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -170,17 +170,19 @@ public:
   virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
 
   // nsIDOMHTMLInputElement
   NS_DECL_NSIDOMHTMLINPUTELEMENT
 
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) override
   {
-    return nsGenericHTMLElement::GetEditor(aEditor);
+    nsCOMPtr<nsIEditor> editor = GetEditor();
+    editor.forget(aEditor);
+    return NS_OK;
   }
 
   NS_IMETHOD SetUserInput(const nsAString& aInput) override;
 
   // Overriden nsIFormControl methods
   NS_IMETHOD Reset() override;
   NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override;
   NS_IMETHOD SaveState() override;
@@ -237,17 +239,17 @@ public:
   NS_IMETHOD_(bool) IsPlainTextControl() const override;
   NS_IMETHOD_(bool) IsPasswordTextControl() const override;
   NS_IMETHOD_(int32_t) GetCols() override;
   NS_IMETHOD_(int32_t) GetWrapCols() override;
   NS_IMETHOD_(int32_t) GetRows() override;
   NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue) override;
   NS_IMETHOD_(bool) ValueChanged() const override;
   NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
-  NS_IMETHOD_(nsIEditor*) GetTextEditor() override;
+  NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
   NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD CreateEditor() override;
   NS_IMETHOD_(Element*) GetRootEditorNode() override;
   NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
   NS_IMETHOD_(Element*) GetPlaceholderNode() override;
@@ -868,16 +870,19 @@ public:
 
   bool NumberSpinnerDownButtonIsDepressed() const
   {
     return mNumberControlSpinnerIsSpinning && !mNumberControlSpinnerSpinsUp;
   }
 
   bool MozIsTextField(bool aExcludePassword);
 
+  /**
+   * GetEditor() is for webidl bindings.
+   */
   nsIEditor* GetEditor();
 
   void SetUserInput(const nsAString& aInput,
                     nsIPrincipal& aSubjectPrincipal);
 
   /**
    * If aValue contains a valid floating-point number in the format specified
    * by the HTML 5 spec:
@@ -1117,16 +1122,18 @@ protected:
   /**
    * Returns if autocomplete attribute applies for the current type.
    */
   bool DoesAutocompleteApply() const;
 
   void FreeData();
   nsTextEditorState *GetEditorState() const;
 
+  mozilla::TextEditor* GetTextEditorFromState();
+
   /**
    * Manages the internal data storage across type changes.
    */
   void HandleTypeChange(uint8_t aNewType, bool aNotify);
 
   /**
    * Sanitize the value of the element depending of its current type.
    * See: http://www.whatwg.org/specs/web-apps/current-work/#value-sanitization-algorithm
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -254,20 +254,20 @@ HTMLTextAreaElement::GetValue(nsAString&
 }
 
 void
 HTMLTextAreaElement::GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const
 {
   mState.GetValue(aValue, aIgnoreWrap);
 }
 
-NS_IMETHODIMP_(nsIEditor*)
+NS_IMETHODIMP_(TextEditor*)
 HTMLTextAreaElement::GetTextEditor()
 {
-  return GetEditor();
+  return mState.GetTextEditor();
 }
 
 NS_IMETHODIMP_(nsISelectionController*)
 HTMLTextAreaElement::GetSelectionController()
 {
   return mState.GetSelectionController();
 }
 
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -15,16 +15,17 @@
 #include "nsCOMPtr.h"
 #include "nsGenericHTMLElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsIConstraintValidation.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/HTMLInputElementBinding.h"
 #include "nsGkAtoms.h"
 
+#include "mozilla/TextEditor.h"
 #include "nsTextEditorState.h"
 
 class nsIControllers;
 class nsIDocument;
 class nsPresContext;
 class nsPresState;
 
 namespace mozilla {
@@ -62,17 +63,19 @@ public:
   }
 
   // nsIDOMHTMLTextAreaElement
   NS_DECL_NSIDOMHTMLTEXTAREAELEMENT
 
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) override
   {
-    return nsGenericHTMLElement::GetEditor(aEditor);
+    nsCOMPtr<nsIEditor> editor = GetEditor();
+    editor.forget(aEditor);
+    return NS_OK;
   }
   NS_IMETHOD SetUserInput(const nsAString& aInput) override;
 
   // nsIFormControl
   NS_IMETHOD Reset() override;
   NS_IMETHOD SubmitNamesValues(HTMLFormSubmission* aFormSubmission) override;
   NS_IMETHOD SaveState() override;
   virtual bool RestoreState(nsPresState* aState) override;
@@ -89,17 +92,17 @@ public:
   NS_IMETHOD_(bool) IsPlainTextControl() const override;
   NS_IMETHOD_(bool) IsPasswordTextControl() const override;
   NS_IMETHOD_(int32_t) GetCols() override;
   NS_IMETHOD_(int32_t) GetWrapCols() override;
   NS_IMETHOD_(int32_t) GetRows() override;
   NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue) override;
   NS_IMETHOD_(bool) ValueChanged() const override;
   NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
-  NS_IMETHOD_(nsIEditor*) GetTextEditor() override;
+  NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() override;
   NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD CreateEditor() override;
   NS_IMETHOD_(Element*) GetRootEditorNode() override;
   NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
   NS_IMETHOD_(Element*) GetPlaceholderNode() override;
@@ -296,17 +299,17 @@ public:
   Nullable<uint32_t> GetSelectionEnd(ErrorResult& aError);
   void SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd, ErrorResult& aError);
   void GetSelectionDirection(nsAString& aDirection, ErrorResult& aError);
   void SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError);
   void SetSelectionRange(uint32_t aSelectionStart, uint32_t aSelectionEnd, const Optional<nsAString>& aDirecton, ErrorResult& aError);
   nsIControllers* GetControllers(ErrorResult& aError);
   nsIEditor* GetEditor()
   {
-    return mState.GetEditor();
+    return mState.GetTextEditor();
   }
 
 protected:
   virtual ~HTMLTextAreaElement() {}
 
   // get rid of the compiler warning
   using nsGenericHTMLFormElementWithState::IsSingleLineTextControl;
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -6,18 +6,19 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/GenericSpecifiedValuesInlines.h"
+#include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
-#include "mozilla/Likely.h"
+#include "mozilla/TextEditor.h"
 
 #include "nscore.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsQueryObject.h"
 #include "nsIContentInlines.h"
@@ -1872,23 +1873,25 @@ nsGenericHTMLFormElement::GetForm(nsIDOM
   NS_ENSURE_ARG_POINTER(aForm);
   NS_IF_ADDREF(*aForm = mForm);
   return NS_OK;
 }
 
 nsIContent::IMEState
 nsGenericHTMLFormElement::GetDesiredIMEState()
 {
-  nsIEditor* editor = GetEditorInternal();
-  if (!editor)
+  TextEditor* textEditor = GetTextEditorInternal();
+  if (!textEditor) {
     return nsGenericHTMLElement::GetDesiredIMEState();
+  }
   IMEState state;
-  nsresult rv = editor->GetPreferredIMEState(&state);
-  if (NS_FAILED(rv))
+  nsresult rv = textEditor->GetPreferredIMEState(&state);
+  if (NS_FAILED(rv)) {
     return nsGenericHTMLElement::GetDesiredIMEState();
+  }
   return state;
 }
 
 nsresult
 nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument,
                                      nsIContent* aParent,
                                      nsIContent* aBindingParent,
                                      bool aCompileEventHandlers)
@@ -2601,30 +2604,23 @@ nsGenericHTMLElement::DispatchSimulatedC
 {
   WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr,
                          WidgetMouseEvent::eReal);
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
   event.mFlags.mIsPositionless = true;
   return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event);
 }
 
-nsresult
-nsGenericHTMLElement::GetEditor(nsIEditor** aEditor)
-{
-  NS_IF_ADDREF(*aEditor = GetEditorInternal());
-  return NS_OK;
-}
-
 already_AddRefed<nsIEditor>
 nsGenericHTMLElement::GetAssociatedEditor()
 {
   // If contenteditable is ever implemented, it might need to do something different here?
 
-  nsCOMPtr<nsIEditor> editor = GetEditorInternal();
-  return editor.forget();
+  RefPtr<TextEditor> textEditor = GetTextEditorInternal();
+  return textEditor.forget();
 }
 
 bool
 nsGenericHTMLElement::IsCurrentBodyElement()
 {
   // TODO Bug 698498: Should this handle the case where GetBody returns a
   //                  frameset?
   if (!IsHTMLElement(nsGkAtoms::body)) {
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -32,16 +32,17 @@ class nsPresState;
 struct nsSize;
 
 namespace mozilla {
 class EventChainPostVisitor;
 class EventChainPreVisitor;
 class EventChainVisitor;
 class EventListenerManager;
 class EventStates;
+class TextEditor;
 namespace dom {
 class HTMLFormElement;
 class HTMLMenuElement;
 } // namespace dom
 } // namespace mozilla
 
 typedef nsMappedAttributeElement nsGenericHTMLElementBase;
 
@@ -808,21 +809,16 @@ public:
 
   /**
    * See if the document being tested has nav-quirks mode enabled.
    * @param doc the document
    */
   static bool InNavQuirksMode(nsIDocument* aDoc);
 
   /**
-   * Locate an nsIEditor rooted at this content node, if there is one.
-   */
-  nsresult GetEditor(nsIEditor** aEditor);
-
-  /**
    * Helper method for NS_IMPL_URI_ATTR macro.
    * Gets the absolute URI value of an attribute, by resolving any relative
    * URIs in the attribute against the baseuri of the element. If the attribute
    * isn't a relative URI the value of the attribute is returned as is. Only
    * works for attributes in null namespace.
    *
    * @param aAttr      name of attribute.
    * @param aBaseAttr  name of base attribute.
--- a/dom/html/nsITextControlElement.h
+++ b/dom/html/nsITextControlElement.h
@@ -6,24 +6,24 @@
 
 #ifndef nsITextControlElement_h___
 #define nsITextControlElement_h___
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 class nsIContent;
 class nsAString;
-class nsIEditor;
 class nsISelectionController;
 class nsFrameSelection;
 class nsTextControlFrame;
 
 namespace mozilla {
 
 class ErrorResult;
+class TextEditor;
 
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 // IID for the nsITextControl interface
 #define NS_ITEXTCONTROLELEMENT_IID    \
@@ -104,17 +104,17 @@ public:
    */
   NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const = 0;
 
   /**
    * Get the editor object associated with the text editor.
    * The return value is null if the control does not support an editor
    * (for example, if it is a checkbox.)
    */
-  NS_IMETHOD_(nsIEditor*) GetTextEditor() = 0;
+  NS_IMETHOD_(mozilla::TextEditor*) GetTextEditor() = 0;
 
   /**
    * Get the selection controller object associated with the text editor.
    * The return value is null if the control does not support an editor
    * (for example, if it is a checkbox.)
    */
   NS_IMETHOD_(nsISelectionController*) GetSelectionController() = 0;
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -8,17 +8,16 @@
 
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsView.h"
 #include "nsCaret.h"
 #include "nsEditorCID.h"
 #include "nsLayoutCID.h"
 #include "nsITextControlFrame.h" 
-#include "nsIPlaintextEditor.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsTextControlFrame.h"
 #include "nsIControllers.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsITransactionManager.h"
@@ -28,17 +27,16 @@
 #include "nsGenericHTMLElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEditorObserver.h"
 #include "nsIWidget.h"
 #include "nsIDocumentEncoder.h"
 #include "nsISelectionPrivate.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
-#include "nsIEditor.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/TextEditRules.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsTextNode.h"
 #include "nsIController.h"
 #include "mozilla/AutoRestore.h"
@@ -55,37 +53,36 @@ using namespace mozilla;
 using namespace mozilla::dom;
 using mozilla::layers::ScrollInputMethod;
 
 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
 
 class MOZ_STACK_CLASS ValueSetter
 {
 public:
-  explicit ValueSetter(nsIEditor* aEditor)
-    : mEditor(aEditor)
-  {
-    MOZ_ASSERT(aEditor);
-  
+  explicit ValueSetter(TextEditor* aTextEditor)
+    : mTextEditor(aTextEditor)
     // To protect against a reentrant call to SetValue, we check whether
     // another SetValue is already happening for this editor.  If it is,
     // we must wait until we unwind to re-enable oninput events.
-    mEditor->GetSuppressDispatchingInputEvent(&mOuterTransaction);
+    , mOuterTransaction(aTextEditor->IsSuppressingDispatchingInputEvent())
+  {
+    MOZ_ASSERT(aTextEditor);
   }
   ~ValueSetter()
   {
-    mEditor->SetSuppressDispatchingInputEvent(mOuterTransaction);
+    mTextEditor->SetSuppressDispatchingInputEvent(mOuterTransaction);
   }
   void Init()
   {
-    mEditor->SetSuppressDispatchingInputEvent(true);
+    mTextEditor->SetSuppressDispatchingInputEvent(true);
   }
 
 private:
-  nsCOMPtr<nsIEditor> mEditor;
+  RefPtr<TextEditor> mTextEditor;
   bool mOuterTransaction;
 };
 
 class RestoreSelectionState : public Runnable {
 public:
   RestoreSelectionState(nsTextEditorState *aState, nsTextControlFrame *aFrame)
     : mFrame(aFrame),
       mTextEditorState(aState)
@@ -132,77 +129,72 @@ public:
 private:
   nsTextControlFrame* mFrame;
   nsTextEditorState* mTextEditorState;
 };
 
 class MOZ_RAII AutoRestoreEditorState final
 {
 public:
-  explicit AutoRestoreEditorState(nsIEditor* aEditor,
-                                  nsIPlaintextEditor* aPlaintextEditor
+  explicit AutoRestoreEditorState(TextEditor* aTextEditor
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditor(aEditor)
-    , mPlaintextEditor(aPlaintextEditor)
+    : mTextEditor(aTextEditor)
+    , mSavedFlags(mTextEditor->Flags())
+    , mSavedMaxLength(mTextEditor->MaxTextLength())
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    MOZ_ASSERT(mEditor);
-    MOZ_ASSERT(mPlaintextEditor);
-
-    uint32_t flags;
-    mEditor->GetFlags(&mSavedFlags);
-    flags = mSavedFlags;
+    MOZ_ASSERT(mTextEditor);
+
+    uint32_t flags = mSavedFlags;
     flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
     flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
     flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
-    mEditor->SetFlags(flags);
-
-    mPlaintextEditor->GetMaxTextLength(&mSavedMaxLength);
-    mPlaintextEditor->SetMaxTextLength(-1);
+    mTextEditor->SetFlags(flags);
+
+    mTextEditor->SetMaxTextLength(-1);
   }
 
   ~AutoRestoreEditorState()
   {
-     mPlaintextEditor->SetMaxTextLength(mSavedMaxLength);
-     mEditor->SetFlags(mSavedFlags);
+     mTextEditor->SetMaxTextLength(mSavedMaxLength);
+     mTextEditor->SetFlags(mSavedFlags);
   }
 
 private:
-  nsIEditor* mEditor;
-  nsIPlaintextEditor* mPlaintextEditor;
+  TextEditor* mTextEditor;
   uint32_t mSavedFlags;
   int32_t mSavedMaxLength;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class MOZ_RAII AutoDisableUndo final
 {
 public:
-  explicit AutoDisableUndo(nsIEditor* aEditor
+  explicit AutoDisableUndo(TextEditor* aTextEditor
                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditor(aEditor)
+    : mTextEditor(aTextEditor)
     , mPreviousEnabled(true)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    MOZ_ASSERT(mEditor);
+    MOZ_ASSERT(mTextEditor);
 
     bool canUndo;
-    DebugOnly<nsresult> rv = mEditor->CanUndo(&mPreviousEnabled, &canUndo);
+    DebugOnly<nsresult> rv = mTextEditor->CanUndo(&mPreviousEnabled, &canUndo);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-    mEditor->EnableUndo(false);
+    mTextEditor->EnableUndo(false);
   }
 
   ~AutoDisableUndo()
   {
-    mEditor->EnableUndo(mPreviousEnabled);
+    mTextEditor->EnableUndo(mPreviousEnabled);
   }
 
 private:
-  nsIEditor* mEditor;
+  TextEditor* mTextEditor;
   bool mPreviousEnabled;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*static*/
 bool
 nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
   nsITextControlElement::nsHTMLTextWrap& aWrapProp)
@@ -1057,24 +1049,21 @@ nsTextInputListener::EditAction()
   AutoWeakFrame weakFrame = mFrame;
 
   nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
   nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
   NS_ASSERTION(frame, "Where is our frame?");
   //
   // Update the undo / redo menus
   //
-  nsCOMPtr<nsIEditor> editor;
-  frame->GetEditor(getter_AddRefs(editor));
+  RefPtr<TextEditor> textEditor = frame->GetTextEditor();
 
   // Get the number of undo / redo items
-  int32_t numUndoItems = 0;
-  int32_t numRedoItems = 0;
-  editor->GetNumberOfUndoItems(&numUndoItems);
-  editor->GetNumberOfRedoItems(&numRedoItems);
+  int32_t numUndoItems = textEditor->NumberOfUndoItems();
+  int32_t numRedoItems = textEditor->NumberOfRedoItems();
   if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
       (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
     // Modify the menu if undo or redo items are different
     UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
 
     mHadUndoItems = numUndoItems != 0;
     mHadRedoItems = numRedoItems != 0;
   }
@@ -1188,61 +1177,61 @@ void
 nsTextEditorState::Clear()
 {
   if (mBoundFrame) {
     // Oops, we still have a frame!
     // This should happen when the type of a text input control is being changed
     // to something which is not a text control.  In this case, we should pretend
     // that a frame is being destroyed, and clean up after ourselves properly.
     UnbindFromFrame(mBoundFrame);
-    mEditor = nullptr;
+    mTextEditor = nullptr;
   } else {
     // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
     // for us.
     DestroyEditor();
   }
   mTextListener = nullptr;
 }
 
 void nsTextEditorState::Unlink()
 {
   nsTextEditorState* tmp = this;
   tmp->Clear();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelCon)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextEditor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderDiv)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreviewDiv)
 }
 
 void nsTextEditorState::Traverse(nsCycleCollectionTraversalCallback& cb)
 {
   nsTextEditorState* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelCon)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextEditor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderDiv)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreviewDiv)
 }
 
 nsFrameSelection*
 nsTextEditorState::GetConstFrameSelection() {
   if (mSelCon)
     return mSelCon->GetConstFrameSelection();
   return nullptr;
 }
 
-nsIEditor*
-nsTextEditorState::GetEditor()
+TextEditor*
+nsTextEditorState::GetTextEditor()
 {
-  if (!mEditor) {
+  if (!mTextEditor) {
     nsresult rv = PrepareEditor();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
-  return mEditor;
+  return mTextEditor;
 }
 
 nsISelectionController*
 nsTextEditorState::GetSelectionController() const
 {
   return mSelCon;
 }
 
@@ -1290,17 +1279,17 @@ nsTextEditorState::BindToFrame(nsTextCon
   NS_ENSURE_ARG_POINTER(aFrame);
 
   NS_ASSERTION(!mBoundFrame, "Cannot bind twice, need to unbind first");
   NS_ENSURE_TRUE(!mBoundFrame, NS_ERROR_FAILURE);
 
   // If we'll need to transfer our current value to the editor, save it before
   // binding to the frame.
   nsAutoString currentValue;
-  if (mEditor) {
+  if (mTextEditor) {
     GetValue(currentValue, true);
   }
 
   mBoundFrame = aFrame;
 
   nsresult rv = CreateRootNode();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1337,57 +1326,51 @@ nsTextEditorState::BindToFrame(nsTextCon
       }
     }
 
     selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
                                              (mTextListener));
   }
 
   // If an editor exists from before, prepare it for usage
-  if (mEditor) {
+  if (mTextEditor) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
 
     // Set the correct direction on the newly created root node
-    uint32_t flags;
-    nsresult rv = mEditor->GetFlags(&flags);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (flags & nsIPlaintextEditor::eEditorRightToLeft) {
+    if (mTextEditor->IsRightToLeft()) {
       rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), false);
-    } else if (flags & nsIPlaintextEditor::eEditorLeftToRight) {
+    } else if (mTextEditor->IsLeftToRight()) {
       rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), false);
     } else {
       // otherwise, inherit the content node's direction
     }
 
     nsContentUtils::AddScriptRunner(
       new PrepareEditorEvent(*this, content, currentValue));
   }
 
   return NS_OK;
 }
 
 struct PreDestroyer
 {
-  void Init(nsIEditor* aEditor)
-  {
-    mNewEditor = aEditor;
-  }
+  void Init(TextEditor* aTextEditor) { mTextEditor = aTextEditor; }
   ~PreDestroyer()
   {
-    if (mNewEditor) {
-      mNewEditor->PreDestroy(true);
+    if (mTextEditor) {
+      mTextEditor->PreDestroy(true);
     }
   }
-  void Swap(nsCOMPtr<nsIEditor>& aEditor)
+  void Swap(RefPtr<TextEditor>& aTextEditor)
   {
-    return mNewEditor.swap(aEditor);
+    return mTextEditor.swap(aTextEditor);
   }
 private:
-  nsCOMPtr<nsIEditor> mNewEditor;
+  RefPtr<TextEditor> mTextEditor;
 };
 
 nsresult
 nsTextEditorState::PrepareEditor(const nsAString *aValue)
 {
   if (!mBoundFrame) {
     // Cannot create an editor without a bound frame.
     // Don't return a failure code, because js callers can't handle that.
@@ -1402,19 +1385,19 @@ nsTextEditorState::PrepareEditor(const n
   AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
 
   // Don't attempt to initialize recursively!
   InitializationGuard guard(*this);
   if (guard.IsInitializingRecursively()) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  // Note that we don't check mEditor here, because we might already have one
-  // around, in which case we don't create a new one, and we'll just tie the
-  // required machinery to it.
+  // Note that we don't check mTextEditor here, because we might already have
+  // one around, in which case we don't create a new one, and we'll just tie
+  // the required machinery to it.
 
   nsPresContext *presContext = mBoundFrame->PresContext();
   nsIPresShell *shell = presContext->GetPresShell();
 
   // Setup the editor flags
   uint32_t editorFlags = 0;
   if (IsPlainTextControl())
     editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
@@ -1426,49 +1409,46 @@ nsTextEditorState::PrepareEditor(const n
   // All nsTextControlFrames are widgets
   editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
 
   // Spell check is diabled at creation time. It is enabled once
   // the editor comes into focus.
   editorFlags |= nsIPlaintextEditor::eEditorSkipSpellCheck;
 
   bool shouldInitializeEditor = false;
-  nsCOMPtr<nsIEditor> newEditor; // the editor that we might create
+  RefPtr<TextEditor> newTextEditor; // the editor that we might create
   nsresult rv = NS_OK;
   PreDestroyer preDestroyer;
-  if (!mEditor) {
+  if (!mTextEditor) {
     shouldInitializeEditor = true;
 
     // Create an editor
-    newEditor = do_CreateInstance(kTextEditorCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    preDestroyer.Init(newEditor);
+    newTextEditor = new TextEditor();
+    preDestroyer.Init(newTextEditor);
 
     // Make sure we clear out the non-breaking space before we initialize the editor
     rv = mBoundFrame->UpdateValueDisplay(true, true);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     if (aValue || !mEditorInitialized) {
       // Set the correct value in the root node
       rv = mBoundFrame->UpdateValueDisplay(true, !mEditorInitialized, aValue);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    newEditor = mEditor; // just pretend that we have a new editor!
+    newTextEditor = mTextEditor; // just pretend that we have a new editor!
 
     // Don't lose application flags in the process.
-    uint32_t originalFlags = 0;
-    newEditor->GetFlags(&originalFlags);
-    if (originalFlags & nsIPlaintextEditor::eEditorMailMask) {
+    if (newTextEditor->IsMailEditor()) {
       editorFlags |= nsIPlaintextEditor::eEditorMailMask;
     }
   }
 
   // Get the current value of the textfield from the content.
-  // Note that if we've created a new editor, mEditor is null at this stage,
+  // Note that if we've created a new editor, mTextEditor is null at this stage,
   // so we will get the real value from the content.
   nsAutoString defaultValue;
   if (aValue) {
     defaultValue = *aValue;
   } else {
     GetValue(defaultValue, true);
   }
 
@@ -1487,18 +1467,18 @@ nsTextEditorState::PrepareEditor(const n
     // for its content manipulations, and it causes it to fail some security
     // checks deep inside when initializing. So we explictly make it clear that
     // we're native code.
     // Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     AutoNoJSAPI nojsapi;
 
-    rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
-                         defaultValue);
+    rv = newTextEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
+                             defaultValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Initialize the controller for the editor
 
   if (!SuppressEventHandlers(presContext)) {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
@@ -1523,110 +1503,111 @@ nsTextEditorState::PrepareEditor(const n
       rv = controllers->GetControllerCount(&numControllers);
       for (uint32_t i = 0; i < numControllers; i ++) {
         nsCOMPtr<nsIController> controller;
         rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
         if (NS_SUCCEEDED(rv) && controller) {
           nsCOMPtr<nsIControllerContext> editController =
             do_QueryInterface(controller);
           if (editController) {
-            editController->SetCommandContext(newEditor);
+            editController->SetCommandContext(
+              static_cast<nsIEditor*>(newTextEditor));
             found = true;
           }
         }
       }
       if (!found)
         rv = NS_ERROR_FAILURE;
     }
   }
 
   // Initialize the plaintext editor
-  nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(newEditor);
-  if (textEditor) {
-    if (shouldInitializeEditor) {
-      // Set up wrapping
-      textEditor->SetWrapColumn(GetWrapCols());
-    }
-
-    // Set max text field length
-    textEditor->SetMaxTextLength(GetMaxLength());
+  if (shouldInitializeEditor) {
+    // Set up wrapping
+    newTextEditor->SetWrapColumn(GetWrapCols());
   }
 
+  // Set max text field length
+  newTextEditor->SetMaxTextLength(GetMaxLength());
+
   nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
   if (content) {
-    rv = newEditor->GetFlags(&editorFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    editorFlags = newTextEditor->Flags();
 
     // Check if the readonly attribute is set.
     if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
       editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
 
     // Check if the disabled attribute is set.
     // TODO: call IsDisabled() here!
     if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) 
       editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
 
     // Disable the selection if necessary.
     if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
       mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
 
-    newEditor->SetFlags(editorFlags);
+    newTextEditor->SetFlags(editorFlags);
   }
 
   if (shouldInitializeEditor) {
     // Hold on to the newly created editor
-    preDestroyer.Swap(mEditor);
+    preDestroyer.Swap(mTextEditor);
   }
 
   // If we have a default value, insert it under the div we created
   // above, but be sure to use the editor so that '*' characters get
   // displayed for password fields, etc. SetValue() will call the
   // editor for us.
 
   if (!defaultValue.IsEmpty()) {
-    rv = newEditor->SetFlags(editorFlags);
+    rv = newTextEditor->SetFlags(editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
     // So, we use eSetValue_Internal flag only that it will turn off undo.
 
     bool success = SetValue(defaultValue, eSetValue_Internal);
     NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     // Now restore the original editor flags.
-    rv = newEditor->SetFlags(editorFlags);
+    rv = newTextEditor->SetFlags(editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsITransactionManager> transMgr;
-  newEditor->GetTransactionManager(getter_AddRefs(transMgr));
-  NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
-
-  transMgr->SetMaxTransactionCount(nsITextControlElement::DEFAULT_UNDO_CAP);
+  nsCOMPtr<nsITransactionManager> transactionManager =
+    newTextEditor->GetTransactionManager();
+  if (NS_WARN_IF(!transactionManager)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  transactionManager->SetMaxTransactionCount(
+                        nsITextControlElement::DEFAULT_UNDO_CAP);
 
   if (IsPasswordTextControl()) {
     // Disable undo for password textfields.  Note that we want to do this at
     // the very end of InitEditor, so the calls to EnableUndo when setting the
     // default value don't screw us up.
     // Since changing the control type does a reframe, we don't have to worry
     // about dynamic type changes here.
-    newEditor->EnableUndo(false);
+    newTextEditor->EnableUndo(false);
   }
 
   if (!mEditorInitialized) {
-    newEditor->PostCreate();
+    newTextEditor->PostCreate();
     mEverInited = true;
     mEditorInitialized = true;
   }
 
-  if (mTextListener)
-    newEditor->AddEditorObserver(mTextListener);
+  if (mTextListener) {
+    newTextEditor->AddEditorObserver(mTextListener);
+  }
 
   // Restore our selection after being bound to a new frame
   HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
   if (number ? number->IsSelectionCached() : mSelectionCached) {
     if (mRestoringSelection) // paranoia
       mRestoringSelection->Revoke();
     mRestoringSelection = new RestoreSelectionState(this, mBoundFrame);
     if (mRestoringSelection) {
@@ -2085,20 +2066,20 @@ nsTextEditorState::GetParentNumberContro
   return nullptr;
 }
 
 void
 nsTextEditorState::DestroyEditor()
 {
   // notify the editor that we are going away
   if (mEditorInitialized) {
-    if (mTextListener)
-      mEditor->RemoveEditorObserver(mTextListener);
-
-    mEditor->PreDestroy(true);
+    if (mTextListener) {
+      mTextEditor->RemoveEditorObserver(mTextListener);
+    }
+    mTextEditor->PreDestroy(true);
     mEditorInitialized = false;
   }
   ClearValueCache();
 }
 
 void
 nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
 {
@@ -2106,20 +2087,18 @@ nsTextEditorState::UnbindFromFrame(nsTex
 
   // If it was, however, it should be unbounded from the same frame.
   NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
   NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
 
   // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
   // called yet, we need to notify it here because editor may be destroyed
   // before EditAction() is called if selection listener causes flushing layout.
-  bool isInEditAction = false;
-  if (mTextListener && mEditor && mEditorInitialized &&
-      NS_SUCCEEDED(mEditor->GetIsInEditAction(&isInEditAction)) &&
-      isInEditAction) {
+  if (mTextListener && mTextEditor && mEditorInitialized &&
+      mTextEditor->IsInEditAction()) {
     mTextListener->EditAction();
   }
 
   // We need to start storing the value outside of the editor if we're not
   // going to use it anymore, so retrieve it for now.
   nsAutoString value;
   GetValue(value, true);
 
@@ -2426,17 +2405,18 @@ nsTextEditorState::GetValue(nsAString& a
   // IME, GetValue() may be called for appending text or something.  Then, we
   // need to return the latest aValue of SetValue() since the value hasn't
   // been set to the editor yet.
   if (mIsCommittingComposition) {
     aValue = mValueBeingSet;
     return;
   }
 
-  if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
+  if (mTextEditor && mBoundFrame &&
+      (mEditorInitialized || !IsSingleLineTextControl())) {
     bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
     if (canCache && !mCachedValue.IsEmpty()) {
       aValue = mCachedValue;
       return;
     }
 
     aValue.Truncate(); // initialize out param
 
@@ -2469,18 +2449,18 @@ nsTextEditorState::GetValue(nsAString& a
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     // XXXbz if we could just get the textContent of our anonymous content (eg
     // if plaintext editor didn't create <br> nodes all over), we wouldn't need
     // this.
     { /* Scope for AutoNoJSAPI. */
       AutoNoJSAPI nojsapi;
 
-      mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
-                              aValue);
+      mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
+                                  aValue);
     }
     if (canCache) {
       mCachedValue = aValue;
     } else {
       mCachedValue.Truncate();
     }
   } else {
     if (!mTextCtrlElement->ValueChanged() || !mValue) {
@@ -2558,17 +2538,18 @@ nsTextEditorState::SetValue(const nsAStr
       //       forcibly committing composition.
       if (nsContentUtils::IsSafeToRunScript()) {
         WeakPtr<nsTextEditorState> self(this);
         // WARNING: During this call, compositionupdate, compositionend, input
         // events will be fired.  Therefore, everything can occur.  E.g., the
         // document may be unloaded.
         mValueBeingSet = aValue;
         mIsCommittingComposition = true;
-        nsresult rv = mEditor->ForceCompositionEnd();
+        RefPtr<TextEditor> textEditor = mTextEditor;
+        nsresult rv = textEditor->ForceCompositionEnd();
         if (!self.get()) {
           return true;
         }
         mIsCommittingComposition = false;
         // If this is called recursively during committing composition and
         // some of them may be skipped above.  Therefore, we need to set
         // value to the editor with the aValue of the latest call.
         newValue = mValueBeingSet;
@@ -2587,17 +2568,17 @@ nsTextEditorState::SetValue(const nsAStr
   }
 
   // \r is an illegal character in the dom, but people use them,
   // so convert windows and mac platform linebreaks to \n:
   if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
     return false;
   }
 
-  if (mEditor && mBoundFrame) {
+  if (mTextEditor && mBoundFrame) {
     // The InsertText call below might flush pending notifications, which
     // could lead into a scheduled PrepareEditor to be called.  That will
     // lead to crashes (or worse) because we'd be initializing the editor
     // before InsertText returns.  This script blocker makes sure that
     // PrepareEditor cannot be called prematurely.
     nsAutoScriptBlocker scriptBlocker;
 
 #ifdef DEBUG
@@ -2616,51 +2597,47 @@ nsTextEditorState::SetValue(const nsAStr
       currentValue.Assign(*aOldValue);
     } else {
       mBoundFrame->GetText(currentValue);
     }
 
     AutoWeakFrame weakFrame(mBoundFrame);
 
     // this is necessary to avoid infinite recursion
-    if (!currentValue.Equals(newValue))
-    {
-      ValueSetter valueSetter(mEditor);
-
-      nsCOMPtr<nsIDOMDocument> domDoc;
-      mEditor->GetDocument(getter_AddRefs(domDoc));
-      if (!domDoc) {
-        NS_WARNING("Why don't we have a document?");
+    if (!currentValue.Equals(newValue)) {
+      RefPtr<TextEditor> textEditor = mTextEditor;
+      ValueSetter valueSetter(textEditor);
+
+      nsCOMPtr<nsIDocument> document = textEditor->GetDocument();
+      if (NS_WARN_IF(!document)) {
         return true;
       }
 
       // Time to mess with our security context... See comments in GetValue()
       // for why this is needed.  Note that we have to do this up here, because
       // otherwise SelectAll() will fail.
       {
         AutoNoJSAPI nojsapi;
 
         nsCOMPtr<nsISelection> domSel;
         mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                               getter_AddRefs(domSel));
         SelectionBatcher selectionBatcher(domSel ? domSel->AsSelection() :
                                                    nullptr);
 
-        nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(mEditor);
-        if (!plaintextEditor || !weakFrame.IsAlive()) {
-          NS_WARNING("Somehow not a plaintext editor?");
+        if (NS_WARN_IF(!weakFrame.IsAlive())) {
           return true;
         }
 
         valueSetter.Init();
 
         // get the flags, remove readonly, disabled and max-length,
         // set the value, restore flags
         {
-          AutoRestoreEditorState restoreState(mEditor, plaintextEditor);
+          AutoRestoreEditorState restoreState(textEditor);
 
           mTextListener->SettingValue(true);
           bool notifyValueChanged = !!(aFlags & eSetValue_Notify);
           mTextListener->SetValueChanged(notifyValueChanged);
 
           if (aFlags & eSetValue_BySetUserInput) {
             nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
             uint32_t currentLength = currentValue.Length();
@@ -2673,29 +2650,29 @@ nsTextEditorState::SetValue(const nsAStr
             } else {
               // Collapse selection to the end so that we can append data.
               mBoundFrame->SelectAllOrCollapseToEndOfText(false);
             }
             const nsAString& insertValue =
               StringTail(newValue, newlength - currentLength);
 
             if (insertValue.IsEmpty()) {
-              mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
+              textEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
             } else {
-              plaintextEditor->InsertText(insertValue);
+              textEditor->InsertText(insertValue);
             }
           } else {
-            AutoDisableUndo disableUndo(mEditor);
+            AutoDisableUndo disableUndo(textEditor);
             if (domSel) {
               // Since we don't use undo transaction, we don't need to store
               // selection state.  SetText will set selection to tail.
               domSel->RemoveAllRanges();
             }
 
-            plaintextEditor->SetText(newValue);
+            textEditor->SetText(newValue);
           }
 
           mTextListener->SetValueChanged(true);
           mTextListener->SettingValue(false);
 
           if (!notifyValueChanged) {
             // Listener doesn't update frame, but it is required for placeholder
             ValueWasChanged(true);
@@ -2772,20 +2749,20 @@ nsTextEditorState::SetValue(const nsAStr
                                    /* aWasInteractiveUserChange = */ false);
 
   return true;
 }
 
 bool
 nsTextEditorState::HasNonEmptyValue()
 {
-  if (mEditor && mBoundFrame && mEditorInitialized &&
+  if (mTextEditor && mBoundFrame && mEditorInitialized &&
       !mIsCommittingComposition) {
     bool empty;
-    nsresult rv = mEditor->GetDocumentIsEmpty(&empty);
+    nsresult rv = mTextEditor->GetDocumentIsEmpty(&empty);
     if (NS_SUCCEEDED(rv)) {
       return !empty;
     }
   }
 
   nsAutoString value;
   GetValue(value, true);
   return !value.IsEmpty();
@@ -2903,20 +2880,17 @@ nsTextEditorState::HideSelectionIfBlurre
   if (!nsContentUtils::IsFocusedContent(content)) {
     mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   }
 }
 
 bool
 nsTextEditorState::EditorHasComposition()
 {
-  bool isComposing = false;
-  return mEditor &&
-         NS_SUCCEEDED(mEditor->GetComposing(&isComposing)) &&
-         isComposing;
+  return mTextEditor && mTextEditor->IsIMEComposing();
 }
 
 NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
 
 void
 nsAnonDivObserver::CharacterDataChanged(nsIDocument*             aDocument,
                                         nsIContent*              aContent,
                                         CharacterDataChangeInfo* aInfo)
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -9,27 +9,27 @@
 
 #include "nsString.h"
 #include "nsITextControlElement.h"
 #include "nsITextControlFrame.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/TextEditor.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/HTMLInputElementBinding.h"
 #include "mozilla/dom/Nullable.h"
 
 class nsTextInputListener;
 class nsTextControlFrame;
 class nsTextInputSelectionImpl;
 class nsAnonDivObserver;
 class nsISelectionController;
 class nsFrameSelection;
-class nsIEditor;
 class nsITextControlElement;
 class nsFrame;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
@@ -148,17 +148,17 @@ public:
     Unlink();
     mValue.reset();
     mCachedValue.Truncate();
     mValueBeingSet.Truncate();
     mTextCtrlElement = nullptr;
     MOZ_ASSERT(!mMutationObserver);
   }
 
-  nsIEditor* GetEditor();
+  mozilla::TextEditor* GetTextEditor();
   nsISelectionController* GetSelectionController() const;
   nsFrameSelection* GetConstFrameSelection();
   nsresult BindToFrame(nsTextControlFrame* aFrame);
   void UnbindFromFrame(nsTextControlFrame* aFrame);
   nsresult PrepareEditor(const nsAString *aValue = nullptr);
   void InitializeKeyboardEventListeners();
 
   enum SetValueFlags
@@ -445,17 +445,17 @@ private:
   friend class PrepareEditorEvent;
 
   // The text control element owns this object, and ensures that this object
   // has a smaller lifetime.
   nsITextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement;
   // mSelCon is non-null while we have an mBoundFrame.
   RefPtr<nsTextInputSelectionImpl> mSelCon;
   RefPtr<RestoreSelectionState> mRestoringSelection;
-  nsCOMPtr<nsIEditor> mEditor;
+  RefPtr<mozilla::TextEditor> mTextEditor;
   nsCOMPtr<mozilla::dom::Element> mRootNode;
   nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
   nsCOMPtr<mozilla::dom::Element> mPreviewDiv;
   nsTextControlFrame* mBoundFrame;
   RefPtr<nsTextInputListener> mTextListener;
   mozilla::Maybe<nsString> mValue;
   RefPtr<nsAnonDivObserver> mMutationObserver;
   mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -93,16 +93,17 @@
 #endif
 #endif
 
 #include "mozilla/Unused.h"
 
 #include "mozInlineSpellChecker.h"
 #include "nsDocShell.h"
 #include "nsIConsoleListener.h"
+#include "nsIContentViewer.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIIdlePeriod.h"
 #include "nsIDragService.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMutable.h"
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CubebUtils.h"
 
 #include "MediaInfo.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/Sprintf.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "nsAutoRef.h"
 #include "nsDebug.h"
 #include "nsIStringBundle.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
--- a/dom/media/tests/mochitest/test_peerConnection_stats.html
+++ b/dom/media/tests/mochitest/test_peerConnection_stats.html
@@ -198,18 +198,20 @@ var pedanticChecks = report => {
         stat.type + ".bytesReceived is a sane number for a short test. value="
         + stat.bytesReceived);
 
       // packetsLost
       ok(stat.packetsLost < 100,
         stat.type + ".packetsLost is a sane number for a short test. value="
         + stat.packetsLost);
 
+      // This should be much lower for audio, TODO: Bug 1330575
+      let expectedJitter = stat.mediaType == "video" ? 0.5 : 1;
       // jitter
-      ok(stat.jitter < 10, // This should be much lower, TODO: Bug 1330575
+      ok(stat.jitter < expectedJitter,
         stat.type + ".jitter is sane number for a local only test. value="
         + stat.jitter);
 
       // packetsDiscarded
       // special exception for, TODO: Bug 1335967
       // if (!stat.inner.isRemote && stat.discardedPackets !== undefined) {
       //   ok(stat.packetsDiscarded < 100, stat.type
       //     + ".packetsDiscarded is a sane number for a short test. value="
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -327,17 +327,17 @@ EditorBase::PostCreate()
     IMEState newState;
     rv = GetPreferredIMEState(&newState);
     NS_ENSURE_SUCCESS(rv, NS_OK);
     nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
     IMEStateManager::UpdateIMEState(newState, content, *this);
   }
 
   // FYI: This call might cause destroying this editor.
-  IMEStateManager::OnEditorInitialized(this);
+  IMEStateManager::OnEditorInitialized(*this);
 
   return NS_OK;
 }
 
 void
 EditorBase::CreateEventListeners()
 {
   // Don't create the handler twice
@@ -437,17 +437,17 @@ EditorBase::GetDesiredSpellCheckState()
 }
 
 NS_IMETHODIMP
 EditorBase::PreDestroy(bool aDestroyingFrames)
 {
   if (mDidPreDestroy)
     return NS_OK;
 
-  IMEStateManager::OnEditorDestroying(this);
+  IMEStateManager::OnEditorDestroying(*this);
 
   // Let spellchecker clean up its observers etc. It is important not to
   // actually free the spellchecker here, since the spellchecker could have
   // caused flush notifications, which could have gotten here if a textbox
   // is being removed. Setting the spellchecker to nullptr could free the
   // object that is still in use! It will be freed when the editor is
   // destroyed.
   if (mInlineSpellChecker)
@@ -476,17 +476,19 @@ EditorBase::PreDestroy(bool aDestroyingF
 
   mDidPreDestroy = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::GetFlags(uint32_t* aFlags)
 {
-  *aFlags = mFlags;
+  // NOTE: If you need to override this method, you need to make Flags()
+  //       virtual.
+  *aFlags = Flags();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::SetFlags(uint32_t aFlags)
 {
   if (mFlags == aFlags) {
     return NS_OK;
@@ -774,39 +776,75 @@ EditorBase::EnableUndo(bool aEnable)
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::GetNumberOfUndoItems(int32_t* aNumItems)
 {
-  *aNumItems = 0;
-  return mTxnMgr ? mTxnMgr->GetNumberOfUndoItems(aNumItems) : NS_OK;
+  *aNumItems = NumberOfUndoItems();
+  return *aNumItems >= 0 ? NS_OK : NS_ERROR_FAILURE;
+}
+
+int32_t
+EditorBase::NumberOfUndoItems() const
+{
+  if (!mTxnMgr) {
+    return 0;
+  }
+  int32_t numItems = 0;
+  if (NS_WARN_IF(NS_FAILED(mTxnMgr->GetNumberOfUndoItems(&numItems)))) {
+    return -1;
+  }
+  return numItems;
 }
 
 NS_IMETHODIMP
 EditorBase::GetNumberOfRedoItems(int32_t* aNumItems)
 {
-  *aNumItems = 0;
-  return mTxnMgr ? mTxnMgr->GetNumberOfRedoItems(aNumItems) : NS_OK;
+  *aNumItems = NumberOfRedoItems();
+  return *aNumItems >= 0 ? NS_OK : NS_ERROR_FAILURE;
+}
+
+int32_t
+EditorBase::NumberOfRedoItems() const
+{
+  if (!mTxnMgr) {
+    return 0;
+  }
+  int32_t numItems = 0;
+  if (NS_WARN_IF(NS_FAILED(mTxnMgr->GetNumberOfRedoItems(&numItems)))) {
+    return -1;
+  }
+  return numItems;
 }
 
 NS_IMETHODIMP
 EditorBase::GetTransactionManager(nsITransactionManager** aTxnManager)
 {
-  NS_ENSURE_ARG_POINTER(aTxnManager);
-
-  *aTxnManager = nullptr;
-  NS_ENSURE_TRUE(mTxnMgr, NS_ERROR_FAILURE);
-
-  NS_ADDREF(*aTxnManager = mTxnMgr);
+  // NOTE: If you need to override this method, you need to make
+  //       GetTransactionManager() virtual.
+  if (NS_WARN_IF(!aTxnManager)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *aTxnManager = GetTransactionManager().take();
+  if (NS_WARN_IF(!*aTxnManager)) {
+    return NS_ERROR_FAILURE;
+  }
   return NS_OK;
 }
 
+already_AddRefed<nsITransactionManager>
+EditorBase::GetTransactionManager() const
+{
+  nsCOMPtr<nsITransactionManager> transactionManager = mTxnMgr.get();
+  return transactionManager.forget();
+}
+
 NS_IMETHODIMP
 EditorBase::Undo(uint32_t aCount)
 {
   ForceCompositionEnd();
 
   bool hasTxnMgr, hasTransaction = false;
   CanUndo(&hasTxnMgr, &hasTransaction);
   NS_ENSURE_TRUE(hasTransaction, NS_OK);
@@ -5226,33 +5264,39 @@ EditorBase::OnFocus(nsIDOMEventTarget* a
     mInlineSpellChecker->UpdateCurrentDictionary();
     mSpellCheckerDictionaryUpdated = true;
   }
 }
 
 NS_IMETHODIMP
 EditorBase::GetSuppressDispatchingInputEvent(bool* aSuppressed)
 {
-  NS_ENSURE_ARG_POINTER(aSuppressed);
-  *aSuppressed = !mDispatchInputEvent;
+  // NOTE: If you need to override this method, you need to make
+  //       IsSuppressingDispatchingInputEvent() virtual.
+  if (NS_WARN_IF(aSuppressed)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  *aSuppressed = IsSuppressingDispatchingInputEvent();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::SetSuppressDispatchingInputEvent(bool aSuppress)
 {
   mDispatchInputEvent = !aSuppress;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::GetIsInEditAction(bool* aIsInEditAction)
 {
+  // NOTE: If you need to override this method, you need to make
+  //       IsInEditAction() virtual.
   MOZ_ASSERT(aIsInEditAction, "aIsInEditAction must not be null");
-  *aIsInEditAction = mIsInEditAction;
+  *aIsInEditAction = IsInEditAction();
   return NS_OK;
 }
 
 int32_t
 EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
 {
   MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr");
 
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -136,54 +136,72 @@ class Text;
 namespace widget {
 struct IMEState;
 } // namespace widget
 
 /**
  * CachedWeakPtr stores a pointer to a class which inherits nsIWeakReference.
  * If the instance of the class has already been destroyed, this returns
  * nullptr.  Otherwise, returns cached pointer.
+ * If class T inherits nsISupports a lot, specify Base explicitly for avoiding
+ * ambiguous conversion to nsISupports.
  */
-template<class T>
+template<class T, class Base = nsISupports>
 class CachedWeakPtr final
 {
 public:
-  CachedWeakPtr<T>()
+  CachedWeakPtr<T, Base>()
     : mCache(nullptr)
   {
   }
+  explicit CachedWeakPtr<T, Base>(T* aObject)
+  {
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject));
+    mCache = aObject;
+  }
+  explicit CachedWeakPtr<T, Base>(const nsCOMPtr<T>& aOther)
+  {
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get()));
+    mCache = aOther;
+  }
+  explicit CachedWeakPtr<T, Base>(already_AddRefed<T>& aOther)
+  {
+    RefPtr<T> other = aOther;
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get()));
+    mCache = other;
+  }
 
-  CachedWeakPtr<T>& operator=(T* aObject)
+  CachedWeakPtr<T, Base>& operator=(T* aObject)
   {
-    mWeakPtr = do_GetWeakReference(aObject);
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(aObject));
     mCache = aObject;
     return *this;
   }
-  CachedWeakPtr<T>& operator=(const nsCOMPtr<T>& aOther)
+  CachedWeakPtr<T, Base>& operator=(const nsCOMPtr<T>& aOther)
   {
-    mWeakPtr = do_GetWeakReference(aOther);
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(aOther.get()));
     mCache = aOther;
     return *this;
   }
-  CachedWeakPtr<T>& operator=(already_AddRefed<T>& aOther)
+  CachedWeakPtr<T, Base>& operator=(already_AddRefed<T>& aOther)
   {
-    nsCOMPtr<T> other = aOther;
-    mWeakPtr = do_GetWeakReference(other);
+    RefPtr<T> other = aOther;
+    mWeakPtr = do_GetWeakReference(static_cast<Base*>(other.get()));
     mCache = other;
     return *this;
   }
 
   bool IsAlive() const { return mWeakPtr && mWeakPtr->IsAlive(); }
 
   explicit operator bool() const { return mWeakPtr; }
   operator T*() const { return get(); }
   T* get() const
   {
     if (mCache && !mWeakPtr->IsAlive()) {
-      const_cast<CachedWeakPtr<T>*>(this)->mCache = nullptr;
+      const_cast<CachedWeakPtr<T, Base>*>(this)->mCache = nullptr;
     }
     return mCache;
   }
 
 private:
   nsWeakPtr mWeakPtr;
   T* MOZ_NON_OWNING_REF mCache;
 };
@@ -757,16 +775,23 @@ public:
   bool IsIMEComposing() const;
 
   /**
    * Returns true when inserting text should be a part of current composition.
    */
   bool ShouldHandleIMEComposition() const;
 
   /**
+   * Returns number of undo or redo items.  If TransactionManager returns
+   * unexpected error, returns -1.
+   */
+  int32_t NumberOfUndoItems() const;
+  int32_t NumberOfRedoItems() const;
+
+  /**
    * From html rules code - migration in progress.
    */
   static nsresult GetTagString(nsIDOMNode* aNode, nsAString& outString);
   static nsIAtom* GetTag(nsIDOMNode* aNode);
 
   bool NodesSameType(nsIDOMNode* aNode1, nsIDOMNode* aNode2);
   virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2);
 
@@ -862,31 +887,44 @@ public:
    * Likewise, but gets the text control element instead of the root for
    * plaintext editors.
    */
   Element* GetExposedRoot();
 
   /**
    * Accessor methods to flags.
    */
+  uint32_t Flags() const { return mFlags; }
+
   bool IsPlaintextEditor() const
   {
     return (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
   }
 
   bool IsSingleLineEditor() const
   {
     return (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) != 0;
   }
 
   bool IsPasswordEditor() const
   {
     return (mFlags & nsIPlaintextEditor::eEditorPasswordMask) != 0;
   }
 
+  // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if
+  //      the editor inherits the content node's direction.
+  bool IsRightToLeft() const
+  {
+    return (mFlags & nsIPlaintextEditor::eEditorRightToLeft) != 0;
+  }
+  bool IsLeftToRight() const
+  {
+    return (mFlags & nsIPlaintextEditor::eEditorLeftToRight) != 0;
+  }
+
   bool IsReadonly() const
   {
     return (mFlags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
   }
 
   bool IsDisabled() const
   {
     return (mFlags & nsIPlaintextEditor::eEditorDisabledMask) != 0;
@@ -944,16 +982,37 @@ public:
   }
 
   bool IsModifiable() const
   {
     return !IsReadonly();
   }
 
   /**
+   * IsInEditAction() return true while the instance is handling an edit action.
+   * Otherwise, false.
+   */
+  bool IsInEditAction() const { return mIsInEditAction; }
+
+  /**
+   * IsSuppressingDispatchingInputEvent() returns true if the editor stops
+   * dispatching input event.  Otherwise, false.
+   */
+  bool IsSuppressingDispatchingInputEvent() const
+  {
+    return !mDispatchInputEvent;
+  }
+
+  /**
+   * GetTransactionManager() returns transaction manager associated with the
+   * editor.  This may return nullptr if undo/redo hasn't been enabled.
+   */
+  already_AddRefed<nsITransactionManager> GetTransactionManager() const;
+
+  /**
    * Get the input event target. This might return null.
    */
   virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0;
 
   /**
    * Get the focused content, if we're focused.  Returns null otherwise.
    */
   virtual already_AddRefed<nsIContent> GetFocusedContent();
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -1152,17 +1152,17 @@ EditorEventListener::Focus(InternalFocus
   if (DetachedFromEditorOrDefaultPrevented(aFocusEvent)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   NS_ENSURE_TRUE(ps, NS_OK);
   nsCOMPtr<nsIContent> focusedContent = editorBase->GetFocusedContentForIME();
   IMEStateManager::OnFocusInEditor(ps->GetPresContext(), focusedContent,
-                                   editorBase);
+                                   *editorBase);
 
   return NS_OK;
 }
 
 nsresult
 EditorEventListener::Blur(InternalFocusEvent* aBlurEvent)
 {
   if (NS_WARN_IF(!aBlurEvent) || DetachedFromEditor()) {
@@ -1233,18 +1233,17 @@ EditorEventListener::ShouldHandleNativeK
 
   nsCOMPtr<nsIDOMEventTarget> target = aKeyboardEvent->GetDOMEventTarget();
   nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
   if (!targetContent) {
     return false;
   }
 
   RefPtr<EditorBase> editorBase(mEditorBase);
-  nsCOMPtr<nsIHTMLEditor> htmlEditor =
-    do_QueryInterface(static_cast<nsIEditor*>(editorBase));
+  HTMLEditor* htmlEditor = editorBase->AsHTMLEditor();
   if (!htmlEditor) {
     return false;
   }
 
   nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
   if (doc->HasFlag(NODE_IS_EDITABLE)) {
     // Don't need to perform any checks in designMode documents.
     return true;
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -139,18 +139,20 @@ EditActionHandled(nsresult aRv = NS_OK)
  */
 inline EditActionResult
 EditActionCanceled(nsresult aRv = NS_OK)
 {
   return EditActionResult(aRv, true, true);
 }
 
 /***************************************************************************
- * stack based helper class for batching a collection of txns inside a
- * placeholder txn.
+ * stack based helper class for batching a collection of transactions inside a
+ * placeholder transaction.
+ * XXX This is used by mozInlineSpellChecker.  Therefore, cannot use concrete
+ *     editor class.
  */
 class MOZ_RAII AutoPlaceHolderBatch
 {
 private:
   nsCOMPtr<nsIEditor> mEditor;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
 public:
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -26,19 +26,17 @@
 #include "nsROCSSPrimitiveValue.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNode.h"
 #include "nsDOMCSSRGBColor.h"
 #include "nsIDOMWindow.h"
-#include "nsIEditor.h"
 #include "nsIEditRules.h"
-#include "nsIHTMLEditor.h"
 #include "nsIHTMLObjectResizer.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 #include "nsLiteralString.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
@@ -368,17 +366,17 @@ HTMLEditor::SnapToGrid(int32_t& newX, in
 }
 
 nsresult
 HTMLEditor::GrabberClicked()
 {
   // add a mouse move listener to the editor
   nsresult rv = NS_OK;
   if (!mMouseMotionListenerP) {
-    mMouseMotionListenerP = new ResizerMouseMotionListener(this);
+    mMouseMotionListenerP = new ResizerMouseMotionListener(*this);
     if (!mMouseMotionListenerP) {return NS_ERROR_NULL_POINTER;}
 
     nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
     NS_ENSURE_TRUE(piTarget, NS_ERROR_FAILURE);
 
     rv = piTarget->AddEventListener(NS_LITERAL_STRING("mousemove"),
                                      mMouseMotionListenerP,
                                      false, false);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -290,17 +290,17 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
       mLinkHandler = context->GetLinkHandler();
       context->SetLinkHandler(nullptr);
     }
 
     // init the type-in state
     mTypeInState = new TypeInState();
 
     // init the selection listener for image resizing
-    mSelectionListenerP = new ResizerSelectionListener(this);
+    mSelectionListenerP = new ResizerSelectionListener(*this);
 
     if (!IsInteractionAllowed()) {
       // ignore any errors from this in case the file is missing
       AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/EditorOverride.css"));
     }
 
     RefPtr<Selection> selection = GetSelection();
     if (selection) {
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -16,17 +16,16 @@
 
 #include "nsAttrName.h"
 #include "nsCOMPtr.h"
 #include "nsIContentFilter.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsIDocumentObserver.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
-#include "nsIEditor.h"
 #include "nsIEditorMailSupport.h"
 #include "nsIEditorStyleSheets.h"
 #include "nsIEditorUtils.h"
 #include "nsIEditRules.h"
 #include "nsIHTMLAbsPosEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsIHTMLInlineTableEditor.h"
 #include "nsIHTMLObjectResizer.h"
@@ -34,16 +33,17 @@
 #include "nsITableEditor.h"
 #include "nsPoint.h"
 #include "nsStubMutationObserver.h"
 #include "nsTArray.h"
 
 class nsDocumentFragment;
 class nsITransferable;
 class nsIClipboard;
+class nsIDOMMouseEvent;
 class nsILinkHandler;
 class nsTableWrapperFrame;
 class nsIDOMRange;
 class nsRange;
 
 namespace mozilla {
 
 class HTMLEditorEventListener;
@@ -130,16 +130,18 @@ public:
                      bool aSuppressTransaction) override;
   virtual nsresult SetAttributeOrEquivalent(Element* aElement,
                                             nsIAtom* aAttribute,
                                             const nsAString& aValue,
                                             bool aSuppressTransaction) override;
   using EditorBase::RemoveAttributeOrEquivalent;
   using EditorBase::SetAttributeOrEquivalent;
 
+  nsresult MouseMove(nsIDOMMouseEvent* aMouseEvent);
+
   // nsStubMutationObserver overrides
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   // nsIHTMLEditor methods
   NS_DECL_NSIHTMLEDITOR
 
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -45,26 +45,23 @@
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLLinkElement.h"
 #include "nsIDOMHTMLObjectElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
-#include "nsIEditor.h"
-#include "nsIEditorMailSupport.h"
 #include "nsIEditRules.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIParserUtils.h"
-#include "nsIPlaintextEditor.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISupportsUtils.h"
 #include "nsITransferable.h"
 #include "nsIURI.h"
 #include "nsIVariant.h"
 #include "nsLinebreakConverter.h"
 #include "nsLiteralString.h"
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -13,17 +13,16 @@
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMNode.h"
-#include "nsIEditor.h"
 #include "nsIHTMLInlineTableEditor.h"
 #include "nsIHTMLObjectResizer.h"
 #include "nsISupportsImpl.h"
 #include "nsLiteralString.h"
 #include "nsQueryObject.h"
 #include "nsRange.h"
 
 namespace mozilla {
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -24,18 +24,16 @@
 #include "nsID.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMNode.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"
 #include "nsStringFwd.h"
 #include "nsSubstringTuple.h"
 #include "nscore.h"
@@ -48,86 +46,87 @@ namespace mozilla {
 using namespace dom;
 
 /******************************************************************************
  * mozilla::DocumentResizeEventListener
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(DocumentResizeEventListener, nsIDOMEventListener)
 
-DocumentResizeEventListener::DocumentResizeEventListener(nsIHTMLEditor* aEditor)
+DocumentResizeEventListener::DocumentResizeEventListener(
+                               HTMLEditor& aHTMLEditor)
+  : mHTMLEditorWeak(&aHTMLEditor)
 {
-  mEditor = do_GetWeakReference(aEditor);
 }
 
 NS_IMETHODIMP
 DocumentResizeEventListener::HandleEvent(nsIDOMEvent* aMouseEvent)
 {
-  nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
-  if (objectResizer) {
-    return objectResizer->RefreshResizers();
+  RefPtr<HTMLEditor> htmlEditor = mHTMLEditorWeak.get();
+  if (htmlEditor) {
+    return htmlEditor->RefreshResizers();
   }
   return NS_OK;
 }
 
 /******************************************************************************
  * mozilla::ResizerSelectionListener
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(ResizerSelectionListener, nsISelectionListener)
 
-ResizerSelectionListener::ResizerSelectionListener(nsIHTMLEditor* aEditor)
+ResizerSelectionListener::ResizerSelectionListener(HTMLEditor& aHTMLEditor)
+  : mHTMLEditorWeak(&aHTMLEditor)
 {
-  mEditor = do_GetWeakReference(aEditor);
 }
 
 NS_IMETHODIMP
 ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
                                                  nsISelection* aSelection,
                                                  int16_t aReason)
 {
   if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
                   nsISelectionListener::KEYPRESS_REASON |
                   nsISelectionListener::SELECTALL_REASON)) && aSelection) {
     // the selection changed and we need to check if we have to
     // hide and/or redisplay resizing handles
-    nsCOMPtr<nsIHTMLEditor> editor = do_QueryReferent(mEditor);
-    if (editor) {
-      editor->CheckSelectionStateForAnonymousButtons(aSelection);
+    RefPtr<HTMLEditor> htmlEditor = mHTMLEditorWeak.get();
+    if (htmlEditor) {
+      htmlEditor->CheckSelectionStateForAnonymousButtons(aSelection);
     }
   }
 
   return NS_OK;
 }
 
 /******************************************************************************
  * mozilla::ResizerMouseMotionListener
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener)
 
-ResizerMouseMotionListener::ResizerMouseMotionListener(nsIHTMLEditor* aEditor)
+ResizerMouseMotionListener::ResizerMouseMotionListener(HTMLEditor& aHTMLEditor)
+  : mHTMLEditorWeak(&aHTMLEditor)
 {
-  mEditor = do_GetWeakReference(aEditor);
 }
 
 NS_IMETHODIMP
 ResizerMouseMotionListener::HandleEvent(nsIDOMEvent* aMouseEvent)
 {
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
+  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
   if (!mouseEvent) {
     //non-ui event passed in.  bad things.
     return NS_OK;
   }
 
   // Don't do anything special if not an HTML object resizer editor
-  nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryReferent(mEditor);
-  if (objectResizer) {
+  RefPtr<HTMLEditor> htmlEditor = mHTMLEditorWeak.get();
+  if (htmlEditor) {
     // check if we have to redisplay a resizing shadow
-    objectResizer->MouseMove(aMouseEvent);
+    htmlEditor->MouseMove(mouseEvent);
   }
 
   return NS_OK;
 }
 
 /******************************************************************************
  * mozilla::HTMLEditor
  ******************************************************************************/
@@ -356,17 +355,17 @@ HTMLEditor::ShowResizersInner(nsIDOMElem
   nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(doc->GetWindow());
   if (!target) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  mResizeEventListenerP = new DocumentResizeEventListener(this);
+  mResizeEventListenerP = new DocumentResizeEventListener(*this);
   if (!mResizeEventListenerP) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   rv = target->AddEventListener(NS_LITERAL_STRING("resize"),
                                 mResizeEventListenerP, false);
   // XXX Even when it failed to add event listener, should we need to set
   //     _moz_resizing attribute?
   aResizedElement->SetAttribute(NS_LITERAL_STRING("_moz_resizing"), NS_LITERAL_STRING("true"));
@@ -528,17 +527,17 @@ HTMLEditor::StartResizing(nsIDOMElement*
   mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::width,
                                       mResizedObjectWidth);
   mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
                                       mResizedObjectHeight);
 
   // add a mouse move listener to the editor
   nsresult result = NS_OK;
   if (!mMouseMotionListenerP) {
-    mMouseMotionListenerP = new ResizerMouseMotionListener(this);
+    mMouseMotionListenerP = new ResizerMouseMotionListener(*this);
     if (!mMouseMotionListenerP) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
     NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
 
     result = target->AddEventListener(NS_LITERAL_STRING("mousemove"),
@@ -804,26 +803,37 @@ HTMLEditor::GetNewResizingHeight(int32_t
                      GetNewResizingIncrement(aX, aY, kHeight) *
                          mHeightIncrementFactor;
   return std::max(resized, 1);
 }
 
 NS_IMETHODIMP
 HTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent)
 {
+  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
+  if (NS_WARN_IF(!mouseEvent)) {
+    return NS_OK;
+  }
+  return MouseMove(mouseEvent);
+}
+
+nsresult
+HTMLEditor::MouseMove(nsIDOMMouseEvent* aMouseEvent)
+{
+  MOZ_ASSERT(aMouseEvent);
+
   NS_NAMED_LITERAL_STRING(leftStr, "left");
   NS_NAMED_LITERAL_STRING(topStr, "top");
 
   if (mIsResizing) {
     // we are resizing and the mouse pointer's position has changed
     // we have to resdisplay the shadow
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
     int32_t clientX, clientY;
-    mouseEvent->GetClientX(&clientX);
-    mouseEvent->GetClientY(&clientY);
+    aMouseEvent->GetClientX(&clientX);
+    aMouseEvent->GetClientY(&clientY);
 
     int32_t newX = GetNewResizingX(clientX, clientY);
     int32_t newY = GetNewResizingY(clientX, clientY);
     int32_t newWidth  = GetNewResizingWidth(clientX, clientY);
     int32_t newHeight = GetNewResizingHeight(clientX, clientY);
 
     mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::left,
                                         newX);
@@ -833,37 +843,35 @@ HTMLEditor::MouseMove(nsIDOMEvent* aMous
                                         newWidth);
     mCSSEditUtils->SetCSSPropertyPixels(*mResizingShadow, *nsGkAtoms::height,
                                         newHeight);
 
     return SetResizingInfoPosition(newX, newY, newWidth, newHeight);
   }
 
   if (mGrabberClicked) {
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
     int32_t clientX, clientY;
-    mouseEvent->GetClientX(&clientX);
-    mouseEvent->GetClientY(&clientY);
+    aMouseEvent->GetClientX(&clientX);
+    aMouseEvent->GetClientY(&clientY);
 
     int32_t xThreshold =
       LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdX, 1);
     int32_t yThreshold =
       LookAndFeel::GetInt(LookAndFeel::eIntID_DragThresholdY, 1);
 
     if (DeprecatedAbs(clientX - mOriginalX) * 2 >= xThreshold ||
         DeprecatedAbs(clientY - mOriginalY) * 2 >= yThreshold) {
       mGrabberClicked = false;
       StartMoving(nullptr);
     }
   }
   if (mIsMoving) {
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
     int32_t clientX, clientY;
-    mouseEvent->GetClientX(&clientX);
-    mouseEvent->GetClientY(&clientY);
+    aMouseEvent->GetClientX(&clientX);
+    aMouseEvent->GetClientY(&clientY);
 
     int32_t newX = mPositionedObjectX + clientX - mOriginalX;
     int32_t newY = mPositionedObjectY + clientY - mOriginalY;
 
     SnapToGrid(newX, newY);
 
     mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::left,
                                         newX);
--- a/editor/libeditor/HTMLEditorObjectResizerUtils.h
+++ b/editor/libeditor/HTMLEditorObjectResizerUtils.h
@@ -1,16 +1,17 @@
 /* -*- 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 HTMLEditorObjectResizerUtils_h
 #define HTMLEditorObjectResizerUtils_h
 
+#include "mozilla/HTMLEditor.h"
 #include "nsIDOMEventListener.h"
 #include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsLiteralString.h"
 
 class nsIHTMLEditor;
 
@@ -27,56 +28,56 @@ namespace mozilla {
 
 /******************************************************************************
  * mozilla::ResizerSelectionListener
  ******************************************************************************/
 
 class ResizerSelectionListener final : public nsISelectionListener
 {
 public:
-  explicit ResizerSelectionListener(nsIHTMLEditor* aEditor);
+  explicit ResizerSelectionListener(HTMLEditor& aHTMLEditor);
   void Reset();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISELECTIONLISTENER
 
 protected:
   virtual ~ResizerSelectionListener() {}
-  nsWeakPtr mEditor;
+  CachedWeakPtr<HTMLEditor, nsIHTMLEditor> mHTMLEditorWeak;
 };
 
 /******************************************************************************
  * mozilla::ResizerMouseMotionListener
  ******************************************************************************/
 
 class ResizerMouseMotionListener final : public nsIDOMEventListener
 {
 public:
-  explicit ResizerMouseMotionListener(nsIHTMLEditor* aEditor);
+  explicit ResizerMouseMotionListener(HTMLEditor& aHTMLEditor);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
 protected:
   virtual ~ResizerMouseMotionListener() {}
-  nsWeakPtr mEditor;
+  CachedWeakPtr<HTMLEditor, nsIHTMLEditor> mHTMLEditorWeak;
 };
 
 /******************************************************************************
  * mozilla::DocumentResizeEventListener
  ******************************************************************************/
 
 class DocumentResizeEventListener final : public nsIDOMEventListener
 {
 public:
-  explicit DocumentResizeEventListener(nsIHTMLEditor* aEditor);
+  explicit DocumentResizeEventListener(HTMLEditor& aHTMLEditor);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
 protected:
   virtual ~DocumentResizeEventListener() {}
-  nsWeakPtr mEditor;
+  CachedWeakPtr<HTMLEditor, nsIHTMLEditor> mHTMLEditorWeak;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef HTMLEditorObjectResizerUtils_h
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -21,17 +21,16 @@
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMElement.h"
-#include "nsIEditor.h"
 #include "nsIEditRules.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsISupportsImpl.h"
 #include "nsLiteralString.h"
 #include "nsRange.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -17,17 +17,16 @@
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
-#include "nsIEditor.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISupportsUtils.h"
 #include "nsITableCellLayout.h" // For efficient access to table cell
 #include "nsITableEditor.h"
 #include "nsLiteralString.h"
 #include "nsQueryFrame.h"
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -965,18 +965,22 @@ TextEditor::SetMaxTextLength(int32_t aMa
 {
   mMaxTextLength = aMaxTextLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextEditor::GetMaxTextLength(int32_t* aMaxTextLength)
 {
-  NS_ENSURE_TRUE(aMaxTextLength, NS_ERROR_INVALID_POINTER);
-  *aMaxTextLength = mMaxTextLength;
+  // NOTE: If you need to override this method, you need to make
+  //       MaxTextLength() virtual.
+  if (NS_WARN_IF(!aMaxTextLength)) {
+    return NS_ERROR_INVALID_POINTER;
+  }
+  *aMaxTextLength = MaxTextLength();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextEditor::GetWrapWidth(int32_t* aWrapColumn)
 {
   NS_ENSURE_TRUE( aWrapColumn, NS_ERROR_NULL_POINTER);
 
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -172,16 +172,18 @@ public:
    * principals match, or we are in a editor context where this doesn't matter.
    * Otherwise, the data must be sanitized first.
    */
   bool IsSafeToInsertData(nsIDOMDocument* aSourceDoc);
 
   static void GetDefaultEditorPrefs(int32_t& aNewLineHandling,
                                     int32_t& aCaretStyle);
 
+  int32_t MaxTextLength() const { return mMaxTextLength; }
+
 protected:
   virtual ~TextEditor();
 
   NS_IMETHOD InitRules();
   void BeginEditorInit();
   nsresult EndEditorInit();
 
   already_AddRefed<nsIDocumentEncoder> GetAndInitDocEncoder(
--- a/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
+++ b/gfx/skia/skia/src/ports/SkTypeface_win_dw.cpp
@@ -351,26 +351,38 @@ SkAdvancedTypefaceMetrics* DWriteFontTyp
 
     info->fAscent = SkToS16(dwfm.ascent);
     info->fDescent = SkToS16(dwfm.descent);
     info->fCapHeight = SkToS16(dwfm.capHeight);
 
     // SkAdvancedTypefaceMetrics::fFontName is in theory supposed to be
     // the PostScript name of the font. However, due to the way it is currently
     // used, it must actually be a family name.
-    SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
-    hr = fDWriteFontFamily->GetFamilyNames(&familyNames);
+    if (fDWriteFontFamily) {
+      SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
+      hr = fDWriteFontFamily->GetFamilyNames(&familyNames);
+
+      UINT32 familyNameLen;
+      hr = familyNames->GetStringLength(0, &familyNameLen);
+
+      SkSMallocWCHAR familyName(familyNameLen+1);
+      hr = familyNames->GetString(0, familyName.get(), familyNameLen+1);
 
-    UINT32 familyNameLen;
-    hr = familyNames->GetStringLength(0, &familyNameLen);
-
-    SkSMallocWCHAR familyName(familyNameLen+1);
-    hr = familyNames->GetString(0, familyName.get(), familyNameLen+1);
-
-    hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName);
+      hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName);
+    } else {
+      // The name length would be passed to WideCharToMultiByte. It allows
+      // setting -1 in WideCharToMultiByte. The document says "If this
+      // parameter is -1, the function processes the entire input string,
+      // including the terminating null character. Therefore, the resulting
+      // character string has a terminating null character, and the length
+      // returned by the function includes this character."
+      // https://msdn.microsoft.com/library/windows/desktop/dd374130(v=vs.85).aspx
+      hr = sk_wchar_to_skstring(L"Unknown", -1 /* == strlen("Unknown") + 1 */,
+                                &info->fFontName);
+    }
 
     if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
     }
 
     DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType();
     if (fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE &&
         fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION)
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -176,50 +176,43 @@ ServoRestyleManager::ClearRestyleStateFr
  * need to track during the post-traversal.
  *
  * This is currently used to properly compute change hints when the parent
  * element of this node is a display: contents node, and also to avoid computing
  * the style for text children more than once per element.
  */
 struct ServoRestyleManager::TextPostTraversalState
 {
-  nsStyleContext& mParentContext;
-  ServoStyleSet& mStyleSet;
-  RefPtr<nsStyleContext> mStyle;
-  bool mShouldPostHints;
-  bool mShouldComputeHints;
-  nsChangeHint mComputedHint;
-  nsChangeHint mHintsHandled;
-
+public:
   TextPostTraversalState(nsStyleContext& aParentContext,
-                         ServoStyleSet& aStyleSet,
                          bool aDisplayContentsParentStyleChanged,
-                         nsChangeHint aHintsHandled)
+                         ServoRestyleState& aParentRestyleState)
     : mParentContext(aParentContext)
-    , mStyleSet(aStyleSet)
+    , mParentRestyleState(aParentRestyleState)
     , mStyle(nullptr)
     , mShouldPostHints(aDisplayContentsParentStyleChanged)
     , mShouldComputeHints(aDisplayContentsParentStyleChanged)
     , mComputedHint(nsChangeHint_Empty)
-    , mHintsHandled(aHintsHandled)
   {}
 
+  nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
+
   nsStyleContext& ComputeStyle(nsIContent* aTextNode)
   {
     if (!mStyle) {
-      mStyle = mStyleSet.ResolveStyleForText(aTextNode, &mParentContext);
+      mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
+        aTextNode, &mParentContext);
     }
     MOZ_ASSERT(mStyle);
     return *mStyle;
   }
 
   void ComputeHintIfNeeded(nsIContent* aContent,
                            nsIFrame* aTextFrame,
-                           nsStyleContext& aNewContext,
-                           nsStyleChangeList& aChangeList)
+                           nsStyleContext& aNewContext)
   {
     MOZ_ASSERT(aTextFrame);
     MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
 
     if (MOZ_LIKELY(!mShouldPostHints)) {
       return;
     }
 
@@ -233,46 +226,53 @@ struct ServoRestyleManager::TextPostTrav
     // we'll cross that bridge when we support those in stylo.
     if (mShouldComputeHints) {
       mShouldComputeHints = false;
       uint32_t equalStructs, samePointerStructs;
       mComputedHint =
         oldContext->CalcStyleDifference(&aNewContext,
                                         &equalStructs,
                                         &samePointerStructs);
-      mComputedHint = NS_RemoveSubsumedHints(mComputedHint, mHintsHandled);
+      mComputedHint = NS_RemoveSubsumedHints(
+        mComputedHint, mParentRestyleState.ChangesHandled());
     }
 
     if (mComputedHint) {
-      aChangeList.AppendChange(aTextFrame, aContent, mComputedHint);
+      mParentRestyleState.ChangeList().AppendChange(
+        aTextFrame, aContent, mComputedHint);
     }
   }
+
+private:
+  nsStyleContext& mParentContext;
+  ServoRestyleState& mParentRestyleState;
+  RefPtr<nsStyleContext> mStyle;
+  bool mShouldPostHints;
+  bool mShouldComputeHints;
+  nsChangeHint mComputedHint;
 };
 
 static void
 UpdateBlockFramePseudoElements(nsBlockFrame* aFrame,
-                               ServoStyleSet& aStyleSet,
-                               nsStyleChangeList& aChangeList)
+                               ServoRestyleState& aRestyleState)
 {
   if (nsBulletFrame* bullet = aFrame->GetBullet()) {
     RefPtr<nsStyleContext> newContext =
-      aStyleSet.ResolvePseudoElementStyle(
-          aFrame->GetContent()->AsElement(),
-          bullet->StyleContext()->GetPseudoType(),
-          aFrame->StyleContext(),
-          /* aPseudoElement = */ nullptr);
+      aRestyleState.StyleSet().ResolvePseudoElementStyle(
+        aFrame->GetContent()->AsElement(),
+        bullet->StyleContext()->GetPseudoType(),
+        aFrame->StyleContext(),
+        /* aPseudoElement = */ nullptr);
 
-    aFrame->UpdateStyleOfOwnedChildFrame(bullet, newContext, aChangeList);
+    aFrame->UpdateStyleOfOwnedChildFrame(bullet, newContext, aRestyleState);
   }
 }
 
 static void
-UpdateBackdropIfNeeded(nsIFrame* aFrame,
-                       ServoStyleSet& aStyleSet,
-                       nsStyleChangeList& aChangeList)
+UpdateBackdropIfNeeded(nsIFrame* aFrame, ServoRestyleState& aRestyleState)
 {
   const nsStyleDisplay* display = aFrame->StyleContext()->StyleDisplay();
   if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
     return;
   }
 
   // Elements in the top layer are guaranteed to have absolute or fixed
   // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
@@ -287,71 +287,66 @@ UpdateBackdropIfNeeded(nsIFrame* aFrame,
   MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
   nsIFrame* backdropFrame =
     nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
   MOZ_ASSERT(backdropFrame->IsBackdropFrame());
   MOZ_ASSERT(backdropFrame->StyleContext()->GetPseudoType() ==
              CSSPseudoElementType::backdrop);
 
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolvePseudoElementStyle(
-        aFrame->GetContent()->AsElement(),
-        CSSPseudoElementType::backdrop,
-        aFrame->StyleContext(),
-        /* aPseudoElement = */ nullptr);
+    aRestyleState.StyleSet().ResolvePseudoElementStyle(
+      aFrame->GetContent()->AsElement(),
+      CSSPseudoElementType::backdrop,
+      aFrame->StyleContext(),
+      /* aPseudoElement = */ nullptr);
 
-  aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame,
-                                       newContext,
-                                       aChangeList);
+  aFrame->UpdateStyleOfOwnedChildFrame(
+    backdropFrame, newContext, aRestyleState);
 }
 
 static void
 UpdateFramePseudoElementStyles(nsIFrame* aFrame,
-                               ServoStyleSet& aStyleSet,
-                               nsStyleChangeList& aChangeList)
+                               ServoRestyleState& aRestyleState)
 {
   if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
     UpdateBlockFramePseudoElements(static_cast<nsBlockFrame*>(aFrame),
-                                   aStyleSet,
-                                   aChangeList);
+                                   aRestyleState);
   }
 
-  UpdateBackdropIfNeeded(aFrame, aStyleSet, aChangeList);
+  UpdateBackdropIfNeeded(aFrame, aRestyleState);
 }
 
 bool
 ServoRestyleManager::ProcessPostTraversal(Element* aElement,
                                           nsStyleContext* aParentContext,
-                                          ServoStyleSet* aStyleSet,
-                                          nsStyleChangeList& aChangeList,
-                                          nsChangeHint aChangesHandled)
+                                          ServoRestyleState& aRestyleState)
 {
   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
 
   // Grab the change hint from Servo.
   nsChangeHint changeHint = Servo_TakeChangeHint(aElement);
-  changeHint = NS_RemoveSubsumedHints(changeHint, aChangesHandled);
-  aChangesHandled |= changeHint;
+  changeHint =
+    NS_RemoveSubsumedHints(changeHint, aRestyleState.ChangesHandled());
 
   // Handle lazy frame construction by posting a reconstruct for any lazily-
   // constructed roots.
   if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
     changeHint |= nsChangeHint_ReconstructFrame;
     // The only time the primary frame is non-null is when image maps do hacky
     // SetPrimaryFrame calls.
     MOZ_ASSERT(!styleFrame || styleFrame->IsImageFrame());
     styleFrame = nullptr;
   }
 
   // Although we shouldn't generate non-ReconstructFrame hints for elements with
   // no frames, we can still get them here if they were explicitly posted by
   // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
   // :visited.  Skip processing these hints if there is no frame.
   if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
-    aChangeList.AppendChange(styleFrame, aElement, changeHint);
+    aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
   }
 
   // If our change hint is reconstruct, we delegate to the frame constructor,
   // which consumes the new style and expects the old style to be on the frame.
   //
   // XXXbholley: We should teach the frame constructor how to clear the dirty
   // descendants bit to avoid the traversal here.
   if (changeHint & nsChangeHint_ReconstructFrame) {
@@ -376,45 +371,43 @@ ServoRestyleManager::ProcessPostTraversa
     displayContentsNode =
       PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
     if (displayContentsNode) {
       oldStyleContext = displayContentsNode->mStyle;
     }
   }
 
   RefPtr<ServoComputedValues> computedValues =
-    aStyleSet->ResolveServoStyle(aElement);
+    aRestyleState.StyleSet().ResolveServoStyle(aElement);
 
   // Note that we rely in the fact that we don't cascade pseudo-element styles
   // separately right now (that is, if a pseudo style changes, the normal style
   // changes too).
   //
   // Otherwise we should probably encode that information somehow to avoid
   // expensive checks in the common case.
   //
   // Also, we're going to need to check for pseudos of display: contents
   // elements, though that is buggy right now even in non-stylo mode, see
   // bug 1251799.
   const bool recreateContext = oldStyleContext &&
     oldStyleContext->ComputedValues() != computedValues;
 
+  ServoRestyleState childrenRestyleState(aRestyleState, changeHint);
+
   RefPtr<nsStyleContext> newContext = nullptr;
   if (recreateContext) {
     MOZ_ASSERT(styleFrame || displayContentsNode);
 
     auto pseudo = aElement->GetPseudoElementType();
     nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
       ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
 
-    newContext =
-      aStyleSet->GetContext(computedValues.forget(),
-                            aParentContext,
-                            pseudoTag,
-                            pseudo,
-                            aElement);
+    newContext = aRestyleState.StyleSet().GetContext(
+      computedValues.forget(), aParentContext, pseudoTag, pseudo, aElement);
 
     newContext->EnsureSameStructsCached(oldStyleContext);
 
     // XXX This could not always work as expected: there are kinds of content
     // with the first split and the last sharing style, but others not. We
     // should handle those properly.
     // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_
     // right, but not other cases where we happen to have different styles on
@@ -425,99 +418,95 @@ ServoRestyleManager::ProcessPostTraversa
     }
 
     if (MOZ_UNLIKELY(displayContentsNode)) {
       MOZ_ASSERT(!styleFrame);
       displayContentsNode->mStyle = newContext;
     }
 
     if (styleFrame) {
-      styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint);
-      UpdateFramePseudoElementStyles(styleFrame, *aStyleSet, aChangeList);
+      styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
+      UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
     }
 
     if (!aElement->GetParent()) {
       // This is the root.  Update styles on the viewport as needed.
       ViewportFrame* viewport =
         do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
       if (viewport) {
-        viewport->UpdateStyle(*aStyleSet, aChangeList);
+        viewport->UpdateStyle(childrenRestyleState);
       }
     }
 
     // Some changes to animations don't affect the computed style and yet still
     // require the layer to be updated. For example, pausing an animation via
     // the Web Animations API won't affect an element's style but still
     // requires to update the animation on the layer.
     //
     // We can sometimes reach this when the animated style is being removed.
     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
     // style or not, we need to call it *after* setting |newContext| to
     // |styleFrame| to ensure the animated transform has been removed first.
-    AddLayerChangesForAnimation(styleFrame, aElement, aChangeList);
+    AddLayerChangesForAnimation(
+      styleFrame, aElement, aRestyleState.ChangeList());
   }
 
   const bool descendantsNeedFrames =
     aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   const bool traverseElementChildren =
     aElement->HasDirtyDescendantsForServo() ||
     aElement->HasAnimationOnlyDirtyDescendantsForServo() ||
     descendantsNeedFrames;
   const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
   bool recreatedAnyContext = recreateContext;
   if (traverseElementChildren || traverseTextChildren) {
     nsStyleContext* upToDateContext =
       recreateContext ? newContext : oldStyleContext;
 
     StyleChildrenIterator it(aElement);
-    TextPostTraversalState textState(
-        *upToDateContext,
-        *aStyleSet,
-        displayContentsNode && recreateContext,
-        aChangesHandled);
+    TextPostTraversalState textState(*upToDateContext,
+                                     displayContentsNode && recreateContext,
+                                     childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
-        recreatedAnyContext |=
-          ProcessPostTraversal(n->AsElement(), upToDateContext,
-                               aStyleSet, aChangeList, aChangesHandled);
+        recreatedAnyContext |= ProcessPostTraversal(
+          n->AsElement(), upToDateContext, childrenRestyleState);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
-        recreatedAnyContext |=
-          ProcessPostTraversalForText(n, aChangeList, textState);
+        recreatedAnyContext |= ProcessPostTraversalForText(n, textState);
       }
     }
   }
 
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
   aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
   return recreatedAnyContext;
 }
 
 bool
 ServoRestyleManager::ProcessPostTraversalForText(
     nsIContent* aTextNode,
-    nsStyleChangeList& aChangeList,
     TextPostTraversalState& aPostTraversalState)
 {
   // Handle lazy frame construction.
   if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
-    aChangeList.AppendChange(nullptr, aTextNode, nsChangeHint_ReconstructFrame);
+    aPostTraversalState.ChangeList().AppendChange(
+      nullptr, aTextNode, nsChangeHint_ReconstructFrame);
     return true;
   }
 
   // Handle restyle.
   nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
   if (!primaryFrame) {
     return false;
   }
 
   RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
   nsStyleContext& newContext = aPostTraversalState.ComputeStyle(aTextNode);
-  aPostTraversalState.ComputeHintIfNeeded(
-      aTextNode, primaryFrame, newContext, aChangeList);
+  aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newContext);
 
   for (nsIFrame* f = primaryFrame; f;
        f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
     f->SetStyleContext(&newContext);
   }
 
   return true;
 }
@@ -637,19 +626,18 @@ ServoRestyleManager::DoProcessPendingRes
     }
 
     // Recreate style contexts, and queue up change hints (which also handle
     // lazy frame construction).
     nsStyleChangeList currentChanges(StyleBackendType::Servo);
     DocumentStyleRootIterator iter(doc);
     bool anyStyleChanged = false;
     while (Element* root = iter.GetNextStyleRoot()) {
-      anyStyleChanged |=
-        ProcessPostTraversal(
-            root, nullptr, styleSet, currentChanges, nsChangeHint(0));
+      ServoRestyleState state(*styleSet, currentChanges);
+      anyStyleChanged |= ProcessPostTraversal(root, nullptr, state);
     }
 
     // Process the change hints.
     //
     // Unfortunately, the frame constructor can generate new change hints while
     // processing existing ones. We redirect those into a secondary queue and
     // iterate until there's nothing left.
     ReentrantChangeList newChanges;
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -23,16 +23,46 @@ class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
 class nsStyleChangeList;
 
 namespace mozilla {
 
 /**
+ * A stack class used to pass some common restyle state in a slightly more
+ * comfortable way than a bunch of individual arguments.
+ */
+class ServoRestyleState
+{
+public:
+  ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList)
+    : mStyleSet(aStyleSet)
+    , mChangeList(aChangeList)
+    , mChangesHandled(nsChangeHint(0))
+  {}
+
+  ServoRestyleState(ServoRestyleState& aParentState,
+                    nsChangeHint aHintForThisFrame)
+    : mStyleSet(aParentState.mStyleSet)
+    , mChangeList(aParentState.mChangeList)
+    , mChangesHandled(aParentState.mChangesHandled | aHintForThisFrame)
+  {}
+
+  nsChangeHint ChangesHandled() const { return mChangesHandled; }
+  nsStyleChangeList& ChangeList() { return mChangeList; }
+  ServoStyleSet& StyleSet() { return mStyleSet; }
+
+private:
+  ServoStyleSet& mStyleSet;
+  nsStyleChangeList& mChangeList;
+  const nsChangeHint mChangesHandled;
+};
+
+/**
  * Restyle manager for a Servo-backed style system.
  */
 class ServoRestyleManager : public RestyleManager
 {
   friend class ServoStyleSet;
 
 public:
   typedef ServoElementSnapshotTable SnapshotTable;
@@ -122,23 +152,20 @@ private:
    *
    * Returns whether any style did actually change. There may be cases where we
    * didn't need to change any style after all, for example, when a content
    * attribute changes that happens not to have any effect on the style of that
    * element or any descendant or sibling.
    */
   bool ProcessPostTraversal(Element* aElement,
                             nsStyleContext* aParentContext,
-                            ServoStyleSet* aStyleSet,
-                            nsStyleChangeList& aChangeList,
-                            nsChangeHint aChangesHandledForDescendants);
+                            ServoRestyleState& aRestyleState);
 
   struct TextPostTraversalState;
   bool ProcessPostTraversalForText(nsIContent* aTextNode,
-                                   nsStyleChangeList& aChangeList,
                                    TextPostTraversalState& aState);
 
   inline ServoStyleSet* StyleSet() const
   {
     MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
                "ServoRestyleManager should only be used with a Servo-flavored "
                "style backend");
     return PresContext()->StyleSet()->AsServo();
--- a/layout/forms/nsITextControlFrame.h
+++ b/layout/forms/nsITextControlFrame.h
@@ -3,32 +3,35 @@
  * 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 nsITextControlFrame_h___
 #define nsITextControlFrame_h___
  
 #include "nsIFormControlFrame.h"
 
-class nsIEditor;
 class nsISelectionController;
 class nsFrameSelection;
 
+namespace mozilla {
+class TextEditor;
+} // namespace mozilla
+
 class nsITextControlFrame : public nsIFormControlFrame
 {
 public:
   NS_DECL_QUERYFRAME_TARGET(nsITextControlFrame)
 
   enum SelectionDirection {
     eNone,
     eForward,
     eBackward
   };
 
-  NS_IMETHOD    GetEditor(nsIEditor **aEditor) = 0;
+  NS_IMETHOD_(already_AddRefed<mozilla::TextEditor>) GetTextEditor() = 0;
 
   NS_IMETHOD    SetSelectionRange(uint32_t aSelectionStart,
                                   uint32_t aSelectionEnd,
                                   SelectionDirection aDirection = eNone) = 0;
 
   NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon) = 0;
   virtual nsFrameSelection* GetOwnedFrameSelection() = 0;
 
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -719,29 +719,27 @@ nsresult nsTextControlFrame::SetFormProp
         return NS_OK;
       }
     }
     mIsProcessing = false;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsTextControlFrame::GetEditor(nsIEditor **aEditor)
+NS_IMETHODIMP_(already_AddRefed<TextEditor>)
+nsTextControlFrame::GetTextEditor()
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-
-  nsresult rv = EnsureEditorInitialized();
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(EnsureEditorInitialized()))) {
+    return nullptr;
+  }
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
-  NS_ASSERTION(txtCtrl, "Content not a text control element");
-  *aEditor = txtCtrl->GetTextEditor();
-  NS_IF_ADDREF(*aEditor);
-  return NS_OK;
+  MOZ_ASSERT(txtCtrl, "Content not a text control element");
+  RefPtr<TextEditor> textEditor = txtCtrl->GetTextEditor();
+  return textEditor.forget();
 }
 
 nsresult
 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
                                          uint32_t aStartOffset,
                                          nsIDOMNode *aEndNode,
                                          uint32_t aEndOffset,
                                          nsITextControlFrame::SelectionDirection aDirection)
@@ -810,22 +808,21 @@ nsTextControlFrame::ScrollSelectionIntoV
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
 {
   NS_ENSURE_ARG_POINTER(aRootElement);
 
-  nsCOMPtr<nsIEditor> editor;
-  GetEditor(getter_AddRefs(editor));
-  if (!editor)
+  RefPtr<TextEditor> textEditor = GetTextEditor();
+  if (!textEditor) {
     return NS_OK;
-
-  return editor->GetRootElement(aRootElement);
+  }
+  return textEditor->GetRootElement(aRootElement);
 }
 
 nsresult
 nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect)
 {
   nsCOMPtr<nsIDOMElement> rootElement;
   nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -984,76 +981,72 @@ nsTextControlFrame::AttributeChanged(int
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
   nsISelectionController* selCon = txtCtrl->GetSelectionController();
   const bool needEditor = nsGkAtoms::maxlength == aAttribute ||
                             nsGkAtoms::readonly == aAttribute ||
                             nsGkAtoms::disabled == aAttribute ||
                             nsGkAtoms::spellcheck == aAttribute;
-  nsCOMPtr<nsIEditor> editor;
-  if (needEditor) {
-    GetEditor(getter_AddRefs(editor));
-  }
-  if ((needEditor && !editor) || !selCon) {
+  RefPtr<TextEditor> textEditor = needEditor ? GetTextEditor() : nullptr;
+  if ((needEditor && !textEditor) || !selCon) {
     return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
   }
 
   if (nsGkAtoms::maxlength == aAttribute) {
     int32_t maxLength;
     bool maxDefined = GetMaxLength(&maxLength);
-    nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor);
     if (textEditor) {
       if (maxDefined) { // set the maxLength attribute
         textEditor->SetMaxTextLength(maxLength);
         // if maxLength>docLength, we need to truncate the doc content
       } else { // unset the maxLength attribute
         textEditor->SetMaxTextLength(-1);
       }
     }
     return NS_OK;
   }
 
   if (nsGkAtoms::readonly == aAttribute) {
     uint32_t flags;
-    editor->GetFlags(&flags);
+    textEditor->GetFlags(&flags);
     if (AttributeExists(nsGkAtoms::readonly)) { // set readonly
       flags |= nsIPlaintextEditor::eEditorReadonlyMask;
       if (nsContentUtils::IsFocusedContent(mContent)) {
         selCon->SetCaretEnabled(false);
       }
     } else { // unset readonly
       flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
-      if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
+      if (!textEditor->IsDisabled() &&
           nsContentUtils::IsFocusedContent(mContent)) {
         selCon->SetCaretEnabled(true);
       }
     }
-    editor->SetFlags(flags);
+    textEditor->SetFlags(flags);
     return NS_OK;
   }
 
   if (nsGkAtoms::disabled == aAttribute) {
     uint32_t flags;
-    editor->GetFlags(&flags);
+    textEditor->GetFlags(&flags);
     int16_t displaySelection = nsISelectionController::SELECTION_OFF;
     const bool focused = nsContentUtils::IsFocusedContent(mContent);
     const bool hasAttr = AttributeExists(nsGkAtoms::disabled);
     if (hasAttr) { // set disabled
       flags |= nsIPlaintextEditor::eEditorDisabledMask;
     } else { // unset disabled
       flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
       displaySelection = focused ? nsISelectionController::SELECTION_ON
                                  : nsISelectionController::SELECTION_HIDDEN;
     }
     selCon->SetDisplaySelection(displaySelection);
     if (focused) {
       selCon->SetCaretEnabled(!hasAttr);
     }
-    editor->SetFlags(flags);
+    textEditor->SetFlags(flags);
     return NS_OK;
   }
 
   if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) {
     UpdateValueDisplay(true);
     return NS_OK;
   }
 
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -12,18 +12,18 @@
 #include "nsIContent.h"
 #include "nsITextControlFrame.h"
 #include "nsITextControlElement.h"
 #include "nsIStatefulFrame.h"
 
 class nsISelectionController;
 class EditorInitializerEntryTracker;
 class nsTextEditorState;
-class nsIEditor;
 namespace mozilla {
+class TextEditor;
 enum class CSSPseudoElementType : uint8_t;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 class nsTextControlFrame final : public nsContainerFrame,
                                  public nsIAnonymousContentCreator,
@@ -134,17 +134,17 @@ public:
 //==== BEGIN NSIFORMCONTROLFRAME
   virtual void SetFocus(bool aOn , bool aRepaint) override;
   virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
 
 //==== END NSIFORMCONTROLFRAME
 
 //==== NSITEXTCONTROLFRAME
 
-  NS_IMETHOD    GetEditor(nsIEditor **aEditor) override;
+  NS_IMETHOD_(already_AddRefed<mozilla::TextEditor>) GetTextEditor() override;
   NS_IMETHOD    SetSelectionRange(uint32_t aSelectionStart,
                                   uint32_t aSelectionEnd,
                                   SelectionDirection aDirection = eNone) override;
   NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon) override;
   virtual nsFrameSelection* GetOwnedFrameSelection() override;
 
   /**
    * Ensure mEditor is initialized with the proper flags and the default value.
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -5,16 +5,17 @@
 
 /*
  * rendering object that is the root of the frame tree, which contains
  * the document's scrollbars and contains fixed-positioned elements
  */
 
 #include "mozilla/ViewportFrame.h"
 
+#include "mozilla/ServoRestyleManager.h"
 #include "nsGkAtoms.h"
 #include "nsIScrollableFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "GeckoProfiler.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsPlaceholderFrame.h"
@@ -411,34 +412,33 @@ ViewportFrame::ComputeCustomOverflow(nsO
   if (rootScrollFrame && !rootScrollFrame->IsIgnoringViewportClipping()) {
     return false;
   }
 
   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
 }
 
 void
-ViewportFrame::UpdateStyle(ServoStyleSet& aStyleSet,
-                           nsStyleChangeList& aChangeList)
+ViewportFrame::UpdateStyle(ServoRestyleState& aRestyleState)
 {
   nsStyleContext* oldContext = StyleContext();
   nsIAtom* pseudo = oldContext->GetPseudo();
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(pseudo, nullptr);
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo, nullptr);
 
   // We're special because we have a null GetContent(), so don't call things
   // like UpdateStyleOfOwnedChildFrame that try to append changes for the
   // content to the change list.  Nor do we computed a changehint, since we have
   // no way to apply it anyway.
   newContext->EnsureSameStructsCached(oldContext);
 
   MOZ_ASSERT(!GetNextContinuation(), "Viewport has continuations?");
   SetStyleContext(newContext);
 
-  UpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, nsChangeHint_Empty);
+  UpdateStyleOfOwnedAnonBoxes(aRestyleState);
 }
 
 void
 ViewportFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
 {
   if (mFrames.NotEmpty()) {
     aResult.AppendElement(mFrames.FirstChild());
   }
--- a/layout/generic/ViewportFrame.h
+++ b/layout/generic/ViewportFrame.h
@@ -13,16 +13,18 @@
 
 #include "mozilla/Attributes.h"
 #include "nsContainerFrame.h"
 
 class nsPresContext;
 
 namespace mozilla {
 
+class ServoRestyleState;
+
 /**
   * ViewportFrame is the parent of a single child - the doc root frame or a scroll frame
   * containing the doc root frame. ViewportFrame stores this child in its primary child
   * list.
   */
 class ViewportFrame : public nsContainerFrame {
 public:
   NS_DECL_QUERYFRAME
@@ -71,17 +73,17 @@ public:
    * @return the rect to use as containing block rect
    */
   nsRect AdjustReflowInputAsContainingBlock(ReflowInput* aReflowInput) const;
 
   /**
    * Update our style (and recursively the styles of any anonymous boxes we
    * might own)
    */
-  void UpdateStyle(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList);
+  void UpdateStyle(ServoRestyleState& aStyleSet);
 
   /**
    * Return our single anonymous box child.
    */
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10197,19 +10197,17 @@ nsFrame::BoxMetrics() const
 {
   nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
   NS_ASSERTION(metrics, "A box layout method was called but InitBoxMetrics was never called");
   return metrics;
 }
 
 void
 nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
-                                    ServoStyleSet& aStyleSet,
-                                    nsStyleChangeList& aChangeList,
-                                    nsChangeHint aHintForThisFrame)
+                                    ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(aChildFrame->GetParent() == this,
              "This should only be used for children!");
   MOZ_ASSERT((!GetContent() && IsViewportFrame()) ||
              aChildFrame->GetContent() == GetContent(),
              "What content node is it a frame for?");
   MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
              "Only first continuations should end up here");
@@ -10218,53 +10216,58 @@ nsIFrame::UpdateStyleOfChildAnonBox(nsIF
   // statically...  But this API is a bit nicer.
   nsIAtom* pseudo = aChildFrame->StyleContext()->GetPseudo();
   MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(pseudo), "Child is not an anon box?");
   MOZ_ASSERT(!nsCSSAnonBoxes::IsNonInheritingAnonBox(pseudo),
              "Why did the caller bother calling us?");
 
   // Anon boxes inherit from their parent; that's us.
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(pseudo, StyleContext());
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
+                                                                StyleContext());
 
   nsChangeHint childHint =
-    UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aChangeList);
+    UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
 
   // Now that we've updated the style on aChildFrame, check whether it itself
   // has anon boxes to deal with.
-  aChildFrame->UpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, childHint);
+  ServoRestyleState childrenState(aRestyleState, childHint);
+  aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
 }
 
 nsChangeHint
 nsIFrame::UpdateStyleOfOwnedChildFrame(nsIFrame* aChildFrame,
                                        nsStyleContext* aNewStyleContext,
-                                       nsStyleChangeList& aChangeList)
+                                       ServoRestyleState& aRestyleState)
 {
   // Figure out whether we have an actual change.  It's important that we do
   // this, for several reasons:
   //
   // 1) Even if all the child's changes are due to properties it inherits from
   //    us, it's possible that no one ever asked us for those style structs and
   //    hence changes to them aren't reflected in aHintForThisFrame at all.
   //
   // 2) Content can change stylesheets that change the styles of pseudos, and
   //    extensions can add/remove stylesheets that change the styles of
   //    anonymous boxes directly.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint childHint = aChildFrame->StyleContext()->CalcStyleDifference(
     aNewStyleContext,
     &equalStructs,
     &samePointerStructs);
+  childHint = NS_RemoveSubsumedHints(childHint, aRestyleState.ChangesHandled());
   if (childHint) {
     if (childHint & nsChangeHint_ReconstructFrame) {
       // If we generate a reconstruct here, remove any non-reconstruct hints we
       // may have already generated for this content.
-      aChangeList.PopChangesForContent(aChildFrame->GetContent());
-    }
-    aChangeList.AppendChange(aChildFrame, aChildFrame->GetContent(), childHint);
+      aRestyleState.ChangeList().PopChangesForContent(
+        aChildFrame->GetContent());
+    }
+    aRestyleState.ChangeList().AppendChange(
+      aChildFrame, aChildFrame->GetContent(), childHint);
   }
 
   for (nsIFrame* kid = aChildFrame; kid; kid = kid->GetNextContinuation()) {
     kid->SetStyleContext(aNewStyleContext);
   }
 
   return childHint;
 }
@@ -10556,49 +10559,44 @@ nsIFrame::UpdateWidgetProperties()
   }
   if (nsCOMPtr<nsIWidget> widget = GetWindowWidget(presContext)) {
     widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
     widget->SetWindowTransform(ComputeWidgetTransform());
   }
 }
 
 void
-nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
-                                        nsStyleChangeList& aChangeList,
-                                        nsChangeHint aHintForThisFrame)
+nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState)
 {
   // As a special case, we check for {ib}-split block frames here, rather
   // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
   // that returns them.
   //
   // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
   // return *all* of the in-flow {ib}-split block frames, not just the first
   // one.  For restyling, we really just need the first in flow, and the other
   // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
   // know about them at all, since these block frames never create NAC.  So we
   // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
   // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
   if (IsInlineFrame()) {
     if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
-      static_cast<nsInlineFrame*>(this)->
-        UpdateStyleOfOwnedAnonBoxesForIBSplit(aStyleSet, aChangeList,
-                                              aHintForThisFrame);
+      static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
+        aRestyleState);
     }
     return;
   }
 
   AutoTArray<OwnedAnonBox,4> frames;
   AppendDirectlyOwnedAnonBoxes(frames);
   for (OwnedAnonBox& box : frames) {
     if (box.mUpdateStyleFn) {
-      box.mUpdateStyleFn(this, box.mAnonBoxFrame,
-                         aStyleSet, aChangeList, aHintForThisFrame);
+      box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
     } else {
-      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame,
-                                aStyleSet, aChangeList, aHintForThisFrame);
+      UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
     }
   }
 }
 
 /* virtual */ void
 nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
 {
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES));
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -94,17 +94,17 @@ struct nsMargin;
 struct CharacterDataChangeInfo;
 
 namespace mozilla {
 
 enum class CSSPseudoElementType : uint8_t;
 class EventStates;
 struct ReflowInput;
 class ReflowOutput;
-class ServoStyleSet;
+class ServoRestyleState;
 class DisplayItemData;
 class EffectSet;
 
 namespace layers {
 class Layer;
 } // namespace layers
 
 namespace gfx {
@@ -3286,69 +3286,65 @@ public:
    * @return The style context that should be the parent of this frame's
    *         style context.  Null is permitted, and means that this frame's
    *         style context should be the root of the style context tree.
    */
   virtual nsStyleContext* GetParentStyleContext(nsIFrame** aProviderFrame) const = 0;
 
   /**
    * Called by ServoRestyleManager to update the style contexts of anonymous
-   * boxes directly associtated with this frame.  The passed-in ServoStyleSet
-   * can be used to create new style contexts as needed.
+   * boxes directly associtated with this frame.
+   *
+   * The passed-in ServoRestyleState can be used to create new style contexts
+   * as needed, as well as posting changes to the change list.
+   *
+   * It's guaranteed to already have a change in it for this frame and this
+   * frame's content.
    *
    * This function will be called after this frame's style context has already
    * been updated.  This function will only be called on frames which have the
    * NS_FRAME_OWNS_ANON_BOXES bit set.
-   *
-   * The nsStyleChangeList can be used to append additional changes.  It's
-   * guaranteed to already have a change in it for this frame and this frame's
-   * content and a change hint of aHintForThisFrame.
-   */
-  void UpdateStyleOfOwnedAnonBoxes(mozilla::ServoStyleSet& aStyleSet,
-                                   nsStyleChangeList& aChangeList,
-                                   nsChangeHint aHintForThisFrame) {
+   */
+  void UpdateStyleOfOwnedAnonBoxes(mozilla::ServoRestyleState& aRestyleState)
+  {
     if (GetStateBits() & NS_FRAME_OWNS_ANON_BOXES) {
-      DoUpdateStyleOfOwnedAnonBoxes(aStyleSet, aChangeList, aHintForThisFrame);
+      DoUpdateStyleOfOwnedAnonBoxes(aRestyleState);
     }
   }
 
 protected:
   // This does the actual work of UpdateStyleOfOwnedAnonBoxes.  It calls
   // AppendDirectlyOwnedAnonBoxes to find all of the anonymous boxes
   // owned by this frame, and then updates styles on each of them.
-  void DoUpdateStyleOfOwnedAnonBoxes(mozilla::ServoStyleSet& aStyleSet,
-                                     nsStyleChangeList& aChangeList,
-                                     nsChangeHint aHintForThisFrame);
+  void DoUpdateStyleOfOwnedAnonBoxes(mozilla::ServoRestyleState& aRestyleState);
 
   // A helper for DoUpdateStyleOfOwnedAnonBoxes for the specific case
   // of the owned anon box being a child of this frame.
   void UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
-                                 mozilla::ServoStyleSet& aStyleSet,
-                                 nsStyleChangeList& aChangeList,
-                                 nsChangeHint aHintForThisFrame);
+                                 mozilla::ServoRestyleState& aRestyleState);
 
 public:
   // A helper both for UpdateStyleOfChildAnonBox, and to update frame-backed
   // pseudo-elements in ServoRestyleManager.
   //
   // This gets a style context that will be the new style context for
   // `aChildFrame`, and takes care of updating it, calling CalcStyleDifference,
   // and adding to the change list as appropriate.
   //
   // Returns the generated change hint for the frame.
   nsChangeHint UpdateStyleOfOwnedChildFrame(
-      nsIFrame* aChildFrame,
-      nsStyleContext* aNewStyleContext,
-      nsStyleChangeList& aChangeList);
+    nsIFrame* aChildFrame,
+    nsStyleContext* aNewStyleContext,
+    mozilla::ServoRestyleState& aRestyleState);
 
   struct OwnedAnonBox
   {
-    typedef void (*UpdateStyleFn)(nsIFrame* aOwningFrame, nsIFrame* aAnonBox,
-                                  mozilla::ServoStyleSet&,
-                                  nsStyleChangeList&, nsChangeHint);
+    typedef void (*UpdateStyleFn)(nsIFrame* aOwningFrame,
+                                  nsIFrame* aAnonBox,
+                                  mozilla::ServoRestyleState& aRestyleState);
 
     explicit OwnedAnonBox(nsIFrame* aAnonBoxFrame,
                           UpdateStyleFn aUpdateStyleFn = nullptr)
       : mAnonBoxFrame(aAnonBoxFrame)
       , mUpdateStyleFn(aUpdateStyleFn)
     {}
 
     nsIFrame* mAnonBoxFrame;
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1010,19 +1010,17 @@ nsInlineFrame::AccessibleType()
     return a11y::eHyperTextType;
 
   return a11y::eNoType;
 }
 #endif
 
 void
 nsInlineFrame::UpdateStyleOfOwnedAnonBoxesForIBSplit(
-    ServoStyleSet& aStyleSet,
-    nsStyleChangeList& aChangeList,
-    nsChangeHint aHintForThisFrame)
+  ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES,
              "Why did we get called?");
   MOZ_ASSERT(GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
              "Why did we have the NS_FRAME_OWNS_ANON_BOXES bit set?");
   // Note: this assert _looks_ expensive, but it's cheap in all the cases when
   // it passes!
   MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(this) == this,
@@ -1032,23 +1030,23 @@ nsInlineFrame::UpdateStyleOfOwnedAnonBox
              "We should be the primary frame for our element");
 
   nsIFrame* blockFrame = GetProperty(nsIFrame::IBSplitSibling());
   MOZ_ASSERT(blockFrame, "Why did we have an IB split?");
 
   // The anonymous block's style inherits from ours, and we already have our new
   // style context.
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
       nsCSSAnonBoxes::mozBlockInsideInlineWrapper, StyleContext());
 
   // We're guaranteed that newContext only differs from the old style context on
   // the block in things they might inherit from us.  And changehint processing
   // guarantees walking the continuation and ib-sibling chains, so our existing
-  // changehint beign in aChangeList is good enough.  So we don't need to touch
+  // changehint being in aChangeList is good enough.  So we don't need to touch
   // aChangeList at all here.
 
   while (blockFrame) {
     MOZ_ASSERT(!blockFrame->GetPrevContinuation(),
                "Must be first continuation");
 
     MOZ_ASSERT(blockFrame->StyleContext()->GetPseudo() ==
                nsCSSAnonBoxes::mozBlockInsideInlineWrapper,
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -112,19 +112,17 @@ public:
     return (GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET)
              ? !!(GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST)
              : (!GetNextInFlow());
   }
 
   // Restyles the block wrappers around our non-inline-outside kids.
   // This will only be called when such wrappers in fact exist.
   void UpdateStyleOfOwnedAnonBoxesForIBSplit(
-      mozilla::ServoStyleSet& aStyleSet,
-      nsStyleChangeList& aChangeList,
-      nsChangeHint aHintForThisFrame);
+    mozilla::ServoRestyleState& aRestyleState);
 
 protected:
   // Additional reflow state used during our reflow methods
   struct InlineReflowInput {
     nsIFrame* mPrevFrame;
     nsInlineFrame* mNextInFlow;
     nsIFrame*      mLineContainer;
     nsLineLayout*  mLineLayout;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -34,16 +34,17 @@
 #include "nsCSSAnonBoxes.h"
 #include "nsIPresShell.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIScriptError.h"
 #include "nsFrameManager.h"
 #include "nsError.h"
 #include "nsCSSFrameConstructor.h"
+#include "mozilla/ServoRestyleManager.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSProps.h"
 #include "RestyleTracker.h"
 #include "nsStyleChangeList.h"
@@ -8008,43 +8009,43 @@ nsTableFrame::AppendDirectlyOwnedAnonBox
                nsCSSAnonBoxes::tableWrapper,
              "What happened to our parent?");
   aResult.AppendElement(
     OwnedAnonBox(wrapper, &UpdateStyleOfOwnedAnonBoxesForTableWrapper));
 }
 
 /* static */ void
 nsTableFrame::UpdateStyleOfOwnedAnonBoxesForTableWrapper(
-    nsIFrame* aOwningFrame,
-    nsIFrame* aWrapperFrame,
-    ServoStyleSet& aStyleSet,
-    nsStyleChangeList& aChangeList,
-    nsChangeHint aHintForThisFrame)
+  nsIFrame* aOwningFrame,
+  nsIFrame* aWrapperFrame,
+  ServoRestyleState& aRestyleState)
 {
   MOZ_ASSERT(aWrapperFrame->StyleContext()->GetPseudo() ==
                nsCSSAnonBoxes::tableWrapper,
              "What happened to our parent?");
 
   RefPtr<nsStyleContext> newContext =
-    aStyleSet.ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableWrapper,
-                                                 aOwningFrame->StyleContext());
+    aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
+      nsCSSAnonBoxes::tableWrapper, aOwningFrame->StyleContext());
 
   // Figure out whether we have an actual change.  It's important that we do
   // this, even though all the wrapper's changes are due to properties it
   // inherits from us, because it's possible that no one ever asked us for those
   // style structs and hence changes to them aren't reflected in
   // aHintForThisFrame at all.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint wrapperHint = aWrapperFrame->StyleContext()->CalcStyleDifference(
     newContext,
     &equalStructs,
     &samePointerStructs);
+  wrapperHint =
+    NS_RemoveSubsumedHints(wrapperHint, aRestyleState.ChangesHandled());
   if (wrapperHint) {
-    aChangeList.AppendChange(aWrapperFrame, aWrapperFrame->GetContent(),
-                             wrapperHint);
+    aRestyleState.ChangeList().AppendChange(
+      aWrapperFrame, aWrapperFrame->GetContent(), wrapperHint);
   }
 
   for (nsIFrame* cur = aWrapperFrame; cur; cur = cur->GetNextContinuation()) {
     cur->SetStyleContext(newContext);
   }
 
   MOZ_ASSERT(!(aWrapperFrame->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES),
              "Wrapper frame doesn't have any anon boxes of its own!");
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -593,21 +593,19 @@ public:
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
 
   // Return our wrapper frame.
   void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
 
 protected:
   static void UpdateStyleOfOwnedAnonBoxesForTableWrapper(
-      nsIFrame* aOwningFrame,
-      nsIFrame* aWrapperFrame,
-      mozilla::ServoStyleSet& aStyleSet,
-      nsStyleChangeList& aChangeList,
-      nsChangeHint aHintForThisFrame);
+    nsIFrame* aOwningFrame,
+    nsIFrame* aWrapperFrame,
+    mozilla::ServoRestyleState& aRestyleState);
 
   /** protected constructor.
     * @see NewFrame
     */
   explicit nsTableFrame(nsStyleContext* aContext, ClassID aID = kClassID);
 
   /** destructor, responsible for mColumnLayoutData */
   virtual ~nsTableFrame();
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -957,17 +957,18 @@ WebrtcVideoConduit::GetRTPStats(unsigned
   CSFLogVerbose(logTag, "%s for VideoConduit:%p", __FUNCTION__, this);
   {
     MutexAutoLock lock(mCodecMutex);
     if (!mRecvStream) {
       return false;
     }
 
     const webrtc::VideoReceiveStream::Stats& stats = mRecvStream->GetStats();
-    *jitterMs = stats.rtcp_stats.jitter;
+    *jitterMs =
+        stats.rtcp_stats.jitter / (webrtc::kVideoPayloadTypeFrequency / 1000);
     *cumulativeLost = stats.rtcp_stats.cumulative_lost;
   }
   return true;
 }
 
 bool WebrtcVideoConduit::GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp,
                                                uint32_t* jitterMs,
                                                uint32_t* packetsReceived,
@@ -989,17 +990,18 @@ bool WebrtcVideoConduit::GetRTCPReceiver
     uint32_t ssrc = mSendStreamConfig.rtp.ssrcs.front();
     auto ind = sendStats.substreams.find(ssrc);
     if (ind == sendStats.substreams.end()) {
       CSFLogError(logTag,
         "%s for VideoConduit:%p ssrc not found in SendStream stats.",
         __FUNCTION__, this);
       return false;
     }
-    *jitterMs = ind->second.rtcp_stats.jitter;
+    *jitterMs = ind->second.rtcp_stats.jitter
+        / (webrtc::kVideoPayloadTypeFrequency / 1000);
     *cumulativeLost = ind->second.rtcp_stats.cumulative_lost;
     *bytesReceived = ind->second.rtp_stats.MediaPayloadBytes();
     *packetsReceived = ind->second.rtp_stats.transmitted.packets;
     auto stats = mCall->Call()->GetStats();
     int64_t rtt = stats.rtt_ms;
 #ifdef DEBUG
     if (rtt > INT32_MAX) {
       CSFLogError(logTag,
--- a/mobile/android/app/lint.xml
+++ b/mobile/android/app/lint.xml
@@ -57,17 +57,16 @@
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/tabs/TabPanelBackButton.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditText.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/util/ViewUtil.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java"/>
         <ignore path="src/main/res/values/styles.xml"/>
-        <ignore path="src/main/res/values/themes.xml"/>
     </issue>
 
     <!-- We fixed all "Registered" lint errors. However the current gradle plugin has a bug where
          it ignores @SuppressLint annotations for this check. See CrashReporter class and
          https://code.google.com/p/android/issues/detail?id=204846 -->
     <issue id="Registered" severity="warning" />
 
     <!-- WHEN YOU FIX A LINT WARNING, ADD IT TO THIS LIST.
--- a/mobile/android/app/src/main/res/values-v21/themes.xml
+++ b/mobile/android/app/src/main/res/values-v21/themes.xml
@@ -31,9 +31,19 @@
         <item name="android:listViewStyle">@style/Widget.ListView</item>
         <item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
         <item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
         <item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
         <item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
         <item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
     </style>
 
+    <style name="OverlayActivity.V21" parent="OverlayActivity.Base">
+        <!-- Set the app's title bar color in the recent app switcher.
+
+             Note: We'd prefer not to show up in the recent app switcher (bug 1137928). -->
+        <item name="android:colorPrimary">@color/text_and_tabs_tray_grey</item>
+        <!-- We display the overlay on top of other Activities so show their status bar. -->
+        <item name="android:statusBarColor">@android:color/transparent</item>
+    </style>
+    <style name="OverlayActivity" parent="OverlayActivity.V21"/>
+
 </resources>
--- a/mobile/android/app/src/main/res/values/themes.xml
+++ b/mobile/android/app/src/main/res/values/themes.xml
@@ -100,29 +100,23 @@
         <item name="menuItemActionBarStyle">@style/Widget.MenuItemActionBar</item>
         <item name="menuItemActionModeStyle">@style/GeckoActionBar.Button</item>
         <item name="topSitesGridItemViewStyle">@style/Widget.TopSitesGridItemView</item>
         <item name="topSitesGridViewStyle">@style/Widget.TopSitesGridView</item>
         <item name="topSitesThumbnailViewStyle">@style/Widget.TopSitesThumbnailView</item>
     </style>
 
     <!-- Make an activity appear like an overlay. -->
-    <style name="OverlayActivity" parent="Gecko">
+    <style name="OverlayActivity.Base" parent="Gecko">
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:backgroundDimEnabled">true</item>
-
-        <!-- Set the app's title bar color in the recent app switcher.
-
-             Note: We'd prefer not to show up in the recent app switcher (bug 1137928). -->
-        <item name="android:colorPrimary">@color/text_and_tabs_tray_grey</item>
-        <!-- We display the overlay on top of other Activities so show their status bar. -->
-        <item name="android:statusBarColor">@android:color/transparent</item>
     </style>
+    <style name="OverlayActivity" parent="OverlayActivity.Base"/>
 
     <!--
         Themes for CustomTabsActivity. Since CustomTabsActivity usually be used by 3-rd party apps,
         to create separated themes to keep look and feel be consistent with ordinary Android app.
         And ensure changes to CustomTabsActivity won't effect GeckoApp.
     -->
 
     <style name="GeckoCustomTabs" parent="Theme.AppCompat.Light.NoActionBar">
@@ -135,14 +129,14 @@
     <style name="Bookmark" parent="Theme.AppCompat.Light.DialogWhenLarge"/>
     <style name="Bookmark.Gecko" parent="Gecko">
         <item name="toolbarStyle">@style/BookmarkToolbarStyle</item>
         <item name="colorAccent">@color/fennec_ui_orange</item>
         <item name="colorControlNormal">@color/disabled_grey</item>
 
         <item name="android:textColorHint">@color/tabs_tray_icon_grey</item>
     </style>
-    <style name="BookmarkToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
+    <style name="BookmarkToolbarStyle.Base" parent="@style/Widget.AppCompat.Toolbar">
         <item name="android:paddingRight">5dp</item>
-        <item name="android:paddingEnd">5dp</item>
     </style>
+    <style name="BookmarkToolbarStyle" parent="BookmarkToolbarStyle.Base"/>
 
 </resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/values-v17/themes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<resources>
+
+    <style name="BookmarkToolbarStyle.V17" parent="BookmarkToolbarStyle.Base">
+        <item name="android:paddingEnd">5dp</item>
+    </style>
+    <style name="BookmarkToolbarStyle" parent="BookmarkToolbarStyle.V17"/>
+
+</resources>
--- a/servo/README.md
+++ b/servo/README.md
@@ -1,13 +1,13 @@
 # The Servo Parallel Browser Engine Project
 
 [![Linux Build Status](https://img.shields.io/travis/servo/servo/master.svg?label=Linux%20build)](https://travis-ci.org/servo/servo)  [![Windows Build Status](https://img.shields.io/appveyor/ci/servo/servo/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/servo/servo/branch/master)  [![Changelog #228](https://img.shields.io/badge/changelog-%23228-9E978E.svg)](https://changelog.com/podcast/228)
 
-Servo is a prototype web browser engine written in the
+Servo is a web browser engine written in the
 [Rust](https://github.com/rust-lang/rust) language. It is currently developed on
 64bit OS X, 64bit Linux, and Android.
 
 Servo welcomes contribution from everyone.  See
 [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`HACKING_QUICKSTART.md`](docs/HACKING_QUICKSTART.md)
 for help getting started.
 
 Visit the [Servo Project page](https://servo.org/) for news and guides.
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -497,17 +497,16 @@ fn compute_style_for_animation_step(cont
                 guard.declarations().iter().rev()
                      .map(|&(ref decl, _importance)| (decl, CascadeLevel::Animations))
             };
 
             // This currently ignores visited styles, which seems acceptable,
             // as existing browsers don't appear to animate visited styles.
             let computed =
                 properties::apply_declarations(context.stylist.device(),
-                                               /* is_root = */ false,
                                                iter,
                                                previous_style,
                                                previous_style,
                                                /* cascade_info = */ None,
                                                /* visited_style = */ None,
                                                &*context.error_reporter,
                                                font_metrics_provider,
                                                CascadeFlags::empty(),
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -9,16 +9,17 @@
 
 use {Atom, Namespace, LocalName};
 use applicable_declarations::ApplicableDeclarationBlock;
 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
 #[cfg(feature = "gecko")] use context::UpdateAnimationsTasks;
 use data::ElementData;
 use element_state::ElementState;
 use font_metrics::FontMetricsProvider;
+use media_queries::Device;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 #[cfg(feature = "gecko")] use properties::animated_properties::AnimationValue;
 #[cfg(feature = "gecko")] use properties::animated_properties::TransitionProperty;
 use rule_tree::CascadeLevel;
 use selector_parser::{AttrValue, ElementExt, PreExistingComputedValues};
 use selector_parser::{PseudoClassStringArg, PseudoElement};
 use selectors::matching::{ElementSelectorFlags, VisitedHandlingMode};
 use selectors::sink::Push;
@@ -299,16 +300,23 @@ pub trait TElement : Eq + PartialEq + De
     ///
     /// XXXManishearth It would be better to make this a type parameter on
     /// ThreadLocalStyleContext and StyleContext
     type FontMetricsProvider: FontMetricsProvider;
 
     /// Get this element as a node.
     fn as_node(&self) -> Self::ConcreteNode;
 
+    /// A debug-only check that the device's owner doc matches the actual doc
+    /// we're the root of.
+    ///
+    /// Otherwise we may set document-level state incorrectly, like the root
+    /// font-size used for rem units.
+    fn owner_doc_matches_for_testing(&self, _: &Device) -> bool { true }
+
     /// Returns the depth of this element in the DOM.
     fn depth(&self) -> usize {
         let mut depth = 0;
         let mut curr = *self;
         while let Some(parent) = curr.traversal_parent() {
             depth += 1;
             curr = parent;
         }
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -41,17 +41,17 @@ pub struct PerDocumentStyleDataImpl {
 /// and unexpected races while trying to mutate it.
 pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
 
 impl PerDocumentStyleData {
     /// Create a dummy `PerDocumentStyleData`.
     pub fn new(pres_context: RawGeckoPresContextOwned) -> Self {
         let device = Device::new(pres_context);
         let quirks_mode = unsafe {
-            (*(*device.pres_context).mDocument.raw::<nsIDocument>()).mCompatMode
+            (*device.pres_context().mDocument.raw::<nsIDocument>()).mCompatMode
         };
 
         PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
             stylist: Stylist::new(device, quirks_mode.into()),
             stylesheets: StylesheetSet::new(),
             font_faces: vec![],
             counter_styles: FnvHashMap::default(),
         }))
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -9,17 +9,17 @@ use context::QuirksMode;
 use cssparser::{CssStringWriter, Parser, RGBA, Token, BasicParseError};
 use euclid::Size2D;
 use font_metrics::get_metrics_provider_for_product;
 use gecko::values::convert_nscolor_to_rgba;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit, nsStringBuffer};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags};
-use gecko_bindings::structs::RawGeckoPresContextOwned;
+use gecko_bindings::structs::{nsPresContext, RawGeckoPresContextOwned};
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::{ComputedValues, StyleBuilder};
 use properties::longhands::font_size;
 use selectors::parser::SelectorParseError;
 use std::fmt::{self, Write};
 use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
 use str::starts_with_ignore_ascii_case;
@@ -31,17 +31,17 @@ use values::{CSSFloat, specified};
 use values::computed::{self, ToComputedValue};
 
 /// The `Device` in Gecko wraps a pres context, has a default values computed,
 /// and contains all the viewport rule state.
 pub struct Device {
     /// NB: The pres context lifetime is tied to the styleset, who owns the
     /// stylist, and thus the `Device`, so having a raw pres context pointer
     /// here is fine.
-    pub pres_context: RawGeckoPresContextOwned,
+    pres_context: RawGeckoPresContextOwned,
     default_values: Arc<ComputedValues>,
     viewport_override: Option<ViewportConstraints>,
     /// The font size of the root element
     /// This is set when computing the style of the root
     /// element, and used for rem units in other elements.
     ///
     /// When computing the style of the root element, there can't be any
     /// other style being computed at the same time, given we need the style of
@@ -100,21 +100,26 @@ impl Device {
         Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
     }
 
     /// Set the font size of the root element (for rem)
     pub fn set_root_font_size(&self, size: Au) {
         self.root_font_size.store(size.0 as isize, Ordering::Relaxed)
     }
 
+    /// Gets the pres context associated with this document.
+    pub fn pres_context(&self) -> &nsPresContext {
+        unsafe { &*self.pres_context }
+    }
+
     /// Recreates the default computed values.
     pub fn reset_computed_values(&mut self) {
         // NB: A following stylesheet flush will populate this if appropriate.
         self.viewport_override = None;
-        self.default_values = ComputedValues::default_values(unsafe { &*self.pres_context });
+        self.default_values = ComputedValues::default_values(self.pres_context());
         self.used_root_font_size.store(false, Ordering::Relaxed);
     }
 
     /// Returns whether we ever looked up the root font size of the Device.
     pub fn used_root_font_size(&self) -> bool {
         self.used_root_font_size.load(Ordering::Relaxed)
     }
 
@@ -129,45 +134,45 @@ impl Device {
     }
 
     /// Returns the current media type of the device.
     pub fn media_type(&self) -> MediaType {
         unsafe {
             // FIXME(emilio): Gecko allows emulating random media with
             // mIsEmulatingMedia / mMediaEmulated . Refactor both sides so that
             // is supported (probably just making MediaType an Atom).
-            if (*self.pres_context).mMedium == atom!("screen").as_ptr() {
+            if self.pres_context().mMedium == atom!("screen").as_ptr() {
                 MediaType::Screen
             } else {
-                debug_assert!((*self.pres_context).mMedium == atom!("print").as_ptr());
+                debug_assert!(self.pres_context().mMedium == atom!("print").as_ptr());
                 MediaType::Print
             }
         }
     }
 
     /// Returns the current viewport size in app units.
     pub fn au_viewport_size(&self) -> Size2D<Au> {
         self.viewport_override.as_ref().map(|v| {
             Size2D::new(Au::from_f32_px(v.size.width),
                         Au::from_f32_px(v.size.height))
         }).unwrap_or_else(|| unsafe {
             // TODO(emilio): Need to take into account scrollbars.
-            Size2D::new(Au((*self.pres_context).mVisibleArea.width),
-                        Au((*self.pres_context).mVisibleArea.height))
+            let area = &self.pres_context().mVisibleArea;
+            Size2D::new(Au(area.width), Au(area.height))
         })
     }
 
     /// Returns whether document colors are enabled.
     pub fn use_document_colors(&self) -> bool {
-        unsafe { (*self.pres_context).mUseDocumentColors() != 0 }
+        self.pres_context().mUseDocumentColors() != 0
     }
 
     /// Returns the default background color.
     pub fn default_background_color(&self) -> RGBA {
-        convert_nscolor_to_rgba(unsafe { (*self.pres_context).mBackgroundColor })
+        convert_nscolor_to_rgba(self.pres_context().mBackgroundColor)
     }
 }
 
 /// A expression for gecko contains a reference to the media feature, the value
 /// the media query contained, and the range to evaluate.
 #[derive(Debug, Clone)]
 pub struct Expression {
     feature: &'static nsMediaFeature,
--- a/servo/components/style/gecko/values.rs
+++ b/servo/components/style/gecko/values.rs
@@ -396,17 +396,17 @@ pub fn round_border_to_device_pixels(wid
     }
 }
 
 impl CounterStyleOrNone {
     /// Convert this counter style to a Gecko CounterStylePtr.
     pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr, device: &Device) {
         use gecko_bindings::bindings::Gecko_SetCounterStyleToName as set_name;
         use gecko_bindings::bindings::Gecko_SetCounterStyleToSymbols as set_symbols;
-        let pres_context = unsafe { &*device.pres_context };
+        let pres_context = device.pres_context();
         match self {
             CounterStyleOrNone::None => unsafe {
                 set_name(gecko_value, atom!("none").into_addrefed(), pres_context);
             },
             CounterStyleOrNone::Name(name) => unsafe {
                 set_name(gecko_value, name.0.into_addrefed(), pres_context);
             },
             CounterStyleOrNone::Symbols(symbols_type, symbols) => {
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -611,17 +611,17 @@ fn selector_flags_to_node_flags(flags: E
     gecko_flags
 }
 
 fn get_animation_rule(element: &GeckoElement,
                       cascade_level: CascadeLevel)
                       -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
     use gecko_bindings::sugar::ownership::HasSimpleFFI;
     // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
-    let mut animation_values = AnimationValueMap::new();
+    let mut animation_values = AnimationValueMap::default();
     if unsafe { Gecko_GetAnimationRule(element.0,
                                        cascade_level,
                                        AnimationValueMap::as_ffi_mut(&mut animation_values)) } {
         let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
         Some(Arc::new(shared_lock.wrap(
             PropertyDeclarationBlock::from_animation_value_map(&animation_values))))
     } else {
         None
@@ -667,17 +667,17 @@ impl FontMetricsProvider for GeckoFontMe
         cache.push((font_name.clone(), sizes));
         sizes.size_for_generic(font_family)
     }
 
     fn query(&self, font: &Font, font_size: Au, wm: WritingMode,
              in_media_query: bool, device: &Device) -> FontMetricsQueryResult {
         use gecko_bindings::bindings::Gecko_GetFontMetrics;
         let gecko_metrics = unsafe {
-            Gecko_GetFontMetrics(&*device.pres_context,
+            Gecko_GetFontMetrics(device.pres_context(),
                                  wm.is_vertical() && !wm.is_sideways(),
                                  font.gecko(),
                                  font_size.0,
                                  // we don't use the user font set in a media query
                                  !in_media_query)
         };
         let metrics = FontMetrics {
             x_height: Au(gecko_metrics.mXSize),
@@ -766,16 +766,21 @@ impl<'le> TElement for GeckoElement<'le>
             };
         }
     }
 
     fn as_node(&self) -> Self::ConcreteNode {
         unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
     }
 
+    fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
+        self.as_node().owner_doc() as *const structs::nsIDocument ==
+            device.pres_context().mDocument.raw::<structs::nsIDocument>()
+    }
+
     fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
         if !self.may_have_style_attribute() {
             return None;
         }
 
         let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) };
         declarations.map_or(None, |s| s.as_arc_opt())
     }
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -487,17 +487,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
             invalidated_self |= result.invalidated_self;
             if !result.effective_for_next {
                 sibling_invalidations.remove(i);
             } else {
                 i += 1;
             }
         }
 
-        sibling_invalidations.extend(new_sibling_invalidations.into_iter());
+        sibling_invalidations.extend(new_sibling_invalidations.drain());
         invalidated_self
     }
 
     /// Process a given invalidation list coming from our parent,
     /// adding to `descendant_invalidations` and `sibling_invalidations` as
     /// needed.
     ///
     /// Returns whether our style was invalidated as a result.
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -12,33 +12,33 @@ use cascade_info::CascadeInfo;
 use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
 use data::{ComputedStyle, ElementData, RestyleData};
 use dom::{TElement, TNode};
 use font_metrics::FontMetricsProvider;
 use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
 use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
 use invalidation::element::restyle_hints::RestyleHint;
 use log::LogLevel::Trace;
-use properties::{ALLOW_SET_ROOT_FONT_SIZE, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
 use properties::{AnimationRules, CascadeFlags, ComputedValues};
+use properties::{IS_ROOT_ELEMENT, PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
 use properties::{VISITED_DEPENDENT_ONLY, cascade};
 use properties::longhands::display::computed_value as display;
 use rule_tree::{CascadeLevel, StrongRuleNode};
 use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
 use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
 use selectors::matching::{VisitedHandlingMode, AFFECTED_BY_PSEUDO_ELEMENTS};
 use sharing::StyleSharingBehavior;
 use stylearc::Arc;
 use stylist::RuleInclusion;
 
 /// Whether we are cascading for an eager pseudo-element or something else.
 ///
 /// Controls where we inherit styles from, and whether display:contents is
 /// prohibited.
-#[derive(PartialEq, Copy, Clone)]
+#[derive(PartialEq, Copy, Clone, Debug)]
 enum CascadeTarget {
     /// Inherit from the parent element, as normal CSS dictates, _or_ from the
     /// closest non-Native Anonymous element in case this is Native Anonymous
     /// Content. display:contents is allowed.
     Normal,
     /// Inherit from the primary style, this is used while computing eager
     /// pseudos, like ::before and ::after when we're traversing the parent.
     /// Also prohibits display:contents from having an effect.
@@ -268,18 +268,19 @@ trait PrivateMatchMethods: TElement {
         if self.skip_root_and_item_based_display_fixup() {
             cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP)
         }
         if cascade_visited.visited_dependent_only() {
             cascade_flags.insert(VISITED_DEPENDENT_ONLY);
         }
         if self.is_native_anonymous() || cascade_target == CascadeTarget::EagerPseudo {
             cascade_flags.insert(PROHIBIT_DISPLAY_CONTENTS);
-        } else {
-            cascade_flags.insert(ALLOW_SET_ROOT_FONT_SIZE);
+        } else if self.is_root() {
+            debug_assert!(self.owner_doc_matches_for_testing(shared_context.stylist.device()));
+            cascade_flags.insert(IS_ROOT_ELEMENT);
         }
 
         // Grab the inherited values.
         let parent_el;
         let parent_data;
         let style_to_inherit_from = match cascade_target {
             CascadeTarget::Normal => {
                 parent_el = self.inheritance_parent();
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1546,17 +1546,17 @@ fn static_assert() {
         for (current, feature) in current_settings.iter_mut().zip(feature_settings.iter()) {
             current.mTag = feature.mTag;
             current.mValue = feature.mValue;
         }
     }
 
     pub fn fixup_none_generic(&mut self, device: &Device) {
         unsafe {
-            bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, &*device.pres_context)
+            bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, device.pres_context())
         }
     }
 
     pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
         use properties::longhands::font_family::computed_value::FontFamily;
 
         let list = &mut self.gecko.mFont.fontlist;
         unsafe { Gecko_FontFamilyList_Clear(list); }
@@ -1616,17 +1616,17 @@ fn static_assert() {
         } else {
             self.gecko.mSize = v.0;
             self.fixup_font_min_size(device);
             Some(Au(parent.gecko.mScriptUnconstrainedSize))
         }
     }
 
     pub fn fixup_font_min_size(&mut self, device: &Device) {
-        unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, &*device.pres_context) }
+        unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, device.pres_context()) }
     }
 
     pub fn apply_unconstrained_font_size(&mut self, v: Au) {
         self.gecko.mScriptUnconstrainedSize = v.0;
     }
 
     /// Calculates the constrained and unconstrained font sizes to be inherited
     /// from the parent.
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -25,17 +25,17 @@ use properties::longhands::transform::co
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
 use properties::longhands::visibility::computed_value::T as Visibility;
 #[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
 use selectors::parser::SelectorParseError;
 use smallvec::SmallVec;
 use std::cmp;
-#[cfg(feature = "gecko")] use std::collections::HashMap;
+#[cfg(feature = "gecko")] use fnv::FnvHashMap;
 use style_traits::ParseError;
 use super::ComputedValues;
 use values::{Auto, CSSFloat, CustomIdent, Either};
 use values::animated::effects::{Filter as AnimatedFilter, FilterList as AnimatedFilterList};
 use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use values::computed::{BorderCornerRadius, ClipRect};
 use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
@@ -435,17 +435,17 @@ impl AnimatedProperty {
         }
     }
 }
 
 /// A collection of AnimationValue that were composed on an element.
 /// This HashMap stores the values that are the last AnimationValue to be
 /// composed for each TransitionProperty.
 #[cfg(feature = "gecko")]
-pub type AnimationValueMap = HashMap<AnimatableLonghand, AnimationValue>;
+pub type AnimationValueMap = FnvHashMap<AnimatableLonghand, AnimationValue>;
 #[cfg(feature = "gecko")]
 unsafe impl HasFFI for AnimationValueMap {
     type FFIType = RawServoAnimationValueMap;
 }
 #[cfg(feature = "gecko")]
 unsafe impl HasSimpleFFI for AnimationValueMap {}
 
 /// An enum to represent a single computed value belonging to an animated
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -209,51 +209,39 @@
     initial_value="computed::LengthOrNumber::zero().into()",
     initial_specified_value="specified::LengthOrNumber::zero().into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
     animation_value_type="none",
     boxed=True)}
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
-    use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Debug, Clone, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
-    #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Debug, Clone, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub RepeatKeyword,
                               pub Option<RepeatKeyword>);
 
     define_css_keyword_enum!(RepeatKeyword:
                              "stretch" => Stretch,
                              "repeat" => Repeat,
                              "round" => Round,
                              "space" => Space);
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-            self.0.to_css(dest)?;
-            if let Some(second) = self.1 {
-                dest.write_str(" ")?;
-                second.to_css(dest)?;
-            }
-            Ok(())
-        }
-    }
-
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(RepeatKeyword::Stretch, RepeatKeyword::Stretch)
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue(RepeatKeyword::Stretch, None)
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -103,17 +103,17 @@
         }
 
         impl ToComputedValue for SystemColor {
             type ComputedValue = u32; // nscolor
             #[inline]
             fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
                 unsafe {
                     Gecko_GetLookAndFeelSystemColor(*self as i32,
-                                                    &*cx.device.pres_context)
+                                                    cx.device.pres_context())
                 }
             }
 
             #[inline]
             fn from_computed_value(_: &Self::ComputedValue) -> Self {
                 unreachable!()
             }
         }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -2383,19 +2383,22 @@ https://drafts.csswg.org/css-fonts-4/#lo
                         SystemFont::${to_camel_case(font)} => {
                             LookAndFeel_FontID::eFont_${to_camel_case(font.replace("-moz-", ""))}
                         }
                     % endfor
                 };
 
                 let mut system: nsFont = unsafe { mem::uninitialized() };
                 unsafe {
-                    bindings::Gecko_nsFont_InitSystem(&mut system, id as i32,
-                                            cx.style.get_font().gecko(),
-                                            &*cx.device.pres_context)
+                    bindings::Gecko_nsFont_InitSystem(
+                        &mut system,
+                        id as i32,
+                        cx.style.get_font().gecko(),
+                        cx.device.pres_context()
+                    )
                 }
                 let family = system.fontlist.mFontlist.iter().map(|font| {
                     use properties::longhands::font_family::computed_value::*;
                     FontFamily::FamilyName(FamilyName {
                         name: (&*font.mName).into(),
                         quoted: true
                     })
                 }).collect::<Vec<_>>();
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -18,18 +18,16 @@
                          extra_gecko_values="right left top-outside bottom-outside",
                          needs_conversion="True",
                          animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
 
 <%helpers:longhand name="border-spacing" animation_value_type="ComputedValue" boxed="True"
                    spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
     use app_units::Au;
-    use std::fmt;
-    use style_traits::ToCss;
     use values::specified::{AllowQuirks, Length};
 
     pub mod computed_value {
         use app_units::Au;
         use properties::animated_properties::Animatable;
 
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
@@ -59,44 +57,31 @@
             #[inline]
             fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
                 Ok(self.horizontal.compute_squared_distance(&other.horizontal)? +
                    self.vertical.compute_squared_distance(&other.vertical)?)
             }
         }
     }
 
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+    #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
     pub struct SpecifiedValue {
         pub horizontal: Length,
         pub vertical: Option<Length>,
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T {
             horizontal: Au(0),
             vertical: Au(0),
         }
     }
 
-    impl ToCss for SpecifiedValue {
-        fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-            where W: fmt::Write,
-        {
-            self.horizontal.to_css(dest)?;
-            if let Some(vertical) = self.vertical.as_ref() {
-                dest.write_str(" ")?;
-                vertical.to_css(dest)?;
-            }
-            Ok(())
-        }
-    }
-
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let horizontal = self.horizontal.to_computed_value(context);
             computed_value::T {
                 horizontal: horizontal,
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2467,30 +2467,34 @@ static CASCADE_PROPERTY: [CascadePropert
 ];
 
 bitflags! {
     /// A set of flags to tweak the behavior of the `cascade` function.
     pub flags CascadeFlags: u8 {
         /// Whether to inherit all styles from the parent. If this flag is not
         /// present, non-inherited styles are reset to their initial values.
         const INHERIT_ALL = 0x01,
+
         /// Whether to skip any root element and flex/grid item display style
         /// fixup.
         const SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP = 0x02,
+
         /// Whether to only cascade properties that are visited dependent.
         const VISITED_DEPENDENT_ONLY = 0x04,
-        /// Should we modify the device's root font size
-        /// when computing the root?
+
+        /// Whether the given element we're styling is the document element,
+        /// that is, matches :root.
         ///
-        /// Not set for native anonymous content since some NAC
-        /// form their own root, but share the device.
+        /// Not set for native anonymous content since some NAC form their own
+        /// root, but share the device.
         ///
-        /// ::backdrop and all NAC will resolve rem units against
-        /// the toplevel root element now.
-        const ALLOW_SET_ROOT_FONT_SIZE = 0x08,
+        /// This affects some style adjustments, like blockification, and means
+        /// that it may affect global state, like the Device's root font-size.
+        const IS_ROOT_ELEMENT = 0x08,
+
         /// Whether to convert display:contents into display:inline.  This
         /// is used by Gecko to prevent display:contents on generated
         /// content.
         const PROHIBIT_DISPLAY_CONTENTS = 0x10,
     }
 }
 
 /// Performs the CSS cascade, computing new styles for an element from its parent style.
@@ -2515,25 +2519,23 @@ pub fn cascade(device: &Device,
                visited_style: Option<Arc<ComputedValues>>,
                cascade_info: Option<<&mut CascadeInfo>,
                error_reporter: &ParseErrorReporter,
                font_metrics_provider: &FontMetricsProvider,
                flags: CascadeFlags,
                quirks_mode: QuirksMode)
                -> ComputedValues {
     debug_assert_eq!(parent_style.is_some(), layout_parent_style.is_some());
-    let (is_root_element, inherited_style, layout_parent_style) = match parent_style {
+    let (inherited_style, layout_parent_style) = match parent_style {
         Some(parent_style) => {
-            (false,
-             parent_style,
+            (parent_style,
              layout_parent_style.unwrap())
         },
         None => {
-            (true,
-             device.default_computed_values(),
+            (device.default_computed_values(),
              device.default_computed_values())
         }
     };
 
     let iter_declarations = || {
         rule_node.self_and_ancestors().flat_map(|node| {
             let cascade_level = node.cascade_level();
             let source = node.style_source();
@@ -2553,33 +2555,31 @@ pub fn cascade(device: &Device,
                         Some((declaration, cascade_level))
                     } else {
                         None
                     }
                 })
         })
     };
     apply_declarations(device,
-                       is_root_element,
                        iter_declarations,
                        inherited_style,
                        layout_parent_style,
                        visited_style,
                        cascade_info,
                        error_reporter,
                        font_metrics_provider,
                        flags,
                        quirks_mode)
 }
 
 /// NOTE: This function expects the declaration with more priority to appear
 /// first.
 #[allow(unused_mut)] // conditionally compiled code for "position"
 pub fn apply_declarations<'a, F, I>(device: &Device,
-                                    is_root_element: bool,
                                     iter_declarations: F,
                                     inherited_style: &ComputedValues,
                                     layout_parent_style: &ComputedValues,
                                     visited_style: Option<Arc<ComputedValues>>,
                                     mut cascade_info: Option<<&mut CascadeInfo>,
                                     error_reporter: &ParseErrorReporter,
                                     font_metrics_provider: &FontMetricsProvider,
                                     flags: CascadeFlags,
@@ -2624,17 +2624,17 @@ pub fn apply_declarations<'a, F, I>(devi
                           visited_style,
                           % for style_struct in data.active_style_structs():
                               inherited_style.${style_struct.name_lower}_arc(),
                           % endfor
                           )
     };
 
     let mut context = computed::Context {
-        is_root_element: is_root_element,
+        is_root_element: flags.contains(IS_ROOT_ELEMENT),
         device: device,
         inherited_style: inherited_style,
         layout_parent_style: layout_parent_style,
         style: builder,
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: quirks_mode,
@@ -2778,23 +2778,24 @@ pub fn apply_declarations<'a, F, I>(devi
                             }
                         }
                     }
 
                     // In case of just the language changing, the parent could have had no generic,
                     // which Gecko just does regular cascading with. Do the same.
                     // This can only happen in the case where the language changed but the family did not
                     if generic != structs::kGenericFont_NONE {
-                        let pres_context = context.device.pres_context;
-                        let gecko_font = context.mutate_style().mutate_font().gecko_mut();
+                        let gecko_font = context.style.mutate_font().gecko_mut();
                         gecko_font.mGenericID = generic;
                         unsafe {
-                            bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(gecko_font,
-                                                                                 &*pres_context,
-                                                                                 generic);
+                            bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric(
+                                gecko_font,
+                                context.device.pres_context(),
+                                generic
+                            );
                         }
                     }
                 }
             % endif
 
             // It is important that font_size is computed before
             // the late properties (for em units), but after font-family
             // (for the base-font-size dependence for default and keyword font-sizes)
@@ -2848,27 +2849,27 @@ pub fn apply_declarations<'a, F, I>(devi
                                                  default_style,
                                                  &mut context,
                                                  &mut cacheable,
                                                  &mut cascade_info,
                                                  error_reporter);
             % endif
             }
 
-            if is_root_element && flags.contains(ALLOW_SET_ROOT_FONT_SIZE) {
+            if context.is_root_element {
                 let s = context.style.get_font().clone_font_size();
                 context.device.set_root_font_size(s);
             }
         % endif
     % endfor
 
     let mut style = context.style;
 
     {
-        StyleAdjuster::new(&mut style, is_root_element)
+        StyleAdjuster::new(&mut style)
             .adjust(context.layout_parent_style, flags);
     }
 
     % if product == "gecko":
         if let Some(ref mut bg) = style.get_background_if_mutated() {
             bg.fill_arrays();
         }
 
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -213,29 +213,29 @@ impl RuleTree {
             return current;
         }
 
         //
         // Insert important declarations, in order of increasing importance,
         // followed by any transition rule.
         //
 
-        for source in important_author.into_iter() {
+        for source in important_author.drain() {
             current = current.ensure_child(self.root.downgrade(), source, AuthorImportant);
         }
 
         if let Some(source) = important_style_attr {
             current = current.ensure_child(self.root.downgrade(), source, StyleAttributeImportant);
         }
 
-        for source in important_user.into_iter() {
+        for source in important_user.drain() {
             current = current.ensure_child(self.root.downgrade(), source, UserImportant);
         }
 
-        for source in important_ua.into_iter() {
+        for source in important_ua.drain() {
             current = current.ensure_child(self.root.downgrade(), source, UAImportant);
         }
 
         if let Some(source) = transition {
             current = current.ensure_child(self.root.downgrade(), source, Transitions);
         }
 
         current
@@ -297,17 +297,17 @@ impl RuleTree {
                                 guards: &StylesheetGuards)
                                 -> Option<StrongRuleNode> {
         debug_assert!(level.is_unique_per_element());
         // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
         // the refcount churn.
         let mut current = path.clone();
 
         // First walk up until the first less-or-equally specific rule.
-        let mut children = vec![];
+        let mut children = SmallVec::<[_; 10]>::new();
         while current.get().level > level {
             children.push((current.get().source.clone(), current.get().level));
             current = current.parent().unwrap().clone();
         }
 
         // Then remove the one at the level we want to replace, if any.
         //
         // NOTE: Here we assume that only one rule can be at the level we're
@@ -364,17 +364,19 @@ impl RuleTree {
                                                    StyleSource::Declarations(pdb.clone()),
                                                    level);
                 }
             }
         }
 
         // Now the rule is in the relevant place, push the children as
         // necessary.
-        Some(self.insert_ordered_rules_from(current, children.into_iter().rev()))
+        let rule =
+            self.insert_ordered_rules_from(current, children.drain().rev());
+        Some(rule)
     }
 
     /// Returns new rule nodes without Transitions level rule.
     pub fn remove_transition_rule_if_applicable(&self, path: &StrongRuleNode) -> StrongRuleNode {
         // Return a clone if there is no transition level.
         if path.cascade_level() != CascadeLevel::Transitions {
             return path.clone();
         }
@@ -387,25 +389,26 @@ impl RuleTree {
         // Return a clone if there are no animation rules.
         if !path.has_animation_or_transition_rules() {
             return path.clone();
         }
 
         let iter = path.self_and_ancestors().take_while(
             |node| node.cascade_level() >= CascadeLevel::SMILOverride);
         let mut last = path;
-        let mut children = vec![];
+        let mut children = SmallVec::<[_; 10]>::new();
         for node in iter {
             if !node.cascade_level().is_animation() {
                 children.push((node.get().source.clone(), node.cascade_level()));
             }
             last = node;
         }
 
-        self.insert_ordered_rules_from(last.parent().unwrap().clone(), children.into_iter().rev())
+        let rule = self.insert_ordered_rules_from(last.parent().unwrap().clone(), children.drain().rev());
+        rule
     }
 }
 
 /// The number of RuleNodes added to the free list before we will consider
 /// doing a GC when calling maybe_gc().  (The value is copied from Gecko,
 /// where it likely did not result from a rigorous performance analysis.)
 const RULE_TREE_GC_INTERVAL: usize = 300;
 
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -2,35 +2,33 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A struct to encapsulate all the style fixups a computed style needs in order
 //! for it to adhere to the CSS spec.
 
 use app_units::Au;
 use properties::{self, CascadeFlags, ComputedValues};
-use properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, StyleBuilder};
+use properties::{IS_ROOT_ELEMENT, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, StyleBuilder};
 use properties::longhands::display::computed_value::T as display;
 use properties::longhands::float::computed_value::T as float;
 use properties::longhands::overflow_x::computed_value::T as overflow;
 use properties::longhands::position::computed_value::T as position;
 
 
 /// An unsized struct that implements all the adjustment methods.
 pub struct StyleAdjuster<'a, 'b: 'a> {
     style: &'a mut StyleBuilder<'b>,
-    is_root_element: bool,
 }
 
 impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
     /// Trivially constructs a new StyleAdjuster.
-    pub fn new(style: &'a mut StyleBuilder<'b>, is_root_element: bool) -> Self {
+    pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
         StyleAdjuster {
             style: style,
-            is_root_element: is_root_element,
         }
     }
 
     /// https://fullscreen.spec.whatwg.org/#new-stacking-layer
     ///
     ///    Any position value other than 'absolute' and 'fixed' are
     ///    computed to 'absolute' if the element is in a top layer.
     ///
@@ -61,32 +59,32 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
             ($if_what:expr) => {
                 if !blockify {
                     blockify = $if_what;
                 }
             }
         }
 
         if !flags.contains(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) {
-            blockify_if!(self.is_root_element);
+            blockify_if!(flags.contains(IS_ROOT_ELEMENT));
             blockify_if!(layout_parent_style.get_box().clone_display().is_item_container());
         }
 
         let is_item_or_root = blockify;
 
         blockify_if!(self.style.floated());
         blockify_if!(self.style.out_of_flow_positioned());
 
         if !blockify {
             return;
         }
 
         let display = self.style.get_box().clone_display();
         let blockified_display =
-            display.equivalent_block_display(self.is_root_element);
+            display.equivalent_block_display(flags.contains(IS_ROOT_ELEMENT));
         if display != blockified_display {
             self.style.mutate_box().set_adjusted_display(blockified_display,
                                                          is_item_or_root);
         }
     }
 
     /// Adjust the style for text style.
     ///
--- a/servo/components/style/stylesheets/document_rule.rs
+++ b/servo/components/style/stylesheets/document_rule.rs
@@ -136,17 +136,17 @@ impl UrlMatchingFunction {
 
         let pattern = nsCString::from(match *self {
             UrlMatchingFunction::Url(ref url) => url.as_str(),
             UrlMatchingFunction::UrlPrefix(ref pat) |
             UrlMatchingFunction::Domain(ref pat) |
             UrlMatchingFunction::RegExp(ref pat) => pat,
         });
         unsafe {
-            Gecko_DocumentRule_UseForPresentation(&*device.pres_context, &*pattern, func)
+            Gecko_DocumentRule_UseForPresentation(device.pres_context(), &*pattern, func)
         }
     }
 
     #[cfg(not(feature = "gecko"))]
     /// Evaluate a URL matching function.
     pub fn evaluate(&self, _: &Device) -> bool {
         false
     }
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -38,17 +38,17 @@ define_css_keyword_enum!(ShapeBox:
     "padding-box" => PaddingBox,
     "content-box" => ContentBox
 );
 add_impls_for_keyword_enum!(ShapeBox);
 
 /// A shape source, for some reference box.
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToComputedValue)]
+#[derive(Clone, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum ShapeSource<BasicShape, ReferenceBox> {
     Url(SpecifiedUrl),
     Shape(BasicShape, Option<ReferenceBox>),
     Box(ReferenceBox),
     None,
 }
 
 #[allow(missing_docs)]
@@ -121,32 +121,16 @@ define_css_keyword_enum!(FillRule:
 );
 add_impls_for_keyword_enum!(FillRule);
 
 impl<B, T> HasViewportPercentage for ShapeSource<B, T> {
     #[inline]
     fn has_viewport_percentage(&self) -> bool { false }
 }
 
-impl<B: ToCss, T: ToCss> ToCss for ShapeSource<B, T> {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            ShapeSource::Url(ref url) => url.to_css(dest),
-            ShapeSource::Shape(ref shape, Some(ref ref_box)) => {
-                shape.to_css(dest)?;
-                dest.write_str(" ")?;
-                ref_box.to_css(dest)
-            },
-            ShapeSource::Shape(ref shape, None) => shape.to_css(dest),
-            ShapeSource::Box(ref val) => val.to_css(dest),
-            ShapeSource::None => dest.write_str("none"),
-        }
-    }
-}
-
 impl<L> ToCss for InsetRect<L>
     where L: ToCss + PartialEq
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("inset(")?;
         self.rect.to_css(dest)?;
         if let Some(ref radius) = self.round {
             dest.write_str(" round ")?;
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -118,17 +118,17 @@ pub enum GradientItem<Color, LengthOrPer
     /// A color stop.
     ColorStop(ColorStop<Color, LengthOrPercentage>),
     /// An interpolation hint.
     InterpolationHint(LengthOrPercentage),
 }
 
 /// A color stop.
 /// https://drafts.csswg.org/css-images/#typedef-color-stop-list
-#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToComputedValue)]
+#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct ColorStop<Color, LengthOrPercentage> {
     /// The color of this stop.
     pub color: Color,
     /// The position of this stop.
     pub position: Option<LengthOrPercentage>,
 }
 
@@ -316,29 +316,16 @@ impl<C, L> fmt::Debug for ColorStop<C, L
         write!(f, "{:?}", self.color)?;
         if let Some(ref pos) = self.position {
             write!(f, " {:?}", pos)?;
         }
         Ok(())
     }
 }
 
-impl<C, L> ToCss for ColorStop<C, L>
-    where C: ToCss, L: ToCss,
-{
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        self.color.to_css(dest)?;
-        if let Some(ref position) = self.position {
-            dest.write_str(" ")?;
-            position.to_css(dest)?;
-        }
-        Ok(())
-    }
-}
-
 impl<C> ToCss for ImageRect<C>
     where C: ToCss,
 {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("-moz-image-rect(")?;
         self.url.to_css(dest)?;
         dest.write_str(", ")?;
         self.top.to_css(dest)?;
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -249,31 +249,31 @@ impl ToComputedValue for Color {
             Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed),
             Color::Complex(ref complex) => *complex,
             #[cfg(feature = "gecko")]
             Color::System(system) =>
                 convert_nscolor_to_computedcolor(system.to_computed_value(_context)),
             #[cfg(feature = "gecko")]
             Color::Special(special) => {
                 use self::gecko::SpecialColorKeyword as Keyword;
-                let pres_context = unsafe { &*_context.device.pres_context };
+                let pres_context = _context.device.pres_context();
                 convert_nscolor_to_computedcolor(match special {
                     Keyword::MozDefaultColor => pres_context.mDefaultColor,
                     Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor,
                     Keyword::MozHyperlinktext => pres_context.mLinkColor,
                     Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
                     Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
                 })
             }
             #[cfg(feature = "gecko")]
             Color::InheritFromBodyQuirk => {
                 use dom::TElement;
                 use gecko::wrapper::GeckoElement;
                 use gecko_bindings::bindings::Gecko_GetBody;
-                let pres_context = unsafe { &*_context.device.pres_context };
+                let pres_context = _context.device.pres_context();
                 let body = unsafe {
                     Gecko_GetBody(pres_context)
                 };
                 if let Some(body) = body {
                     let wrap = GeckoElement(body);
                     let borrow = wrap.borrow_data();
                     ComputedColor::rgba(borrow.as_ref().unwrap()
                                               .styles().primary.values()
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -1,23 +1,19 @@
 /* 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/. */
 
 //! Specified types for CSS values related to effects.
 
 use cssparser::{BasicParseError, Parser, Token};
 use parser::{Parse, ParserContext};
-#[cfg(feature = "gecko")]
-use std::fmt;
 use style_traits::ParseError;
 #[cfg(not(feature = "gecko"))]
 use style_traits::StyleParseError;
-#[cfg(feature = "gecko")]
-use style_traits::ToCss;
 use values::computed::{Context, Number as ComputedNumber, ToComputedValue};
 use values::computed::effects::DropShadow as ComputedDropShadow;
 use values::generics::effects::Filter as GenericFilter;
 use values::generics::effects::FilterList as GenericFilterList;
 use values::specified::{Angle, Percentage};
 #[cfg(feature = "gecko")]
 use values::specified::color::Color;
 use values::specified::length::Length;
@@ -50,17 +46,17 @@ pub enum Factor {
 #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub enum DropShadow {}
 
 /// A specified value for the `drop-shadow()` filter.
 ///
 /// Contrary to the canonical order from the spec, the color is serialised
 /// first, like in Gecko's computed values and in all Webkit's values.
 #[cfg(feature = "gecko")]
-#[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
 pub struct DropShadow {
     /// Color.
     pub color: Option<Color>,
     /// Horizontal radius.
     pub horizontal: Length,
     /// Vertical radius.
     pub vertical: Length,
     /// Blur radius.
@@ -215,30 +211,8 @@ impl ToComputedValue for DropShadow {
         DropShadow {
             color: Some(ToComputedValue::from_computed_value(&computed.color)),
             horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
             vertical: ToComputedValue::from_computed_value(&computed.vertical),
             blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
         }
     }
 }
-
-#[cfg(feature = "gecko")]
-impl ToCss for DropShadow {
-    #[inline]
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-    where
-        W: fmt::Write,
-    {
-        if let Some(ref color) = self.color {
-            color.to_css(dest)?;
-            dest.write_str(" ")?;
-        }
-        self.horizontal.to_css(dest)?;
-        dest.write_str(" ")?;
-        self.vertical.to_css(dest)?;
-        if let Some(ref blur) = self.blur {
-            dest.write_str(" ")?;
-            blur.to_css(dest)?;
-        }
-        Ok(())
-    }
-}
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -346,18 +346,17 @@ impl PhysicalLength {
 
     /// Computes the given character width.
     pub fn to_computed_value(&self, context: &Context) -> Au {
         use gecko_bindings::bindings;
         // Same as Gecko
         const MM_PER_INCH: f32 = 25.4;
 
         let physical_inch = unsafe {
-            let pres_context = &*context.device.pres_context;
-            bindings::Gecko_GetAppUnitsPerPhysicalInch(&pres_context)
+            bindings::Gecko_GetAppUnitsPerPhysicalInch(context.device.pres_context())
         };
 
         let inch = self.0 / MM_PER_INCH;
 
         to_au_round(inch, physical_inch as f32)
     }
 }
 
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -22,17 +22,17 @@ pub type Position = GenericPosition<Hori
 /// The specified value of a horizontal position.
 pub type HorizontalPosition = PositionComponent<X>;
 
 /// The specified value of a vertical position.
 pub type VerticalPosition = PositionComponent<Y>;
 
 /// The specified value of a component of a CSS `<position>`.
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, ToCss)]
 pub enum PositionComponent<S> {
     /// `center`
     Center,
     /// `<lop>`
     Length(LengthOrPercentage),
     /// `<side> <lop>?`
     Side(S, Option<LengthOrPercentage>),
 }
@@ -192,37 +192,16 @@ impl<S: Parse> PositionComponent<S> {
 
 impl<S> PositionComponent<S> {
     /// `0%`
     pub fn zero() -> Self {
         PositionComponent::Length(LengthOrPercentage::Percentage(Percentage(0.)))
     }
 }
 
-impl<S: ToCss> ToCss for PositionComponent<S> {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        match *self {
-            PositionComponent::Center => {
-                dest.write_str("center")
-            },
-            PositionComponent::Length(ref lop) => {
-                lop.to_css(dest)
-            },
-            PositionComponent::Side(ref keyword, ref lop) => {
-                keyword.to_css(dest)?;
-                if let Some(ref lop) = *lop {
-                    dest.write_str(" ")?;
-                    lop.to_css(dest)?;
-                }
-                Ok(())
-            },
-        }
-    }
-}
-
 impl<S: Side> ToComputedValue for PositionComponent<S> {
     type ComputedValue = ComputedLengthOrPercentage;
 
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
             PositionComponent::Center => {
                 ComputedLengthOrPercentage::Percentage(Percentage(0.5))
             },
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -12,34 +12,32 @@ pub fn derive(input: syn::DeriveInput) -
     let mut where_clause = where_clause.clone();
     for param in &input.generics.ty_params {
         where_clause.predicates.push(where_predicate(syn::Ty::Path(None, param.ident.clone().into())))
     }
 
     let style = synstructure::BindStyle::Ref.into();
     let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
         let mut identifier = to_css_identifier(variant.ident.as_ref());
-        let mut expr = if let Some((first, rest)) = bindings.split_first() {
-            if has_free_params(&first.field.ty, &input.generics.ty_params) {
-                where_clause.predicates.push(where_predicate(first.field.ty.clone()));
-            }
-            let mut expr = quote! {
-                ::style_traits::ToCss::to_css(#first, dest)
-            };
-            for binding in rest {
+        let mut expr = if !bindings.is_empty() {
+            let mut expr = quote! {};
+            for binding in bindings {
                 if has_free_params(&binding.field.ty, &input.generics.ty_params) {
                     where_clause.predicates.push(where_predicate(binding.field.ty.clone()));
                 }
                 expr = quote! {
-                    #expr?;
-                    ::std::fmt::Write::write_str(dest, " ")?;
-                    ::style_traits::ToCss::to_css(#binding, dest)
+                    #expr
+                    writer.item(#binding)?;
                 };
             }
-            expr
+            quote! {{
+                let mut writer = ::style_traits::values::SequenceWriter::new(&mut *dest, " ");
+                #expr
+                Ok(())
+            }}
         } else {
             quote! {
                 ::std::fmt::Write::write_str(dest, #identifier)
             }
         };
         let mut css_attrs = variant.attrs.iter().filter(|attr| attr.name() == "css");
         let is_function = css_attrs.next().map_or(false, |attr| {
             match attr.value {
@@ -75,17 +73,17 @@ pub fn derive(input: syn::DeriveInput) -
                 ::std::fmt::Write::write_str(dest, ")")
             }
         }
         Some(expr)
     });
 
     quote! {
         impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
-            #[allow(unused_variables, unused_imports)]
+            #[allow(unused_variables)]
             #[inline]
             fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
             where
                 W: ::std::fmt::Write
             {
                 match *self {
                     #match_body
                 }
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -1,65 +1,191 @@
 /* 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/. */
 
 //! Helper types and traits for the handling of CSS values.
 
 use app_units::Au;
 use cssparser::{UnicodeRange, serialize_string};
-use std::fmt;
+use std::fmt::{self, Write};
 
 /// Serialises a value according to its CSS representation.
 ///
 /// This trait is implemented for `str` and its friends, serialising the string
 /// contents as a CSS quoted string.
 ///
 /// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:
 /// * unit variants get serialised as the `snake-case` representation
 ///   of their name;
 /// * unit variants whose name starts with "Moz" or "Webkit" are prepended
 ///   with a "-";
 /// * variants with fields get serialised as the space-separated serialisations
 ///   of their fields.
 pub trait ToCss {
     /// Serialize `self` in CSS syntax, writing to `dest`.
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write;
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write;
 
     /// Serialize `self` in CSS syntax and return a string.
     ///
     /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
     #[inline]
     fn to_css_string(&self) -> String {
         let mut s = String::new();
         self.to_css(&mut s).unwrap();
         s
     }
 }
 
 impl<'a, T> ToCss for &'a T where T: ToCss + ?Sized {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         (*self).to_css(dest)
     }
 }
 
 impl ToCss for str {
     #[inline]
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         serialize_string(self, dest)
     }
 }
 
 impl ToCss for String {
     #[inline]
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         serialize_string(self, dest)
     }
 }
 
+impl<T> ToCss for Option<T>
+where
+    T: ToCss,
+{
+    #[inline]
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
+        self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
+    }
+}
+
+/// Convenience wrapper to serialise CSS values separated by a given string.
+pub struct SequenceWriter<'a, W> {
+    writer: TrackedWriter<W>,
+    separator: &'a str,
+}
+
+impl<'a, W> SequenceWriter<'a, W>
+where
+    W: Write,
+{
+    /// Create a new sequence writer.
+    #[inline]
+    pub fn new(writer: W, separator: &'a str) -> Self {
+        SequenceWriter {
+            writer: TrackedWriter::new(writer),
+            separator: separator,
+        }
+    }
+
+    /// Serialises a CSS value, writing any separator as necessary.
+    ///
+    /// The separator is never written before any `item` produces any output,
+    /// and is written in subsequent calls only if the `item` produces some
+    /// output on its own again. This lets us handle `Option<T>` fields by
+    /// just not printing anything on `None`.
+    #[inline]
+    pub fn item<T>(&mut self, item: &T) -> fmt::Result
+    where
+        T: ToCss,
+    {
+        if self.writer.has_written {
+            item.to_css(&mut PrefixedWriter::new(&mut self.writer, self.separator))
+        } else {
+            item.to_css(&mut self.writer)
+        }
+    }
+}
+
+struct TrackedWriter<W> {
+    writer: W,
+    has_written: bool,
+}
+
+impl<W> TrackedWriter<W>
+where
+    W: Write,
+{
+    #[inline]
+    fn new(writer: W) -> Self {
+        TrackedWriter {
+            writer: writer,
+            has_written: false,
+        }
+    }
+}
+
+impl<W> Write for TrackedWriter<W>
+where
+    W: Write,
+{
+    #[inline]
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        if !s.is_empty() {
+            self.has_written = true;
+        }
+        self.writer.write_str(s)
+    }
+
+    #[inline]
+    fn write_char(&mut self, c: char) -> fmt::Result {
+        self.has_written = true;
+        self.writer.write_char(c)
+    }
+}
+
+struct PrefixedWriter<'a, W> {
+    writer: W,
+    prefix: Option<&'a str>,
+}
+
+impl<'a, W> PrefixedWriter<'a, W>
+where
+    W: Write,
+{
+    #[inline]
+    fn new(writer: W, prefix: &'a str) -> Self {
+        PrefixedWriter {
+            writer: writer,
+            prefix: Some(prefix),
+        }
+    }
+}
+
+impl<'a, W> Write for PrefixedWriter<'a, W>
+where
+    W: Write,
+{
+    #[inline]
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        if !s.is_empty() {
+            if let Some(prefix) = self.prefix.take() {
+                self.writer.write_str(prefix)?;
+            }
+        }
+        self.writer.write_str(s)
+    }
+
+    #[inline]
+    fn write_char(&mut self, c: char) -> fmt::Result {
+        if let Some(prefix) = self.prefix.take() {
+            self.writer.write_str(prefix)?;
+        }
+        self.writer.write_char(c)
+    }
+}
+
 /// Type used as the associated type in the `OneOrMoreSeparated` trait on a
 /// type to indicate that a serialized list of elements of this type is
 /// separated by commas.
 pub struct CommaSeparator;
 
 /// Type used as the associated type in the `OneOrMoreSeparated` trait on a
 /// type to indicate that a serialized list of elements of this type is
 /// separated by spaces.
@@ -98,45 +224,45 @@ pub trait OneOrMoreSeparated {
     type S: Separator;
 }
 
 impl OneOrMoreSeparated for UnicodeRange {
     type S = CommaSeparator;
 }
 
 impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreSeparated {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         let mut iter = self.iter();
         iter.next().unwrap().to_css(dest)?;
         for item in iter {
             dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;
             item.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 impl<T> ToCss for Box<T> where T: ?Sized + ToCss {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
-        where W: fmt::Write,
+        where W: Write,
     {
         (**self).to_css(dest)
     }
 }
 
 impl ToCss for Au {
-    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         write!(dest, "{}px", self.to_f64_px())
     }
 }
 
 macro_rules! impl_to_css_for_predefined_type {
     ($name: ty) => {
         impl<'a> ToCss for $name {
-            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
                 ::cssparser::ToCss::to_css(self, dest)
             }
         }
     };
 }
 
 impl_to_css_for_predefined_type!(f32);
 impl_to_css_for_predefined_type!(i32);
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1541,17 +1541,17 @@ pub extern "C" fn Servo_ComputedValues_I
     let maybe_arc = ComputedValues::arc_from_borrowed(&parent_style);
 
     let for_text = target == structs::InheritTarget::Text;
     let style = if let Some(reference) = maybe_arc.as_ref() {
         let mut style =
             StyleBuilder::for_inheritance(reference,
                                           &data.default_computed_values());
         if for_text {
-            StyleAdjuster::new(&mut style, /* is_root = */ false)
+            StyleAdjuster::new(&mut style)
                 .adjust_for_text();
         }
 
         Arc::new(style.build())
     } else {
         debug_assert!(!for_text);
         data.default_computed_values().clone()
     };
@@ -1603,18 +1603,18 @@ pub extern "C" fn Servo_StyleSet_Drop(da
 
 /// Updating the stylesheets and redoing selector matching is always happens
 /// before the document element is inserted. Therefore we don't need to call
 /// `force_dirty` here.
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: RawServoStyleSetBorrowed) {
     let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
     let quirks_mode = unsafe {
-        (*(*data.stylist.device().pres_context).mDocument
-                                               .raw::<nsIDocument>()).mCompatMode
+        (*data.stylist.device().pres_context().mDocument.raw::<nsIDocument>())
+            .mCompatMode
     };
 
     data.stylist.set_quirks_mode(quirks_mode.into());
 }
 
 fn parse_property_into(declarations: &mut SourcePropertyDeclaration,
                        property_id: PropertyId,
                        value: *const nsACString,
--- a/servo/python/servo/testing_commands.py
+++ b/servo/python/servo/testing_commands.py
@@ -40,17 +40,18 @@ from servo_tidy import tidy
 from servo_tidy_tests import test_tidy
 
 SCRIPT_PATH = os.path.split(__file__)[0]
 PROJECT_TOPLEVEL_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", ".."))
 WEB_PLATFORM_TESTS_PATH = os.path.join("tests", "wpt", "web-platform-tests")
 SERVO_TESTS_PATH = os.path.join("tests", "wpt", "mozilla", "tests")
 
 TEST_SUITES = OrderedDict([
-    ("tidy", {"kwargs": {"all_files": False, "no_progress": False, "self_test": False},
+    ("tidy", {"kwargs": {"all_files": False, "no_progress": False, "self_test": False,
+                         "stylo": False},
               "include_arg": "include"}),
     ("wpt", {"kwargs": {"release": False},
              "paths": [path.abspath(WEB_PLATFORM_TESTS_PATH),
                        path.abspath(SERVO_TESTS_PATH)],
              "include_arg": "include"}),
     ("css", {"kwargs": {"release": False},
              "paths": [path.abspath(path.join("tests", "wpt", "css-tests"))],
              "include_arg": "include"}),
@@ -106,17 +107,18 @@ class MachCommands(CommandBase):
                      help="Don't show progress for tidy")
     @CommandArgument('--self-test', default=False, action="store_true",
                      help="Run unit tests for tidy")
     @CommandArgument('--all', default=False, action="store_true", dest="all_suites",
                      help="Run all test suites")
     def test(self, params, render_mode=DEFAULT_RENDER_MODE, release=False, tidy_all=False,
              no_progress=False, self_test=False, all_suites=False):
         suites = copy.deepcopy(TEST_SUITES)
-        suites["tidy"]["kwargs"] = {"all_files": tidy_all, "no_progress": no_progress, "self_test": self_test}
+        suites["tidy"]["kwargs"] = {"all_files": tidy_all, "no_progress": no_progress, "self_test": self_test,
+                                    "stylo": False}
         suites["wpt"]["kwargs"] = {"release": release}
         suites["css"]["kwargs"] = {"release": release}
         suites["unit"]["kwargs"] = {}
         suites["compiletest"]["kwargs"] = {"release": release}
 
         selected_suites = OrderedDict()
 
         if params is None:
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -50,17 +50,16 @@ cppunit:
             default:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --cppunittest-suite=cppunittest
 
 crashtest:
@@ -98,17 +97,16 @@ crashtest:
                 script: desktop_unittest.py
                 chunked: false
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --reftest-suite=crashtest
     tier:
         by-test-platform:
@@ -131,16 +129,18 @@ external-media-tests-base:
             windows7-32/debug: built-projects
             default: built-projects
     mozharness:
         script: firefox_media_tests_taskcluster.py
         config:
             by-test-platform:
                 windows.*:
                     - mediatests/taskcluster_windows_config.py
+                macosx.*:
+                    - mediatests/taskcluster_posix_config.py
                 default:
                     - mediatests/taskcluster_posix_config.py
                     - remove_executables.py
 
 external-media-tests-twitch:
     description: "External Media Test Twitch run"
     suite: external-media-tests/twitch
     treeherder-symbol: tc-VP(b-t)
@@ -159,16 +159,18 @@ external-media-tests-twitch:
             windows7-32/debug: built-projects
             default: built-projects
     mozharness:
         script: firefox_media_tests_taskcluster.py
         config:
             by-test-platform:
                 windows.*:
                     - mediatests/taskcluster_windows_config.py
+                macosx.*:
+                    - mediatests/taskcluster_posix_config.py
                 default:
                     - mediatests/taskcluster_posix_config.py
                     - remove_executables.py
         extra-options:
             - "--suite=media-twitch-tests"
 
 external-media-tests-youtube:
     description: "External Media Test Youtube run"
@@ -186,16 +188,18 @@ external-media-tests-youtube:
             windows7-32/debug: built-projects
             default: built-projects
     mozharness:
         script: firefox_media_tests_taskcluster.py
         config:
             by-test-platform:
                 windows.*:
                     - mediatests/taskcluster_windows_config.py
+                macosx.*:
+                    - mediatests/taskcluster_posix_config.py
                 default:
                     - mediatests/taskcluster_posix_config.py
                     - remove_executables.py
         extra-options:
             - "--suite=media-youtube-tests"
 
 firefox-ui-functional-local:
     description: "Firefox-ui-tests functional run"
@@ -273,17 +277,16 @@ gtest:
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --gtest-suite=gtest
 
 jittest:
@@ -309,17 +312,16 @@ jittest:
             default: true
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --jittest-suite=jittest-chunked
     when:
         files-changed:
@@ -373,17 +375,16 @@ jsreftest:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 chunked: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --reftest-suite=jsreftest
     when:
         files-changed:
@@ -436,16 +437,18 @@ marionette:
                     - --test-suite=marionette
             default:
                 script: marionette.py
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
                         windows.*:
                             - marionette/windows_taskcluster_config.py
+                        macosx.*:
+                            - marionette/prod_config.py
                         default:
                             - marionette/prod_config.py
                             - remove_executables.py
 
 marionette-headless:
     description: "Marionette headless unittest run"
     suite: marionette
     treeherder-symbol: tc(MnH)
@@ -470,16 +473,18 @@ marionette-headless:
             default: ['all']
     mozharness:
         by-test-platform:
             default:
                 script: marionette.py
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
+                        macosx.*: 
+                            - marionette/prod_config.py
                         default:
                             - marionette/prod_config.py
                             - remove_executables.py
                 extra-options:
                     by-test-platform:
                         default:
                             - --headless
 
@@ -536,17 +541,16 @@ mochitest:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 chunked: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     by-test-platform:
                         linux64-jsdcov/opt:
                             - --mochitest-suite=plain-chunked-coverage
@@ -572,17 +576,16 @@ mochitest-a11y:
         no-read-buildbot-config: true
         chunked: false
         mochitest-flavor: a11y
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=a11y
 
 mochitest-browser-chrome:
@@ -622,17 +625,16 @@ mochitest-browser-chrome:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             by-test-platform:
                 linux64-jsdcov/opt:
                     - --mochitest-suite=browser-chrome-coverage
@@ -742,16 +744,18 @@ mochitest-chrome-style:
     docker-image: {"in-tree": "desktop1604-test"}
     e10s: false
     mozharness:
         mochitest-flavor: chrome
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                macosx.*:
+                    - unittests/mac_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=chrome-style
 
 mochitest-clipboard:
     description: "Mochitest clipboard run"
@@ -833,17 +837,16 @@ mochitest-devtools-chrome:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             by-test-platform:
                 linux64-jsdcov/opt:
                     - --mochitest-suite=mochitest-devtools-chrome-coverage
@@ -889,17 +892,16 @@ mochitest-gpu:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 chunked: false
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=plain-gpu,chrome-gpu,browser-chrome-gpu
     tier:
         by-test-platform:
@@ -926,17 +928,16 @@ mochitest-jetpack:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: false
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=jetpack-package
             - --mochitest-suite=jetpack-addon
 
@@ -983,17 +984,16 @@ mochitest-media:
                   by-test-platform:
                     macosx64.*: false
                     default: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=mochitest-media
     tier:
         by-test-platform:
@@ -1008,16 +1008,18 @@ mochitest-style:
     docker-image: {"in-tree": "desktop1604-test"}
     e10s: both
     mozharness:
         mochitest-flavor: plain
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
+                macosx.*:
+                    - unittests/mac_unittest.py
                 default:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=plain-style
 
 mochitest-valgrind:
     description: "Mochitest plain Valgrind run"
@@ -1035,16 +1037,18 @@ mochitest-valgrind:
         mochitest-flavor: plain
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
+                macosx.*:
+                    - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --mochitest-suite=valgrind-plain
 
 mochitest-webgl:
     description: "Mochitest webgl run"
@@ -1090,17 +1094,16 @@ mochitest-webgl:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 chunked: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --mochitest-suite=mochitest-gl
     tier:
         by-test-platform:
@@ -1151,17 +1154,16 @@ reftest:
                   by-test-platform:
                     macosx64/opt: false
                     default: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     - --reftest-suite=reftest
     tier:
         by-test-platform:
@@ -1185,17 +1187,16 @@ reftest-no-accel:
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - unittests/win_taskcluster_unittest.py
                 macosx.*:
-                    - remove_executables.py
                     - unittests/mac_unittest.py
                 linux.*:
                     - unittests/linux_unittest.py
                     - remove_executables.py
         extra-options:
             - --reftest-suite=reftest-no-accel
 
 reftest-stylo:
@@ -1233,18 +1234,22 @@ reftest-stylo:
             linux64-stylo-sequential/debug:
                 by-project:
                     mozilla-central: true
                     default: true
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
-            - unittests/linux_unittest.py
-            - remove_executables.py
+            by-test-platform:
+                macosx.*:
+                    - unittests/mac_unittest.py
+                default:
+                    - unittests/linux_unittest.py
+                    - remove_executables.py
         extra-options:
             - --reftest-suite=reftest-stylo
 
 robocop:
     description: "Robocop run"
     suite: robocop
     treeherder-symbol: tc-M(rc)
     instance-size: xlarge
@@ -1338,17 +1343,17 @@ talos-g1:
             - --add-option
             - --webServer,localhost
 
 talos-g2:
     description: "Talos g2"
     suite: talos
     try-name: g2
     treeherder-symbol: tc-T(g2)
-    max-run-time: 3600
+    max-run-time: 7200
     run-on-projects:
         by-test-platform:
             linux64-stylo/.*: ['mozilla-central', 'try']
             linux64-stylo-sequential/.*: ['try']
             default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
     e10s: true
     mozharness:
         script: talos_script.py
@@ -1426,17 +1431,16 @@ talos-g5:
     max-run-time: 3600
     e10s: true
     mozharness:
         script: talos_script.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 macosx.*:
-                    - remove_executables.py
                     - talos/mac_config.py
                 default:
                     - talos/linux_config.py
                     - remove_executables.py
         extra-options:
             - --suite=g5
             - --add-option
             - --webServer,localhost
@@ -1564,16 +1568,18 @@ web-platform-tests:
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         chunked: true
         config:
             by-test-platform:
                 windows.*:
                     - web_platform_tests/prod_config_windows_taskcluster.py
+                macosx.*:
+                    - web_platform_tests/prod_config.py
                 default:
                     - web_platform_tests/prod_config.py
                     - remove_executables.py
         extra-options:
             - --test-type=testharness
 
 web-platform-tests-reftests:
     description: "Web platform reftest run"
@@ -1596,16 +1602,18 @@ web-platform-tests-reftests:
             default: built-projects
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - web_platform_tests/prod_config_windows_taskcluster.py
+                macosx.*:
+                    - web_platform_tests/prod_config.py
                 default:
                     - web_platform_tests/prod_config.py
                     - remove_executables.py
         extra-options:
             - --test-type=reftest
 
 web-platform-tests-wdspec:
     description: "Web platform webdriver-spec run"
@@ -1617,16 +1625,18 @@ web-platform-tests-wdspec:
     checkout: true
     mozharness:
         script: web_platform_tests.py
         no-read-buildbot-config: true
         config:
             by-test-platform:
                 windows.*:
                     - web_platform_tests/prod_config_windows_taskcluster.py
+                macosx.*:
+                    - web_platform_tests/prod_config.py
                 default:
                     - web_platform_tests/prod_config.py
                     - remove_executables.py
         extra-options:
             - --test-type=wdspec
 
 xpcshell:
     description: "xpcshell test run"
@@ -1671,17 +1681,16 @@ xpcshell:
             default:
                 script: desktop_unittest.py
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
                         windows.*:
                             - unittests/win_taskcluster_unittest.py
                         macosx.*:
-                            - remove_executables.py
                             - unittests/mac_unittest.py
                         linux.*:
                             - unittests/linux_unittest.py
                             - remove_executables.py
                 extra-options:
                     by-test-platform:
                         linux64-jsdcov/opt:
                             - --xpcshell-suite=xpcshell-coverage
--- a/testing/mozbase/mozlog/mozlog/pytest_mozlog/plugin.py
+++ b/testing/mozbase/mozlog/mozlog/pytest_mozlog/plugin.py
@@ -1,15 +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/.
 
 import mozlog
 import time
 
+import pytest
+
 
 def pytest_addoption(parser):
     # We can't simply use mozlog.commandline.add_logging_group(parser) here because
     # Pytest's parser doesn't have the add_argument_group method Mozlog expects.
     group = parser.getgroup('mozlog')
 
     for name, (_class, _help) in mozlog.commandline.log_formatters.iteritems():
         group.addoption('--log-{0}'.format(name), action='append', help=_help)
@@ -56,16 +58,17 @@ class MozLog(object):
         '''Called before test collection; records suite start time to log later'''
         self.start_time = int(time.time() * 1000)  # in ms for Mozlog compatibility
         self.run_info = getattr(session.config, '_metadata', None)
 
     def pytest_collection_finish(self, session):
         '''Called after test collection is completed, just before tests are run (suite start)'''
         self._log_suite_start([item.nodeid for item in session.items])
 
+    @pytest.mark.optionalhook
     def pytest_xdist_node_collection_finished(self, node, ids):
         '''Called after each pytest-xdist node collection is completed'''
         self._log_suite_start(ids)
 
     def pytest_sessionfinish(self, session, exitstatus):
         self.logger.suite_end()
 
     def pytest_runtest_logstart(self, nodeid, location):
--- a/testing/mozharness/mozharness/base/vcs/mercurial.py
+++ b/testing/mozharness/mozharness/base/vcs/mercurial.py
@@ -365,17 +365,17 @@ class MercurialVCS(ScriptMixin, LogMixin
             raise VCSException('vcs share base not defined; '
                                'refusing to operate sub-optimally')
 
         if not self.robustcheckout_path:
             raise VCSException('could not find the robustcheckout Mercurial extension')
 
         # Log HG version and install info to aid debugging.
         self.run_command(self.hg + ['--version'])
-        self.run_command(self.hg + ['debuginstall'])
+        self.run_command(self.hg + ['debuginstall', '--config=ui.username=worker'])
 
         args = self.hg + [
             '--config', 'extensions.robustcheckout=%s' % self.robustcheckout_path,
             'robustcheckout', repo_url, dest, '--sharebase', share_base,
         ]
         if purge:
             args.append('--purge')
         if upstream:
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
@@ -51,16 +51,17 @@ def run(path, server_config, session_con
 
     # TODO(ato): Deal with timeouts
 
     with TemporaryDirectory() as cache:
         pytest.main(["--strict",  # turn warnings into errors
                      "--verbose",  # show each individual subtest
                      "--capture", "no",  # enable stdout/stderr from tests
                      "--basetemp", cache,  # temporary directory
+                     "-p", "no:mozlog",
                      path],
                     plugins=plugins)
 
     return recorder.results
 
 
 class SubtestResultRecorder(object):
     def __init__(self):
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -11,29 +11,28 @@
 #include "nsIContent.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsISelection.h"
 #include "nsISelectionController.h"
 #include "nsIFrame.h"
 #include "nsITextControlFrame.h"
 #include "nsIFormControl.h"
-#include "nsIEditor.h"
-#include "nsIPlaintextEditor.h"
 #include "nsTextFragment.h"
 #include "nsString.h"
 #include "nsIAtom.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIDOMElement.h"
 #include "nsIWordBreaker.h"
 #include "nsCRT.h"
 #include "nsRange.h"
 #include "nsContentUtils.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/TextEditor.h"
 
 using namespace mozilla;
 
 // Yikes!  Casting a char to unichar can fill with ones!
 #define CHAR_TO_UNICHAR(c) ((char16_t)(const unsigned char)c)
 
 static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
 static NS_DEFINE_CID(kCPreContentIteratorCID, NS_PRECONTENTITERATOR_CID);
@@ -368,31 +367,24 @@ nsFindContentIterator::SetupInnerIterato
   }
   NS_ASSERTION(!aContent->IsRootOfNativeAnonymousSubtree(), "invalid call");
 
   nsITextControlFrame* tcFrame = do_QueryFrame(aContent->GetPrimaryFrame());
   if (!tcFrame) {
     return;
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  tcFrame->GetEditor(getter_AddRefs(editor));
-  if (!editor) {
-    return;
-  }
-
   // don't mess with disabled input fields
-  uint32_t editorFlags = 0;
-  editor->GetFlags(&editorFlags);
-  if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask) {
+  RefPtr<TextEditor> textEditor = tcFrame->GetTextEditor();
+  if (!textEditor || textEditor->IsDisabled()) {
     return;
   }
 
   nsCOMPtr<nsIDOMElement> rootElement;
-  editor->GetRootElement(getter_AddRefs(rootElement));
+  textEditor->GetRootElement(getter_AddRefs(rootElement));
 
   nsCOMPtr<nsIDOMRange> innerRange = CreateRange(aContent);
   nsCOMPtr<nsIDOMRange> outerRange = CreateRange(aContent);
   if (!innerRange || !outerRange) {
     return;
   }
 
   // now create the inner-iterator
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -231,17 +231,23 @@ var Settings = {
           Cu.import("resource://gre/modules/Messaging.jsm");
           EventDispatcher.instance.sendRequest({
             type: "Settings:Show",
             resource: "preferences_privacy",
           });
         } else {
           // Show the data choices preferences on desktop.
           let mainWindow = getMainWindowWithPreferencesPane();
-          mainWindow.openPreferences("privacy-reports", {origin: "aboutTelemetry"});
+          // The advanced subpanes are only supported in the old organization,
+          // which will be removed by bug 1349689.
+          if (Preferences.get("browser.preferences.useOldOrganization")) {
+            mainWindow.openAdvancedPreferences("dataChoicesTab", {origin: "aboutTelemetry"});
+          } else {
+            mainWindow.openPreferences("privacy-reports", {origin: "aboutTelemetry"});
+          }
         }
       });
     }
   },
 
   detachObservers() {
     for (let setting of this.SETTINGS) {
       Preferences.ignore(setting.pref, this.render, this);