Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Tue, 10 Apr 2018 12:56:48 +0300
changeset 468627 a8061a09cd7064a8783ca9e67979d77fb52e001e
parent 468571 db03e2cdae9ab05482f98e3c567896234c66e226 (current diff)
parent 468626 73a3eabd2bd29d2ef8945648615f49ed29b326c9 (diff)
child 468628 50b945170f334bb5c7c3dad5a8e975251a369b3b
child 468638 42a2db5c685491852db3561e7e06661adbf0ab0d
child 468655 7e2492c45ab41d26f131d64ebc9a3e10fae65e9f
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
a8061a09cd70 / 61.0a1 / 20180410100115 / files
nightly linux64
a8061a09cd70 / 61.0a1 / 20180410100115 / files
nightly mac
a8061a09cd70 / 61.0a1 / 20180410100115 / files
nightly win32
a8061a09cd70 / 61.0a1 / 20180410100115 / files
nightly win64
a8061a09cd70 / 61.0a1 / 20180410100115 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/base/nsIDOMSerializer.idl
dom/interfaces/core/nsIDOMXMLDocument.idl
dom/interfaces/xul/nsIDOMXULElement.idl
dom/media/test/dynamic_redirect.sjs
dom/webbrowserpersist/nsIWebBrowserPersistable.idl
layout/painting/nsDisplayList.cpp
modules/libpref/init/all.js
testing/web-platform/meta/css/css-grid/alignment/grid-gutters-010.html.ini
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -27,17 +27,16 @@
 #include "StyleInfo.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
 #include "TreeWalker.h"
 #include "XULDocument.h"
 
 #include "nsIDOMElement.h"
 #include "nsIDOMXULButtonElement.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDOMXULLabelElement.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsINodeList.h"
 #include "nsPIDOMWindow.h"
 
 #include "nsIDocument.h"
 #include "nsIContent.h"
--- a/accessible/generic/FormControlAccessible.cpp
+++ b/accessible/generic/FormControlAccessible.cpp
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // NOTE: alphabetically ordered
 
 #include "FormControlAccessible.h"
 #include "Role.h"
 
 #include "mozilla/FloatingPoint.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDOMXULControlElement.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // ProgressMeterAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -43,17 +43,16 @@ const nsIAccessibleTraversalRule = Ci.ns
 const nsIAccessibleValue = Ci.nsIAccessibleValue;
 
 const nsIObserverService = Ci.nsIObserverService;
 
 const nsIDOMDocument = Ci.nsIDOMDocument;
 const nsIDOMEvent = Ci.nsIDOMEvent;
 const nsIDOMNode = Ci.nsIDOMNode;
 const nsIDOMWindow = Ci.nsIDOMWindow;
-const nsIDOMXULElement = Ci.nsIDOMXULElement;
 
 const nsIPropertyElement = Ci.nsIPropertyElement;
 
 // //////////////////////////////////////////////////////////////////////////////
 // OS detect
 
 const MAC = (navigator.platform.includes("Mac"));
 const LINUX = (navigator.platform.includes("Linux"));
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1064,26 +1064,26 @@ function synthClick(aNodeOrID, aCheckerO
     if (targetNode instanceof nsIDOMDocument) {
       targetNode =
         this.DOMNode.body ? this.DOMNode.body : this.DOMNode.documentElement;
     }
 
     // Scroll the node into view, otherwise synth click may fail.
     if (isHTMLElement(targetNode)) {
       targetNode.scrollIntoView(true);
-    } else if (targetNode instanceof nsIDOMXULElement) {
+    } else if (ChromeUtils.getClassName(targetNode) == "XULElement") {
       var targetAcc = getAccessible(targetNode);
       targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE);
     }
 
     var x = 1, y = 1;
     if (aArgs && ("where" in aArgs) && aArgs.where == "right") {
       if (isHTMLElement(targetNode))
         x = targetNode.offsetWidth - 1;
-      else if (targetNode instanceof nsIDOMXULElement)
+    else if (ChromeUtils.getClassName(targetNode) == "XULElement")
         x = targetNode.boxObject.width - 1;
     }
     synthesizeMouse(targetNode, x, y, aArgs ? aArgs : {});
   };
 
   this.finalCheck = function synthClick_finalCheck() {
     // Scroll top window back.
     window.top.scrollTo(0, 0);
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -9,17 +9,16 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
 #include "XULFormControlAccessible.h"
 
 #include "nsIDOMElement.h"
-#include "nsIDOMXULElement.h"
 #include "nsIMutableArray.h"
 #include "nsIDOMXULContainerElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIServiceManager.h"
 #include "nsIPresShell.h"
 #include "nsIContent.h"
 #include "nsMenuBarFrame.h"
--- a/accessible/xul/XULSelectControlAccessible.cpp
+++ b/accessible/xul/XULSelectControlAccessible.cpp
@@ -8,17 +8,16 @@
 
 #include "nsAccessibilityService.h"
 #include "DocAccessible.h"
 
 #include "nsIDOMXULContainerElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMElement.h"
-#include "nsIDOMXULElement.h"
 #include "nsIMutableArray.h"
 #include "nsIServiceManager.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyboardEventBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -19,17 +19,16 @@
 #include "XULTreeGridAccessible.h"
 #include "nsQueryObject.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIAutoCompleteInput.h"
 #include "nsIAutoCompletePopup.h"
 #include "nsIBoxObject.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULTreeElement.h"
 #include "nsITreeSelection.h"
 #include "nsIMutableArray.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
--- a/browser/base/content/test/contextMenu/browser_contextmenu_linkopen.js
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_linkopen.js
@@ -13,25 +13,25 @@ async function activateContextAndWaitFor
   switch (where) {
     case "tab":
       contextMenuItem += "intab";
       openPromise = BrowserTestUtils.waitForNewTab(gBrowser, TEST_LINK, false);
       closeMethod = async (tab) => BrowserTestUtils.removeTab(tab);
       break;
     case "privatewindow":
       contextMenuItem += "private";
-      openPromise = BrowserTestUtils.waitForNewWindow(TEST_LINK).then(win => {
+      openPromise = BrowserTestUtils.waitForNewWindow({url: TEST_LINK}).then(win => {
         ok(PrivateBrowsingUtils.isWindowPrivate(win), "Should have opened a private window.");
         return win;
       });
       closeMethod = async (win) => BrowserTestUtils.closeWindow(win);
       break;
     case "window":
       // No contextMenuItem suffix for normal new windows;
-      openPromise = BrowserTestUtils.waitForNewWindow(TEST_LINK).then(win => {
+      openPromise = BrowserTestUtils.waitForNewWindow({url: TEST_LINK}).then(win => {
         ok(!PrivateBrowsingUtils.isWindowPrivate(win), "Should have opened a normal window.");
         return win;
       });
       closeMethod = async (win) => BrowserTestUtils.closeWindow(win);
       break;
   }
   let contextMenu = document.getElementById("contentAreaContextMenu");
   is(contextMenu.state, "closed", "checking if popup is closed");
--- a/browser/base/content/test/general/browser_newTabDrop.js
+++ b/browser/base/content/test/general/browser_newTabDrop.js
@@ -1,166 +1,219 @@
+const ANY_URL = undefined;
+
 registerCleanupFunction(async function cleanup() {
   while (gBrowser.tabs.length > 1) {
     BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
   }
   Services.search.currentEngine = originalEngine;
   let engine = Services.search.getEngineByName("MozSearch");
   Services.search.removeEngine(engine);
 });
 
 let originalEngine;
 add_task(async function test_setup() {
+  // This test opens multiple tabs and some confirm dialogs, that takes long.
+  requestLongerTimeout(2);
+
   // Stop search-engine loads from hitting the network
   Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET",
                                        "http://example.com/?q={searchTerms}");
   let engine = Services.search.getEngineByName("MozSearch");
   originalEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
 });
 
 // New Tab Button opens any link.
-add_task(async function() { await dropText("mochi.test/first", 1); });
-add_task(async function() { await dropText("javascript:'bad'", 1); });
-add_task(async function() { await dropText("jAvascript:'bad'", 1); });
-add_task(async function() { await dropText("mochi.test/second", 1); });
-add_task(async function() { await dropText("data:text/html,bad", 1); });
-add_task(async function() { await dropText("mochi.test/third", 1); });
+add_task(async function single_url() {
+  await dropText("mochi.test/first",
+                 ["http://www.mochi.test/first"]);
+});
+add_task(async function single_javascript() {
+  await dropText("javascript:'bad'",
+                 ["javascript:'bad'"]);
+});
+add_task(async function single_javascript_capital() {
+  await dropText("jAvascript:'bad'",
+                 ["javascript:'bad'"]);
+});
+add_task(async function single_url2() {
+  await dropText("mochi.test/second",
+                 ["http://www.mochi.test/second"]);
+});
+add_task(async function single_data_url() {
+  await dropText("data:text/html,bad",
+                 ["data:text/html,bad"]);
+});
+add_task(async function single_url3() {
+  await dropText("mochi.test/third",
+                 ["http://www.mochi.test/third"]);
+});
 
 // Single text/plain item, with multiple links.
-add_task(async function() { await dropText("mochi.test/1\nmochi.test/2", 2); });
-add_task(async function() { await dropText("javascript:'bad1'\nmochi.test/3", 2); });
-add_task(async function() { await dropText("mochi.test/4\ndata:text/html,bad1", 2); });
+add_task(async function multiple_urls() {
+  await dropText("mochi.test/1\nmochi.test/2",
+                 [
+                   "http://www.mochi.test/1",
+                   "http://www.mochi.test/2",
+                 ]);
+});
+add_task(async function multiple_urls_javascript() {
+  await dropText("javascript:'bad1'\nmochi.test/3",
+                 [
+                   "javascript:'bad1'",
+                   "http://www.mochi.test/3",
+                 ]);
+});
+add_task(async function multiple_urls_data() {
+  await dropText("mochi.test/4\ndata:text/html,bad1",
+                 [
+                   "http://www.mochi.test/4",
+                   "data:text/html,bad1",
+                 ]);
+});
 
 // Multiple text/plain items, with single and multiple links.
-add_task(async function() {
+add_task(async function multiple_items_single_and_multiple_links() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/5"}],
               [{type: "text/plain",
-                data: "mochi.test/6\nmochi.test/7"}]], 3);
+                data: "mochi.test/6\nmochi.test/7"}]],
+             [
+               "http://www.mochi.test/5",
+               "http://www.mochi.test/6",
+               "http://www.mochi.test/7",
+             ]);
 });
 
 // Single text/x-moz-url item, with multiple links.
 // "text/x-moz-url" has titles in even-numbered lines.
-add_task(async function() {
+add_task(async function single_moz_url_multiple_links() {
   await drop([[{type: "text/x-moz-url",
-                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]], 2);
+                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]],
+             [
+               "http://www.mochi.test/8",
+               "http://www.mochi.test/9",
+             ]);
 });
 
 // Single item with multiple types.
-add_task(async function() {
+add_task(async function single_item_multiple_types() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/10"},
                {type: "text/x-moz-url",
-                data: "mochi.test/11\nTITLE11"}]], 1);
+                data: "mochi.test/11\nTITLE11"}]],
+             ["http://www.mochi.test/11"]);
 });
 
 // Warn when too many URLs are dropped.
 add_task(async function multiple_tabs_under_max() {
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/multi" + i);
   }
-  await dropText(urls.join("\n"), 5);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/multi0",
+                   "http://www.mochi.test/multi1",
+                   "http://www.mochi.test/multi2",
+                   "http://www.mochi.test/multi3",
+                   "http://www.mochi.test/multi4",
+                 ]);
 });
 add_task(async function multiple_tabs_over_max_accept() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/accept" + i);
   }
-  await dropText(urls.join("\n"), 5);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/accept0",
+                   "http://www.mochi.test/accept1",
+                   "http://www.mochi.test/accept2",
+                   "http://www.mochi.test/accept3",
+                   "http://www.mochi.test/accept4",
+                 ]);
 
   await confirmPromise;
 
   await popPrefs();
 });
 add_task(async function multiple_tabs_over_max_cancel() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/cancel" + i);
   }
-  await dropText(urls.join("\n"), 0);
+  await dropText(urls.join("\n"), []);
 
   await confirmPromise;
 
   await popPrefs();
 });
 
 // Open URLs ignoring non-URL.
 add_task(async function multiple_urls() {
   await dropText(`
     mochi.test/urls0
     mochi.test/urls1
     mochi.test/urls2
     non url0
     mochi.test/urls3
     non url1
     non url2
-`, 4);
+`, [
+  "http://www.mochi.test/urls0",
+  "http://www.mochi.test/urls1",
+  "http://www.mochi.test/urls2",
+  "http://www.mochi.test/urls3",
+]);
 });
 
 // Open single search if there's no URL.
 add_task(async function multiple_text() {
   await dropText(`
     non url0
     non url1
     non url2
-`, 1);
+`, [
+  ANY_URL
+]);
 });
 
-function dropText(text, expectedTabOpenCount = 0) {
-  return drop([[{type: "text/plain", data: text}]], expectedTabOpenCount);
+function dropText(text, expectedURLs) {
+  return drop([[{type: "text/plain", data: text}]], expectedURLs);
 }
 
-async function drop(dragData, expectedTabOpenCount = 0) {
+async function drop(dragData, expectedURLs) {
   let dragDataString = JSON.stringify(dragData);
-  info(`Starting test for datagData:${dragDataString}; expectedTabOpenCount:${expectedTabOpenCount}`);
+  info(`Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}`);
   let EventUtils = {};
   Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
   // Since synthesizeDrop triggers the srcElement, need to use another button.
   let dragSrcElement = document.getElementById("downloads-button");
   ok(dragSrcElement, "Downloads button exists");
   let newTabButton = document.getElementById("new-tab-button");
   ok(newTabButton, "New Tab button exists");
 
   let awaitDrop = BrowserTestUtils.waitForEvent(newTabButton, "drop");
-  let actualTabOpenCount = 0;
-  let openedTabs = [];
-  let promiseTabsOpen = null;
-  let resolve = null;
-  let handler = function(event) {
-    openedTabs.push(event.target);
-    actualTabOpenCount++;
-    if (actualTabOpenCount == expectedTabOpenCount && resolve) {
-      resolve();
-    }
-  };
-  gBrowser.tabContainer.addEventListener("TabOpen", handler);
-  if (expectedTabOpenCount) {
-    promiseTabsOpen = new Promise(r => { resolve = r; });
-  }
+
+  let loadedPromises = expectedURLs.map(
+    url => BrowserTestUtils.waitForNewTab(gBrowser, url, false, true));
 
   EventUtils.synthesizeDrop(dragSrcElement, newTabButton, dragData, "link", window);
 
-  if (promiseTabsOpen) {
-    await promiseTabsOpen;
-    info("Got TabOpen event");
-    for (let tab of openedTabs) {
-      BrowserTestUtils.removeTab(tab);
-    }
+  let tabs = await Promise.all(loadedPromises);
+  for (let tab of tabs) {
+    BrowserTestUtils.removeTab(tab);
   }
 
   await awaitDrop;
   ok(true, "Got drop event");
-
-  gBrowser.tabContainer.removeEventListener("TabOpen", handler);
-  is(actualTabOpenCount, expectedTabOpenCount,
-     "No other tabs are opened");
 }
--- a/browser/base/content/test/general/browser_newWindowDrop.js
+++ b/browser/base/content/test/general/browser_newWindowDrop.js
@@ -26,143 +26,184 @@ add_task(async function test_setup() {
     CustomizableUI.ensureWidgetPlacedInWindow("new-window-button", window);
     registerCleanupFunction(function() {
       CustomizableUI.removeWidgetFromArea("new-window-button");
     });
   }
 });
 
 // New Window Button opens any link.
-add_task(async function() { await dropText("mochi.test/first", 1); });
-add_task(async function() { await dropText("javascript:'bad'", 1); });
-add_task(async function() { await dropText("jAvascript:'bad'", 1); });
-add_task(async function() { await dropText("mochi.test/second", 1); });
-add_task(async function() { await dropText("data:text/html,bad", 1); });
-add_task(async function() { await dropText("mochi.test/third", 1); });
+add_task(async function single_url() {
+  await dropText("mochi.test/first",
+                 ["http://www.mochi.test/first"]);
+});
+add_task(async function single_javascript() {
+  await dropText("javascript:'bad'",
+                 ["about:blank"]);
+});
+add_task(async function single_javascript_capital() {
+  await dropText("jAvascript:'bad'",
+                 ["about:blank"]);
+});
+add_task(async function single_url2() {
+  await dropText("mochi.test/second",
+                 ["http://www.mochi.test/second"]);
+});
+add_task(async function single_data_url() {
+  await dropText("data:text/html,bad",
+                 ["data:text/html,bad"]);
+});
+add_task(async function single_url3() {
+  await dropText("mochi.test/third",
+                 ["http://www.mochi.test/third"]);
+});
 
 // Single text/plain item, with multiple links.
-add_task(async function() { await dropText("mochi.test/1\nmochi.test/2", 2); });
-add_task(async function() { await dropText("javascript:'bad1'\nmochi.test/3", 2); });
-add_task(async function() { await dropText("mochi.test/4\ndata:text/html,bad1", 2); });
+add_task(async function multiple_urls() {
+  await dropText("mochi.test/1\nmochi.test/2",
+                 [
+                   "http://www.mochi.test/1",
+                   "http://www.mochi.test/2",
+                 ]);
+});
+add_task(async function multiple_urls_javascript() {
+  await dropText("javascript:'bad1'\nmochi.test/3",
+                 [
+                   "javascript:'bad1'",
+                   "http://www.mochi.test/3",
+                 ]);
+});
+add_task(async function multiple_urls_data() {
+  await dropText("mochi.test/4\ndata:text/html,bad1",
+                 [
+                   "http://www.mochi.test/4",
+                   "data:text/html,bad1",
+                 ]);
+});
 
 // Multiple text/plain items, with single and multiple links.
-add_task(async function() {
+add_task(async function multiple_items_single_and_multiple_links() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/5"}],
               [{type: "text/plain",
-                data: "mochi.test/6\nmochi.test/7"}]], 3);
+                data: "mochi.test/6\nmochi.test/7"}]],
+             [
+               "http://www.mochi.test/5",
+               "http://www.mochi.test/6",
+               "http://www.mochi.test/7",
+             ]);
 });
 
 // Single text/x-moz-url item, with multiple links.
 // "text/x-moz-url" has titles in even-numbered lines.
-add_task(async function() {
+add_task(async function single_moz_url_multiple_links() {
   await drop([[{type: "text/x-moz-url",
-                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]], 2);
+                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]],
+             [
+               "http://www.mochi.test/8",
+               "http://www.mochi.test/9",
+             ]);
 });
 
 // Single item with multiple types.
-add_task(async function() {
+add_task(async function single_item_multiple_types() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/10"},
                {type: "text/x-moz-url",
-                data: "mochi.test/11\nTITLE11"}]], 1);
+                data: "mochi.test/11\nTITLE11"}]],
+             ["http://www.mochi.test/11"]);
 });
 
 // Warn when too many URLs are dropped.
 add_task(async function multiple_tabs_under_max() {
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/multi" + i);
   }
-  await dropText(urls.join("\n"), 5);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/multi0",
+                   "http://www.mochi.test/multi1",
+                   "http://www.mochi.test/multi2",
+                   "http://www.mochi.test/multi3",
+                   "http://www.mochi.test/multi4",
+                 ]);
 });
 add_task(async function multiple_tabs_over_max_accept() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/accept" + i);
   }
-  await dropText(urls.join("\n"), 5, true);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/accept0",
+                   "http://www.mochi.test/accept1",
+                   "http://www.mochi.test/accept2",
+                   "http://www.mochi.test/accept3",
+                   "http://www.mochi.test/accept4",
+                 ],
+                 true);
 
   await confirmPromise;
 
   await popPrefs();
 });
 add_task(async function multiple_tabs_over_max_cancel() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/cancel" + i);
   }
-  await dropText(urls.join("\n"), 0, true);
+  await dropText(urls.join("\n"), [], true);
 
   await confirmPromise;
 
   await popPrefs();
 });
 
-function dropText(text, expectedWindowOpenCount = 0,
+function dropText(text, expectedURLs,
                   ignoreFirstWindow = false) {
-  return drop([[{type: "text/plain", data: text}]], expectedWindowOpenCount,
+  return drop([[{type: "text/plain", data: text}]], expectedURLs,
               ignoreFirstWindow);
 }
 
-async function drop(dragData, expectedWindowOpenCount = 0,
+async function drop(dragData, expectedURLs,
                     ignoreFirstWindow = false) {
   let dragDataString = JSON.stringify(dragData);
-  info(`Starting test for datagData:${dragDataString}; expectedWindowOpenCount:${expectedWindowOpenCount}`);
+  info(`Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}`);
   let EventUtils = {};
   Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
   // Since synthesizeDrop triggers the srcElement, need to use another button.
   let dragSrcElement = document.getElementById("downloads-button");
   ok(dragSrcElement, "Downloads button exists");
   let newWindowButton = document.getElementById("new-window-button");
   ok(newWindowButton, "New Window button exists");
 
   let tmp = {};
   ChromeUtils.import("resource://testing-common/TestUtils.jsm", tmp);
 
   let awaitDrop = BrowserTestUtils.waitForEvent(newWindowButton, "drop");
-  let actualWindowOpenCount = 0;
-  let openedWindows = [];
-  let checkCount = function(window) {
-    if (ignoreFirstWindow) {
-      // When dropping too many dialog, the confirm dialog is opened and
-      // domwindowopened notification is dispatched for it, before opening
-      // windows for dropped items.  Ignore it.
-      ignoreFirstWindow = false;
-      return false;
-    }
 
-    // Add observer as soon as domWindow is opened to avoid missing the topic.
-    let awaitStartup = tmp.TestUtils.topicObserved("browser-delayed-startup-finished",
-                                                   subject => subject == window);
-    openedWindows.push([window, awaitStartup]);
-    actualWindowOpenCount++;
-    return actualWindowOpenCount == expectedWindowOpenCount;
-  };
-  let awaitWindowOpen = expectedWindowOpenCount && BrowserTestUtils.domWindowOpened(null, checkCount);
+  let loadedPromises = expectedURLs.map(
+    url => BrowserTestUtils.waitForNewWindow({
+      url,
+      anyWindow: true,
+      maybeErrorPage: true,
+    }));
 
   EventUtils.synthesizeDrop(dragSrcElement, newWindowButton, dragData, "link", window);
 
-  let windowsOpened = false;
-  if (awaitWindowOpen) {
-    await awaitWindowOpen;
-    info("Got Window opened");
-    windowsOpened = true;
-    for (let [window, awaitStartup] of openedWindows.reverse()) {
-      // Wait for startup before closing, to properly close the browser window.
-      await awaitStartup;
-      await BrowserTestUtils.closeWindow(window);
-    }
+  let windows = await Promise.all(loadedPromises);
+  for (let window of windows) {
+    await BrowserTestUtils.closeWindow(window);
   }
-  is(windowsOpened, !!expectedWindowOpenCount, `Windows for ${dragDataString} should only open if any of dropped items are valid`);
 
   await awaitDrop;
   ok(true, "Got drop event");
 }
--- a/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
+++ b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
@@ -19,33 +19,33 @@ add_task(async function() {
   // Set pref to open in new window.
   Services.prefs.setIntPref("browser.link.open_newwindow", 2);
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("browser.link.open_newwindow");
   });
 
   // Open new http window from JavaScript in file:// page and check that we get
   // a new window with the correct page and features.
-  let promiseNewWindow = BrowserTestUtils.waitForNewWindow(TEST_HTTP);
+  let promiseNewWindow = BrowserTestUtils.waitForNewWindow({url: TEST_HTTP});
   await ContentTask.spawn(browser, TEST_HTTP, uri => {
     content.open(uri, "_blank");
   });
   let win = await promiseNewWindow;
   registerCleanupFunction(async function() {
     await BrowserTestUtils.closeWindow(win);
   });
   ok(win, "Check that an http window loaded when using window.open.");
   ok(win.menubar.visible,
      "Check that the menu bar on the new window is visible.");
   ok(win.toolbar.visible,
      "Check that the tool bar on the new window is visible.");
 
   // Open new http window from a link in file:// page and check that we get a
   // new window with the correct page and features.
-  promiseNewWindow = BrowserTestUtils.waitForNewWindow(TEST_HTTP);
+  promiseNewWindow = BrowserTestUtils.waitForNewWindow({url: TEST_HTTP});
   await BrowserTestUtils.synthesizeMouseAtCenter("#linkToExample", {}, browser);
   let win2 = await promiseNewWindow;
   registerCleanupFunction(async function() {
     await BrowserTestUtils.closeWindow(win2);
   });
   ok(win2, "Check that an http window loaded when using link.");
   ok(win2.menubar.visible,
      "Check that the menu bar on the new window is visible.");
--- a/browser/base/content/test/general/browser_tabDrop.js
+++ b/browser/base/content/test/general/browser_tabDrop.js
@@ -1,8 +1,10 @@
+const ANY_URL = undefined;
+
 registerCleanupFunction(async function cleanup() {
   while (gBrowser.tabs.length > 1) {
     BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
   }
   Services.search.currentEngine = originalEngine;
   let engine = Services.search.getEngineByName("MozSearch");
   Services.search.removeEngine(engine);
 });
@@ -12,129 +14,172 @@ add_task(async function test_setup() {
   // Stop search-engine loads from hitting the network
   Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET",
                                        "http://example.com/?q={searchTerms}");
   let engine = Services.search.getEngineByName("MozSearch");
   originalEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
 });
 
-add_task(async function() { await dropText("mochi.test/first", 1); });
-add_task(async function() { await dropText("javascript:'bad'"); });
-add_task(async function() { await dropText("jAvascript:'bad'"); });
-add_task(async function() { await dropText("search this", 1); });
-add_task(async function() { await dropText("mochi.test/second", 1); });
-add_task(async function() { await dropText("data:text/html,bad"); });
-add_task(async function() { await dropText("mochi.test/third", 1); });
+add_task(async function single_url() {
+  await dropText("mochi.test/first",
+                 ["http://www.mochi.test/first"]);
+});
+add_task(async function single_javascript() {
+  await dropText("javascript:'bad'", []);
+});
+add_task(async function single_javascript_capital() {
+  await dropText("jAvascript:'bad'", []);
+});
+add_task(async function single_search() {
+  await dropText("search this",
+                 [ANY_URL]);
+});
+add_task(async function single_url2() {
+  await dropText("mochi.test/second",
+                 ["http://www.mochi.test/second"]);
+});
+add_task(async function single_data_url() {
+  await dropText("data:text/html,bad", []);
+});
+add_task(async function single_url3() {
+  await dropText("mochi.test/third",
+                 ["http://www.mochi.test/third"]);
+});
 
 // Single text/plain item, with multiple links.
-add_task(async function() { await dropText("mochi.test/1\nmochi.test/2", 2); });
-add_task(async function() { await dropText("javascript:'bad1'\nmochi.test/3", 0); });
-add_task(async function() { await dropText("mochi.test/4\ndata:text/html,bad1", 0); });
+add_task(async function multiple_urls() {
+  await dropText("mochi.test/1\nmochi.test/2",
+                 [
+                   "http://www.mochi.test/1",
+                   "http://www.mochi.test/2",
+                 ]);
+});
+add_task(async function multiple_urls_javascript() {
+  await dropText("javascript:'bad1'\nmochi.test/3", []);
+});
+add_task(async function multiple_urls_data() {
+  await dropText("mochi.test/4\ndata:text/html,bad1", []);
+});
 
 // Multiple text/plain items, with single and multiple links.
-add_task(async function() {
+add_task(async function multiple_items_single_and_multiple_links() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/5"}],
               [{type: "text/plain",
-                data: "mochi.test/6\nmochi.test/7"}]], 3);
+                data: "mochi.test/6\nmochi.test/7"}]],
+             [
+               "http://www.mochi.test/5",
+               "http://www.mochi.test/6",
+               "http://www.mochi.test/7",
+             ]);
 });
 
 // Single text/x-moz-url item, with multiple links.
 // "text/x-moz-url" has titles in even-numbered lines.
-add_task(async function() {
+add_task(async function single_moz_url_multiple_links() {
   await drop([[{type: "text/x-moz-url",
-                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]], 2);
+                data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9"}]],
+             [
+               "http://www.mochi.test/8",
+               "http://www.mochi.test/9",
+             ]);
 });
 
 // Single item with multiple types.
-add_task(async function() {
+add_task(async function single_item_multiple_types() {
   await drop([[{type: "text/plain",
                 data: "mochi.test/10"},
                {type: "text/x-moz-url",
-                data: "mochi.test/11\nTITLE11"}]], 1);
+                data: "mochi.test/11\nTITLE11"}]],
+             ["http://www.mochi.test/11"]);
 });
 
 // Warn when too many URLs are dropped.
 add_task(async function multiple_tabs_under_max() {
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/multi" + i);
   }
-  await dropText(urls.join("\n"), 5);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/multi0",
+                   "http://www.mochi.test/multi1",
+                   "http://www.mochi.test/multi2",
+                   "http://www.mochi.test/multi3",
+                   "http://www.mochi.test/multi4",
+                 ]);
 });
 add_task(async function multiple_tabs_over_max_accept() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/accept" + i);
   }
-  await dropText(urls.join("\n"), 5, true);
+  await dropText(urls.join("\n"),
+                 [
+                   "http://www.mochi.test/accept0",
+                   "http://www.mochi.test/accept1",
+                   "http://www.mochi.test/accept2",
+                   "http://www.mochi.test/accept3",
+                   "http://www.mochi.test/accept4",
+                 ]);
 
   await confirmPromise;
 
   await popPrefs();
 });
 add_task(async function multiple_tabs_over_max_cancel() {
   await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]);
 
   let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel");
 
   let urls = [];
   for (let i = 0; i < 5; i++) {
     urls.push("mochi.test/cancel" + i);
   }
-  await dropText(urls.join("\n"), 0, true);
+  await dropText(urls.join("\n"), []);
 
   await confirmPromise;
 
   await popPrefs();
 });
 
-function dropText(text, expectedTabOpenCount = 0) {
-  return drop([[{type: "text/plain", data: text}]], expectedTabOpenCount);
+function dropText(text, expectedURLs) {
+  return drop([[{type: "text/plain", data: text}]], expectedURLs);
 }
 
-async function drop(dragData, expectedTabOpenCount = 0) {
+async function drop(dragData, expectedURLs) {
   let dragDataString = JSON.stringify(dragData);
-  info(`Starting test for datagData:${dragDataString}; expectedTabOpenCount:${expectedTabOpenCount}`);
+  info(`Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}`);
   let EventUtils = {};
   Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
   let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
-  let actualTabOpenCount = 0;
-  let openedTabs = [];
-  let checkCount = function(event) {
-    openedTabs.push(event.target);
-    actualTabOpenCount++;
-    return actualTabOpenCount == expectedTabOpenCount;
-  };
-  let awaitTabOpen = expectedTabOpenCount && BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen", false, checkCount);
+
+  let loadedPromises = expectedURLs.map(
+    url => BrowserTestUtils.waitForNewTab(gBrowser, url, false, true));
+
   // A drop type of "link" onto an existing tab would normally trigger a
   // load in that same tab, but tabbrowser code in _getDragTargetTab treats
   // drops on the outer edges of a tab differently (loading a new tab
   // instead). Make events created by synthesizeDrop have all of their
   // coordinates set to 0 (screenX/screenY), so they're treated as drops
   // on the outer edge of the tab, thus they open new tabs.
   var event = {
     clientX: 0,
     clientY: 0,
     screenX: 0,
     screenY: 0,
   };
   EventUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, dragData, "link", window, undefined, event);
-  let tabsOpened = false;
-  if (awaitTabOpen) {
-    await awaitTabOpen;
-    info("Got TabOpen event");
-    tabsOpened = true;
-    for (let tab of openedTabs) {
-      BrowserTestUtils.removeTab(tab);
-    }
+
+  let tabs = await Promise.all(loadedPromises);
+  for (let tab of tabs) {
+    BrowserTestUtils.removeTab(tab);
   }
-  is(tabsOpened, !!expectedTabOpenCount, `Tabs for ${dragDataString} should only open if any of dropped items are valid`);
 
   await awaitDrop;
   ok(true, "Got drop event");
 }
--- a/browser/base/content/test/popups/browser_popupUI.js
+++ b/browser/base/content/test/popups/browser_popupUI.js
@@ -1,13 +1,13 @@
 function test() {
   waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({ set: [[ "dom.disable_open_during_load", false ]] });
 
-  let popupOpened = BrowserTestUtils.waitForNewWindow("about:blank");
+  let popupOpened = BrowserTestUtils.waitForNewWindow({url: "about:blank"});
   BrowserTestUtils.openNewForegroundTab(gBrowser,
     "data:text/html,<html><script>popup=open('about:blank','','width=300,height=200')</script>"
   );
   popupOpened.then((win) => testPopupUI(win));
 }
 
 function testPopupUI(win) {
   var doc = win.document;
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -26,17 +26,17 @@ let windowTracker;
 const getSender = (extension, target, sender) => {
   let tabId;
   if ("tabId" in sender) {
     // The message came from a privileged extension page running in a tab. In
     // that case, it should include a tabId property (which is filled in by the
     // page-open listener below).
     tabId = sender.tabId;
     delete sender.tabId;
-  } else if (target instanceof Ci.nsIDOMXULElement ||
+  } else if (ExtensionUtils.instanceOf(target, "XULElement") ||
              ExtensionUtils.instanceOf(target, "HTMLIFrameElement")) {
     tabId = tabTracker.getBrowserData(target).tabId;
   }
 
   if (tabId) {
     let tab = extension.tabManager.get(tabId, null);
     if (tab) {
       sender.tab = tab.convert();
--- a/browser/components/extensions/test/browser/browser_ext_windows.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows.js
@@ -89,17 +89,17 @@ add_task(async function testWindowTitle(
       permissions: ["tabs"],
     },
   });
 
   await extension.startup();
   let {Management: {global: {windowTracker}}} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
 
   async function createApiWin(options) {
-    let promiseLoaded = BrowserTestUtils.waitForNewWindow(START_URL);
+    let promiseLoaded = BrowserTestUtils.waitForNewWindow({url: START_URL});
     extension.sendMessage("create", options);
     let apiWin = await extension.awaitMessage("created");
     let realWin = windowTracker.getWindow(apiWin.id);
     await promiseLoaded;
     let expectedPreface = options.titlePreface ? options.titlePreface : "";
     ok(realWin.document.title.startsWith(expectedPreface),
        "Created window has the expected title preface.");
     ok(realWin.document.title.includes(START_TITLE),
--- a/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
+++ b/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
@@ -1,17 +1,17 @@
 "use strict";
 
 const PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 const TEST_PAGE = PATH + "file_triggeringprincipal_oa.html";
 const DUMMY_PAGE = PATH + "empty_file.html";
 
 add_task(async function test_principal_right_click_open_link_in_new_private_win() {
   await BrowserTestUtils.withNewTab(TEST_PAGE, async function(browser) {
-    let promiseNewWindow = BrowserTestUtils.waitForNewWindow(DUMMY_PAGE);
+    let promiseNewWindow = BrowserTestUtils.waitForNewWindow({url: DUMMY_PAGE});
 
     // simulate right-click open link in new private window
     BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
       document.getElementById("context-openlinkprivate").doCommand();
       event.target.hidePopup();
       return true;
     });
     BrowserTestUtils.synthesizeMouseAtCenter("#checkPrincipalOA",
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
@@ -35,17 +35,17 @@ add_task(async function test_private_pop
   // First, open a private browsing window, and load our
   // testing page.
   let privBrowser = privWin.gBrowser.selectedBrowser;
   await BrowserTestUtils.loadURI(privBrowser, WINDOW_BODY);
   await BrowserTestUtils.browserLoaded(privBrowser);
 
   // Next, click on the link in the testing page, and ensure
   // that a private popup window is opened.
-  let openedPromise = BrowserTestUtils.waitForNewWindow(POPUP_LINK);
+  let openedPromise = BrowserTestUtils.waitForNewWindow({url: POPUP_LINK});
 
   await BrowserTestUtils.synthesizeMouseAtCenter("#first", {}, privBrowser);
   let popupWin = await openedPromise;
   ok(PrivateBrowsingUtils.isWindowPrivate(popupWin),
      "Popup window was private.");
 
   // Now click on the link in the popup, and ensure that a new
   // tab is opened in the original private browsing window.
--- a/browser/components/sessionstore/test/browser_394759_behavior.js
+++ b/browser/components/sessionstore/test/browser_394759_behavior.js
@@ -21,17 +21,17 @@
  */
 function testWindows(windowsToOpen, expectedResults) {
   return (async function() {
     for (let winData of windowsToOpen) {
       let features = "chrome,dialog=no," +
                      (winData.isPopup ? "all=no" : "all");
       let url = "http://example.com/?window=" + windowsToOpen.length;
 
-      let openWindowPromise = BrowserTestUtils.waitForNewWindow(url);
+      let openWindowPromise = BrowserTestUtils.waitForNewWindow({url});
       openDialog(getBrowserURL(), "", features, url);
       let win = await openWindowPromise;
       await BrowserTestUtils.closeWindow(win);
     }
 
     let closedWindowData = JSON.parse(ss.getClosedWindowData());
     let numPopups = closedWindowData.filter(function(el, i, arr) {
       return el.isPopup;
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1449601.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <script>
+      var blob = new Blob(["foo"], { type: "text/plain" });
+      var url = URL.createObjectURL(blob);
+      var ifr = document.createElement("iframe");
+      ifr.src = url;
+      document.body.appendChild(ifr);
+
+      onload = function() {
+        try { window.find('foo',false,true,true,true,true) } catch(e) { }
+      }
+    </script>
+  </body>
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -233,9 +233,10 @@ load 1405771.html
 load 1406109-1.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1324463.html
 pref(dom.webcomponents.customelements.enabled,true) load 1413815.html
 load 1411473.html
 pref(dom.webcomponents.shadowdom.enabled,false) load 1422931.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1419799.html
 skip-if(!browserIsRemote) pref(dom.webcomponents.customelements.enabled,true) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902
 pref(dom.webcomponents.shadowdom.enabled,true) load 1428053.html
+load 1449601.html
 load 1445670.html
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -13,17 +13,16 @@ with Files("**"):
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'mozIDOMWindow.idl',
     'nsIContentPolicy.idl',
     'nsIDocumentEncoder.idl',
     'nsIDOMDOMRequest.idl',
     'nsIDOMParser.idl',
-    'nsIDOMSerializer.idl',
     'nsIDroppedLinkHandler.idl',
     'nsIFrameLoaderOwner.idl',
     'nsIImageLoadingContent.idl',
     'nsIMessageManager.idl',
     'nsIObjectLoadingContent.idl',
     'nsIPerformanceMetrics.idl',
     'nsIRemoteWindowContext.idl',
     'nsIScriptChannel.idl',
--- a/dom/base/nsDOMSerializer.cpp
+++ b/dom/base/nsDOMSerializer.cpp
@@ -17,130 +17,106 @@
 #include "nsINode.h"
 
 using namespace mozilla;
 
 nsDOMSerializer::nsDOMSerializer()
 {
 }
 
-nsDOMSerializer::~nsDOMSerializer()
+static already_AddRefed<nsIDocumentEncoder>
+SetUpEncoder(nsINode& aRoot, const nsAString& aCharset, ErrorResult& aRv)
 {
-}
-
-// QueryInterface implementation for nsDOMSerializer
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMSerializer)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSerializer)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMSerializer, mOwner)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMSerializer)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMSerializer)
-
-
-static nsresult
-SetUpEncoder(nsIDOMNode *aRoot, const nsACString& aCharset,
-             nsIDocumentEncoder **aEncoder)
-{
-  *aEncoder = nullptr;
-
   nsresult rv;
   nsCOMPtr<nsIDocumentEncoder> encoder =
     do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xhtml+xml", &rv);
-  if (NS_FAILED(rv))
-    return rv;
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
 
-  nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
-  MOZ_ASSERT(root);
-
-  nsIDocument* doc = root->OwnerDoc();
-  bool entireDocument = (doc == root);
-  nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(doc));
+  nsIDocument* doc = aRoot.OwnerDoc();
+  bool entireDocument = (doc == &aRoot);
 
   // This method will fail if no document
-  rv = encoder->Init(domDoc, NS_LITERAL_STRING("application/xhtml+xml"),
-                     nsIDocumentEncoder::OutputRaw |
-                     nsIDocumentEncoder::OutputDontRewriteEncodingDeclaration);
+  rv = encoder->
+    NativeInit(doc, NS_LITERAL_STRING("application/xhtml+xml"),
+               nsIDocumentEncoder::OutputRaw |
+               nsIDocumentEncoder::OutputDontRewriteEncodingDeclaration);
 
-  if (NS_FAILED(rv))
-    return rv;
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
 
-  nsAutoCString charset(aCharset);
+  NS_ConvertUTF16toUTF8 charset(aCharset);
   if (charset.IsEmpty()) {
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
-    NS_ASSERTION(doc, "Need a document");
     doc->GetDocumentCharacterSet()->Name(charset);
   }
   rv = encoder->SetCharset(charset);
-  if (NS_FAILED(rv))
-    return rv;
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
 
   // If we are working on the entire document we do not need to
   // specify which part to serialize
   if (!entireDocument) {
-    rv = encoder->SetNode(aRoot);
+    rv = encoder->SetNativeNode(&aRoot);
   }
 
-  if (NS_SUCCEEDED(rv)) {
-    encoder.forget(aEncoder);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
   }
 
-  return rv;
+  return encoder.forget();
 }
 
 void
 nsDOMSerializer::SerializeToString(nsINode& aRoot, nsAString& aStr,
-                                   ErrorResult& rv)
+                                   ErrorResult& aRv)
 {
-  rv = nsDOMSerializer::SerializeToString(aRoot.AsDOMNode(), aStr);
-}
+  aStr.Truncate();
 
-NS_IMETHODIMP
-nsDOMSerializer::SerializeToString(nsIDOMNode *aRoot, nsAString& _retval)
-{
-  NS_ENSURE_ARG_POINTER(aRoot);
-
-  _retval.Truncate();
-
-  if (!nsContentUtils::CanCallerAccess(aRoot)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  if (!nsContentUtils::CanCallerAccess(&aRoot)) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
   }
 
-  nsCOMPtr<nsIDocumentEncoder> encoder;
-  nsresult rv = SetUpEncoder(aRoot, EmptyCString(), getter_AddRefs(encoder));
-  if (NS_FAILED(rv))
-    return rv;
+  nsCOMPtr<nsIDocumentEncoder> encoder =
+    SetUpEncoder(aRoot, EmptyString(), aRv);
+  if (aRv.Failed()) {
+    return;
+  }
 
-  return encoder->EncodeToString(_retval);
+  nsresult rv = encoder->EncodeToString(aStr);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
 }
 
 void
 nsDOMSerializer::SerializeToStream(nsINode& aRoot, nsIOutputStream* aStream,
-                                   const nsAString& aCharset, ErrorResult& rv)
+                                   const nsAString& aCharset,
+                                   ErrorResult& aRv)
 {
-  rv = nsDOMSerializer::SerializeToStream(aRoot.AsDOMNode(), aStream,
-                                          NS_ConvertUTF16toUTF8(aCharset));
-}
+  if (NS_WARN_IF(!aStream)) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
 
-NS_IMETHODIMP
-nsDOMSerializer::SerializeToStream(nsIDOMNode *aRoot,
-                                   nsIOutputStream *aStream,
-                                   const nsACString& aCharset)
-{
-  NS_ENSURE_ARG_POINTER(aRoot);
-  NS_ENSURE_ARG_POINTER(aStream);
   // The charset arg can be empty, in which case we get the document's
   // charset and use that when serializing.
 
-  if (!nsContentUtils::CanCallerAccess(aRoot)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  // No point doing a CanCallerAccess check, because we can only be
+  // called by system JS or C++.
+  nsCOMPtr<nsIDocumentEncoder> encoder =
+    SetUpEncoder(aRoot, aCharset, aRv);
+  if (aRv.Failed()) {
+    return;
   }
 
-  nsCOMPtr<nsIDocumentEncoder> encoder;
-  nsresult rv = SetUpEncoder(aRoot, aCharset, getter_AddRefs(encoder));
-  if (NS_FAILED(rv))
-    return rv;
-
-  return encoder->EncodeToStream(aStream);
+  nsresult rv = encoder->EncodeToStream(aStream);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
 }
--- a/dom/base/nsDOMSerializer.h
+++ b/dom/base/nsDOMSerializer.h
@@ -2,68 +2,48 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDOMSerializer_h_
 #define nsDOMSerializer_h_
 
-#include "nsIDOMSerializer.h"
-#include "nsWrapperCache.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/XMLSerializerBinding.h"
 
 class nsINode;
+class nsIOutputStream;
 
-class nsDOMSerializer final : public nsIDOMSerializer,
-                              public nsWrapperCache
+class nsDOMSerializer final : public mozilla::dom::NonRefcountedDOMObject
 {
 public:
   nsDOMSerializer();
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMSerializer)
-
-  // nsIDOMSerializer
-  NS_DECL_NSIDOMSERIALIZER
-
   // WebIDL API
-  static already_AddRefed<nsDOMSerializer>
+  static nsDOMSerializer*
   Constructor(const mozilla::dom::GlobalObject& aOwner,
               mozilla::ErrorResult& rv)
   {
-    RefPtr<nsDOMSerializer> domSerializer = new nsDOMSerializer(aOwner.GetAsSupports());
-    return domSerializer.forget();
+    return new nsDOMSerializer();
   }
 
   void
   SerializeToString(nsINode& aRoot, nsAString& aStr,
                     mozilla::ErrorResult& rv);
 
   void
   SerializeToStream(nsINode& aRoot, nsIOutputStream* aStream,
-                    const nsAString& aCharset, mozilla::ErrorResult& rv);
+                    const nsAString& aCharset,
+                    mozilla::ErrorResult& aRv);
 
-  nsISupports* GetParentObject() const
+  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+                  JS::MutableHandle<JSObject*> aReflector)
   {
-    return mOwner;
-  }
-
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
-  {
-    return mozilla::dom::XMLSerializerBinding::Wrap(aCx, this, aGivenProto);
+    return mozilla::dom::XMLSerializerBinding::Wrap(aCx, this, aGivenProto,
+                                                    aReflector);
   }
-
-private:
-  virtual ~nsDOMSerializer();
-
-  explicit nsDOMSerializer(nsISupports* aOwner) : mOwner(aOwner)
-  {
-    MOZ_ASSERT(aOwner);
-  }
-
-  nsCOMPtr<nsISupports> mOwner;
 };
 
 
 #endif
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -152,18 +152,18 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrame
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   if (aIID.Equals(NS_GET_IID(nsFrameLoader))) {
       // We want to end up with a pointer that can then be reinterpret_cast
       // from nsISupports* to nsFrameLoader* and end up with |this|.
       foundInterface = reinterpret_cast<nsISupports*>(this);
   } else
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserPersistable)
-  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
+  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
                              bool aNetworkCreated, int32_t aJSPluginID)
   : mOwnerContent(aOwner)
   , mDetachedSubdocFrame(nullptr)
   , mOpener(aOpener)
   , mRemoteBrowser(nullptr)
@@ -3225,32 +3225,21 @@ nsFrameLoader::DestroyBrowserFrameScript
   }
 }
 
 void
 nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
                                 nsIWebBrowserPersistDocumentReceiver* aRecv,
                                 ErrorResult& aRv)
 {
-  nsresult rv = StartPersistence(aOuterWindowID, aRecv);
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-  }
-}
-
-NS_IMETHODIMP
-nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
-                                nsIWebBrowserPersistDocumentReceiver* aRecv)
-{
-  if (!aRecv) {
-    return NS_ERROR_INVALID_POINTER;
-  }
+  MOZ_ASSERT(aRecv);
 
   if (mRemoteBrowser) {
-    return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv);
+    mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv, aRv);
+    return;
   }
 
   nsCOMPtr<nsIDocument> rootDoc =
     mDocShell ? mDocShell->GetDocument() : nullptr;
   nsCOMPtr<nsIDocument> foundDoc;
   if (aOuterWindowID) {
     foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
   } else {
@@ -3259,17 +3248,16 @@ nsFrameLoader::StartPersistence(uint64_t
 
   if (!foundDoc) {
     aRecv->OnError(NS_ERROR_NO_CONTENT);
   } else {
     nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
       new mozilla::WebBrowserPersistLocalDocument(foundDoc);
     aRecv->OnDocumentReady(pdoc);
   }
-  return NS_OK;
 }
 
 void
 nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange)
 {
   if (mRemoteBrowser && mOwnerContent) {
     nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
     if (!docShell) {
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -20,32 +20,32 @@
 #include "nsWrapperCache.h"
 #include "nsIURI.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "nsStubMutationObserver.h"
 #include "Units.h"
-#include "nsIWebBrowserPersistable.h"
 #include "nsIFrame.h"
 #include "nsPluginTags.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
 class nsInProcessTabChildGlobal;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
 class nsIDocShellTreeItem;
 class nsIDocShellTreeOwner;
 class nsILoadContext;
 class nsIMessageSender;
 class nsIPrintSettings;
+class nsIWebBrowserPersistDocumentReceiver;
 class nsIWebProgressListener;
 
 namespace mozilla {
 
 class OriginAttributes;
 
 namespace dom {
 class ChromeMessageSender;
@@ -71,18 +71,17 @@ class RenderFrameParent;
 typedef struct _GtkWidget GtkWidget;
 #endif
 
 // IID for nsFrameLoader, because some places want to QI to it.
 #define NS_FRAMELOADER_IID                                      \
   { 0x297fd0ea, 0x1b4a, 0x4c9a,                                 \
       { 0xa4, 0x04, 0xe5, 0x8b, 0xe8, 0x95, 0x10, 0x50 } }
 
-class nsFrameLoader final : public nsIWebBrowserPersistable,
-                            public nsStubMutationObserver,
+class nsFrameLoader final : public nsStubMutationObserver,
                             public mozilla::dom::ipc::MessageManagerCallback,
                             public nsWrapperCache
 {
   friend class AutoResetInShow;
   friend class AutoResetInFrameSwap;
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
@@ -91,20 +90,19 @@ public:
   static nsFrameLoader* Create(mozilla::dom::Element* aOwner,
                                nsPIDOMWindowOuter* aOpener,
                                bool aNetworkCreated,
                                int32_t aJSPluginID = nsFakePluginTag::NOT_JSPLUGIN);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAMELOADER_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameLoader,
-                                                         nsIWebBrowserPersistable)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameLoader)
+
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
-  NS_DECL_NSIWEBBROWSERPERSISTABLE
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void StartDestroy();
   void DestroyDocShell();
   void DestroyComplete();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
   mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
   nsresult CreateStaticClone(nsFrameLoader* aDest);
@@ -506,12 +504,12 @@ private:
   bool mFreshProcess : 1;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsFrameLoader, NS_FRAMELOADER_IID)
 
 inline nsISupports*
 ToSupports(nsFrameLoader* aFrameLoader)
 {
-  return static_cast<nsIWebBrowserPersistable*>(aFrameLoader);
+  return aFrameLoader;
 }
 
 #endif
deleted file mode 100644
--- a/dom/base/nsIDOMSerializer.idl
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIOutputStream;
-interface nsIDOMNode;
-
-/**
- * The nsIDOMSerializer interface is really a placeholder till the W3C
- * DOM Working Group defines a mechanism for serializing DOM nodes.
- * An instance of this interface can be used to serialize a DOM document
- * or any DOM subtree.
- */
-
-[uuid(9fd4ba15-e67c-4c98-b52c-7715f62c9196)]
-interface nsIDOMSerializer : nsISupports
-{
-  /**
-   * The subtree rooted by the specified element is serialized to
-   * a string.
-   * 
-   * @param root The root of the subtree to be serialized. This could
-   *             be any node, including a Document.
-   * @returns The serialized subtree in the form of a Unicode string
-   */
-  AString serializeToString(in nsIDOMNode root);
-
-  /**
-   * The subtree rooted by the specified element is serialized to
-   * a byte stream using the character set specified.
-   * @param root The root of the subtree to be serialized. This could
-   *             be any node, including a Document.
-   * @param stream The byte stream to which the subtree is serialized.
-   * @param charset The name of the character set to use for the encoding
-   *                to a byte stream.  If this string is empty and root is
-   *                a document, the document's character set will be used.
-   */
-  void serializeToStream(in nsIDOMNode root, in nsIOutputStream stream,
-                         in AUTF8String charset);
-};
-
-%{ C++
-#define NS_XMLSERIALIZER_CID                        \
- { /* a6cf9124-15b3-11d2-932e-00805f8add32 */       \
-   0xa6cf9124, 0x15b3, 0x11d2,                      \
-  {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
-#define NS_XMLSERIALIZER_CONTRACTID \
-"@mozilla.org/xmlextras/xmlserializer;1"
-%}
-
--- a/dom/base/test/chrome/test_domparsing.xul
+++ b/dom/base/test/chrome/test_domparsing.xul
@@ -65,18 +65,17 @@ runTest(new DOMParser(), new XMLSerializ
   parser.init();
   throws(function() {
     parser.init();
   }, "NS_ERROR_UNEXPECTED", "init method should throw when called twice");
 }
 
 runTest(Cc["@mozilla.org/xmlextras/domparser;1"]
         .createInstance(Ci.nsIDOMParser),
-        Cc["@mozilla.org/xmlextras/xmlserializer;1"]
-        .createInstance(Ci.nsIDOMSerializer));
+        new XMLSerializer());
 
 function runTest(parser, serializer) {
   is(typeof parser.parseFromString, "function", "parseFromString should exist");
   is(typeof parser.parseFromBuffer, "function", "parseFromBuffer should exist");
   is(typeof parser.parseFromStream, "function", "parseFromStream should exist");
   is(typeof parser.init, "function", "init should exist");
 
   is(typeof serializer.serializeToString, "function", "serializeToString should exist");
--- a/dom/base/test/unit/head_xml.js
+++ b/dom/base/test/unit/head_xml.js
@@ -8,22 +8,22 @@ const I                    = Ci;
 const C                    = Cc;
 
 const nsIFile         = I.nsIFile;
 const nsIProperties        = I.nsIProperties;
 const nsIFileInputStream   = I.nsIFileInputStream;
 const nsIInputStream       = I.nsIInputStream;
 
 const nsIDOMParser         = I.nsIDOMParser;
-const nsIDOMSerializer     = I.nsIDOMSerializer;
 const nsIDOMDocument       = I.nsIDOMDocument;
 const nsIDOMElement        = I.nsIDOMElement;
 const nsIDOMNode           = I.nsIDOMNode;
 const nsIDOMNodeList       = I.nsIDOMNodeList;
-const nsIDOMXULElement     = I.nsIDOMXULElement;
+
+Cu.importGlobalProperties(["XMLSerializer"]);
 
 function DOMParser() {
   var parser = C["@mozilla.org/xmlextras/domparser;1"].createInstance(nsIDOMParser);
   parser.init();
   return parser;
 }
 
 var __testsDirectory = null;
@@ -54,18 +54,17 @@ function ParseXML(data) {
 
   Assert.equal(data instanceof nsIInputStream, true);
   
   return DOMParser().parseFromStream(data, "UTF-8", data.available(),
                                      "application/xml");
 }
 
 function DOMSerializer() {
-  return C["@mozilla.org/xmlextras/xmlserializer;1"]
-          .createInstance(nsIDOMSerializer);
+  return new XMLSerializer();
 }
 
 function SerializeXML(node) {
   return DOMSerializer().serializeToString(node);
 }
 
 function roundtrip(obj) {
   if (typeof(obj) == "string") {
--- a/dom/base/test/unit/test_nodelist.js
+++ b/dom/base/test/unit/test_nodelist.js
@@ -170,30 +170,30 @@ function test_getElementsByTagNameNS()
 function test_getElementsByAttribute()
 {
   var doc = ParseFile("nodelist_data_2.xul");
   var root = doc.documentElement;
 
   // Sadly, DOMParser can't create XULDocument objects.  But at least we have a
   // XULElement!
 
-  Assert.ok(root instanceof nsIDOMXULElement);
+  Assert.equal(ChromeUtils.getClassName(root), "XULElement");
 
   Assert.ok(root.getElementsByAttribute("foo", "foo") instanceof
             nsIDOMNodeList);
 
   var master1 = doc.getElementById("master1");
   var master2 = doc.getElementById("master2");
   var master3 = doc.getElementById("master3");
   var external = doc.getElementById("external");
 
-  Assert.ok(master1 instanceof nsIDOMXULElement);
-  Assert.ok(master2 instanceof nsIDOMXULElement);
-  Assert.ok(master3 instanceof nsIDOMXULElement);
-  Assert.ok(external instanceof nsIDOMXULElement);
+  Assert.equal(ChromeUtils.getClassName(master1), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(master2), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(master3), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(external), "XULElement");
 
   // Basic tests
   Assert.equal(root.getElementsByAttribute("foo", "foo").length,
                14);
   Assert.equal(master1.getElementsByAttribute("foo", "foo").length,
                4);
 
   Assert.equal(root.getElementsByAttribute("foo", "bar").length,
@@ -287,31 +287,31 @@ function test_getElementsByAttribute()
 function test_getElementsByAttributeNS()
 {
   var doc = ParseFile("nodelist_data_2.xul");
   var root = doc.documentElement;
 
   // Sadly, DOMParser can't create XULDocument objects.  But at least we have a
   // XULElement!
 
-  Assert.ok(root instanceof nsIDOMXULElement);
+  Assert.equal(ChromeUtils.getClassName(root), "XULElement");
 
   // Check that getElementsByAttributeNS returns a nodelist.
   Assert.ok(root.getElementsByAttributeNS("*", "*", "*") instanceof
             nsIDOMNodeList);
 
   var master1 = doc.getElementById("master1");
   var master2 = doc.getElementById("master2");
   var master3 = doc.getElementById("master3");
   var external = doc.getElementById("external");
 
-  Assert.ok(master1 instanceof nsIDOMXULElement);
-  Assert.ok(master2 instanceof nsIDOMXULElement);
-  Assert.ok(master3 instanceof nsIDOMXULElement);
-  Assert.ok(external instanceof nsIDOMXULElement);
+  Assert.equal(ChromeUtils.getClassName(master1), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(master2), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(master3), "XULElement");
+  Assert.equal(ChromeUtils.getClassName(external), "XULElement");
   
   // Test wildcard namespace
   Assert.equal(root.getElementsByAttributeNS("*", "foo", "foo").length,
                38);
   Assert.equal(master1.getElementsByAttributeNS("*", "foo", "foo").length,
                11);
   Assert.equal(master2.getElementsByAttributeNS("*", "foo", "foo").length,
                10);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1381,16 +1381,17 @@ DOMInterfaces = {
 },
 
 'XMLHttpRequestEventTarget': {
     'concrete': False
 },
 
 'XMLSerializer': {
     'nativeType': 'nsDOMSerializer',
+    'wrapperCache': False
 },
 
 'XPathEvaluator': {
     'wrapperCache': False
 },
 
 'XPathExpression': {
     'wrapperCache': False,
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -744,20 +744,39 @@ EventDispatcher::Dispatch(nsISupports* a
     }
   }
 
 #ifdef DEBUG
   if (NS_IsMainThread() &&
       aEvent->mMessage != eVoidEvent &&
       !nsContentUtils::IsSafeToRunScript()) {
     nsCOMPtr<nsINode> node = do_QueryInterface(target);
-    if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
-      NS_WARNING("Fix the caller!");
+    if (!node) {
+      // If the target is not a node, just go ahead and assert that this is
+      // unsafe.  There really shouldn't be any other event targets in documents
+      // that are not being rendered or scripted.
+      NS_ERROR("This is unsafe! Fix the caller!");
     } else {
-      NS_ERROR("This is unsafe! Fix the caller!");
+      // If this is a node, it's possible that this is some sort of DOM tree
+      // that is never accessed by script (for example an SVG image or XBL
+      // binding document or whatnot).  We really only want to warn/assert here
+      // if there might be actual scripted listeners for this event, so restrict
+      // the warnings/asserts to the case when script can or once could touch
+      // this node's document.
+      nsIDocument* doc = node->OwnerDoc();
+      bool hasHadScriptHandlingObject;
+      nsIGlobalObject* global =
+        doc->GetScriptHandlingObject(hasHadScriptHandlingObject);
+      if (global || hasHadScriptHandlingObject) {
+        if (nsContentUtils::IsChromeDoc(doc)) {
+          NS_WARNING("Fix the caller!");
+        } else {
+          NS_ERROR("This is unsafe! Fix the caller!");
+        }
+      }
     }
   }
 
   if (aDOMEvent) {
     WidgetEvent* innerEvent = aDOMEvent->WidgetEventPtr();
     NS_ASSERTION(innerEvent == aEvent,
                   "The inner event of aDOMEvent is not the same as aEvent!");
   }
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -681,33 +681,49 @@ EventListenerManager::ListenerCanHandle(
     if (!aEvent->mFlags.mInSystemGroup && !aListener->mIsChrome) {
       return false;
     }
   }
   MOZ_ASSERT(mIsMainThreadELM);
   return aListener->mEventMessage == aEventMessage;
 }
 
+static bool
+DefaultToPassiveTouchListeners()
+{
+  static bool sDefaultToPassiveTouchListeners = false;
+  static bool sIsPrefCached = false;
+
+  if (!sIsPrefCached) {
+    sIsPrefCached = true;
+    Preferences::AddBoolVarCache(&sDefaultToPassiveTouchListeners,
+                                 "dom.event.default_to_passive_touch_listeners");
+  }
+
+  return sDefaultToPassiveTouchListeners;
+}
+
 void
 EventListenerManager::AddEventListenerByType(
                         EventListenerHolder aListenerHolder,
                         const nsAString& aType,
                         const EventListenerFlags& aFlags,
                         const Optional<bool>& aPassive)
 {
   RefPtr<nsAtom> atom;
   EventMessage message = mIsMainThreadELM ?
     nsContentUtils::GetEventMessageAndAtomForListener(aType,
                                                       getter_AddRefs(atom)) :
     eUnidentifiedEvent;
 
   EventListenerFlags flags = aFlags;
   if (aPassive.WasPassed()) {
     flags.mPassive = aPassive.Value();
-  } else if (message == eTouchStart || message == eTouchMove) {
+  } else if ((message == eTouchStart || message == eTouchMove) &&
+             mIsMainThreadELM && DefaultToPassiveTouchListeners()) {
     nsCOMPtr<nsINode> node;
     nsCOMPtr<nsPIDOMWindowInner> win;
     if ((win = GetTargetAsInnerWindow()) ||
         ((node = do_QueryInterface(mTarget)) &&
          (node == node->OwnerDoc() ||
           node == node->OwnerDoc()->GetRootElement() ||
           node == node->OwnerDoc()->GetBody()))) {
       flags.mPassive = true;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -53,17 +53,16 @@
 #include "nsPIWindowRoot.h"
 #include "nsIWebNavigation.h"
 #include "nsIContentViewer.h"
 #include "nsFrameManager.h"
 #include "nsITabChild.h"
 #include "nsPluginFrame.h"
 #include "nsMenuPopupFrame.h"
 
-#include "nsIDOMXULElement.h"
 #include "nsIObserverService.h"
 #include "nsIDocShell.h"
 #include "nsIMozBrowserFrame.h"
 
 #include "nsSubDocumentFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsUnicharUtils.h"
--- a/dom/fetch/BodyExtractor.cpp
+++ b/dom/fetch/BodyExtractor.cpp
@@ -5,19 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BodyExtractor.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/XMLHttpRequest.h"
+#include "mozilla/UniquePtr.h"
 #include "nsContentUtils.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMSerializer.h"
+#include "nsDOMSerializer.h"
 #include "nsIGlobalObject.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIStorageStream.h"
 #include "nsStringStream.h"
 
 namespace mozilla {
 namespace dom {
@@ -68,18 +68,17 @@ BodyExtractor<const ArrayBufferView>::Ge
 }
 
 template<> nsresult
 BodyExtractor<nsIDocument>::GetAsStream(nsIInputStream** aResult,
                                         uint64_t* aContentLength,
                                         nsACString& aContentTypeWithCharset,
                                         nsACString& aCharset) const
 {
-  nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(mBody));
-  NS_ENSURE_STATE(domdoc);
+  NS_ENSURE_STATE(mBody);
   aCharset.AssignLiteral("UTF-8");
 
   nsresult rv;
   nsCOMPtr<nsIStorageStream> storStream;
   rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIOutputStream> output;
@@ -102,23 +101,25 @@ BodyExtractor<nsIDocument>::GetAsStream(
     uint32_t written;
     rv = output->Write(utf8Serialized.get(), utf8Serialized.Length(), &written);
     NS_ENSURE_SUCCESS(rv, rv);
 
     MOZ_ASSERT(written == utf8Serialized.Length());
   } else {
     aContentTypeWithCharset.AssignLiteral("application/xml;charset=UTF-8");
 
-    nsCOMPtr<nsIDOMSerializer> serializer =
-      do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
+    auto serializer = MakeUnique<nsDOMSerializer>();
 
     // Make sure to use the encoding we'll send
-    rv = serializer->SerializeToStream(domdoc, output, aCharset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ErrorResult res;
+    serializer->SerializeToStream(*mBody, output, NS_LITERAL_STRING("UTF-8"),
+                                  res);
+    if (NS_WARN_IF(res.Failed())) {
+      return res.StealNSResult();
+    }
   }
 
   output->Close();
 
   uint32_t length;
   rv = storStream->GetLength(&length);
   NS_ENSURE_SUCCESS(rv, rv);
   *aContentLength = length;
--- a/dom/fetch/moz.build
+++ b/dom/fetch/moz.build
@@ -43,16 +43,18 @@ UNIFIED_SOURCES += [
 ]
 
 IPDL_SOURCES += [
     'ChannelInfo.ipdlh',
     'FetchTypes.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
+    # For nsDOMSerializer
+    '/dom/base',
     # For HttpBaseChannel.h dependencies
     '/netwerk/base',
     # For nsDataHandler.h
     '/netwerk/protocol/data',
     # For HttpBaseChannel.h
     '/netwerk/protocol/http',
 ]
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1803,16 +1803,17 @@ void HTMLMediaElement::AbortExistingLoad
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
   mMediaInfo = MediaInfo();
   mIsEncrypted = false;
   mPendingEncryptedInitData.Reset();
   mWaitingForKey = NOT_WAITING_FOR_KEY;
   mSourcePointer = nullptr;
+  mAttemptPlayUponLoadedMetadata = false;
 
   mTags = nullptr;
 
   if (mNetworkState != NETWORK_EMPTY) {
     NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
     // ChangeNetworkState() will call UpdateAudioChannelPlayingState()
     // indirectly which depends on mPaused. So we need to update mPaused first.
     if (!mPaused) {
@@ -3951,17 +3952,17 @@ void
 HTMLMediaElement::NotifyXPCOMShutdown()
 {
   ShutdownDecoder();
 }
 
 already_AddRefed<Promise>
 HTMLMediaElement::Play(ErrorResult& aRv)
 {
-  LOG(LogLevel::Debug, ("%p Play() called by JS", this));
+  LOG(LogLevel::Debug, ("%p Play() called by JS readyState=%d", this, mReadyState));
 
   if (mAudioChannelWrapper && mAudioChannelWrapper->IsPlaybackBlocked()) {
     MaybeDoLoad();
 
     // A blocked media element will be resumed later, so we return a pending
     // promise which might be resolved/rejected depends on the result of
     // resuming the blocked media element.
     RefPtr<Promise> promise = CreateDOMPromise(aRv);
@@ -3990,17 +3991,22 @@ HTMLMediaElement::PlayInternal(ErrorResu
 
   // 4.8.12.8
   // When the play() method on a media element is invoked, the user agent must
   // run the following steps.
 
   // 4.8.12.8 - Step 1:
   // If the media element is not allowed to play, return a promise rejected
   // with a "NotAllowedError" DOMException and abort these steps.
-  if (!IsAllowedToPlay()) {
+  // Note: IsAllowedToPlay() needs to know whether there is an audio track
+  // in the resource, and for that we need to be at readyState HAVE_METADATA
+  // or above. So only reject here if we're at readyState HAVE_METADATA. If
+  // we're below that, we'll we delay fulfilling the play promise until we've
+  // reached readyState >= HAVE_METADATA below.
+  if (mReadyState >= HAVE_METADATA && !IsAllowedToPlay()) {
     // NOTE: for promise-based-play, will return a rejected promise here.
     LOG(LogLevel::Debug,
         ("%p Play() promise rejected because not allowed to play.", this));
     aRv.Throw(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   // 4.8.12.8 - Step 2:
@@ -4048,31 +4054,40 @@ HTMLMediaElement::PlayInternal(ErrorResu
 
   // Even if we just did Load() or ResumeLoad(), we could already have a decoder
   // here if we managed to clone an existing decoder.
   if (mDecoder) {
     if (mDecoder->IsEnded()) {
       SetCurrentTime(0);
     }
     if (!mPausedForInactiveDocumentOrChannel) {
-      nsresult rv = mDecoder->Play();
-      if (NS_FAILED(rv)) {
-        // We don't need to remove the _promise_ from _mPendingPlayPromises_ here.
-        // If something wrong between |mPendingPlayPromises.AppendElement(promise);|
-        // and here, the _promise_ should already have been rejected. Otherwise,
-        // the _promise_ won't be returned to JS at all, so just leave it in the
-        // _mPendingPlayPromises_ and let it be resolved/rejected with the
-        // following actions and the promise-resolution won't be observed at all.
-        LOG(LogLevel::Debug,
-            ("%p Play() promise rejected because failed to play MediaDecoder.",
-             this));
-        aRv.Throw(rv);
-        return nullptr;
+      if (mReadyState < HAVE_METADATA) {
+        // We don't know whether the autoplay policy will allow us to play yet,
+        // as we don't yet know whether the media has audio tracks. So delay
+        // starting playback until we've loaded metadata.
+        mAttemptPlayUponLoadedMetadata = true;
+      } else {
+        nsresult rv = mDecoder->Play();
+        if (NS_FAILED(rv)) {
+          // We don't need to remove the _promise_ from _mPendingPlayPromises_ here.
+          // If something wrong between |mPendingPlayPromises.AppendElement(promise);|
+          // and here, the _promise_ should already have been rejected. Otherwise,
+          // the _promise_ won't be returned to JS at all, so just leave it in the
+          // _mPendingPlayPromises_ and let it be resolved/rejected with the
+          // following actions and the promise-resolution won't be observed at all.
+          LOG(LogLevel::Debug,
+              ("%p Play() promise rejected because failed to play MediaDecoder.",
+              this));
+          aRv.Throw(rv);
+          return nullptr;
+        }
       }
     }
+  } else if (mReadyState < HAVE_METADATA) {
+    mAttemptPlayUponLoadedMetadata = true;
   }
 
   if (mCurrentPlayRangeStart == -1.0) {
     mCurrentPlayRangeStart = CurrentTime();
   }
 
   const bool oldPaused = mPaused;
   mPaused = false;
@@ -4921,17 +4936,17 @@ HTMLMediaElement::FinishDecoderSetup(Med
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   if (mPausedForInactiveDocumentOrChannel) {
     mDecoder->Suspend();
   }
 
   nsresult rv = NS_OK;
-  if (!mPaused) {
+  if (!mPaused && !mAttemptPlayUponLoadedMetadata) {
     SetPlayedOrSeeked(true);
     if (!mPausedForInactiveDocumentOrChannel) {
       rv = mDecoder->Play();
     }
   }
 
   if (NS_FAILED(rv)) {
     ShutdownDecoder();
@@ -5925,16 +5940,25 @@ HTMLMediaElement::ChangeReadyState(nsMed
     DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
     mLoadedDataFired = true;
   }
 
   if (oldState < HAVE_FUTURE_DATA &&
       mReadyState >= HAVE_FUTURE_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
     if (!mPaused) {
+      if (mAttemptPlayUponLoadedMetadata && mDecoder) {
+        mAttemptPlayUponLoadedMetadata = false;
+        if (IsAllowedToPlay()) {
+          mDecoder->Play();
+        } else {
+          AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
+          mPaused = true;
+        }
+      }
       NotifyAboutPlaying();
     }
   }
 
   CheckAutoplayDataReady();
 
   if (oldState < HAVE_ENOUGH_DATA &&
       mReadyState >= HAVE_ENOUGH_DATA) {
@@ -6045,16 +6069,17 @@ void HTMLMediaElement::CheckAutoplayData
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
   if (mDecoder) {
     SetPlayedOrSeeked(true);
     if (mCurrentPlayRangeStart == -1.0) {
       mCurrentPlayRangeStart = CurrentTime();
     }
+    MOZ_ASSERT(!mAttemptPlayUponLoadedMetadata);
     mDecoder->Play();
   } else if (mSrcStream) {
     SetPlayedOrSeeked(true);
   }
 
   // For blocked media, the event would be pending until it is resumed.
   DispatchAsyncEvent(NS_LITERAL_STRING("play"));
 
@@ -6382,16 +6407,17 @@ void HTMLMediaElement::SuspendOrResumeEl
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
+          MOZ_ASSERT(!mAttemptPlayUponLoadedMetadata);
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
         DispatchPendingMediaEvents();
       }
     }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1814,16 +1814,23 @@ private:
   // True if media element has been marked as 'tainted' and can't
   // participate in video decoder suspending.
   bool mHasSuspendTaint = false;
 
   // True if media element has been forced into being considered 'hidden'.
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   bool mForcedHidden = false;
 
+  // True if we attempted to play before the media element had loaded
+  // metadata, and we need to attempt the play once we reach loaded metadata.
+  // If autoplay is disabled, we can't decide whether to allow a play()
+  // until we've loaded metadata, as we need to know whether the resource
+  // has an audio track.
+  bool mAttemptPlayUponLoadedMetadata = false;
+
   // True if audio tracks and video tracks are constructed and added into the
   // track list, false if all tracks are removed from the track list.
   bool mMediaTracksConstructed = false;
 
   Visibility mVisibilityState = Visibility::UNTRACKED;
 
   UniquePtr<ErrorSink> mErrorSink;
 
--- a/dom/interfaces/core/moz.build
+++ b/dom/interfaces/core/moz.build
@@ -9,13 +9,12 @@ with Files("**"):
 
 XPIDL_SOURCES += [
     'nsIDOMDocument.idl',
     'nsIDOMDocumentFragment.idl',
     'nsIDOMElement.idl',
     'nsIDOMNode.idl',
     'nsIDOMNodeList.idl',
     'nsIDOMNSEditableElement.idl',
-    'nsIDOMXMLDocument.idl',
 ]
 
 XPIDL_MODULE = 'dom_core'
 
deleted file mode 100644
--- a/dom/interfaces/core/nsIDOMXMLDocument.idl
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-// The only reason this interface still exists is that we have addon and Firefox
-// code QIing to it and using it for instanceof via Components.interfaces, and
-// the interface needs to exist in order for the shim stuff in
-// xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp to work.
-[uuid(89ab39cb-c568-4d85-bd34-306d5cd5164d)]
-interface nsIDOMXMLDocument : nsISupports
-{
-};
--- a/dom/interfaces/xul/moz.build
+++ b/dom/interfaces/xul/moz.build
@@ -9,17 +9,16 @@ with Files("**"):
 
 XPIDL_SOURCES += [
     'nsIDOMXULButtonElement.idl',
     'nsIDOMXULCheckboxElement.idl',
     'nsIDOMXULCommandDispatcher.idl',
     'nsIDOMXULContainerElement.idl',
     'nsIDOMXULControlElement.idl',
     'nsIDOMXULDescriptionElement.idl',
-    'nsIDOMXULElement.idl',
     'nsIDOMXULLabeledControlEl.idl',
     'nsIDOMXULLabelElement.idl',
     'nsIDOMXULMenuListElement.idl',
     'nsIDOMXULMultSelectCntrlEl.idl',
     'nsIDOMXULRelatedElement.idl',
     'nsIDOMXULSelectCntrlEl.idl',
     'nsIDOMXULSelectCntrlItemEl.idl',
     'nsIDOMXULTextboxElement.idl',
--- a/dom/interfaces/xul/nsIDOMXULContainerElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULContainerElement.idl
@@ -1,68 +1,22 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMXULElement.idl"
+#include "nsISupports.idl"
 interface nsIDOMXULContainerElement;
 
 [scriptable, uuid(800a68c7-b854-4597-a436-3055ce5c5c96)]
 interface nsIDOMXULContainerItemElement : nsISupports
 {
   /**
    * Returns the parent container if any.
    */
   readonly attribute nsIDOMXULContainerElement parentContainer;
 };
 
 [scriptable, uuid(b2bc96b8-31fc-42f4-937a-bd27291af40b)]
 interface nsIDOMXULContainerElement : nsIDOMXULContainerItemElement
 {
-  /**
-   * Creates an item for the given label and value and appends it to the
-   * container.
-   *
-   * @param aLabel - the label for the new item
-   * @param aValue - the value of the new item
-   */
-  nsIDOMXULElement appendItem(in DOMString aLabel, in DOMString aValue);
-
-  /**
-   * Creates an item for the given label and value and inserts it into the
-   * container at the specified position.
-   *
-   * @param aIndex - the index where the new item will be inserted
-   * @param aLabel - the label for the new item
-   * @param aValue - the value of the new item
-   */
-  nsIDOMXULElement insertItemAt(in long aIndex, in DOMString aLabel,
-                                in DOMString aValue);
-
-  /**
-   * Removes an item from the container.
-   *
-   * @param aIndex - index of the item to remove
-   */
-  nsIDOMXULElement removeItemAt(in long aIndex);
-
-  /**
-   * Returns a count of items in the container.
-   */
-  readonly attribute unsigned long itemCount;
-
-  /**
-   * Returns the index of an item or -1 if the item is not in the container.
-   *
-   * @param aItem - the item to determine the index of
-   */
-  long getIndexOfItem(in nsIDOMXULElement aItem);
-
-  /**
-   * Returns the item at a given index or null if the item is not is the
-   * container.
-   *
-   * @param aIndex - the index of the item to return
-   */
-  nsIDOMXULElement getItemAtIndex(in long aIndex);
 };
 
--- a/dom/interfaces/xul/nsIDOMXULControlElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULControlElement.idl
@@ -1,15 +1,14 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMElement.idl"
-#include "nsIDOMXULElement.idl"
+#include "nsISupports.idl"
 
 interface nsIControllers;
 
 [scriptable, uuid(ea7f92d0-b379-4107-91b4-1e69bdd771e3)]
 interface nsIDOMXULControlElement : nsISupports {
   attribute boolean disabled;
   attribute long tabIndex;
   
--- a/dom/interfaces/xul/nsIDOMXULDescriptionElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULDescriptionElement.idl
@@ -1,15 +1,15 @@
 
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMXULElement.idl"
+#include "nsISupports.idl"
 
 [scriptable, uuid(64c3500e-e258-4d49-b7ca-c93ab0931ce4)]
 interface nsIDOMXULDescriptionElement : nsISupports {
   attribute boolean disabled;
   attribute boolean crop;
   attribute DOMString value;
 };
 
deleted file mode 100644
--- a/dom/interfaces/xul/nsIDOMXULElement.idl
+++ /dev/null
@@ -1,17 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMElement.idl"
-
-interface nsIRDFCompositeDataSource;
-interface nsIRDFResource;
-interface nsIControllers;
-interface nsIBoxObject;
-
-
-[uuid(75435ab3-6863-42a1-ade3-025393d9e80e)]
-interface nsIDOMXULElement : nsIDOMElement
-{
-};
--- a/dom/interfaces/xul/nsIDOMXULMenuListElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULMenuListElement.idl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMXULSelectCntrlEl.idl"
 interface nsIDOMXULTextBoxElement;
+interface nsIDOMNode;
 
 [scriptable, uuid(36c16a17-c0e9-4b35-951b-81a147314ef1)]
 interface nsIDOMXULMenuListElement : nsIDOMXULSelectControlElement {
   attribute boolean editable;
   attribute boolean open;
   
   // label of selected option or value of textfield for editable menu lists
   readonly attribute DOMString label;
--- a/dom/interfaces/xul/nsIDOMXULMultSelectCntrlEl.idl
+++ b/dom/interfaces/xul/nsIDOMXULMultSelectCntrlEl.idl
@@ -1,15 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMXULSelectCntrlEl.idl"
 
+interface nsIDOMNodeList;
+
 [scriptable, uuid(40654a10-8204-4f06-9f21-7baa31c7b1dd)]
 interface nsIDOMXULMultiSelectControlElement : nsIDOMXULSelectControlElement
 {
   attribute DOMString selType;
 
   attribute nsIDOMXULSelectControlItemElement currentItem;
   attribute long currentIndex;
 
--- a/dom/interfaces/xul/nsIDOMXULSelectCntrlItemEl.idl
+++ b/dom/interfaces/xul/nsIDOMXULSelectCntrlItemEl.idl
@@ -1,14 +1,14 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMXULElement.idl"
+#include "nsISupports.idl"
 interface nsIDOMXULSelectControlElement;
 
 [scriptable, uuid(5c6be58f-17df-4750-88a5-4a59ac28adc9)]
 interface nsIDOMXULSelectControlItemElement : nsISupports {
   attribute boolean disabled;
   attribute DOMString crop;
   attribute DOMString image;
   attribute DOMString label;
--- a/dom/interfaces/xul/nsIDOMXULTreeElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULTreeElement.idl
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIDOMXULElement.idl"
-#include "nsIDOMElement.idl"
+#include "nsISupports.idl"
 
 interface nsITreeColumns;
 interface nsITreeView;
+interface nsIDOMElement;
 interface nsIDOMXULTextBoxElement;
 
 /**
  * @status UNDER_DEVELOPMENT
  */
 
 [scriptable, uuid(013b62af-1e2f-4b07-9091-d7c0fc4687e2)]
 interface nsIDOMXULTreeElement : nsISupports
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3185,27 +3185,36 @@ ContentParent::KillHard(const char* aRea
       NS_LITERAL_CSTRING("ipc_channel_error"),
       reason);
 
     Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
 
     RefPtr<ContentParent> self = this;
     std::function<void(bool)> callback = [self](bool aResult) {
       self->OnGenerateMinidumpComplete(aResult);
+      MessageChannel* channel = self->GetIPCChannel();
+      if (channel) {
+        channel->Close();
+      }
     };
     // Generate the report and insert into the queue for submittal.
     mCrashReporter->GenerateMinidumpAndPair(Process(),
                                             nullptr,
                                             NS_LITERAL_CSTRING("browser"),
                                             Move(callback),
                                             true);
     return;
   }
 
   OnGenerateMinidumpComplete(false);
+
+  MessageChannel* channel = GetIPCChannel();
+  if (channel) {
+    channel->Close();
+  }
 }
 
 void
 ContentParent::OnGenerateMinidumpComplete(bool aDumpResult)
 {
   if (mCrashReporter && aDumpResult) {
     // CrashReporterHost::GenerateMinidumpAndPair() is successful.
     mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -64,16 +64,17 @@ using class mozilla::WidgetTouchEvent fr
 using class mozilla::WidgetPluginEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using mozilla::CSSToScreenScale from "Units.h";
 using mozilla::CommandInt from "mozilla/EventForwards.h";
 using mozilla::WritingMode from "mozilla/WritingModes.h";
 using nsIWidget::TouchPointerState from "nsIWidget.h";
+using nsCursor from "nsIWidget.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
 using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h";
 using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
 using mozilla::EventMessage from "mozilla/EventForwards.h";
 using nsEventStatus from "mozilla/EventForwards.h";
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using nsSizeMode from "nsIWidgetListener.h";
 using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
@@ -361,17 +362,17 @@ parent:
     /**
      * Set the native cursor.
      * @param value
      *   The widget cursor to set.
      * @param force
      *   Invalidate any locally cached cursor settings and force an
      *   update.
      */
-    async SetCursor(uint32_t value, bool force);
+    async SetCursor(nsCursor value, bool force);
 
     /**
      * Set the native cursor using a custom image.
      * @param cursorData
      *   Serialized image data.
      * @param width
      *   Width of the image.
      * @param height
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -131,18 +131,17 @@ namespace mozilla {
 namespace dom {
 
 TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
 
 NS_IMPL_ISUPPORTS(TabParent,
                   nsITabParent,
                   nsIAuthPromptProvider,
                   nsISecureBrowserUI,
-                  nsISupportsWeakReference,
-                  nsIWebBrowserPersistable)
+                  nsISupportsWeakReference)
 
 TabParent::TabParent(nsIContentParent* aManager,
                      const TabId& aTabId,
                      const TabContext& aContext,
                      uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mContentCache(*this)
@@ -1717,19 +1716,19 @@ TabParent::RecvAsyncMessage(const nsStri
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!ReceiveMessage(aMessage, false, &data, &cpows, aPrincipal, nullptr)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-TabParent::RecvSetCursor(const uint32_t& aCursor, const bool& aForce)
+TabParent::RecvSetCursor(const nsCursor& aCursor, const bool& aForce)
 {
-  mCursor = static_cast<nsCursor>(aCursor);
+  mCursor = aCursor;
   mCustomCursor = nullptr;
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     if (aForce) {
       widget->ClearCachedCursor();
     }
     if (mTabSetsCursor) {
@@ -3432,29 +3431,33 @@ TabParent::TakeDragVisualization(RefPtr<
 
 bool
 TabParent::AsyncPanZoomEnabled() const
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   return widget && widget->AsyncPanZoomEnabled();
 }
 
-NS_IMETHODIMP
+void
 TabParent::StartPersistence(uint64_t aOuterWindowID,
-                            nsIWebBrowserPersistDocumentReceiver* aRecv)
+                            nsIWebBrowserPersistDocumentReceiver* aRecv,
+                            ErrorResult& aRv)
 {
   nsCOMPtr<nsIContentParent> manager = Manager();
   if (!manager->IsContentParent()) {
-    return NS_ERROR_UNEXPECTED;
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
   }
   auto* actor = new WebBrowserPersistDocumentParent();
   actor->SetOnReady(aRecv);
-  return manager->AsContentParent()
-    ->SendPWebBrowserPersistDocumentConstructor(actor, this, aOuterWindowID)
-    ? NS_OK : NS_ERROR_FAILURE;
+  bool ok = manager->AsContentParent()
+    ->SendPWebBrowserPersistDocumentConstructor(actor, this, aOuterWindowID);
+  if (!ok) {
+    aRv.Throw(NS_ERROR_FAILURE);
+  }
   // (The actor will be destroyed on constructor failure.)
 }
 
 NS_IMETHODIMP
 TabParent::StartApzAutoscroll(float aAnchorX, float aAnchorY,
                               nsViewID aScrollId, uint32_t aPresShellId,
                               bool* aOutRetval)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -22,29 +22,29 @@
 #include "mozilla/Move.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
 #include "nsIKeyEventInPluginCallback.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
-#include "nsIWebBrowserPersistable.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsRefreshDriver.h"
 #include "nsWeakReference.h"
 #include "Units.h"
 #include "nsIWidget.h"
 
 class nsFrameLoader;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 class nsILoadContext;
 class nsIDocShell;
+class nsIWebBrowserPersistDocumentReceiver;
 
 namespace mozilla {
 
 namespace a11y {
 class DocAccessibleParent;
 }
 
 namespace jsipc {
@@ -82,17 +82,16 @@ class StructuredCloneData;
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
                       , public nsIKeyEventInPluginCallback
                       , public nsSupportsWeakReference
                       , public TabContext
-                      , public nsIWebBrowserPersistable
                       , public LiveResizeListener
 {
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
   virtual ~TabParent();
 
 public:
   // Helper class for ContentParent::RecvCreateWindow.
@@ -268,18 +267,18 @@ public:
     const bool& aIsVertical,
     const LayoutDeviceIntPoint& aPoint) override;
 
   virtual mozilla::ipc::IPCResult
   RecvEnableDisableCommands(const nsString& aAction,
                             nsTArray<nsCString>&& aEnabledCommands,
                             nsTArray<nsCString>&& aDisabledCommands) override;
 
-  virtual mozilla::ipc::IPCResult
-  RecvSetCursor(const uint32_t& aValue, const bool& aForce) override;
+  virtual mozilla::ipc::IPCResult RecvSetCursor(const nsCursor& aValue,
+                                                const bool& aForce) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCustomCursor(const nsCString& aUri,
                                                       const uint32_t& aWidth,
                                                       const uint32_t& aHeight,
                                                       const uint32_t& aStride,
                                                       const uint8_t& aFormat,
                                                       const uint32_t& aHotspotX,
                                                       const uint32_t& aHotspotY,
@@ -485,17 +484,20 @@ public:
                                     PIndexedDBPermissionRequestParent* aActor)
                                     override;
 
   bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSISECUREBROWSERUI
-  NS_DECL_NSIWEBBROWSERPERSISTABLE
+
+  void StartPersistence(uint64_t aOuterWindowID,
+                        nsIWebBrowserPersistDocumentReceiver* aRecv,
+                        ErrorResult& aRv);
 
   bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
 
   bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer,
                              const bool& aIsPrivateData,
                              const IPC::Principal& aRequestingPrincipal,
                              const uint32_t& aContentPolicyType);
 
--- a/dom/media/ChannelMediaDecoder.cpp
+++ b/dom/media/ChannelMediaDecoder.cpp
@@ -162,19 +162,19 @@ ChannelMediaDecoder::NotifyPrincipalChan
     // We'll receive one notification when the channel's initial principal
     // is known, after all HTTP redirects have resolved. This isn't really a
     // principal change, so return here to avoid the mSameOriginMedia check
     // below.
     mInitialChannelPrincipalKnown = true;
     return;
   }
   if (!mSameOriginMedia &&
-      DecoderTraits::CrossOriginRedirectsProhibited(ContainerType())) {
-    // For some content types we block mid-flight channel redirects to cross
-    // origin destinations due to security constraints. See bug 1441153.
+      Preferences::GetBool("media.block-midflight-redirects", true)) {
+    // Block mid-flight redirects to non CORS same origin destinations.
+    // See bugs 1441153, 1443942.
     LOG("ChannnelMediaDecoder prohibited cross origin redirect blocked.");
     NetworkError(MediaResult(NS_ERROR_DOM_BAD_URI,
                              "Prohibited cross origin redirect blocked"));
   }
 }
 
 void
 ChannelMediaDecoder::ResourceCallback::NotifySuspendedStatusChanged(
--- a/dom/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -280,16 +280,21 @@ ChannelMediaResource::OnStartRequest(nsI
     // Not an HTTP channel. Assume data will be sent from position zero.
     startOffset = 0;
   }
 
   // Update principals before OnDataAvailable() putting the data in the cache.
   // This is important, we want to make sure all principals are updated before
   // any consumer can see the new data.
   UpdatePrincipal();
+  if (owner->HasError()) {
+    // Updating the principal resulted in an error. Abort the load.
+    CloseChannel();
+    return NS_OK;
+  }
 
   mCacheStream.NotifyDataStarted(mLoadID, startOffset, seekable, length);
   mIsTransportSeekable = seekable;
 
   mSuspendAgent.Delegate(mChannel);
 
   // Fires an initial progress event.
   owner->DownloadProgressed();
@@ -312,17 +317,17 @@ ChannelMediaResource::IsTransportSeekabl
   MOZ_ASSERT(NS_IsMainThread());
   return mIsTransportSeekable;
 }
 
 nsresult
 ChannelMediaResource::ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
                                               int64_t& aRangeStart,
                                               int64_t& aRangeEnd,
-                                              int64_t& aRangeTotal)
+                                              int64_t& aRangeTotal) const
 {
   NS_ENSURE_ARG(aHttpChan);
 
   nsAutoCString rangeStr;
   nsresult rv = aHttpChan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Range"),
                                              rangeStr);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_FALSE(rangeStr.IsEmpty(), NS_ERROR_ILLEGAL_VALUE);
@@ -441,40 +446,82 @@ ChannelMediaResource::OnDataAvailable(ui
       return rv;
     NS_ASSERTION(read > 0, "Read 0 bytes while data was available?");
     count -= read;
   }
 
   return NS_OK;
 }
 
+int64_t
+ChannelMediaResource::CalculateStreamLength() const
+{
+  if (!mChannel) {
+    return -1;
+  }
+
+  nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
+  if (!hc) {
+    return -1;
+  }
+
+  bool succeeded = false;
+  Unused << hc->GetRequestSucceeded(&succeeded);
+  if (!succeeded) {
+    return -1;
+  }
+
+  // We can't determine the length of uncompressed payload.
+  const bool isCompressed = IsPayloadCompressed(hc);
+  if (isCompressed) {
+    return -1;
+  }
+
+  int64_t contentLength = -1;
+  if (NS_FAILED(hc->GetContentLength(&contentLength))) {
+    return -1;
+  }
+
+  uint32_t responseStatus = 0;
+  Unused << hc->GetResponseStatus(&responseStatus);
+  if (responseStatus != HTTP_PARTIAL_RESPONSE_CODE) {
+    return contentLength;
+  }
+
+  // We have an HTTP Byte Range response. The Content-Length is the length
+  // of the response, not the resource. We need to parse the Content-Range
+  // header and extract the range total in order to get the stream length.
+  int64_t rangeStart = 0;
+  int64_t rangeEnd = 0;
+  int64_t rangeTotal = 0;
+  bool gotRangeHeader = NS_SUCCEEDED(
+    ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal));
+  if (gotRangeHeader && rangeTotal != -1) {
+    contentLength = std::max(contentLength, rangeTotal);
+  }
+  return contentLength;
+}
+
 nsresult
 ChannelMediaResource::Open(nsIStreamListener** aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   MOZ_ASSERT(aStreamListener);
   MOZ_ASSERT(mChannel);
 
-  int64_t cl = -1;
-  nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
-  if (hc && !IsPayloadCompressed(hc)) {
-    if (NS_FAILED(hc->GetContentLength(&cl))) {
-      cl = -1;
-    }
-  }
-
-  nsresult rv = mCacheStream.Init(cl);
+  int64_t streamLength = CalculateStreamLength();
+  nsresult rv = mCacheStream.Init(streamLength);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   mSharedInfo = new SharedInfo;
   mSharedInfo->mResources.AppendElement(this);
 
-  mIsLiveStream = cl < 0;
+  mIsLiveStream = streamLength < 0;
   mListener = new Listener(this, 0, ++mLoadID);
   *aStreamListener = mListener;
   NS_ADDREF(*aStreamListener);
   return NS_OK;
 }
 
 nsresult
 ChannelMediaResource::OpenChannel(int64_t aOffset)
--- a/dom/media/ChannelMediaResource.h
+++ b/dom/media/ChannelMediaResource.h
@@ -226,17 +226,21 @@ protected:
   void UpdatePrincipal();
 
   // Parses 'Content-Range' header and returns results via parameters.
   // Returns error if header is not available, values are not parse-able or
   // values are out of range.
   nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
                                    int64_t& aRangeStart,
                                    int64_t& aRangeEnd,
-                                   int64_t& aRangeTotal);
+                                   int64_t& aRangeTotal) const;
+
+  // Calculates the length of the resource using HTTP headers, if this
+  // is an HTTP channel. Returns -1 on failure, or for non HTTP channels.
+  int64_t CalculateStreamLength() const;
 
   struct Closure
   {
     uint32_t mLoadID;
     ChannelMediaResource* mResource;
   };
 
   static nsresult CopySegmentToCache(nsIInputStream* aInStream,
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -320,16 +320,9 @@ bool DecoderTraits::IsSupportedInVideoDo
     ADTSDecoder::IsSupportedType(*type) ||
     FlacDecoder::IsSupportedType(*type) ||
 #ifdef MOZ_ANDROID_HLS_SUPPORT
     HLSDecoder::IsSupportedType(*type) ||
 #endif
     false;
 }
 
-/* static */
-bool
-DecoderTraits::CrossOriginRedirectsProhibited(const MediaContainerType& aType)
-{
-  return WaveDecoder::IsSupportedType(aType);
-}
-
 } // namespace mozilla
--- a/dom/media/DecoderTraits.h
+++ b/dom/media/DecoderTraits.h
@@ -52,18 +52,14 @@ public:
 
   // Returns true if aType is MIME type of hls.
   static bool IsHttpLiveStreamingType(const MediaContainerType& aType);
 
   // Returns true if aType is matroska type.
   static bool IsMatroskaType(const MediaContainerType& aType);
 
   static bool IsSupportedType(const MediaContainerType& aType);
-
-  // For some content types we block channel redirects to cross origin
-  // destinations due to security constraints. See bug 1441153.
-  static bool CrossOriginRedirectsProhibited(const MediaContainerType& aType);
 };
 
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1,33 +1,26 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
-#ifdef XP_WIN
-// Include Windows headers required for enabling high precision timers.
-#include "windows.h"
-#include "mmsystem.h"
-#endif
-
 #include <algorithm>
 #include <stdint.h>
 
 #include "mediasink/AudioSink.h"
 #include "mediasink/AudioSinkWrapper.h"
 #include "mediasink/DecodedStream.h"
 #include "mediasink/OutputStreamManager.h"
 #include "mediasink/VideoSink.h"
 #include "mozilla/IndexSequence.h"
 #include "mozilla/Logging.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/NotNull.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Tuple.h"
 #include "nsIMemoryReporter.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
@@ -2700,19 +2693,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
   INIT_MIRROR(mLooping, false),
   INIT_MIRROR(mSameOriginMedia, false),
   INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
   INIT_CANONICAL(mDuration, NullableTimeUnit()),
   INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()),
   INIT_CANONICAL(mIsAudioDataAudible, false)
-#ifdef XP_WIN
-  , mShouldUseHiResTimers(Preferences::GetBool("media.hi-res-timers.enabled", true))
-#endif
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   InitVideoQueuePrefs();
 
   DDLINKCHILD("reader", aReader);
 }
@@ -2720,20 +2710,16 @@ MediaDecoderStateMachine::MediaDecoderSt
 #undef INIT_WATCHABLE
 #undef INIT_MIRROR
 #undef INIT_CANONICAL
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
-
-#ifdef XP_WIN
-  MOZ_ASSERT(!mHiResTimersRequested);
-#endif
 }
 
 void
 MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
@@ -2924,22 +2910,16 @@ MediaDecoderStateMachine::StopPlayback()
   MOZ_ASSERT(OnTaskQueue());
   LOG("StopPlayback()");
 
   if (IsPlaying()) {
     mOnPlaybackEvent.Notify(MediaPlaybackEvent{
       MediaPlaybackEvent::PlaybackStopped, mPlaybackOffset });
     mMediaSink->SetPlaying(false);
     MOZ_ASSERT(!IsPlaying());
-#ifdef XP_WIN
-    if (mHiResTimersRequested) {
-      mHiResTimersRequested = false;
-      timeEndPeriod(1);
-    }
-#endif
   }
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   // Should try to start playback only after decoding first frames.
   MOZ_ASSERT(mSentFirstFrameLoadedEvent);
@@ -2952,30 +2932,16 @@ void MediaDecoderStateMachine::MaybeStar
   if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
     LOG("Not starting playback [mPlayState=%d]", mPlayState.Ref());
     return;
   }
 
   LOG("MaybeStartPlayback() starting playback");
   StartMediaSink();
 
-#ifdef XP_WIN
-  if (!mHiResTimersRequested && mShouldUseHiResTimers) {
-    mHiResTimersRequested = true;
-    // Ensure high precision timers are enabled on Windows, otherwise the state
-    // machine isn't woken up at reliable intervals to set the next frame, and we
-    // drop frames while painting. Note that each call must be matched by a
-    // corresponding timeEndPeriod() call. Enabling high precision timers causes
-    // the CPU to wake up more frequently on Windows 7 and earlier, which causes
-    // more CPU load and battery use. So we only enable high precision timers
-    // when we're actually playing.
-    timeBeginPeriod(1);
-  }
-#endif
-
   if (!IsPlaying()) {
     mMediaSink->SetPlaying(true);
     MOZ_ASSERT(IsPlaying());
   }
 
   mOnPlaybackEvent.Notify(
     MediaPlaybackEvent{ MediaPlaybackEvent::PlaybackStarted, mPlaybackOffset });
 }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -746,24 +746,13 @@ public:
   AbstractCanonical<media::TimeUnit>* CanonicalCurrentPosition()
   {
     return &mCurrentPosition;
   }
   AbstractCanonical<bool>* CanonicalIsAudioDataAudible()
   {
     return &mIsAudioDataAudible;
   }
-
-#ifdef XP_WIN
-  // Whether we've called timeBeginPeriod(1) to request high resolution
-  // timers. We request high resolution timers when playback starts, and
-  // turn them off when playback is paused. Enabling high resolution
-  // timers can cause higher CPU usage and battery drain on Windows 7.
-  bool mHiResTimersRequested = false;
-  // Whether we should enable high resolution timers. This is initialized at
-  // MDSM construction, and mirrors the value of media.hi-res-timers.enabled.
-  const bool mShouldUseHiResTimers;
-#endif
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -195,16 +195,17 @@ private:
 
   // resume background video decoding when the cursor is hovering over the tab.
   DECL_MEDIA_PREF("media.resume-bkgnd-video-on-tabhover",     ResumeVideoDecodingOnTabHover, bool, false);
 
   DECL_MEDIA_PREF("media.videocontrols.lock-video-orientation",  VideoOrientationLockEnabled, bool, false);
 
   // Media Seamless Looping
   DECL_MEDIA_PREF("media.seamless-looping",                   SeamlessLooping, bool, true);
+
 public:
   // Manage the singleton:
   static MediaPrefs& GetSingleton();
   static bool SingletonExists();
 
 private:
   template<class T> friend class StaticAutoPtr;
   static StaticAutoPtr<MediaPrefs> sInstance;
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -1,14 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifdef XP_WIN
+// Include Windows headers required for enabling high precision timers.
+#include "windows.h"
+#include "mmsystem.h"
+#endif
+
 #include "MediaQueue.h"
 #include "VideoSink.h"
 #include "MediaPrefs.h"
 #include "VideoUtils.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 
 namespace mozilla {
@@ -40,22 +46,29 @@ VideoSink::VideoSink(AbstractThread* aTh
   , mVideoQueue(aVideoQueue)
   , mContainer(aContainer)
   , mProducerID(ImageContainer::AllocateProducerID())
   , mFrameStats(aFrameStats)
   , mHasVideo(false)
   , mUpdateScheduler(aThread)
   , mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
   , mMinVideoQueueSize(MediaPrefs::RuinAvSync() ? 1 : 0)
+#ifdef XP_WIN
+  , mHiResTimersRequested(false)
+#endif
+
 {
   MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
 }
 
 VideoSink::~VideoSink()
 {
+#ifdef XP_WIN
+  MOZ_ASSERT(!mHiResTimersRequested);
+#endif
 }
 
 const MediaSink::PlaybackParams&
 VideoSink::GetPlaybackParams() const
 {
   AssertOwnerThread();
 
   return mAudioSink->GetPlaybackParams();
@@ -133,16 +146,40 @@ void
 VideoSink::SetPreservesPitch(bool aPreservesPitch)
 {
   AssertOwnerThread();
 
   mAudioSink->SetPreservesPitch(aPreservesPitch);
 }
 
 void
+VideoSink::EnsureHighResTimersOnOnlyIfPlaying()
+{
+#ifdef XP_WIN
+  const bool needed = IsPlaying();
+  if (needed == mHiResTimersRequested) {
+    return;
+  }
+  if (needed) {
+    // Ensure high precision timers are enabled on Windows, otherwise the VideoSink
+    // isn't woken up at reliable intervals to set the next frame, and we
+    // drop frames while painting. Note that each call must be matched by a
+    // corresponding timeEndPeriod() call. Enabling high precision timers causes
+    // the CPU to wake up more frequently on Windows 7 and earlier, which causes
+    // more CPU load and battery use. So we only enable high precision timers
+    // when we're actually playing.
+    timeBeginPeriod(1);
+  } else {
+    timeEndPeriod(1);
+  }
+  mHiResTimersRequested = needed;
+#endif
+}
+
+void
 VideoSink::SetPlaying(bool aPlaying)
 {
   AssertOwnerThread();
   VSINK_LOG_V(" playing (%d) -> (%d)", mAudioSink->IsPlaying(), aPlaying);
 
   if (!aPlaying) {
     // Reset any update timer if paused.
     mUpdateScheduler.Reset();
@@ -156,16 +193,18 @@ VideoSink::SetPlaying(bool aPlaying)
   mAudioSink->SetPlaying(aPlaying);
 
   if (mHasVideo && aPlaying) {
     // There's no thread in VideoSink for pulling video frames, need to trigger
     // rendering while becoming playing status. because the VideoQueue may be
     // full already.
     TryUpdateRenderedVideoFrames();
   }
+
+  EnsureHighResTimersOnOnlyIfPlaying();
 }
 
 void
 VideoSink::Start(const TimeUnit& aStartTime, const MediaInfo& aInfo)
 {
   AssertOwnerThread();
   VSINK_LOG("[%s]", __func__);
 
@@ -219,16 +258,18 @@ VideoSink::Stop()
   mUpdateScheduler.Reset();
   if (mHasVideo) {
     DisconnectListener();
     mVideoSinkEndRequest.DisconnectIfExists();
     mEndPromiseHolder.ResolveIfExists(true, __func__);
     mEndPromise = nullptr;
   }
   mVideoFrameEndTime = TimeUnit::Zero();
+
+  EnsureHighResTimersOnOnlyIfPlaying();
 }
 
 bool
 VideoSink::IsStarted() const
 {
   AssertOwnerThread();
 
   return mAudioSink->IsStarted();
--- a/dom/media/mediasink/VideoSink.h
+++ b/dom/media/mediasink/VideoSink.h
@@ -74,16 +74,18 @@ private:
   virtual ~VideoSink();
 
   // VideoQueue listener related.
   void OnVideoQueuePushed(RefPtr<VideoData>&& aSample);
   void OnVideoQueueFinished();
   void ConnectListener();
   void DisconnectListener();
 
+  void EnsureHighResTimersOnOnlyIfPlaying();
+
   // Sets VideoQueue images into the VideoFrameContainer. Called on the shared
   // state machine thread. The first aMaxFrames (at most) are set.
   // aClockTime and aClockTimeStamp are used as the baseline for deriving
   // timestamps for the frames; when omitted, aMaxFrames must be 1 and
   // a null timestamp is passed to the VideoFrameContainer.
   // If the VideoQueue is empty, this does nothing.
   void RenderVideoFrames(int32_t aMaxFrames, int64_t aClockTime = 0,
                          const TimeStamp& aClickTimeStamp = TimeStamp());
@@ -146,14 +148,23 @@ private:
   // Talos tests for the compositor require at least one frame in the
   // video queue so that the compositor has something to composit during
   // the talos test when the decode is stressed. We have a minimum size
   // on the video queue in order to facilitate this talos test.
   // Note: Normal playback should not have a queue size of more than 0,
   // otherwise A/V sync will be ruined! *Only* make this non-zero for
   // testing purposes.
   const uint32_t mMinVideoQueueSize;
+
+#ifdef XP_WIN
+  // Whether we've called timeBeginPeriod(1) to request high resolution
+  // timers. We request high resolution timers when playback starts, and
+  // turn them off when playback is paused. Enabling high resolution
+  // timers can cause higher CPU usage and battery drain on Windows 7,
+  // but reduces our frame drop rate.
+  bool mHiResTimersRequested;
+#endif
 };
 
 } // namespace media
 } // namespace mozilla
 
 #endif
--- a/dom/media/test/AutoplayTestUtils.js
+++ b/dom/media/test/AutoplayTestUtils.js
@@ -11,26 +11,16 @@ function playAndPostResult(muted, parent
         parent_window.postMessage({played: true}, "*");
       },
       () => {
         parent_window.postMessage({played: false}, "*");
       }
     );
 }
 
-function nextEvent(eventTarget, eventName) {
-  return new Promise(function(resolve, reject) {
-    let f = function(event) {
-      eventTarget.removeEventListener(eventName, f, false);
-      resolve(event);
-    };
-    eventTarget.addEventListener(eventName, f, false);
-  });
-}
-
 function nextWindowMessage() {
   return nextEvent(window, "message");
 }
 
 function log(msg) {
   var log_pane = document.body;
   log_pane.appendChild(document.createTextNode(msg));
   log_pane.appendChild(document.createElement("br"));
--- a/dom/media/test/background_video.js
+++ b/dom/media/test/background_video.js
@@ -9,32 +9,16 @@
 function startTest(test) {
   info(test.desc);
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({ 'set': test.prefs }, () => {
     manager.runTests(test.tests, test.runTest);
   });
 }
 
-/**
- * @param {HTMLMediaElement} video target of interest.
- * @param {string} eventName the event to wait on.
- * @returns {Promise} A promise that is resolved when event happens.
- */
-function nextEvent(video, eventName) {
-  return new Promise(function (resolve, reject) {
-    let f = function (event) {
-      ok(true, `${video.token} ${eventName}.`);
-      video.removeEventListener(eventName, f, false);
-      resolve(event);
-    };
-    video.addEventListener(eventName, f, false);
-  });
-}
-
 function nextVideoEnded(video) {
   return nextEvent(video, 'ended');
 }
 
 function nextVideoPlaying(video) {
   return nextEvent(video, 'playing');
 }
 
deleted file mode 100644
--- a/dom/media/test/dynamic_redirect.sjs
+++ /dev/null
@@ -1,67 +0,0 @@
-function parseQuery(query, key) {
-  for (let p of query.split('&')) {
-    if (p == key) {
-      return true;
-    }
-    if (p.startsWith(key + "=")) {
-      return p.substring(key.length + 1);
-    }
-  }
-}
-
-// Return seek.ogv file content for the first request with a given key.
-// All subsequent requests return a redirect to a different-origin resource.
-function handleRequest(request, response)
-{
-  var query = request.queryString;
-  var key = parseQuery(query, "key");
-  var resource = parseQuery(query, "res");
-  var nested = parseQuery(query, "nested") || false;
-
-  dump("Received request for key = "+ key +"\n");
-  if (!nested) {
-    if (getState(key) == "redirect") {
-      var origin = request.host == "mochi.test" ? "example.org" : "mochi.test:8888";
-      response.setStatusLine(request.httpVersion, 303, "See Other");
-      let url = "http://" + origin +
-                "/tests/dom/media/test/dynamic_redirect.sjs?nested&" + query;
-      dump("Redirecting to "+ url + "\n");
-      response.setHeader("Location", url);
-      response.setHeader("Content-Type", "text/html");
-      return;
-    }
-    setState(key, "redirect");
-  }
-  var file = Components.classes["@mozilla.org/file/directory_service;1"].
-                        getService(Components.interfaces.nsIProperties).
-                        get("CurWorkD", Components.interfaces.nsIFile);
-  var fis  = Components.classes['@mozilla.org/network/file-input-stream;1'].
-                        createInstance(Components.interfaces.nsIFileInputStream);
-  var bis  = Components.classes["@mozilla.org/binaryinputstream;1"].
-                        createInstance(Components.interfaces.nsIBinaryInputStream);
-  var paths = "tests/dom/media/test/" + resource;
-  var split = paths.split("/");
-  for (var i = 0; i < split.length; ++i) {
-    file.append(split[i]);
-  }
-  fis.init(file, -1, -1, false);
-
-  bis.setInputStream(fis);
-  var bytes = bis.readBytes(bis.available());
-  let [from, to] = request.getHeader("range").split("=")[1].split("-").map(s => parseInt(s));
-  to = to || Math.max(from, bytes.length - 1);
-  byterange = bytes.substring(from, to + 1);
-
-  let contentRange = "bytes "+ from +"-"+ to +"/"+ bytes.length;
-  let contentLength = (to - from + 1).toString();
-  dump("Response Content-Range = "+ contentRange +"\n");
-  dump("Response Content-Length = "+ contentLength +"\n");
-
-  response.setStatusLine(request.httpVersion, 206, "Partial Content");
-  response.setHeader("Content-Range", contentRange);
-  response.setHeader("Content-Length", contentLength, false);
-  response.setHeader("Content-Type", "video/ogg", false);
-  response.setHeader("Accept-Ranges", "bytes", false);
-  response.write(byterange, byterange.length);
-  bis.close();
-}
--- a/dom/media/test/file_autoplay_policy_activation_frame.html
+++ b/dom/media/test/file_autoplay_policy_activation_frame.html
@@ -1,12 +1,13 @@
 <!DOCTYPE HTML>
 <html>
   <head>
     <title>Autoplay policy frame</title>
+    <script type="text/javascript" src="manifest.js"></script>
     <script type="text/javascript" src="AutoplayTestUtils.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
     <style>
       video {
         width: 50%;
         height: 50%;
       }
     </style>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/file_autoplay_policy_play_before_loadedmetadata.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <title>Autoplay policy window</title>
+  <style>
+    video {
+      width: 50%;
+      height: 50%;
+    }
+  </style>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="AutoplayTestUtils.js"></script>
+</head>
+
+<body>
+  <pre id="test">
+      <script>
+
+        window.is = window.opener.is;
+        window.info = window.opener.info;
+
+        async function testPlayBeforeLoadedMetata(testCase, parent_window) {
+          info("testPlayBeforeLoadedMetata: " + testCase.resource);
+
+          let element = document.createElement("video");
+          element.preload = "auto";
+          element.src = testCase.resource;
+          document.body.appendChild(element);
+
+          let played = await element.play().then(() => true, () => false);
+          let msg = testCase.resource + " should " + (!testCase.shouldPlay ? "not " : "") + "play";
+          is(played, testCase.shouldPlay, msg);
+
+          removeNodeAndSource(element);
+        }
+
+        nextWindowMessage().then(
+          async (event) => {
+            await testPlayBeforeLoadedMetata(event.data, event.source);
+            event.source.postMessage("done", "*");
+          });
+
+      </script>
+    </pre>
+</body>
+
+</html>
\ No newline at end of file
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -1586,16 +1586,31 @@ function once(target, name, cb) {
     }, {once: true});
   });
   if (cb) {
     p.then(cb);
   }
   return p;
 }
 
+/**
+ * @param {HTMLMediaElement} video target of interest.
+ * @param {string} eventName the event to wait on.
+ * @returns {Promise} A promise that is resolved when event happens.
+ */
+function nextEvent(video, eventName) {
+  return new Promise(function (resolve, reject) {
+    let f = function (event) {
+      video.removeEventListener(eventName, f, false);
+      resolve(event);
+    };
+    video.addEventListener(eventName, f, false);
+  });
+}
+
 function TimeStamp(token) {
   function pad(x) {
     return (x < 10) ? "0" + x : x;
   }
   var now = new Date();
   var ms = now.getMilliseconds();
   var time = "[" +
              pad(now.getHours()) + ":" +
new file mode 100644
--- /dev/null
+++ b/dom/media/test/midflight-redirect.sjs
@@ -0,0 +1,78 @@
+function parseQuery(query, key) {
+  for (let p of query.split('&')) {
+    if (p == key) {
+      return true;
+    }
+    if (p.startsWith(key + "=")) {
+      return p.substring(key.length + 1);
+    }
+  }
+}
+
+// Return the first few bytes in a short byte range response. When Firefox
+// requests subsequent bytes in a second range request, respond with a
+// redirect. Requests after the first redirected are serviced as expected.
+function handleRequest(request, response)
+{
+  var query = request.queryString;
+  var resource = parseQuery(query, "resource");
+  var type = parseQuery(query, "type") || "application/octet-stream";
+  var redirected = parseQuery(query, "redirected") || false;
+  var useCors = parseQuery(query, "cors") || false;
+
+  var file = Components.classes["@mozilla.org/file/directory_service;1"].
+                        getService(Components.interfaces.nsIProperties).
+                        get("CurWorkD", Components.interfaces.nsIFile);
+  var fis  = Components.classes['@mozilla.org/network/file-input-stream;1'].
+                        createInstance(Components.interfaces.nsIFileInputStream);
+  var bis  = Components.classes["@mozilla.org/binaryinputstream;1"].
+                        createInstance(Components.interfaces.nsIBinaryInputStream);
+  var paths = "tests/dom/media/test/" + resource;
+  var split = paths.split("/");
+  for (var i = 0; i < split.length; ++i) {
+    file.append(split[i]);
+  }
+  fis.init(file, -1, -1, false);
+
+  bis.setInputStream(fis);
+  var bytes = bis.readBytes(bis.available());
+  let [from, to] = request.getHeader("range").split("=")[1].split("-").map(s => parseInt(s));
+
+  if (!redirected && from > 0) {
+    var origin = request.host == "mochi.test" ? "example.org" : "mochi.test:8888";
+    response.setStatusLine(request.httpVersion, 303, "See Other");
+    let url = "http://" + origin +
+              "/tests/dom/media/test/midflight-redirect.sjs?redirected&" + query;
+    response.setHeader("Location", url);
+    response.setHeader("Content-Type", "text/html");
+    return;
+  }
+
+  if (isNaN(to)) {
+    to = bytes.length - 1;
+  }
+
+  if (from == 0 && !redirected) {
+    to = parseInt(parseQuery(query, "redirectAt")) || Math.floor(bytes.length / 4);
+  }
+  to = Math.min(to, bytes.length - 1);
+
+  // Note: 'to' is the first index *excluded*, so we need (to + 1)
+  // in the substring end here.
+  byterange = bytes.substring(from, to + 1);
+
+  let contentRange = "bytes " + from + "-" + to + "/" + bytes.length;
+  let contentLength = byterange.length.toString();
+
+  response.setStatusLine(request.httpVersion, 206, "Partial Content");
+  response.setHeader("Content-Range", contentRange);
+  response.setHeader("Content-Length", contentLength, false);
+  response.setHeader("Content-Type", type, false);
+  response.setHeader("Accept-Ranges", "bytes", false);
+  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  if (redirected && useCors) {
+    response.setHeader("Access-Control-Allow-Origin", "*");
+  }
+  response.write(byterange, byterange.length);
+  bis.close();
+}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -429,23 +429,23 @@ support-files =
   detodos-short.webm
   detodos-short.webm^headers^
   detodos-recorder-test.opus
   detodos-recorder-test.opus^headers^
   detodos-short.opus
   detodos-short.opus^headers^
   dirac.ogg
   dirac.ogg^headers^
-  dynamic_redirect.sjs
   dynamic_resource.sjs
   eme.js
   file_access_controls.html
   file_autoplay_policy_unmute_pauses.html
   file_autoplay_policy_activation_window.html
   file_autoplay_policy_activation_frame.html
+  file_autoplay_policy_play_before_loadedmetadata.html
   flac-s24.flac
   flac-s24.flac^headers^
   flac-noheader-s16.flac
   flac-noheader-s16.flac^headers^
   fragment_noplay.js
   fragment_play.js
   gizmo.mp4
   gizmo.mp4^headers^
@@ -490,16 +490,17 @@ support-files =
   invalid-m2c1.opus
   invalid-m2c1.opus^headers^
   invalid-neg_discard.webm
   invalid-neg_discard.webm^headers^
   invalid-preskip.webm
   invalid-preskip.webm^headers^
   long.vtt
   manifest.js
+  midflight-redirect.sjs
   multiple-bos.ogg
   multiple-bos.ogg^headers^
   multiple-bos-more-header-fileds.ogg
   multiple-bos-more-header-fileds.ogg^headers^
   no-cues.webm
   no-cues.webm^headers^
   notags.mp3
   notags.mp3^headers^
@@ -690,16 +691,18 @@ skip-if = true # bug 475110 - disabled s
 [test_autoplay_contentEditable.html]
 skip-if = android_version == '15' || android_version == '17' || android_version == '22' # android(bug 1232305, bug 1232318, bug 1372457)
 [test_autoplay_policy.html]
 skip-if = android_version == '23' # bug 1424903
 [test_autoplay_policy_activation.html]
 skip-if = android_version == '23' # bug 1424903
 [test_autoplay_policy_unmute_pauses.html]
 skip-if = android_version == '23' # bug 1424903
+[test_autoplay_policy_play_before_loadedmetadata.html]
+skip-if = android_version == '23' # bug 1424903
 [test_buffered.html]
 skip-if = android_version == '15' || android_version == '22' # bug 1308388, android(bug 1232305)
 [test_bug448534.html]
 [test_bug463162.xhtml]
 [test_bug465498.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_bug495145.html]
 skip-if = (os == 'mac' && os_version == '10.6') || (toolkit == 'android')  # bug 1311229, android(bug 1232305)
@@ -945,16 +948,17 @@ skip-if = android_version == '17' # andr
 tags=msg
 [test_mediatrack_events.html]
 skip-if = android_version == '17' # android(bug 1232305)
 [test_mediatrack_parsing_ogg.html]
 skip-if = android_version == '17' # android(bug 1232305)
 [test_mediatrack_replay_from_end.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_metadata.html]
+[test_midflight_redirect_blocked.html]
 [test_mixed_principals.html]
 skip-if = toolkit == 'android' # bug 1309814, android(bug 1232305)
 [test_mozHasAudio.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_multiple_mediastreamtracks.html]
 skip-if = android_version == '17' # android(bug 1232305)
 [test_networkState.html]
 skip-if = android_version == '17' # android(bug 1232305)
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_autoplay_policy_play_before_loadedmetadata.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <title>Autoplay policy test</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="AutoplayTestUtils.js"></script>
+</head>
+
+<body>
+  <pre id="test">
+      <script>
+
+        window.is = SimpleTest.is;
+        window.info = SimpleTest.info;
+
+        // Tests that videos which have no audio track will play if play()
+        // is called before the video has reached readyState >= HAVE_METADATA.
+
+        gTestPrefs.push(["media.autoplay.enabled", false],
+          ["media.autoplay.enabled.user-gestures-needed", true]);
+
+        SpecialPowers.pushPrefEnv({ 'set': gTestPrefs }, () => {
+          runTest();
+        });
+
+        let testCases = [
+          {
+            resource: "320x240.ogv", // Only video track.
+            shouldPlay: true,
+          },
+          {
+            resource: "short.mp4", // Audio and video track.
+            shouldPlay: false,
+          },
+        ];
+
+        let child_url = "file_autoplay_policy_play_before_loadedmetadata.html";
+
+        async function runTest() {
+          for (testCase of testCases) {
+            // Run each test in a new window, to ensure its user gesture
+            // activation state isn't tainted by preceeding tests.
+            let child = window.open(child_url, "", "width=500,height=500");
+            await once(child, "load");
+            child.postMessage(testCase, window.origin);
+            let result = await nextWindowMessage();
+            child.close();
+          }
+          SimpleTest.finish();
+        }
+
+        SimpleTest.waitForExplicitFinish();
+
+      </script>
+    </pre>
+</body>
+
+</html>
\ No newline at end of file
--- a/dom/media/test/test_mediarecorder_principals.html
+++ b/dom/media/test/test_mediarecorder_principals.html
@@ -10,109 +10,123 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="manifest.js"></script>
 </head>
 <body>
 <div>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1018299">Test for MediaRecorder Principal Handling</a>
 </div>
 
-<video id="v1" preload="metadata"></video>
-<video id="v2" preload="metadata"></video>
-
 <pre id="test">
 <script type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 let throwOutside = e => setTimeout(() => { throw e; });
 
-// Generate a random key. The first load with that key will return
-// data, the second and subsequent loads with that key will return a redirect
-// to a different origin ('localhost:8888' will be redirected to 'example.org',
-// and 'example.org' will be redirected to 'localhost:8888').
-// Loading data from two different origins should be detected by the media
-// cache and result in a null principal so that the MediaRecorder usages below
-// fail.
-// This test relies on that preloading the metadata then forcing another load
-// via video.load() will result in two requests taking place to retreive the
-// resource.
-let key = Math.floor(Math.random()*100000000);
-let interval;
+// Loading data from a resource that changes origins while streaming should
+// be detected by the media cache and result in a null principal so that the
+// MediaRecorder usages below fail.
 
-function testPrincipals(resource) {
+// This test relies on midflight-redirect.sjs returning the the first quarter of
+// the resource as a byte range response, and then hanging up, and when Firefox
+// requests the remainder midflight-redirect.sjs serves a redirect to another origin.
+
+async function testPrincipals(resource) {
   if (!resource) {
     todo(false, "No types supported");
     return;
   }
-  // First test: Load file from same-origin first, then get redirected to
-  // another origin before attempting to record stream.
-  let video = document.getElementById("v1");
+  await testPrincipals1(resource);
+  await testPrincipals2(resource);
+}
+
+function makeVideo() {
+  let video = document.createElement("video");
+  video.preload = "metadata";
+  video.controls = true;
+  document.body.appendChild(video);
+  return video;
+}
+
+// First test: Load file from same-origin first, then get redirected to
+// another origin before attempting to record stream.
+async function testPrincipals1(resource) {
+  let video = makeVideo();
   video.src =
-      "http://mochi.test:8888/tests/dom/media/test/dynamic_redirect.sjs?key=v1_" +
-      key + "&res=" + resource.name;
-  return new Promise(resolve => video.onloadedmetadata = resolve).then(() => {
-    video.load();
-    video.play();
-    interval = setInterval(() => info("video.currentTime = "+ video.currentTime), 1000);
+      "http://mochi.test:8888/tests/dom/media/test/midflight-redirect.sjs" +
+      "?resource=" + resource.name + "&type=" + resource.type;
+
+  let errorBarrier = once(video, "error");
+  // Wait for the video to load to metadata. We can then start capturing.
+  // Must also handle the download bursting and hitting the error before we
+  // reach loadedmetadata. Normally we reach loadedmetadata first, but
+  // rarely we hit the redirect first.
+  await Promise.race([once(video, "loadedmetadata"), errorBarrier]);
+
+  let rec = new MediaRecorder(video.mozCaptureStreamUntilEnded());
+  video.play();
+
+  // Wait until we hit a playback error. This means our download has hit the redirect.
+  await errorBarrier;
+
+  // Try to record, it should be blocked with a security error.
+  try {
+    rec.start();
+    ok(false, "mediaRecorder.start() must throw SecurityError, but didn't throw at all");
+  } catch (ex) {
+    is(ex.name, "SecurityError", "mediaRecorder.start() must throw SecurityError");
+  }
+  removeNodeAndSource(video);
+}
 
-    let msg = "mediaRecorder.start() must throw SecurityError";
-    return new Promise(resolve => video.onplaying = resolve)
-    .then(() => waitUntil(() => video.currentTime > resource.duration / 5))
-    // Test failure of the next step only, so "catch-bypass" any errors above.
-    .then(() => Promise.resolve()
-      .then(() => new MediaRecorder(video.mozCaptureStreamUntilEnded()).start())
-      .then(() => ok(false, msg), e => is(e.name, "SecurityError", msg)), 0)
-    .then(() => clearInterval(interval));
-  })
-  .then(() => {
-    // Second test: Load file from same-origin first, but record ASAP, before
-    // getting redirected to another origin.
-    let video = document.getElementById("v2");
-    video.src =
-        "http://mochi.test:8888/tests/dom/media/test/dynamic_redirect.sjs?key=v2_" +
-        key + "&res=" + resource.name;
-    let rec, hasStopped, hasEnded = new Promise(r => video.onended = r);
-    let data = [];
+// Second test: Load file from same-origin first, but record ASAP, before
+// getting redirected to another origin.
+async function testPrincipals2(resource) {
+  let video = makeVideo();
+  video.src =
+      "http://mochi.test:8888/tests/dom/media/test/midflight-redirect.sjs" +
+      "?resource=" + resource.name + "&type=" + resource.type;
+
+  // Wait for the video to load to metadata. We can then start capturing.
+  // Must also handle the download bursting and hitting the error before we
+  // reach loadedmetadata. Normally we reach loadedmetadata first, but
+  // rarely we hit the redirect first.
+  await Promise.race([once(video, "loadedmetadata"), once(video, "error")]);
+
+  let ended = false;
+  once(video, "ended", () => ended = true);
 
-    let msgNoThrow = "mediaRecorder.start() should not throw here";
-    let msgSecErr = "mediaRecorder.onerror must fire SecurityError";
-    let msgOnStop = "mediaRecorder.onstop must also have fired";
-    return new Promise(resolve => video.onloadedmetadata = resolve).then(() => {
-      rec = new MediaRecorder(video.mozCaptureStreamUntilEnded());
-      rec.ondataavailable = e => data.push(e.data);
-      rec.start();
-      video.load();
-      hasStopped = new Promise(resolve => rec.onstop = resolve);
-      video.play();
-    })
-    .then(() => ok(true, msgNoThrow), e => is(e.error.name, null, msgNoThrow))
-    .then(() => Promise.race([
-      new Promise((_, reject) => rec.onerror = e => reject(e.error)),
-      hasEnded
-    ]))
-    .then(() => ok(false, msgSecErr), e => {
-      is(e.name, "SecurityError", msgSecErr);
-      ok(e.stack.includes('test_mediarecorder_principals.html'),
-      'Events fired from onerror should include an error with a stack trace indicating ' +
-      'an error in this test');
-    })
-    .then(() => Promise.race([hasStopped, hasEnded.then(() => Promise.reject())]))
-    .then(() => ok(true, msgOnStop), e => ok(false, msgOnStop))
-    .then(() => clearInterval(interval));
-  });
+  // Start capturing. It should work.
+  let rec;
+  let errorBarrier;
+  try {
+    rec = new MediaRecorder(video.mozCaptureStreamUntilEnded());
+    errorBarrier = nextEvent(rec, "error");
+    rec.start();
+    ok(true, "mediaRecorder.start() should not throw here, and didn't");
+  } catch (ex) {
+    ok(false, "mediaRecorder.start() unexpectedly threw " + ex.name + " (" + ex.message + ")");
+  }
+
+  // Play the video, this should result in a SecurityError on the recorder.
+  let hasStopped = once(rec, "stop");
+  video.play();
+  let error = (await errorBarrier).error;
+  is(error.name, "SecurityError", "mediaRecorder.onerror must fire SecurityError");
+  ok(error.stack.includes('test_mediarecorder_principals.html'),
+    'Events fired from onerror should include an error with a stack trace indicating ' +
+    'an error in this test');
+  is(ended, false, "Playback should not have reached end");
+  await hasStopped;
+  is(ended, false, "Playback should definitely not have reached end");
+
+  removeNodeAndSource(video);
 }
 
 testPrincipals({ name:"pixel_aspect_ratio.mp4", type:"video/mp4", duration:28 })
-.catch(e => throwOutside(e))
-.then(() => SimpleTest.finish())
-.catch(e => throwOutside(e));
-
-let stop = stream => stream.getTracks().forEach(track => track.stop());
-let wait = ms => new Promise(resolve => setTimeout(resolve, ms));
-let waitUntil = f => new Promise(resolve => {
-  let ival = setInterval(() => f() && resolve(clearInterval(ival)), 100);
-});
+  .catch(e => throwOutside(e))
+  .then(() => SimpleTest.finish());
 
 </script>
 </pre>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_midflight_redirect_blocked.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test mid-flight cross site redirects are blocked</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="text/javascript" src="manifest.js"></script>
+  </head>
+  <body>
+    <pre id='test'>
+    <script class="testbody" type='application/javascript'>
+
+      function testIfLoadsToMetadata(test, useCors) {
+        return new Promise(function(resolve, reject) {
+          var elemType = getMajorMimeType(test.type);
+          var element = document.createElement(elemType);
+
+          if (useCors) {
+            element.crossOrigin = "anonymous";
+          }
+
+          // Log events for debugging.
+          [
+            "suspend", "play", "canplay", "canplaythrough", "loadstart",
+            "loadedmetadata", "loadeddata", "playing", "ended", "error",
+            "stalled", "emptied", "abort", "waiting", "pause"
+          ].forEach((eventName) => {
+            element.addEventListener(eventName, (event)=> {
+              info(test.name + " " + event.type);
+            });
+          });
+
+          element.addEventListener("loadedmetadata", ()=>{
+            resolve(true);
+            removeNodeAndSource(element);
+          }, false);
+
+          element.addEventListener("error", ()=>{
+            resolve(false);
+            removeNodeAndSource(element);
+          }, false);
+
+          var noise = Math.floor(Math.random() * 100000000);
+          // Note: request redirect before the end of metadata, otherwise we won't
+          // error before metadata has loaded if mixed origins are allowed.
+          element.src = "midflight-redirect.sjs?resource=" + test.name
+                      + (useCors ? "&cors" : "")
+                      + "&type=" + test.type
+                      + "&redirectAt=200";
+          element.preload = "metadata";
+          document.body.appendChild(element);
+          element.load()
+        });
+      }
+
+      let v = document.createElement("video");
+      const testCases = gSmallTests.filter(t => v.canPlayType(t.type));
+
+      function testMediaLoad(expectedToLoad, message, useCors) {
+        return new Promise(async function(resolve, reject) {
+          for (let test of testCases) {
+            let loaded = await testIfLoadsToMetadata(test, useCors);
+            is(loaded, expectedToLoad, test.name + " " + message);
+          }
+          resolve();
+        });
+      }
+
+      async function runTest() {
+        try {
+          SimpleTest.info("Allowing midflight redirects...");
+          await SpecialPowers.pushPrefEnv({'set': [["media.block-midflight-redirects", false]]});
+
+          SimpleTest.info("Test that all media plays...");
+          await testMediaLoad(true, "expected to load", false);
+
+          SimpleTest.info("Blocking midflight redirects...");
+          await SpecialPowers.pushPrefEnv({'set': [["media.block-midflight-redirects", true]]});
+
+          SimpleTest.info("Test that all media no longer play...");
+          await testMediaLoad(false, "expected to be blocked", false);
+
+          SimpleTest.info("Test that all media play if CORS used...");
+          await testMediaLoad(true, "expected to play with CORS", true);
+        } catch (e) {
+          info("Exception " + e.message);
+          ok(false, "Threw exception " + e.message);
+        }
+        SimpleTest.finish();
+      }
+
+      SimpleTest.waitForExplicitFinish();
+
+      addLoadEvent(runTest);
+
+    </script>
+    </pre>
+  </body>
+</html>
--- a/dom/media/test/test_mixed_principals.html
+++ b/dom/media/test/test_mixed_principals.html
@@ -2,93 +2,82 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=489415
 -->
 <head>
   <title>Test for Bug 489415</title>
   <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <style>
+    video {
+      width: 40%;
+      border: solid black 1px;
+    }
+  </style>
 </head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489415">Mozilla Bug 489415</a>
-<p id="display"></p>
-
-<video id="v1" preload="metadata" onended="onendedcb('v1')"></video>
-<video id="v2" preload="metadata" onended="onendedcb('v2')"></video>
-
-<pre id="test">
-<script type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
-
-var v1 = document.getElementById("v1");
-var v2 = document.getElementById("v2");
-var count = 0;
 
-function onendedcb(id) {
-  var c = document.createElement("canvas");
-  var ctx = c.getContext("2d");
-  var v = document.getElementById(id);
-  ctx.drawImage(v, 0, 0);
-  try {
-    c.toDataURL();
-    ok(false, "Failed to throw exception in toDataURL for " + id);
-  } catch (ex) {
-    ok(true, "Threw exception in toDataURL for " + id);
-  }
-  if (++count == 2) {
-    SimpleTest.finish();
-  }
-}
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489415">Mozilla Bug 489415</a>
+  <p id="display"></p>
+  <pre id="test">
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+
+    var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({ set: p });
+    var count = 0;
 
-function testMixedPrincipals(resource) {
-  // In some OS(XP) can not play mp4. Add this checking for the test failure.
-  if (!resource || !v1.canPlayType(resource.type)) {
-    todo(false, "No types supported");
-    SimpleTest.finish();
-    return;
-  }
-  // Make sure the media cache size(50MB) is large enough that the data could
-  // be download in MediaCache completely.
-  return pushPrefs(['media.cache_size', 50*1024])
-  .then(() => {
-
-    // Generate a random key. The first load with that key will return
-    // data, the second and subsequent loads with that key will return a redirect
-    // to a different origin ('localhost:8888' will be redirected to 'example.org',
-    // and 'example.org' will be redirected to 'localhost:8888'). We rely on the
-    // fact that Ogg will do a seek to the end of the resource, triggering a new
-    // load with the same key which will return a same-origin resource.
-    // Loading data from two different origins should be detected by the media
-    // cache and result in a null principal so that the canvas usage above fails.
-    var key = Math.floor(Math.random()*100000000);
-
-    // In v1, try loading from same-origin first and then getting redirected to
-    // another origin.
-    v1.src =
-        "http://mochi.test:8888/tests/dom/media/test/dynamic_redirect.sjs?key=v1_" +
-        key + "&res=" + resource.name;
-    v1.onloadeddata = function () {
-      // To limit readahead, avoid racing with playback and "catching up" mode.
-      v1.play();
+    function canReadBack(video) {
+      var c = document.createElement("canvas");
+      var ctx = c.getContext("2d");
+      ctx.drawImage(video, 0, 0);
+      try {
+        c.toDataURL();
+        return true;
+      } catch (ex) {
+        return false;
+      }
     }
 
-    // In v2, try loading cross-origin first and then getting redirected to
-    // our origin.
-    v2.src = "http://example.org/tests/dom/media/test/dynamic_redirect.sjs?key=v2_" + key + "&res=" + resource.name;
-    v2.onloadeddata = function () {
-      // To limit readahead, avoid racing with playback and "catching up" mode.
-      v2.play();
+    function runTest(origin, shouldReadBackOnLoad) {
+      return new Promise(function (resolve, reject) {
+        // Load will redirect mid-flight, which will be detected and should error,
+        // and we should no longer be able to readback.
+        var video = document.createElement("video");
+        video.preload = "metadata";
+        video.controls = true;
+        var url = "http://" + origin + "/tests/dom/media/test/midflight-redirect.sjs"
+                  + "?resource=pixel_aspect_ratio.mp4&type=video/mp4";
+        SimpleTest.info("Loading from " + url);
+        video.src = url;
+        document.body.appendChild(video);
+
+        once(video, "loadeddata", () => {
+          is(canReadBack(video), shouldReadBackOnLoad, "Should be able to readback");
+          video.play();
+        });
+
+        once(video, "error", () => {
+          is(canReadBack(video), false, "Should not be able to readback after error");
+          removeNodeAndSource(video);
+          resolve();
+        });
+
+        once(video, "ended", () => {
+          ok(false, "Should not be able to playback to end, we should have errored!");
+          removeNodeAndSource(video);
+          resolve();
+        });
+
+      });
     }
-  });
-}
 
-testMixedPrincipals({ name:"pixel_aspect_ratio.mp4", type:"video/mp4", duration:28});
+    Promise.all([
+      runTest("mochi.test:8888", true),
+      runTest("example.org", false),
+    ]).then(SimpleTest.finish);
 
-</script>
+  </script>
 </pre>
-
 </body>
 </html>
--- a/dom/tests/browser/browser_noopener.js
+++ b/dom/tests/browser/browser_noopener.js
@@ -39,17 +39,17 @@ async function doTests(private, containe
   for (let test of TESTS) {
     const testid = `${test.id} (private=${private}, container=${container}, alwaysNewWindow=${alwaysNewWindow})`;
     let originalTab = BrowserTestUtils.addTab(window.gBrowser, TEST_URL, tabOpenOptions);
     await BrowserTestUtils.browserLoaded(originalTab.linkedBrowser);
     await BrowserTestUtils.switchTab(window.gBrowser, originalTab);
 
     let waitFor;
     if (test.newWindow || alwaysNewWindow) {
-      waitFor = BrowserTestUtils.waitForNewWindow(TARGET_URL);
+      waitFor = BrowserTestUtils.waitForNewWindow({url: TARGET_URL});
       // Confirm that this window has private browsing set if we're doing a private browsing test
     } else {
       waitFor = BrowserTestUtils.waitForNewTab(window.gBrowser, TARGET_URL, true);
     }
 
     BrowserTestUtils.synthesizeMouseAtCenter(test.id, {}, window.gBrowser.getBrowserForTab(originalTab));
 
     let tab;
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -324,17 +324,19 @@ ResourceReader::OnWalkSubframe(nsIDOMNod
     NS_ENSURE_STATE(loaderOwner);
     RefPtr<nsFrameLoader> loader = loaderOwner->GetFrameLoader();
     NS_ENSURE_STATE(loader);
 
     ++mOutstandingDocuments;
     // Pass in 0 as the outer window ID so that we start
     // persisting the root of this subframe, and not some other
     // subframe child of this subframe.
-    nsresult rv = loader->StartPersistence(0, this);
+    ErrorResult err;
+    loader->StartPersistence(0, this, err);
+    nsresult rv = err.StealNSResult();
     if (NS_FAILED(rv)) {
         if (rv == NS_ERROR_NO_CONTENT) {
             // Just ignore frames with no content document.
             rv = NS_OK;
         }
         // StartPersistence won't eventually call this if it failed,
         // so this does so (to keep mOutstandingDocuments correct).
         DocumentDone(rv);
--- a/dom/webbrowserpersist/moz.build
+++ b/dom/webbrowserpersist/moz.build
@@ -6,17 +6,16 @@
 
 # moved from embedding/components/webbrowserpersist Jan 13, 2017
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 XPIDL_SOURCES += [
     'nsCWebBrowserPersist.idl',
     'nsIWebBrowserPersist.idl',
-    'nsIWebBrowserPersistable.idl',
     'nsIWebBrowserPersistDocument.idl',
 ]
 
 XPIDL_MODULE = 'webbrowserpersist'
 
 IPDL_SOURCES += [
     'PWebBrowserPersistDocument.ipdl',
     'PWebBrowserPersistResources.ipdl',
--- a/dom/webbrowserpersist/nsIWebBrowserPersist.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersist.idl
@@ -243,17 +243,17 @@ interface nsIWebBrowserPersist : nsICanc
    *                           document and all subdocuments into or nullptr to use
    *                           the default behaviour.
    * @param aEncodingFlags     Flags to pass to the encoder.
    * @param aWrapColumn        For text documents, indicates the desired width to
    *                           wrap text at. Parameter is ignored if wrapping is not
    *                           specified by the encoding flags.
    *
    * @see nsIWebBrowserPersistDocument
-   * @see nsIWebBrowserPersistable
+   * @see WebBrowserPersistable
    * @see nsIFile
    * @see nsIURI
    *
    * @throws NS_ERROR_INVALID_ARG One or more arguments was invalid.
    */
   void saveDocument(in nsISupports aDocument,
      in nsISupports aFile, in nsISupports aDataPath,
      in string aOutputContentType, in unsigned long aEncodingFlags,
--- a/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
@@ -182,16 +182,19 @@ interface nsIWebBrowserPersistWriteCompl
                 in ACString aContentType,
                 in nsresult aStatus);
 };
 
 /**
  * Asynchronous callback for creating a persistable document from some
  * other object.
  *
- * @see nsIWebBrowserPersistable.
+ * XXXbz This should really be changed to just return a promise that
+ * then gets resolved or rejected...
+ *
+ * @see WebBrowserPersistable in FrameLoader.webidl.
  */
 [scriptable, uuid(321e3174-594f-4036-b7be-791b821bd376)]
 interface nsIWebBrowserPersistDocumentReceiver : nsISupports
 {
   void onDocumentReady(in nsIWebBrowserPersistDocument aDocument);
   void onError(in nsresult aFailure);
 };
deleted file mode 100644
--- a/dom/webbrowserpersist/nsIWebBrowserPersistable.idl
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- Mode: IDL; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIWebBrowserPersistDocumentReceiver;
-
-/**
- * Interface for objects which represent a document that can be
- * serialized with nsIWebBrowserPersist.  This interface is
- * asynchronous because the actual document can be in another process
- * (e.g., if this object is an nsFrameLoader for an out-of-process
- * frame).
- *
- * Warning: this is currently implemented only by nsFrameLoader, and
- * may change in the future to become more frame-loader-specific or be
- * merged into nsFrameLoader.  See bug 1101100 comment #34.
- *
- * @see nsIWebBrowserPersistDocumentReceiver
- * @see nsIWebBrowserPersistDocument
- * @see nsIWebBrowserPersist
- *
- * @param aOuterWindowID
- *        The outer window ID of the subframe we'd like to persist.
- *        If set at 0, nsIWebBrowserPersistable will attempt to persist
- *        the top-level document. If the outer window ID is for a subframe
- *        that does not exist, or is not held beneath the nsIWebBrowserPersistable,
- *        aRecv's onError method will be called with NS_ERROR_NO_CONTENT.
- * @param aRecv
- *        The nsIWebBrowserPersistDocumentReceiver is a callback that
- *        will be fired once the document is ready for persisting.
- */
-[uuid(f4c3fa8e-83e9-49f8-ac6f-951fc7541fe4)]
-interface nsIWebBrowserPersistable : nsISupports
-{
-  void startPersistence(in unsigned long long aOuterWindowID,
-                        in nsIWebBrowserPersistDocumentReceiver aRecv);
-};
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -50,17 +50,16 @@
 #include "nsIImageLoadingContent.h"
 
 #include "ftpCore.h"
 #include "nsITransport.h"
 #include "nsISocketTransport.h"
 #include "nsIStringBundle.h"
 #include "nsIProtocolHandler.h"
 
-#include "nsIWebBrowserPersistable.h"
 #include "nsWebBrowserPersist.h"
 #include "WebBrowserPersistLocalDocument.h"
 
 #include "nsIContent.h"
 #include "nsIMIMEInfo.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/Printf.h"
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -174,16 +174,39 @@ interface FrameLoader {
 
   /**
    * Is `true` if the frameloader is dead (destroy has been called on it)
    */
   [Pure]
   readonly attribute boolean isDead;
 };
 
+/**
+ * Interface for objects which represent a document that can be
+ * serialized with nsIWebBrowserPersist.  This interface is
+ * asynchronous because the actual document can be in another process
+ * (e.g., if this object is a FrameLoader for an out-of-process
+ * frame).
+ *
+ * XXXbz This method should really just return a Promise...
+ *
+ * @see nsIWebBrowserPersistDocumentReceiver
+ * @see nsIWebBrowserPersistDocument
+ * @see nsIWebBrowserPersist
+ *
+ * @param aOuterWindowID
+ *        The outer window ID of the subframe we'd like to persist.
+ *        If set at 0, WebBrowserPersistable will attempt to persist
+ *        the top-level document. If the outer window ID is for a subframe
+ *        that does not exist, or is not held beneath the WebBrowserPersistable,
+ *        aRecv's onError method will be called with NS_ERROR_NO_CONTENT.
+ * @param aRecv
+ *        The nsIWebBrowserPersistDocumentReceiver is a callback that
+ *        will be fired once the document is ready for persisting.
+ */
 [NoInterfaceObject]
 interface WebBrowserPersistable
 {
   [Throws]
   void startPersistence(unsigned long long aOuterWindowID,
                         nsIWebBrowserPersistDocumentReceiver aRecv);
 };
 
--- a/dom/webidl/XMLSerializer.webidl
+++ b/dom/webidl/XMLSerializer.webidl
@@ -5,16 +5,34 @@
  * The origin of this IDL file is
  * http://domparsing.spec.whatwg.org/#the-xmlserializer-interface
  */
 
 interface OutputStream;
 
 [Constructor]
 interface XMLSerializer {
+  /**
+   * The subtree rooted by the specified element is serialized to
+   * a string.
+   *
+   * @param root The root of the subtree to be serialized. This could
+   *             be any node, including a Document.
+   * @returns The serialized subtree in the form of a Unicode string
+   */
   [Throws]
   DOMString serializeToString(Node root);
 
   // Mozilla-specific stuff
+  /**
+   * The subtree rooted by the specified element is serialized to
+   * a byte stream using the character set specified.
+   * @param root The root of the subtree to be serialized. This could
+   *             be any node, including a Document.
+   * @param stream The byte stream to which the subtree is serialized.
+   * @param charset The name of the character set to use for the encoding
+   *                to a byte stream.  If this string is empty and root is
+   *                a document, the document's character set will be used.
+   */
   [Throws, ChromeOnly]
   void serializeToStream(Node root, OutputStream stream, DOMString? charset);
 };
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -50,17 +50,16 @@
 #include "nsIOutputStream.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISupportsPriority.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIUploadChannel.h"
 #include "nsIUploadChannel2.h"
-#include "nsIDOMSerializer.h"
 #include "nsXPCOM.h"
 #include "nsIDOMEventListener.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIVariant.h"
 #include "nsVariant.h"
 #include "nsIScriptError.h"
 #include "nsIStreamConverterService.h"
 #include "nsICachingChannel.h"
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -254,19 +254,16 @@ XMLDocument::XMLDocument(const char* aCo
 }
 
 XMLDocument::~XMLDocument()
 {
   // XXX We rather crash than hang
   mLoopingForSyncLoad = false;
 }
 
-// QueryInterface implementation for XMLDocument
-NS_IMPL_ISUPPORTS_INHERITED(XMLDocument, nsDocument, nsIDOMXMLDocument)
-
 nsresult
 XMLDocument::Init()
 {
   nsresult rv = nsDocument::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
--- a/dom/xml/XMLDocument.h
+++ b/dom/xml/XMLDocument.h
@@ -5,32 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_XMLDocument_h
 #define mozilla_dom_XMLDocument_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsDocument.h"
-#include "nsIDOMXMLDocument.h"
 #include "nsIScriptContext.h"
 
 class nsIURI;
 class nsIChannel;
 
 namespace mozilla {
 namespace dom {
 
-class XMLDocument : public nsDocument,
-                    public nsIDOMXMLDocument
+class XMLDocument : public nsDocument
 {
 public:
   explicit XMLDocument(const char* aContentType = "application/xml");
 
-  NS_DECL_ISUPPORTS_INHERITED
+  NS_INLINE_DECL_REFCOUNTING_INHERITED(XMLDocument, nsDocument)
 
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
   virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
                           nsIPrincipal* aPrincipal) override;
 
   virtual void SetSuppressParserErrorElement(bool aSuppress) override;
   virtual bool SuppressParserErrorElement() override;
 
@@ -41,19 +39,16 @@ public:
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener **aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aSink = nullptr) override;
 
   virtual void EndLoad() override;
 
-  // nsIDOMXMLDocument
-  NS_DECL_NSIDOMXMLDOCUMENT
-
   virtual nsresult Init() override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   virtual void DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const override;
   // DocAddSizeOfIncludingThis is inherited from nsIDocument.
 
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -26,17 +26,16 @@
 #include "XULDocument.h"
 
 #include "nsError.h"
 #include "nsIBoxObject.h"
 #include "nsIChromeRegistry.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIContentViewer.h"
-#include "nsIDOMXULElement.h"
 #include "nsIStreamListener.h"
 #include "nsITimer.h"
 #include "nsDocShell.h"
 #include "nsGkAtoms.h"
 #include "nsXMLContentSink.h"
 #include "nsXULContentSink.h"
 #include "nsXULContentUtils.h"
 #include "nsIXULOverlayProvider.h"
--- a/dom/xul/nsXULCommandDispatcher.cpp
+++ b/dom/xul/nsXULCommandDispatcher.cpp
@@ -11,17 +11,16 @@
  */
 
 #include "nsIContent.h"
 #include "nsFocusManager.h"
 #include "nsIControllers.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsRDFCID.h"
 #include "nsXULCommandDispatcher.h"
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -10,17 +10,16 @@
 #include "nsIDOMEvent.h"
 #include "nsAtom.h"
 #include "nsIBaseWindow.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMXULCommandDispatcher.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/DeclarationBlockInlines.h"
@@ -112,17 +111,17 @@ public:
 
   explicit nsXULElementTearoff(nsXULElement* aElement)
     : mElement(aElement)
   {
   }
 
   NS_FORWARD_NSIFRAMELOADEROWNER(static_cast<nsXULElement*>(mElement.get())->)
 private:
-  nsCOMPtr<nsIDOMXULElement> mElement;
+  RefPtr<nsXULElement> mElement;
 };
 
 NS_IMPL_CYCLE_COLLECTION(nsXULElementTearoff, mElement)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULElementTearoff)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULElementTearoff)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULElementTearoff)
@@ -290,18 +289,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
     // Why aren't we unlinking the prototype?
     tmp->ClearHasID();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement)
 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement)
-    NS_INTERFACE_TABLE_INHERITED(nsXULElement, nsIDOMNode, nsIDOMElement,
-                                 nsIDOMXULElement)
+    NS_INTERFACE_TABLE_INHERITED(nsXULElement, nsIDOMNode, nsIDOMElement)
     NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
     NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIFrameLoaderOwner,
                                    new nsXULElementTearoff(this))
 NS_INTERFACE_MAP_END_INHERITING(nsStyledElement)
 
 //----------------------------------------------------------------------
 // nsIDOMNode interface
 
@@ -1526,17 +1524,17 @@ void
 nsXULElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
                                ErrorResult& rv)
 {
     if (!GetExistingDOMSlots()) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(ToSupports(this));
     aOtherLoaderOwner.SwapFrameLoaders(flo, rv);
 }
 
 void
 nsXULElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                                ErrorResult& rv)
 {
     if (&aOtherLoaderOwner == this) {
@@ -1544,17 +1542,17 @@ nsXULElement::SwapFrameLoaders(nsXULElem
         return;
     }
 
     if (!GetExistingDOMSlots()) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(ToSupports(this));
     aOtherLoaderOwner.SwapFrameLoaders(flo, rv);
 }
 
 void
 nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                                mozilla::ErrorResult& rv)
 {
     if (!GetExistingDOMSlots()) {
@@ -1564,17 +1562,17 @@ nsXULElement::SwapFrameLoaders(nsIFrameL
 
     RefPtr<nsFrameLoader> loader = GetFrameLoader();
     RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader();
     if (!loader || !otherLoader) {
         rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
         return;
     }
 
-    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this));
+    nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(ToSupports(this));
     rv = loader->SwapWithOtherLoader(otherLoader, flo, aOtherLoaderOwner);
 }
 
 NS_IMETHODIMP
 nsXULElement::GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement)
 {
     for (nsIContent* current = GetParent(); current;
          current = current->GetParent()) {
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -15,17 +15,16 @@
 #include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMEvent.h"
 #include "nsIServiceManager.h"
 #include "nsAtom.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsIControllers.h"
 #include "nsIDOMElement.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIURI.h"
 #include "nsLayoutCID.h"
 #include "nsAttrAndChildArray.h"
 #include "nsGkAtoms.h"
 #include "nsStringFwd.h"
 #include "nsStyledElement.h"
 #include "nsIFrameLoaderOwner.h"
@@ -330,17 +329,17 @@ enum {
   XUL_ELEMENT_HAS_POPUP_LISTENER =        XUL_ELEMENT_FLAG_BIT(1)
 };
 
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
 
 #undef XUL_ELEMENT_FLAG_BIT
 
 class nsXULElement final : public nsStyledElement,
-                           public nsIDOMXULElement
+                           public nsIDOMElement
 {
 public:
     using Element::Blur;
     using Element::Focus;
     explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo);
 
     static nsresult
     Create(nsXULPrototypeElement* aPrototype, nsIDocument* aDocument,
@@ -383,19 +382,16 @@ public:
 
     virtual bool IsNodeOfType(uint32_t aFlags) const override;
     virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
 
     virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
                                                 int32_t aModType) const override;
     NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
-    // nsIDOMXULElement
-    NS_DECL_NSIDOMXULELEMENT
-
     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                            bool aPreallocateChildren) const override;
     virtual mozilla::EventStates IntrinsicState() const override;
 
     void PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv);
 
     virtual void RecompileScriptEventListeners() override;
 
--- a/gfx/layers/LayerAttributes.h
+++ b/gfx/layers/LayerAttributes.h
@@ -1,202 +1,241 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 #ifndef mozilla_gfx_layers_LayerAttributes_h
 #define mozilla_gfx_layers_LayerAttributes_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/gfx/Types.h"
 #include "mozilla/layers/LayersTypes.h"
 
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 namespace mozilla {
 namespace layers {
 
-// Data stored for scroll thumb container layers.
-struct ScrollThumbData {
-  ScrollThumbData()
-    : mThumbRatio(0.0f)
-    , mIsAsyncDraggable(false)
-  {}
-  ScrollThumbData(ScrollDirection aDirection,
-                  float aThumbRatio,
-                  CSSCoord aThumbStart,
-                  CSSCoord aThumbLength,
-                  bool aIsAsyncDraggable,
-                  CSSCoord aScrollTrackStart,
-                  CSSCoord aScrollTrackLength)
+enum class ScrollbarLayerType : uint8_t { None, Thumb, Container };
+
+/**
+ *  It stores data for scroll thumb layer or container layers.
+ */
+struct ScrollbarData {
+  ScrollbarData() = default;
+
+  ScrollbarData(ScrollDirection aDirection,
+                ScrollbarLayerType aScrollbarLayerType,
+                float aThumbRatio,
+                CSSCoord aThumbStart,
+                CSSCoord aThumbLength,
+                bool aThumbIsAsyncDraggable,
+                CSSCoord aScrollTrackStart,
+                CSSCoord aScrollTrackLength)
     : mDirection(Some(aDirection))
+    , mScrollbarLayerType(aScrollbarLayerType)
     , mThumbRatio(aThumbRatio)
     , mThumbStart(aThumbStart)
     , mThumbLength(aThumbLength)
-    , mIsAsyncDraggable(aIsAsyncDraggable)
+    , mThumbIsAsyncDraggable(aThumbIsAsyncDraggable)
     , mScrollTrackStart(aScrollTrackStart)
     , mScrollTrackLength(aScrollTrackLength)
   {}
 
+  /**
+   * The mDirection contains a direction if mScrollbarLayerType is Thumb
+   * or Container, otherwise it's empty.
+   */
   Maybe<ScrollDirection> mDirection;
-  // The scrollbar thumb ratio is the ratio of the thumb position (in the CSS
-  // pixels of the scrollframe's parent's space) to the scroll position (in the
-  // CSS pixels of the scrollframe's space).
-  float mThumbRatio;
+
+  /**
+   * Indicate what kind of layer this data is for. All possibilities are defined in
+   * enum ScrollbarLayerType
+   */
+  ScrollbarLayerType mScrollbarLayerType = ScrollbarLayerType::None;
+
+  /**
+   * The scrollbar thumb ratio is the ratio of the thumb position (in the CSS
+   * pixels of the scrollframe's parent's space) to the scroll position (in the
+   * CSS pixels of the scrollframe's space).
+   */
+  float mThumbRatio = 0.0f;
+
   CSSCoord mThumbStart;
   CSSCoord mThumbLength;
-  // Whether the scrollbar thumb can be dragged asynchronously.
-  bool mIsAsyncDraggable;
+
+  /**
+   * Whether the scrollbar thumb can be dragged asynchronously.
+   */
+  bool mThumbIsAsyncDraggable = false;
+
   CSSCoord mScrollTrackStart;
   CSSCoord mScrollTrackLength;
+  uint64_t mTargetViewId = FrameMetrics::NULL_SCROLL_ID;
 
-  bool operator==(const ScrollThumbData& aOther) const {
+  bool operator==(const ScrollbarData& aOther) const {
     return mDirection == aOther.mDirection &&
+           mScrollbarLayerType == aOther.mScrollbarLayerType &&
            mThumbRatio == aOther.mThumbRatio &&
            mThumbStart == aOther.mThumbStart &&
            mThumbLength == aOther.mThumbLength &&
-           mIsAsyncDraggable == aOther.mIsAsyncDraggable &&
+           mThumbIsAsyncDraggable == aOther.mThumbIsAsyncDraggable &&
            mScrollTrackStart == aOther.mScrollTrackStart &&
-           mScrollTrackLength == aOther.mScrollTrackLength;
+           mScrollTrackLength == aOther.mScrollTrackLength &&
+           mTargetViewId == aOther.mTargetViewId;
   }
-  bool operator!=(const ScrollThumbData& aOther) const {
+  bool operator!=(const ScrollbarData& aOther) const {
     return !(*this == aOther);
   }
 };
 
-// Infrequently changing layer attributes that require no special
-// serialization work.
+/**
+ * Infrequently changing layer attributes that require no special
+ * serialization work.
+ */
 class SimpleLayerAttributes final
 {
   friend struct IPC::ParamTraits<mozilla::layers::SimpleLayerAttributes>;
 public:
   SimpleLayerAttributes()
    : mTransformIsPerspective(false),
      mPostXScale(1.0f),
      mPostYScale(1.0f),
      mContentFlags(0),
      mOpacity(1.0f),
      mIsFixedPosition(false),
-     mScrollbarTargetContainerId(FrameMetrics::NULL_SCROLL_ID),
      mMixBlendMode(gfx::CompositionOp::OP_OVER),
      mForceIsolatedGroup(false)
   {
   }
 
-  //
-  // Setters.
-  // All set methods return true if values changed, false otherwise.
-  //
+  /**
+   * Setters.
+   * All set methods return true if values changed, false otherwise.
+   */
 
   bool SetPostScale(float aXScale, float aYScale) {
     if (mPostXScale == aXScale && mPostYScale == aYScale) {
       return false;
     }
     mPostXScale = aXScale;
     mPostYScale = aYScale;
     return true;
   }
+
   bool SetContentFlags(uint32_t aFlags) {
     if (aFlags == mContentFlags) {
       return false;
     }
     mContentFlags = aFlags;
     return true;
   }
+
   bool SetOpacity(float aOpacity) {
     if (aOpacity == mOpacity) {
       return false;
     }
     mOpacity = aOpacity;
     return true;
   }
+
   bool SetIsFixedPosition(bool aFixedPosition) {
     if (mIsFixedPosition == aFixedPosition) {
       return false;
     }
     mIsFixedPosition = aFixedPosition;
     return true;
   }
-  bool SetScrollThumbData(FrameMetrics::ViewID aScrollId, const ScrollThumbData& aThumbData) {
-    if (mScrollbarTargetContainerId == aScrollId &&
-        mThumbData == aThumbData)
+
+  bool SetScrollbarData(const ScrollbarData& aScrollbarData) {
+    if (mScrollbarData == aScrollbarData)
     {
       return false;
     }
-    mScrollbarTargetContainerId = aScrollId;
-    mThumbData = aThumbData;
+    mScrollbarData = aScrollbarData;
     return true;
   }
-  bool SetScrollbarContainer(FrameMetrics::ViewID aScrollId,
+
+  bool SetScrollbarContainer(FrameMetrics::ViewID aTargetViewId,
                              ScrollDirection aDirection) {
-    if (mScrollbarContainerDirection &&
-        *mScrollbarContainerDirection == aDirection &&
-        mScrollbarTargetContainerId == aScrollId) {
+    if (mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container &&
+        mScrollbarData.mDirection &&
+        *mScrollbarData.mDirection == aDirection &&
+        mScrollbarData.mTargetViewId == aTargetViewId) {
       return false;
     }
-    mScrollbarContainerDirection = Some(aDirection);
-    mScrollbarTargetContainerId = aScrollId;
+    mScrollbarData.mDirection = Some(aDirection);
+    mScrollbarData.mTargetViewId = aTargetViewId;
+    mScrollbarData.mScrollbarLayerType = ScrollbarLayerType::Container;
     return true;
   }
+
   bool SetMixBlendMode(gfx::CompositionOp aMixBlendMode) {
     if (mMixBlendMode == aMixBlendMode) {
       return false;
     }
     mMixBlendMode = aMixBlendMode;
     return true;
   }
+
   bool SetForceIsolatedGroup(bool aForceIsolatedGroup) {
     if (mForceIsolatedGroup == aForceIsolatedGroup) {
       return false;
     }
     mForceIsolatedGroup = aForceIsolatedGroup;
     return true;
   }
+
   bool SetTransform(const gfx::Matrix4x4& aMatrix) {
     if (mTransform == aMatrix) {
       return false;
     }
     mTransform = aMatrix;
     return true;
   }
+
   bool SetTransformIsPerspective(bool aIsPerspective) {
     if (mTransformIsPerspective == aIsPerspective) {
       return false;
     }
     mTransformIsPerspective = aIsPerspective;
     return true;
   }
+
   bool SetScrolledClip(const Maybe<LayerClip>& aScrolledClip) {
     if (mScrolledClip == aScrolledClip) {
       return false;
     }
     mScrolledClip = aScrolledClip;
     return true;
   }
-  bool SetFixedPositionData(FrameMetrics::ViewID aScrollId,
+
+  bool SetFixedPositionData(FrameMetrics::ViewID aTargetViewId,
                             const LayerPoint& aAnchor,
                             int32_t aSides)
   {
     if (mFixedPositionData &&
-        mFixedPositionData->mScrollId == aScrollId &&
+        mFixedPositionData->mScrollId == aTargetViewId &&
         mFixedPositionData->mAnchor == aAnchor &&
         mFixedPositionData->mSides == aSides) {
       return false;
     }
     if (!mFixedPositionData) {
       mFixedPositionData.emplace();
     }
-    mFixedPositionData->mScrollId = aScrollId;
+    mFixedPositionData->mScrollId = aTargetViewId;
     mFixedPositionData->mAnchor = aAnchor;
     mFixedPositionData->mSides = aSides;
     return true;
   }
+
   bool SetStickyPositionData(FrameMetrics::ViewID aScrollId,
                              LayerRectAbsolute aOuter, LayerRectAbsolute aInner)
   {
     if (mStickyPositionData &&
         mStickyPositionData->mOuter.IsEqualEdges(aOuter) &&
         mStickyPositionData->mInner.IsEqualEdges(aInner)) {
       return false;
     }
@@ -204,132 +243,145 @@ public:
       mStickyPositionData.emplace();
     }
     mStickyPositionData->mScrollId = aScrollId;
     mStickyPositionData->mOuter = aOuter;
     mStickyPositionData->mInner = aInner;
     return true;
   }
 
-  // This returns true if scrolling info is equivalent for the purposes of
-  // APZ hit testing.
+  /**
+   * This returns true if scrolling info is equivalent for the purposes of
+   * APZ hit testing.
+   */
   bool HitTestingInfoIsEqual(const SimpleLayerAttributes& aOther) const {
-    if (mScrollbarContainerDirection != aOther.mScrollbarContainerDirection) {
+    if (mScrollbarData != aOther.mScrollbarData) {
       return false;
     }
-    if (mScrollbarTargetContainerId != aOther.mScrollbarTargetContainerId) {
-      return false;
-    }
-    if (mThumbData != aOther.mThumbData) {
-      return false;
-    }
-    if (FixedPositionScrollContainerId() != aOther.FixedPositionScrollContainerId()) {
+    if (GetFixedPositionScrollContainerId() != aOther.GetFixedPositionScrollContainerId()) {
       return false;
     }
     if (mTransform != aOther.mTransform) {
       return false;
     }
     return true;
   }
 
-  //
-  // Getters.
-  //
+  /**
+   * Getters.
+   */
 
-  float PostXScale() const {
+  float GetPostXScale() const {
     return mPostXScale;
   }
-  float PostYScale() const {
+
+  float GetPostYScale() const {
     return mPostYScale;
   }
-  uint32_t ContentFlags() const {
+
+  uint32_t GetContentFlags() const {
     return mContentFlags;
   }
-  float Opacity() const {
+
+  float GetOpacity() const {
     return mOpacity;
   }
+
   bool IsFixedPosition() const {
     return mIsFixedPosition;
   }
-  FrameMetrics::ViewID ScrollbarTargetContainerId() const {
-    return mScrollbarTargetContainerId;
+
+  FrameMetrics::ViewID GetScrollbarTargetViewId() const {
+    return mScrollbarData.mTargetViewId;
+  }
+
+  const ScrollbarData& GetScrollbarData() const {
+    return mScrollbarData;
   }
-  const ScrollThumbData& ThumbData() const {
-    return mThumbData;
-  }
+
   Maybe<ScrollDirection> GetScrollbarContainerDirection() const {
-    return mScrollbarContainerDirection;
+    return (mScrollbarData.mScrollbarLayerType == ScrollbarLayerType::Container)
+      ? mScrollbarData.mDirection
+      : Nothing();
   }
-  gfx::CompositionOp MixBlendMode() const {
+
+  gfx::CompositionOp GetMixBlendMode() const {
     return mMixBlendMode;
   }
-  bool ForceIsolatedGroup() const {
+
+  bool GetForceIsolatedGroup() const {
     return mForceIsolatedGroup;
   }
-  const gfx::Matrix4x4& Transform() const {
+
+  const gfx::Matrix4x4& GetTransform() const {
     return mTransform;
   }
-  bool TransformIsPerspective() const {
+
+  bool GetTransformIsPerspective() const {
     return mTransformIsPerspective;
   }
-  const Maybe<LayerClip>& ScrolledClip() const {
+
+  const Maybe<LayerClip>& GetScrolledClip() const {
     return mScrolledClip;
   }
-  FrameMetrics::ViewID FixedPositionScrollContainerId() const {
+
+  FrameMetrics::ViewID GetFixedPositionScrollContainerId() const {
     return mFixedPositionData
            ? mFixedPositionData->mScrollId
            : FrameMetrics::NULL_SCROLL_ID;
   }
-  LayerPoint FixedPositionAnchor() const {
+
+  LayerPoint GetFixedPositionAnchor() const {
     return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint();
   }
-  int32_t FixedPositionSides() const {
+
+  int32_t GetFixedPositionSides() const {
     return mFixedPositionData ? mFixedPositionData->mSides : eSideBitsNone;
   }
+
   bool IsStickyPosition() const {
     return !!mStickyPositionData;
   }
-  FrameMetrics::ViewID StickyScrollContainerId() const {
+
+  FrameMetrics::ViewID GetStickyScrollContainerId() const {
     return mStickyPositionData->mScrollId;
   }
-  const LayerRectAbsolute& StickyScrollRangeOuter() const {
+
+  const LayerRectAbsolute& GetStickyScrollRangeOuter() const {
     return mStickyPositionData->mOuter;
   }
-  const LayerRectAbsolute& StickyScrollRangeInner() const {
+
+  const LayerRectAbsolute& GetStickyScrollRangeInner() const {
     return mStickyPositionData->mInner;
   }
 
   bool operator ==(const SimpleLayerAttributes& aOther) const {
     return mTransform == aOther.mTransform &&
            mTransformIsPerspective == aOther.mTransformIsPerspective &&
            mScrolledClip == aOther.mScrolledClip &&
            mPostXScale == aOther.mPostXScale &&
            mPostYScale == aOther.mPostYScale &&
            mContentFlags == aOther.mContentFlags &&
            mOpacity == aOther.mOpacity &&
            mIsFixedPosition == aOther.mIsFixedPosition &&
-           mScrollbarTargetContainerId == aOther.mScrollbarTargetContainerId &&
-           mThumbData == aOther.mThumbData &&
-           mScrollbarContainerDirection == aOther.mScrollbarContainerDirection &&
+           mScrollbarData == aOther.mScrollbarData &&
            mMixBlendMode == aOther.mMixBlendMode &&
            mForceIsolatedGroup == aOther.mForceIsolatedGroup;
   }
 
 private:
   gfx::Matrix4x4 mTransform;
   bool mTransformIsPerspective;
   Maybe<LayerClip> mScrolledClip;
   float mPostXScale;
   float mPostYScale;
   uint32_t mContentFlags;
   float mOpacity;
   bool mIsFixedPosition;
-  uint64_t mScrollbarTargetContainerId;
-  ScrollThumbData mThumbData;
-  Maybe<ScrollDirection> mScrollbarContainerDirection;
+  ScrollbarData mScrollbarData;
   gfx::CompositionOp mMixBlendMode;
   bool mForceIsolatedGroup;
 
   struct FixedPositionData {
     FrameMetrics::ViewID mScrollId;
     LayerPoint mAnchor;
     int32_t mSides;
   };
@@ -337,16 +389,18 @@ private:
 
   struct StickyPositionData {
     FrameMetrics::ViewID mScrollId;
     LayerRectAbsolute mOuter;
     LayerRectAbsolute mInner;
   };
   Maybe<StickyPositionData> mStickyPositionData;
 
-  // This class may only contain plain-old-data members that can be safely
-  // copied over IPC. Make sure to add new members to operator ==.
+  /**
+   * This class may only contain plain-old-data members that can be safely
+   * copied over IPC. Make sure to add new members to operator ==.
+   */
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_gfx_layers_LayerAttributes_h
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -405,37 +405,37 @@ public:
     MOZ_ASSERT(IsValid());
 
     if (mLayer->AsRefLayer()) {
       return mLayer->AsRefLayer()->GetEventRegionsOverride();
     }
     return EventRegionsOverride::NoOverride;
   }
 
-  const ScrollThumbData& GetScrollThumbData() const
+  const ScrollbarData& GetScrollbarData() const
   {
     MOZ_ASSERT(IsValid());
 
-    return mLayer->GetScrollThumbData();
+    return mLayer->GetScrollbarData();
   }
 
   uint64_t GetScrollbarAnimationId() const
   {
     MOZ_ASSERT(IsValid());
     // This function is only really needed for template-compatibility with
     // WebRenderScrollDataWrapper. Although it will be called, the return
     // value is not used.
     return 0;
   }
 
   FrameMetrics::ViewID GetScrollbarTargetContainerId() const
   {
     MOZ_ASSERT(IsValid());
 
-    return mLayer->GetScrollbarTargetContainerId();
+    return mLayer->GetScrollbarTargetViewId();
   }
 
   Maybe<ScrollDirection> GetScrollbarContainerDirection() const
   {
     MOZ_ASSERT(IsValid());
     return mLayer->GetScrollbarContainerDirection();
   }
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -544,17 +544,17 @@ Layer::CalculateScissorRect(const Render
     scissor.MoveBy(-container->GetIntermediateSurfaceRect().TopLeft());
   }
   return currentClip.Intersect(scissor);
 }
 
 Maybe<ParentLayerIntRect>
 Layer::GetScrolledClipRect() const
 {
-  const Maybe<LayerClip> clip = mSimpleAttrs.ScrolledClip();
+  const Maybe<LayerClip> clip = mSimpleAttrs.GetScrolledClip();
   return clip ? Some(clip->GetClipRect()) : Nothing();
 }
 
 const ScrollMetadata&
 Layer::GetScrollMetadata(uint32_t aIndex) const
 {
   MOZ_ASSERT(aIndex < GetScrollMetadataCount());
   return mScrollMetadata[aIndex];
@@ -584,17 +584,17 @@ Layer::IsScrollableWithoutContent() cons
   return AsContainerLayer()
       && HasScrollableFrameMetrics()
       && !GetFirstChild();
 }
 
 Matrix4x4
 Layer::GetTransform() const
 {
-  Matrix4x4 transform = mSimpleAttrs.Transform();
+  Matrix4x4 transform = mSimpleAttrs.GetTransform();
   transform.PostScale(GetPostXScale(), GetPostYScale(), 1.0f);
   if (const ContainerLayer* c = AsContainerLayer()) {
     transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
   }
   return transform;
 }
 
 const CSSTransformMatrix
@@ -628,17 +628,17 @@ bool
 Layer::HasTransformAnimation() const
 {
   return mAnimationInfo.HasTransformAnimation();
 }
 
 void
 Layer::ApplyPendingUpdatesForThisTransaction()
 {
-  if (mPendingTransform && *mPendingTransform != mSimpleAttrs.Transform()) {
+  if (mPendingTransform && *mPendingTransform != mSimpleAttrs.GetTransform()) {
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
     mSimpleAttrs.SetTransform(*mPendingTransform);
     MutatedSimple();
   }
   mPendingTransform = nullptr;
 
   if (mAnimationInfo.ApplyPendingUpdatesForThisTransaction()) {
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this));
@@ -653,17 +653,17 @@ Layer::ApplyPendingUpdatesForThisTransac
       Mutated();
     }
   }
 }
 
 float
 Layer::GetLocalOpacity()
 {
-  float opacity = mSimpleAttrs.Opacity();
+  float opacity = mSimpleAttrs.GetOpacity();
   if (HostLayer* shadow = AsHostLayer())
     opacity = shadow->GetShadowOpacity();
   return std::min(std::max(opacity, 0.0f), 1.0f);
 }
 
 float
 Layer::GetEffectiveOpacity()
 {
@@ -673,25 +673,25 @@ Layer::GetEffectiveOpacity()
     opacity *= c->GetLocalOpacity();
   }
   return opacity;
 }
 
 CompositionOp
 Layer::GetEffectiveMixBlendMode()
 {
-  if (mSimpleAttrs.MixBlendMode() != CompositionOp::OP_OVER)
-    return mSimpleAttrs.MixBlendMode();
+  if (mSimpleAttrs.GetMixBlendMode() != CompositionOp::OP_OVER)
+    return mSimpleAttrs.GetMixBlendMode();
   for (ContainerLayer* c = GetParent(); c && !c->UseIntermediateSurface();
     c = c->GetParent()) {
-    if(c->mSimpleAttrs.MixBlendMode() != CompositionOp::OP_OVER)
-      return c->mSimpleAttrs.MixBlendMode();
+    if(c->mSimpleAttrs.GetMixBlendMode() != CompositionOp::OP_OVER)
+      return c->mSimpleAttrs.GetMixBlendMode();
   }
 
-  return mSimpleAttrs.MixBlendMode();
+  return mSimpleAttrs.GetMixBlendMode();
 }
 
 Matrix4x4
 Layer::ComputeTransformToPreserve3DRoot()
 {
   Matrix4x4 transform = GetLocalTransform();
   for (Layer* layer = GetParent();
        layer && layer->Extend3DContext();
@@ -1761,24 +1761,24 @@ Layer::PrintInfo(std::stringstream& aStr
   aStream << aPrefix;
   aStream << nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this).get();
 
   layers::PrintInfo(aStream, AsHostLayer());
 
   if (mClipRect) {
     AppendToString(aStream, *mClipRect, " [clip=", "]");
   }
-  if (mSimpleAttrs.ScrolledClip()) {
-    AppendToString(aStream, mSimpleAttrs.ScrolledClip()->GetClipRect(), " [scrolled-clip=", "]");
-    if (const Maybe<size_t>& ix = mSimpleAttrs.ScrolledClip()->GetMaskLayerIndex()) {
+  if (mSimpleAttrs.GetScrolledClip()) {
+    AppendToString(aStream, mSimpleAttrs.GetScrolledClip()->GetClipRect(), " [scrolled-clip=", "]");
+    if (const Maybe<size_t>& ix = mSimpleAttrs.GetScrolledClip()->GetMaskLayerIndex()) {
       AppendToString(aStream, ix.value(), " [scrolled-mask=", "]");
     }
   }
-  if (1.0 != mSimpleAttrs.PostXScale() || 1.0 != mSimpleAttrs.PostYScale()) {
-    aStream << nsPrintfCString(" [postScale=%g, %g]", mSimpleAttrs.PostXScale(), mSimpleAttrs.PostYScale()).get();
+  if (1.0 != mSimpleAttrs.GetPostXScale() || 1.0 != mSimpleAttrs.GetPostYScale()) {
+    aStream << nsPrintfCString(" [postScale=%g, %g]", mSimpleAttrs.GetPostXScale(), mSimpleAttrs.GetPostYScale()).get();
   }
   if (!GetBaseTransform().IsIdentity()) {
     AppendToString(aStream, GetBaseTransform(), " [transform=", "]");
   }
   if (!GetEffectiveTransform().IsIdentity()) {
     AppendToString(aStream, GetEffectiveTransform(), " [effective-transform=", "]");
   }
   if (GetTransformIsPerspective()) {
@@ -1811,22 +1811,22 @@ Layer::PrintInfo(std::stringstream& aStr
     aStream << " [combines3DTransformWithAncestors]";
   }
   if (Is3DContextLeaf()) {
     aStream << " [is3DContextLeaf]";
   }
   if (GetScrollbarContainerDirection().isSome()) {
     aStream << " [scrollbar]";
   }
-  if (Maybe<ScrollDirection> thumbDirection = GetScrollThumbData().mDirection) {
+  if (Maybe<ScrollDirection> thumbDirection = GetScrollbarData().mDirection) {
     if (*thumbDirection == ScrollDirection::eVertical) {
-      aStream << nsPrintfCString(" [vscrollbar=%" PRIu64 "]", GetScrollbarTargetContainerId()).get();
+      aStream << nsPrintfCString(" [vscrollbar=%" PRIu64 "]", GetScrollbarTargetViewId()).get();
     }
     if (*thumbDirection == ScrollDirection::eHorizontal) {
-      aStream << nsPrintfCString(" [hscrollbar=%" PRIu64 "]", GetScrollbarTargetContainerId()).get();
+      aStream << nsPrintfCString(" [hscrollbar=%" PRIu64 "]", GetScrollbarTargetViewId()).get();
     }
   }
   if (GetIsFixedPosition()) {
     LayerPoint anchor = GetFixedPositionAnchor();
     aStream << nsPrintfCString(" [isFixedPosition scrollId=%" PRIu64 " sides=0x%x anchor=%s]",
                      GetFixedPositionScrollContainerId(),
                      GetFixedPositionSides(),
                      ToString(anchor).c_str()).get();
@@ -1962,21 +1962,21 @@ Layer::DumpPacket(layerscope::LayersPack
   }
   // Opacity
   layer->set_opacity(GetOpacity());
   // Content opaque
   layer->set_copaque(static_cast<bool>(GetContentFlags() & CONTENT_OPAQUE));
   // Component alpha
   layer->set_calpha(static_cast<bool>(GetContentFlags() & CONTENT_COMPONENT_ALPHA));
   // Vertical or horizontal bar
-  if (Maybe<ScrollDirection> thumbDirection = GetScrollThumbData().mDirection) {
-    layer->set_direct(*thumbDirection == ScrollDirection::eVertical ?
+  if (GetScrollbarData().mScrollbarLayerType == layers::ScrollbarLayerType::Thumb) {
+    layer->set_direct(*GetScrollbarData().mDirection == ScrollDirection::eVertical ?
                       LayersPacket::Layer::VERTICAL :
                       LayersPacket::Layer::HORIZONTAL);
-    layer->set_barid(GetScrollbarTargetContainerId());
+    layer->set_barid(GetScrollbarTargetViewId());
   }
 
   // Mask layer
   if (mMaskLayer) {
     layer->set_mask(reinterpret_cast<uint64_t>(mMaskLayer.get()));
   }
 
   // DisplayList log.
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1020,17 +1020,17 @@ public:
     if (mSimpleAttrs.SetForceIsolatedGroup(aForceIsolatedGroup)) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this));
       MutatedSimple();
     }
   }
 
   bool GetForceIsolatedGroup() const
   {
-    return mSimpleAttrs.ForceIsolatedGroup();
+    return mSimpleAttrs.GetForceIsolatedGroup();
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set a clip rect which will be applied to this layer as it is
    * composited to the destination. The coordinates are relative to
    * the parent layer (i.e. the contents of this layer
    * are transformed before this clip rect is applied).
@@ -1258,19 +1258,19 @@ public:
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    * If a layer is a scroll thumb container layer, set the scroll identifier
    * of the scroll frame scrolled by the thumb, and other data related to the
    * thumb.
    */
-  void SetScrollThumbData(FrameMetrics::ViewID aScrollId, const ScrollThumbData& aThumbData)
+  void SetScrollbarData(const ScrollbarData& aThumbData)
   {
-    if (mSimpleAttrs.SetScrollThumbData(aScrollId, aThumbData)) {
+    if (mSimpleAttrs.SetScrollbarData(aThumbData)) {
       MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this));
       MutatedSimple();
     }
   }
 
   // Set during construction for the container layer of scrollbar components.
   // |aScrollId| holds the scroll identifier of the scrollable content that
   // the scrollbar is for.
@@ -1286,22 +1286,22 @@ public:
   void SetSimpleAttributes(const SimpleLayerAttributes& aAttrs) {
     mSimpleAttrs = aAttrs;
   }
   const SimpleLayerAttributes& GetSimpleAttributes() const {
     return mSimpleAttrs;
   }
 
   // These getters can be used anytime.
-  float GetOpacity() { return mSimpleAttrs.Opacity(); }
-  gfx::CompositionOp GetMixBlendMode() const { return mSimpleAttrs.MixBlendMode(); }
+  float GetOpacity() { return mSimpleAttrs.GetOpacity(); }
+  gfx::CompositionOp GetMixBlendMode() const { return mSimpleAttrs.GetMixBlendMode(); }
   const Maybe<ParentLayerIntRect>& GetClipRect() const { return mClipRect; }
-  const Maybe<LayerClip>& GetScrolledClip() const { return mSimpleAttrs.ScrolledClip(); }
+  const Maybe<LayerClip>& GetScrolledClip() const { return mSimpleAttrs.GetScrolledClip(); }
   Maybe<ParentLayerIntRect> GetScrolledClipRect() const;
-  uint32_t GetContentFlags() { return mSimpleAttrs.ContentFlags(); }
+  uint32_t GetContentFlags() { return mSimpleAttrs.GetContentFlags(); }
   const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
   const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const;
   const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const;
   uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); }
   const nsTArray<ScrollMetadata>& GetAllScrollMetadata() { return mScrollMetadata; }
   bool HasScrollableFrameMetrics() const;
   bool IsScrollableWithoutContent() const;
   const EventRegions& GetEventRegions() const { return mEventRegions; }
@@ -1321,31 +1321,31 @@ public:
   Layer* GetPrevSibling() { return mPrevSibling; }
   const Layer* GetPrevSibling() const { return mPrevSibling; }
   virtual Layer* GetFirstChild() const { return nullptr; }
   virtual Layer* GetLastChild() const { return nullptr; }
   gfx::Matrix4x4 GetTransform() const;
   // Same as GetTransform(), but returns the transform as a strongly-typed
   // matrix. Eventually this will replace GetTransform().
   const CSSTransformMatrix GetTransformTyped() const;
-  const gfx::Matrix4x4& GetBaseTransform() const { return mSimpleAttrs.Transform(); }
+  const gfx::Matrix4x4& GetBaseTransform() const { return mSimpleAttrs.GetTransform(); }
   // Note: these are virtual because ContainerLayerComposite overrides them.
-  virtual float GetPostXScale() const { return mSimpleAttrs.PostXScale(); }
-  virtual float GetPostYScale() const { return mSimpleAttrs.PostYScale(); }
+  virtual float GetPostXScale() const { return mSimpleAttrs.GetPostXScale(); }
+  virtual float GetPostYScale() const { return mSimpleAttrs.GetPostYScale(); }
   bool GetIsFixedPosition() { return mSimpleAttrs.IsFixedPosition(); }
-  bool GetTransformIsPerspective() const { return mSimpleAttrs.TransformIsPerspective(); }
+  bool GetTransformIsPerspective() const { return mSimpleAttrs.GetTransformIsPerspective(); }
   bool GetIsStickyPosition() { return mSimpleAttrs.IsStickyPosition(); }
-  FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mSimpleAttrs.FixedPositionScrollContainerId(); }
-  LayerPoint GetFixedPositionAnchor() { return mSimpleAttrs.FixedPositionAnchor(); }
-  int32_t GetFixedPositionSides() { return mSimpleAttrs.FixedPositionSides(); }
-  FrameMetrics::ViewID GetStickyScrollContainerId() { return mSimpleAttrs.StickyScrollContainerId(); }
-  const LayerRectAbsolute& GetStickyScrollRangeOuter() { return mSimpleAttrs.StickyScrollRangeOuter(); }
-  const LayerRectAbsolute& GetStickyScrollRangeInner() { return mSimpleAttrs.StickyScrollRangeInner(); }
-  FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mSimpleAttrs.ScrollbarTargetContainerId(); }
-  const ScrollThumbData& GetScrollThumbData() const { return mSimpleAttrs.ThumbData(); }
+  FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mSimpleAttrs.GetFixedPositionScrollContainerId(); }
+  LayerPoint GetFixedPositionAnchor() { return mSimpleAttrs.GetFixedPositionAnchor(); }
+  int32_t GetFixedPositionSides() { return mSimpleAttrs.GetFixedPositionSides(); }
+  FrameMetrics::ViewID GetStickyScrollContainerId() { return mSimpleAttrs.GetStickyScrollContainerId(); }
+  const LayerRectAbsolute& GetStickyScrollRangeOuter() { return mSimpleAttrs.GetStickyScrollRangeOuter(); }
+  const LayerRectAbsolute& GetStickyScrollRangeInner() { return mSimpleAttrs.GetStickyScrollRangeInner(); }
+  FrameMetrics::ViewID GetScrollbarTargetViewId() { return mSimpleAttrs.GetScrollbarTargetViewId(); }
+  const ScrollbarData& GetScrollbarData() const { return mSimpleAttrs.GetScrollbarData(); }
   bool IsScrollbarContainer() { return mSimpleAttrs.GetScrollbarContainerDirection().isSome(); }
   Maybe<ScrollDirection> GetScrollbarContainerDirection() { return mSimpleAttrs.GetScrollbarContainerDirection(); }
   Layer* GetMaskLayer() const { return mMaskLayer; }
   bool HasPendingTransform() const { return mPendingTransform; }
 
   void CheckCanary() const { mCanary.Check(); }
 
   // Ancestor mask layers are associated with FrameMetrics, but for simplicity
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -19,17 +19,17 @@ namespace wr {
 class TransactionBuilder;
 struct WrTransformProperty;
 } // namespace wr
 
 namespace layers {
 
 class APZCTreeManager;
 class LayerMetricsWrapper;
-struct ScrollThumbData;
+struct ScrollbarData;
 
 /**
  * This interface exposes APZ methods related to "sampling" (i.e. reading the
  * async transforms produced by APZ). These methods should all be called on
  * the sampler thread.
  */
 class APZSampler {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZSampler)
@@ -53,17 +53,17 @@ public:
    * |aContent| instead of |aApzc| and |aMetrics|; aContent is the
    * LayerMetricsWrapper corresponding to the scroll frame that is scrolled by
    * the scroll thumb, and so the APZC and metrics can be obtained from
    * |aContent|.
    */
   LayerToParentLayerMatrix4x4 ComputeTransformForScrollThumb(
       const LayerToParentLayerMatrix4x4& aCurrentTransform,
       const LayerMetricsWrapper& aContent,
-      const ScrollThumbData& aThumbData,
+      const ScrollbarData& aThumbData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   ParentLayerPoint GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer);
   AsyncTransform GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetOverscrollTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aLayer);
 
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -629,17 +629,17 @@ APZCTreeManager::PushStateToWR(wr::Trans
         MOZ_ASSERT(scrollTargetApzc);
         LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
             [&](const FrameMetrics& aMetrics) {
                 return ComputeTransformForScrollThumb(
                     aNode->GetTransform() * AsyncTransformMatrix(),
                     scrollTargetNode->GetTransform().ToUnknownMatrix(),
                     scrollTargetApzc,
                     aMetrics,
-                    aNode->GetScrollThumbData(),
+                    aNode->GetScrollbarData(),
                     scrollTargetNode->IsAncestorOf(aNode),
                     nullptr);
             });
         aTransformArray.AppendElement(wr::ToWrTransformProperty(
             aNode->GetScrollbarAnimationId(),
             transform));
       });
 
@@ -860,18 +860,17 @@ APZCTreeManager::PrepareNodeForLayer(con
         aLayer.GetTransformTyped(),
         (!parentHasPerspective && aLayer.GetClipRect())
           ? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
           : Nothing(),
         GetEventRegionsOverride(aParent, aLayer),
         aLayer.IsBackfaceHidden());
     node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                            aLayer.GetScrollbarAnimationId(),
-                           aLayer.GetScrollThumbData(),
-                           aLayer.GetScrollbarContainerDirection());
+                           aLayer.GetScrollbarData());
     node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
     return node;
   }
 
   AsyncPanZoomController* apzc = nullptr;
   // If we get here, aLayer is a scrollable layer and somebody
   // has registered a GeckoContentController for it, so we need to ensure
   // it has an APZC instance to manage its scrolling.
@@ -1081,18 +1080,17 @@ APZCTreeManager::PrepareNodeForLayer(con
         aLayer.IsBackfaceHidden());
   }
 
   // Note: if layer properties must be propagated to nodes, RecvUpdate in
   // LayerTransactionParent.cpp must ensure that APZ will be notified
   // when those properties change.
   node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                          aLayer.GetScrollbarAnimationId(),
-                         aLayer.GetScrollThumbData(),
-                         aLayer.GetScrollbarContainerDirection());
+                         aLayer.GetScrollbarData());
   node->SetFixedPosData(aLayer.GetFixedPositionScrollContainerId());
   return node;
 }
 
 template<typename PanGestureOrScrollWheelInput>
 static bool
 WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput)
 {
@@ -1208,17 +1206,17 @@ APZCTreeManager::ReceiveInputEvent(Input
           confFlags.mTargetConfirmed = false;
         }
         result = mInputQueue->ReceiveInputEvent(
           apzc, confFlags, mouseInput, aOutInputBlockId);
 
         // If we're starting an async scrollbar drag
         if (apzDragEnabled && startsDrag && hitScrollbarNode &&
             hitScrollbarNode->IsScrollThumbNode() &&
-            hitScrollbarNode->GetScrollThumbData().mIsAsyncDraggable) {
+            hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable) {
           SetupScrollbarDrag(mouseInput, hitScrollbarNode.get(), apzc.get());
         }
 
         if (result == nsEventStatus_eConsumeDoDefault) {
           // This input event is part of a drag block, so whether or not it is
           // directed at a scrollbar depends on whether the drag block started
           // on a scrollbar.
           hitScrollbar = mInputQueue->IsDragOnScrollbar(hitScrollbar);
@@ -1588,17 +1586,17 @@ APZCTreeManager::ProcessTouchInput(Multi
         &mHitResultForInputBlock, &hitScrollbarNode);
 
     // Check if this event starts a scrollbar touch-drag. The conditions
     // checked are similar to the ones we check for MOUSE_INPUT starting
     // a scrollbar mouse-drag.
     mInScrollbarTouchDrag = gfxPrefs::APZDragEnabled() &&
                             gfxPrefs::APZTouchDragEnabled() && hitScrollbarNode &&
                             hitScrollbarNode->IsScrollThumbNode() &&
-                            hitScrollbarNode->GetScrollThumbData().mIsAsyncDraggable;
+                            hitScrollbarNode->GetScrollbarData().mThumbIsAsyncDraggable;
 
     MOZ_ASSERT(touchBehaviors.Length() == aInput.mTouches.Length());
     for (size_t i = 0; i < touchBehaviors.Length(); i++) {
       APZCTM_LOG("Touch point has allowed behaviours 0x%02x\n", touchBehaviors[i]);
       if (touchBehaviors[i] == AllowedTouchBehavior::UNKNOWN) {
         // If there's any unknown items in the list, throw it out and we'll
         // wait for the main thread to send us a notification.
         touchBehaviors.Clear();
@@ -1763,17 +1761,17 @@ APZCTreeManager::SetupScrollbarDrag(Mous
                                     const HitTestingTreeNode* aScrollThumbNode,
                                     AsyncPanZoomController* aApzc)
 {
   DragBlockState* dragBlock = mInputQueue->GetCurrentDragBlock();
   if (!dragBlock) {
     return;
   }
 
-  const ScrollThumbData& thumbData = aScrollThumbNode->GetScrollThumbData();
+  const ScrollbarData& thumbData = aScrollThumbNode->GetScrollbarData();
   MOZ_ASSERT(thumbData.mDirection.isSome());
 
   // Record the thumb's position at the start of the drag.
   // We snap back to this position if, during the drag, the mouse
   // gets sufficiently far away from the scrollbar.
   dragBlock->SetInitialThumbPos(thumbData.mThumbStart);
 
   // Under some conditions, we can confirm the drag block right away.
@@ -3001,17 +2999,17 @@ APZCTreeManager::ComputeTransformForNode
       MOZ_ASSERT(scrollTargetApzc);
       return scrollTargetApzc->CallWithLastContentPaintMetrics(
         [&](const FrameMetrics& aMetrics) {
           return ComputeTransformForScrollThumb(
               aNode->GetTransform() * AsyncTransformMatrix(),
               scrollTargetNode->GetTransform().ToUnknownMatrix(),
               scrollTargetApzc,
               aMetrics,
-              aNode->GetScrollThumbData(),
+              aNode->GetScrollbarData(),
               scrollTargetNode->IsAncestorOf(aNode),
               nullptr);
         });
     }
   }
   // Otherwise, the node does not have an async transform.
   return aNode->GetTransform() * AsyncTransformMatrix();
 }
@@ -3055,17 +3053,17 @@ APZCTreeManager::GetAPZTestData(LayersId
 }
 
 /*static*/ LayerToParentLayerMatrix4x4
 APZCTreeManager::ComputeTransformForScrollThumb(
     const LayerToParentLayerMatrix4x4& aCurrentTransform,
     const Matrix4x4& aScrollableContentTransform,
     AsyncPanZoomController* aApzc,
     const FrameMetrics& aMetrics,
-    const ScrollThumbData& aThumbData,
+    const ScrollbarData& aScrollbarData,
     bool aScrollbarIsDescendant,
     AsyncTransformComponentMatrix* aOutClipTransform)
 {
   // We only apply the transform if the scroll-target layer has non-container
   // children (i.e. when it has some possibly-visible content). This is to
   // avoid moving scroll-bars in the situation that only a scroll information
   // layer has been built for a scroll frame, as this would result in a
   // disparity between scrollbars and visible content.
@@ -3078,32 +3076,32 @@ APZCTreeManager::ComputeTransformForScro
   AsyncTransformComponentMatrix asyncTransform =
     aApzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
 
   // |asyncTransform| represents the amount by which we have scrolled and
   // zoomed since the last paint. Because the scrollbar was sized and positioned based
   // on the painted content, we need to adjust it based on asyncTransform so that
   // it reflects what the user is actually seeing now.
   AsyncTransformComponentMatrix scrollbarTransform;
-  if (*aThumbData.mDirection == ScrollDirection::eVertical) {
+  if (*aScrollbarData.mDirection == ScrollDirection::eVertical) {
     const ParentLayerCoord asyncScrollY = asyncTransform._42;
     const float asyncZoomY = asyncTransform._22;
 
     // The scroll thumb needs to be scaled in the direction of scrolling by the
     // inverse of the async zoom. This is because zooming in decreases the
     // fraction of the whole srollable rect that is in view.
     const float yScale = 1.f / asyncZoomY;
 
     // Note: |metrics.GetZoom()| doesn't yet include the async zoom.
     const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().yScale * asyncZoomY);
 
     // Here we convert the scrollbar thumb ratio into a true unitless ratio by
     // dividing out the conversion factor from the scrollframe's parent's space
     // to the scrollframe's space.
-    const float ratio = aThumbData.mThumbRatio /
+    const float ratio = aScrollbarData.mThumbRatio /
         (aMetrics.GetPresShellResolution() * asyncZoomY);
     // The scroll thumb needs to be translated in opposite direction of the
     // async scroll. This is because scrolling down, which translates the layer
     // content up, should result in moving the scroll thumb down.
     ParentLayerCoord yTranslation = -asyncScrollY * ratio;
 
     // The scroll thumb additionally needs to be translated to compensate for
     // the scale applied above. The origin with respect to which the scale is
@@ -3130,27 +3128,27 @@ APZCTreeManager::ComputeTransformForScro
       // resolution-cancelling transform which ensures the scroll thumb isn't
       // actually rendered at a larger scale.
       yTranslation *= aMetrics.GetPresShellResolution();
     }
 
     scrollbarTransform.PostScale(1.f, yScale, 1.f);
     scrollbarTransform.PostTranslate(0, yTranslation, 0);
   }
-  if (*aThumbData.mDirection == ScrollDirection::eHorizontal) {
+  if (*aScrollbarData.mDirection == ScrollDirection::eHorizontal) {
     // See detailed comments under the VERTICAL case.
 
     const ParentLayerCoord asyncScrollX = asyncTransform._41;
     const float asyncZoomX = asyncTransform._11;
 
     const float xScale = 1.f / asyncZoomX;
 
     const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().xScale * asyncZoomX);
 
-    const float ratio = aThumbData.mThumbRatio /
+    const float ratio = aScrollbarData.mThumbRatio /
         (aMetrics.GetPresShellResolution() * asyncZoomX);
     ParentLayerCoord xTranslation = -asyncScrollX * ratio;
 
     const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().x * ratio);
     const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
     const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
     const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
     xTranslation -= thumbOriginDeltaPL;
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -519,17 +519,17 @@ public:
    * @return The new shadow transform for the scroll thumb layer, including
    *    any pre- or post-scales.
    */
   static LayerToParentLayerMatrix4x4 ComputeTransformForScrollThumb(
       const LayerToParentLayerMatrix4x4& aCurrentTransform,
       const gfx::Matrix4x4& aScrollableContentTransform,
       AsyncPanZoomController* aApzc,
       const FrameMetrics& aMetrics,
-      const ScrollThumbData& aThumbData,
+      const ScrollbarData& aScrollbarData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -62,17 +62,17 @@ APZSampler::SampleAnimations(const Layer
   );
 
   return activeAnimations;
 }
 
 LayerToParentLayerMatrix4x4
 APZSampler::ComputeTransformForScrollThumb(const LayerToParentLayerMatrix4x4& aCurrentTransform,
                                            const LayerMetricsWrapper& aContent,
-                                           const ScrollThumbData& aThumbData,
+                                           const ScrollbarData& aThumbData,
                                            bool aScrollbarIsDescendant,
                                            AsyncTransformComponentMatrix* aOutClipTransform)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   return mApz->ComputeTransformForScrollThumb(aCurrentTransform,
                                               aContent.GetTransform(),
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -976,19 +976,20 @@ nsEventStatus AsyncPanZoomController::Ha
   if (aEvent.mType == MouseInput::MouseType::MOUSE_DOWN) {
     SetState(SCROLLBAR_DRAG);
   }
 
   if (aEvent.mType != MouseInput::MouseType::MOUSE_MOVE) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
-  const ScrollThumbData& thumbData = node->GetScrollThumbData();
-  MOZ_ASSERT(thumbData.mDirection.isSome());
-  ScrollDirection direction = *thumbData.mDirection;
+  const ScrollbarData& scrollbarData = node->GetScrollbarData();
+  MOZ_ASSERT(scrollbarData.mScrollbarLayerType == layers::ScrollbarLayerType::Thumb);
+  MOZ_ASSERT(scrollbarData.mDirection.isSome());
+  ScrollDirection direction = *scrollbarData.mDirection;
 
   bool isMouseAwayFromThumb = false;
   if (int snapMultiplier = gfxPrefs::SliderSnapMultiplier()) {
     // It's fine to ignore the async component of the thumb's transform,
     // because any async transform of the thumb will be in the direction of
     // scrolling, but here we're interested in the other direction.
     ParentLayerRect thumbRect =
         (node->GetTransform() * AsyncTransformMatrix()).TransformBounds(
@@ -1004,22 +1005,22 @@ nsEventStatus AsyncPanZoomController::Ha
     }
   }
 
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   CSSCoord thumbPosition;
   if (isMouseAwayFromThumb) {
     thumbPosition = aInitialThumbPos;
   } else {
-    thumbPosition = ConvertScrollbarPoint(aEvent.mLocalOrigin, thumbData) -
+    thumbPosition = ConvertScrollbarPoint(aEvent.mLocalOrigin, scrollbarData) -
                     aDragMetrics.mScrollbarDragOffset;
   }
 
-  CSSCoord maxThumbPos = thumbData.mScrollTrackLength;
-  maxThumbPos -= thumbData.mThumbLength;
+  CSSCoord maxThumbPos = scrollbarData.mScrollTrackLength;
+  maxThumbPos -= scrollbarData.mThumbLength;
 
   float scrollPercent = thumbPosition / maxThumbPos;
 
   CSSCoord minScrollPosition =
     GetAxisStart(direction, mFrameMetrics.GetScrollableRect().TopLeft());
   CSSCoord maxScrollPosition =
     GetAxisStart(direction, mFrameMetrics.GetScrollableRect().BottomRight()) -
     GetAxisLength(direction, mFrameMetrics.CalculateCompositedRectInCssPixels());
@@ -1699,17 +1700,17 @@ AsyncPanZoomController::ConvertToGecko(c
                 PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
     return true;
   }
   return false;
 }
 
 CSSCoord
 AsyncPanZoomController::ConvertScrollbarPoint(const ParentLayerPoint& aScrollbarPoint,
-                                              const ScrollThumbData& aThumbData) const
+                                              const ScrollbarData& aThumbData) const
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   // First, get it into the right coordinate space.
   CSSPoint scrollbarPoint = aScrollbarPoint / mFrameMetrics.GetZoom();
   // The scrollbar can be transformed with the frame but the pres shell
   // resolution is only applied to the scroll frame.
   scrollbarPoint = scrollbarPoint * mFrameMetrics.GetPresShellResolution();
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -498,17 +498,17 @@ public:
   bool CanScroll(ScrollDirection aDirection) const;
 
   /**
    * Convert a point on the scrollbar from this APZC's ParentLayer coordinates
    * to CSS coordinates relative to the beginning of the scroll track.
    * Only the component in the direction of scrolling is returned.
    */
   CSSCoord ConvertScrollbarPoint(const ParentLayerPoint& aScrollbarPoint,
-                                 const ScrollThumbData& aThumbData) const;
+                                 const ScrollbarData& aThumbData) const;
 
   void NotifyMozMouseScrollEvent(const nsString& aString) const;
 
   bool OverscrollBehaviorAllowsSwipe() const;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AsyncPanZoomController();
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -91,71 +91,67 @@ HitTestingTreeNode::SetLastChild(HitTest
       aChild->SetApzcParent(parent);
     }
   }
 }
 
 void
 HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
                                      const uint64_t& aScrollbarAnimationId,
-                                     const ScrollThumbData& aThumbData,
-                                     const Maybe<ScrollDirection>& aScrollContainerDirection)
+                                     const ScrollbarData& aScrollbarData)
 {
   mScrollViewId = aScrollViewId;
   mScrollbarAnimationId = aScrollbarAnimationId;
-  mScrollThumbData = aThumbData;
-  mScrollbarContainerDirection = aScrollContainerDirection;
+  mScrollbarData = aScrollbarData;
 }
 
 bool
 HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
 {
   return IsScrollThumbNode() &&
-         mScrollThumbData.mDirection == aDragMetrics.mDirection &&
+         mScrollbarData.mDirection == aDragMetrics.mDirection &&
          mScrollViewId == aDragMetrics.mViewId;
 }
 
 bool
 HitTestingTreeNode::IsScrollThumbNode() const
 {
-  return mScrollThumbData.mDirection.isSome();
+  return mScrollbarData.mScrollbarLayerType == layers::ScrollbarLayerType::Thumb;
 }
 
 bool
 HitTestingTreeNode::IsScrollbarNode() const
 {
-  return mScrollbarContainerDirection.isSome() || IsScrollThumbNode();
+  return mScrollbarData.mScrollbarLayerType != layers::ScrollbarLayerType::None;
 }
 
 ScrollDirection
 HitTestingTreeNode::GetScrollbarDirection() const
 {
   MOZ_ASSERT(IsScrollbarNode());
-  if (mScrollThumbData.mDirection.isSome()) {
-    return *(mScrollThumbData.mDirection);
-  }
-  return *mScrollbarContainerDirection;
+  MOZ_ASSERT(mScrollbarData.mDirection.isSome());
+  return *mScrollbarData.mDirection;
 }
 
 FrameMetrics::ViewID
 HitTestingTreeNode::GetScrollTargetId() const
 {
   return mScrollViewId;
 }
 
 const uint64_t&
 HitTestingTreeNode::GetScrollbarAnimationId() const
 {
   return mScrollbarAnimationId;
 }
 
-const ScrollThumbData&
-HitTestingTreeNode::GetScrollThumbData() const
+const ScrollbarData&
+HitTestingTreeNode::GetScrollbarData() const
 {
-  return mScrollThumbData;
+  return mScrollbarData;
 }
 
 void
 HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
 {
   mFixedPosTarget = aFixedPosTarget;
 }
 
@@ -386,17 +382,17 @@ HitTestingTreeNode::Dump(const char* aPr
   printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%sr=(%s) t=(%s) c=(%s)%s%s\n",
     aPrefix, this, mApzc.get(),
     mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=0x%" PRIx64, uint64_t(mLayersId)).get(),
     (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "",
     (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
     (mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() : "",
     Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
     mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none",
-    mScrollbarContainerDirection.isSome() ? " scrollbar" : "",
+    mScrollbarData.mDirection.isSome() ? " scrollbar" : "",
     IsScrollThumbNode() ? " scrollthumb" : "");
   if (mLastChild) {
     mLastChild->Dump(nsPrintfCString("%s  ", aPrefix).get());
   }
 }
 
 void
 HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent)
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -92,25 +92,24 @@ public:
                       const EventRegionsOverride& aOverride,
                       bool aIsBackfaceHidden);
   bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
 
   /* Scrollbar info */
 
   void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
                         const uint64_t& aScrollbarAnimationId,
-                        const ScrollThumbData& aThumbData,
-                        const Maybe<ScrollDirection>& aScrollContainerDirection);
+                        const ScrollbarData& aScrollbarData);
   bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
   bool IsScrollbarNode() const;  // Scroll thumb or scrollbar container layer.
   // This can only be called if IsScrollbarNode() is true
   ScrollDirection GetScrollbarDirection() const;
   bool IsScrollThumbNode() const;  // Scroll thumb container layer.
   FrameMetrics::ViewID GetScrollTargetId() const;
-  const ScrollThumbData& GetScrollThumbData() const;
+  const ScrollbarData& GetScrollbarData() const;
   const uint64_t& GetScrollbarAnimationId() const;
 
   /* Fixed pos info */
 
   void SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget);
   FrameMetrics::ViewID GetFixedPosTarget() const;
 
   /* Convert |aPoint| into the LayerPixel space for the layer corresponding to
@@ -145,21 +144,18 @@ private:
   // represents the scroll id of the scroll frame scrolled by the scrollbar.
   FrameMetrics::ViewID mScrollViewId;
 
   // This is only set to non-zero if WebRender is enabled, and only for HTTNs
   // where IsScrollThumbNode() returns true. It holds the animation id that we
   // use to move the thumb node to reflect async scrolling.
   uint64_t mScrollbarAnimationId;
 
-  // This is set for scroll thumb Container layers only.
-  ScrollThumbData mScrollThumbData;
-
-  // This is set for scroll track Container layers only.
-  Maybe<ScrollDirection> mScrollbarContainerDirection;
+  // This is set for scrollbar Container and Thumb layers.
+  ScrollbarData mScrollbarData;
 
   FrameMetrics::ViewID mFixedPosTarget;
 
   /* Let {L,M} be the {layer, scrollable metrics} pair that this node
    * corresponds to in the layer tree. mEventRegions contains the event regions
    * from L, in the case where event-regions are enabled. If event-regions are
    * disabled, it will contain the visible region of L, which we use as an
    * approximation to the hit region for the purposes of obscuring other layers.
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -71,17 +71,18 @@ var subtests = [
   // For the following tests, we want to make sure APZ doesn't wait for a content
   // response that is never going to arrive. To detect this we set the content response
   // timeout to a day, so that the entire test times out and fails if APZ does
   // end up waiting.
   {'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
                                                 ["apz.test.fails_with_native_injection", isWindows]]},
 
   {'file': 'helper_tap_default_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
-                                                        ["apz.test.fails_with_native_injection", isWindows]]},
+                                                        ["apz.test.fails_with_native_injection", isWindows],
+                                                        ["dom.event.default_to_passive_touch_listeners", true]]},
 
   // Simple test to exercise touch-action CSS property
   {'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
   // More complex touch-action tests, with overlapping regions and such
   {'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
   // Tests that touch-action CSS properties are handled in APZ without waiting
   // on the main-thread, when possible
   {'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -748,19 +748,19 @@ MoveScrollbarForLayerMargin(Layer* aRoot
                             const ScreenMargin& aFixedLayerMargins)
 {
   // See bug 1223928 comment 9 - once we can detect the RCD with just the
   // isRootContent flag on the metrics, we can probably move this code into
   // ApplyAsyncTransformToScrollbar rather than having it as a separate
   // adjustment on the layer tree.
   Layer* scrollbar = BreadthFirstSearch<ReverseIterator>(aRoot,
     [aRootScrollId](Layer* aNode) {
-      return (aNode->GetScrollThumbData().mDirection.isSome() &&
-              *aNode->GetScrollThumbData().mDirection == ScrollDirection::eHorizontal &&
-              aNode->GetScrollbarTargetContainerId() == aRootScrollId);
+      return (aNode->GetScrollbarData().mDirection.isSome() &&
+              *aNode->GetScrollbarData().mDirection == ScrollDirection::eHorizontal &&
+              aNode->GetScrollbarTargetViewId() == aRootScrollId);
     });
   if (scrollbar) {
     // Shift the horizontal scrollbar down into the new space exposed by the
     // dynamic toolbar hiding. Technically we should also scale the vertical
     // scrollbar a bit to expand into the new space but it's not as noticeable
     // and it would add a lot more complexity, so we're going with the "it's not
     // worth it" justification.
     TranslateShadowLayer(scrollbar, ParentLayerPoint(0, -aFixedLayerMargins.bottom), true, nullptr);
@@ -1029,33 +1029,33 @@ AsyncCompositionManager::ApplyAsyncConte
                 maskLayer->GetLocalTransformTyped() * combinedAsyncTransform);
           }
 
           appliedTransform = true;
         }
 
         ExpandRootClipRect(layer, fixedLayerMargins);
 
-        if (layer->GetScrollThumbData().mDirection.isSome()) {
+        if (layer->GetScrollbarData().mScrollbarLayerType == layers::ScrollbarLayerType::Thumb) {
           ApplyAsyncTransformToScrollbar(layer);
         }
       });
 
   return appliedTransform;
 }
 
 static bool
 LayerIsScrollbarTarget(const LayerMetricsWrapper& aTarget, Layer* aScrollbar)
 {
   if (!aTarget.GetApzc()) {
     return false;
   }
   const FrameMetrics& metrics = aTarget.Metrics();
   MOZ_ASSERT(metrics.IsScrollable());
-  if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetContainerId()) {
+  if (metrics.GetScrollId() != aScrollbar->GetScrollbarTargetViewId()) {
     return false;
   }
   return !metrics.IsScrollInfoLayer();
 }
 
 static void
 ApplyAsyncTransformToScrollbarForContent(const RefPtr<APZSampler>& aSampler,
                                          Layer* aScrollbar,
@@ -1064,17 +1064,17 @@ ApplyAsyncTransformToScrollbarForContent
 {
   AsyncTransformComponentMatrix clipTransform;
 
   MOZ_ASSERT(aSampler);
   LayerToParentLayerMatrix4x4 transform =
       aSampler->ComputeTransformForScrollThumb(
           aScrollbar->GetLocalTransformTyped(),
           aContent,
-          aScrollbar->GetScrollThumbData(),
+          aScrollbar->GetScrollbarData(),
           aScrollbarIsDescendant,
           &clipTransform);
 
   if (aScrollbarIsDescendant) {
     // We also need to make a corresponding change on the clip rect of all the
     // layers on the ancestor chain from the scrollbar layer up to but not
     // including the layer with the async transform. Otherwise the scrollbar
     // shifts but gets clipped and so appears to flicker.
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -103,25 +103,25 @@ public:
 
   // If the layer is marked as scale-to-resolution, add a post-scale
   // to the layer's transform equal to the pres shell resolution we're
   // scaling to. This cancels out the post scale of '1 / resolution'
   // added by Layout. TODO: It would be nice to get rid of both of these
   // post-scales.
   virtual float GetPostXScale() const override {
     if (mScaleToResolution) {
-      return mSimpleAttrs.PostXScale() * mPresShellResolution;
+      return mSimpleAttrs.GetPostXScale() * mPresShellResolution;
     }
-    return mSimpleAttrs.PostXScale();
+    return mSimpleAttrs.GetPostXScale();
   }
   virtual float GetPostYScale() const override {
     if (mScaleToResolution) {
-      return mSimpleAttrs.PostYScale() * mPresShellResolution;
+      return mSimpleAttrs.GetPostYScale() * mPresShellResolution;
     }
-    return mSimpleAttrs.PostYScale();
+    return mSimpleAttrs.GetPostYScale();
   }
 
   virtual const char* Name() const override { return "ContainerLayerComposite"; }
   UniquePtr<PreparedData> mPrepared;
 
   RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
 };
 
--- a/gfx/layers/wr/WebRenderScrollData.h
+++ b/gfx/layers/wr/WebRenderScrollData.h
@@ -72,18 +72,18 @@ public:
   EventRegions GetEventRegions() const { return mEventRegions; }
   void SetEventRegionsOverride(const EventRegionsOverride& aOverride) { mEventRegionsOverride = aOverride; }
   EventRegionsOverride GetEventRegionsOverride() const { return mEventRegionsOverride; }
 
   const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; }
   void SetReferentId(LayersId aReferentId) { mReferentId = Some(aReferentId); }
   Maybe<LayersId> GetReferentId() const { return mReferentId; }
 
-  void SetScrollThumbData(const ScrollThumbData& aData) { mScrollThumbData = aData; }
-  const ScrollThumbData& GetScrollThumbData() const { return mScrollThumbData; }
+  void SetScrollbarData(const ScrollbarData& aData) { mScrollbarData = aData; }
+  const ScrollbarData& GetScrollbarData() const { return mScrollbarData; }
   void SetScrollbarAnimationId(const uint64_t& aId) { mScrollbarAnimationId = aId; }
   const uint64_t& GetScrollbarAnimationId() const { return mScrollbarAnimationId; }
   void SetScrollbarTargetContainerId(FrameMetrics::ViewID aId) { mScrollbarTargetContainerId = aId; }
   FrameMetrics::ViewID GetScrollbarTargetContainerId() const { return mScrollbarTargetContainerId; }
   void SetScrollbarContainerDirection(ScrollDirection aDirection) { mScrollbarContainerDirection = Some(aDirection); }
   Maybe<ScrollDirection> GetScrollbarContainerDirection() const { return mScrollbarContainerDirection; }
 
   void SetFixedPositionScrollContainerId(FrameMetrics::ViewID aId) { mFixedPosScrollContainerId = aId; }
@@ -110,17 +110,17 @@ private:
   // over IPC, and use on the parent side in APZ.
 
   gfx::Matrix4x4 mTransform;
   bool mTransformIsPerspective;
   EventRegions mEventRegions;
   LayerIntRegion mVisibleRegion;
   Maybe<LayersId> mReferentId;
   EventRegionsOverride mEventRegionsOverride;
-  ScrollThumbData mScrollThumbData;
+  ScrollbarData mScrollbarData;
   uint64_t mScrollbarAnimationId;
   FrameMetrics::ViewID mScrollbarTargetContainerId;
   Maybe<ScrollDirection> mScrollbarContainerDirection;
   FrameMetrics::ViewID mFixedPosScrollContainerId;
 };
 
 // Data needed by APZ, for the whole layer tree. One instance of this class
 // is created for each transaction sent over PWebRenderBridge. It is populated
@@ -196,25 +196,25 @@ private:
   uint32_t mPaintSequenceNumber;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 namespace IPC {
 
-// When ScrollThumbData is stored on the layer tree, it's part of
+// When ScrollbarData is stored on the layer tree, it's part of
 // SimpleAttributes which itself uses PlainOldDataSerializer, so
-// we don't need a ParamTraits specialization for ScrollThumbData
-// separately. Here, however, ScrollThumbData is stored as part
+// we don't need a ParamTraits specialization for ScrollbarData
+// separately. Here, however, ScrollbarData is stored as part
 // of WebRenderLayerScrollData whose fields are serialized
 // individually, so we do.
 template<>
-struct ParamTraits<mozilla::layers::ScrollThumbData>
-  : public PlainOldDataSerializer<mozilla::layers::ScrollThumbData>
+struct ParamTraits<mozilla::layers::ScrollbarData>
+  : public PlainOldDataSerializer<mozilla::layers::ScrollbarData>
 { };
 
 template<>
 struct ParamTraits<mozilla::layers::WebRenderLayerScrollData>
 {
   typedef mozilla::layers::WebRenderLayerScrollData paramType;
 
   static void
@@ -223,17 +223,17 @@ struct ParamTraits<mozilla::layers::WebR
     WriteParam(aMsg, aParam.mDescendantCount);
     WriteParam(aMsg, aParam.mScrollIds);
     WriteParam(aMsg, aParam.mTransform);
     WriteParam(aMsg, aParam.mTransformIsPerspective);
     WriteParam(aMsg, aParam.mEventRegions);
     WriteParam(aMsg, aParam.mVisibleRegion);
     WriteParam(aMsg, aParam.mReferentId);
     WriteParam(aMsg, aParam.mEventRegionsOverride);
-    WriteParam(aMsg, aParam.mScrollThumbData);
+    WriteParam(aMsg, aParam.mScrollbarData);
     WriteParam(aMsg, aParam.mScrollbarAnimationId);
     WriteParam(aMsg, aParam.mScrollbarTargetContainerId);
     WriteParam(aMsg, aParam.mScrollbarContainerDirection);
     WriteParam(aMsg, aParam.mFixedPosScrollContainerId);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
@@ -241,17 +241,17 @@ struct ParamTraits<mozilla::layers::WebR
     return ReadParam(aMsg, aIter, &aResult->mDescendantCount)
         && ReadParam(aMsg, aIter, &aResult->mScrollIds)
         && ReadParam(aMsg, aIter, &aResult->mTransform)
         && ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective)
         && ReadParam(aMsg, aIter, &aResult->mEventRegions)
         && ReadParam(aMsg, aIter, &aResult->mVisibleRegion)
         && ReadParam(aMsg, aIter, &aResult->mReferentId)
         && ReadParam(aMsg, aIter, &aResult->mEventRegionsOverride)
-        && ReadParam(aMsg, aIter, &aResult->mScrollThumbData)
+        && ReadParam(aMsg, aIter, &aResult->mScrollbarData)
         && ReadParam(aMsg, aIter, &aResult->mScrollbarAnimationId)
         && ReadParam(aMsg, aIter, &aResult->mScrollbarTargetContainerId)
         && ReadParam(aMsg, aIter, &aResult->mScrollbarContainerDirection)
         && ReadParam(aMsg, aIter, &aResult->mFixedPosScrollContainerId);
   }
 };
 
 template<>
--- a/gfx/layers/wr/WebRenderScrollDataWrapper.h
+++ b/gfx/layers/wr/WebRenderScrollDataWrapper.h
@@ -280,20 +280,20 @@ public:
   }
 
   EventRegionsOverride GetEventRegionsOverride() const
   {
     MOZ_ASSERT(IsValid());
     return mLayer->GetEventRegionsOverride();
   }
 
-  const ScrollThumbData& GetScrollThumbData() const
+  const ScrollbarData& GetScrollbarData() const
   {
     MOZ_ASSERT(IsValid());
-    return mLayer->GetScrollThumbData();
+    return mLayer->GetScrollbarData();
   }
 
   uint64_t GetScrollbarAnimationId() const
   {
     MOZ_ASSERT(IsValid());
     return mLayer->GetScrollbarAnimationId();
   }
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -51,16 +51,17 @@
 #include "mozilla/dom/FileReaderBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/UnionConversions.h"
 #include "mozilla/dom/URLBinding.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 #include "mozilla/dom/XMLHttpRequest.h"
+#include "mozilla/dom/XMLSerializerBinding.h"
 #include "mozilla/dom/FormDataBinding.h"
 #include "mozilla/DeferredFinalize.h"
 
 using namespace mozilla;
 using namespace JS;
 using namespace xpc;
 
 using mozilla::dom::DestroyProtoAndIfaceCache;
@@ -827,16 +828,18 @@ xpc::GlobalProperties::Parse(JSContext* 
         } else if (!strcmp(name.ptr(), "TextEncoder")) {
             TextEncoder = true;
         } else if (!strcmp(name.ptr(), "URL")) {
             URL = true;
         } else if (!strcmp(name.ptr(), "URLSearchParams")) {
             URLSearchParams = true;
         } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
             XMLHttpRequest = true;
+        } else if (!strcmp(name.ptr(), "XMLSerializer")) {
+            XMLSerializer = true;
         } else if (!strcmp(name.ptr(), "atob")) {
             atob = true;
         } else if (!strcmp(name.ptr(), "btoa")) {
             btoa = true;
         } else if (!strcmp(name.ptr(), "caches")) {
             caches = true;
         } else if (!strcmp(name.ptr(), "crypto")) {
             crypto = true;
@@ -920,16 +923,20 @@ xpc::GlobalProperties::Define(JSContext*
     if (URLSearchParams &&
         !dom::URLSearchParamsBinding::GetConstructorObject(cx))
         return false;
 
     if (XMLHttpRequest &&
         !dom::XMLHttpRequestBinding::GetConstructorObject(cx))
         return false;
 
+    if (XMLSerializer &&
+        !dom::XMLSerializerBinding::GetConstructorObject(cx))
+        return false;
+
     if (atob &&
         !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
         return false;
 
     if (btoa &&
         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
         return false;
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2618,16 +2618,17 @@ struct GlobalProperties {
     bool InspectorUtils : 1;
     bool MessageChannel: 1;
     bool NodeFilter : 1;
     bool TextDecoder : 1;
     bool TextEncoder : 1;
     bool URL : 1;
     bool URLSearchParams : 1;
     bool XMLHttpRequest : 1;
+    bool XMLSerializer : 1;
 
     // Ad-hoc property names we implement.
     bool atob : 1;
     bool btoa : 1;
     bool caches : 1;
     bool crypto : 1;
     bool fetch : 1;
     bool indexedDB : 1;
new file mode 100644
--- /dev/null
+++ b/layout/base/LayoutConstants.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* constants used throughout the Layout module */
+
+#ifndef LayoutConstants_h___
+#define LayoutConstants_h___
+
+#include "nsSize.h"  // for NS_MAXSIZE
+
+/**
+ * Constant used to indicate an unconstrained size.
+ */
+#define NS_UNCONSTRAINEDSIZE NS_MAXSIZE
+
+// NOTE: There are assumptions all over that these have the same value,
+//       namely NS_UNCONSTRAINEDSIZE.
+#define NS_INTRINSICSIZE    NS_UNCONSTRAINEDSIZE
+#define NS_AUTOHEIGHT       NS_UNCONSTRAINEDSIZE
+#define NS_AUTOOFFSET       NS_UNCONSTRAINEDSIZE
+
+// +1 is to avoid clamped huge margin values being processed as auto margins
+#define NS_AUTOMARGIN       (NS_UNCONSTRAINEDSIZE + 1)
+
+#define NS_INTRINSIC_WIDTH_UNKNOWN nscoord_MIN
+
+
+#endif // LayoutConstants_h___
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -30,16 +30,17 @@ if CONFIG['MOZ_DEBUG']:
         'nsAutoLayoutPhase.cpp',
     ]
 
 XPIDL_MODULE = 'layout_base'
 
 EXPORTS += [
     'CaretAssociationHint.h',
     'FrameProperties.h',
+    'LayoutConstants.h',
     'LayoutLogging.h',
     'MobileViewportManager.h',
     'nsAutoLayoutPhase.h',
     'nsBidi.h',
     'nsBidiPresUtils.h',
     'nsCaret.h',
     'nsChangeHint.h',
     'nsCompatibility.h',
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5046,18 +5046,16 @@ GetDefiniteSize(const nsStyleCoord&     
       if (calc->mPercent != 0.0f) {
         if (aPercentageBasis.isNothing()) {
           return false;
         }
         auto wm = aFrame->GetWritingMode();
         nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
                                    : aPercentageBasis.value().BSize(wm);
         if (pb == NS_UNCONSTRAINEDSIZE) {
-          // XXXmats given that we're calculating an intrinsic size here,
-          // maybe we should back-compute the calc-size using AddPercents?
           return false;
         }
         *aResult = std::max(0, calc->mLength +
                                NSToCoordFloorClamped(pb * calc->mPercent));
       } else {
         *aResult = std::max(0, calc->mLength);
       }
       return true;
@@ -5291,44 +5289,35 @@ AddIntrinsicSizeOffset(gfxContext* aRend
                        const nscoord* aFixedMaxSize,
                        const nsStyleCoord& aStyleMaxSize,
                        uint32_t aFlags,
                        PhysicalAxis aAxis)
 {
   nscoord result = aContentSize;
   nscoord min = aContentMinSize;
   nscoord coordOutsideSize = 0;
-  float pctOutsideSize = 0;
-  float pctTotal = 0.0f;
 
   if (!(aFlags & nsLayoutUtils::IGNORE_PADDING)) {
     coordOutsideSize += aOffsets.hPadding;
-    pctOutsideSize += aOffsets.hPctPadding;
   }
 
   coordOutsideSize += aOffsets.hBorder;
 
   if (aBoxSizing == StyleBoxSizing::Border) {
     min += coordOutsideSize;
     result = NSCoordSaturatingAdd(result, coordOutsideSize);
-    pctTotal += pctOutsideSize;
 
     coordOutsideSize = 0;
-    pctOutsideSize = 0.0f;
   }
 
   coordOutsideSize += aOffsets.hMargin;
-  pctOutsideSize += aOffsets.hPctMargin;
 
   min += coordOutsideSize;
   result = NSCoordSaturatingAdd(result, coordOutsideSize);
-  pctTotal += pctOutsideSize;
-
-  const bool shouldAddPercent = aType == nsLayoutUtils::PREF_ISIZE ||
-                                (aFlags & nsLayoutUtils::ADD_PERCENTS);
+
   nscoord size;
   if (aType == nsLayoutUtils::MIN_ISIZE &&
       (((aStyleSize.HasPercent() || aStyleMaxSize.HasPercent()) &&
         aFrame->IsFrameOfType(nsIFrame::eReplacedSizing)) ||
        (aStyleSize.HasPercent() &&
         FormControlShrinksForPercentISize(aFrame)))) {
     // A percentage width or max-width on replaced elements means they
     // can shrink to 0.
@@ -5336,78 +5325,54 @@ AddIntrinsicSizeOffset(gfxContext* aRend
     // text inputs.
     // Note that if this is max-width, this overrides the fixed-width
     // rule in the next condition.
     result = 0; // let |min| handle padding/border/margin
   } else if (GetAbsoluteCoord(aStyleSize, size) ||
              GetIntrinsicCoord(aStyleSize, aRenderingContext, aFrame,
                                PROP_WIDTH, size)) {
     result = size + coordOutsideSize;
-    if (shouldAddPercent) {
-      result = nsLayoutUtils::AddPercents(result, pctOutsideSize);
-    }
-  } else {
-    // NOTE: We could really do a lot better for percents and for some
-    // cases of calc() containing percent (certainly including any where
-    // the coefficient on the percent is positive and there are no max()
-    // expressions).  However, doing better for percents wouldn't be
-    // backwards compatible.
-    if (shouldAddPercent) {
-      result = nsLayoutUtils::AddPercents(result, pctTotal);
-    }
   }
 
   nscoord maxSize = aFixedMaxSize ? *aFixedMaxSize : 0;
   if (aFixedMaxSize ||
       GetIntrinsicCoord(aStyleMaxSize, aRenderingContext, aFrame,
                         PROP_MAX_WIDTH, maxSize)) {
     maxSize += coordOutsideSize;
-    if (shouldAddPercent) {
-      maxSize = nsLayoutUtils::AddPercents(maxSize, pctOutsideSize);
-    }
     if (result > maxSize) {
       result = maxSize;
     }
   }
 
   nscoord minSize = aFixedMinSize ? *aFixedMinSize : 0;
   if (aFixedMinSize ||
       GetIntrinsicCoord(aStyleMinSize, aRenderingContext, aFrame,
                         PROP_MIN_WIDTH, minSize)) {
     minSize += coordOutsideSize;
-    if (shouldAddPercent) {
-      minSize = nsLayoutUtils::AddPercents(minSize, pctOutsideSize);
-    }
     if (result < minSize) {
       result = minSize;
     }
   }
 
-  if (shouldAddPercent) {
-    min = nsLayoutUtils::AddPercents(min, pctTotal);
-  }
   if (result < min) {
     result = min;
   }
 
   const nsStyleDisplay* disp = aFrame->StyleDisplay();
   if (aFrame->IsThemed(disp)) {
     LayoutDeviceIntSize devSize;
     bool canOverride = true;
     nsPresContext* pc = aFrame->PresContext();
     pc->GetTheme()->GetMinimumWidgetSize(pc, aFrame, disp->mAppearance,
                                          &devSize, &canOverride);
     nscoord themeSize =
       pc->DevPixelsToAppUnits(aAxis == eAxisVertical ? devSize.height
                                                      : devSize.width);
     // GetMinimumWidgetSize() returns a border-box width.
     themeSize += aOffsets.hMargin;
-    if (shouldAddPercent) {
-      themeSize = nsLayoutUtils::AddPercents(themeSize, aOffsets.hPctMargin);
-    }
     if (themeSize > result || !canOverride) {
       result = themeSize;
     }
   }
   return result;
 }
 
 static void
@@ -5631,19 +5596,29 @@ nsLayoutUtils::IntrinsicForAxis(Physical
   }
 
   if (aFrame->IsTableFrame()) {
     // Tables can't shrink smaller than their intrinsic minimum width,
     // no matter what.
     min = aFrame->GetMinISize(aRenderingContext);
   }
 
+  nscoord pmPercentageBasis = NS_UNCONSTRAINEDSIZE;
+  if (aPercentageBasis.isSome()) {
+    // The padding/margin percentage basis is the inline-size in the parent's
+    // writing-mode.
+    auto childWM = aFrame->GetWritingMode();
+    pmPercentageBasis =
+      aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM) ?
+        aPercentageBasis->BSize(childWM) :
+        aPercentageBasis->ISize(childWM);
+  }
   nsIFrame::IntrinsicISizeOffsetData offsets =
-    MOZ_LIKELY(isInlineAxis) ? aFrame->IntrinsicISizeOffsets()
-                             : aFrame->IntrinsicBSizeOffsets();
+    MOZ_LIKELY(isInlineAxis) ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
+                             : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
   nscoord contentBoxSize = result;
   result = AddIntrinsicSizeOffset(aRenderingContext, aFrame, offsets, aType,
                                   boxSizing, result, min, styleISize,
                                   haveFixedMinISize ? &minISize : nullptr,
                                   styleMinISize,
                                   haveFixedMaxISize ? &maxISize : nullptr,
                                   styleMaxISize,
                                   aFlags, aAxis);
@@ -5674,37 +5649,36 @@ nsLayoutUtils::IntrinsicForContainer(gfx
   MOZ_ASSERT(aFrame && aFrame->GetParent());
   // We want the size aFrame will contribute to its parent's inline-size.
   PhysicalAxis axis =
     aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
   return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType, Nothing(), aFlags);
 }
 
 /* static */ nscoord
-nsLayoutUtils::MinSizeContributionForAxis(PhysicalAxis        aAxis,
-                                          gfxContext*         aRC,
-                                          nsIFrame*           aFrame,
-                                          IntrinsicISizeType  aType,
-                                          uint32_t            aFlags)
+nsLayoutUtils::MinSizeContributionForAxis(PhysicalAxis       aAxis,
+                                          gfxContext*        aRC,
+                                          nsIFrame*          aFrame,
+                                          IntrinsicISizeType aType,
+                                          const LogicalSize& aPercentageBasis,
+                                          uint32_t           aFlags)
 {
   MOZ_ASSERT(aFrame);
   MOZ_ASSERT(aFrame->IsFlexOrGridItem(),
              "only grid/flex items have this behavior currently");
 
 #ifdef DEBUG_INTRINSIC_WIDTH
   nsFrame::IndentBy(stderr, gNoiseIndent);
   static_cast<nsFrame*>(aFrame)->ListTag(stderr);
   printf_stderr(" %s min-isize for %s WM:\n",
                 aType == MIN_ISIZE ? "min" : "pref",
                 aWM.IsVertical() ? "vertical" : "horizontal");
 #endif
 
-  // Note: this method is only meant for grid/flex items which always
-  // include percentages in their intrinsic size.
-  aFlags |= nsLayoutUtils::ADD_PERCENTS;
+  // Note: this method is only meant for grid/flex items.
   const nsStylePosition* const stylePos = aFrame->StylePosition();
   const nsStyleCoord* style = aAxis == eAxisHorizontal ? &stylePos->mMinWidth
                                                        : &stylePos->mMinHeight;
   nscoord minSize;
   nscoord* fixedMinSize = nullptr;
   auto minSizeUnit = style->GetUnit();
   if (minSizeUnit == eStyleUnit_Auto) {
     if (aFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) {
@@ -5739,24 +5713,29 @@ nsLayoutUtils::MinSizeContributionForAxi
 #endif
     return NS_UNCONSTRAINEDSIZE;
   }
 
   // If aFrame is a container for font size inflation, then shrink
   // wrapping inside of it should not apply font size inflation.
   AutoMaybeDisableFontInflation an(aFrame);
 
-  PhysicalAxis ourInlineAxis =
-    aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);
+  // The padding/margin percentage basis is the inline-size in the parent's
+  // writing-mode.
+  auto childWM = aFrame->GetWritingMode();
+  nscoord pmPercentageBasis =
+    aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM) ?
+      aPercentageBasis.BSize(childWM) :
+      aPercentageBasis.ISize(childWM);
+  PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(eLogicalAxisInline);
   nsIFrame::IntrinsicISizeOffsetData offsets =
-    ourInlineAxis == aAxis ? aFrame->IntrinsicISizeOffsets()
-                           : aFrame->IntrinsicBSizeOffsets();
+    ourInlineAxis == aAxis ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
+                           : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
   nscoord result = 0;
   nscoord min = 0;
-
   const nsStyleCoord& maxISize =
     aAxis == eAxisHorizontal ? stylePos->mMaxWidth : stylePos->mMaxHeight;
   result = AddIntrinsicSizeOffset(aRC, aFrame, offsets, aType,
                                   stylePos->mBoxSizing,
                                   result, min, *style, fixedMinSize,
                                   *style, nullptr, maxISize, aFlags, aAxis);
 
 #ifdef DEBUG_INTRINSIC_WIDTH
@@ -10355,37 +10334,8 @@ nsLayoutUtils::ParseFontLanguageOverride
     }
     result = (result << 8) + ch;
   }
   while (index++ < 4) {
     result = (result << 8) + 0x20;
   }
   return result;
 }
-
-/* static */ nscoord
-nsLayoutUtils::ResolveGapToLength(const nsStyleCoord& aCoord,
-                                  nscoord aPercentageBasis)
-{
-  switch (aCoord.GetUnit()) {
-    case eStyleUnit_Normal:
-      return nscoord(0);
-    case eStyleUnit_Coord:
-      return aCoord.GetCoordValue();
-    case eStyleUnit_Percent:
-      if (aPercentageBasis == NS_UNCONSTRAINEDSIZE) {
-        return nscoord(0);
-      }
-      return NSToCoordFloorClamped(aPercentageBasis *
-                                   aCoord.GetPercentValue());
-    case eStyleUnit_Calc: {
-      nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
-      if (aPercentageBasis == NS_UNCONSTRAINEDSIZE) {
-        return std::max(nscoord(0), calc->mLength);
-      }
-      return std::max(nscoord(0), calc->mLength +
-        NSToCoordFloorClamped(aPercentageBasis * calc->mPercent));
-    }
-    default:
-      MOZ_ASSERT_UNREACHABLE("Unexpected unit!");
-      return nscoord(0);
-  }
-}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -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/. */
 
 #ifndef nsLayoutUtils_h__
 #define nsLayoutUtils_h__
 
+#include "LayoutConstants.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsBoundingMetrics.h"
 #include "nsChangeHint.h"
 #include "nsFrameList.h"
@@ -170,16 +171,17 @@ public:
   typedef mozilla::CSSSize CSSSize;
   typedef mozilla::CSSIntSize CSSIntSize;
   typedef mozilla::CSSRect CSSRect;
   typedef mozilla::ScreenMargin ScreenMargin;
   typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize;
   typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::StyleGeometryBox StyleGeometryBox;
   typedef mozilla::SVGImageContext SVGImageContext;
+  typedef mozilla::LogicalSize LogicalSize;
 
   /**
    * Finds previously assigned ViewID for the given content element, if any.
    * Returns whether a ViewID was previously assigned.
    */
   static bool FindIDFor(const nsIContent* aContent, ViewID* aOutViewId);
 
   /**
@@ -1404,38 +1406,38 @@ public:
 
   /**
    * Get the contribution of aFrame to its containing block's intrinsic
    * size for the given physical axis.  This considers the child's intrinsic
    * width, its 'width', 'min-width', and 'max-width' properties (or 'height'
    * variations if that's what matches aAxis) and its padding, border and margin
    * in the corresponding dimension.
    * @param aPercentageBasis an optional percentage basis (in aFrame's WM).
-   *   Pass NS_UNCONSTRAINEDSIZE if the basis is indefinite in either/both axes.
+   *   If the basis is indefinite in a given axis, pass a size with
+   *   NS_UNCONSTRAINEDSIZE in that component.
    *   If you pass Nothing() a percentage basis will be calculated from aFrame's
    *   ancestors' computed size in the relevant axis, if needed.
    * @param aMarginBoxMinSizeClamp make the result fit within this margin-box
    * size by reducing the *content size* (flooring at zero).  This is used for:
    * https://drafts.csswg.org/css-grid/#min-size-auto
    */
   enum class IntrinsicISizeType { MIN_ISIZE, PREF_ISIZE };
   static const auto MIN_ISIZE = IntrinsicISizeType::MIN_ISIZE;
   static const auto PREF_ISIZE = IntrinsicISizeType::PREF_ISIZE;
   enum {
     IGNORE_PADDING = 0x01,
     BAIL_IF_REFLOW_NEEDED = 0x02, // returns NS_INTRINSIC_WIDTH_UNKNOWN if so
     MIN_INTRINSIC_ISIZE = 0x04, // use min-width/height instead of width/height
-    ADD_PERCENTS = 0x08, // apply AddPercents also for MIN_ISIZE
   };
   static nscoord
   IntrinsicForAxis(mozilla::PhysicalAxis aAxis,
                    gfxContext*           aRenderingContext,
                    nsIFrame*             aFrame,
                    IntrinsicISizeType    aType,
-                   const mozilla::Maybe<mozilla::LogicalSize>& aPercentageBasis = mozilla::Nothing(),
+                   const mozilla::Maybe<LogicalSize>& aPercentageBasis = mozilla::Nothing(),
                    uint32_t              aFlags = 0,
                    nscoord               aMarginBoxMinSizeClamp = NS_MAXSIZE);
   /**
    * Calls IntrinsicForAxis with aFrame's parent's inline physical axis.
    */
   static nscoord IntrinsicForContainer(gfxContext*         aRenderingContext,
                                        nsIFrame*           aFrame,
                                        IntrinsicISizeType  aType,
@@ -1450,41 +1452,28 @@ public:
    * the "specified size" for:
    * https://drafts.csswg.org/css-grid/#min-size-auto
    * Note that the "transferred size" is not handled here; use IntrinsicForAxis.
    * Note that any percentage in 'width'/'height' makes it count as indefinite.
    * If the 'min-' property is 'auto' and 'overflow' is not 'visible', then it
    * calculates the result as if the 'min-' computed value is zero.
    * Otherwise, return NS_UNCONSTRAINEDSIZE.
    *
+   * @param aPercentageBasis the percentage basis (in aFrame's WM).
+   *   Pass NS_UNCONSTRAINEDSIZE if the basis is indefinite in either/both axes.
    * @note this behavior is specific to Grid/Flexbox (currently) so aFrame
    * should be a grid/flex item.
    */
-  static nscoord MinSizeContributionForAxis(mozilla::PhysicalAxis aAxis,
-                                            gfxContext*           aRC,
-                                            nsIFrame*             aFrame,
-                                            IntrinsicISizeType    aType,
-                                            uint32_t              aFlags = 0);
-
-  /**
-   * This function increases an initial intrinsic size, 'aCurrent', according
-   * to the given 'aPercent', such that the size-increase makes up exactly
-   * 'aPercent' percent of the returned value.  If 'aPercent' or 'aCurrent' are
-   * less than or equal to zero the original 'aCurrent' value is returned.
-   * If 'aPercent' is greater than or equal to 1.0 the value nscoord_MAX is
-   * returned.
-   */
-  static nscoord AddPercents(nscoord aCurrent, float aPercent)
-  {
-    if (aPercent > 0.0f && aCurrent > 0) {
-      return MOZ_UNLIKELY(aPercent >= 1.0f) ? nscoord_MAX
-        : NSToCoordRound(float(aCurrent) / (1.0f - aPercent));
-    }
-    return aCurrent;
-  }
+  static nscoord
+  MinSizeContributionForAxis(mozilla::PhysicalAxis aAxis,
+                            gfxContext*            aRC,
+                            nsIFrame*              aFrame,
+                            IntrinsicISizeType     aType,
+                            const LogicalSize&     aPercentageBasis,
+                            uint32_t               aFlags = 0);
 
   /*
    * Convert nsStyleCoord to nscoord when percentages depend on the
    * containing block size.
    * @param aPercentBasis The width or height of the containing block
    * (whichever the client wants to use for resolving percentages).
    */
   static nscoord ComputeCBDependentValue(nscoord aPercentBasis,
@@ -3064,22 +3053,70 @@ public:
                                   nsTArray<gfxFontFeature>& aFeatureSettings);
 
   static void ComputeFontVariations(const nsCSSValuePairList* aVariationsList,
                                     nsTArray<gfxFontVariation>& aVariationSettings);
 
   static uint32_t ParseFontLanguageOverride(const nsAString& aLangTag);
 
   /**
+   * Resolve a CSS <length-percentage> value to a definite size.
+   */
+  template<bool clampNegativeResultToZero>
+  static nscoord ResolveToLength(const nsStyleCoord& aCoord,
+                                 nscoord aPercentageBasis)
+  {
+    NS_WARNING_ASSERTION(aPercentageBasis >= nscoord(0), "nscoord overflow?");
+
+    switch (aCoord.GetUnit()) {
+      case eStyleUnit_Coord:
+        MOZ_ASSERT(!clampNegativeResultToZero || aCoord.GetCoordValue() >= 0,
+                   "This value should have been rejected by the style system");
+        return aCoord.GetCoordValue();
+      case eStyleUnit_Percent:
+        if (aPercentageBasis == NS_UNCONSTRAINEDSIZE) {
+          return nscoord(0);
+        }
+        MOZ_ASSERT(!clampNegativeResultToZero || aCoord.GetPercentValue() >= 0,
+                   "This value should have been rejected by the style system");
+        return NSToCoordFloorClamped(aPercentageBasis *
+                                     aCoord.GetPercentValue());
+      case eStyleUnit_Calc: {
+        nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
+        nscoord result;
+        if (aPercentageBasis == NS_UNCONSTRAINEDSIZE) {
+          result = calc->mLength;
+        } else {
+          result = calc->mLength +
+            NSToCoordFloorClamped(aPercentageBasis * calc->mPercent);
+        }
+        if (clampNegativeResultToZero && result < 0) {
+          return nscoord(0);
+        }
+        return result;
+      }
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unexpected unit!");
+        return nscoord(0);
+    }
+  }
+
+  /**
    * Resolve a column-gap/row-gap to a definite size.
    * @note This method resolves 'normal' to zero.
    *   Callers who want different behavior should handle 'normal' on their own.
    */
   static nscoord ResolveGapToLength(const nsStyleCoord& aGap,
-                                    nscoord aPercentageBasis);
+                                    nscoord aPercentageBasis)
+  {
+    if (aGap.GetUnit() == eStyleUnit_Normal) {
+      return nscoord(0);
+    }
+    return ResolveToLength<true>(aGap, aPercentageBasis);
+  }
 
 private:
   static uint32_t sFontSizeInflationEmPerLine;
   static uint32_t sFontSizeInflationMinTwips;
   static uint32_t sFontSizeInflationLineThreshold;
   static int32_t  sFontSizeInflationMappingIntercept;
   static uint32_t sFontSizeInflationMaxRatio;
   static bool sFontSizeInflationForceEnabled;
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -52,17 +52,16 @@
 
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
 
 // Transformiix stuff
 #include "mozilla/dom/XPathEvaluator.h"
 
 #include "mozilla/dom/DOMParser.h"
-#include "nsDOMSerializer.h"
 
 // view stuff
 #include "nsContentCreatorFunctions.h"
 
 #include "nsHostObjectURI.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsIControllerCommandTable.h"
 #include "nsJSProtocolHandler.h"
@@ -194,17 +193,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(HTMLEdito
 { 0xe1e79dec, 0x4085, 0x4994, { 0xac, 0x5b, 0x74, 0x4b, 0x01, 0x66, 0x97, 0xe6 } }
 
 #define PRESENTATION_TCP_SESSION_TRANSPORT_CID \
 { 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
 
 already_AddRefed<nsIPresentationService> NS_CreatePresentationService();
 
 // Factory Constructor
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer)
 typedef nsHostObjectURI::Mutator nsHostObjectURIMutator;
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURIMutator)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMParser)
 NS_GENERIC_FACTORY_CONSTRUCTOR(LocalStorageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(SessionStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService,
@@ -545,17 +543,16 @@ NS_DEFINE_NAMED_CID(NS_JSPROTOCOLHANDLER
 NS_DEFINE_NAMED_CID(NS_JSURI_CID);
 NS_DEFINE_NAMED_CID(NS_JSURIMUTATOR_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOWCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOWCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_PLUGINDOCLOADERFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_PLUGINDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_VIDEODOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_STYLESHEETSERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURI_CID);
 NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURIMUTATOR_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
 NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID);
@@ -788,17 +785,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_JSURI_CID, false, nullptr, nsJSURIMutatorConstructor }, // do_CreateInstance returns mutator
   { &kNS_JSURIMUTATOR_CID, false, nullptr, nsJSURIMutatorConstructor },
   { &kNS_WINDOWCOMMANDTABLE_CID, false, nullptr, CreateWindowCommandTableConstructor },
   { &kNS_WINDOWCONTROLLER_CID, false, nullptr, CreateWindowControllerWithSingletonCommandTable },
   { &kNS_PLUGINDOCLOADERFACTORY_CID, false, nullptr, CreateContentDLF },
   { &kNS_PLUGINDOCUMENT_CID, false, nullptr, CreatePluginDocument },
   { &kNS_VIDEODOCUMENT_CID, false, nullptr, CreateVideoDocument },
   { &kNS_STYLESHEETSERVICE_CID, false, nullptr, nsStyleSheetServiceConstructor },
-  { &kNS_XMLSERIALIZER_CID, false, nullptr, nsDOMSerializerConstructor },
   { &kNS_HOSTOBJECTURI_CID, false, nullptr, nsHostObjectURIMutatorConstructor }, // do_CreateInstance returns mutator
   { &kNS_HOSTOBJECTURIMUTATOR_CID, false, nullptr, nsHostObjectURIMutatorConstructor },
   { &kNS_DOMPARSER_CID, false, nullptr, DOMParserConstructor },
   { &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, SessionStorageManagerConstructor },
   { &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, LocalStorageManagerConstructor },
   { &kNS_TEXTEDITOR_CID, false, nullptr, TextEditorConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
   { &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor },
@@ -896,17 +892,16 @@ static const mozilla::Module::ContractID
 #ifdef MOZ_XUL
   { "@mozilla.org/xul/xul-sort-service;1", &kNS_XULSORTSERVICE_CID },
 #endif
   { CONTENT_DLF_CONTRACTID, &kNS_CONTENT_DOCUMENT_LOADER_FACTORY_CID },
   { NS_JSPROTOCOLHANDLER_CONTRACTID, &kNS_JSPROTOCOLHANDLER_CID },
   { NS_WINDOWCONTROLLER_CONTRACTID, &kNS_WINDOWCONTROLLER_CID },
   { PLUGIN_DLF_CONTRACTID, &kNS_PLUGINDOCLOADERFACTORY_CID },
   { NS_STYLESHEETSERVICE_CONTRACTID, &kNS_STYLESHEETSERVICE_CID },
-  { NS_XMLSERIALIZER_CONTRACTID, &kNS_XMLSERIALIZER_CID },
   { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
   { "@mozilla.org/dom/localStorage-manager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
   // Keeping the old ContractID for backward compatibility
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
   { QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID },
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3378,17 +3378,17 @@ nsIFrame::BuildDisplayListForStackingCon
 
   if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) {
     clipState.Restore();
     resultList.AppendToTop(
       MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &resultList,
                                        aBuilder->CurrentActiveScrolledRoot(),
                                        nsDisplayOwnLayerFlags::eNone,
                                        mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
-                                       ScrollThumbData{}, /* aForceActive = */ false));
+                                       ScrollbarData{}, /* aForceActive = */ false));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
 
   /* If we have sticky positioning, wrap it in a sticky position item.
    */
   if (useFixedPosition) {
@@ -5479,78 +5479,54 @@ nsIFrame::InlinePrefISizeData::ForceBrea
   mCurrentLine =
     NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
   mPrevLines = std::max(mPrevLines, mCurrentLine);
   mCurrentLine = mTrailingWhitespace = 0;
   mSkipWhitespace = true;
   mLineIsEmpty = true;
 }
 
-static void
-AddCoord(const nsStyleCoord& aStyle,
-         nsIFrame* aFrame,
-         nscoord* aCoord, float* aPercent,
-         bool aClampNegativeToZero)
-{
-  switch (aStyle.GetUnit()) {
-    case eStyleUnit_Coord: {
-      NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0,
-                   "unexpected negative value");
-      *aCoord += aStyle.GetCoordValue();
-      return;
-    }
-    case eStyleUnit_Percent: {
-      NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
-                   "unexpected negative value");
-      *aPercent += aStyle.GetPercentValue();
-      return;
-    }
-    case eStyleUnit_Calc: {
-      const nsStyleCoord::Calc *calc = aStyle.GetCalcValue();
-      if (aClampNegativeToZero) {
-        // This is far from ideal when one is negative and one is positive.
-        *aCoord += std::max(calc->mLength, 0);
-        *aPercent += std::max(calc->mPercent, 0.0f);
-      } else {
-        *aCoord += calc->mLength;
-        *aPercent += calc->mPercent;
-      }
-      return;
-    }
-    default: {
-      return;
-    }
-  }
+static nscoord
+ResolveMargin(const nsStyleCoord& aStyle, nscoord aPercentageBasis)
+{
+  if (aStyle.GetUnit() == eStyleUnit_Auto) {
+    return nscoord(0);
+  }
+  return nsLayoutUtils::ResolveToLength<false>(aStyle, aPercentageBasis);
+}
+
+static nscoord
+ResolvePadding(const nsStyleCoord& aStyle, nscoord aPercentageBasis)
+{
+  return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
 }
 
 static nsIFrame::IntrinsicISizeOffsetData
-IntrinsicSizeOffsets(nsIFrame* aFrame, bool aForISize)
+IntrinsicSizeOffsets(nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize)
 {
   nsIFrame::IntrinsicISizeOffsetData result;
   WritingMode wm = aFrame->GetWritingMode();
-  const nsStyleMargin* styleMargin = aFrame->StyleMargin();
+  const auto& margin = aFrame->StyleMargin()->mMargin;
   bool verticalAxis = aForISize == wm.IsVertical();
-  AddCoord(verticalAxis ? styleMargin->mMargin.GetTop()
-                        : styleMargin->mMargin.GetLeft(),
-           aFrame, &result.hMargin, &result.hPctMargin,
-           false);
-  AddCoord(verticalAxis ? styleMargin->mMargin.GetBottom()
-                        : styleMargin->mMargin.GetRight(),
-           aFrame, &result.hMargin, &result.hPctMargin,
-           false);
-
-  const nsStylePadding* stylePadding = aFrame->StylePadding();
-  AddCoord(verticalAxis ? stylePadding->mPadding.GetTop()
-                        : stylePadding->mPadding.GetLeft(),
-           aFrame, &result.hPadding, &result.hPctPadding,
-           true);
-  AddCoord(verticalAxis ? stylePadding->mPadding.GetBottom()
-                        : stylePadding->mPadding.GetRight(),
-           aFrame, &result.hPadding, &result.hPctPadding,
-           true);
+  if (verticalAxis) {
+    result.hMargin += ResolveMargin(margin.GetTop(), aPercentageBasis);
+    result.hMargin += ResolveMargin(margin.GetBottom(), aPercentageBasis);
+  } else {
+    result.hMargin += ResolveMargin(margin.GetLeft(), aPercentageBasis);
+    result.hMargin += ResolveMargin(margin.GetRight(), aPercentageBasis);
+  }
+
+  const auto& padding = aFrame->StylePadding()->mPadding;
+  if (verticalAxis) {
+    result.hPadding += ResolvePadding(padding.GetTop(), aPercentageBasis);
+    result.hPadding += ResolvePadding(padding.GetBottom(), aPercentageBasis);
+  } else {
+    result.hPadding += ResolvePadding(padding.GetLeft(), aPercentageBasis);
+    result.hPadding += ResolvePadding(padding.GetRight(), aPercentageBasis);
+  }
 
   const nsStyleBorder* styleBorder = aFrame->StyleBorder();
   if (verticalAxis) {
     result.hBorder += styleBorder->GetComputedBorderWidth(eSideTop);
     result.hBorder += styleBorder->GetComputedBorderWidth(eSideBottom);
   } else {
     result.hBorder += styleBorder->GetComputedBorderWidth(eSideLeft);
     result.hBorder += styleBorder->GetComputedBorderWidth(eSideRight);
@@ -5570,32 +5546,31 @@ IntrinsicSizeOffsets(nsIFrame* aFrame, b
 
     nsIntMargin padding;
     if (presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
                                                   aFrame, disp->mAppearance,
                                                   &padding)) {
       result.hPadding =
         presContext->DevPixelsToAppUnits(verticalAxis ? padding.TopBottom()
                                                       : padding.LeftRight());
-      result.hPctPadding = 0;
     }
   }
   return result;
 }
 
 /* virtual */ nsIFrame::IntrinsicISizeOffsetData
-nsFrame::IntrinsicISizeOffsets()
-{
-  return IntrinsicSizeOffsets(this, true);
+nsFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis)
+{
+  return IntrinsicSizeOffsets(this, aPercentageBasis, true);
 }
 
 nsIFrame::IntrinsicISizeOffsetData
-nsIFrame::IntrinsicBSizeOffsets()
-{
-  return IntrinsicSizeOffsets(this, false);
+nsIFrame::IntrinsicBSizeOffsets(nscoord aPercentageBasis)
+{
+  return IntrinsicSizeOffsets(this, aPercentageBasis, false);
 }
 
 /* virtual */ IntrinsicSize
 nsFrame::GetIntrinsicSize()
 {
   return IntrinsicSize(); // default is width/height set to eStyleUnit_None
 }
 
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -269,17 +269,18 @@ public:
 
   void MarkIntrinsicISizesDirty() override;
   nscoord GetMinISize(gfxContext *aRenderingContext) override;
   nscoord GetPrefISize(gfxContext *aRenderingContext) override;
   void AddInlineMinISize(gfxContext *aRenderingContext,
                          InlineMinISizeData *aData) override;
   void AddInlinePrefISize(gfxContext *aRenderingContext,
                           InlinePrefISizeData *aData) override;
-  IntrinsicISizeOffsetData IntrinsicISizeOffsets() override;
+  IntrinsicISizeOffsetData
+  IntrinsicISizeOffsets(nscoord aPercentageBasis = NS_UNCONSTRAINEDSIZE) override;
   mozilla::IntrinsicSize GetIntrinsicSize() override;
   nsSize GetIntrinsicRatio() override;
 
   mozilla::LogicalSize
   ComputeSize(gfxContext*                 aRenderingContext,
               mozilla::WritingMode        aWM,
               const mozilla::LogicalSize& aCBSize,
               nscoord                     aAvailableISize,
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -3,19 +3,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/. */
 
 /* rendering object for CSS "display: grid | inline-grid" */
 
 #include "nsGridContainerFrame.h"
 
-#include <algorithm> // for std::stable_sort
 #include <functional>
 #include <limits>
+#include <stdlib.h> // for div()
 #include "gfxContext.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/CSSAlignUtils.h"
 #include "mozilla/CSSOrderAwareFrameIterator.h"
 #include "mozilla/dom/GridBinding.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Maybe.h"
@@ -119,52 +119,16 @@ ResolveToDefiniteSize(const nsStyleCoord
 {
   MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
   if (::IsPercentOfIndefiniteSize(aCoord, aPercentBasis)) {
     return nscoord(0);
   }
   return std::max(nscoord(0), aCoord.ComputeCoordPercentCalc(aPercentBasis));
 }
 
-static bool
-GetPercentSizeParts(const nsStyleCoord& aCoord, nscoord* aLength, float* aPercent)
-{
-  switch (aCoord.GetUnit()) {
-    case eStyleUnit_Percent:
-      *aLength = 0;
-      *aPercent = aCoord.GetPercentValue();
-      return true;
-    case eStyleUnit_Calc: {
-      nsStyleCoord::Calc* calc = aCoord.GetCalcValue();
-      *aLength = calc->mLength;
-      *aPercent = calc->mPercent;
-      return true;
-    }
-    default:
-      return false;
-  }
-}
-
-static void
-ResolvePercentSizeParts(const nsStyleCoord& aCoord, nscoord aPercentBasis,
-                        nscoord* aLength, float* aPercent)
-{
-  MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
-  if (aPercentBasis != NS_UNCONSTRAINEDSIZE) {
-    *aLength = std::max(nscoord(0),
-                        aCoord.ComputeCoordPercentCalc(aPercentBasis));
-    *aPercent = 0.0f;
-    return;
-  }
-  if (!GetPercentSizeParts(aCoord, aLength, aPercent)) {
-    *aLength = aCoord.ToLength();
-    *aPercent = 0.0f;
-  }
-}
-
 // Synthesize a baseline from a border box.  For an alphabetical baseline
 // this is the end edge of the border box.  For a central baseline it's
 // the center of the border box.
 // https://drafts.csswg.org/css-align-3/#synthesize-baselines
 // For a 'first baseline' the measure is from the border-box start edge and
 // for a 'last baseline' the measure is from the border-box end edge.
 static nscoord
 SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup,
@@ -925,81 +889,58 @@ struct nsGridContainerFrame::TrackSizing
     const uint32_t numTracks = mMinSizingFunctions.Length();
     MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
     nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
     if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
       // "Otherwise, the specified track list repeats only once."
       return 1;
     }
     nscoord repeatTrackSize = 0;
-    // Note that the repeat() track size is included in |sum| in this loop.
+    // Note that one repeat() track size is included in |sum| in this loop.
     nscoord sum = 0;
     const nscoord percentBasis = aSize;
     for (uint32_t i = 0; i < numTracks; ++i) {
       // "treating each track as its max track sizing function if that is
       // definite or as its minimum track sizing function otherwise"
       // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
       const auto& maxCoord = mMaxSizingFunctions[i];
       const auto* coord = &maxCoord;
       if (!coord->IsCoordPercentCalcUnit()) {
         coord = &mMinSizingFunctions[i];
         if (!coord->IsCoordPercentCalcUnit()) {
           return 1;
         }
       }
       nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
       if (i == mRepeatAutoStart) {
-        if (percentBasis != NS_UNCONSTRAINEDSIZE) {
-          // Use a minimum 1px for the repeat() track-size.
-          if (trackSize < AppUnitsPerCSSPixel()) {
-            trackSize = AppUnitsPerCSSPixel();
-          }
+        // Use a minimum 1px for the repeat() track-size.
+        if (trackSize < AppUnitsPerCSSPixel()) {
+          trackSize = AppUnitsPerCSSPixel();
         }
         repeatTrackSize = trackSize;
       }
       sum += trackSize;
     }
-    nscoord gridGap;
-    float percentSum = 0.0f;
-    float gridGapPercent;
-    ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent);
+    nscoord gridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aSize);
     if (numTracks > 1) {
       // Add grid-gaps for all the tracks including the repeat() track.
       sum += gridGap * (numTracks - 1);
-      percentSum = gridGapPercent * (numTracks - 1);
     }
     // Calculate the max number of tracks that fits without overflow.
     nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
-    nscoord size = nsLayoutUtils::AddPercents(sum, percentSum);
-    if (available - size < 0) {
+    nscoord spaceToFill = available - sum;
+    if (spaceToFill <= 0) {
       // "if any number of repetitions would overflow, then 1 repetition"
       return 1;
     }
-    uint32_t numRepeatTracks = 1;
-    bool exactFit = false;
-    while (true) {
-      sum += gridGap + repeatTrackSize;
-      percentSum += gridGapPercent;
-      nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum);
-      if (newSize <= size) {
-        // Adding more repeat-tracks won't make forward progress.
-        return numRepeatTracks;
-      }
-      size = newSize;
-      nscoord remaining = available - size;
-      exactFit = remaining == 0;
-      if (remaining >= 0) {
-        ++numRepeatTracks;
-      }
-      if (remaining <= 0) {
-        break;
-      }
-    }
-
-    if (!exactFit && maxFill == NS_UNCONSTRAINEDSIZE) {
+    // Calculate the max number of tracks that fits without overflow.
+    div_t q = div(spaceToFill, repeatTrackSize + gridGap);
+    // The +1 here is for the one repeat track we already accounted for above.
+    uint32_t numRepeatTracks = q.quot + 1;
+    if (q.rem != 0 && maxFill == NS_UNCONSTRAINEDSIZE) {
       // "Otherwise, if the grid container has a definite min size in
       // the relevant axis, the number of repetitions is the largest possible
       // positive integer that fulfills that minimum requirement."
       ++numRepeatTracks; // one more to ensure the grid is at least min-size
     }
     // Clamp the number of repeat tracks so that the last line <= kMaxLine.
     // (note that |numTracks| already includes one repeat() track)
     const uint32_t maxRepeatTracks = nsStyleGridLine::kMaxLine - numTracks;
@@ -1634,23 +1575,16 @@ struct nsGridContainerFrame::Tracks
   /**
    * Apply 'align/justify-content', whichever is relevant for this axis.
    * https://drafts.csswg.org/css-align-3/#propdef-align-content
    */
   void AlignJustifyContent(const nsStylePosition* aStyle,
                            WritingMode            aWM,
                            const LogicalSize&     aContainerSize);
 
-  /**
-   * Return the intrinsic size by back-computing percentages as:
-   * IntrinsicSize = SumOfCoordSizes / (1 - SumOfPercentages).
-   */
-  nscoord BackComputedIntrinsicSize(const TrackSizingFunctions& aFunctions,
-                                    const nsStyleCoord& aGridGap) const;
-
   nscoord GridLineEdge(uint32_t aLine, GridLineSide aSide) const
   {
     if (MOZ_UNLIKELY(mSizes.IsEmpty())) {
       // https://drafts.csswg.org/css-grid/#grid-definition
       // "... the explicit grid still contains one grid line in each axis."
       MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid");
       return nscoord(0);
     }
@@ -1944,21 +1878,20 @@ struct MOZ_STACK_CLASS nsGridContainerFr
 
     // Copy in the computed grid info state bit
     if (mSharedGridData->mGenerateComputedGridInfo) {
       aGridContainerFrame->AddStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES);
     }
   }
 
   /**
-   * Calculate our track sizes.  If the given aContentBox block-axis size is
-   * unconstrained, it is assigned to the resulting intrinsic block-axis size.
+   * Calculate our track sizes.
    */
   void CalculateTrackSizes(const Grid&        aGrid,
-                           LogicalSize&       aContentBox,
+                           const LogicalSize& aContentBox,
                            SizingConstraint   aConstraint);
 
   /**
    * Return the percentage basis for a grid item in its writing-mode.
    * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in
    * both axes since we know all track sizes are indefinite at this point
    * (we calculate column sizes before row sizes).  Otherwise, assert that
    * column sizes are known and calculate the size for aGridItem.mArea.mCols
@@ -2434,17 +2367,17 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    */
   uint32_t mExplicitGridOffsetCol;
   uint32_t mExplicitGridOffsetRow;
 };
 
 void
 nsGridContainerFrame::GridReflowInput::CalculateTrackSizes(
   const Grid&        aGrid,
-  LogicalSize&       aContentBox,
+  const LogicalSize& aContentBox,
   SizingConstraint   aConstraint)
 {
   mCols.Initialize(mColFunctions, mGridStyle->mGridColumnGap,
                    aGrid.mGridColEnd, aContentBox.ISize(mWM));
   mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap,
                    aGrid.mGridRowEnd, aContentBox.BSize(mWM));
 
   mCols.CalculateSizes(*this, mGridItems, mColFunctions,
@@ -2452,22 +2385,16 @@ nsGridContainerFrame::GridReflowInput::C
                        aConstraint);
   mCols.AlignJustifyContent(mGridStyle, mWM, aContentBox);
   // Column positions and sizes are now final.
   mCols.mCanResolveLineRangeSize = true;
 
   mRows.CalculateSizes(*this, mGridItems, mRowFunctions,
                        aContentBox.BSize(mWM), &GridArea::mRows,
                        aConstraint);
-  if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) {
-    aContentBox.BSize(mWM) =
-      mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap);
-    mRows.mGridGap =
-      ::ResolveToDefiniteSize(mGridStyle->mGridRowGap, aContentBox.BSize(mWM));
-  }
 }
 
 /**
  * (XXX share this utility function with nsFlexContainerFrame at some point)
  *
  * Helper for BuildDisplayList, to implement this special-case for grid
  * items from the spec:
  *   The painting order of grid items is exactly the same as inline blocks,
@@ -3559,17 +3486,17 @@ nsGridContainerFrame::Tracks::Initialize
                              aFunctions.NumExplicitTracks());
   mSizes.SetLength(aNumTracks);
   PodZero(mSizes.Elements(), mSizes.Length());
   for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) {
     mStateUnion |= mSizes[i].Initialize(aContentBoxSize,
                                         aFunctions.MinSizingFor(i),
                                         aFunctions.MaxSizingFor(i));
   }
-  mGridGap = ::ResolveToDefiniteSize(aGridGap, aContentBoxSize);
+  mGridGap = nsLayoutUtils::ResolveGapToLength(aGridGap, aContentBoxSize);
   mContentBoxSize = aContentBoxSize;
 }
 
 /**
  * Reflow aChild in the given aAvailableSize.
  */
 static nscoord
 MeasuringReflow(nsIFrame*           aChild,
@@ -3650,18 +3577,17 @@ ContentContribution(const GridItemInfo& 
                     IntrinsicISizeType        aConstraint,
                     nscoord                   aMinSizeClamp = NS_MAXSIZE,
                     uint32_t                  aFlags = 0)
 {
   nsIFrame* child = aGridItem.mFrame;
   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
   nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint,
                    aPercentageBasis,
-                   aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED |
-                            nsLayoutUtils::ADD_PERCENTS,
+                   aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED,
                    aMinSizeClamp);
   if (size == NS_INTRINSIC_WIDTH_UNKNOWN) {
     // We need to reflow the child to find its BSize contribution.
     // XXX this will give mostly correct results for now (until bug 1174569).
     nscoord availISize = INFINITE_ISIZE_COORD;
     nscoord availBSize = NS_UNCONSTRAINEDSIZE;
     auto childWM = child->GetWritingMode();
     const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM);
@@ -3688,25 +3614,17 @@ ContentContribution(const GridItemInfo& 
     if (isOrthogonal == (aAxis == eLogicalAxisInline)) {
       bMinSizeClamp = aMinSizeClamp;
     } else {
       iMinSizeClamp = aMinSizeClamp;
     }
     LogicalSize availableSize(childWM, availISize, availBSize);
     size = ::MeasuringReflow(child, aState.mReflowInput, aRC, availableSize,
                              cbSize, iMinSizeClamp, bMinSizeClamp);
-    nsIFrame::IntrinsicISizeOffsetData offsets = child->IntrinsicBSizeOffsets();
-    size += offsets.hMargin;
-    auto percent = offsets.hPctMargin;
-    if (availBSize == NS_UNCONSTRAINEDSIZE) {
-      // We always want to add in percent padding too, unless we already did so
-      // using a resolved column size above.
-      percent += offsets.hPctPadding;
-    }
-    size = nsLayoutUtils::AddPercents(size, percent);
+    size += child->GetLogicalUsedMargin(childWM).BStartEnd(childWM);
     nscoord overflow = size - aMinSizeClamp;
     if (MOZ_UNLIKELY(overflow > 0)) {
       nscoord contentSize = child->ContentBSize(childWM);
       nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
       // XXXmats deal with percentages better, see bug 1300369 comment 27.
       size -= contentSize - newContentSize;
     }
   }
@@ -3801,41 +3719,43 @@ MinSize(const GridItemInfo&    aGridItem
     axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight;
   if (sizeStyle.GetUnit() != eStyleUnit_Auto) {
     nscoord s =
       MinContentContribution(aGridItem, aState, aRC, aCBWM, aAxis, aCache);
     aCache->mMinSize.emplace(s);
     return s;
   }
 
+  if (aCache->mPercentageBasis.isNothing()) {
+    aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem));
+  }
+
   // https://drafts.csswg.org/css-grid/#min-size-auto
   // This calculates the min-content contribution from either a definite
   // min-width (or min-height depending on aAxis), or the "specified /
   // transferred size" for min-width:auto if overflow == visible (as min-width:0
   // otherwise), or NS_UNCONSTRAINEDSIZE for other min-width intrinsic values
   // (which results in always taking the "content size" part below).
   MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
              "baseline offset should be non-negative at this point");
   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
              aGridItem.mBaselineOffset[aAxis] == nscoord(0),
              "baseline offset should be zero when not baseline-aligned");
   nscoord sz = aGridItem.mBaselineOffset[aAxis] +
     nsLayoutUtils::MinSizeContributionForAxis(axis, aRC, child,
-                                              nsLayoutUtils::MIN_ISIZE);
+                                              nsLayoutUtils::MIN_ISIZE,
+                                              *aCache->mPercentageBasis);
   const nsStyleCoord& style = axis == eAxisHorizontal ? stylePos->mMinWidth
                                                       : stylePos->mMinHeight;
   auto unit = style.GetUnit();
   if (unit == eStyleUnit_Enumerated ||
       (unit == eStyleUnit_Auto &&
        child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) {
     // Now calculate the "content size" part and return whichever is smaller.
     MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE);
-    if (aCache->mPercentageBasis.isNothing()) {
-      aCache->mPercentageBasis.emplace(aState.PercentageBasisFor(aAxis, aGridItem));
-    }
     sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
                                           aCache->mPercentageBasis,
                                           nsLayoutUtils::MIN_ISIZE,
                                           aCache->mMinSizeClamp,
                                           nsLayoutUtils::MIN_INTRINSIC_ISIZE));
   }
   aCache->mMinSize.emplace(sz);
   return sz;
@@ -4829,46 +4749,16 @@ nsGridContainerFrame::Tracks::AlignJusti
       roundingError -= 1;
       spacing += 1;
     }
     pos += sz.mBase + spacing;
   }
   MOZ_ASSERT(!roundingError, "we didn't distribute all rounding error?");
 }
 
-nscoord
-nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
-  const TrackSizingFunctions& aFunctions,
-  const nsStyleCoord& aGridGap) const
-{
-  // Sum up the current sizes (where percentage tracks were treated as 'auto')
-  // in 'size'.
-  nscoord size = 0;
-  for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
-    size += mSizes[i].mBase;
-  }
-
-  // Add grid-gap contributions to 'size' and calculate a 'percent' sum.
-  float percent = 0.0f;
-  size_t numTracks = mSizes.Length();
-  if (numTracks > 1) {
-    const size_t gridGapCount = numTracks - 1;
-    nscoord gridGapLength;
-    float gridGapPercent;
-    if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) {
-      percent = gridGapCount * gridGapPercent;
-    } else {
-      gridGapLength = aGridGap.ToLength();
-    }
-    size += gridGapCount * gridGapLength;
-  }
-
-  return std::max(0, nsLayoutUtils::AddPercents(size, percent));
-}
-
 void
 nsGridContainerFrame::LineRange::ToPositionAndLength(
   const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const
 {
   MOZ_ASSERT(mStart != kAutoLine && mEnd != kAutoLine,
              "expected a definite LineRange");
   MOZ_ASSERT(mStart < mEnd);
   nscoord startPos = aTrackSizes[mStart].mPosition;
@@ -6046,25 +5936,30 @@ nsGridContainerFrame::Reflow(nsPresConte
     RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
   }
   const nscoord computedBSize = aReflowInput.ComputedBSize();
   const nscoord computedISize = aReflowInput.ComputedISize();
   const WritingMode& wm = gridReflowInput.mWM;
   LogicalSize computedSize(wm, computedISize, computedBSize);
 
   nscoord consumedBSize = 0;
-  nscoord bSize;
+  nscoord bSize = 0;
   if (!prevInFlow) {
     Grid grid;
     grid.PlaceGridItems(gridReflowInput, aReflowInput.ComputedMinSize(),
                         computedSize, aReflowInput.ComputedMaxSize());
 
     gridReflowInput.CalculateTrackSizes(grid, computedSize,
                                         SizingConstraint::eNoConstraint);
-    bSize = computedSize.BSize(wm);
+    // Note: we can't use GridLineEdge here since we haven't calculated
+    // the rows' mPosition yet (happens in AlignJustifyContent below).
+    for (const auto& sz : gridReflowInput.mRows.mSizes) {
+      bSize += sz.mBase;
+    }
+    bSize += gridReflowInput.mRows.SumOfGridGaps();
   } else {
     consumedBSize = ConsumedBSize(wm);
     gridReflowInput.InitializeForContinuation(this, consumedBSize);
     const uint32_t numRows = gridReflowInput.mRows.mSizes.Length();
     bSize = gridReflowInput.mRows.GridLineEdge(numRows,
                                                GridLineSide::eAfterGridGap);
   }
   if (computedBSize == NS_AUTOHEIGHT) {
@@ -6477,18 +6372,24 @@ nsGridContainerFrame::IntrinsicISize(gfx
   }
   state.mCols.Initialize(state.mColFunctions, state.mGridStyle->mGridColumnGap,
                          grid.mGridColEnd, NS_UNCONSTRAINEDSIZE);
   auto constraint = aType == nsLayoutUtils::MIN_ISIZE ?
     SizingConstraint::eMinContent : SizingConstraint::eMaxContent;
   state.mCols.CalculateSizes(state, state.mGridItems, state.mColFunctions,
                              NS_UNCONSTRAINEDSIZE, &GridArea::mCols,
                              constraint);
-  return state.mCols.BackComputedIntrinsicSize(state.mColFunctions,
-                                               state.mGridStyle->mGridColumnGap);
+  state.mCols.mGridGap =
+    nsLayoutUtils::ResolveGapToLength(state.mGridStyle->mGridColumnGap,
+                                      NS_UNCONSTRAINEDSIZE);
+  nscoord length = 0;
+  for (const TrackSize& sz : state.mCols.mSizes) {
+    length += sz.mBase;
+  }
+  return length + state.mCols.SumOfGridGaps();
 }
 
 nscoord
 nsGridContainerFrame::GetMinISize(gfxContext* aRC)
 {
   DISPLAY_MIN_WIDTH(this, mCachedMinISize);
   if (mCachedMinISize == NS_INTRINSIC_WIDTH_UNKNOWN) {
     mCachedMinISize = IntrinsicISize(aRC, nsLayoutUtils::MIN_ISIZE);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -20,16 +20,17 @@
    we're midway through this process, so you will see inlined functions and member
    variables in this file.  -dwh */
 
 #include <algorithm>
 #include <stdio.h>
 
 #include "CaretAssociationHint.h"
 #include "FrameProperties.h"
+#include "LayoutConstants.h"
 #include "mozilla/layout/FrameChildList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/SmallPointerArray.h"
 #include "mozilla/WritingModes.h"
 #include "nsDirection.h"
 #include "nsFrameList.h"
 #include "nsFrameState.h"
 #include "mozilla/ReflowOutput.h"
@@ -135,40 +136,22 @@ typedef uint32_t nsSplittableType;
 #define NS_FRAME_SPLITTABLE_NON_RECTANGULAR 0x3
 
 #define NS_FRAME_IS_SPLITTABLE(type)\
   (0 != ((type) & NS_FRAME_SPLITTABLE))
 
 #define NS_FRAME_IS_NOT_SPLITTABLE(type)\
   (0 == ((type) & NS_FRAME_SPLITTABLE))
 
-#define NS_INTRINSIC_WIDTH_UNKNOWN nscoord_MIN
-
 //----------------------------------------------------------------------
 
 #define NS_SUBTREE_DIRTY(_frame)  \
   (((_frame)->GetStateBits() &      \
     (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0)
 
-/**
- * Constant used to indicate an unconstrained size.
- *
- * @see #Reflow()
- */
-#define NS_UNCONSTRAINEDSIZE NS_MAXSIZE
-
-#define NS_INTRINSICSIZE    NS_UNCONSTRAINEDSIZE
-#define NS_AUTOHEIGHT       NS_UNCONSTRAINEDSIZE
-// +1 is to avoid clamped huge margin values being processed as auto margins
-#define NS_AUTOMARGIN       (NS_UNCONSTRAINEDSIZE + 1)
-#define NS_AUTOOFFSET       NS_UNCONSTRAINEDSIZE
-// NOTE: there are assumptions all over that these have the same value, namely NS_UNCONSTRAINEDSIZE
-//       if any are changed to be a value other than NS_UNCONSTRAINEDSIZE
-//       at least update AdjustComputedHeight/Width and test ad nauseum
-
 // 1 million CSS pixels less than our max app unit measure.
 // For reflowing with an "infinite" available inline space per [css-sizing].
 // (reflowing with an NS_UNCONSTRAINEDSIZE available inline size isn't allowed
 //  and leads to assertions)
 #define INFINITE_ISIZE_COORD nscoord(NS_MAXSIZE - (1000000*60))
 
 //----------------------------------------------------------------------
 
@@ -2350,33 +2333,37 @@ public:
    */
   virtual void
   AddInlinePrefISize(gfxContext *aRenderingContext,
                      InlinePrefISizeData *aData) = 0;
 
   /**
    * Return the horizontal components of padding, border, and margin
    * that contribute to the intrinsic width that applies to the parent.
+   * @param aPercentageBasis the percentage basis to use for padding/margin -
+   *   i.e. the Containing Block's inline-size
    */
   struct IntrinsicISizeOffsetData {
     nscoord hPadding, hBorder, hMargin;
-    float hPctPadding, hPctMargin;
 
     IntrinsicISizeOffsetData()
       : hPadding(0), hBorder(0), hMargin(0)
-      , hPctPadding(0.0f), hPctMargin(0.0f)
     {}
   };
-  virtual IntrinsicISizeOffsetData IntrinsicISizeOffsets() = 0;
+  virtual IntrinsicISizeOffsetData
+  IntrinsicISizeOffsets(nscoord aPercentageBasis = NS_UNCONSTRAINEDSIZE) = 0;
 
   /**
    * Return the bsize components of padding, border, and margin
    * that contribute to the intrinsic width that applies to the parent.
-   */
-  IntrinsicISizeOffsetData IntrinsicBSizeOffsets();
+   * @param aPercentageBasis the percentage basis to use for padding/margin -
+   *   i.e. the Containing Block's inline-size
+   */
+  IntrinsicISizeOffsetData
+  IntrinsicBSizeOffsets(nscoord aPercentageBasis = NS_UNCONSTRAINEDSIZE);
 
   virtual mozilla::IntrinsicSize GetIntrinsicSize() = 0;
 
   /**
    * Get the intrinsic ratio of this element, or nsSize(0,0) if it has
    * no intrinsic ratio.  The intrinsic ratio is the ratio of the
    * height/width of a box with an intrinsic size or the intrinsic
    * aspect ratio of a scalable vector image without an intrinsic size.
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6890,17 +6890,17 @@ nsDisplayTableBlendContainer::CreateForB
 {
   return MakeDisplayItem<nsDisplayTableBlendContainer>(aBuilder, aFrame, aList, aActiveScrolledRoot, true, aAncestorFrame);
 }
 
 nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList,
                                      const ActiveScrolledRoot* aActiveScrolledRoot,
                                      nsDisplayOwnLayerFlags aFlags, ViewID aScrollTarget,
-                                     const ScrollThumbData& aThumbData,
+                                     const ScrollbarData& aThumbData,
                                      bool aForceActive,
                                      bool aClearClipChain)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, aClearClipChain)
     , mFlags(aFlags)
     , mScrollTarget(aScrollTarget)
     , mThumbData(aThumbData)
     , mForceActive(aForceActive)
     , mWrAnimationId(0)
@@ -6932,17 +6932,17 @@ nsDisplayOwnLayer::GetLayerState(nsDispl
   }
 
   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, mAnimatedGeometryRoot);
 }
 
 bool
 nsDisplayOwnLayer::IsScrollThumbLayer() const
 {
-  return mThumbData.mDirection.isSome();
+  return mThumbData.mScrollbarLayerType == layers::ScrollbarLayerType::Thumb;
 }
 
 bool
 nsDisplayOwnLayer::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) const
 {
   // Render scroll thumb layers even if they are invisible, because async
   // scrolling might bring them into view.
   return IsScrollThumbLayer();
@@ -6954,17 +6954,18 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayL
                               LayerManager* aManager,
                               const ContainerLayerParameters& aContainerParameters)
 {
   RefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            aContainerParameters, nullptr,
                            FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR);
   if (IsScrollThumbLayer()) {
-    layer->SetScrollThumbData(mScrollTarget, mThumbData);
+    mThumbData.mTargetViewId = mScrollTarget;
+    layer->SetScrollbarData(mThumbData);
   }
   if (mFlags & nsDisplayOwnLayerFlags::eScrollbarContainer) {
     ScrollDirection dir = (mFlags & nsDisplayOwnLayerFlags::eVerticalScrollbar)
                         ? ScrollDirection::eVertical
                         : ScrollDirection::eHorizontal;
     layer->SetScrollbarContainer(mScrollTarget, dir);
   }
 
@@ -7010,17 +7011,17 @@ nsDisplayOwnLayer::CreateWebRenderComman
 bool
 nsDisplayOwnLayer::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
                                     mozilla::layers::WebRenderLayerScrollData* aLayerData)
 {
   bool ret = false;
   if (IsScrollThumbLayer()) {
     ret = true;
     if (aLayerData) {
-      aLayerData->SetScrollThumbData(mThumbData);
+      aLayerData->SetScrollbarData(mThumbData);
       aLayerData->SetScrollbarAnimationId(mWrAnimationId);
       aLayerData->SetScrollbarTargetContainerId(mScrollTarget);
     }
   }
   if (mFlags & nsDisplayOwnLayerFlags::eScrollbarContainer) {
     ret = true;
     if (aLayerData) {
       ScrollDirection dir = (mFlags & nsDisplayOwnLayerFlags::eVerticalScrollbar)
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -5473,17 +5473,17 @@ enum class nsDisplayOwnLayerFlags {
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsDisplayOwnLayerFlags)
 
 /**
  * A display item that has no purpose but to ensure its contents get
  * their own layer.
  */
 class nsDisplayOwnLayer : public nsDisplayWrapList {
 public:
-  typedef mozilla::layers::ScrollThumbData ScrollThumbData;
+  typedef mozilla::layers::ScrollbarData ScrollbarData;
 
   /**
    * @param aFlags eGenerateSubdocInvalidations :
    * Add UserData to the created ContainerLayer, so that invalidations
    * for this layer are send to our nsPresContext.
    * eGenerateScrollableLayer : only valid on nsDisplaySubDocument (and
    * subclasses), indicates this layer is to be a scrollable layer, so call
    * ComputeFrameMetrics, etc.
@@ -5491,17 +5491,17 @@ public:
    * is set in the flags, this parameter should be the ViewID of the
    * scrollable content this scrollbar is for.
    */
   nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList,
                     const ActiveScrolledRoot* aActiveScrolledRoot,
                     nsDisplayOwnLayerFlags aFlags = nsDisplayOwnLayerFlags::eNone,
                     ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
-                    const ScrollThumbData& aThumbData = ScrollThumbData{},
+                    const ScrollbarData& aThumbData = ScrollbarData{},
                     bool aForceActive = true,
                     bool aClearClipChain = false);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOwnLayer();
 #endif
   nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, const nsDisplayOwnLayer& aOther)
     : nsDisplayWrapList(aBuilder, aOther)
     , mFlags(aOther.mFlags)
@@ -5546,17 +5546,17 @@ public:
   NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)
 protected:
   nsDisplayOwnLayerFlags mFlags;
   ViewID mScrollTarget;
   // If this nsDisplayOwnLayer represents a scroll thumb layer, mThumbData
   // stores information about the scroll thumb. Otherwise, mThumbData will be
   // default-constructed (in particular with mDirection == Nothing())
   // and can be ignored.
-  ScrollThumbData mThumbData;
+  ScrollbarData mThumbData;
   bool mForceActive;
   uint64_t mWrAnimationId;
 };
 
 /**
  * A display item for subdocuments. This is more or less the same as nsDisplayOwnLayer,
  * except that it always populates the FrameMetrics instance on the ContainerLayer it
  * builds.
--- a/layout/reftests/bugs/403519-2-ref.html
+++ b/layout/reftests/bugs/403519-2-ref.html
@@ -12,17 +12,17 @@ table {
   width: 200px;
   border-spacing: 0;
 }
 </style>
 </head>
 <body>
 <table>
   <tbody><tr>
-    <td colspan="2" style="width: 100%;"><div>&nbsp;</div></td>
+    <td colspan="2"><div>&nbsp;</div></td>
     <td>b</td>
   </tr>
   <tr>
     <td>&nbsp;</td>
     <td>&nbsp;</td>
     <td>&nbsp;</td>
   </tr>
 </tbody></table>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -551,19 +551,19 @@ fuzzy-if(skiaContent,2,4) == 362594-2c.h
 skip-if(Android) == 363706-1.html 363706-1-ref.html
 != 363706-1.html about:blank
 == 363728-1.html 363728-1-ref.html
 == 363728-2.html 363728-2-ref.html
 fuzzy-if(skiaContent||Android,4,11) == 363858-1.html 363858-1-ref.html
 == 363858-2.html 363858-2-ref.html
 == 363858-3.html 363858-3-ref.html
 == 363858-4.html 363858-4-ref.html
-fuzzy-if(OSX,45,2) fuzzy-if(winWidget,114,1) fails-if(webrender&&winWidget) == 363858-5a.html 363858-5-ref.html
+# fuzzy-if(OSX,45,2) fuzzy-if(winWidget,114,1) fails-if(webrender&&winWidget) == 363858-5a.html 363858-5-ref.html # bug 1452797
 == 363858-5b.html 363858-5-ref.html
-fuzzy-if(OSX,45,2) fuzzy-if(winWidget,114,1) fails-if(webrender&&winWidget) == 363858-6a.html 363858-6-ref.html
+# fuzzy-if(OSX,45,2) fuzzy-if(winWidget,114,1) fails-if(webrender&&winWidget) == 363858-6a.html 363858-6-ref.html # bug 1452797
 == 363858-6b.html 363858-6-ref.html
 == 363874.html 363874-ref.html
 == 363874-max-width.html 363874-max-width-ref.html
 == 364066-1.html 364066-1-ref.html
 == 364079-1.html 364079-1-ref.html
 == 364318-1.xhtml 364318-1-ref.xhtml
 == 364861-1.html 364861-1-ref.html
 == 364862-1.html 364862-1-ref.html
--- a/layout/reftests/css-grid/grid-auto-min-sizing-definite-001-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-definite-001-ref.html
@@ -60,55 +60,73 @@ b40 {
   height: 40px;
   border: 1px solid pink;
   z-index: 1; position:relative;
 }
 
 .h.r {
   height: 42px;
   width: 42px;
-  /* This margin-left is 20% of 98px-wide grid area */
-  margin-left: 19.6px;
-  /* This padding-bottom is 10% of 98px wide grid area */
-  /* This padding-left   is 30% of 98px wide grid area */
-  padding: 1px 3px 9.8px 29.4px;
+  /* 49px is the percentage basis, i.e. the column size */
+  margin-left: calc(0.2 * 49px);
+  padding: 1px 3px calc(0.1 * 49px) calc(0.3 * 49px);
 }
 .v.r {
   height: 42px;
   width: 42px;
   /* This margin-left is 20% of 54px-wide grid area */
-  margin-left: 10.8px;
-  /* This padding-bottom is 10% of 54px wide grid area */
-  /* This padding-left   is 30% of 54px wide grid area */
-  padding: 1px 3px 5.4px 16.2px;
+  /* 27px is the percentage basis, i.e. the column size */
+  margin-left: calc(0.2 * 27px);
+  padding: 1px 3px calc(0.1 * 27px) calc(0.3 * 27px);
 }
 
 .r { position:relative; }
 
+.t4 { width: 49px;
+      height: calc(10px /* item min-height */ +
+                   7px  /* item margin-top */ +
+                   1px /* item padding-top */ +
+                   1px /* item border-top */ +
+                   calc(0.5 * 49px) /* item margin-bottom */ +
+                   calc(0.1 * 49px) /* item padding-bottom */);
+    }
 .t6 { width:46px; }
-.t8 { width:118px; height: 102.5px; }
+.t8 { width: 27px;
+      height: calc(30px /* item min-height */ +
+                   7px  /* item margin-top */ +
+                   3px /* item padding-top */ +
+                   1px /* item border-top */ +
+                   calc(0.5 * 27px) /* item margin-bottom */ +
+                   calc(0.1 * 27px) /* item padding-bottom */);
+    }
+   
 
 xx {
   display: block;
   background: lime;
-  padding:32px 32px 16px 32px;
-  margin: 0 0 32px 16px;
+  padding: 32px 32px calc(0.2 * 32px) calc(0.4 * 32px);
+  margin: 0 0 calc(0.4 * 32px) calc(0.2 * 32px);
+}
+.t9, .t10 {
+  position: relative;
+  z-index: 1;
+  grid: calc(32px + calc(0.4 * 32px) + calc(0.2 * 32px)) 0 / 32px;
 }
   </style>
 </head>
 <body>
 
 
 <div class="grid"><span class="h"><x></x></span></div>
 <div class="grid"><span class="h bb"><x></x></span></div>
 <div class="grid"><span class="h"><x></x></span><span class="h"><x></x></span></div>
-<div class="grid" style="grid:48px / 122px"><span class="h r"><b40></b40></span></div>
+<div class="grid t4"><span class="h r"><b40></b40></span></div>
 <br>
 <div class="grid"><span class="v"><x></x></span></div>
-<div class="grid t6"><span class="v bb"><x></x></span></div>
+<div class="grid"><span class="v bb"><x></x></span></div>
 <div class="grid"><span class="v"><x></x></span><span class="v"><x></x></span></div>
 <div class="grid t8"><span class="v r"><b40></b40></span></div>
 
-<div class="grid"><xx class="v"></xx></div>
-<div class="grid v"><xx class="h"></xx></div>
+<div class="grid t9"><xx class="v"></xx></div>
+<div class="grid v t10"><xx class="h"></xx></div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html
@@ -31,17 +31,17 @@ br { clear:both; }
 <script>
 var coltest = [
 "height:30px; max-width:200%", "height:30px; width:200%", "height:30px; width:50%",
 "width:50%", "max-width:50%", "height:30px", "height:200%; width:80px", "height:50%",
 "height:50%; width:20px", "max-height:50%", "max-height:50%; max-width:6px",
 "min-height:40%", "min-height:40%; max-width:30px"
 ];
 var results = [
-"360px", "360px", "360px", "24px", "24px", "360px", "80px", "24px", "24px", "24px",
+"24px", "24px", "24px", "24px", "24px", "24px", "80px", "24px", "24px", "24px",
 "24px", "24px", "24px"
 ];
 var item_width = [
 "48px", "48px", "24px", "24px", "24px;max-width:none;", "360px", "80px", "60px", "24px", "24px",
 "24px;max-width:none;", "48px", "30px"
 ];
 
 var h3 = document.createElement('h3');
--- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html
@@ -31,17 +31,17 @@ br { clear:both; }
 
 <script>
 var rowtest = [
 "width:50%; max-height:200%", "width:50%; height:200%", "width:4px; height:50%",
 "height:50%", "max-height:200%", "max-height:50%", "max-width:50%", "width:50%; height:20px",
 "min-width:80%; max-height:20px", "min-width:50%", "margin-left: 50px; width:50%"
 ];
 var results = [
-"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px"
+"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "312px/52px"
 ];
 var item_height = [
 "0", "0", "0", "0", "0", "0", "12px", "20px", "20px", "24px", "312px"
 ];
 
 var h3 = document.createElement('h3');
 h3.appendChild(document.createTextNode("grid-template-rows:minmax(auto,0.0001fr)"));
 document.body.appendChild(h3);
--- a/layout/reftests/css-grid/grid-auto-min-sizing-percent-001-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-percent-001-ref.html
@@ -49,19 +49,19 @@ br { clear:both; }
 .c200 { grid-template-columns: 200px; }
 #px-border .c100 { grid-template-columns: minmax(120px,0) 1fr; }
 #px-border .c100calc100 { grid-template-columns: minmax(120px,0) 1fr; }
 #px-border .c100100 { grid-template-columns: minmax(120px,0) 120px; }
 #px-border .c200 { grid-template-columns: 240px; }
 .c10 { grid-template-columns: minmax(10px,0) 1fr; }
 #px-border .c10 { grid-template-columns: minmax(30px,0) 1fr; }
 
-#percent-border .c100 { grid-template-columns: 111px 0; }
-#percent-border .c100calc100 { grid-template-columns: 100px 11px; }
-#percent-border .c10 { grid-template-columns: minmax(11px,0) 0; }
+#percent-border .c100 { grid-template-columns: 100px 0; }
+#percent-border .c100calc100 { grid-template-columns: 100px 10px; }
+#percent-border .c10 { grid-template-columns: minmax(10px,0) 0; }
 #percent-border .c100100 { grid-template-columns: minmax(100px,0) 150px; }
 #percent-border .c200 { grid-template-columns: 250px; }
   </style>
 </head>
 <body>
 
 <table border="1">
 <tr><th>no border/padding/margin</th><th>'border-left:20px'</th><th>'padding-left:10%'</th>
@@ -94,28 +94,28 @@ var styles = [
 "min-width:calc(100px + 50%); max-width:150px",
 ];
 var grids = [
 "grid",
 "grid",
 "grid c100",
 "grid c100",
 "grid",
-"grid c200",
+"grid c100",
 "grid c10",
 "grid c100",
 "grid c100",
 "grid",
 "grid",
 "grid",
 "grid",
 "grid c100",
 "grid c100",
 "grid",
-"grid c200",
+"grid c100",
 "grid c10",
 "grid c100",
 "grid c100",
 "grid",
 "grid",
 ];
 var containers = [ "no-border", "px-border", "percent-border" ];
 for (var i = 0; i < containers.length; ++i) {
--- a/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-002-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-002-ref.html
@@ -31,17 +31,17 @@ br { clear:both; }
 <script>
 var coltest = [
 "height:30px; max-width:200%", "height:30px; width:200%", "height:30px; width:50%",
 "width:50%", "max-width:50%", "height:30px", "height:200%; width:80px", "height:50%",
 "height:50%; width:20px", "max-height:50%", "max-height:50%; max-width:6px",
 "min-height:40%", "min-height:40%; max-width:30px"
 ];
 var results = [
-"360px", "360px", "360px", "24px", "24px", "360px", "80px", "24px", "20px", "24px",
+"360px", "0px", "0px", "0px", "24px", "360px", "80px", "24px", "20px", "24px",
 "6px", "24px", "24px"
 ];
 var item_width = [
 "0px", "0px", "0px", "0px", "0px", "360px", "80px", "60px", "20px", "24px",
 "6px", "48px", "30px"
 ];
 
 var h3 = document.createElement('h3');
--- a/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html
+++ b/layout/reftests/css-grid/grid-auto-min-sizing-transferred-size-004-ref.html
@@ -31,17 +31,17 @@ br { clear:both; }
 
 <script>
 var rowtest = [
 "width:50%; max-height:200%", "width:50%; height:200%", "width:4px; height:50%",
 "height:50%", "max-height:200%", "max-height:50%", "max-width:50%", "width:50%; height:20px",
 "min-width:80%; max-height:20px", "min-width:50%", "margin-left: 50px; width:50%"
 ];
 var results = [
-"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "24px/52px"
+"0/2px", "0/2px", "0/4px", "0/2px", "0/2px", "0/2px", "12px/2px", "20px/2px", "20px/2px", "24px/2px", "312px/52px"
 ];
 var item_height = [
 "0", "0", "0", "0", "0", "0", "12px", "20px", "20px", "24px", "312px"
 ];
 
 var h3 = document.createElement('h3');
 h3.appendChild(document.createTextNode("grid-template-rows:minmax(auto,0.0001fr)"));
 document.body.appendChild(h3);
--- a/layout/reftests/css-grid/grid-item-sizing-percent-003-ref.html
+++ b/layout/reftests/css-grid/grid-item-sizing-percent-003-ref.html
@@ -64,74 +64,74 @@ a {
   background: blue;
 }
 </style>
 </head>
 <body>
 
 <div style="float:left">
 <div class="grid" style="grid-template-rows:calc(12px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-rows:calc(18px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(21px/0.9)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:26px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left" class="maxw">
 <div class="grid" style="grid-template-rows:calc(12px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-rows:calc(18px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(21px/.9)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:26px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left" class="minw">
 <div class="grid" style="grid-template-rows:calc(12px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-rows:calc(18px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(21px/.9)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(21px/.9)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:26px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 36px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left">
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(22.5px)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(30px)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(30px)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:28px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:34px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 54px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <br clear="all">
 
 <div style="float:left" class="maxw">
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(22.5px)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(30px)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(30px)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:28px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:34px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 54px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left" class="minw">
-<div class="grid" style="grid-template-rows:calc(15px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(15px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(22.5px)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:calc(30px)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-rows:0 calc(30px)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:20px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 30px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:28px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:34px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-rows:0 54px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-item-sizing-percent-004-ref.html
+++ b/layout/reftests/css-grid/grid-item-sizing-percent-004-ref.html
@@ -57,78 +57,78 @@ a {
   background: blue;
 }
 </style>
 </head>
 <body>
 
 <div style="float:left">
 <div class="grid" style="grid-template-columns:calc(10px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(18.75px)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(18.75px)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:15px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 15px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-columns:calc(16px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(26.25px)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(51.25px)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(26.25px)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:21px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 41px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 21px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <br clear="all">
 
 <div style="float:left" class="maxw">
 <div class="grid" style="grid-template-columns:calc(30px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(43.75px)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:35px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-columns:calc(36px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(51.25px)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(51.25px)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(51.25px)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:41px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 41px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 41px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <br clear="all">
 
 <div style="float:left" class="minw">
 <div class="grid" style="grid-template-columns:calc(10px)"><div class="item"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(18.75px)"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(43.75px)"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(18.75px)"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:15px"><div class="item pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox p"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 15px"><div class="item c2 pbox p"><x></x><x></x><x></x></div><a></a></div>
 <div class="grid" style="grid-template-columns:calc(16px)"><div class="item pbox b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(26.25px)"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(51.25px)"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(26.25px)"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:21px"><div class="item pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 41px"><div class="item c1 pbox p b"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 21px"><div class="item c2 pbox p b"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left">
-<div class="grid" style="grid-template-columns:calc(15px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(35px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(15px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(20px/.7)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(37.15px)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(37.15px)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:15px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 15px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:20px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:26px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 26px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <br clear="all">
 
 <div style="float:left" class="maxw">
-<div class="grid" style="grid-template-columns:calc(35px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(35px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(35px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(57.15px)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(46px/.7)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(46px/.7)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:35px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:40px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:46px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 46px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 <div style="float:left" class="minw">
-<div class="grid" style="grid-template-columns:calc(15px/.9)"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(35px/.9)"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(15px/.9)"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(20px/.7)"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:calc(37.15px)"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
-<div class="grid" style="grid-template-columns:0 calc(37.15px)"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:15px"><div class="item pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 35px"><div class="item c1 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 15px"><div class="item c2 pbox m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:20px"><div class="item pbox p m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:26px"><div class="item pbox p b m"><x></x><x></x><x></x></div><a></a></div>
+<div class="grid" style="grid-template-columns:0 26px"><div class="item c2 pbox p b m"><x></x><x></x><x></x></div><a></a></div>
 </div>
 
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-percent-grid-gap-001-ref.html
+++ b/layout/reftests/css-grid/grid-percent-grid-gap-001-ref.html
@@ -60,26 +60,26 @@ br { clear: both; }
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
 </div>
 </div>
 
 <div class="float">
-<div class="grid" style="align-self:start; justify-self:start; grid-gap:15px">
+<div class="grid" style="width:60px; height:60px; grid-column-gap:12px">
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
 </div>
 </div>
 
 <div class="inline-grid">
-<div class="grid" style="align-self:start; justify-self:start; grid-gap:15px">
+<div class="grid" style="width:60px; height:60px; grid-column-gap:12px; align-self:start; justify-self:start;">
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
 </div>
 </div>
 
 <br>
@@ -136,19 +136,19 @@ br { clear: both; }
 <div class="grid calcgap" style="height:120px; grid-gap:0">
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
   <span><x></x></span>
 </div>
 </div>
 
-<div class="float" style="margin-top:-50px">
-<div class="grid" style="min-width:300%; grid-gap:15px 15px">
-  <span><x></x></span>
-  <span style="margin-left:31px; width:30px"><x></x></span>
-  <span><x></x></span>
-  <span style="margin-left:31px; width:30px"><x></x></span>
+<div class="float" style="margin-top:-50px; width:62px">
+<div class="grid" style="padding-left:186px; height:60px; grid-column-gap:calc(186px * 0.2);">
+  <span style="margin-left:-186px; width:30px"><x></x></span>
+  <span style="margin-left:-186px; width:30px"><x></x></span>
+  <span style="margin-left:-186px; width:30px"><x></x></span>
+  <span style="margin-left:-186px; width:30px"><x></x></span>
 </div>
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
@@ -78,22 +78,26 @@ x:last-child {
 
 fill,fit {
   float: left;
   height: 400px;
 }
 
 .zero-progress {
   grid-row-gap: calc(10px - 1%);
-  grid-template-rows: [a] 10px repeat(4, [b] minmax(0,auto) [c]) [d];
+  grid-template-rows: [a] 10px repeat(3, [b] 0 [c]) [d];
 }
 .w50.zero-progress {
   grid-row-gap: calc(10px - 1%);
   grid-template-rows: [a] 10px repeat(3, [b] 0 [c]) [d];
 }
+.mw50.zero-progress {
+  grid-row-gap: calc(10px - 1%);
+  grid-template-rows: [a] 10px repeat(4, [b] 0 [c]) [d];
+}
 </style>
 </head>
 <body>
 
 <fill>
 
 <div class="grid r1"><x></x><x></x><a></a><b></b><x></x></div>
 <div class="grid r1 float"><x></x><x></x><a></a><b></b><x></x></div>
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -47,29 +47,29 @@ fails == grid-item-sizing-percent-002.ht
 == grid-item-sizing-percent-003.html grid-item-sizing-percent-003-ref.html
 == grid-item-sizing-percent-004.html grid-item-sizing-percent-004-ref.html
 == grid-item-sizing-px-001.html grid-item-sizing-percent-001-ref.html
 == grid-item-dir-001.html grid-item-dir-001-ref.html
 fuzzy-if(winWidget,70,130) fuzzy-if(cocoaWidget,85,180) == grid-col-max-sizing-max-content-001.html grid-col-max-sizing-max-content-001-ref.html
 fuzzy-if(winWidget,70,130) fuzzy-if(cocoaWidget,85,180) == grid-col-max-sizing-max-content-002.html grid-col-max-sizing-max-content-002-ref.html
 == grid-min-max-content-sizing-001.html grid-min-max-content-sizing-001-ref.html
 == grid-min-max-content-sizing-002.html grid-min-max-content-sizing-002-ref.html
-# fails fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min-sizing-definite-001-ref.html
+fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min-sizing-definite-001-ref.html
 == grid-auto-min-sizing-intrinsic-001.html grid-auto-min-sizing-intrinsic-001-ref.html
 == grid-auto-min-sizing-intrinsic-002.html grid-auto-min-sizing-intrinsic-002-ref.html
 == grid-auto-min-sizing-intrinsic-003.html grid-auto-min-sizing-intrinsic-003-ref.html
 == grid-auto-min-sizing-intrinsic-004.html grid-auto-min-sizing-intrinsic-004-ref.html
 == grid-auto-min-sizing-transferred-size-001.html grid-auto-min-sizing-transferred-size-001-ref.html
-fails == grid-auto-min-sizing-transferred-size-002.html grid-auto-min-sizing-transferred-size-002-ref.html
+== grid-auto-min-sizing-transferred-size-002.html grid-auto-min-sizing-transferred-size-002-ref.html
 == grid-auto-min-sizing-transferred-size-003.html grid-auto-min-sizing-transferred-size-003-ref.html
-fails == grid-auto-min-sizing-transferred-size-004.html grid-auto-min-sizing-transferred-size-004-ref.html
+== grid-auto-min-sizing-transferred-size-004.html grid-auto-min-sizing-transferred-size-004-ref.html
 == grid-auto-min-sizing-min-content-min-size-001.html grid-auto-min-sizing-min-content-min-size-001-ref.html
-fails == grid-auto-min-sizing-min-content-min-size-002.html grid-auto-min-sizing-min-content-min-size-002-ref.html
+== grid-auto-min-sizing-min-content-min-size-002.html grid-auto-min-sizing-min-content-min-size-002-ref.html
 == grid-auto-min-sizing-min-content-min-size-003.html grid-auto-min-sizing-min-content-min-size-003-ref.html
-fails == grid-auto-min-sizing-min-content-min-size-004.html grid-auto-min-sizing-min-content-min-size-004-ref.html
+== grid-auto-min-sizing-min-content-min-size-004.html grid-auto-min-sizing-min-content-min-size-004-ref.html
 == grid-min-content-min-sizing-transferred-size-001.html grid-min-content-min-sizing-transferred-size-001-ref.html
 == grid-min-content-min-sizing-transferred-size-002.html grid-min-content-min-sizing-transferred-size-002-ref.html
 == grid-min-content-min-sizing-transferred-size-003.html grid-min-content-min-sizing-transferred-size-003-ref.html
 == grid-min-content-min-sizing-transferred-size-004.html grid-min-content-min-sizing-transferred-size-004-ref.html
 skip-if(Android) == grid-auto-min-sizing-percent-001.html grid-auto-min-sizing-percent-001-ref.html # bug 1305716
 == grid-track-intrinsic-sizing-001.html grid-track-intrinsic-sizing-001-ref.html
 == grid-track-intrinsic-sizing-002.html grid-track-intrinsic-sizing-002-ref.html
 == grid-track-intrinsic-sizing-003.html grid-track-intrinsic-sizing-003-ref.html
--- a/layout/reftests/writing-mode/1174450-intrinsic-sizing-ref.html
+++ b/layout/reftests/writing-mode/1174450-intrinsic-sizing-ref.html
@@ -8,30 +8,30 @@
   <title>Testcase for bug 1174450</title>
   <style type="text/css">
 body,html { color:black; background:white; font-size:12px; line-height:16px; padding:0; margin:0; }
 	
 div.v, div.h {
   display: block;
   position: relative;
   border: 1px dashed silver;
-  width:92px;
-  height:60px;
+  width:74px;
+  height:24px;
 }
 div.h {
-  width:124px;
-  height:98px;
+  width:62px;
+  height:61.2px;
 }
 .h span { 
-  margin: 7px 13px 62px 25px;
-  padding: 1px 3px 12px 37px;
+  margin: 7px 13px 32px 12px;
+  padding: 1px 3px 6px 19px;
 }
 .v span { 
-  margin: 7px 13px 30px 12px;
-  padding: 1px 3px 6px 18px;
+  margin: 7px 13px 30px 5px;
+  padding: 1px 3px 2px 7px;
 }
 
 span { 
   display: block;
   position: absolute;
   width: 30px;
   height: 10px;
   background: lime;
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -772,22 +772,22 @@ nsTableCellFrame::GetPrefISize(gfxContex
 
   nsIFrame *inner = mFrames.FirstChild();
   result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
                                                 nsLayoutUtils::PREF_ISIZE);
   return result;
 }
 
 /* virtual */ nsIFrame::IntrinsicISizeOffsetData
-nsTableCellFrame::IntrinsicISizeOffsets()
+nsTableCellFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis)
 {
-  IntrinsicISizeOffsetData result = nsContainerFrame::IntrinsicISizeOffsets();
+  IntrinsicISizeOffsetData result =
+    nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
 
   result.hMargin = 0;
-  result.hPctMargin = 0;
 
   WritingMode wm = GetWritingMode();
   result.hBorder = GetBorderWidth(wm).IStartEnd(wm);
 
   return result;
 }
 
 #ifdef DEBUG
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -105,17 +105,18 @@ public:
                                 const nsDisplayListSet& aLists) override;
 
   virtual nsresult ProcessBorders(nsTableFrame* aFrame,
                                   nsDisplayListBuilder* aBuilder,
                                   const nsDisplayListSet& aLists);
 
   virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override;
-  virtual IntrinsicISizeOffsetData IntrinsicISizeOffsets() override;
+  IntrinsicISizeOffsetData IntrinsicISizeOffsets(nscoord aPercentageBasis =
+                                                 NS_UNCONSTRAINEDSIZE) override;
 
   virtual void Reflow(nsPresContext*      aPresContext,
                       ReflowOutput& aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus&      aStatus) override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1766,26 +1766,25 @@ nsTableFrame::GetPrefISize(gfxContext *a
     CalcBCBorders();
 
   ReflowColGroups(aRenderingContext);
 
   return LayoutStrategy()->GetPrefISize(aRenderingContext, false);
 }
 
 /* virtual */ nsIFrame::IntrinsicISizeOffsetData
-nsTableFrame::IntrinsicISizeOffsets()
-{
-  IntrinsicISizeOffsetData result = nsContainerFrame::IntrinsicISizeOffsets();
+nsTableFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis)
+{
+  IntrinsicISizeOffsetData result =
+    nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
 
   result.hMargin = 0;
-  result.hPctMargin = 0;
 
   if (IsBorderCollapse()) {
     result.hPadding = 0;
-    result.hPctPadding = 0;
 
     WritingMode wm = GetWritingMode();
     LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
     result.hBorder = outerBC.IStartEnd(wm);
   }
 
   return result;
 }
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -303,17 +303,18 @@ public:
                                            const mozilla::layers::StackingContextHelper& aSc,
                                            const nsPoint& aPt);
 
   virtual void MarkIntrinsicISizesDirty() override;
   // For border-collapse tables, the caller must not add padding and
   // border to the results of these functions.
   virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override;
-  virtual IntrinsicISizeOffsetData IntrinsicISizeOffsets() override;
+  IntrinsicISizeOffsetData IntrinsicISizeOffsets(nscoord aPercentageBasis =
+                                                 NS_UNCONSTRAINEDSIZE) override;
 
   virtual mozilla::LogicalSize
   ComputeSize(gfxContext*                 aRenderingContext,
               mozilla::WritingMode        aWM,
               const mozilla::LogicalSize& aCBSize,
               nscoord                     aAvailableISize,
               const mozilla::LogicalSize& aMargin,
               const mozilla::LogicalSize& aBorder,
--- a/layout/xul/BoxObject.cpp
+++ b/layout/xul/BoxObject.cpp
@@ -9,21 +9,17 @@
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsContainerFrame.h"
 #include "nsIDocShell.h"
 #include "nsReadableUtils.h"
 #include "nsView.h"
-#ifdef MOZ_XUL
-#include "nsIDOMXULElement.h"
-#else
 #include "nsIDOMElement.h"
-#endif
 #include "nsLayoutUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsSupportsPrimitives.h"
 #include "mozilla/dom/Element.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/dom/BoxObjectBinding.h"
 
 // Implementation /////////////////////////////////////////////////////////////////
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -1386,17 +1386,17 @@ nsBoxFrame::BuildDisplayList(nsDisplayLi
 
     DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
 
     // Wrap the list to make it its own layer
     aLists.Content()->AppendToTop(
       MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR,
                                          nsDisplayOwnLayerFlags::eNone,
                                          mozilla::layers::FrameMetrics::NULL_SCROLL_ID,
-                                         mozilla::layers::ScrollThumbData{}, true, true));
+                                         mozilla::layers::ScrollbarData{}, true, true));
   }
 }
 
 void
 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                         const nsDisplayListSet& aLists)
 {
   nsIFrame* kid = mFrames.FirstChild();
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -49,17 +49,17 @@
 #include "mozilla/layers/InputAPZContext.h"
 #include <algorithm>
 
 using namespace mozilla;
 using mozilla::layers::APZCCallbackHelper;
 using mozilla::layers::AsyncDragMetrics;
 using mozilla::layers::InputAPZContext;
 using mozilla::layers::ScrollDirection;
-using mozilla::layers::ScrollThumbData;
+using mozilla::layers::ScrollbarData;
 
 bool nsSliderFrame::gMiddlePref = false;
 int32_t nsSliderFrame::gSnapMultiplier;
 
 // Turn this on if you want to debug slider frames.
 #undef DEBUG_SLIDER
 
 static already_AddRefed<nsIContent>
@@ -455,23 +455,24 @@ nsSliderFrame::BuildDisplayListForChildr
       // Restore the saved clip so it applies to the thumb container layer.
       thumbContentsClipState.Restore();
 
       // Wrap the list to make it its own layer.
       const ActiveScrolledRoot* ownLayerASR = contASRTracker.GetContainerASR();
       aLists.Content()->AppendToTop(
         MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR,
                                            flags, scrollTargetId,
-                                           ScrollThumbData{scrollDirection,
-                                                           GetThumbRatio(),
-                                                           thumbStart,
-                                                           thumbLength,
-                                                           isAsyncDraggable,
-                                                           sliderTrackStart,
-                                                           sliderTrackLength}));
+                                           ScrollbarData{scrollDirection,
+                                                         layers::ScrollbarLayerType::Thumb,
+                                                         GetThumbRatio(),
+                                                         thumbStart,
+                                                         thumbLength,
+                                                         isAsyncDraggable,
+                                                         sliderTrackStart,
+                                                         sliderTrackLength}));
 
       return;
     }
   }
 
   nsBoxFrame::BuildDisplayListForChildren(aBuilder, aLists);
 }
 
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -31,17 +31,16 @@
 #include "nsCSSAnonBoxes.h"
 
 #include "gfxContext.h"
 #include "nsIContent.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsIBoxObject.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNodeList.h"
-#include "nsIDOMXULElement.h"
 #include "nsIDocument.h"
 #include "nsCSSRendering.h"
 #include "nsString.h"
 #include "nsContainerFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsVariant.h"
 #include "nsWidgetsCID.h"
--- a/mobile/android/components/extensions/ext-android.js
+++ b/mobile/android/components/extensions/ext-android.js
@@ -5,17 +5,17 @@
 const getSender = (extension, target, sender) => {
   let tabId = -1;
   if ("tabId" in sender) {
     // The message came from a privileged extension page running in a tab. In
     // that case, it should include a tabId property (which is filled in by the
     // page-open listener below).
     tabId = sender.tabId;
     delete sender.tabId;
-  } else if (target instanceof Ci.nsIDOMXULElement) {
+  } else if (ChromeUtils.getClassName(target) == "XULElement") {
     tabId = tabTracker.getBrowserData(target).tabId;
   }
 
   if (tabId != null && tabId >= 0) {
     let tab = extension.tabManager.get(tabId, null);
     if (tab) {
       sender.tab = tab.convert();
     }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5838,8 +5838,12 @@ pref("layers.omtp.paint-workers", 1);
 #endif
 pref("layers.omtp.release-capture-on-main-thread", false);
 pref("layers.omtp.dump-capture", false);
 
 // Limits the depth of recursive conversion of data when opening
 // a content to view.  This is mostly intended to prevent infinite
 // loops with faulty converters involved.
 pref("general.document_open_conversion_depth_limit", 20);
+
+// If true, touchstart and touchmove listeners on window, document,
+// documentElement and document.body are passive by default.
+pref("dom.event.default_to_passive_touch_listeners", false);
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -254,21 +254,26 @@ var BrowserTestUtils = {
    * @param {xul:browser} browser
    *        A xul:browser.
    * @param {Boolean} includeSubFrames
    *        A boolean indicating if loads from subframes should be included.
    * @param {optional string or function} wantLoad
    *        If a function, takes a URL and returns true if that's the load we're
    *        interested in. If a string, gives the URL of the load we're interested
    *        in. If not present, the first load resolves the promise.
+   * @param {optional boolean} maybeErrorPage
+   *        If true, this uses DOMContentLoaded event instead of load event.
+   *        Also wantLoad will be called with visible URL, instead of
+   *        'about:neterror?...' for error page.
    *
    * @return {Promise}
    * @resolves When a load event is triggered for the browser.
    */
-  browserLoaded(browser, includeSubFrames=false, wantLoad=null) {
+  browserLoaded(browser, includeSubFrames=false, wantLoad=null,
+                maybeErrorPage=false) {
     // Passing a url as second argument is a common mistake we should prevent.
     if (includeSubFrames && typeof includeSubFrames != "boolean") {
       throw("The second argument to browserLoaded should be a boolean.");
     }
 
     // If browser belongs to tabbrowser-tab, ensure it has been
     // inserted into the document.
     let tabbrowser = browser.ownerGlobal.gBrowser;
@@ -284,21 +289,27 @@ var BrowserTestUtils = {
       } else {
         // It's a string.
         return wantLoad == url;
       }
     }
 
     return new Promise(resolve => {
       let mm = browser.ownerGlobal.messageManager;
-      mm.addMessageListener("browser-test-utils:loadEvent", function onLoad(msg) {
+      let eventName = maybeErrorPage
+          ? "browser-test-utils:DOMContentLoadedEvent"
+          : "browser-test-utils:loadEvent";
+      mm.addMessageListener(eventName, function onLoad(msg) {
+        // See testing/mochitest/BrowserTestUtils/content/content-utils.js for
+        // the difference between visibleURL and internalURL.
         if (msg.target == browser && (!msg.data.subframe || includeSubFrames) &&
-            isWanted(msg.data.url)) {
-          mm.removeMessageListener("browser-test-utils:loadEvent", onLoad);
-          resolve(msg.data.url);
+            isWanted(maybeErrorPage
+                     ? msg.data.visibleURL : msg.data.internalURL)) {
+          mm.removeMessageListener(eventName, onLoad);
+          resolve(msg.data.internalURL);
         }
       });
     });
   },
 
   /**
    * Waits for the selected browser to load in a new window. This
    * is most useful when you've got a window that might not have
@@ -475,54 +486,89 @@ var BrowserTestUtils = {
       };
       tabbrowser.addTabsProgressListener(progressListener);
     });
   },
 
   /**
    * Waits for the next browser window to open and be fully loaded.
    *
-   * @param {string} initialBrowserLoaded (optional)
-   *        If set, we will wait until the initial browser in the new
-   *        window has loaded a particular page. If unset, the initial
-   *        browser may or may not have finished loading its first page
-   *        when the resulting Promise resolves.
+   * @param aParams
+   *        {
+   *          url: A string (optional). If set, we will wait until the initial
+   *               browser in the new window has loaded a particular page.
+   *               If unset, the initial browser may or may not have finished
+   *               loading its first page when the resulting Promise resolves.
+   *          anyWindow: True to wait for the url to be loaded in any new
+   *                     window, not just the next one opened.
+   *          maybeErrorPage: See browserLoaded function.
+   *        }
    * @return {Promise}
    *         A Promise which resolves the next time that a DOM window
    *         opens and the delayed startup observer notification fires.
    */
-  async waitForNewWindow(initialBrowserLoaded=null) {
-    let win = await this.domWindowOpened();
-
-    let promises = [
-      TestUtils.topicObserved("browser-delayed-startup-finished",
-                              subject => subject == win),
-    ];
-
-    if (initialBrowserLoaded) {
-      await this.waitForEvent(win, "DOMContentLoaded");
+  waitForNewWindow(aParams = {}) {
+    let {
+      url = null,
+      anyWindow = false,
+      maybeErrorPage = false,
+    } = aParams;
 
-      let browser = win.gBrowser.selectedBrowser;
-
-      // Retrieve the given browser's current process type.
-      let process =
-        browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
-                                : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-      if (win.gMultiProcessBrowser &&
-          !E10SUtils.canLoadURIInProcess(initialBrowserLoaded, process)) {
-        await this.waitForEvent(browser, "XULFrameLoaderCreated");
-      }
-
-      let loadPromise = this.browserLoaded(browser, false, initialBrowserLoaded);
-      promises.push(loadPromise);
+    if (anyWindow && !url) {
+      throw new Error("url should be specified if anyWindow is true");
     }
 
-    await Promise.all(promises);
+    return new Promise(resolve => {
+      let observe = async (win, topic, data) => {
+        if (topic != "domwindowopened") {
+          return;
+        }
+
+        if (!anyWindow) {
+          Services.ww.unregisterNotification(observe);
+        }
+
+        if (url) {
+          await this.waitForEvent(win, "DOMContentLoaded");
+
+          if (win.document.documentURI != "chrome://browser/content/browser.xul") {
+            return;
+          }
+        }
+
+        let promises = [
+          TestUtils.topicObserved("browser-delayed-startup-finished",
+                                  subject => subject == win),
+        ];
 
-    return win;
+        if (url) {
+          let browser = win.gBrowser.selectedBrowser;
+
+          // Retrieve the given browser's current process type.
+          let process =
+              browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
+              : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+          if (win.gMultiProcessBrowser &&
+              !E10SUtils.canLoadURIInProcess(url, process)) {
+            await this.waitForEvent(browser, "XULFrameLoaderCreated");
+          }
+
+          let loadPromise = this.browserLoaded(browser, false, url, maybeErrorPage);
+          promises.push(loadPromise);
+        }
+
+        await Promise.all(promises);
+
+        if (anyWindow) {
+          Services.ww.unregisterNotification(observe);
+        }
+        resolve(win);
+      };
+      Services.ww.registerNotification(observe);
+    });
   },
 
   /**
    * Loads a new URI in the given browser and waits until we really started
    * loading. In e10s browser.loadURI() can be an asynchronous operation due
    * to having to switch the browser's remoteness and keep its shistory data.
    *
    * @param {xul:browser} browser
--- a/testing/mochitest/BrowserTestUtils/content/content-utils.js
+++ b/testing/mochitest/BrowserTestUtils/content/content-utils.js
@@ -1,14 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+addEventListener("DOMContentLoaded", function(event) {
+  let subframe = event.target != content.document;
+  // For error page, internalURL is 'about:neterror?...', and visibleURL
+  // is the original URL.
+  sendAsyncMessage("browser-test-utils:DOMContentLoadedEvent",
+    {subframe: subframe, internalURL: event.target.documentURI,
+     visibleURL: content.document.location.href});
+}, true);
+
 addEventListener("load", function(event) {
   let subframe = event.target != content.document;
   sendAsyncMessage("browser-test-utils:loadEvent",
-    {subframe: subframe, url: event.target.documentURI});
+    {subframe: subframe, internalURL: event.target.documentURI,
+     visibleURL: content.document.location.href});
 }, true);
 
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-grid/alignment/grid-gutters-010.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[grid-gutters-010.html]
-  expected: FAIL
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -790,18 +790,18 @@ nsFind::PeekNextChar(nsRange* aSearchRan
   do {
     tc = nullptr;
     NextNode(aSearchRange, aStartPoint, aEndPoint, false);
 
     // Get the text content:
     tc = do_QueryInterface(mIterNode);
 
     // Get the block parent.
-    nsCOMPtr<nsIDOMNode> blockParent;
-    rv = GetBlockParent(mIterNode->AsDOMNode(), getter_AddRefs(blockParent));
+    nsCOMPtr<nsINode> blockParent;
+    rv = GetBlockParent(mIterNode, getter_AddRefs(blockParent));
     if (NS_FAILED(rv))
       return L'\0';
 
     // If out of nodes or in new parent.
     if (!mIterNode || !tc || (blockParent != mLastBlockParent))
       return L'\0';
 
     frag = tc->GetText();
@@ -890,27 +890,23 @@ nsFind::SkipNode(nsIContent* aContent)
     content = content->GetParent();
   }
 
   return false;
 #endif /* HAVE_BIDI_ITERATOR */
 }
 
 nsresult
-nsFind::GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent)
+nsFind::GetBlockParent(nsINode* aNode, nsINode** aParent)
 {
-  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-  // non-nsCOMPtr temporary so we don't keep addrefing/releasing as we
-  // go up the tree.
-  nsINode* curNode = node;
+  nsINode* curNode = aNode;
   while (curNode) {
     nsIContent* parent = curNode->GetParent();
     if (parent && IsBlockNode(parent)) {
-      *aParent = parent->AsDOMNode();
-      NS_ADDREF(*aParent);
+      *aParent = do_AddRef(parent).take();
       return NS_OK;
     }
     curNode = parent;
   }
   return NS_ERROR_FAILURE;
 }
 
 // Call ResetAll before returning, to remove all references to external objects.
@@ -1025,18 +1021,18 @@ nsFind::Find(const char16_t* aPatText, n
         // to search again (from beginning/end).
         ResetAll();
         return NS_OK;
       }
 
       // We have a new text content. If its block parent is different from the
       // block parent of the last text content, then we need to clear the match
       // since we don't want to find across block boundaries.
-      nsCOMPtr<nsIDOMNode> blockParent;
-      GetBlockParent(mIterNode->AsDOMNode(), getter_AddRefs(blockParent));
+      nsCOMPtr<nsINode> blockParent;
+      GetBlockParent(mIterNode, getter_AddRefs(blockParent));
 #ifdef DEBUG_FIND
       printf("New node: old blockparent = %p, new = %p\n",
              (void*)mLastBlockParent.get(), (void*)blockParent.get());
 #endif
       if (blockParent != mLastBlockParent) {
 #ifdef DEBUG_FIND
         printf("Different block parent!\n");
 #endif
--- a/toolkit/components/find/nsFind.h
+++ b/toolkit/components/find/nsFind.h
@@ -45,18 +45,18 @@ protected:
   // Use "find entire words" mode by setting to a word breaker or null, to
   // disable "entire words" mode.
   RefPtr<mozilla::intl::WordBreaker> mWordBreaker;
 
   int32_t mIterOffset;
   nsCOMPtr<nsINode> mIterNode;
 
   // Last block parent, so that we will notice crossing block boundaries:
-  nsCOMPtr<nsIDOMNode> mLastBlockParent;
-  nsresult GetBlockParent(nsIDOMNode* aNode, nsIDOMNode** aParent);
+  nsCOMPtr<nsINode> mLastBlockParent;
+  nsresult GetBlockParent(nsINode* aNode, nsINode** aParent);
 
   // Utility routines:
   bool IsBlockNode(nsIContent* aNode);
   bool SkipNode(nsIContent* aNode);
   bool IsVisibleNode(nsINode* aNode);
 
   // Move in the right direction for our search:
   nsresult NextNode(nsRange* aSearchRange,
--- a/toolkit/components/reader/ReaderMode.jsm
+++ b/toolkit/components/reader/ReaderMode.jsm
@@ -27,17 +27,17 @@ const CLASSES_TO_PRESERVE = [
   "visuallyhidden",
   "wp-caption",
   "wp-caption-text",
 ];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+Cu.importGlobalProperties(["XMLHttpRequest", "XMLSerializer"]);
 
 ChromeUtils.defineModuleGetter(this, "CommonUtils", "resource://services-common/utils.js");
 ChromeUtils.defineModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm");
 ChromeUtils.defineModuleGetter(this, "LanguageDetector", "resource:///modules/translation/LanguageDetector.jsm");
 
@@ -466,18 +466,17 @@ var ReaderMode = {
     let uriParam = {
       spec: doc.baseURIObject.spec,
       host: doc.baseURIObject.host,
       prePath: doc.baseURIObject.prePath,
       scheme: doc.baseURIObject.scheme,
       pathBase: Services.io.newURI(".", null, doc.baseURIObject).spec
     };
 
-    let serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
-                     createInstance(Ci.nsIDOMSerializer);
+    let serializer = new XMLSerializer();
     let serializedDoc = serializer.serializeToString(doc);
 
     let options = {
       classesToPreserve: CLASSES_TO_PRESERVE,
     };
 
     let article = null;
     try {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -288,17 +288,17 @@
     "expires_in_version": "never",
     "description": "Time from vsync to finishing a composite in milliseconds.",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50
   },
   "CONTENT_PROCESS_LAUNCH_TIME_MS" : {
     "record_in_processes": ["main", "content"],
-    "alert_emails": ["bsmedberg@mozilla.com", "mconley@mozilla.com"],
+    "alert_emails": ["fgomes@mozilla.com", "mconley@mozilla.com"],
     "expires_in_version": "57",
     "bug_numbers": [1304790],
     "kind": "exponential",
     "high": 64000,
     "n_buckets": 100,
     "releaseChannelCollection": "opt-out",
     "description": "Content process launch time until the GetXPCOMProcessAttributes message is received, in milliseconds"
   },
@@ -1922,64 +1922,64 @@
     "bug_numbers": [1377340],
     "kind": "categorical",
     "labels": ["NetworkNoRace", "CacheNoRace", "NetworkRace", "CacheRace", "NetworkDelayedRace", "CacheDelayedRace"],
     "description": "Whether we raced network with the cache."
   },
   "NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "62",
-    "alert_emails": ["necko@mozilla.com", "bsmedberg@mozilla.com"],
+    "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1354407],
     "kind": "exponential",
     "high": 60000,
     "n_buckets": 100,
     "description": "Time in milliseconds that we saved when we race cache with network."
   },
   "NETWORK_RACE_CACHE_WITH_NETWORK_OCEC_ON_START_DIFF": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "62",
-    "alert_emails": ["necko@mozilla.com", "bsmedberg@mozilla.com"],
+    "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1354407],
     "kind": "linear",
     "high": 1000,
     "n_buckets": 100,
     "description": "Time in milliseconds between onStartRequest from the cache and onCacheEntryCheck. Report only when net wins and OCEC is before onStartRequest from net."
   },
   "NETWORK_RACE_CACHE_BANDWIDTH_RACE_NETWORK_WIN": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "62",
     "kind": "exponential",
     "low": 32,
     "high": 16777216,
     "n_buckets": 100,
     "description": "Amount of bytes received when we decide to race cache with network and network wins.",
-    "alert_emails": ["necko@mozilla.com", "bsmedberg@mozilla.com"],
+    "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1354405]
   },
   "NETWORK_RACE_CACHE_BANDWIDTH_RACE_CACHE_WIN": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "62",
     "kind": "exponential",
     "low": 32,
     "high": 16777216,
     "n_buckets": 100,
     "description": "Amount of bytes received when we decide to race cache with network and cache wins.",
-    "alert_emails": ["necko@mozilla.com", "bsmedberg@mozilla.com"],
+    "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1354405]
   },
   "NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "62",
     "kind": "exponential",
     "low": 32,
     "high": 16777216,
     "n_buckets": 100,
     "description": "Amount of bytes received when we decide not to race cache with network.",
-    "alert_emails": ["necko@mozilla.com", "bsmedberg@mozilla.com"],
+    "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1354405]
   },
   "NETWORK_RACE_CACHE_VALIDATION": {
     "record_in_processes": ["main"],
     "expires_in_version": "62",
     "alert_emails": ["necko@mozilla.com"],
     "bug_numbers": [1377223],
     "kind": "categorical",
@@ -7977,77 +7977,77 @@
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 50,
     "description": "Time spent to display first page in PDF Viewer (ms)"
   },
   "PLUGINS_NOTIFICATION_SHOWN": {
     "record_in_processes": ["main", "content"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "boolean",
     "description": "The number of times the click-to-activate notification was shown: false: shown by in-content activation true: shown by location bar activation",
     "bug_numbers": [902075, 1345894],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_NOTIFICATION_PLUGIN_COUNT": {
     "record_in_processes": ["main", "content"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 5,
     "description": "The number of plugins present in the click-to-activate notification, minus one (1, 2, 3, 4, more than 4)",
     "bug_numbers": [902075, 1345894],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_NOTIFICATION_USER_ACTION_2": {
     "record_in_processes": ["main", "content"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 8,
     "description": "User actions taken in the plugin notification: 0: allownow 1: allowalways 2: block 3: blockalways",
     "bug_numbers": [902075, 1345894, 1398972],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_INFOBAR_SHOWN": {
     "record_in_processes": ["main"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "boolean",
     "description": "Count of when the hidden-plugin infobar was displayed.",
     "bug_numbers": [902075, 1345894],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_INFOBAR_BLOCK": {
     "record_in_processes": ["main"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "boolean",
     "description": "Count the number of times the user clicked 'block' on the hidden-plugin infobar.",
     "bug_numbers": [902075, 1345894],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_INFOBAR_ALLOW": {
     "record_in_processes": ["main"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "boolean",
     "description": "Count the number of times the user clicked 'allow' on the hidden-plugin infobar.",
     "bug_numbers": [902075, 1345894],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "PLUGINS_INFOBAR_DISMISSED": {
     "record_in_processes": ["main"],
     "releaseChannelCollection": "opt-out",
-    "expires_in_version": "60",
+    "expires_in_version": "never",
     "kind": "boolean",
     "description": "Count the number of times the user explicitly dismisses the hidden-plugin infobar without choosing allow or continue-blocking.",
     "bug_numbers": [1368060],
-    "alert_emails": ["bsmedberg@mozilla.com"]
+    "alert_emails": ["flashvideo-2015@mozilla.com"]
   },
   "POPUP_NOTIFICATION_STATS": {
     "record_in_processes": ["main", "content"],
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["firefox-dev@mozilla.org"],
     "bug_numbers": [1207089],
     "expires_in_version": "55",
     "kind": "enumerated",
--- a/toolkit/components/thumbnails/PageThumbUtils.jsm
+++ b/toolkit/components/thumbnails/PageThumbUtils.jsm
@@ -295,17 +295,17 @@ var PageThumbUtils = {
 
   shouldStoreContentThumbnail(aDocument, aDocShell) {
     if (BrowserUtils.isToolbarVisible(aDocShell, "findbar")) {
       return false;
     }
 
     // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
     //       that currently regresses Talos SVG tests.
-    if (aDocument instanceof Ci.nsIDOMXMLDocument) {
+    if (ChromeUtils.getClassName(aDocument) === "XMLDocument") {
       return false;
     }
 
     let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
 
     // Don't take screenshots of about: pages.
     if (webNav.currentURI.schemeIs("about")) {
       return false;
--- a/toolkit/components/windowcreator/test/browser_bug1204626.js
+++ b/toolkit/components/windowcreator/test/browser_bug1204626.js
@@ -6,18 +6,17 @@ const testPageURL = contentBase + "bug12
 
 function one_test(delay, continuation) {
   let delayStr = delay === null ? "no delay" : "delay = " + delay + "ms";
   let browser;
 
   BrowserTestUtils.openNewForegroundTab(gBrowser, testPageURL).then((tab) => {
     browser = tab.linkedBrowser;
     let persistable = browser.QueryInterface(Ci.nsIFrameLoaderOwner)
-                             .frameLoader
-                             .QueryInterface(Ci.nsIWebBrowserPersistable);
+                             .frameLoader;
     persistable.startPersistence(/* outer window ID: */ 0, {
       onDocumentReady,
       onError(status) {
         ok(false, new Components.Exception("startPersistence failed", status));
         continuation();
       }
     });
   });
--- a/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js
+++ b/toolkit/components/windowwatcher/test/browser_new_content_window_from_chrome_principal.js
@@ -8,17 +8,17 @@
 add_task(async function test_chrome_opens_window() {
   // This magic value of 2 means that by default, when content tries
   // to open a new window, it'll actually open in a new window instead
   // of a new tab.
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.link.open_newwindow", 2],
   ]});
 
-  let newWinPromise = BrowserTestUtils.waitForNewWindow("http://example.com/");
+  let newWinPromise = BrowserTestUtils.waitForNewWindow({url: "http://example.com/"});
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     content.open("http://example.com/", "_blank");
   });
 
   let win = await newWinPromise;
   let browser = win.gBrowser.selectedBrowser;
 
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -39,17 +39,17 @@ var gNotificationParents = new WeakMap;
 
 function getAnchorFromBrowser(aBrowser, aAnchorID) {
   let attrPrefix = aAnchorID ? aAnchorID.replace("notification-icon", "") : "";
   let anchor = aBrowser.getAttribute(attrPrefix + ICON_ANCHOR_ATTRIBUTE) ||
                aBrowser[attrPrefix + ICON_ANCHOR_ATTRIBUTE] ||
                aBrowser.getAttribute(ICON_ANCHOR_ATTRIBUTE) ||
                aBrowser[ICON_ANCHOR_ATTRIBUTE];
   if (anchor) {
-    if (anchor instanceof Ci.nsIDOMXULElement) {
+    if (ChromeUtils.getClassName(anchor) == "XULElement") {
       return anchor;
     }
     return aBrowser.ownerDocument.getElementById(anchor);
   }
   return null;
 }
 
 function getNotificationFromElement(aElement) {
@@ -208,19 +208,19 @@ Notification.prototype = {
  *            suppressed for this window. This state is checked on construction
  *            and when the "anchorVisibilityChange" method is called.
  *        }
  */
 function PopupNotifications(tabbrowser, panel,
                                                       iconBox, options = {}) {
   if (!tabbrowser)
     throw "Invalid tabbrowser";
-  if (iconBox && !(iconBox instanceof Ci.nsIDOMXULElement))
+  if (iconBox && ChromeUtils.getClassName(iconBox) != "XULElement")
     throw "Invalid iconBox";
-  if (!(panel instanceof Ci.nsIDOMXULElement))
+  if (ChromeUtils.getClassName(panel) != "XULElement")
     throw "Invalid panel";
 
   this._shouldSuppress = options.shouldSuppress || (() => false);
   this._suppress = this._shouldSuppress();
 
   this.window = tabbrowser.ownerGlobal;
   this.panel = panel;
   this.tabbrowser = tabbrowser;
@@ -1080,17 +1080,17 @@ PopupNotifications.prototype = {
    *                      browser tab
    * @param anchors       is a XUL element or a Set of XUL elements that the
    *                      notifications panel(s) will be anchored to.
    * @param dismissShowing if true, dismiss any currently visible notifications
    *                       if there are no notifications to show. Otherwise,
    *                       currently displayed notifications will be left alone.
    */
   _update: function PopupNotifications_update(notifications, anchors = new Set(), dismissShowing = false) {
-    if (anchors instanceof Ci.nsIDOMXULElement)
+    if (ChromeUtils.getClassName(anchors) == "XULElement")
       anchors = new Set([anchors]);
 
     if (!notifications)
       notifications = this._currentNotifications;
 
     let haveNotifications = notifications.length > 0;
     if (!anchors.size && haveNotifications)
       anchors = this._getAnchorsForNotifications(notifications);
--- a/toolkit/modules/WindowDraggingUtils.jsm
+++ b/toolkit/modules/WindowDraggingUtils.jsm
@@ -47,17 +47,17 @@ WindowDraggingElement.prototype = {
     while (target != this._elem) {
       if (!this.dragTags.includes(target.localName))
         return false;
       target = target.parentNode;
     }
     return true;
   },
   isPanel() {
-    return this._elem instanceof Ci.nsIDOMXULElement &&
+    return ChromeUtils.getClassName(this._elem) == "XULElement" &&
            this._elem.localName == "panel";
   },
   handleEvent(aEvent) {
     let isPanel = this.isPanel();
     switch (aEvent.type) {
       case "mousedown":
         if (!this.shouldDrag(aEvent))
           return;
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -222,17 +222,17 @@ var ContentPolicyManager = {
   init() {
     Services.ppmm.initialProcessData.webRequestContentPolicies = this.policyData;
 
     Services.ppmm.addMessageListener("WebRequest:ShouldLoad", this);
     Services.mm.addMessageListener("WebRequest:ShouldLoad", this);
   },
 
   receiveMessage(msg) {
-    let browser = msg.target instanceof Ci.nsIDOMXULElement ? msg.target : null;
+    let browser = ChromeUtils.getClassName(msg.target) == "XULElement" ? msg.target : null;
 
     let requestId = `fakeRequest-${++nextFakeRequestId}`;
     for (let id of msg.data.ids) {
       let callback = this.policies.get(id);
       if (!callback) {
         // It's possible that this listener has been removed and the
         // child hasn't learned yet.
         continue;
--- a/toolkit/modules/addons/WebRequestContent.js
+++ b/toolkit/modules/addons/WebRequestContent.js
@@ -122,17 +122,18 @@ var ContentPolicy = {
 
     function getWindowId(window) {
       return window.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils)
         .outerWindowID;
     }
 
     if (policyType == Ci.nsIContentPolicy.TYPE_SUBDOCUMENT ||
-        (node instanceof Ci.nsIDOMXULElement && node.localName == "browser")) {
+        (ChromeUtils.getClassName(node) == "XULElement" &&
+         node.localName == "browser")) {
       // Chrome sets frameId to the ID of the sub-window. But when
       // Firefox loads an iframe, it sets |node| to the <iframe>
       // element, whose window is the parent window. We adopt the
       // Chrome behavior here.
       node = node.contentWindow;
     }
 
     if (node) {
--- a/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js
+++ b/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js
@@ -87,17 +87,17 @@ add_task(async function target_blank() {
 
 add_task(async function new_window() {
   // Tests that a link that forces us to open a new window (by specifying a
   // width and a height in window.open) opens a new window for the load,
   // realizes that we need to close that window and returns the *original*
   // window as the window context.
   await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(browser) {
     let dialogAppeared = promiseHelperAppDialog();
-    let windowOpened = BrowserTestUtils.waitForNewWindow(false);
+    let windowOpened = BrowserTestUtils.waitForNewWindow();
 
     await BrowserTestUtils.synthesizeMouseAtCenter("#new_window", {}, browser);
 
     let windowContext = await dialogAppeared;
     is(windowContext.gBrowser.selectedBrowser.currentURI.spec, URL,
        "got the right windowContext");
     let win = await windowOpened;
 
--- a/widget/WidgetMessageUtils.h
+++ b/widget/WidgetMessageUtils.h
@@ -34,11 +34,17 @@ struct ParamTraits<LookAndFeelInt>
     return false;
   }
 };
 
 template<>
 struct ParamTraits<nsTransparencyMode> : public ContiguousEnumSerializerInclusive<nsTransparencyMode, eTransparencyOpaque, eTransparencyBorderlessGlass>
 { };
 
+template<>
+struct ParamTraits<nsCursor>
+  : public ContiguousEnumSerializer<nsCursor, eCursor_standard, eCursorCount>
+{
+};
+
 } // namespace IPC
 
 #endif // WidgetMessageUtils_h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -3637,18 +3637,21 @@ nsWindow::Create(nsIWidget* aParent,
                                aInitData->mSupportTranslucency);
 
         // mozilla.widget.use-argb-visuals is a hidden pref defaulting to false
         // to allow experimentation
         if (Preferences::GetBool("mozilla.widget.use-argb-visuals", false))
             useAlphaVisual = true;
 
 #ifdef GL_PROVIDER_GLX
-        bool useWebRender = gfxPlatform::Initialized() &&
-            gfx::gfxVars::UseWebRender() &&
+        // Ensure gfxPlatform is initialized, since that is what initializes
+        // gfxVars, used below.
+        Unused << gfxPlatform::GetPlatform();
+
+        bool useWebRender = gfx::gfxVars::UseWebRender() &&
             AllowWebRenderForThisWindow();
 
         // If using WebRender on X11, we need to select a visual with a depth buffer,
         // as well as an alpha channel if transparency is requested. This must be done
         // before the widget is realized.
         if (mIsX11Display && useWebRender) {
             auto display =
                 GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell));
--- a/xpcom/ds/PLDHashTable.cpp
+++ b/xpcom/ds/PLDHashTable.cpp
@@ -471,27 +471,26 @@ PLDHashTable::ChangeTable(int32_t aDelta
     return false;
   }
 
   uint32_t nbytes;
   if (!SizeOfEntryStore(newCapacity, mEntrySize, &nbytes)) {
     return false;   // overflowed
   }
 
-  char* newEntryStore = (char*)malloc(nbytes);
+  char* newEntryStore = (char*)calloc(1, nbytes);
   if (!newEntryStore) {
     return false;
   }
 
   // We can't fail from here on, so update table parameters.
   mHashShift = kHashBits - newLog2;
   mRemovedCount = 0;
 
   // Assign the new entry store to table.
-  memset(newEntryStore, 0, nbytes);
   char* oldEntryStore;
   char* oldEntryAddr;
   oldEntryAddr = oldEntryStore = mEntryStore.Get();
   mEntryStore.Set(newEntryStore, &mGeneration);
   PLDHashMoveEntry moveEntry = mOps->moveEntry;
 
   // Copy only live entries, leaving removed ones behind.
   uint32_t oldCapacity = 1u << oldLog2;
@@ -550,21 +549,20 @@ PLDHashTable::Add(const void* aKey, cons
 #endif
 
   // Allocate the entry storage if it hasn't already been allocated.
   if (!mEntryStore.Get()) {
     uint32_t nbytes;
     // We already checked this in the constructor, so it must still be true.
     MOZ_RELEASE_ASSERT(SizeOfEntryStore(CapacityFromHashShift(), mEntrySize,
                                         &nbytes));
-    mEntryStore.Set((char*)malloc(nbytes), &mGeneration);
+    mEntryStore.Set((char*)calloc(1, nbytes), &mGeneration);
     if (!mEntryStore.Get()) {
       return nullptr;
     }
-    memset(mEntryStore.Get(), 0, nbytes);
   }
 
   // If alpha is >= .75, grow or compress the table. If aKey is already in the
   // table, we may grow once more than necessary, but only if we are on the
   // edge of being overloaded.
   uint32_t capacity = Capacity();
   if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
     // Compress if a quarter or more of all entries are removed.
--- a/xpcom/ds/nsCRT.cpp
+++ b/xpcom/ds/nsCRT.cpp
@@ -106,41 +106,16 @@ nsCRT::strcmp(const char16_t* aStr1, con
     }
     if (aStr2) {  // aStr1 must have been null
       return 1;
     }
   }
   return 0;
 }
 
-const char*
-nsCRT::memmem(const char* aHaystack, uint32_t aHaystackLen,
-              const char* aNeedle, uint32_t aNeedleLen)
-{
-  // Sanity checking
-  if (!(aHaystack && aNeedle && aHaystackLen && aNeedleLen &&
-        aNeedleLen <= aHaystackLen)) {
-    return nullptr;
-  }
-
-#ifdef HAVE_MEMMEM
-  return (const char*)::memmem(aHaystack, aHaystackLen, aNeedle, aNeedleLen);
-#else
-  // No memmem means we need to roll our own.  This isn't really optimized
-  // for performance ... if that becomes an issue we can take some inspiration
-  // from the js string compare code in jsstr.cpp
-  for (uint32_t i = 0; i < aHaystackLen - aNeedleLen; i++) {
-    if (!memcmp(aHaystack + i, aNeedle, aNeedleLen)) {
-      return aHaystack + i;
-    }
-  }
-#endif
-  return nullptr;
-}
-
 // This should use NSPR but NSPR isn't exporting its PR_strtoll function
 // Until then...
 int64_t
 nsCRT::atoll(const char* aStr)
 {
   if (!aStr) {
     return 0;
   }
--- a/xpcom/ds/nsCRT.h
+++ b/xpcom/ds/nsCRT.h
@@ -80,22 +80,16 @@ public:
     * WARNING - STRTOK WHACKS str THE FIRST TIME IT IS CALLED *
     * MAKE A COPY OF str IF YOU NEED TO USE IT AFTER strtok() *
   */
   static char* strtok(char* aStr, const char* aDelims, char** aNewStr);
 
   /// Like strcmp except for ucs2 strings
   static int32_t strcmp(const char16_t* aStr1, const char16_t* aStr2);
 
-  // The GNU libc has memmem, which is strstr except for binary data
-  // This is our own implementation that uses memmem on platforms
-  // where it's available.
-  static const char* memmem(const char* aHaystack, uint32_t aHaystackLen,
-                            const char* aNeedle, uint32_t aNeedleLen);
-
   // String to longlong
   static int64_t atoll(const char* aStr);
 
   static char ToUpper(char aChar) { return NS_ToUpper(aChar); }
   static char ToLower(char aChar) { return NS_ToLower(aChar); }
 
   static bool IsUpper(char aChar) { return NS_IsUpper(aChar); }
   static bool IsLower(char aChar) { return NS_IsLower(aChar); }
--- a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp
@@ -15,38 +15,33 @@
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMGeoPositionError.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDOMParser.h"
 #include "nsIDOMRange.h"
-#include "nsIDOMSerializer.h"
-#include "nsIDOMXMLDocument.h"
-#include "nsIDOMXULElement.h"
 #include "nsIListBoxObject.h"
 #include "nsIMessageManager.h"
 #include "nsISelection.h"
 #include "nsITreeBoxObject.h"
-#include "nsIWebBrowserPersistable.h"
 
 #include "mozilla/dom/CSSPrimitiveValueBinding.h"
 #include "mozilla/dom/CSSStyleDeclarationBinding.h"
 #include "mozilla/dom/CSSStyleSheetBinding.h"
 #include "mozilla/dom/CSSValueBinding.h"
 #include "mozilla/dom/CSSValueListBinding.h"
 #include "mozilla/dom/DOMParserBinding.h"
 #include "mozilla/dom/DOMRequestBinding.h"
 #include "mozilla/dom/DocumentBinding.h"
 #include "mozilla/dom/DocumentFragmentBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/EventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
-#include "mozilla/dom/FrameLoaderBinding.h"
 #include "mozilla/dom/HTMLAnchorElementBinding.h"
 #include "mozilla/dom/HTMLAreaElementBinding.h"
 #include "mozilla/dom/HTMLButtonElementBinding.h"
 #include "mozilla/dom/HTMLFrameSetElementBinding.h"
 #include "mozilla/dom/HTMLHtmlElementBinding.h"
 #include "mozilla/dom/HTMLInputElementBinding.h"
 #include "mozilla/dom/ListBoxObjectBinding.h"
 #include "mozilla/dom/MediaListBinding.h"
@@ -60,20 +55,17 @@
 #include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/SelectionBinding.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/StyleSheetBinding.h"
 #include "mozilla/dom/StyleSheetListBinding.h"
 #include "mozilla/dom/SVGElementBinding.h"
 #include "mozilla/dom/TimeEventBinding.h"
 #include "mozilla/dom/TreeBoxObjectBinding.h"
-#include "mozilla/dom/XMLDocumentBinding.h"
-#include "mozilla/dom/XMLSerializerBinding.h"
 #include "mozilla/dom/XULDocumentBinding.h"
-#include "mozilla/dom/XULElementBinding.h"
 
 using namespace mozilla;
 
 struct ComponentsInterfaceShimEntry {
   constexpr
   ComponentsInterfaceShimEntry(const char* aName, const nsIID& aIID,
                                const dom::NativePropertyHooks* aNativePropHooks)
     : geckoName(aName), iid(aIID), nativePropHooks(aNativePropHooks) {}
@@ -136,21 +128,17 @@ const ComponentsInterfaceShimEntry kComp
   DEFINE_SHIM(HTMLInputElement),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIListBoxObject, ListBoxObject),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIMessageSender, MessageSender),
   DEFINE_SHIM(NodeList),
   DEFINE_SHIM(Node),
   DEFINE_SHIM(OfflineResourceList),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMParser, DOMParser),
   DEFINE_SHIM(Range),
-  DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOMSerializer, XMLSerializer),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsITreeBoxObject, TreeBoxObject),
-  DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIWebBrowserPersistable, FrameLoader),
-  DEFINE_SHIM(XMLDocument),
-  DEFINE_SHIM(XULElement),
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsISelection, Selection),
 };
 
 #undef DEFINE_SHIM
 #undef DEFINE_SHIM_WITH_CUSTOM_INTERFACE
 
 NS_IMPL_ISUPPORTS(ShimInterfaceInfo, nsISupports, nsIInterfaceInfo)
 
--- a/xpcom/reflect/xptinfo/xptinfo.h