merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 16 Sep 2017 11:35:02 +0200
changeset 430795 27e7b4261e4b5a016e09adb5332a181e43ab520e
parent 430770 6c1b45e7e94dbf343813eda03f1fd0b3259b59b1 (current diff)
parent 430794 eb321219373e544f8db4f23bc83e8203fed7e9d2 (diff)
child 430796 3b375d85383a82301d3c251901cee18bffcaa22f
child 430807 5e5e2372b2c05bf68b4cd0908bdf22d8c4575a9d
child 430819 28e04e7d8935169dfa48d2b24fa712afbfbd57f6
child 430845 0e7c43ea336cfd8b26ce71031757c9c5f32f84a1
push id7768
push userryanvm@gmail.com
push dateSat, 16 Sep 2017 16:13:49 +0000
treeherdermozilla-beta@3b375d85383a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
27e7b4261e4b / 57.0a1 / 20170916100147 / files
nightly linux64
27e7b4261e4b / 57.0a1 / 20170916100147 / files
nightly mac
27e7b4261e4b / 57.0a1 / 20170916100147 / files
nightly win32
27e7b4261e4b / 57.0a1 / 20170916100147 / files
nightly win64
27e7b4261e4b / 57.0a1 / 20170916100147 / 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 mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 1Wa3jV6n7Wj
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
modules/libpref/init/all.js
testing/web-platform/meta/css/css-fonts-3/font-kerning-03.html.ini
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1258,16 +1258,17 @@ var gBrowserInit = {
       }
 
       let linkedBrowser = tabArgument.linkedBrowser;
       if (linkedBrowser) {
         remoteType = linkedBrowser.remoteType;
         isRemote = remoteType != E10SUtils.NOT_REMOTE;
         sameProcessAsFrameLoader = linkedBrowser.frameLoader;
       }
+      initBrowser.removeAttribute("blank");
     }
 
     gBrowser.updateBrowserRemoteness(initBrowser, isRemote, {
       remoteType, sameProcessAsFrameLoader
     });
   },
 
   onLoad() {
@@ -1611,16 +1612,23 @@ var gBrowserInit = {
     });
 
     let mm = window.messageManager;
     mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
       mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
       firstBrowserPaintDeferred.resolve();
     });
 
+    let initialBrowser = gBrowser.selectedBrowser;
+    mm.addMessageListener("Browser:FirstNonBlankPaint",
+                          function onFirstNonBlankPaint() {
+      mm.removeMessageListener("Browser:FirstNonBlankPaint", onFirstNonBlankPaint);
+      initialBrowser.removeAttribute("blank");
+    });
+
     this._uriToLoadPromise.then(uriToLoad => {
       if (!uriToLoad || uriToLoad == "about:blank") {
         return;
       }
 
       // We don't check if uriToLoad is a XULElement because this case has
       // already been handled before first paint, and the argument cleared.
       if (uriToLoad instanceof Ci.nsIArray) {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -1045,8 +1045,16 @@ addMessageListener("AllowScriptsToClose"
          .getInterface(Ci.nsIDOMWindowUtils)
          .allowScriptsToClose();
 });
 
 addEventListener("MozAfterPaint", function onFirstPaint() {
   removeEventListener("MozAfterPaint", onFirstPaint);
   sendAsyncMessage("Browser:FirstPaint");
 });
+
+// Remove this once bug 1397365 is fixed.
+addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
+  if (content.document.documentURI == "about:blank")
+    return;
+  removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
+  sendAsyncMessage("Browser:FirstNonBlankPaint");
+});
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -22,17 +22,17 @@
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown,tabcontainer"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1" notificationside="top">
             <xul:hbox flex="1" class="browserSidebarContainer">
               <xul:vbox flex="1" class="browserContainer">
                 <xul:stack flex="1" class="browserStack" anonid="browserStack">
                   <xul:browser anonid="initialBrowser" type="content" message="true" messagemanagergroup="browsers"
-                               primary="true"
+                               primary="true" blank="true"
                                xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist,datetimepicker"/>
                 </xul:stack>
               </xul:vbox>
             </xul:hbox>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -189,18 +189,21 @@ function alterContent(browser, task, arg
 function getPanelForNode(node) {
   while (node.localName != "panel") {
     node = node.parentNode;
   }
   return node;
 }
 
 var awaitBrowserLoaded = browser => ContentTask.spawn(browser, null, () => {
-  if (content.document.readyState !== "complete") {
-    return ContentTaskUtils.waitForEvent(this, "load", true).then(() => {});
+  if (content.document.readyState !== "complete" ||
+      content.document.documentURI === "about:blank") {
+    return ContentTaskUtils.waitForEvent(this, "load", true, event => {
+      return content.document.documentURI !== "about:blank";
+    }).then(() => {});
   }
 });
 
 var awaitExtensionPanel = async function(extension, win = window, awaitLoad = true) {
   let {originalTarget: browser} = await BrowserTestUtils.waitForEvent(
     win.document, "WebExtPopupLoaded", true,
     event => event.detail.extension.id === extension.id);
 
--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
@@ -13,24 +13,24 @@ const IFRAME2 = URL_ROOT + "doc_boxmodel
 add_task(function* () {
   yield addTab(IFRAME1);
   let {inspector, view, testActor} = yield openBoxModelView();
 
   yield testFirstPage(inspector, view, testActor);
 
   info("Navigate to the second page");
   let onMarkupLoaded = waitForMarkupLoaded(inspector);
-  yield testActor.eval(`content.location.href="${IFRAME2}"`);
+  yield testActor.eval(`location.href="${IFRAME2}"`);
   yield onMarkupLoaded;
 
   yield testSecondPage(inspector, view, testActor);
 
   info("Go back to the first page");
   onMarkupLoaded = waitForMarkupLoaded(inspector);
-  yield testActor.eval("content.history.back();");
+  yield testActor.eval("history.back();");
   yield onMarkupLoaded;
 
   yield testBackToFirstPage(inspector, view, testActor);
 });
 
 function* testFirstPage(inspector, view, testActor) {
   info("Test that the box model view works on the first page");
 
--- a/devtools/client/inspector/boxmodel/test/head.js
+++ b/devtools/client/inspector/boxmodel/test/head.js
@@ -98,25 +98,25 @@ function waitForMarkupLoaded(inspector) 
   return Promise.all([
     waitForUpdate(inspector),
     inspector.once("markuploaded"),
   ]);
 }
 
 function getStyle(testActor, selector, propertyName) {
   return testActor.eval(`
-    content.document.querySelector("${selector}")
-                    .style.getPropertyValue("${propertyName}");
+    document.querySelector("${selector}")
+            .style.getPropertyValue("${propertyName}");
   `);
 }
 
 function setStyle(testActor, selector, propertyName, value) {
   return testActor.eval(`
-    content.document.querySelector("${selector}")
-                    .style.${propertyName} = "${value}";
+    document.querySelector("${selector}")
+            .style.${propertyName} = "${value}";
   `);
 }
 
 /**
  * The box model doesn't participate in the inspector's update mechanism, so simply
  * calling the default selectNode isn't enough to guarantee that the box model view has
  * finished updating. We also need to wait for the "boxmodel-view-updated" event.
  */
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-added.js
@@ -48,17 +48,17 @@ add_task(function* () {
   ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
 
   info("Adding the #grid2 container in the content page.");
   let onGridListUpdate = waitUntilState(store, state =>
     state.grids.length == 2 &&
     state.grids[0].highlighted &&
     !state.grids[1].highlighted);
   testActor.eval(`
-    content.document.getElementById("grid2").classList.add("grid");
+    document.getElementById("grid2").classList.add("grid");
   `);
   yield onGridListUpdate;
 
   info("Checking the new Grid Inspector state.");
   is(gridList.childNodes.length, 2, "Two grid containers are listed.");
   ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
     "CSS grid highlighter is created in the highlighters overlay.");
   ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
--- a/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-on-mutation-element-removed.js
@@ -46,17 +46,17 @@ add_task(function* () {
   ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
     "CSS grid highlighter is created in the highlighters overlay.");
   ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
 
   info("Removing the #grid container in the content page.");
   let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   onCheckboxChange = waitUntilState(store, state => state.grids.length == 0);
   testActor.eval(`
-    content.document.getElementById("grid").remove();
+    document.getElementById("grid").remove();
   `);
   yield onHighlighterHidden;
   yield onCheckboxChange;
 
   info("Checking the CSS grid highlighter is not shown.");
   ok(!highlighters.gridHighlighterShown, "No CSS grid highlighter is shown.");
   let noGridList = doc.querySelector(".grid-pane .devtools-sidepanel-no-result");
   ok(noGridList, "The message no grid containers is displayed.");
--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-updates-on-grid-change.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-updates-on-grid-change.js
@@ -52,19 +52,19 @@ add_task(function* () {
         resolve();
       }
     };
     inspector.reflowTracker.trackReflows(listener, listener.callback);
   });
   let onGridOutlineChanged = waitForDOM(doc, ".grid-outline-cell", 4);
 
   testActor.eval(`
-    const div = content.document.createElement("div");
+    const div = document.createElement("div");
     div.textContent = "item 3";
-    content.document.querySelector(".container").appendChild(div);
+    document.querySelector(".container").appendChild(div);
   `);
 
   yield onReflow;
   elements = yield onGridOutlineChanged;
 
   info("Checking the grid outline is correct.");
   is(elements.length, 4, "Grid outline was changed.");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
+++ b/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
@@ -17,17 +17,17 @@ add_task(function* () {
   yield selectNode("img", inspector);
   yield assertCopyImageDataAvailable(inspector);
   let expectedSrc = yield testActor.getAttribute("img", "src");
   yield triggerCopyImageUrlAndWaitForClipboard(expectedSrc, inspector);
 
   yield selectNode("canvas", inspector);
   yield assertCopyImageDataAvailable(inspector);
   let expectedURL = yield testActor.eval(`
-    content.document.querySelector(".canvas").toDataURL();`);
+    document.querySelector(".canvas").toDataURL();`);
   yield triggerCopyImageUrlAndWaitForClipboard(expectedURL, inspector);
 
   // Check again that the menu isn't available on the DIV (to make sure our
   // menu updating mechanism works)
   yield selectNode("div", inspector);
   yield assertCopyImageDataNotAvailable(inspector);
 });
 
--- a/devtools/client/inspector/markup/test/browser_markup_html_edit_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_02.js
@@ -13,38 +13,38 @@ const TEST_DATA = [
   {
     selector: "#badMarkup1",
     oldHTML: "<div id=\"badMarkup1\">badMarkup1</div>",
     newHTML: "<div id=\"badMarkup1\">badMarkup1</div> hanging</div>",
     validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
       is(pageNodeFront, selectedNodeFront, "Original element is selected");
 
       let textNodeName = yield testActor.eval(`
-        content.document.querySelector("#badMarkup1").nextSibling.nodeName
+        document.querySelector("#badMarkup1").nextSibling.nodeName
       `);
       let textNodeData = yield testActor.eval(`
-        content.document.querySelector("#badMarkup1").nextSibling.data
+        document.querySelector("#badMarkup1").nextSibling.data
       `);
       is(textNodeName, "#text", "Sibling is a text element");
       is(textNodeData, " hanging", "New text node has expected text content");
     }
   },
   {
     selector: "#badMarkup2",
     oldHTML: "<div id=\"badMarkup2\">badMarkup2</div>",
     newHTML: "<div id=\"badMarkup2\">badMarkup2</div> hanging<div></div>" +
              "</div></div></body>",
     validate: function* ({pageNodeFront, selectedNodeFront, testActor}) {
       is(pageNodeFront, selectedNodeFront, "Original element is selected");
 
       let textNodeName = yield testActor.eval(`
-        content.document.querySelector("#badMarkup2").nextSibling.nodeName
+        document.querySelector("#badMarkup2").nextSibling.nodeName
       `);
       let textNodeData = yield testActor.eval(`
-        content.document.querySelector("#badMarkup2").nextSibling.data
+        document.querySelector("#badMarkup2").nextSibling.data
       `);
       is(textNodeName, "#text", "Sibling is a text element");
       is(textNodeData, " hanging", "New text node has expected text content");
     }
   },
   {
     selector: "#badMarkup3",
     oldHTML: "<div id=\"badMarkup3\">badMarkup3</div>",
--- a/devtools/client/inspector/markup/test/browser_markup_html_edit_03.js
+++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_03.js
@@ -116,43 +116,43 @@ function* testHead(inspector, testActor)
 
   let onUpdated = inspector.once("inspector-updated");
   let onReselected = inspector.markup.once("reselectedonremoved");
   yield inspector.markup.updateNodeOuterHTML(headFront, headHTML,
                                              currentHeadHTML);
   yield onReselected;
   yield onUpdated;
 
-  is((yield testActor.eval("content.document.title")), "New Title",
+  is((yield testActor.eval("document.title")), "New Title",
      "New title has been added");
-  is((yield testActor.eval("content.foo")), undefined,
+  is((yield testActor.eval("window.foo")), undefined,
      "Script has not been executed");
   is((yield testActor.getProperty("head", "outerHTML")), headHTML,
      "<head> HTML has been updated");
   is((yield testActor.getNumberOfElementMatches("body")), 1,
      "no extra <body>s have been added");
 }
 
 function* testDocumentElement(inspector, testActor) {
   let currentDocElementOuterHMTL = yield testActor.eval(
-    "content.document.documentElement.outerHMTL");
+    "document.documentElement.outerHMTL");
   let docElementHTML = "<html id=\"updated\" foo=\"bar\"><head>" +
                        "<title>Updated from document element</title>" +
                        "<script>window.foo=\"bar\";</script></head><body>" +
                        "<p>Hello</p></body></html>";
   let docElementFront = yield inspector.markup.walker.documentElement();
 
   let onReselected = inspector.markup.once("reselectedonremoved");
   yield inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML,
     currentDocElementOuterHMTL);
   yield onReselected;
 
-  is((yield testActor.eval("content.document.title")),
+  is((yield testActor.eval("document.title")),
      "Updated from document element", "New title has been added");
-  is((yield testActor.eval("content.foo")),
+  is((yield testActor.eval("window.foo")),
      undefined, "Script has not been executed");
   is((yield testActor.getAttribute("html", "id")),
      "updated", "<html> ID has been updated");
   is((yield testActor.getAttribute("html", "class")),
      null, "<html> class has been updated");
   is((yield testActor.getAttribute("html", "foo")),
      "bar", "<html> attribute has been updated");
   is((yield testActor.getProperty("html", "outerHTML")),
@@ -162,31 +162,31 @@ function* testDocumentElement(inspector,
   is((yield testActor.getNumberOfElementMatches("body")),
      1, "no extra <body>s have been added");
   is((yield testActor.getProperty("body", "textContent")),
      "Hello", "document.body.textContent has been updated");
 }
 
 function* testDocumentElement2(inspector, testActor) {
   let currentDocElementOuterHMTL = yield testActor.eval(
-    "content.document.documentElement.outerHMTL");
+    "document.documentElement.outerHMTL");
   let docElementHTML = "<html id=\"somethingelse\" class=\"updated\"><head>" +
                        "<title>Updated again from document element</title>" +
                        "<script>window.foo=\"bar\";</script></head><body>" +
                        "<p>Hello again</p></body></html>";
   let docElementFront = yield inspector.markup.walker.documentElement();
 
   let onReselected = inspector.markup.once("reselectedonremoved");
   inspector.markup.updateNodeOuterHTML(docElementFront, docElementHTML,
     currentDocElementOuterHMTL);
   yield onReselected;
 
-  is((yield testActor.eval("content.document.title")),
+  is((yield testActor.eval("document.title")),
      "Updated again from document element", "New title has been added");
-  is((yield testActor.eval("content.foo")),
+  is((yield testActor.eval("window.foo")),
      undefined, "Script has not been executed");
   is((yield testActor.getAttribute("html", "id")),
      "somethingelse", "<html> ID has been updated");
   is((yield testActor.getAttribute("html", "class")),
      "updated", "<html> class has been updated");
   is((yield testActor.getAttribute("html", "foo")),
      null, "<html> attribute has been removed");
   is((yield testActor.getProperty("html", "outerHTML")),
--- a/devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js
+++ b/devtools/client/inspector/markup/test/browser_markup_keybindings_scrolltonode.js
@@ -71,17 +71,17 @@ add_task(function* () {
  * @param {Boolean} expected
  *        true if the element is expected to be in the viewport, false otherwise
  * @param {TestActor} testActor
  *        current test actor
  * @return {Promise} promise
  */
 function* checkElementIsInViewport(selector, expected, testActor) {
   let isInViewport = yield testActor.eval(`
-    let node = content.document.querySelector("${selector}");
+    let node = document.querySelector("${selector}");
     let rect = node.getBoundingClientRect();
     rect.bottom >= 0 && rect.right >= 0 &&
-      rect.top <= content.innerHeight && rect.left <= content.innerWidth;
+      rect.top <= window.innerHeight && rect.left <= window.innerWidth;
   `);
 
   is(isInViewport, expected,
     selector + " in the viewport: expected to be " + expected);
 }
--- a/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
@@ -99,32 +99,32 @@ const TEST_DATA = [
       ok(newAttrContainer.beforeMutationFlag, "attribute-container same as earlier");
     }
   },
   {
     desc: "Adding ::after element",
     numMutations: 2,
     test: function* (testActor) {
       yield testActor.eval(`
-        let node1 = content.document.querySelector("#node1");
+        let node1 = document.querySelector("#node1");
         node1.classList.add("pseudo");
       `);
     },
     check: function* (inspector) {
       let {children} = yield getContainerForSelector("#node1", inspector);
       is(children.childNodes.length, 2,
         "Node1 now has 2 children (text child and ::after");
     }
   },
   {
     desc: "Removing ::after element",
     numMutations: 2,
     test: function* (testActor) {
       yield testActor.eval(`
-        let node1 = content.document.querySelector("#node1");
+        let node1 = document.querySelector("#node1");
         node1.classList.remove("pseudo");
       `);
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
       ok(container.inlineTextChild, "Has single text child.");
     }
   },
@@ -141,17 +141,17 @@ const TEST_DATA = [
       is(container.editor.elt.querySelector(".text").textContent.trim(),
          "newtext", "Single text child editor updated.");
     }
   },
   {
     desc: "Adding a second text child",
     test: function* (testActor) {
       yield testActor.eval(`
-        let node1 = content.document.querySelector("#node1");
+        let node1 = document.querySelector("#node1");
         let newText = node1.ownerDocument.createTextNode("more");
         node1.appendChild(newText);
       `);
     },
     check: function* (inspector) {
       let container = yield getContainerForSelector("#node1", inspector);
       ok(!container.inlineTextChild, "Does not have single text child.");
       ok(container.canExpand, "Can expand container with child nodes.");
@@ -218,33 +218,33 @@ const TEST_DATA = [
       is(container.children.querySelector(".text").textContent.trim(), "foo",
         "The span's textcontent is correct");
     }
   },
   {
     desc: "Removing child nodes",
     test: function* (testActor) {
       yield testActor.eval(`
-        let node4 = content.document.querySelector("#node4");
+        let node4 = document.querySelector("#node4");
         while (node4.firstChild) {
           node4.removeChild(node4.firstChild);
         }
       `);
     },
     check: function* (inspector) {
       let {children} = yield getContainerForSelector("#node4", inspector);
       is(children.innerHTML, "", "Children have been removed");
     }
   },
   {
     desc: "Appending a child to a different parent",
     test: function* (testActor) {
       yield testActor.eval(`
-        let node17 = content.document.querySelector("#node17");
-        let node2 = content.document.querySelector("#node2");
+        let node17 = document.querySelector("#node17");
+        let node2 = document.querySelector("#node2");
         node2.appendChild(node17);
       `);
     },
     check: function* (inspector) {
       let {children} = yield getContainerForSelector("#node16", inspector);
       is(children.innerHTML, "",
          "Node17 has been removed from its node16 parent");
 
@@ -266,19 +266,19 @@ const TEST_DATA = [
     // body
     //   node1
     //     node20
     //      node21
     //      node18
     //        node19
     test: function* (testActor) {
       yield testActor.eval(`
-        let node18 = content.document.querySelector("#node18");
-        let node20 = content.document.querySelector("#node20");
-        let node1 = content.document.querySelector("#node1");
+        let node18 = document.querySelector("#node18");
+        let node20 = document.querySelector("#node20");
+        let node1 = document.querySelector("#node1");
         node1.appendChild(node20);
         node20.appendChild(node18);
       `);
     },
     check: function* (inspector) {
       yield inspector.markup.expandAll();
 
       let {children} = yield getContainerForSelector("#node1", inspector);
--- a/devtools/client/inspector/markup/test/browser_markup_mutation_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_mutation_02.js
@@ -20,35 +20,35 @@ const TEST_URL = URL_ROOT + "doc_markup_
 //   flash instead of the whole node
 // - flashedNode: [optional] the css selector of the node that is expected to
 //   flash in the markup-view as a result of the mutation.
 //   If missing, the rootNode (".list") will be expected to flash
 const TEST_DATA = [{
   desc: "Adding a new node should flash the new node",
   mutate: function* (testActor) {
     yield testActor.eval(`
-      let newLi = content.document.createElement("LI");
+      let newLi = document.createElement("LI");
       newLi.textContent = "new list item";
-      content.document.querySelector(".list").appendChild(newLi);
+      document.querySelector(".list").appendChild(newLi);
     `);
   },
   flashedNode: ".list li:nth-child(3)"
 }, {
   desc: "Removing a node should flash its parent",
   mutate: function* (testActor) {
     yield testActor.eval(`
-      let root = content.document.querySelector(".list");
+      let root = document.querySelector(".list");
       root.removeChild(root.lastElementChild);
     `);
   }
 }, {
   desc: "Re-appending an existing node should only flash this node",
   mutate: function* (testActor) {
     yield testActor.eval(`
-      let root = content.document.querySelector(".list");
+      let root = document.querySelector(".list");
       root.appendChild(root.firstElementChild);
     `);
   },
   flashedNode: ".list .item:last-child"
 }, {
   desc: "Adding an attribute should flash the attribute",
   attribute: "test-name",
   mutate: function* (testActor) {
@@ -67,30 +67,30 @@ const TEST_DATA = [{
   mutate: function* (testActor) {
     yield testActor.setAttribute(".list", "class", "list value-" + Date.now());
   }
 }, {
   desc: "Multiple changes to an attribute should flash the attribute",
   attribute: "class",
   mutate: function* (testActor) {
     yield testActor.eval(`
-      let root = content.document.querySelector(".list");
+      let root = document.querySelector(".list");
       root.removeAttribute("class");
       root.setAttribute("class", "list value-" + Date.now());
       root.setAttribute("class", "list value-" + Date.now());
       root.removeAttribute("class");
       root.setAttribute("class", "list value-" + Date.now());
       root.setAttribute("class", "list value-" + Date.now());
     `);
   }
 }, {
   desc: "Removing an attribute should flash the node",
   mutate: function* (testActor) {
     yield testActor.eval(`
-      let root = content.document.querySelector(".list");
+      let root = document.querySelector(".list");
       root.removeAttribute("class");
     `);
   }
 }];
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
 
--- a/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_node_not_displayed_02.js
@@ -10,55 +10,55 @@
 const TEST_URL = URL_ROOT + "doc_markup_not_displayed.html";
 const TEST_DATA = [
   {
     desc: "Hiding a node by creating a new stylesheet",
     selector: "#normal-div",
     before: true,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        let div = content.document.createElement("div");
+        let div = document.createElement("div");
         div.id = "new-style";
         div.innerHTML = "<style>#normal-div {display:none;}</style>";
-        content.document.body.appendChild(div);
+        document.body.appendChild(div);
       `);
     },
     after: false
   },
   {
     desc: "Showing a node by deleting an existing stylesheet",
     selector: "#normal-div",
     before: false,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        content.document.getElementById("new-style").remove();
+        document.getElementById("new-style").remove();
       `);
     },
     after: true
   },
   {
     desc: "Hiding a node by changing its style property",
     selector: "#display-none",
     before: false,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        let node = content.document.querySelector("#display-none");
+        let node = document.querySelector("#display-none");
         node.style.display = "block";
       `);
     },
     after: true
   },
   {
     desc: "Showing a node by removing its hidden attribute",
     selector: "#hidden-true",
     before: false,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        content.document.querySelector("#hidden-true")
-                        .removeAttribute("hidden");
+        document.querySelector("#hidden-true")
+                .removeAttribute("hidden");
       `);
     },
     after: true
   },
   {
     desc: "Hiding a node by adding a hidden attribute",
     selector: "#hidden-true",
     before: true,
@@ -68,45 +68,45 @@ const TEST_DATA = [
     after: false
   },
   {
     desc: "Showing a node by changin a stylesheet's rule",
     selector: "#hidden-via-stylesheet",
     before: false,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        content.document.styleSheets[0]
-                        .cssRules[0].style
-                        .setProperty("display", "inline");
+        document.styleSheets[0]
+                .cssRules[0].style
+                .setProperty("display", "inline");
       `);
     },
     after: true
   },
   {
     desc: "Hiding a node by adding a new rule to a stylesheet",
     selector: "#hidden-via-stylesheet",
     before: true,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        content.document.styleSheets[0].insertRule(
+        document.styleSheets[0].insertRule(
           "#hidden-via-stylesheet {display: none;}", 1);
       `);
     },
     after: false
   },
   {
     desc: "Hiding a node by adding a class that matches an existing rule",
     selector: "#normal-div",
     before: true,
     changeStyle: function* (testActor) {
       yield testActor.eval(`
-        content.document.styleSheets[0].insertRule(
+        document.styleSheets[0].insertRule(
           ".a-new-class {display: none;}", 2);
-        content.document.querySelector("#normal-div")
-                        .classList.add("a-new-class");
+        document.querySelector("#normal-div")
+                .classList.add("a-new-class");
       `);
     },
     after: false
   }
 ];
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
--- a/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js
+++ b/devtools/client/inspector/markup/test/browser_markup_update-on-navigtion.js
@@ -10,17 +10,17 @@ const URL_2 = URL_ROOT + "doc_markup_upd
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(URL_1);
 
   assertMarkupViewIsLoaded();
   yield selectNode("#one", inspector);
 
   let willNavigate = inspector.target.once("will-navigate");
-  yield testActor.eval(`content.location = "${URL_2}"`);
+  yield testActor.eval(`window.location = "${URL_2}"`);
 
   info("Waiting for will-navigate");
   yield willNavigate;
 
   info("Navigation to page 2 has started, the inspector should be empty");
   assertMarkupViewIsEmpty();
 
   info("Waiting for new-root");
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -107,17 +107,17 @@ Task.async(function* (selector, inspecto
  * Retrieve the nodeValue for the firstChild of a provided selector on the content page.
  *
  * @param {String} selector
  * @param {TestActorFront} testActor The current TestActorFront instance.
  * @return {String} the nodeValue of the first
  */
 function* getFirstChildNodeValue(selector, testActor) {
   let nodeValue = yield testActor.eval(`
-    content.document.querySelector("${selector}").firstChild.nodeValue;
+    document.querySelector("${selector}").firstChild.nodeValue;
   `);
   return nodeValue;
 }
 
 /**
  * Using the markupview's _waitForChildren function, wait for all queued
  * children updates to be handled.
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
--- a/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js
@@ -32,13 +32,13 @@ add_task(function* () {
   let onHighlighterShown = highlighters.once("grid-highlighter-shown");
   gridToggle.click();
   yield onHighlighterShown;
   ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
 
   let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
   info("Remove the #grid container in the content page");
   testActor.eval(`
-    content.document.querySelector("#grid").remove();
+    document.querySelector("#grid").remove();
   `);
   yield onHighlighterHidden;
   ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden.");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_05.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shapes-toggle_05.js
@@ -31,13 +31,13 @@ add_task(function* () {
   let onHighlighterShown = highlighters.once("shapes-highlighter-shown");
   shapeToggle.click();
   yield onHighlighterShown;
   ok(highlighters.shapesHighlighterShown, "CSS shapes highlighter is shown.");
 
   let onHighlighterHidden = highlighters.once("shapes-highlighter-hidden");
   info("Remove the #shapes container in the content page");
   testActor.eval(`
-    content.document.querySelector("#shape").remove();
+    document.querySelector("#shape").remove();
   `);
   yield onHighlighterHidden;
   ok(!highlighters.shapesHighlighterShown, "CSS shapes highlighter is hidden.");
 });
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -52,18 +52,18 @@ addTab = function (url) {
  * @param {TestActor} testActor
  * @param {String} selector
  *        The selector used to obtain the element.
  * @param {String} name
  *        name of the property.
  */
 function getStyle(testActor, selector, propName) {
   return testActor.eval(`
-    content.document.querySelector("${selector}")
-                    .style.getPropertyValue("${propName}");
+    document.querySelector("${selector}")
+            .style.getPropertyValue("${propName}");
   `);
 }
 
 /**
  * When a tooltip is closed, this ends up "commiting" the value changed within
  * the tooltip (e.g. the color in case of a colorpicker) which, in turn, ends up
  * setting the value of the corresponding css property in the rule-view.
  * Use this function to close the tooltip and make sure the test waits for the
--- a/devtools/client/inspector/test/browser_inspector_menu-06-other.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-06-other.js
@@ -79,17 +79,17 @@ add_task(function* () {
     let allMenuItems = openContextMenuAndGetAllItems(inspector);
     let deleteNode = allMenuItems.find(item => item.id === "node-menu-delete");
     deleteNode.click();
 
     yield new Promise(resolve => {
       executeSoon(resolve);
     });
 
-    ok((yield testActor.eval("!!content.document.documentElement")),
+    ok((yield testActor.eval("!!document.documentElement")),
        "Document element still alive.");
   }
 
   function* testScrollIntoView() {
     // Follow up bug to add this test - https://bugzilla.mozilla.org/show_bug.cgi?id=1154107
     todo(false, "Verify that node is scrolled into the viewport.");
   }
 });
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -30,17 +30,16 @@ const { classes: Cc, interfaces: Ci, uti
 var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 var { LoadContextInfo } = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 var { gDevTools } = require("devtools/client/framework/devtools");
 var Services = require("Services");
 var promise = require("promise");
-var defer = require("devtools/shared/defer");
 
 this.EXPORTED_SYMBOLS = ["AppCacheUtils"];
 
 function AppCacheUtils(documentOrUri) {
   this._parseManifest = this._parseManifest.bind(this);
 
   if (documentOrUri) {
     if (typeof documentOrUri == "string") {
@@ -53,206 +52,204 @@ function AppCacheUtils(documentOrUri) {
 }
 
 AppCacheUtils.prototype = {
   get cachePath() {
     return "";
   },
 
   validateManifest: function ACU_validateManifest() {
-    let deferred = defer();
-    this.errors = [];
-    // Check for missing manifest.
-    this._getManifestURI().then(manifestURI => {
-      this.manifestURI = manifestURI;
+    return new Promise((resolve, reject) => {
+      this.errors = [];
+      // Check for missing manifest.
+      this._getManifestURI().then(manifestURI => {
+        this.manifestURI = manifestURI;
 
-      if (!this.manifestURI) {
-        this._addError(0, "noManifest");
-        deferred.resolve(this.errors);
-      }
+        if (!this.manifestURI) {
+          this._addError(0, "noManifest");
+          resolve(this.errors);
+        }
 
-      this._getURIInfo(this.manifestURI).then(uriInfo => {
-        this._parseManifest(uriInfo).then(() => {
-          // Sort errors by line number.
-          this.errors.sort(function (a, b) {
-            return a.line - b.line;
+        this._getURIInfo(this.manifestURI).then(uriInfo => {
+          this._parseManifest(uriInfo).then(() => {
+            // Sort errors by line number.
+            this.errors.sort(function (a, b) {
+              return a.line - b.line;
+            });
+            resolve(this.errors);
           });
-          deferred.resolve(this.errors);
         });
       });
     });
-
-    return deferred.promise;
   },
 
   _parseManifest: function ACU__parseManifest(uriInfo) {
-    let deferred = defer();
-    let manifestName = uriInfo.name;
-    let manifestLastModified = new Date(uriInfo.responseHeaders["last-modified"]);
+    return new Promise((resolve, reject) => {
+      let manifestName = uriInfo.name;
+      let manifestLastModified = new Date(uriInfo.responseHeaders["last-modified"]);
 
-    if (uriInfo.charset.toLowerCase() != "utf-8") {
-      this._addError(0, "notUTF8", uriInfo.charset);
-    }
+      if (uriInfo.charset.toLowerCase() != "utf-8") {
+        this._addError(0, "notUTF8", uriInfo.charset);
+      }
 
-    if (uriInfo.mimeType != "text/cache-manifest") {
-      this._addError(0, "badMimeType", uriInfo.mimeType);
-    }
+      if (uriInfo.mimeType != "text/cache-manifest") {
+        this._addError(0, "badMimeType", uriInfo.mimeType);
+      }
 
-    let parser = new ManifestParser(uriInfo.text, this.manifestURI);
-    let parsed = parser.parse();
+      let parser = new ManifestParser(uriInfo.text, this.manifestURI);
+      let parsed = parser.parse();
 
-    if (parsed.errors.length > 0) {
-      this.errors.push.apply(this.errors, parsed.errors);
-    }
+      if (parsed.errors.length > 0) {
+        this.errors.push.apply(this.errors, parsed.errors);
+      }
 
-    // Check for duplicate entries.
-    let dupes = {};
-    for (let parsedUri of parsed.uris) {
-      dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
-      dupes[parsedUri.uri].push({
-        line: parsedUri.line,
-        section: parsedUri.section,
-        original: parsedUri.original
-      });
-    }
-    for (let [uri, value] of Object.entries(dupes)) {
-      if (value.length > 1) {
-        this._addError(0, "duplicateURI", uri, JSON.stringify(value));
+      // Check for duplicate entries.
+      let dupes = {};
+      for (let parsedUri of parsed.uris) {
+        dupes[parsedUri.uri] = dupes[parsedUri.uri] || [];
+        dupes[parsedUri.uri].push({
+          line: parsedUri.line,
+          section: parsedUri.section,
+          original: parsedUri.original
+        });
+      }
+      for (let [uri, value] of Object.entries(dupes)) {
+        if (value.length > 1) {
+          this._addError(0, "duplicateURI", uri, JSON.stringify(value));
+        }
       }
-    }
 
-    // Loop through network entries making sure that fallback and cache don't
-    // contain uris starting with the network uri.
-    for (let neturi of parsed.uris) {
-      if (neturi.section == "NETWORK") {
+      // Loop through network entries making sure that fallback and cache don't
+      // contain uris starting with the network uri.
+      for (let neturi of parsed.uris) {
+        if (neturi.section == "NETWORK") {
+          for (let parsedUri of parsed.uris) {
+            if (parsedUri.section !== "NETWORK" &&
+                parsedUri.uri.startsWith(neturi.uri)) {
+              this._addError(neturi.line, "networkBlocksURI", neturi.line,
+                             neturi.original, parsedUri.line, parsedUri.original,
+                             parsedUri.section);
+            }
+          }
+        }
+      }
+
+      // Loop through fallback entries making sure that fallback and cache don't
+      // contain uris starting with the network uri.
+      for (let fb of parsed.fallbacks) {
         for (let parsedUri of parsed.uris) {
-          if (parsedUri.section !== "NETWORK" &&
-              parsedUri.uri.startsWith(neturi.uri)) {
-            this._addError(neturi.line, "networkBlocksURI", neturi.line,
-                           neturi.original, parsedUri.line, parsedUri.original,
+          if (parsedUri.uri.startsWith(fb.namespace)) {
+            this._addError(fb.line, "fallbackBlocksURI", fb.line,
+                           fb.original, parsedUri.line, parsedUri.original,
                            parsedUri.section);
           }
         }
       }
-    }
 
-    // Loop through fallback entries making sure that fallback and cache don't
-    // contain uris starting with the network uri.
-    for (let fb of parsed.fallbacks) {
-      for (let parsedUri of parsed.uris) {
-        if (parsedUri.uri.startsWith(fb.namespace)) {
-          this._addError(fb.line, "fallbackBlocksURI", fb.line,
-                         fb.original, parsedUri.line, parsedUri.original,
-                         parsedUri.section);
-        }
-      }
-    }
+      // Check that all resources exist and that their cach-control headers are
+      // not set to no-store.
+      let current = -1;
+      for (let i = 0, len = parsed.uris.length; i < len; i++) {
+        let parsedUri = parsed.uris[i];
+        this._getURIInfo(parsedUri.uri).then(uriInfo => {
+          current++;
+
+          if (uriInfo.success) {
+            // Check that the resource was not modified after the manifest was last
+            // modified. If it was then the manifest file should be refreshed.
+            let resourceLastModified =
+              new Date(uriInfo.responseHeaders["last-modified"]);
 
-    // Check that all resources exist and that their cach-control headers are
-    // not set to no-store.
-    let current = -1;
-    for (let i = 0, len = parsed.uris.length; i < len; i++) {
-      let parsedUri = parsed.uris[i];
-      this._getURIInfo(parsedUri.uri).then(uriInfo => {
-        current++;
+            if (manifestLastModified < resourceLastModified) {
+              this._addError(parsedUri.line, "fileChangedButNotManifest",
+                             uriInfo.name, manifestName, parsedUri.line);
+            }
 
-        if (uriInfo.success) {
-          // Check that the resource was not modified after the manifest was last
-          // modified. If it was then the manifest file should be refreshed.
-          let resourceLastModified =
-            new Date(uriInfo.responseHeaders["last-modified"]);
-
-          if (manifestLastModified < resourceLastModified) {
-            this._addError(parsedUri.line, "fileChangedButNotManifest",
-                           uriInfo.name, manifestName, parsedUri.line);
+            // If cache-control: no-store the file will not be added to the
+            // appCache.
+            if (uriInfo.nocache) {
+              this._addError(parsedUri.line, "cacheControlNoStore",
+                             parsedUri.original, parsedUri.line);
+            }
+          } else if (parsedUri.original !== "*") {
+            this._addError(parsedUri.line, "notAvailable",
+                           parsedUri.original, parsedUri.line);
           }
 
-          // If cache-control: no-store the file will not be added to the
-          // appCache.
-          if (uriInfo.nocache) {
-            this._addError(parsedUri.line, "cacheControlNoStore",
-                           parsedUri.original, parsedUri.line);
+          if (current == len - 1) {
+            resolve();
           }
-        } else if (parsedUri.original !== "*") {
-          this._addError(parsedUri.line, "notAvailable",
-                         parsedUri.original, parsedUri.line);
-        }
-
-        if (current == len - 1) {
-          deferred.resolve();
-        }
-      });
-    }
-
-    return deferred.promise;
+        });
+      }
+    });
   },
 
   _getURIInfo: function ACU__getURIInfo(uri) {
-    let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
-                        .createInstance(Ci.nsIScriptableInputStream);
-    let deferred = defer();
-    let buffer = "";
-    var channel = NetUtil.newChannel({
-      uri: uri,
-      loadUsingSystemPrincipal: true,
-      securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
-    });
+    return new Promise((resolve, reject) => {
+      let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
+                          .createInstance(Ci.nsIScriptableInputStream);
+      let buffer = "";
+      var channel = NetUtil.newChannel({
+        uri: uri,
+        loadUsingSystemPrincipal: true,
+        securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
+      });
 
-    // Avoid the cache:
-    channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
-    channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+      // Avoid the cache:
+      channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+      channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
 
-    channel.asyncOpen2({
-      onStartRequest: function (request, context) {
-        // This empty method is needed in order for onDataAvailable to be
-        // called.
-      },
+      channel.asyncOpen2({
+        onStartRequest: function (request, context) {
+          // This empty method is needed in order for onDataAvailable to be
+          // called.
+        },
 
-      onDataAvailable: function (request, context, stream, offset, count) {
-        request.QueryInterface(Ci.nsIHttpChannel);
-        inputStream.init(stream);
-        buffer = buffer.concat(inputStream.read(count));
-      },
+        onDataAvailable: function (request, context, stream, offset, count) {
+          request.QueryInterface(Ci.nsIHttpChannel);
+          inputStream.init(stream);
+          buffer = buffer.concat(inputStream.read(count));
+        },
 
-      onStopRequest: function onStartRequest(request, context, statusCode) {
-        if (statusCode === 0) {
-          request.QueryInterface(Ci.nsIHttpChannel);
+        onStopRequest: function onStartRequest(request, context, statusCode) {
+          if (statusCode === 0) {
+            request.QueryInterface(Ci.nsIHttpChannel);
 
-          let result = {
-            name: request.name,
-            success: request.requestSucceeded,
-            status: request.responseStatus + " - " + request.responseStatusText,
-            charset: request.contentCharset || "utf-8",
-            mimeType: request.contentType,
-            contentLength: request.contentLength,
-            nocache: request.isNoCacheResponse() || request.isNoStoreResponse(),
-            prePath: request.URI.prePath + "/",
-            text: buffer
-          };
+            let result = {
+              name: request.name,
+              success: request.requestSucceeded,
+              status: request.responseStatus + " - " + request.responseStatusText,
+              charset: request.contentCharset || "utf-8",
+              mimeType: request.contentType,
+              contentLength: request.contentLength,
+              nocache: request.isNoCacheResponse() || request.isNoStoreResponse(),
+              prePath: request.URI.prePath + "/",
+              text: buffer
+            };
 
-          result.requestHeaders = {};
-          request.visitRequestHeaders(function (header, value) {
-            result.requestHeaders[header.toLowerCase()] = value;
-          });
+            result.requestHeaders = {};
+            request.visitRequestHeaders(function (header, value) {
+              result.requestHeaders[header.toLowerCase()] = value;
+            });
 
-          result.responseHeaders = {};
-          request.visitResponseHeaders(function (header, value) {
-            result.responseHeaders[header.toLowerCase()] = value;
-          });
+            result.responseHeaders = {};
+            request.visitResponseHeaders(function (header, value) {
+              result.responseHeaders[header.toLowerCase()] = value;
+            });
 
-          deferred.resolve(result);
-        } else {
-          deferred.resolve({
-            name: request.name,
-            success: false
-          });
+            resolve(result);
+          } else {
+            resolve({
+              name: request.name,
+              success: false
+            });
+          }
         }
-      }
+      });
     });
-    return deferred.promise;
   },
 
   listEntries: function ACU_show(searchTerm) {
     if (!Services.prefs.getBoolPref("browser.cache.disk.enable")) {
       throw new Error(l10n.GetStringFromName("cacheDisabled"));
     }
 
     let entries = [];
@@ -308,53 +305,52 @@ AppCacheUtils.prototype = {
 
     let appCacheStorage = Services.cache2.appCacheStorage(LoadContextInfo.default, null);
     appCacheStorage.asyncEvictStorage({
       onCacheEntryDoomed: function (result) {}
     });
   },
 
   _getManifestURI: function ACU__getManifestURI() {
-    let deferred = defer();
+    return new Promise((resolve, reject) => {
+      let getURI = () => {
+        let htmlNode = this.doc.querySelector("html[manifest]");
+        if (htmlNode) {
+          let pageUri = this.doc.location ? this.doc.location.href : this.uri;
+          let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
+          let manifestURI = htmlNode.getAttribute("manifest");
 
-    let getURI = () => {
-      let htmlNode = this.doc.querySelector("html[manifest]");
-      if (htmlNode) {
-        let pageUri = this.doc.location ? this.doc.location.href : this.uri;
-        let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
-        let manifestURI = htmlNode.getAttribute("manifest");
+          if (manifestURI.startsWith("/")) {
+            manifestURI = manifestURI.substr(1);
+          }
 
-        if (manifestURI.startsWith("/")) {
-          manifestURI = manifestURI.substr(1);
+          return origin + manifestURI;
         }
-
-        return origin + manifestURI;
-      }
-    };
+      };
 
-    if (this.doc) {
-      let uri = getURI();
-      return promise.resolve(uri);
-    } else {
-      this._getURIInfo(this.uri).then(uriInfo => {
-        if (uriInfo.success) {
-          let html = uriInfo.text;
-          let parser = _DOMParser;
-          this.doc = parser.parseFromString(html, "text/html");
-          let uri = getURI();
-          deferred.resolve(uri);
-        } else {
-          this.errors.push({
-            line: 0,
-            msg: l10n.GetStringFromName("invalidURI")
-          });
-        }
-      });
-    }
-    return deferred.promise;
+      if (this.doc) {
+        let uri = getURI();
+        return resolve(uri);
+      } else {
+        this._getURIInfo(this.uri).then(uriInfo => {
+          if (uriInfo.success) {
+            let html = uriInfo.text;
+            let parser = _DOMParser;
+            this.doc = parser.parseFromString(html, "text/html");
+            let uri = getURI();
+            resolve(uri);
+          } else {
+            this.errors.push({
+              line: 0,
+              msg: l10n.GetStringFromName("invalidURI")
+            });
+          }
+        });
+      }
+    });
   },
 
   _addError: function ACU__addError(line, l10nString, ...params) {
     let msg;
 
     if (params) {
       msg = l10n.formatStringFromName(l10nString, params, params.length);
     } else {
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const promise = require("promise");
-const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const { TargetFactory } = require("devtools/client/framework/target");
 const Telemetry = require("devtools/client/shared/telemetry");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 const {Task} = require("devtools/shared/task");
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
@@ -304,35 +303,33 @@ DeveloperToolbar.prototype.toggle = func
  * toggle the toolbar.
  * The method returns a promise that would be resolved once focused; if the toolbar is not
  * visible yet it will be automatically shown.
  */
 DeveloperToolbar.prototype.focus = function () {
   if (this.visible) {
     // If the toolbar was just inserted, the <textbox> may still have
     // its binding in process of being applied and not be focusable yet
-    let waitForBinding = defer();
-
-    let checkBinding = () => {
-      // Bail out if the toolbar has been destroyed in the meantime
-      if (!this._input) {
-        waitForBinding.reject();
-        return;
-      }
-      // mInputField is a xbl field of <xul:textbox>
-      if (typeof this._input.mInputField != "undefined") {
-        this._input.focus();
-        waitForBinding.resolve();
-      } else {
-        this._input.ownerDocument.defaultView.setTimeout(checkBinding, 50);
-      }
-    };
-    checkBinding();
-
-    return waitForBinding.promise;
+    return new Promise((resolve, reject) => {
+      let checkBinding = () => {
+        // Bail out if the toolbar has been destroyed in the meantime
+        if (!this._input) {
+          reject();
+          return;
+        }
+        // mInputField is a xbl field of <xul:textbox>
+        if (typeof this._input.mInputField != "undefined") {
+          this._input.focus();
+          resolve();
+        } else {
+          this._input.ownerDocument.defaultView.setTimeout(checkBinding, 50);
+        }
+      };
+      checkBinding();
+    });
   }
 
   return this.show(true);
 };
 
 /**
  * Called from browser.xul in response to menu-click or keyboard shortcut to
  * toggle the toolbar
@@ -812,87 +809,86 @@ OutputPanel.create = function (devtoolba
   let outputPanel = Object.create(OutputPanel.prototype);
   return outputPanel._init(devtoolbar);
 };
 
 /**
  * @private See OutputPanel.create
  */
 OutputPanel.prototype._init = function (devtoolbar) {
-  this._devtoolbar = devtoolbar;
-  this._input = this._devtoolbar._input;
-  this._toolbar = this._devtoolbar._doc.getElementById("developer-toolbar");
+  return new Promise((resolve, reject) => {
+    this._devtoolbar = devtoolbar;
+    this._input = this._devtoolbar._input;
+    this._toolbar = this._devtoolbar._doc.getElementById("developer-toolbar");
 
-  /*
-  <tooltip|panel id="gcli-output"
-         noautofocus="true"
-         noautohide="true"
-         class="gcli-panel">
-    <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
-                 id="gcli-output-frame"
-                 src="chrome://devtools/content/commandline/commandlineoutput.xhtml"
-                 sandbox="allow-same-origin"/>
-  </tooltip|panel>
-  */
+    /*
+    <tooltip|panel id="gcli-output"
+           noautofocus="true"
+           noautohide="true"
+           class="gcli-panel">
+      <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
+                   id="gcli-output-frame"
+                   src="chrome://devtools/content/commandline/commandlineoutput.xhtml"
+                   sandbox="allow-same-origin"/>
+    </tooltip|panel>
+    */
 
-  // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
-  // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
-  this._panel = this._devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
+    // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
+    this._panel = this._devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
 
-  this._panel.id = "gcli-output";
-  this._panel.classList.add("gcli-panel");
+    this._panel.id = "gcli-output";
+    this._panel.classList.add("gcli-panel");
 
-  if (isLinux) {
-    this.canHide = false;
-    this._onpopuphiding = this._onpopuphiding.bind(this);
-    this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
-  } else {
-    this._panel.setAttribute("noautofocus", "true");
-    this._panel.setAttribute("noautohide", "true");
+    if (isLinux) {
+      this.canHide = false;
+      this._onpopuphiding = this._onpopuphiding.bind(this);
+      this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
+    } else {
+      this._panel.setAttribute("noautofocus", "true");
+      this._panel.setAttribute("noautohide", "true");
 
-    // Bug 692348: On Windows and OSX if a panel has no content and no height
-    // openPopup fails to display it. Setting the height to 1px alows the panel
-    // to be displayed before has content or a real height i.e. the first time
-    // it is displayed.
-    this._panel.setAttribute("height", "1px");
-  }
+      // Bug 692348: On Windows and OSX if a panel has no content and no height
+      // openPopup fails to display it. Setting the height to 1px alows the panel
+      // to be displayed before has content or a real height i.e. the first time
+      // it is displayed.
+      this._panel.setAttribute("height", "1px");
+    }
 
-  this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
+    this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
-  this._frame = this._devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
-  this._frame.id = "gcli-output-frame";
-  this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlineoutput.xhtml");
-  this._frame.setAttribute("sandbox", "allow-same-origin");
-  this._panel.appendChild(this._frame);
+    this._frame = this._devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
+    this._frame.id = "gcli-output-frame";
+    this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlineoutput.xhtml");
+    this._frame.setAttribute("sandbox", "allow-same-origin");
+    this._panel.appendChild(this._frame);
 
-  this.displayedOutput = undefined;
+    this.displayedOutput = undefined;
 
-  this._update = this._update.bind(this);
+    this._update = this._update.bind(this);
 
-  // Wire up the element from the iframe, and resolve the promise
-  let deferred = defer();
-  let onload = () => {
-    this._frame.removeEventListener("load", onload, true);
+    // Wire up the element from the iframe, and resolve the promise
+    let onload = () => {
+      this._frame.removeEventListener("load", onload, true);
 
-    this.document = this._frame.contentDocument;
-    this._copyTheme();
+      this.document = this._frame.contentDocument;
+      this._copyTheme();
 
-    this._div = this.document.getElementById("gcli-output-root");
-    this._div.classList.add("gcli-row-out");
-    this._div.setAttribute("aria-live", "assertive");
+      this._div = this.document.getElementById("gcli-output-root");
+      this._div.classList.add("gcli-row-out");
+      this._div.setAttribute("aria-live", "assertive");
 
-    let styles = this._toolbar.ownerDocument.defaultView
-                    .getComputedStyle(this._toolbar);
-    this._div.setAttribute("dir", styles.direction);
+      let styles = this._toolbar.ownerDocument.defaultView
+                      .getComputedStyle(this._toolbar);
+      this._div.setAttribute("dir", styles.direction);
 
-    deferred.resolve(this);
-  };
-  this._frame.addEventListener("load", onload, true);
-
-  return deferred.promise;
+      resolve(this);
+    };
+    this._frame.addEventListener("load", onload, true);
+  });
 };
 
 /* Copy the current devtools theme attribute into the iframe,
    so it can be styled correctly. */
 OutputPanel.prototype._copyTheme = function () {
   if (this.document) {
     let theme = this._devtoolbar._doc.getElementById("browser-bottombox")
                   .getAttribute("devtoolstheme");
@@ -1131,88 +1127,86 @@ TooltipPanel.create = function (devtoolb
   let tooltipPanel = Object.create(TooltipPanel.prototype);
   return tooltipPanel._init(devtoolbar);
 };
 
 /**
  * @private See TooltipPanel.create
  */
 TooltipPanel.prototype._init = function (devtoolbar) {
-  let deferred = defer();
-
-  this._devtoolbar = devtoolbar;
-  this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
-  this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
-  this._dimensions = { start: 0, end: 0 };
+  return new Promise((resolve, reject) => {
+    this._devtoolbar = devtoolbar;
+    this._input = devtoolbar._doc.querySelector(".gclitoolbar-input-node");
+    this._toolbar = devtoolbar._doc.querySelector("#developer-toolbar");
+    this._dimensions = { start: 0, end: 0 };
 
-  /*
-  <tooltip|panel id="gcli-tooltip"
-         type="arrow"
-         noautofocus="true"
-         noautohide="true"
-         class="gcli-panel">
-    <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
-                 id="gcli-tooltip-frame"
-                 src="chrome://devtools/content/commandline/commandlinetooltip.xhtml"
-                 flex="1"
-                 sandbox="allow-same-origin"/>
-  </tooltip|panel>
-  */
+    /*
+    <tooltip|panel id="gcli-tooltip"
+           type="arrow"
+           noautofocus="true"
+           noautohide="true"
+           class="gcli-panel">
+      <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
+                   id="gcli-tooltip-frame"
+                   src="chrome://devtools/content/commandline/commandlinetooltip.xhtml"
+                   flex="1"
+                   sandbox="allow-same-origin"/>
+    </tooltip|panel>
+    */
 
-  // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
-  // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
-  this._panel = devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
+    // TODO: Switch back from tooltip to panel when metacity focus issue is fixed:
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=780102
+    this._panel = devtoolbar._doc.createElement(isLinux ? "tooltip" : "panel");
 
-  this._panel.id = "gcli-tooltip";
-  this._panel.classList.add("gcli-panel");
+    this._panel.id = "gcli-tooltip";
+    this._panel.classList.add("gcli-panel");
 
-  if (isLinux) {
-    this.canHide = false;
-    this._onpopuphiding = this._onpopuphiding.bind(this);
-    this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
-  } else {
-    this._panel.setAttribute("noautofocus", "true");
-    this._panel.setAttribute("noautohide", "true");
+    if (isLinux) {
+      this.canHide = false;
+      this._onpopuphiding = this._onpopuphiding.bind(this);
+      this._panel.addEventListener("popuphiding", this._onpopuphiding, true);
+    } else {
+      this._panel.setAttribute("noautofocus", "true");
+      this._panel.setAttribute("noautohide", "true");
 
-    // Bug 692348: On Windows and OSX if a panel has no content and no height
-    // openPopup fails to display it. Setting the height to 1px alows the panel
-    // to be displayed before has content or a real height i.e. the first time
-    // it is displayed.
-    this._panel.setAttribute("height", "1px");
-  }
+      // Bug 692348: On Windows and OSX if a panel has no content and no height
+      // openPopup fails to display it. Setting the height to 1px alows the panel
+      // to be displayed before has content or a real height i.e. the first time
+      // it is displayed.
+      this._panel.setAttribute("height", "1px");
+    }
 
-  this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
+    this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
-  this._frame = devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
-  this._frame.id = "gcli-tooltip-frame";
-  this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlinetooltip.xhtml");
-  this._frame.setAttribute("flex", "1");
-  this._frame.setAttribute("sandbox", "allow-same-origin");
-  this._panel.appendChild(this._frame);
+    this._frame = devtoolbar._doc.createElementNS(NS_XHTML, "iframe");
+    this._frame.id = "gcli-tooltip-frame";
+    this._frame.setAttribute("src", "chrome://devtools/content/commandline/commandlinetooltip.xhtml");
+    this._frame.setAttribute("flex", "1");
+    this._frame.setAttribute("sandbox", "allow-same-origin");
+    this._panel.appendChild(this._frame);
 
-  /**
-   * Wire up the element from the iframe, and resolve the promise.
-   */
-  let onload = () => {
-    this._frame.removeEventListener("load", onload, true);
+    /**
+     * Wire up the element from the iframe, and resolve the promise.
+     */
+    let onload = () => {
+      this._frame.removeEventListener("load", onload, true);
 
-    this.document = this._frame.contentDocument;
-    this._copyTheme();
-    this.hintElement = this.document.getElementById("gcli-tooltip-root");
-    this._connector = this.document.getElementById("gcli-tooltip-connector");
+      this.document = this._frame.contentDocument;
+      this._copyTheme();
+      this.hintElement = this.document.getElementById("gcli-tooltip-root");
+      this._connector = this.document.getElementById("gcli-tooltip-connector");
 
-    let styles = this._toolbar.ownerDocument.defaultView
-                    .getComputedStyle(this._toolbar);
-    this.hintElement.setAttribute("dir", styles.direction);
+      let styles = this._toolbar.ownerDocument.defaultView
+                      .getComputedStyle(this._toolbar);
+      this.hintElement.setAttribute("dir", styles.direction);
 
-    deferred.resolve(this);
-  };
-  this._frame.addEventListener("load", onload, true);
-
-  return deferred.promise;
+      resolve(this);
+    };
+    this._frame.addEventListener("load", onload, true);
+  });
 };
 
 /* Copy the current devtools theme attribute into the iframe,
    so it can be styled correctly. */
 TooltipPanel.prototype._copyTheme = function () {
   if (this.document) {
     let theme = this._devtoolbar._doc.getElementById("browser-bottombox")
                   .getAttribute("devtoolstheme");
--- a/devtools/client/shared/doorhanger.js
+++ b/devtools/client/shared/doorhanger.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
 const { Task } = require("devtools/shared/task");
-const defer = require("devtools/shared/defer");
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
 const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
 const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
 const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";
 
 /**
@@ -127,33 +126,31 @@ function setDoorhangerStyle(panel, frame
 
   frame.style.borderRadius = "5px";
   frame.setAttribute("flex", "1");
   frame.setAttribute("width", "450");
   frame.setAttribute("height", "179");
 }
 
 function onFrameLoad(frame) {
-  let { resolve, promise } = defer();
-
-  if (frame.contentWindow) {
-    let domHelper = new DOMHelpers(frame.contentWindow);
-    domHelper.onceDOMReady(resolve);
-  } else {
-    let callback = () => {
-      frame.removeEventListener("DOMContentLoaded", callback);
-      resolve();
-    };
-    frame.addEventListener("DOMContentLoaded", callback);
-  }
-
-  return promise;
+  return new Promise((resolve, reject) => {
+    if (frame.contentWindow) {
+      let domHelper = new DOMHelpers(frame.contentWindow);
+      domHelper.onceDOMReady(resolve);
+    } else {
+      let callback = () => {
+        frame.removeEventListener("DOMContentLoaded", callback);
+        resolve();
+      };
+      frame.addEventListener("DOMContentLoaded", callback);
+    }
+  });
 }
 
 function getGBrowser() {
   return Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
 }
 
 function wait(n) {
-  let { resolve, promise } = defer();
-  setTimeout(resolve, n);
-  return promise;
+  return new Promise((resolve, reject) => {
+    setTimeout(resolve, n);
+  });
 }
--- a/devtools/client/shared/frame-script-utils.js
+++ b/devtools/client/shared/frame-script-utils.js
@@ -2,17 +2,16 @@
  * 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/. */
 
 /* eslint-env browser */
 /* global addMessageListener, sendAsyncMessage, content */
 "use strict";
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 const {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const defer = require("devtools/shared/defer");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyGetter(this, "nsIProfilerModule", () => {
   return Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
 });
 
 addMessageListener("devtools:test:history", function ({ data }) {
   content.history[data.direction]();
@@ -46,42 +45,42 @@ addMessageListener("devtools:test:consol
  *        }
  *
  * @return Promise A promise that's resolved with object
  *         { status: XMLHttpRequest.status,
  *           response: XMLHttpRequest.response }
  *
  */
 function promiseXHR(data) {
-  let xhr = new content.XMLHttpRequest();
+  return new Promise((resolve, reject) => {
+    let xhr = new content.XMLHttpRequest();
 
-  let method = data.method || "GET";
-  let url = data.url || content.location.href;
-  let body = data.body || "";
+    let method = data.method || "GET";
+    let url = data.url || content.location.href;
+    let body = data.body || "";
 
-  if (data.nocache) {
-    url += "?devtools-cachebust=" + Math.random();
-  }
+    if (data.nocache) {
+      url += "?devtools-cachebust=" + Math.random();
+    }
 
-  let deferred = defer();
-  xhr.addEventListener("loadend", function (event) {
-    deferred.resolve({ status: xhr.status, response: xhr.response });
-  }, {once: true});
+    xhr.addEventListener("loadend", function (event) {
+      resolve({ status: xhr.status, response: xhr.response });
+    }, {once: true});
 
-  xhr.open(method, url);
+    xhr.open(method, url);
 
-  // Set request headers
-  if (data.requestHeaders) {
-    data.requestHeaders.forEach(header => {
-      xhr.setRequestHeader(header.name, header.value);
-    });
-  }
+    // Set request headers
+    if (data.requestHeaders) {
+      data.requestHeaders.forEach(header => {
+        xhr.setRequestHeader(header.name, header.value);
+      });
+    }
 
-  xhr.send(body);
-  return deferred.promise;
+    xhr.send(body);
+  });
 }
 
 /**
  * Performs XMLHttpRequest request(s) in the context of the page. The data
  * parameter can be either a single object or an array of objects described
  * below. The requests will be performed one at a time in the order they appear
  * in the data.
  *
--- a/devtools/client/shared/getjson.js
+++ b/devtools/client/shared/getjson.js
@@ -1,17 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {CC} = require("chrome");
-const defer = require("devtools/shared/defer");
-const promise = require("promise");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
 
 const XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
 
 /**
  * Downloads and caches a JSON file from an URL given by a pref.
@@ -20,57 +18,56 @@ const XMLHttpRequest = CC("@mozilla.org/
  *        The preference for the target URL
  *
  * @return {Promise}
  *         - Resolved with the JSON object in case of successful request
  *           or cache hit
  *         - Rejected with an error message in case of failure
  */
 exports.getJSON = function (prefName) {
-  let deferred = defer();
-  let xhr = new XMLHttpRequest();
-
-  // We used to store cached data in preferences, but now we use asyncStorage
-  // Migration step: if it still exists, move this now useless preference in its
-  // new location and clear it
-  if (Services.prefs.prefHasUserValue(prefName + "_cache")) {
-    let json = Services.prefs.getCharPref(prefName + "_cache");
-    asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
-      // Could not move the cache, let's log the error but continue
-      console.error(e);
-    });
-    Services.prefs.clearUserPref(prefName + "_cache");
-  }
+  return new Promise((resolve, reject) => {
+    let xhr = new XMLHttpRequest();
 
-  function readFromStorage(networkError) {
-    asyncStorage.getItem(prefName + "_cache").then(function (json) {
-      if (!json) {
-        return promise.reject("Empty cache for " + prefName);
-      }
-      return deferred.resolve(json);
-    }).catch(function (e) {
-      deferred.reject("JSON not available, CDN error: " + networkError +
-                      ", storage error: " + e);
-    });
-  }
-
-  xhr.onload = () => {
-    try {
-      let json = JSON.parse(xhr.responseText);
+    // We used to store cached data in preferences, but now we use asyncStorage
+    // Migration step: if it still exists, move this now useless preference in its
+    // new location and clear it
+    if (Services.prefs.prefHasUserValue(prefName + "_cache")) {
+      let json = Services.prefs.getCharPref(prefName + "_cache");
       asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
-        // Could not update cache, let's log the error but continue
+        // Could not move the cache, let's log the error but continue
         console.error(e);
       });
-      deferred.resolve(json);
-    } catch (e) {
-      readFromStorage(e);
+      Services.prefs.clearUserPref(prefName + "_cache");
     }
-  };
+
+    function readFromStorage(networkError) {
+      asyncStorage.getItem(prefName + "_cache").then(function (json) {
+        if (!json) {
+          return reject("Empty cache for " + prefName);
+        }
+        return resolve(json);
+      }).catch(function (e) {
+        reject("JSON not available, CDN error: " + networkError +
+                        ", storage error: " + e);
+      });
+    }
 
-  xhr.onerror = (e) => {
-    readFromStorage(e);
-  };
+    xhr.onload = () => {
+      try {
+        let json = JSON.parse(xhr.responseText);
+        asyncStorage.setItem(prefName + "_cache", json).catch(function (e) {
+          // Could not update cache, let's log the error but continue
+          console.error(e);
+        });
+        resolve(json);
+      } catch (e) {
+        readFromStorage(e);
+      }
+    };
 
-  xhr.open("get", Services.prefs.getCharPref(prefName));
-  xhr.send();
+    xhr.onerror = (e) => {
+      readFromStorage(e);
+    };
 
-  return deferred.promise;
+    xhr.open("get", Services.prefs.getCharPref(prefName));
+    xhr.send();
+  });
 };
--- a/devtools/client/shared/poller.js
+++ b/devtools/client/shared/poller.js
@@ -1,15 +1,13 @@
 /* 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";
-loader.lazyRequireGetter(this, "defer",
-  "promise", true);
 
 /**
  * @constructor Poller
  * Takes a function that is to be called on an interval,
  * and can be turned on and off via methods to execute `fn` on the interval
  * specified during `on`. If `fn` returns a promise, the polling waits for
  * that promise to resolve before waiting the interval to call again.
  *
@@ -58,30 +56,30 @@ Poller.prototype.on = function pollerOn(
 
 /**
  * Turns off polling. Returns a promise that resolves when
  * the last outstanding `fn` call finishes if it's an async function.
  *
  * @return {Promise}
  */
 Poller.prototype.off = function pollerOff() {
-  let { resolve, promise } = defer();
-  if (this._timer) {
-    clearTimeout(this._timer);
-    this._timer = null;
-  }
+  return new Promise((resolve, reject) => {
+    if (this._timer) {
+      clearTimeout(this._timer);
+      this._timer = null;
+    }
 
-  // Settle an inflight poll call before resolving
-  // if using a promise-backed poll function
-  if (this._inflight) {
-    this._inflight.then(resolve);
-  } else {
-    resolve();
-  }
-  return promise;
+    // Settle an inflight poll call before resolving
+    // if using a promise-backed poll function
+    if (this._inflight) {
+      this._inflight.then(resolve);
+    } else {
+      resolve();
+    }
+  });
 };
 
 /**
  * Turns off polling and removes the reference to the poller function.
  * Resolves when the last outstanding `fn` call finishes if it's an async
  * function.
  */
 Poller.prototype.destroy = function pollerDestroy() {
--- a/devtools/client/shared/redux/middleware/promise.js
+++ b/devtools/client/shared/redux/middleware/promise.js
@@ -1,54 +1,52 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { generateUUID } = require("devtools/shared/generate-uuid");
-const defer = require("devtools/shared/defer");
 const {
   entries, toObject, executeSoon
 } = require("devtools/shared/DevToolsUtils");
 const PROMISE = exports.PROMISE = "@@dispatch/promise";
 
 function promiseMiddleware({ dispatch, getState }) {
   return next => action => {
     if (!(PROMISE in action)) {
       return next(action);
     }
-
-    const promiseInst = action[PROMISE];
-    const seqId = generateUUID().toString();
-
-    // Create a new action that doesn't have the promise field and has
-    // the `seqId` field that represents the sequence id
-    action = Object.assign(
-      toObject(entries(action).filter(pair => pair[0] !== PROMISE)), { seqId }
-    );
-
-    dispatch(Object.assign({}, action, { status: "start" }));
-
     // Return the promise so action creators can still compose if they
     // want to.
-    const deferred = defer();
-    promiseInst.then(value => {
-      executeSoon(() => {
-        dispatch(Object.assign({}, action, {
-          status: "done",
-          value: value
-        }));
-        deferred.resolve(value);
-      });
-    }, error => {
-      executeSoon(() => {
-        dispatch(Object.assign({}, action, {
-          status: "error",
-          error: error.message || error
-        }));
-        deferred.reject(error);
+    return new Promise((resolve, reject) => {
+      const promiseInst = action[PROMISE];
+      const seqId = generateUUID().toString();
+
+      // Create a new action that doesn't have the promise field and has
+      // the `seqId` field that represents the sequence id
+      action = Object.assign(
+        toObject(entries(action).filter(pair => pair[0] !== PROMISE)), { seqId }
+      );
+
+      dispatch(Object.assign({}, action, { status: "start" }));
+
+      promiseInst.then(value => {
+        executeSoon(() => {
+          dispatch(Object.assign({}, action, {
+            status: "done",
+            value: value
+          }));
+          resolve(value);
+        });
+      }, error => {
+        executeSoon(() => {
+          dispatch(Object.assign({}, action, {
+            status: "error",
+            error: error.message || error
+          }));
+          reject(error);
+        });
       });
     });
-    return deferred.promise;
   };
 }
 
 exports.promise = promiseMiddleware;
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -1,27 +1,27 @@
 /* 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/. */
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 .theme-light {
-  --rule-highlight-background-color: #ffee99;
+  --rule-highlight-background-color: var(--theme-highlight-yellow);
   --rule-overridden-item-border-color: var(--theme-content-color3);
   --rule-header-background-color: var(--theme-toolbar-background);
 }
 
 .theme-dark {
-  --rule-highlight-background-color: #594724;
+  --rule-highlight-background-color: #521C76;
   --rule-overridden-item-border-color: var(--theme-content-color1);
   --rule-header-background-color: #141416;
 }
 
 .theme-firebug {
-  --rule-highlight-background-color: #ffee99;
+  --rule-highlight-background-color: var(--theme-highlight-yellow);
   --rule-property-name: darkgreen;
   --rule-property-value: darkblue;
   --rule-overridden-item-border-color: var(--theme-content-color2);
   --rule-header-background-color: var(--theme-header-background);
 }
 
 /* Rule View Tabpanel */
 
@@ -545,17 +545,18 @@
 .ruleview-propertycontainer  > * {
   vertical-align: middle;
 }
 
 .ruleview-property[dirty] {
   border-left-color: var(--theme-highlight-green);
 }
 
-.ruleview-highlight {
+.ruleview-highlight > .ruleview-namecontainer,
+.ruleview-highlight > .ruleview-propertyvaluecontainer {
   background-color: var(--rule-highlight-background-color);
 }
 
 .ruleview-namecontainer > .ruleview-propertyname,
 .ruleview-propertyvaluecontainer > .ruleview-propertyvalue {
   border-bottom: 1px dashed transparent;
 }
 
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -1,16 +1,16 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
 .theme-light {
-  --searchbox-background-color: #ffee99;
+  --searchbox-background-color: var(--theme-highlight-yellow);
   --searchbox-border-color: #ffbf00;
   --searcbox-no-match-background-color: #ffe5e5;
   --searcbox-no-match-border-color: #e52e2e;
 
   --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
   --filter-image: url(chrome://devtools/skin/images/filter.svg);
   --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);
 
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -47,24 +47,26 @@
   --theme-body-color-alt: var(--grey-40);
   --theme-body-color-inactive: #999797;
   --theme-content-color1: #292e33;
   --theme-content-color2: #8fa1b2;
   --theme-content-color3: #667380;
 
   --theme-highlight-green: var(--green-70);
   --theme-highlight-blue: var(--blue-55);
+  --theme-highlight-purple: var(--blue-70);
+  --theme-highlight-red: var(--magenta-65);
+  --theme-highlight-yellow: #FFF89E;
+
+  /* These theme-highlight color variables have not been photonized. */
   --theme-highlight-bluegrey: #0072ab;
-  --theme-highlight-purple: var(--blue-70);
   --theme-highlight-lightorange: #d97e00;
   --theme-highlight-orange: #f13c00;
-  --theme-highlight-red: var(--magenta-65);
   --theme-highlight-pink: #b82ee5;
   --theme-highlight-gray: #dde1e4;
-  --theme-highlight-yellow: #ffffb4;
 
   /* For accessibility purposes we want to enhance the focus styling. This
    * should improve keyboard navigation usability. */
   --theme-focus-outline-color: #000000;
 
   /* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
   --theme-graphs-green: #85d175;
   --theme-graphs-blue: #83b7f6;
@@ -129,24 +131,26 @@
   --theme-body-color-alt: var(--grey-50);
   --theme-body-color-inactive: var(--grey-40);
   --theme-content-color1: var(--grey-30);
   --theme-content-color2: var(--grey-40);
   --theme-content-color3: #939393;
 
   --theme-highlight-green: #86DE74;
   --theme-highlight-blue: #75BFFF;
+  --theme-highlight-purple: #B98EFF;
+  --theme-highlight-red: #FF7DE9;
+  --theme-highlight-yellow: #FFF89E;
+
+  /* These theme-highlight color variables have not been photonized. */
   --theme-highlight-bluegrey: #5e88b0;
-  --theme-highlight-purple: #B98EFF;
   --theme-highlight-lightorange: #d99b28;
   --theme-highlight-orange: #d96629;
-  --theme-highlight-red: #FF7DE9;
   --theme-highlight-pink: #df80ff;
   --theme-highlight-gray: #e9f4fe;
-  --theme-highlight-yellow: #ffffb4;
 
   /* For accessibility purposes we want to enhance the focus styling. This
    * should improve keyboard navigation usability. */
   --theme-focus-outline-color: #ced3d9;
 
   /* Colors used in Graphs, like performance tools. Mostly similar to some "highlight-*" colors. */
   --theme-graphs-green: #70bf53;
   --theme-graphs-blue: #46afe3;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1868,18 +1868,18 @@ Element::UnbindFromTree(bool aDeep, bool
   // we're not leaving behind a pointer to ourselves as the PresContext's
   // cached provider of the viewport's scrollbar styles.
   if (document) {
     nsIPresShell* presShell = document->GetShell();
     if (presShell) {
       nsPresContext* presContext = presShell->GetPresContext();
       if (presContext) {
         MOZ_ASSERT(this !=
-                   presContext->GetViewportScrollbarStylesOverrideNode(),
-                   "Leaving behind a raw pointer to this node (as having "
+                   presContext->GetViewportScrollbarStylesOverrideElement(),
+                   "Leaving behind a raw pointer to this element (as having "
                    "propagated scrollbar styles) - that's dangerous...");
       }
     }
   }
 #endif
 
   ClearInDocument();
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7729,16 +7729,20 @@ class CGPerSignatureCall(CGThing):
             argsPre.append("cx")
 
         needsUnwrap = False
         argsPost = []
         if isConstructor:
             needsUnwrap = True
             needsUnwrappedVar = False
             unwrappedVar = "obj"
+            if descriptor.interface.isJSImplemented():
+                # We need the desired proto in our constructor, because the
+                # constructor will actually construct our reflector.
+                argsPost.append("desiredProto")
         elif descriptor.interface.isJSImplemented():
             if not idlNode.isStatic():
                 needsUnwrap = True
                 needsUnwrappedVar = True
                 argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)")
         elif needScopeObject(returnType, arguments, self.extendedAttributes,
                              descriptor.wrapperCache, True,
                              idlNode.getExtendedAttribute("StoreInSlot")):
@@ -15330,42 +15334,48 @@ class CGJSImplMethod(CGJSImplMember):
                                 variadicIsSequence=True,
                                 passJSBitsAsNeeded=False,
                                 virtual=virtual,
                                 override=override)
 
     def getArgs(self, returnType, argList):
         if self.isConstructor:
             # Skip the JSCompartment bits for constructors; it's handled
-            # manually in getImpl.
-            return CGNativeMember.getArgs(self, returnType, argList)
+            # manually in getImpl.  But we do need our aGivenProto argument.  We
+            # allow it to be omitted if the default proto is desired.
+            return (CGNativeMember.getArgs(self, returnType, argList) +
+                    [Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr")])
         return CGJSImplMember.getArgs(self, returnType, argList)
 
     def getImpl(self):
         args = self.getArgs(self.signature[0], self.signature[1])
         if not self.isConstructor:
             return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args))
 
         assert self.descriptor.interface.isJSImplemented()
         if self.name != 'Constructor':
             raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
         if len(self.signature[1]) != 0:
             # The first two arguments to the constructor implementation are not
-            # arguments to the WebIDL constructor, so don't pass them to __Init()
+            # arguments to the WebIDL constructor, so don't pass them to
+            # __Init().  The last argument is the prototype we're supposed to
+            # use, and shouldn't get passed to __Init() either.
             assert args[0].argType == 'const GlobalObject&'
             assert args[1].argType == 'JSContext*'
-            constructorArgs = [arg.name for arg in args[2:]]
+            assert args[-1].argType == 'JS::Handle<JSObject*>'
+            assert args[-1].name == 'aGivenProto'
+            constructorArgs = [arg.name for arg in args[2:-1]]
             constructorArgs.append("js::GetObjectCompartment(scopeObj)")
             initCall = fill(
                 """
                 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
                 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
                 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
                 JS::Rooted<JS::Value> wrappedVal(cx);
-                if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal)) {
+                if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
                   //XXX Assertion disabled for now, see bug 991271.
                   MOZ_ASSERT(true || JS_IsExceptionPending(cx));
                   aRv.Throw(NS_ERROR_UNEXPECTED);
                   return nullptr;
                 }
                 // Initialize the object with the constructor arguments.
                 impl->mImpl->__Init(${args});
                 if (aRv.Failed()) {
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -76,8 +76,9 @@ skip-if = debug == false
 [test_iterable.html]
 skip-if = debug == false
 [test_oom_reporting.html]
 [test_domProxyArrayLengthGetter.html]
 [test_exceptionSanitization.html]
 skip-if = os == "android"
 [test_stringBindings.html]
 skip-if = debug == false
+[test_jsimplemented_subclassing.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_jsimplemented_subclassing.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1400275
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1400275</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1400275 **/
+    class Foo extends RTCPeerConnection {
+    }
+
+    var obj = new Foo();
+    is(Object.getPrototypeOf(obj), Foo.prototype,
+       "Should have the right prototype");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400275">Mozilla Bug 1400275</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -348,17 +348,17 @@ AutoChildOpArgs::Add(JSContext* aCx, Int
 }
 
 const CacheOpArgs&
 AutoChildOpArgs::SendAsOpArgs()
 {
   MOZ_DIAGNOSTIC_ASSERT(!mSent);
   mSent = true;
   for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
-    autoStream->TakeValue();
+    autoStream->TakeOptionalValue();
   }
   return mOpArgs;
 }
 
 // --------------------------------------------
 
 AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
                                        const CacheOpResult& aOpResult,
@@ -501,17 +501,17 @@ AutoParentOpResult::Add(const SavedReque
 }
 
 const CacheOpResult&
 AutoParentOpResult::SendAsOpResult()
 {
   MOZ_DIAGNOSTIC_ASSERT(!mSent);
   mSent = true;
   for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
-    autoStream->TakeValue();
+    autoStream->TakeOptionalValue();
   }
   return mOpResult;
 }
 
 void
 AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
                                           StreamList* aStreamList,
                                           CacheResponse* aResponseOut)
@@ -532,17 +532,16 @@ void
 AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
                                         CacheReadStream* aReadStreamOut)
 {
   MOZ_DIAGNOSTIC_ASSERT(aStreamList);
   MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
   MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   nsCOMPtr<nsIInputStream> stream = aStreamList->Extract(aId);
-  MOZ_DIAGNOSTIC_ASSERT(stream);
 
   if (!mStreamControl) {
     mStreamControl = static_cast<CacheStreamControlParent*>(
       mManager->SendPCacheStreamControlConstructor(new CacheStreamControlParent()));
 
     // If this failed, then the child process is gone.  Warn and allow actor
     // cleanup to proceed as normal.
     if (!mStreamControl) {
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -251,22 +251,24 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla:
 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache);
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::Cache, mGlobal);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
+Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor, Namespace aNamespace)
   : mGlobal(aGlobal)
   , mActor(aActor)
+  , mNamespace(aNamespace)
 {
   MOZ_DIAGNOSTIC_ASSERT(mGlobal);
   MOZ_DIAGNOSTIC_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mNamespace != INVALID_NAMESPACE);
   mActor->SetListener(this);
 }
 
 already_AddRefed<Promise>
 Cache::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
              const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   if (NS_WARN_IF(!mActor)) {
@@ -280,17 +282,19 @@ Cache::Match(JSContext* aCx, const Reque
     ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params), 1);
+  AutoChildOpArgs args(this,
+                       CacheMatchArgs(CacheRequest(), params, GetOpenMode()),
+                       1);
 
   args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return ExecuteOp(args, aRv);
 }
@@ -304,17 +308,19 @@ Cache::MatchAll(JSContext* aCx, const Op
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1);
+  AutoChildOpArgs args(this,
+                       CacheMatchAllArgs(void_t(), params, GetOpenMode()),
+                       1);
 
   if (aRequest.WasPassed()) {
     RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest.Value(),
                                                    IgnoreBody, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
 
@@ -486,17 +492,19 @@ Cache::Keys(JSContext* aCx, const Option
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
-  AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1);
+  AutoChildOpArgs args(this,
+                       CacheKeysArgs(void_t(), params, GetOpenMode()),
+                       1);
 
   if (aRequest.WasPassed()) {
     RefPtr<InternalRequest> ir =
       ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
@@ -678,11 +686,17 @@ Cache::PutAll(JSContext* aCx, const nsTA
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
 
   return ExecuteOp(args, aRv);
 }
 
+OpenMode
+Cache::GetOpenMode() const
+{
+  return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy;
+}
+
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/Cache.h
+++ b/dom/cache/Cache.h
@@ -36,17 +36,17 @@ namespace cache {
 class AutoChildOpArgs;
 class CacheChild;
 
 class Cache final : public nsISupports
                   , public nsWrapperCache
                   , public TypeUtils
 {
 public:
-  Cache(nsIGlobalObject* aGlobal, CacheChild* aActor);
+  Cache(nsIGlobalObject* aGlobal, CacheChild* aActor, Namespace aNamespace);
 
   // webidl interface methods
   already_AddRefed<Promise>
   Match(JSContext* aCx, const RequestOrUSVString& aRequest,
         const CacheQueryOptions& aOptions, ErrorResult& aRv);
   already_AddRefed<Promise>
   MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
            const CacheQueryOptions& aOptions, ErrorResult& aRv);
@@ -102,18 +102,22 @@ private:
   AddAll(const GlobalObject& aGlobal, nsTArray<RefPtr<Request>>&& aRequestList,
          CallerType aCallerType, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
          const nsTArray<RefPtr<Response>>& aResponseList,
          ErrorResult& aRv);
 
+  OpenMode
+  GetOpenMode() const;
+
   nsCOMPtr<nsIGlobalObject> mGlobal;
   CacheChild* mActor;
+  const Namespace mNamespace;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Cache)
 };
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -153,35 +153,35 @@ CacheOpChild::Recv__delete__(const Error
     }
     case CacheOpResult::TStorageHasResult:
     {
       mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
       break;
     }
     case CacheOpResult::TStorageOpenResult:
     {
-      auto actor = static_cast<CacheChild*>(
-        aResult.get_StorageOpenResult().actorChild());
+      auto result = aResult.get_StorageOpenResult();
+      auto actor = static_cast<CacheChild*>(result.actorChild());
 
       // If we have a success status then we should have an actor.  Gracefully
       // reject instead of crashing, though, if we get a nullptr here.
       MOZ_DIAGNOSTIC_ASSERT(actor);
       if (!actor) {
         ErrorResult status;
         status.ThrowTypeError<MSG_CACHE_OPEN_FAILED>();
         mPromise->MaybeReject(status);
         break;
       }
 
       RefPtr<CacheWorkerHolder> workerHolder =
         CacheWorkerHolder::PreferBehavior(GetWorkerHolder(),
                                           CacheWorkerHolder::AllowIdleShutdownStart);
 
       actor->SetWorkerHolder(workerHolder);
-      RefPtr<Cache> cache = new Cache(mGlobal, actor);
+      RefPtr<Cache> cache = new Cache(mGlobal, actor, result.ns());
       mPromise->MaybeResolve(cache);
       break;
     }
     case CacheOpResult::TStorageDeleteResult:
     {
       mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
       break;
     }
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -328,17 +328,17 @@ CacheStorage::Match(JSContext* aCx, cons
     return nullptr;
   }
 
   CacheQueryParams params;
   ToCacheQueryParams(params, aOptions);
 
   nsAutoPtr<Entry> entry(new Entry());
   entry->mPromise = promise;
-  entry->mArgs = StorageMatchArgs(CacheRequest(), params);
+  entry->mArgs = StorageMatchArgs(CacheRequest(), params, GetOpenMode());
   entry->mRequest = request;
 
   mPendingRequests.AppendElement(entry.forget());
   MaybeRunPendingRequests();
 
   return promise.forget();
 }
 
@@ -612,11 +612,17 @@ CacheStorage::MaybeRunPendingRequests()
       entry->mPromise->MaybeReject(rv);
       continue;
     }
     mActor->ExecuteOp(mGlobal, entry->mPromise, this, args.SendAsOpArgs());
   }
   mPendingRequests.Clear();
 }
 
+OpenMode
+CacheStorage::GetOpenMode() const
+{
+  return mNamespace == CHROME_ONLY_NAMESPACE ? OpenMode::Eager : OpenMode::Lazy;
+}
+
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheStorage.h
+++ b/dom/cache/CacheStorage.h
@@ -99,16 +99,19 @@ private:
   CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
                const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                CacheWorkerHolder* aWorkerHolder);
   explicit CacheStorage(nsresult aFailureResult);
   ~CacheStorage();
 
   void MaybeRunPendingRequests();
 
+  OpenMode
+  GetOpenMode() const;
+
   const Namespace mNamespace;
   nsCOMPtr<nsIGlobalObject> mGlobal;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
   RefPtr<CacheWorkerHolder> mWorkerHolder;
 
   // weak ref cleared in DestroyInternal
   CacheStorageChild* mActor;
 
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -95,23 +95,41 @@ CacheStreamControlChild::SerializeContro
 
 void
 CacheStreamControlChild::SerializeStream(CacheReadStream* aReadStreamOut,
                                          nsIInputStream* aStream,
                                          nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
-  MOZ_DIAGNOSTIC_ASSERT(aStream);
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
   autoStream->Serialize(aStream, Manager());
   aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
+CacheStreamControlChild::OpenStream(const nsID& aId, InputStreamResolver&& aResolver)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
+
+  if (mDestroyStarted) {
+    aResolver(nullptr);
+    return;
+  }
+
+  SendOpenStream(aId)->Then(GetCurrentThreadSerialEventTarget(), __func__,
+  [aResolver](const OptionalIPCStream& aOptionalStream) {
+    nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream);
+    aResolver(Move(stream));
+  }, [aResolver](PromiseRejectReason aReason) {
+    aResolver(nullptr);
+  });
+}
+
+void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
   Unused << SendNoteClosed(aId);
 
   // A stream has closed.  If we delayed StartDestry() due to this stream
   // being read, then we should check to see if any of the remaining streams
   // are active.  If none of our other streams have been read, then we can
--- a/dom/cache/CacheStreamControlChild.h
+++ b/dom/cache/CacheStreamControlChild.h
@@ -35,16 +35,19 @@ public:
   // StreamControl methods
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
   SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
                   nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) override;
 
+  virtual void
+  OpenStream(const nsID& aId, InputStreamResolver&& aResolver) override;
+
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
   AssertOwningThread() override;
 #endif
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -54,26 +54,44 @@ CacheStreamControlParent::SerializeContr
 
 void
 CacheStreamControlParent::SerializeStream(CacheReadStream* aReadStreamOut,
                                           nsIInputStream* aStream,
                                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
-  MOZ_DIAGNOSTIC_ASSERT(aStream);
 
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
   DebugOnly<bool> ok = autoStream->Serialize(aStream, Manager());
   MOZ_ASSERT(ok);
 
   aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
+CacheStreamControlParent::OpenStream(const nsID& aId,
+                                     InputStreamResolver&& aResolver)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
+  MOZ_DIAGNOSTIC_ASSERT(aResolver);
+
+  if (!mStreamList || !mStreamList->ShouldOpenStreamFor(aId)) {
+    aResolver(nullptr);
+    return;
+  }
+
+  // Make sure to add ourself as a Listener even thought we are using
+  // a separate resolver function to signal the completion of the
+  // operation.  The Manager uses the existence of the Listener to ensure
+  // that its safe to complete the operation.
+  mStreamList->GetManager()->ExecuteOpenStream(this, Move(aResolver), aId);
+}
+
+void
 CacheStreamControlParent::NoteClosedAfterForget(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   RecvNoteClosed(aId);
 }
 
 #ifdef DEBUG
 void
@@ -88,22 +106,48 @@ CacheStreamControlParent::ActorDestroy(A
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   CloseAllReadStreamsWithoutReporting();
   // If the initial SendPStreamControlConstructor() fails we will
   // be called before mStreamList is set.
   if (!mStreamList) {
     return;
   }
+  mStreamList->GetManager()->RemoveListener(this);
   mStreamList->RemoveStreamControl(this);
   mStreamList->NoteClosedAll();
   mStreamList = nullptr;
 }
 
 mozilla::ipc::IPCResult
+CacheStreamControlParent::RecvOpenStream(const nsID& aStreamId,
+                                         OpenStreamResolver&& aResolver)
+{
+  NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
+
+  // This is safe because:
+  //  1. We add ourself to the Manager as an operation Listener in OpenStream().
+  //  2. We remove ourself as a Listener from the Manager in ActorDestroy().
+  //  3. The Manager will not "complete" the operation if the Listener has
+  //     been removed.  This means the lambda will not be invoked.
+  //  4. The ActorDestroy() will also cause the child-side MozPromise for
+  //     this async returning method to be rejected.  So we don't have to
+  //     call the resolver in this case.
+  CacheStreamControlParent* self = this;
+
+  OpenStream(aStreamId, [self, aResolver](nsCOMPtr<nsIInputStream>&& aStream) {
+      AutoIPCStream stream;
+      Unused << stream.Serialize(aStream, self->Manager());
+      aResolver(stream.TakeOptionalValue());
+    });
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 CacheStreamControlParent::RecvNoteClosed(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   MOZ_DIAGNOSTIC_ASSERT(mStreamList);
   mStreamList->NoteClosed(aId);
   return IPC_OK();
 }
 
--- a/dom/cache/CacheStreamControlParent.h
+++ b/dom/cache/CacheStreamControlParent.h
@@ -2,32 +2,34 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_CacheStreamControlParent_h
 #define mozilla_dom_cache_CacheStreamControlParent_h
 
+#include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/PCacheStreamControlParent.h"
 #include "mozilla/dom/cache/StreamControl.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
 namespace ipc {
 class AutoIPCStream;
 } // namespace ipc
 namespace dom {
 namespace cache {
 
 class ReadStream;
 class StreamList;
 
 class CacheStreamControlParent final : public PCacheStreamControlParent
                                      , public StreamControl
+                                     , Manager::Listener
 {
 public:
   CacheStreamControlParent();
   ~CacheStreamControlParent();
 
   void SetStreamList(StreamList* aStreamList);
   void Close(const nsID& aId);
   void CloseAll();
@@ -36,27 +38,34 @@ public:
   // StreamControl methods
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) override;
 
   virtual void
   SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
                   nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) override;
 
+  virtual void
+  OpenStream(const nsID& aId, InputStreamResolver&& aResolver) override;
+
 private:
   virtual void
   NoteClosedAfterForget(const nsID& aId) override;
 
 #ifdef DEBUG
   virtual void
   AssertOwningThread() override;
 #endif
 
   // PCacheStreamControlParent methods
   virtual void ActorDestroy(ActorDestroyReason aReason) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvOpenStream(const nsID& aStreamId, OpenStreamResolver&& aResolve) override;
+
   virtual mozilla::ipc::IPCResult RecvNoteClosed(const nsID& aId) override;
 
   void NotifyClose(const nsID& aId);
   void NotifyCloseAll();
 
   // Cycle with StreamList via a weak-ref to us.  Cleanup occurs when the actor
   // is deleted by the PBackground manager.  ActorDestroy() then calls
   // StreamList::RemoveStreamControl() to clear the weak ref.
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -5,16 +5,18 @@
 include protocol PCache;
 include protocol PCacheStreamControl;
 include protocol PChildToParentStream;
 include IPCStream;
 include ChannelInfo;
 include PBackgroundSharedTypes;
 
 using HeadersGuardEnum from "mozilla/dom/FetchIPCTypes.h";
+using Namespace from "mozilla/dom/cache/IPCUtils.h";
+using OpenMode from "mozilla/dom/cache/IPCUtils.h";
 using ReferrerPolicy from "mozilla/dom/FetchIPCTypes.h";
 using RequestCredentials from "mozilla/dom/FetchIPCTypes.h";
 using RequestMode from "mozilla/dom/FetchIPCTypes.h";
 using RequestCache from "mozilla/dom/FetchIPCTypes.h";
 using RequestRedirect from "mozilla/dom/FetchIPCTypes.h";
 using ResponseType from "mozilla/dom/FetchIPCTypes.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct nsID from "nsID.h";
@@ -31,17 +33,17 @@ struct CacheQueryParams
   bool cacheNameSet;
   nsString cacheName;
 };
 
 struct CacheReadStream
 {
   nsID id;
   nullable PCacheStreamControl control;
-  IPCStream stream;
+  OptionalIPCStream stream;
 };
 
 union CacheReadStreamOrVoid
 {
   void_t;
   CacheReadStream;
 };
 
@@ -101,22 +103,24 @@ struct CacheRequestResponse
   CacheRequest request;
   CacheResponse response;
 };
 
 struct CacheMatchArgs
 {
   CacheRequest request;
   CacheQueryParams params;
+  OpenMode openMode;
 };
 
 struct CacheMatchAllArgs
 {
   CacheRequestOrVoid requestOrVoid;
   CacheQueryParams params;
+  OpenMode openMode;
 };
 
 struct CachePutAllArgs
 {
   CacheRequestResponse[] requestResponseList;
 };
 
 struct CacheDeleteArgs
@@ -124,22 +128,24 @@ struct CacheDeleteArgs
   CacheRequest request;
   CacheQueryParams params;
 };
 
 struct CacheKeysArgs
 {
   CacheRequestOrVoid requestOrVoid;
   CacheQueryParams params;
+  OpenMode openMode;
 };
 
 struct StorageMatchArgs
 {
   CacheRequest request;
   CacheQueryParams params;
+  OpenMode openMode;
 };
 
 struct StorageHasArgs
 {
   nsString key;
 };
 
 struct StorageOpenArgs
@@ -202,16 +208,17 @@ struct StorageMatchResult
 struct StorageHasResult
 {
   bool success;
 };
 
 struct StorageOpenResult
 {
   nullable PCache actor;
+  Namespace ns;
 };
 
 struct StorageDeleteResult
 {
   bool success;
 };
 
 struct StorageKeysResult
--- a/dom/cache/IPCUtils.h
+++ b/dom/cache/IPCUtils.h
@@ -13,11 +13,18 @@
 
 namespace IPC {
   template<>
   struct ParamTraits<mozilla::dom::cache::Namespace> :
     public ContiguousEnumSerializer<mozilla::dom::cache::Namespace,
                                     mozilla::dom::cache::DEFAULT_NAMESPACE,
                                     mozilla::dom::cache::NUMBER_OF_NAMESPACES>
   {};
+
+  template<>
+  struct ParamTraits<mozilla::dom::cache::OpenMode> :
+    public ContiguousEnumSerializer<mozilla::dom::cache::OpenMode,
+                                    mozilla::dom::cache::OpenMode::Eager,
+                                    mozilla::dom::cache::OpenMode::NumTypes>
+  {};
 } // namespace IPC
 
 #endif // mozilla_dom_cache_IPCUtils_h
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -539,21 +539,23 @@ public:
 
     if (!mFoundResponse || !mResponse.mHasBodyId
                         || IsHeadRequest(mArgs.request(), mArgs.params())) {
       mResponse.mHasBodyId = false;
       return rv;
     }
 
     nsCOMPtr<nsIInputStream> stream;
-    rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+    if (mArgs.openMode() == OpenMode::Eager) {
+      rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+    }
 
-    mStreamList->Add(mResponse.mBodyId, stream);
+    mStreamList->Add(mResponse.mBodyId, Move(stream));
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (!mFoundResponse) {
@@ -604,22 +606,24 @@ public:
     for (uint32_t i = 0; i < mSavedResponses.Length(); ++i) {
       if (!mSavedResponses[i].mHasBodyId
           || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
         mSavedResponses[i].mHasBodyId = false;
         continue;
       }
 
       nsCOMPtr<nsIInputStream> stream;
-      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
-                    getter_AddRefs(stream));
-      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+      if (mArgs.openMode() == OpenMode::Eager) {
+        rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
+                      getter_AddRefs(stream));
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+        if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+      }
 
-      mStreamList->Add(mSavedResponses[i].mBodyId, stream);
+      mStreamList->Add(mSavedResponses[i].mBodyId, Move(stream));
     }
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
@@ -1152,22 +1156,24 @@ public:
     for (uint32_t i = 0; i < mSavedRequests.Length(); ++i) {
       if (!mSavedRequests[i].mHasBodyId
           || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
         mSavedRequests[i].mHasBodyId = false;
         continue;
       }
 
       nsCOMPtr<nsIInputStream> stream;
-      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
-                    getter_AddRefs(stream));
-      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+      if (mArgs.openMode() == OpenMode::Eager) {
+        rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
+                      getter_AddRefs(stream));
+        if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+        if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+      }
 
-      mStreamList->Add(mSavedRequests[i].mBodyId, stream);
+      mStreamList->Add(mSavedRequests[i].mBodyId, Move(stream));
     }
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
@@ -1216,22 +1222,24 @@ public:
 
     if (!mFoundResponse || !mSavedResponse.mHasBodyId
                         || IsHeadRequest(mArgs.request(), mArgs.params())) {
       mSavedResponse.mHasBodyId = false;
       return rv;
     }
 
     nsCOMPtr<nsIInputStream> stream;
-    rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
-                  getter_AddRefs(stream));
-    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+    if (mArgs.openMode() == OpenMode::Eager) {
+      rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
+                    getter_AddRefs(stream));
+      if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+      if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
+    }
 
-    mStreamList->Add(mSavedResponse.mBodyId, stream);
+    mStreamList->Add(mSavedResponse.mBodyId, Move(stream));
 
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     if (!mFoundResponse) {
@@ -1329,17 +1337,19 @@ public:
     MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
     MOZ_DIAGNOSTIC_ASSERT(aRv.Failed() || mCacheId != INVALID_CACHE_ID);
-    aListener->OnOpComplete(Move(aRv), StorageOpenResult(), mCacheId);
+    aListener->OnOpComplete(Move(aRv),
+                            StorageOpenResult(nullptr, nullptr, mNamespace),
+                            mCacheId);
   }
 
 private:
   const Namespace mNamespace;
   const StorageOpenArgs mArgs;
   CacheId mCacheId;
 };
 
@@ -1447,16 +1457,53 @@ public:
 
 private:
   const Namespace mNamespace;
   nsTArray<nsString> mKeys;
 };
 
 // ----------------------------------------------------------------------------
 
+class Manager::OpenStreamAction final : public Manager::BaseAction
+{
+public:
+  OpenStreamAction(Manager* aManager, ListenerId aListenerId,
+                   InputStreamResolver&& aResolver, const nsID& aBodyId)
+    : BaseAction(aManager, aListenerId)
+    , mResolver(Move(aResolver))
+    , mBodyId(aBodyId)
+  { }
+
+  virtual nsresult
+  RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
+                        mozIStorageConnection* aConn) override
+  {
+    nsresult rv = BodyOpen(aQuotaInfo, aDBDir, mBodyId,
+                           getter_AddRefs(mBodyStream));
+    if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
+    if (NS_WARN_IF(!mBodyStream)) { return NS_ERROR_FILE_NOT_FOUND; }
+
+    return rv;
+  }
+
+  virtual void
+  Complete(Listener* aListener, ErrorResult&& aRv) override
+  {
+    mResolver(Move(mBodyStream));
+    mResolver = nullptr;
+  }
+
+private:
+  InputStreamResolver mResolver;
+  const nsID mBodyId;
+  nsCOMPtr<nsIInputStream> mBodyStream;
+};
+
+// ----------------------------------------------------------------------------
+
 //static
 Manager::ListenerId Manager::sNextListenerId = 0;
 
 void
 Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult)
 {
   OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
                nsTArray<SavedRequest>(), nullptr);
@@ -1814,16 +1861,44 @@ Manager::ExecuteStorageOp(Listener* aLis
     default:
       MOZ_CRASH("Unknown CacheStorage operation!");
   }
 
   context->Dispatch(action);
 }
 
 void
+Manager::ExecuteOpenStream(Listener* aListener, InputStreamResolver&& aResolver,
+                           const nsID& aBodyId)
+{
+  NS_ASSERT_OWNINGTHREAD(Manager);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aResolver);
+
+  if (NS_WARN_IF(mState == Closing)) {
+    aResolver(nullptr);
+    return;
+  }
+
+  RefPtr<Context> context = mContext;
+  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
+
+  // We save the listener simply to track the existence of the caller here.
+  // Our returned value will really be passed to the resolver when the
+  // operation completes.  In the future we should remove the Listener
+  // mechanism in favor of std::function or MozPromise.
+  ListenerId listenerId = SaveListener(aListener);
+
+  RefPtr<Action> action =
+    new OpenStreamAction(this, listenerId, Move(aResolver), aBodyId);
+
+  context->Dispatch(action);
+}
+
+void
 Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
                        const nsTArray<CacheRequestResponse>& aPutList,
                        const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
                        const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   MOZ_DIAGNOSTIC_ASSERT(aListener);
 
--- a/dom/cache/Manager.h
+++ b/dom/cache/Manager.h
@@ -172,16 +172,23 @@ public:
   void ExecutePutAll(Listener* aListener, CacheId aCacheId,
                      const nsTArray<CacheRequestResponse>& aPutList,
                      const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
                      const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
 
   void ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
                         const CacheOpArgs& aOpArgs);
 
+  void ExecuteOpenStream(Listener* aListener, InputStreamResolver&& aResolver,
+                         const nsID& aBodyId);
+
+  void
+  NoteStreamOpenComplete(const nsID& aBodyId, ErrorResult&& aRv,
+                         nsCOMPtr<nsIInputStream>&& aBodyStream);
+
 private:
   class Factory;
   class BaseAction;
   class DeleteOrphanedCacheAction;
 
   class CacheMatchAction;
   class CacheMatchAllAction;
   class CachePutAllAction;
@@ -189,16 +196,18 @@ private:
   class CacheKeysAction;
 
   class StorageMatchAction;
   class StorageHasAction;
   class StorageOpenAction;
   class StorageDeleteAction;
   class StorageKeysAction;
 
+  class OpenStreamAction;
+
   typedef uint64_t ListenerId;
 
   Manager(ManagerId* aManagerId, nsIThread* aIOThread);
   ~Manager();
   void Init(Manager* aOldManager);
   void Shutdown();
 
   void Abort();
--- a/dom/cache/PCacheStreamControl.ipdl
+++ b/dom/cache/PCacheStreamControl.ipdl
@@ -1,25 +1,30 @@
 /* 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 protocol PBackground;
+include protocol PFileDescriptorSet;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include IPCStream;
 
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 protocol PCacheStreamControl
 {
   manager PBackground;
 
 parent:
+  async OpenStream(nsID aStreamId) returns(OptionalIPCStream aStream);
   async NoteClosed(nsID aStreamId);
 
 child:
   async Close(nsID aStreamId);
   async CloseAll();
   async __delete__();
 };
 
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -8,25 +8,27 @@
 
 #include "mozilla/Unused.h"
 #include "mozilla/dom/cache/CacheStreamControlChild.h"
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/SnappyUncompressInputStream.h"
 #include "nsIAsyncInputStream.h"
+#include "nsStringStream.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 using mozilla::Unused;
 using mozilla::ipc::AutoIPCStream;
 using mozilla::ipc::IPCStream;
+using mozilla::ipc::OptionalIPCStream;
 
 // ----------------------------------------------------------------------------
 
 // The inner stream class.  This is where all of the real work is done.  As
 // an invariant Inner::Close() must be called before ~Inner().  This is
 // guaranteed by our outer ReadStream class.
 class ReadStream::Inner final : public ReadStream::Controllable
 {
@@ -87,16 +89,28 @@ private:
   Forget();
 
   void
   NoteClosedOnOwningThread();
 
   void
   ForgetOnOwningThread();
 
+  nsIInputStream*
+  EnsureStream();
+
+  void
+  AsyncOpenStreamOnOwningThread();
+
+  void
+  MaybeAbortAsyncOpenStream();
+
+  void
+  OpenStreamFailed();
+
   // Weak ref to the stream control actor.  The actor will always call either
   // CloseStream() or CloseStreamWithoutReporting() before it's destroyed.  The
   // weak ref is cleared in the resulting NoteClosedOnOwningThread() or
   // ForgetOnOwningThread() method call.
   StreamControl* mControl;
 
   const nsID mId;
   nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
@@ -104,23 +118,24 @@ private:
   enum State
   {
     Open,
     Closed,
     NumStates
   };
   Atomic<State> mState;
   Atomic<bool> mHasEverBeenRead;
-
+  bool mAsyncOpenStarted;
 
   // The wrapped stream objects may not be threadsafe.  We need to be able
   // to close a stream on our owning thread while an IO thread is simultaneously
   // reading the same stream.  Therefore, protect all access to these stream
   // objects with a mutex.
   Mutex mMutex;
+  CondVar mCondVar;
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsIInputStream> mSnappyStream;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::ReadStream::Inner, override)
 };
 
 // ----------------------------------------------------------------------------
 
@@ -197,21 +212,22 @@ private:
 
 ReadStream::Inner::Inner(StreamControl* aControl, const nsID& aId,
                          nsIInputStream* aStream)
   : mControl(aControl)
   , mId(aId)
   , mOwningEventTarget(GetCurrentThreadSerialEventTarget())
   , mState(Open)
   , mHasEverBeenRead(false)
+  , mAsyncOpenStarted(false)
   , mMutex("dom::cache::ReadStream")
+  , mCondVar(mMutex, "dom::cache::ReadStream")
   , mStream(aStream)
-  , mSnappyStream(new SnappyUncompressInputStream(aStream))
+  , mSnappyStream(aStream ? new SnappyUncompressInputStream(aStream) : nullptr)
 {
-  MOZ_DIAGNOSTIC_ASSERT(mStream);
   MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->AddReadStream(this);
 }
 
 void
 ReadStream::Inner::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
                              nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                              ErrorResult& aRv)
@@ -240,17 +256,18 @@ ReadStream::Inner::Serialize(CacheReadSt
   aReadStreamOut->id() = mId;
   mControl->SerializeControl(aReadStreamOut);
 
   {
     MutexAutoLock lock(mMutex);
     mControl->SerializeStream(aReadStreamOut, mStream, aStreamCleanupList);
   }
 
-  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut->stream().type() ==
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut->stream().type() == OptionalIPCStream::Tvoid_t ||
+                        aReadStreamOut->stream().get_IPCStream().type() ==
                         IPCStream::TInputStreamParamsWithFds);
 
   // We're passing ownership across the IPC barrier with the control, so
   // do not signal that the stream is closed here.
   Forget();
 }
 
 void
@@ -283,30 +300,32 @@ ReadStream::Inner::HasEverBeenRead() con
 
 nsresult
 ReadStream::Inner::Close()
 {
   // stream ops can happen on any thread
   nsresult rv = NS_OK;
   {
     MutexAutoLock lock(mMutex);
-    rv = mSnappyStream->Close();
+    if (mSnappyStream) {
+      rv = mSnappyStream->Close();
+    }
   }
   NoteClosed();
   return rv;
 }
 
 nsresult
 ReadStream::Inner::Available(uint64_t* aNumAvailableOut)
 {
   // stream ops can happen on any thread
   nsresult rv = NS_OK;
   {
     MutexAutoLock lock(mMutex);
-    rv = mSnappyStream->Available(aNumAvailableOut);
+    rv = EnsureStream()->Available(aNumAvailableOut);
   }
 
   if (NS_FAILED(rv)) {
     Close();
   }
 
   return rv;
 }
@@ -315,17 +334,17 @@ nsresult
 ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
   MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 
   nsresult rv = NS_OK;
   {
     MutexAutoLock lock(mMutex);
-    rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
+    rv = EnsureStream()->Read(aBuf, aCount, aNumReadOut);
   }
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
       *aNumReadOut == 0) {
     Close();
   }
 
   mHasEverBeenRead = true;
@@ -343,17 +362,17 @@ ReadStream::Inner::ReadSegments(nsWriteS
   if (aCount) {
     mHasEverBeenRead = true;
   }
 
 
   nsresult rv = NS_OK;
   {
     MutexAutoLock lock(mMutex);
-    rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
+    rv = EnsureStream()->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
   }
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
                         rv != NS_ERROR_NOT_IMPLEMENTED) || *aNumReadOut == 0) {
     Close();
   }
 
   // Verify bytes were actually read before marking as being ever read.  For
@@ -367,17 +386,21 @@ ReadStream::Inner::ReadSegments(nsWriteS
   return rv;
 }
 
 nsresult
 ReadStream::Inner::IsNonBlocking(bool* aNonBlockingOut)
 {
   // stream ops can happen on any thread
   MutexAutoLock lock(mMutex);
-  return mSnappyStream->IsNonBlocking(aNonBlockingOut);
+  if (mSnappyStream) {
+    return mSnappyStream->IsNonBlocking(aNonBlockingOut);
+  }
+  *aNonBlockingOut = false;
+  return NS_OK;
 }
 
 ReadStream::Inner::~Inner()
 {
   // Any thread
   MOZ_DIAGNOSTIC_ASSERT(mState == Closed);
   MOZ_DIAGNOSTIC_ASSERT(!mControl);
 }
@@ -423,36 +446,131 @@ ReadStream::Inner::NoteClosedOnOwningThr
 {
   MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 
   // Mark closed and do nothing if we were already closed
   if (!mState.compareExchange(Open, Closed)) {
     return;
   }
 
+  MaybeAbortAsyncOpenStream();
+
   MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->NoteClosed(this, mId);
   mControl = nullptr;
 }
 
 void
 ReadStream::Inner::ForgetOnOwningThread()
 {
   MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
 
   // Mark closed and do nothing if we were already closed
   if (!mState.compareExchange(Open, Closed)) {
     return;
   }
 
+  MaybeAbortAsyncOpenStream();
+
   MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->ForgetReadStream(this);
   mControl = nullptr;
 }
 
+nsIInputStream*
+ReadStream::Inner::EnsureStream()
+{
+  mMutex.AssertCurrentThreadOwns();
+
+  // We need to block the current thread while we open the stream.  We
+  // cannot do this safely from the main owning thread since it would
+  // trigger deadlock.  This should be ok, though, since a blocking
+  // stream like this should never be read on the owning thread anyway.
+  if (mOwningEventTarget->IsOnCurrentThread()) {
+    MOZ_CRASH("Blocking read on the js/ipc owning thread!");
+  }
+
+  if (mSnappyStream) {
+    return mSnappyStream;
+  }
+
+  nsCOMPtr<nsIRunnable> r =
+    NewCancelableRunnableMethod("ReadStream::Inner::AsyncOpenStreamOnOwningThread",
+                                this,
+                                &ReadStream::Inner::AsyncOpenStreamOnOwningThread);
+  nsresult rv = mOwningEventTarget->Dispatch(r.forget(),
+                                             nsIThread::DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    OpenStreamFailed();
+    return mSnappyStream;
+  }
+
+  mCondVar.Wait();
+  MOZ_DIAGNOSTIC_ASSERT(mSnappyStream);
+
+  return mSnappyStream;
+}
+
+void
+ReadStream::Inner::AsyncOpenStreamOnOwningThread()
+{
+  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
+
+  if (!mControl || mState == Closed) {
+    MutexAutoLock lock(mMutex);
+    OpenStreamFailed();
+    mCondVar.NotifyAll();
+    return;
+  }
+
+  if (mAsyncOpenStarted) {
+    return;
+  }
+  mAsyncOpenStarted = true;
+
+  RefPtr<ReadStream::Inner> self = this;
+  mControl->OpenStream(mId, [self](nsCOMPtr<nsIInputStream>&& aStream) {
+    MutexAutoLock lock(self->mMutex);
+    self->mAsyncOpenStarted = false;
+    if (!self->mStream) {
+      if (!aStream) {
+        self->OpenStreamFailed();
+      } else {
+        self->mStream = Move(aStream);
+        self->mSnappyStream = new SnappyUncompressInputStream(self->mStream);
+      }
+    }
+    self->mCondVar.NotifyAll();
+  });
+}
+
+void
+ReadStream::Inner::MaybeAbortAsyncOpenStream()
+{
+  if (!mAsyncOpenStarted) {
+    return;
+  }
+
+  MutexAutoLock lock(mMutex);
+  OpenStreamFailed();
+  mCondVar.NotifyAll();
+}
+
+void
+ReadStream::Inner::OpenStreamFailed()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mStream);
+  MOZ_DIAGNOSTIC_ASSERT(!mSnappyStream);
+  mMutex.AssertCurrentThreadOwns();
+  Unused << NS_NewCStringInputStream(getter_AddRefs(mStream), EmptyCString());
+  mSnappyStream = mStream;
+  mStream->Close();
+  NoteClosed();
+}
+
 // ----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(cache::ReadStream, nsIInputStream, ReadStream);
 
 // static
 already_AddRefed<ReadStream>
 ReadStream::Create(const CacheReadStreamOrVoid& aReadStreamOrVoid)
 {
@@ -469,38 +587,40 @@ ReadStream::Create(const CacheReadStream
 {
   // The parameter may or may not be for a Cache created stream.  The way we
   // tell is by looking at the stream control actor.  If the actor exists,
   // then we know the Cache created it.
   if (!aReadStream.controlChild() && !aReadStream.controlParent()) {
     return nullptr;
   }
 
-  MOZ_DIAGNOSTIC_ASSERT(aReadStream.stream().type() ==
+  MOZ_DIAGNOSTIC_ASSERT(aReadStream.stream().type() == OptionalIPCStream::Tvoid_t ||
+                        aReadStream.stream().get_IPCStream().type() ==
                         IPCStream::TInputStreamParamsWithFds);
 
   // Control is guaranteed to survive this method as ActorDestroy() cannot
   // run on this thread until we complete.
   StreamControl* control;
   if (aReadStream.controlChild()) {
     auto actor = static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
     control = actor;
   } else {
     auto actor = static_cast<CacheStreamControlParent*>(aReadStream.controlParent());
     control = actor;
   }
   MOZ_DIAGNOSTIC_ASSERT(control);
 
   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
-  MOZ_DIAGNOSTIC_ASSERT(stream);
 
   // Currently we expect all cache read streams to be blocking file streams.
 #if !defined(RELEASE_OR_BETA)
-  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
-  MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
+  if (stream) {
+    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
+    MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
+  }
 #endif
 
   RefPtr<Inner> inner = new Inner(control, aReadStream.id(), stream);
   RefPtr<ReadStream> ref = new ReadStream(inner);
   return ref.forget();
 }
 
 // static
--- a/dom/cache/StreamControl.h
+++ b/dom/cache/StreamControl.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_StreamControl_h
 #define mozilla_dom_cache_StreamControl_h
 
 #include "mozilla/dom/cache/ReadStream.h"
+#include "mozilla/dom/cache/Types.h"
 #include "mozilla/RefPtr.h"
 #include "nsTObserverArray.h"
 
 struct nsID;
 
 namespace mozilla {
 namespace ipc {
 class AutoIPCStream;
@@ -31,16 +32,19 @@ public:
   // abstract interface that must be implemented by child class
   virtual void
   SerializeControl(CacheReadStream* aReadStreamOut) = 0;
 
   virtual void
   SerializeStream(CacheReadStream* aReadStreamOut, nsIInputStream* aStream,
                   nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList) = 0;
 
+  virtual void
+  OpenStream(const nsID& aId, InputStreamResolver&& aResolver) = 0;
+
   // inherited implementation of the ReadStream::Controllable list
 
   // Begin controlling the given ReadStream.  This causes a strong ref to
   // be held by the control.  The ReadStream must call NoteClosed() or
   // ForgetReadStream() to release this ref.
   void
   AddReadStream(ReadStream::Controllable* aReadStream);
 
--- a/dom/cache/StreamList.cpp
+++ b/dom/cache/StreamList.cpp
@@ -21,16 +21,37 @@ StreamList::StreamList(Manager* aManager
   , mCacheId(INVALID_CACHE_ID)
   , mStreamControl(nullptr)
   , mActivated(false)
 {
   MOZ_DIAGNOSTIC_ASSERT(mManager);
   mContext->AddActivity(this);
 }
 
+Manager*
+StreamList::GetManager() const
+{
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
+  return mManager;
+}
+
+bool
+StreamList::ShouldOpenStreamFor(const nsID& aId) const
+{
+  NS_ASSERT_OWNINGTHREAD(StreamList);
+
+  for (auto entry : mList) {
+    if (entry.mId == aId) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void
 StreamList::SetStreamControl(CacheStreamControlParent* aStreamControl)
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
   MOZ_DIAGNOSTIC_ASSERT(aStreamControl);
 
   // For cases where multiple streams are serialized for a single list
   // then the control will get passed multiple times.  This is ok, but
@@ -65,25 +86,22 @@ StreamList::Activate(CacheId aCacheId)
   mManager->AddStreamList(this);
 
   for (uint32_t i = 0; i < mList.Length(); ++i) {
     mManager->AddRefBodyId(mList[i].mId);
   }
 }
 
 void
-StreamList::Add(const nsID& aId, nsIInputStream* aStream)
+StreamList::Add(const nsID& aId, nsCOMPtr<nsIInputStream>&& aStream)
 {
   // All streams should be added on IO thread before we set the stream
   // control on the owning IPC thread.
   MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
-  MOZ_DIAGNOSTIC_ASSERT(aStream);
-  Entry* entry = mList.AppendElement();
-  entry->mId = aId;
-  entry->mStream = aStream;
+  mList.AppendElement(Entry(aId, Move(aStream)));
 }
 
 already_AddRefed<nsIInputStream>
 StreamList::Extract(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
   for (uint32_t i = 0; i < mList.Length(); ++i) {
     if (mList[i].mId == aId) {
--- a/dom/cache/StreamList.h
+++ b/dom/cache/StreamList.h
@@ -21,37 +21,45 @@ namespace cache {
 class CacheStreamControlParent;
 class Manager;
 
 class StreamList final : public Context::Activity
 {
 public:
   StreamList(Manager* aManager, Context* aContext);
 
+  Manager* GetManager() const;
+  bool ShouldOpenStreamFor(const nsID& aId) const;
+
   void SetStreamControl(CacheStreamControlParent* aStreamControl);
   void RemoveStreamControl(CacheStreamControlParent* aStreamControl);
 
   void Activate(CacheId aCacheId);
 
-  void Add(const nsID& aId, nsIInputStream* aStream);
+  void Add(const nsID& aId, nsCOMPtr<nsIInputStream>&& aStream);
   already_AddRefed<nsIInputStream> Extract(const nsID& aId);
 
   void NoteClosed(const nsID& aId);
   void NoteClosedAll();
   void Close(const nsID& aId);
   void CloseAll();
 
   // Context::Activity methods
   virtual void Cancel() override;
   virtual bool MatchesCacheId(CacheId aCacheId) const override;
 
 private:
   ~StreamList();
   struct Entry
   {
+    explicit Entry(const nsID& aId, nsCOMPtr<nsIInputStream>&& aStream)
+      : mId(aId)
+      , mStream(Move(aStream))
+    {}
+
     nsID mId;
     nsCOMPtr<nsIInputStream> mStream;
   };
   RefPtr<Manager> mManager;
   RefPtr<Context> mContext;
   CacheId mCacheId;
   CacheStreamControlParent* mStreamControl;
   nsTArray<Entry> mList;
--- a/dom/cache/Types.h
+++ b/dom/cache/Types.h
@@ -2,19 +2,21 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_cache_Types_h
 #define mozilla_dom_cache_Types_h
 
+#include <functional>
 #include <stdint.h>
 #include "nsCOMPtr.h"
 #include "nsIFile.h"
+#include "nsIInputStream.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 enum Namespace
 {
@@ -30,13 +32,22 @@ static const CacheId INVALID_CACHE_ID = 
 struct QuotaInfo
 {
   nsCOMPtr<nsIFile> mDir;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
 };
 
+typedef std::function<void(nsCOMPtr<nsIInputStream>&&)> InputStreamResolver;
+
+enum class OpenMode : uint8_t
+{
+  Eager,
+  Lazy,
+  NumTypes
+};
+
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_Types_h
--- a/dom/events/test/test_bug574663.html
+++ b/dom/events/test/test_bug574663.html
@@ -93,19 +93,34 @@ function sendTouchpadScrollMotion(scroll
   event.lineOrPageDeltaY = 0;
   for (let i = 1; i <= kExtraEvents; ++i) {
     synthesizeWheel(scrollbox, 10, 10, event, win);
   }
 }
 
 function runTest() {
   var win = open('bug574663.html', '_blank', 'width=300,height=300');
-  SimpleTest.waitForFocus(function () {
+  let winUtils = SpecialPowers.getDOMWindowUtils(win);
+
+  let waitUntilPainted = function(callback) {
+    // Until the first non-blank paint, the parent will set the opacity of our
+    // browser to 0 using the 'blank' attribute.
+    // Until the blank attribute is removed, we can't send scroll events.
+    SimpleTest.waitForFocus(function() {
+      SpecialPowers.loadChromeScript(_ => {
+        Components.utils.import("resource://gre/modules/Services.jsm");
+        Services.wm.getMostRecentWindow("navigator:browser")
+                .gBrowser.selectedBrowser.removeAttribute("blank");
+      });
+      waitForPaint(win, winUtils, callback);
+    }, win);
+  };
+
+  waitUntilPainted(function () {
     var scrollbox = win.document.getElementById("scrollbox");
-    let winUtils = SpecialPowers.getDOMWindowUtils(win);
     let outstandingTests = [
       [false, false],
       [false, true],
       [true, false],
       [true, true],
     ];
 
     // grab the refresh driver, since we want to make sure
@@ -146,17 +161,17 @@ function runTest() {
         sendTouchpadScrollMotion(scrollbox, -1, ctrlKey, isMomentum, function() {
           setTimeout(nextTest, 0);
         });
       }
 
       sendTouchpadScrollMotion(scrollbox, 1, ctrlKey, isMomentum, check);
     }
     nextTest();
-  }, win);
+  });
 }
 
 window.onload = function() {
   SpecialPowers.pushPrefEnv({
     "set":[["general.smoothScroll", false],
            ["mousewheel.acceleration.start", -1],
            ["mousewheel.system_scroll_override_on_root_content.enabled", false],
            ["mousewheel.with_control.action", 3]]}, runTest);
--- a/dom/events/test/window_wheel_default_action.html
+++ b/dom/events/test/window_wheel_default_action.html
@@ -43,17 +43,28 @@
 </div>
 <div id="spacerForBody"></div>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-SimpleTest.waitForFocus(runTests, window);
+// Until the first non-blank paint, the parent will set the opacity of our
+// browser to 0 using the 'blank' attribute.
+// Until the blank attribute is removed, we can't send scroll events.
+SimpleTest.waitForFocus(function() {
+  SpecialPowers.loadChromeScript(_ => {
+    Components.utils.import("resource://gre/modules/Services.jsm");
+    Services.wm.getMostRecentWindow("navigator:browser")
+            .gBrowser.selectedBrowser.removeAttribute("blank");
+  });
+  runTests();
+}, window);
+
 SimpleTest.requestFlakyTimeout("untriaged");
 
 var winUtils = SpecialPowers.getDOMWindowUtils(window);
 // grab refresh driver
 winUtils.advanceTimeAndRefresh(100);
 
 var gScrollableElement = document.getElementById("scrollable");
 var gScrolledElement = document.getElementById("scrolled");
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -511,30 +511,44 @@ public:
 
   static PendingInputEventHangAnnotator sSingleton;
 };
 PendingInputEventHangAnnotator PendingInputEventHangAnnotator::sSingleton;
 #endif
 
 NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
 
+class ContentChild::ShutdownCanary final
+{ };
+
 ContentChild* ContentChild::sSingleton;
+StaticAutoPtr<ContentChild::ShutdownCanary> ContentChild::sShutdownCanary;
 
 ContentChild::ContentChild()
  : mID(uint64_t(-1))
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
  , mMainChromeTid(0)
  , mMsaaID(0)
 #endif
  , mIsAlive(true)
  , mShuttingDown(false)
 {
   // This process is a content process, so it's clearly running in
   // multiprocess mode!
   nsDebugImpl::SetMultiprocessMode("Child");
+
+  // When ContentChild is created, the observer service does not even exist.
+  // When ContentChild::RecvSetXPCOMProcessAttributes is called (the first
+  // IPDL call made on this object), shutdown may have already happened. Thus
+  // we create a canary here that relies upon getting cleared if shutdown
+  // happens without requiring the observer service at this time.
+  if (!sShutdownCanary) {
+    sShutdownCanary = new ShutdownCanary();
+    ClearOnShutdown(&sShutdownCanary, ShutdownPhase::Shutdown);
+  }
 }
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4722) /* Silence "destructor never returns" warning */
 #endif
 
 ContentChild::~ContentChild()
@@ -556,16 +570,20 @@ NS_INTERFACE_MAP_END
 
 
 mozilla::ipc::IPCResult
 ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
                                             const StructuredCloneData& aInitialData,
                                             nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
                                             nsTArray<FontFamilyListEntry>&& aFontFamilyList)
 {
+  if (!sShutdownCanary) {
+    return IPC_OK();
+  }
+
   mLookAndFeelCache = Move(aLookAndFeelIntCache);
   mFontFamilies = Move(aFontFamilyList);
   gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
   InitXPCOM(aXPCOMInit, aInitialData);
   InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
 
 #ifdef NS_PRINTING
   // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_ContentChild_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBrowserOrId.h"
 #include "mozilla/dom/PContentChild.h"
+#include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #include "nsWeakPtr.h"
 #include "nsIWindowProvider.h"
@@ -768,16 +769,19 @@ private:
 
   bool mIsForBrowser;
   nsString mRemoteType = NullString();
   bool mIsAlive;
   nsString mProcessName;
 
   static ContentChild* sSingleton;
 
+  class ShutdownCanary;
+  static StaticAutoPtr<ShutdownCanary> sShutdownCanary;
+
   nsCOMPtr<nsIDomainPolicy> mPolicy;
   nsCOMPtr<nsITimer> mForceKillTimer;
 
 #ifdef MOZ_GECKO_PROFILER
   RefPtr<ChildProfilerController> mProfilerController;
 #endif
 
 #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -403,24 +403,28 @@ MediaEngineWebRTCMicrophoneSource::Updat
       LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
     }
     error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise);
     if (error) {
       LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
     }
   }
 
-  mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
-  if (mSkipProcessing) {
-    mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
-    mAudioOutputObserver = nullptr;
-  } else {
-    // make sure we route a copy of the mixed audio output of this MSG to the
-    // AEC
-    mAudioOutputObserver = new AudioOutputObserver();
+  // we don't allow switching from non-fast-path to fast-path on the fly yet
+  if (mState != kStarted) {
+    mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
+    if (mSkipProcessing) {
+      mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
+    } else {
+      // make sure we route a copy of the mixed audio output of this MSG to the
+      // AEC
+      if (!mAudioOutputObserver) {
+        mAudioOutputObserver = new AudioOutputObserver();
+      }
+    }
   }
   SetLastPrefs(prefs);
   return NS_OK;
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
     const MediaEnginePrefs& aPrefs)
@@ -638,16 +642,17 @@ MediaEngineWebRTCMicrophoneSource::Packe
 }
 
 template<typename T>
 void
 MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
                                                  size_t aFrames,
                                                  uint32_t aChannels)
 {
+  MonitorAutoLock lock(mMonitor);
   if (mState != kStarted) {
     return;
   }
 
   if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
     mTotalFrames += aFrames;
     if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
       MOZ_LOG(AudioLogModule(), LogLevel::Debug,
@@ -953,20 +958,16 @@ MediaEngineWebRTCMicrophoneSource::Proce
                                                 length);
       free(buffer);
       if (res == -1) {
         return;
       }
     }
   }
 
-  MonitorAutoLock lock(mMonitor);
-  if (mState != kStarted)
-    return;
-
   uint32_t channels = isStereo ? 2 : 1;
   InsertInGraph<int16_t>(audio10ms, length, channels);
 }
 
 void
 MediaEngineWebRTCAudioCaptureSource::GetName(nsAString &aName) const
 {
   aName.AssignLiteral("AudioCapture");
--- a/dom/tests/mochitest/bugs/test_bug440572.html
+++ b/dom/tests/mochitest/bugs/test_bug440572.html
@@ -22,27 +22,24 @@ function receiveMessage(e)
   is(e.origin, "http://example.org", "wrong sender!");
   messages.set(e.data.from, e.data.result);
 }
 
 window.addEventListener("message", receiveMessage);
 
 function runtests()
 {
-  is(messages.size, 3, "received the right number of messages.");
+  is(messages.size, 2, "received the right number of messages.");
   is(messages.get("test"), "success", "test in frame failed.");
-  isnot(messages.get("content"), "success", "parent[\"content\"] should be the WebIDL property of Window.");
   isnot(messages.get("dump"), "success", "parent[\"dump\"] should be the WebIDL property of Window.");
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 <br>
 <iframe name="test" src="http://example.org:80/tests/dom/tests/mochitest/bugs/iframe_bug440572.html"></iframe>
 <br>
-<iframe name="content" src="http://example.org:80/tests/dom/tests/mochitest/bugs/iframe_bug440572.html"></iframe>
-<br>
 <iframe name="dump" src="http://example.org:80/tests/dom/tests/mochitest/bugs/iframe_bug440572.html"></iframe>
 </body>
 </html>
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -331,17 +331,21 @@ partial interface Window {
    * arguments, plus any additional arguments are passed on as
    * arguments on the dialog's window object (window.arguments).
    */
   [Throws, ChromeOnly, UnsafeInPrerendering] WindowProxy? openDialog(optional DOMString url = "",
                                                                    optional DOMString name = "",
                                                                    optional DOMString options = "",
                                                                    any... extraArguments);
 
-  [Replaceable, Throws, NeedsCallerType] readonly attribute object? content;
+  [
+#ifdef NIGHTLY_BUILD
+   ChromeOnly,
+#endif
+   Replaceable, Throws, NeedsCallerType] readonly attribute object? content;
 
   [Throws, ChromeOnly] any getInterface(IID iid);
 
   /**
    * Same as nsIDOMWindow.windowRoot, useful for event listener targeting.
    */
   [ChromeOnly, Throws]
   readonly attribute WindowRoot? windowRoot;
--- a/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html
@@ -12,16 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 </pre>
+<script src="utils.js"></script>
 <script type="text/javascript">
   SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
 
   function testFrame(src) {
     var iframe = document.createElement("iframe");
     iframe.src = src;
     window.callback = function(result) {
       window.callback = null;
@@ -35,21 +36,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     };
     document.body.appendChild(iframe);
   }
 
   var registration;
 
   function runTest() {
     MockServices.register();
-    testFrame('notificationclick-otherwindow.html');
     navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick-otherwindow.html" }).then(function(reg) {
       registration = reg;
+      return waitForState(reg.installing, 'activated');
     }, function(e) {
       ok(false, "registration should have passed!");
+    }).then(() => {
+      testFrame('notificationclick-otherwindow.html');
     });
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
--- a/dom/workers/test/serviceworkers/test_notificationclick.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -12,16 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 </pre>
+<script src="utils.js"></script>
 <script type="text/javascript">
   SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
 
   function testFrame(src) {
     var iframe = document.createElement("iframe");
     iframe.src = src;
     window.callback = function(result) {
       window.callback = null;
@@ -35,21 +36,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     };
     document.body.appendChild(iframe);
   }
 
   var registration;
 
   function runTest() {
     MockServices.register();
-    testFrame('notificationclick.html');
     navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick.html" }).then(function(reg) {
       registration = reg;
+      return waitForState(reg.installing, 'activated');
     }, function(e) {
       ok(false, "registration should have passed!");
+    }).then(() => {
+      // Now that we know the document will be controlled, create the frame.
+      testFrame('notificationclick.html');
     });
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
--- a/dom/workers/test/serviceworkers/test_notificationclick_focus.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick_focus.html
@@ -12,16 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 </pre>
+<script src="utils.js"></script>
 <script type="text/javascript">
   SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
 
   function testFrame(src) {
     var iframe = document.createElement("iframe");
     iframe.src = src;
     window.callback = function(result) {
       window.callback = null;
@@ -35,21 +36,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     };
     document.body.appendChild(iframe);
   }
 
   var registration;
 
   function runTest() {
     MockServices.register();
-    testFrame('notificationclick_focus.html');
     navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) {
       registration = reg;
+      return waitForState(reg.installing, 'activated');
     }, function(e) {
       ok(false, "registration should have passed!");
+    }).then(() => {
+      testFrame('notificationclick_focus.html');
     });
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
--- a/dom/workers/test/serviceworkers/test_notificationclose.html
+++ b/dom/workers/test/serviceworkers/test_notificationclose.html
@@ -12,16 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265841">Bug 1265841</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 </pre>
+<script src="utils.js"></script>
 <script type="text/javascript">
   SimpleTest.requestFlakyTimeout("Mock alert service dispatches show, click, and close events.");
 
   function testFrame(src) {
     var iframe = document.createElement("iframe");
     iframe.src = src;
     window.callback = function(result) {
       window.callback = null;
@@ -35,21 +36,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     };
     document.body.appendChild(iframe);
   }
 
   var registration;
 
   function runTest() {
     MockServices.register();
-    testFrame('notificationclose.html');
     navigator.serviceWorker.register("notificationclose.js", { scope: "notificationclose.html" }).then(function(reg) {
       registration = reg;
+      return waitForState(reg.installing, 'activated');
     }, function(e) {
       ok(false, "registration should have passed!");
+    }).then(() => {
+      testFrame('notificationclose.html');
     });
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
--- a/gfx/ipc/VsyncBridgeParent.cpp
+++ b/gfx/ipc/VsyncBridgeParent.cpp
@@ -23,17 +23,17 @@ VsyncBridgeParent::Start(Endpoint<PVsync
 
   return parent;
 }
 
 VsyncBridgeParent::VsyncBridgeParent()
  : mOpen(false)
 {
   MOZ_COUNT_CTOR(VsyncBridgeParent);
-  mCompositorThreadRef = new CompositorThreadHolderDebug("VsyncBridge");
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VsyncBridgeParent::~VsyncBridgeParent()
 {
   MOZ_COUNT_DTOR(VsyncBridgeParent);
 }
 
 void
--- a/gfx/ipc/VsyncBridgeParent.h
+++ b/gfx/ipc/VsyncBridgeParent.h
@@ -33,15 +33,15 @@ private:
   VsyncBridgeParent();
   ~VsyncBridgeParent();
 
   void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint);
   void ShutdownImpl();
 
 private:
   bool mOpen;
-  RefPtr<layers::CompositorThreadHolderDebug> mCompositorThreadRef;
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadRef;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // include_gfx_ipc_VsyncBridgeParent_h
--- a/gfx/layers/SourceSurfaceVolatileData.h
+++ b/gfx/layers/SourceSurfaceVolatileData.h
@@ -27,16 +27,17 @@ class SourceSurfaceVolatileData : public
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceVolatileData, override)
 
   SourceSurfaceVolatileData()
     : mMutex("SourceSurfaceVolatileData")
     , mStride(0)
     , mMapCount(0)
     , mFormat(SurfaceFormat::UNKNOWN)
+    , mWasPurged(false)
   {
   }
 
   bool Init(const IntSize &aSize,
             int32_t aStride,
             SurfaceFormat aFormat);
 
   uint8_t *GetData() override { return mVBufPtr; }
@@ -61,20 +62,22 @@ public:
   // we want to allow it for SourceSurfaceVolatileData since it should
   // always be fine (for reading at least).
   //
   // This is the same as the base class implementation except using
   // mMapCount instead of mIsMapped since that breaks for multithread.
   bool Map(MapType, MappedSurface *aMappedSurface) override
   {
     MutexAutoLock lock(mMutex);
+    MOZ_DIAGNOSTIC_ASSERT(!mWasPurged);
     if (mMapCount == 0) {
       mVBufPtr = mVBuf;
     }
     if (mVBufPtr.WasBufferPurged()) {
+      mWasPurged = true;
       return false;
     }
     aMappedSurface->mData = mVBufPtr;
     aMappedSurface->mStride = mStride;
     ++mMapCount;
     return true;
   }
 
@@ -95,14 +98,15 @@ private:
 
   Mutex mMutex;
   int32_t mStride;
   int32_t mMapCount;
   IntSize mSize;
   RefPtr<VolatileBuffer> mVBuf;
   VolatileBufferPtr<uint8_t> mVBufPtr;
   SurfaceFormat mFormat;
+  bool mWasPurged;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_SOURCESURFACEVOLATILEDATA_H_ */
--- a/gfx/layers/ipc/CompositorManagerParent.cpp
+++ b/gfx/layers/ipc/CompositorManagerParent.cpp
@@ -33,18 +33,16 @@ CompositorManagerParent::CreateSameProce
     MOZ_ASSERT_UNREACHABLE("Already initialized");
     return nullptr;
   }
 
   // The child is responsible for setting up the IPC channel in the same
   // process case because if we open from the child perspective, we can do it
   // on the main thread and complete before we return the manager handles.
   RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
-  parent->mCompositorThreadHolder =
-    new CompositorThreadHolderDebug("CompositorManagerSame");
   parent->SetOtherProcessId(base::GetCurrentProcId());
 
   // CompositorManagerParent::Bind would normally add a reference for IPDL but
   // we don't use that in the same process case.
   parent.get()->AddRef();
   sInstance = parent;
 
   if (!sActiveActors) {
@@ -59,18 +57,16 @@ CompositorManagerParent::Create(Endpoint
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // We are creating a manager for the another process, inside the GPU process
   // (or UI process if it subsumbed the GPU process).
   MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
 
   RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
-  bridge->mCompositorThreadHolder =
-    new CompositorThreadHolderDebug("CompositorManagerContent");
 
   RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PCompositorManagerParent>&&>(
     "CompositorManagerParent::Bind",
     bridge,
     &CompositorManagerParent::Bind,
     Move(aEndpoint));
   CompositorThreadHolder::Loop()->PostTask(runnable.forget());
 }
@@ -111,16 +107,17 @@ CompositorManagerParent::CreateSameProce
     new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
                                aUseExternalSurfaceSize, aSurfaceSize);
 
   sInstance->mPendingCompositorBridges.AppendElement(bridge);
   return bridge.forget();
 }
 
 CompositorManagerParent::CompositorManagerParent()
+  : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
 {
 }
 
 CompositorManagerParent::~CompositorManagerParent()
 {
 }
 
 void
--- a/gfx/layers/ipc/CompositorManagerParent.h
+++ b/gfx/layers/ipc/CompositorManagerParent.h
@@ -13,17 +13,17 @@
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/layers/PCompositorManagerParent.h"
 #include "nsTArray.h"                   // for AutoTArray
 
 namespace mozilla {
 namespace layers {
 
 class CompositorBridgeParent;
-class CompositorThreadHolderDebug;
+class CompositorThreadHolder;
 
 class CompositorManagerParent final : public PCompositorManagerParent
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent)
 
 public:
   static already_AddRefed<CompositorManagerParent> CreateSameProcess();
   static void Create(Endpoint<PCompositorManagerParent>&& aEndpoint);
@@ -51,17 +51,17 @@ private:
   ~CompositorManagerParent() override;
 
   void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint);
 
   void DeallocPCompositorManagerParent() override;
 
   void DeferredDestroy();
 
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadHolder;
+  RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
 
   AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/CompositorThread.cpp
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -4,56 +4,26 @@
  * 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 "CompositorThread.h"
 #include "MainThreadUtils.h"
 #include "nsThreadUtils.h"
 #include "CompositorBridgeParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/media/MediaSystemResourceService.h"
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"     // for CrashReporter
-#endif
 
 namespace mozilla {
 
 namespace gfx {
 // See VRManagerChild.cpp
 void ReleaseVRManagerParentSingleton();
 } // namespace gfx
 
 namespace layers {
 
-#ifdef MOZ_CRASHREPORTER
-static Atomic<int32_t> sHoldersNextId(0);
-#endif
-
-CompositorThreadHolderDebug::CompositorThreadHolderDebug(const char* aName)
-  : mHolder(CompositorThreadHolder::GetSingleton())
-{
-#ifdef MOZ_CRASHREPORTER
-  if (XRE_IsParentProcess()) {
-    mId.AppendLiteral("gfxCompositorThread:");
-    mId.Append(aName);
-    mId.AppendLiteral(":");
-    mId.AppendInt(++sHoldersNextId);
-    CrashReporter::AnnotateCrashReport(mId, NS_LITERAL_CSTRING("1"));
-  }
-#endif
-}
-
-CompositorThreadHolderDebug::~CompositorThreadHolderDebug()
-{
-#ifdef MOZ_CRASHREPORTER
-  if (XRE_IsParentProcess()) {
-    CrashReporter::AnnotateCrashReport(mId, NS_LITERAL_CSTRING("0"));
-  }
-#endif
-}
-
 static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
 static bool sFinishedCompositorShutDown = false;
 
 // See ImageBridgeChild.cpp
 void ReleaseImageBridgeParentSingleton();
 
 CompositorThreadHolder* GetCompositorThreadHolder()
 {
--- a/gfx/layers/ipc/CompositorThread.h
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -55,30 +55,14 @@ private:
   base::Thread* const mCompositorThread;
 
   static base::Thread* CreateCompositorThread();
   static void DestroyCompositorThread(base::Thread* aCompositorThread);
 
   friend class CompositorBridgeParent;
 };
 
-class CompositorThreadHolderDebug final
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorThreadHolderDebug)
-
-public:
-  explicit CompositorThreadHolderDebug(const char* aName);
-
-private:
-  ~CompositorThreadHolderDebug();
-
-  RefPtr<CompositorThreadHolder> mHolder;
-#ifdef MOZ_CRASHREPORTER
-  nsAutoCString mId;
-#endif
-};
-
 base::Thread* CompositorThread();
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_CompositorThread_h
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -362,19 +362,17 @@ ImageBridgeParent::GetInstance(ProcessId
   MonitorAutoLock lock(*sImageBridgesLock);
   NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process");
   return sImageBridges[aId];
 }
 
 void
 ImageBridgeParent::OnChannelConnected(int32_t aPid)
 {
-  mCompositorThreadHolder =
-    new CompositorThreadHolderDebug(IsSameProcess() ? "ImageBridgeSame"
-                                                    : "ImageBridge");
+  mCompositorThreadHolder = GetCompositorThreadHolder();
 }
 
 
 bool
 ImageBridgeParent::AllocShmem(size_t aSize,
                       ipc::SharedMemory::SharedMemoryType aType,
                       ipc::Shmem* aShmem)
 {
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -136,15 +136,15 @@ private:
   bool mSetChildThreadPriority;
   bool mClosed;
 
   /**
    * Map of all living ImageBridgeParent instances
    */
   static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
 
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadHolder;
+  RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // gfx_layers_ipc_ImageBridgeParent_h_
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -16,17 +16,17 @@ using namespace mozilla::gfx;
 
 static VideoBridgeParent* sVideoBridgeSingleton;
 
 VideoBridgeParent::VideoBridgeParent()
   : mClosed(false)
 {
   mSelfRef = this;
   sVideoBridgeSingleton = this;
-  mCompositorThreadRef = new CompositorThreadHolderDebug("VideoBridge");
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VideoBridgeParent::~VideoBridgeParent()
 {
   sVideoBridgeSingleton = nullptr;
 }
 
 /* static */ VideoBridgeParent*
--- a/gfx/layers/ipc/VideoBridgeParent.h
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -7,17 +7,17 @@
 #define gfx_layers_ipc_VideoBridgeParent_h_
 
 #include "mozilla/layers/PVideoBridgeParent.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
-class CompositorThreadHolderDebug;
+class CompositorThreadHolder;
 
 class VideoBridgeParent final : public PVideoBridgeParent,
                                 public HostIPCAllocator,
                                 public ShmemAllocator
 {
 public:
   VideoBridgeParent();
   ~VideoBridgeParent();
@@ -58,17 +58,17 @@ public:
   void DeallocShmem(ipc::Shmem& aShmem) override;
 
 private:
   void DeallocPVideoBridgeParent() override;
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VideoBridgeParent> mSelfRef;
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadRef;
+  RefPtr<CompositorThreadHolder> mCompositorThreadRef;
 
   std::map<uint64_t, PTextureParent*> mTextureMap;
 
   bool mClosed;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -161,23 +161,25 @@ FontFeatureTagForVariantWidth(uint32_t a
       return TRUETYPE_TAG('t','w','i','d');
     case NS_FONT_VARIANT_WIDTH_QUARTER:
       return TRUETYPE_TAG('q','w','i','d');
     default:
       return 0;
   }
 }
 
-void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const
+void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle,
+                                    bool aVertical) const
 {
   // add in font-variant features
   gfxFontFeature setting;
 
   // -- kerning
-  setting.mTag = TRUETYPE_TAG('k','e','r','n');
+  setting.mTag = aVertical ? TRUETYPE_TAG('v','k','r','n')
+                           : TRUETYPE_TAG('k','e','r','n');
   switch (kerning) {
     case NS_FONT_KERNING_NONE:
       setting.mValue = 0;
       aStyle->featureSettings.AppendElement(setting);
       break;
     case NS_FONT_KERNING_NORMAL:
       setting.mValue = 1;
       aStyle->featureSettings.AppendElement(setting);
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -135,17 +135,18 @@ struct nsFont {
 
   bool Equals(const nsFont& aOther) const;
 
   nsFont& operator=(const nsFont& aOther);
 
   void CopyAlternates(const nsFont& aOther);
 
   // Add featureSettings into style
-  void AddFontFeaturesToStyle(gfxFontStyle *aStyle) const;
+  void AddFontFeaturesToStyle(gfxFontStyle *aStyle,
+                              bool aVertical) const;
 
   void AddFontVariationsToStyle(gfxFontStyle *aStyle) const;
 };
 
 #define NS_FONT_VARIANT_NORMAL            0
 #define NS_FONT_VARIANT_SMALL_CAPS        1
 
 #endif /* nsFont_h___ */
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -129,17 +129,17 @@ nsFontMetrics::nsFontMetrics(const nsFon
                        aParams.explicitLanguage,
                        aFont.sizeAdjust,
                        aFont.systemFont,
                        mDeviceContext->IsPrinterContext(),
                        aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
                        aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
                        aFont.languageOverride);
 
-    aFont.AddFontFeaturesToStyle(&style);
+    aFont.AddFontFeaturesToStyle(&style, mOrientation == gfxFont::eVertical);
     aFont.AddFontVariationsToStyle(&style);
 
     gfxFloat devToCssSize = gfxFloat(mP2A) /
         gfxFloat(mDeviceContext->AppUnitsPerCSSPixel());
     mFontGroup = gfxPlatform::GetPlatform()->
         CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
                         aParams.userFontSet, devToCssSize);
 }
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -182,29 +182,29 @@ VRManagerParent::RegisterVRManagerInComp
   aVRManager->RegisterWithManager();
 }
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateSameProcess()
 {
   MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(base::GetCurrentProcId(), false);
-  vmp->mCompositorThreadHolder = new layers::CompositorThreadHolderDebug("VRManagerSame");
+  vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
   vmp->mSelfRef = vmp;
   loop->PostTask(NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get()));
   return vmp.get();
 }
 
 bool
 VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
 
   RefPtr<VRManagerParent> vmp = new VRManagerParent(aEndpoint.OtherPid(), false);
-  vmp->mCompositorThreadHolder = new layers::CompositorThreadHolderDebug("VRManager");
+  vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
   loop->PostTask(NewRunnableMethod<Endpoint<PVRManagerParent>&&>(
     "gfx::VRManagerParent::Bind",
     vmp,
     &VRManagerParent::Bind,
     Move(aEndpoint)));
   return true;
 }
 
@@ -223,19 +223,17 @@ VRManagerParent::ActorDestroy(ActorDestr
     NewRunnableMethod("gfx::VRManagerParent::DeferredDestroy",
                       this,
                       &VRManagerParent::DeferredDestroy));
 }
 
 void
 VRManagerParent::OnChannelConnected(int32_t aPid)
 {
-  mCompositorThreadHolder =
-    new layers::CompositorThreadHolderDebug(IsSameProcess() ? "VRManagerSame"
-                                                            : "VRManager");
+  mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
 }
 
 mozilla::ipc::IPCResult
 VRManagerParent::RecvRefreshDisplays()
 {
   // This is called to refresh the VR Displays for Navigator.GetVRDevices().
   // We must pass "true" to VRManager::RefreshVRDisplays()
   // to ensure that the promise returned by Navigator.GetVRDevices
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -119,17 +119,17 @@ private:
 
   void DeferredDestroy();
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VRManagerParent> mSelfRef;
 
   // Keep the compositor thread alive, until we have destroyed ourselves.
-  RefPtr<layers::CompositorThreadHolderDebug> mCompositorThreadHolder;
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadHolder;
 
   // Keep the VRManager alive, until we have destroyed ourselves.
   RefPtr<VRManager> mVRManagerHolder;
   nsRefPtrHashtable<nsUint32HashKey, impl::VRDisplayPuppet> mVRDisplayTests;
   nsRefPtrHashtable<nsUint32HashKey, impl::VRControllerPuppet> mVRControllerTests;
   uint32_t mDisplayTestID;
   uint32_t mControllerTestID;
   bool mHaveEventListener;
--- a/js/src/devtools/automation/autospider.py
+++ b/js/src/devtools/automation/autospider.py
@@ -391,16 +391,22 @@ test_suites |= set(normalize_tests(varia
 test_suites |= set(normalize_tests(variant.get('extra-tests', {}).get('all', [])))
 
 # Now adjust the variant's default test list with command-line arguments.
 test_suites |= set(normalize_tests(args.run_tests.split(",")))
 test_suites -= set(normalize_tests(args.skip_tests.split(",")))
 if 'all' in args.skip_tests.split(","):
     test_suites = []
 
+# Bug 1391877 - Windows test runs are getting mysterious timeouts when run
+# through taskcluster, but only when running multiple jit-test jobs in
+# parallel. Work around them for now.
+if platform.system() == 'Windows':
+    env['JITTEST_EXTRA_ARGS'] = "-j1 " + env.get('JITTEST_EXTRA_ARGS', '')
+
 # Always run all enabled tests, even if earlier ones failed. But return the
 # first failed status.
 results = []
 
 # 'checks' is a superset of 'check-style'.
 if 'checks' in test_suites:
     results.append(run_test_command([MAKE, 'check']))
 elif 'check-style' in test_suites:
--- a/js/src/jit-test/tests/debug/bug1109328.js
+++ b/js/src/jit-test/tests/debug/bug1109328.js
@@ -1,7 +1,8 @@
 try {
     gcslice(0)(""());
 } catch (e) {}
 g = newGlobal()
 g.parent = this
 g.eval("Debugger(parent).onExceptionUnwind=(function(){})");
-gcparam("maxBytes", gcparam("gcBytes"));
+gcparam("maxBytes", gcparam("maxBytes") - 8);
+gc();
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1332,17 +1332,17 @@ RestyleManager::ProcessRestyledFrames(ns
                                              nsGkAtoms::html)) {
         // If the restyled element provided/provides the scrollbar styles for
         // the viewport before and/or after this restyle, AND it's not coopting
         // that responsibility from some other element (which would need
         // reconstruction to make its own scrollframe now), THEN: we don't need
         // to reconstruct - we can just reflow, because no scrollframe is being
         // added/removed.
         nsIContent* prevOverrideNode =
-          presContext->GetViewportScrollbarStylesOverrideNode();
+          presContext->GetViewportScrollbarStylesOverrideElement();
         nsIContent* newOverrideNode =
           presContext->UpdateViewportScrollbarStylesOverride();
 
         if (data.mContent == prevOverrideNode ||
             data.mContent == newOverrideNode) {
           // If we get here, the restyled element provided the scrollbar styles
           // for viewport before this restyle, OR it will provide them after.
           if (!prevOverrideNode || !newOverrideNode ||
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1398500.html
@@ -0,0 +1,21 @@
+<!-- Quirks mode on purpose -->
+<html>
+  <head>
+    <style>
+      body { overflow: scroll; border: 1px solid green; }
+    </style>
+    <script>
+      onload = function() {
+        var newBody = document.createElement("body");
+        newBody.textContent = "This element should not have scrollbars!";
+        document.documentElement.appendChild(newBody);
+        window.nooptimize = newBody.offsetWidth;
+        document.body.remove();
+        newBody.scrollHeight; // Asserts in a debug build
+      }
+    </script>
+  </head>
+  <body>
+    First body
+  </body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -495,8 +495,9 @@ load 1362423-1.html
 load 1381323.html
 asserts-if(!stylo,1) load 1388625-1.html # bug 1389286
 load 1390389.html
 load 1395591-1.html
 load 1395715-1.html
 load 1397398-1.html
 load 1397398-2.html
 load 1397398-3.html
+load 1398500.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -8610,26 +8610,41 @@ nsCSSFrameConstructor::ContentRemoved(ns
 {
   MOZ_ASSERT(aChild);
   AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
   NS_PRECONDITION(mUpdateCount != 0,
                   "Should be in an update while destroying frames");
   nsPresContext* presContext = mPresShell->GetPresContext();
   MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
 
-  if (aChild->IsHTMLElement(nsGkAtoms::body) ||
-      (!aContainer && aChild->IsElement())) {
+  if (aChild == presContext->GetViewportScrollbarStylesOverrideElement()) {
     // We might be removing the element that we propagated viewport scrollbar
     // styles from.  Recompute those. (This clause covers two of the three
     // possible scrollbar-propagation sources: the <body> [as aChild or a
     // descendant] and the root node. The other possible scrollbar-propagation
     // source is a fullscreen element, and we have code elsewhere to update
     // scrollbars after fullscreen elements are removed -- specifically, it's
-    // part of the fullscreen cleanup code called by Element::UnbindFromTree.)
-    presContext->UpdateViewportScrollbarStylesOverride();
+    // part of the fullscreen cleanup code called by Element::UnbindFromTree.
+    // We don't handle the fullscreen case here, because it doesn't change the
+    // scrollbar styles override element stored on the prescontext.)
+    Element* newOverrideElement =
+      presContext->UpdateViewportScrollbarStylesOverride();
+
+    // Now if newOverrideElement is not the root and isn't aChild (which it
+    // could be if all we're doing here is reframing the current override
+    // element), it needs reframing.  In particular, it used to have a
+    // scrollframe (because its overflow was not "visible"), but now it will
+    // propagate its overflow to the viewport, so it should not need a
+    // scrollframe anymore.
+    if (newOverrideElement && newOverrideElement->GetParent() &&
+        newOverrideElement != aChild) {
+      LAYOUT_PHASE_TEMP_EXIT();
+      RecreateFramesForContent(newOverrideElement, InsertionKind::Async);
+      LAYOUT_PHASE_TEMP_REENTER();
+    }
   }
 
 #ifdef DEBUG
   if (gNoisyContentUpdates) {
     printf("nsCSSFrameConstructor::ContentRemoved container=%p child=%p "
            "old-next-sibling=%p\n",
            static_cast<void*>(aContainer),
            static_cast<void*>(aChild),
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -25,16 +25,17 @@
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsIPrintSettings.h"
 #include "nsLanguageAtomService.h"
 #include "mozilla/LookAndFeel.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDOMHTMLDocument.h"
+#include "nsHTMLDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsThreadUtils.h"
 #include "nsFrameManager.h"
 #include "nsLayoutUtils.h"
 #include "nsViewManager.h"
 #include "mozilla/GeckoRestyleManager.h"
 #include "mozilla/RestyleManager.h"
@@ -226,17 +227,17 @@ nsPresContext::nsPresContext(nsIDocument
     mDefaultColor(NS_RGBA(0,0,0,0)),
     mBackgroundColor(NS_RGB(0xFF, 0xFF, 0xFF)),
     mLinkColor(NS_RGB(0x00, 0x00, 0xEE)),
     mActiveLinkColor(NS_RGB(0xEE, 0x00, 0x00)),
     mVisitedLinkColor(NS_RGB(0x55, 0x1A, 0x8B)),
     mFocusBackgroundColor(mBackgroundColor),
     mFocusTextColor(mDefaultColor),
     mBodyTextColor(mDefaultColor),
-    mViewportScrollbarOverrideNode(nullptr),
+    mViewportScrollbarOverrideElement(nullptr),
     mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
     mFocusRingWidth(1),
     mExistThrottledUpdates(false),
     // mImageAnimationMode is initialised below, in constructor body
     mImageAnimationModePref(imgIContainer::kNormalAnimMode),
     mFontGroupCacheDirty(true),
     mInterruptChecksToSkip(0),
     mElementsRestyled(0),
@@ -1452,17 +1453,17 @@ CheckOverflow(const nsStyleDisplay* aDis
     *aStyles = ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
                                NS_STYLE_OVERFLOW_HIDDEN, aDisplay);
   } else {
     *aStyles = ScrollbarStyles(aDisplay);
   }
   return true;
 }
 
-static nsIContent*
+static Element*
 GetPropagatedScrollbarStylesForViewport(nsPresContext* aPresContext,
                                         ScrollbarStyles *aStyles)
 {
   nsIDocument* document = aPresContext->Document();
   Element* docElement = document->GetRootElement();
 
   // docElement might be null if we're doing this after removing it.
   if (!docElement) {
@@ -1474,75 +1475,73 @@ GetPropagatedScrollbarStylesForViewport(
   RefPtr<nsStyleContext> rootStyle =
     styleSet->ResolveStyleFor(docElement, nullptr, LazyComputeBehavior::Allow);
   if (CheckOverflow(rootStyle->StyleDisplay(), aStyles)) {
     // tell caller we stole the overflow style from the root element
     return docElement;
   }
 
   // Don't look in the BODY for non-HTML documents or HTML documents
-  // with non-HTML roots
+  // with non-HTML roots.
   // XXX this should be earlier; we shouldn't even look at the document root
   // for non-HTML documents. Fix this once we support explicit CSS styling
   // of the viewport
   // XXX what about XHTML?
-  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(document));
+  nsHTMLDocument* htmlDoc = document->AsHTMLDocument();
   if (!htmlDoc || !docElement->IsHTMLElement()) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIDOMHTMLElement> body;
-  htmlDoc->GetBody(getter_AddRefs(body));
-  nsCOMPtr<nsIContent> bodyElement = do_QueryInterface(body);
+  Element* bodyElement = htmlDoc->GetBody();
 
   if (!bodyElement ||
       !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
     // The body is not a <body> tag, it's a <frameset>.
     return nullptr;
   }
 
   RefPtr<nsStyleContext> bodyStyle =
-    styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle,
+    styleSet->ResolveStyleFor(bodyElement, rootStyle,
                               LazyComputeBehavior::Allow);
 
   if (CheckOverflow(bodyStyle->StyleDisplay(), aStyles)) {
     // tell caller we stole the overflow style from the body element
     return bodyElement;
   }
 
   return nullptr;
 }
 
-nsIContent*
+Element*
 nsPresContext::UpdateViewportScrollbarStylesOverride()
 {
   // Start off with our default styles, and then update them as needed.
   mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_AUTO,
                                             NS_STYLE_OVERFLOW_AUTO);
-  mViewportScrollbarOverrideNode = nullptr;
+  mViewportScrollbarOverrideElement = nullptr;
   // Don't propagate the scrollbar state in printing or print preview.
   if (!IsPaginated()) {
-    mViewportScrollbarOverrideNode =
+    mViewportScrollbarOverrideElement =
       GetPropagatedScrollbarStylesForViewport(this, &mViewportStyleScrollbar);
   }
 
   nsIDocument* document = Document();
   if (Element* fullscreenElement = document->GetFullscreenElement()) {
     // If the document is in fullscreen, but the fullscreen element is
     // not the root element, we should explicitly suppress the scrollbar
     // here. Note that, we still need to return the original element
     // the styles are from, so that the state of those elements is not
     // affected across fullscreen change.
     if (fullscreenElement != document->GetRootElement() &&
-        fullscreenElement != mViewportScrollbarOverrideNode) {
+        fullscreenElement != mViewportScrollbarOverrideElement) {
       mViewportStyleScrollbar = ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
                                                 NS_STYLE_OVERFLOW_HIDDEN);
     }
   }
-  return mViewportScrollbarOverrideNode;
+  return mViewportScrollbarOverrideElement;
 }
 
 bool
 nsPresContext::ElementWouldPropagateScrollbarStyles(Element* aElement)
 {
   MOZ_ASSERT(IsPaginated(), "Should only be called on paginated contexts");
   if (aElement->GetParent() && !aElement->IsHTMLElement(nsGkAtoms::body)) {
     // We certainly won't be propagating from this element.
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -712,26 +712,26 @@ public:
    * property that should be applied to the viewport. If one is found then we
    * return the element that we took the overflow from (which should then be
    * treated as "overflow: visible"), and we store the overflow style here.
    * If the document is in fullscreen, and the fullscreen element is not the
    * root, the scrollbar of viewport will be suppressed.
    * @return if scroll was propagated from some content node, the content node
    *         it was propagated from.
    */
-  nsIContent* UpdateViewportScrollbarStylesOverride();
+  mozilla::dom::Element* UpdateViewportScrollbarStylesOverride();
 
   /**
    * Returns the cached result from the last call to
    * UpdateViewportScrollbarStylesOverride() -- i.e. return the node
    * whose scrollbar styles we have propagated to the viewport (or nullptr if
    * there is no such node).
    */
-  nsIContent* GetViewportScrollbarStylesOverrideNode() const {
-    return mViewportScrollbarOverrideNode;
+  mozilla::dom::Element* GetViewportScrollbarStylesOverrideElement() const {
+    return mViewportScrollbarOverrideElement;
   }
 
   const ScrollbarStyles& GetViewportScrollbarStylesOverride() const
   {
     return mViewportStyleScrollbar;
   }
 
   /**
@@ -1356,24 +1356,24 @@ protected:
   nscolor               mActiveLinkColor;
   nscolor               mVisitedLinkColor;
 
   nscolor               mFocusBackgroundColor;
   nscolor               mFocusTextColor;
 
   nscolor               mBodyTextColor;
 
-  // This is a non-owning pointer. May be null. If non-null, it's guaranteed
-  // to be pointing to a node that's still alive, because we'll reset it in
-  // UpdateViewportScrollbarStylesOverride() as part of the cleanup code
-  // when this node is removed from the document. (For <body> and the root node,
-  // this call happens in nsCSSFrameConstructor::ContentRemoved(). For
-  // fullscreen elements, it happens in the fullscreen-specific cleanup
-  // invoked by Element::UnbindFromTree().)
-  nsIContent* MOZ_NON_OWNING_REF mViewportScrollbarOverrideNode;
+  // This is a non-owning pointer. May be null. If non-null, it's guaranteed to
+  // be pointing to an element that's still alive, because we'll reset it in
+  // UpdateViewportScrollbarStylesOverride() as part of the cleanup code when
+  // this element is removed from the document. (For <body> and the root
+  // element, this call happens in nsCSSFrameConstructor::ContentRemoved(). For
+  // fullscreen elements, it happens in the fullscreen-specific cleanup invoked
+  // by Element::UnbindFromTree().)
+  mozilla::dom::Element* MOZ_NON_OWNING_REF mViewportScrollbarOverrideElement;
   ScrollbarStyles       mViewportStyleScrollbar;
 
   uint8_t               mFocusRingWidth;
 
   bool mExistThrottledUpdates;
 
   uint16_t              mImageAnimationMode;
   uint16_t              mImageAnimationModePref;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1398500-1-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      body { overflow: scroll; border: 1px solid green; }
+    </style>
+  </head>
+  <body>
+    This element should not have scrollbars!
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1398500-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      body { overflow: scroll; border: 1px solid green; }
+    </style>
+    <script>
+      onload = function() {
+        var newBody = document.createElement("body");
+        newBody.textContent = "This element should not have scrollbars!";
+        document.documentElement.appendChild(newBody);
+        window.nooptimize = newBody.offsetWidth;
+        document.body.remove();
+      }
+    </script>
+  </head>
+  <body>
+    First body
+  </body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2036,8 +2036,9 @@ needs-focus == 1377447-1.html 1377447-1-
 needs-focus != 1377447-1.html 1377447-2.html
 == 1379041.html 1379041-ref.html
 == 1379696.html 1379696-ref.html
 == 1380224-1.html 1380224-1-ref.html
 == 1384065.html 1384065-ref.html
 == 1384275-1.html 1384275-1-ref.html
 == 1381821.html 1381821-ref.html
 == 1395650-1.html 1395650-1-ref.html
+== 1398500-1.html 1398500-1-ref.html
--- a/layout/reftests/css-break/reftest.list
+++ b/layout/reftests/css-break/reftest.list
@@ -1,13 +1,13 @@
 default-preferences pref(layout.css.box-decoration-break.enabled,true)
 
 == box-decoration-break-1.html box-decoration-break-1-ref.html
 fuzzy(1,20) fuzzy-if(skiaContent,1,700) fuzzy-if(webrender,4-4,36-36) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html
-fuzzy(45,460) fuzzy-if(skiaContent,57,374) fuzzy-if(Android,57,1330) fuzzy-if(styloVsGecko,2,1410) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543
+fuzzy(45,460) fuzzy-if(skiaContent,57,439) fuzzy-if(Android,57,1330) fuzzy-if(styloVsGecko,45,1410) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543
 random-if(!gtkWidget) HTTP(..) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html
 == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html
 == box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html
 fuzzy-if(!Android,1,62) fuzzy-if(Android,8,6627) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html #Bug 1313773
 == box-decoration-break-with-bidi.html box-decoration-break-with-bidi-ref.html
 == box-decoration-break-bug-1235152.html box-decoration-break-bug-1235152-ref.html
 == box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913-ref.html
 == vertical-wm-001.html vertical-wm-001-ref.html
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -140,16 +140,20 @@ pref("browser.sessionhistory.bfcacheIgno
 pref("browser.sessionstore.resume_from_crash", true);
 pref("browser.sessionstore.interval", 10000); // milliseconds
 pref("browser.sessionstore.backupInterval", 120000); // milliseconds -> 2 minutes
 pref("browser.sessionstore.max_tabs_undo", 10);
 pref("browser.sessionstore.max_resumed_crashes", 2);
 pref("browser.sessionstore.privacy_level", 0); // saving data: 0 = all, 1 = unencrypted sites, 2 = never
 pref("browser.sessionstore.debug_logging", false);
 
+// Download protection lists are not available on Fennec.
+pref("urlclassifier.downloadAllowTable", "");
+pref("urlclassifier.downloadBlockTable", "");
+
 /* these should help performance */
 pref("mozilla.widget.force-24bpp", true);
 pref("mozilla.widget.use-buffer-pixmap", true);
 pref("mozilla.widget.disable-native-theme", true);
 pref("layout.reflow.synthMouseMove", false);
 pref("layout.css.report_errors", false);
 
 /* download manager (don't show the window or alert) */
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -987,21 +987,16 @@ nsDNSService::Resolve(const nsACString  
 }
 
 NS_IMETHODIMP
 nsDNSService::ResolveNative(const nsACString        &aHostname,
                             uint32_t                 flags,
                             const OriginAttributes  &aOriginAttributes,
                             nsIDNSRecord           **result)
 {
-    // Synchronous resolution is not available on the main thread.
-    if (NS_IsMainThread()) {
-        return NS_ERROR_NOT_AVAILABLE;
-    }
-
     // grab reference to global host resolver and IDN service.  beware
     // simultaneous shutdown!!
     RefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     bool localDomain = false;
     {
         MutexAutoLock lock(mLock);
         res = mResolver;
--- a/testing/mozharness/configs/unittests/linux_unittest.py
+++ b/testing/mozharness/configs/unittests/linux_unittest.py
@@ -289,18 +289,17 @@ config = {
         },
     ],
     "vcs_output_timeout": 1000,
     "minidump_save_path": "%(abs_work_dir)s/../minidumps",
     "buildbot_max_log_size": 209715200,
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
-    "unstructured_flavors": {"mochitest": [],
-                            "xpcshell": [],
+    "unstructured_flavors": {"xpcshell": [],
                             "gtest": [],
                             "mozmill": [],
                             "cppunittest": [],
                             "jittest": [],
                             "mozbase": [],
                             },
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
     "download_minidump_stackwalk": True,
--- a/testing/mozharness/configs/unittests/mac_unittest.py
+++ b/testing/mozharness/configs/unittests/mac_unittest.py
@@ -236,18 +236,17 @@ config = {
         },
     ],
     "vcs_output_timeout": 1000,
     "minidump_save_path": "%(abs_work_dir)s/../minidumps",
     "buildbot_max_log_size": 52428800,
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
-    "unstructured_flavors": {"mochitest": [],
-                            "xpcshell": [],
+    "unstructured_flavors": {"xpcshell": [],
                             "gtest": [],
                             "mozmill": [],
                             "cppunittest": [],
                             "jittest": [],
                             "mozbase": [],
                             },
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
     "download_minidump_stackwalk": True,
--- a/testing/mozharness/configs/unittests/win_unittest.py
+++ b/testing/mozharness/configs/unittests/win_unittest.py
@@ -259,18 +259,17 @@ config = {
         },
     ],
     "vcs_output_timeout": 1000,
     "minidump_save_path": "%(abs_work_dir)s/../minidumps",
     "buildbot_max_log_size": 52428800,
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
-    "unstructured_flavors": {"mochitest": [],
-                            "xpcshell": [],
+    "unstructured_flavors": {"xpcshell": [],
                             "gtest": [],
                             "mozmill": [],
                             "cppunittest": [],
                             "jittest": [],
                             "mozbase": [],
                             },
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
     "download_minidump_stackwalk": True,
--- a/testing/talos/talos/pageloader/chrome/tscroll.js
+++ b/testing/talos/talos/pageloader/chrome/tscroll.js
@@ -108,17 +108,17 @@ function testScroll(target, stepSize, op
     return function() {
       return new Promise(function(resolve) {
         setTimeout(resolve, ms);
       });
     };
   }
 
   function rAF(fn) {
-    return content.requestAnimationFrame(fn);
+    return win.requestAnimationFrame(fn);
   }
 
   function P_rAF() {
     return new Promise(function(resolve) {
       rAF(resolve);
     });
   }
 
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-fonts-3/font-kerning-03.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[font-kerning-03.html]
-  type: reftest
-  expected: FAIL
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -467,19 +467,16 @@ nsresult nsCocoaWindow::CreateNativeWind
   // BorderlessWindow class.
   else if (features == NSBorderlessWindowMask)
     windowClass = [BorderlessWindow class];
 
   // Create the window
   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
                                  backing:NSBackingStoreBuffered defer:YES];
 
-  // By default, hide window titles.
-  [mWindow setTitleVisibility:NSWindowTitleHidden];
-
   // setup our notification delegate. Note that setDelegate: does NOT retain.
   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   [mWindow setDelegate:mDelegate];
 
   // Make sure that the content rect we gave has been honored.
   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   if (!NSEqualRects([mWindow frame], wantedFrame)) {
     // This can happen when the window is not on the primary screen.
@@ -1838,21 +1835,19 @@ nsCocoaWindow::SetTitle(const nsAString&
 
   const nsString& strTitle = PromiseFlatString(aTitle);
   NSString* title = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(strTitle.get())
                                             length:strTitle.Length()];
 
   if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) {
     // Don't cause invalidations.
     [mWindow disableSetNeedsDisplay];
-    [mWindow setTitleVisibility:NSWindowTitleHidden];
     [mWindow setTitle:title];
     [mWindow enableSetNeedsDisplay];
   } else {
-    [mWindow setTitleVisibility:NSWindowTitleVisible];
     [mWindow setTitle:title];
   }
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }