merge inbound to central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sat, 06 Jan 2018 00:27:10 +0200
changeset 436691 56c1eb9c065a52490d1f3438c882e8f90aef9cb8
parent 436646 2a4362ce2d9fa82f62909532a1b534d214761341 (current diff)
parent 436690 62edf9d0a6f9dcc1179e2297e63855f05d8452c9 (diff)
child 436692 b84fe2ad1ca27fc30c2e3f609b8f766185652560
push idunknown
push userunknown
push dateunknown
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge inbound to central. r=merge a=merge
browser/app/profile/firefox.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/tests/mochitest/webcomponents/test_content_element.html
--- a/accessible/tests/mochitest/elm/a11y.ini
+++ b/accessible/tests/mochitest/elm/a11y.ini
@@ -10,8 +10,9 @@ skip-if = buildapp == 'mulet'
 [test_listbox.xul]
 [test_MathMLSpec.html]
 [test_nsApplicationAcc.html]
 [test_plugin.html]
 skip-if = buildapp == 'mulet'
 [test_canvas.html]
 [test_shadowroot.html]
 skip-if = stylo # bug 1293844
+support-files = test_shadowroot_subframe.html
--- a/accessible/tests/mochitest/elm/test_shadowroot.html
+++ b/accessible/tests/mochitest/elm/test_shadowroot.html
@@ -3,57 +3,38 @@
 <head>
   <title>ShadowRoot tests</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
-  <script type="application/javascript"
-          src="../common.js"></script>
-  <script type="application/javascript"
-          src="../role.js"></script>
-
-  <script type="application/javascript">
-    function doTest() {
-      testElm("component", {
-        role: ROLE_GROUPING,
-        children: [
-        {
-          role: ROLE_PUSHBUTTON,
-        },
-        {
-          role: ROLE_LINK,
-        },
-        ]
-      });
-
-      SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
 </head>
 <body>
 
   <a target="_blank"
     title="Ensure accessible objects are created for shadow root"
     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1026125">
      Mozilla Bug 1026125
   </a><br/>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div role="group" id="component"></div>
   <script>
-    var component = document.getElementById("component");
-    var shadow = component.createShadowRoot();
-
-    shadow.innerHTML = "<button>Hello</button>" +
-      '<a href="#"> World</a>';
+    SimpleTest.waitForExplicitFinish();
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
+      var iframe = document.createElement("iframe");
+      iframe.src = "test_shadowroot_subframe.html";
+      document.body.appendChild(iframe);
+    });
   </script>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>ShadowRoot tests</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="../common.js"></script>
+  <script type="application/javascript" src="../role.js"></script>
+
+  <script type="application/javascript">
+    let SimpleTest = window.parent.SimpleTest;
+    let ok = window.parent.ok;
+    let is = window.parent.is;
+
+    function doTest() {
+      testElm("component", {
+        role: ROLE_GROUPING,
+        children: [
+        {
+          role: ROLE_PUSHBUTTON,
+        },
+        {
+          role: ROLE_LINK,
+        },
+        ]
+      });
+
+      SimpleTest.finish();
+    }
+
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+<body>
+  <div role="group" id="component"></div>
+  <script>
+    var component = document.getElementById("component");
+    var shadow = component.attachShadow({mode: "open"});
+
+    shadow.innerHTML = "<button>Hello</button>" +
+      '<a href="#"> World</a>';
+  </script>
+</body>
--- a/accessible/tests/mochitest/hittest/a11y.ini
+++ b/accessible/tests/mochitest/hittest/a11y.ini
@@ -4,11 +4,12 @@ support-files = zoom_tree.xul
   !/accessible/tests/mochitest/letters.gif
 
 [test_browser.html]
 [test_canvas_hitregion.html]
 skip-if = (os == "android" || appname == "b2g")
 [test_general.html]
 [test_menu.xul]
 [test_shadowroot.html]
+support-files = test_shadowroot_subframe.html
 [test_zoom.html]
 [test_zoom_text.html]
 [test_zoom_tree.xul]
--- a/accessible/tests/mochitest/hittest/test_shadowroot.html
+++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
@@ -3,70 +3,38 @@
 <head>
   <title>ShadowRoot hit tests</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 
-  <script type="application/javascript"
-          src="../common.js"></script>
-  <script type="application/javascript"
-          src="../layout.js"></script>
-
-  <script type="application/javascript">
-    function doTest() {
-      var componentAcc = getAccessible("component1");
-      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
-                       componentAcc.firstChild);
-
-      componentAcc = getAccessible("component2");
-      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
-                       componentAcc.firstChild);
-      SimpleTest.finish();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
-  </script>
 </head>
 <body>
 
   <a target="_blank"
      title="Test getChildAtPoint works for shadow DOM content"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027315">
     Mozilla Bug 1027315
   </a><br/>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
-  <div role="group" class="components" id="component1" style="display: inline-block;">
-  <!--
-    <div role="button" id="component-child"
-         style="width: 100px; height: 100px; background-color: pink;">
-    </div>
-  -->
-  </div>
-  <div role="group" class="components"  id="component2" style="display: inline-block;">
-  <!--
-    <button>Hello world</button>
-  -->
-  </div>
   <script>
-    // This routine adds the comment children of each 'component' to its
-    // shadow root.
-    var components = document.querySelectorAll(".components");
-    for (var i = 0; i < components.length; i++) {
-      var component = components[i];
-      var shadow = component.createShadowRoot();
-      for (var child = component.firstChild; child; child = child.nextSibling) {
-        if (child.nodeType === 8)
-          // eslint-disable-next-line no-unsanitized/property
-          shadow.innerHTML = child.data;
-      }
-    }
+    SimpleTest.waitForExplicitFinish();
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
+      var iframe = document.createElement("iframe");
+      iframe.src = "test_shadowroot_subframe.html";
+      document.body.appendChild(iframe);
+    });
   </script>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_shadowroot_subframe.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>ShadowRoot hit tests</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="../common.js"></script>
+  <script type="application/javascript" src="../layout.js"></script>
+
+  <script type="application/javascript">
+    let SimpleTest = window.parent.SimpleTest;
+    let ok = window.parent.ok;
+    let is = window.parent.is;
+
+    function doTest() {
+      var componentAcc = getAccessible("component1");
+      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+                       componentAcc.firstChild);
+
+      componentAcc = getAccessible("component2");
+      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+                       componentAcc.firstChild);
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+<body>
+  <div role="group" class="components" id="component1" style="display: inline-block;">
+  <!--
+    <div role="button" id="component-child"
+         style="width: 100px; height: 100px; background-color: pink;">
+    </div>
+  -->
+  </div>
+  <div role="group" class="components"  id="component2" style="display: inline-block;">
+  <!--
+    <button>Hello world</button>
+  -->
+  </div>
+  <script>
+    // This routine adds the comment children of each 'component' to its
+    // shadow root.
+    var components = document.querySelectorAll(".components");
+    for (var i = 0; i < components.length; i++) {
+      var component = components[i];
+      var shadow = component.attachShadow({mode: "open"});
+      for (var child = component.firstChild; child; child = child.nextSibling) {
+        if (child.nodeType === 8)
+          // eslint-disable-next-line no-unsanitized/property
+          shadow.innerHTML = child.data;
+      }
+    }
+  </script>
+</body>
+</html>
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -10,16 +10,17 @@ support-files =
 [test_bug883708.xhtml]
 [test_bug884251.xhtml]
 [test_bug895082.html]
 [test_bug1040735.html]
 [test_bug1100602.html]
 [test_bug1175913.html]
 [test_bug1189277.html]
 [test_bug1276857.html]
+support-files = test_bug1276857_subframe.html
 [test_canvas.html]
 [test_colorpicker.xul]
 [test_contextmenu.xul]
 [test_cssoverflow.html]
 [test_deck.xul]
 [test_doc.html]
 [test_gencontent.html]
 [test_general.html]
--- a/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -13,80 +13,89 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
     function runTest() {
+      let iframe = document.getElementById("iframe");
+
       // children change will recreate the table
       this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, getNode("c1"))
+        new invokerChecker(EVENT_REORDER, () => {
+          let doc = getNode("iframe").contentDocument;
+          return doc && doc.getElementById("c1");
+        })
       ];
 
       this.invoke = function runTest_invoke() {
         var tree = {
           SECTION: [ // c1
             { TEXT_LEAF: [] }, // Some text
             { TEXT_CONTAINER: [
               { TEXT_LEAF: [] } // something with ..
             ] },
             { TEXT_LEAF: [] } // More text
           ]
         };
-        testAccessibleTree("c1", tree);
+        testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree);
 
-        getNode("c1_t").querySelector("span").remove();
+        iframe.contentDocument.getElementById("c1_t").querySelector("span").remove();
       };
 
       this.finalCheck = function runTest_finalCheck() {
         var tree = {
           SECTION: [ // c1
             { TEXT_LEAF: [] }, // Some text
             { TEXT_LEAF: [] } // More text
           ]
         };
-        testAccessibleTree("c1", tree);
+        testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree);
       };
 
       this.getID = function runTest_getID() {
         return "child DOM node is removed before the layout notifies the a11y about parent removal/show";
       };
     }
 
     function runShadowTest() {
       // children change will recreate the table
       this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, "c2")
+        new invokerChecker(EVENT_REORDER, () => {
+          let doc = getNode("iframe").contentDocument;
+          return doc && doc.getElementById("c2");
+        })
       ];
 
       this.invoke = function runShadowTest_invoke() {
         var tree = {
           SECTION: [ // c2
             { TEXT_LEAF: [] }, // Some text
             { TEXT_CONTAINER: [
               { TEXT_LEAF: [] } // something with ..
             ] },
             { TEXT_LEAF: [] } // More text
           ]
         };
-        testAccessibleTree("c2", tree);
+        testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree);
 
-        gShadowRoot.firstElementChild.querySelector("span").remove();
+        var shadowRoot = iframe.contentDocument.getElementById("c2_c").shadowRoot;
+        shadowRoot.firstElementChild.querySelector("span").remove();
       };
 
       this.finalCheck = function runShadowTest_finalCheck() {
         var tree = {
           SECTION: [ // c2
             { TEXT_LEAF: [] }, // Some text
             { TEXT_LEAF: [] } // More text
           ]
         };
-        testAccessibleTree("c2", tree);
+        testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree);
       };
 
       this.getID = function runShadowTest_getID() {
         return "child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM";
       };
     }
 
     // enableLogging("tree");
@@ -96,44 +105,27 @@
     function doTest() {
       gQueue = new eventQueue();
       gQueue.push(new runTest());
       gQueue.push(new runShadowTest());
       gQueue.invoke(); // will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
+      let iframe = document.createElement("iframe");
+      iframe.id = "iframe";
+      iframe.src = "test_bug1276857_subframe.html";
+      addA11yLoadEvent(doTest, iframe.contentWindow);
+      document.body.appendChild(iframe);
+    });
   </script>
 
 </head>
-
 <body>
-  <p id="display"></p>
-  <div id="content" style="display: none"></div>
-  <pre id="test">
-  </pre>
-
-  <div id="c1">
-    <div id="c1_t" style="display: table" role="presentation">
-    Some text
-    <span style="display: table-cell">something with accessibles goes here</span>
-    More text
-    </div>
-  </div>
-
-  <template id="tmpl">
-    <div style="display: table" role="presentation">
-    Some text
-    <span style="display: table-cell">something with accessibles goes here</span>
-    More text
-    </div>
-  </template>
-
-  <div id="c2"><div id="c2_c" role="presentation"></div></div>
-
-  <script>
-    var gShadowRoot = document.getElementById("c2_c").createShadowRoot();
-    var tmpl = document.getElementById("tmpl");
-    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
-  </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>DOM mutations test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="../role.js"></script>
+</head>
+<body>
+  <div id="c1">
+    <div id="c1_t" style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </div>
+
+  <template id="tmpl">
+    <div style="display: table" role="presentation">
+    Some text
+    <span style="display: table-cell">something with accessibles goes here</span>
+    More text
+    </div>
+  </template>
+
+  <div id="c2"><div id="c2_c" role="presentation"></div></div>
+
+  <script>
+    var gShadowRoot = document.getElementById("c2_c").attachShadow({mode: "open"});
+    var tmpl = document.getElementById("tmpl");
+    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+  </script>
+</body>
+</html>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -518,18 +518,16 @@ pref("general.warnOnAboutConfig",       
 #endif
 
 // This is the pref to control the location bar, change this to true to
 // force this - this makes the origin of popup windows more obvious to avoid
 // spoofing. We would rather not do it by default because it affects UE for web
 // applications, but without it there isn't a really good way to prevent chrome
 // spoofing, see bug 337344
 pref("dom.disable_window_open_feature.location",  true);
-// prevent JS from setting status messages
-pref("dom.disable_window_status_change",          true);
 // allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize",            false);
 // prevent JS from monkeying with window focus, etc
 pref("dom.disable_window_flip",                   true);
 
 // popups.policy 1=allow,2=reject
 pref("privacy.popups.policy",               1);
 pref("privacy.popups.usecustom",            true);
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.244
+Current extension version is: 2.0.250
 
-Taken from upstream commit: 5bf4fb97
+Taken from upstream commit: 6b2ed504
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -24,17 +24,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory();
 	else if(typeof define === 'function' && define.amd)
 		define("pdfjs-dist/build/pdf", [], factory);
 	else if(typeof exports === 'object')
 		exports["pdfjs-dist/build/pdf"] = factory();
 	else
 		root["pdfjs-dist/build/pdf"] = root.pdfjsDistBuildPdf = factory();
-})(this, function() {
+})(typeof self !== 'undefined' ? self : this, function() {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __w_pdfjs_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -1931,17 +1931,17 @@ function getDocument(src) {
     });
   }).catch(task._capability.reject);
   return task;
 }
 function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
   if (worker.destroyed) {
     return Promise.reject(new Error('Worker was destroyed'));
   }
-  let apiVersion = '2.0.244';
+  let apiVersion = '2.0.250';
   source.disableRange = (0, _dom_utils.getDefaultSetting)('disableRange');
   source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
   source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
@@ -3228,18 +3228,18 @@ var InternalRenderTask = function Intern
         }
       }
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.244';
-  exports.build = build = '5bf4fb97';
+  exports.version = version = '2.0.250';
+  exports.build = build = '6b2ed504';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
@@ -4616,18 +4616,18 @@ exports.SVGGraphics = SVGGraphics;
 
 /***/ }),
 /* 8 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.244';
-var pdfjsBuild = '5bf4fb97';
+var pdfjsVersion = '2.0.250';
+var pdfjsBuild = '6b2ed504';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(12);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(6);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(7);
 ;
@@ -7739,18 +7739,18 @@ var _svg = __w_pdfjs_require__(7);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 if (!_global_scope2.default.PDFJS) {
   _global_scope2.default.PDFJS = {};
 }
 var PDFJS = _global_scope2.default.PDFJS;
 {
-  PDFJS.version = '2.0.244';
-  PDFJS.build = '5bf4fb97';
+  PDFJS.version = '2.0.250';
+  PDFJS.build = '6b2ed504';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -24,17 +24,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory();
 	else if(typeof define === 'function' && define.amd)
 		define("pdfjs-dist/build/pdf.worker", [], factory);
 	else if(typeof exports === 'object')
 		exports["pdfjs-dist/build/pdf.worker"] = factory();
 	else
 		root["pdfjs-dist/build/pdf.worker"] = root.pdfjsDistBuildPdfWorker = factory();
-})(this, function() {
+})(typeof self !== 'undefined' ? self : this, function() {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __w_pdfjs_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -12225,18 +12225,19 @@ var JpxImage = function JpxImageClosure(
               siz.Csiz = componentsCount;
               var components = [];
               j = position + 38;
               for (var i = 0; i < componentsCount; i++) {
                 var component = {
                   precision: (data[j] & 0x7F) + 1,
                   isSigned: !!(data[j] & 0x80),
                   XRsiz: data[j + 1],
-                  YRsiz: data[j + 1]
+                  YRsiz: data[j + 2]
                 };
+                j += 3;
                 calculateComponentDimensions(component, siz);
                 components.push(component);
               }
               context.SIZ = siz;
               context.components = components;
               calculateTileGrids(context, components);
               context.QCC = [];
               context.COC = [];
@@ -20778,18 +20779,18 @@ exports.PostScriptCompiler = PostScriptC
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.244';
-var pdfjsBuild = '5bf4fb97';
+var pdfjsVersion = '2.0.250';
+var pdfjsBuild = '6b2ed504';
 var pdfjsCoreWorker = __w_pdfjs_require__(19);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 19 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -20974,17 +20975,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.244';
+    let workerVersion = '2.0.250';
     if (apiVersion !== null && apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _util.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
--- a/browser/themes/linux/preferences/preferences.css
+++ b/browser/themes/linux/preferences/preferences.css
@@ -17,26 +17,16 @@
 .prefpane {
   padding: 8px;
 }
 
 .prefwindow[type="child"] > .prefpane {
   padding: 0px;
 }
 
-.dialog-button-box {
-  padding-bottom: 8px;
-  padding-inline-start: 8px;
-  padding-inline-end: 8px;
-}
-
-.prefwindow[type="child"] .dialog-button-box {
-  padding: 0px;
-}
-
 /* General Pane */
 #useFirefoxSync,
 #getStarted {
   font-size: 90%;
 }
 
 #isNotDefaultLabel {
   font-weight: bold;
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -19,21 +19,16 @@
 
 .prefwindow[type="child"] {
   padding-top: 18px;
   padding-bottom: 15px;
   padding-inline-start: 18px;
   padding-inline-end: 20px;
 }
 
-.dialog-button-box {
-  margin: 0 12px 12px;
-  padding-top: 0 !important;
-}
-
 description {
   margin-bottom: 4px !important;
 }
 
 .prefpane {
   padding-top: 12px;
   padding-bottom: 12px;
   padding-inline-start: 0;
--- a/browser/themes/shared/incontentprefs/dialog.inc.css
+++ b/browser/themes/shared/incontentprefs/dialog.inc.css
@@ -63,8 +63,12 @@ groupbox {
 groupbox description {
   margin-right: 0;
   margin-left: 0;
 }
 
 menulist label {
   font-weight: unset;
 }
+
+.dialog-button-box {
+  padding: 0;
+}
--- a/browser/themes/windows/preferences/preferences.css
+++ b/browser/themes/windows/preferences/preferences.css
@@ -23,26 +23,16 @@
   padding-inline-start: 8px;
   padding-inline-end: 10px;
 }
 
 .prefwindow[type="child"] > .prefpane {
   padding: 0px;
 }
 
-.dialog-button-box {
-  padding-bottom: 10px;
-  padding-inline-start: 8px;
-  padding-inline-end: 10px;
-}
-
-.prefwindow[type="child"] .dialog-button-box {
-  padding: 0px;
-}
-
 /* General Pane */
 
 #useFirefoxSync,
 #getStarted {
   font-size: 90%;
 }
 
 #isNotDefaultLabel {
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,11 +1,11 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Taken from upstream commit: f98e56ff661460044382df55bd33cea601e093f0
+Taken from upstream commit: 0bcf3e687305960077e1255510e424d0437a3b69
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @15.6.2
 - react-dom @15.6.2
 - webpack @3.8.1
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -16420,21 +16420,21 @@ var _breakpointAtLocation2 = _interopReq
 var _visibleBreakpoints = __webpack_require__(1427);
 
 var _visibleBreakpoints2 = _interopRequireDefault(_visibleBreakpoints);
 
 var _isSelectedFrameVisible = __webpack_require__(1505);
 
 var _isSelectedFrameVisible2 = _interopRequireDefault(_isSelectedFrameVisible);
 
-var _getCallStackFrames = __webpack_require__(1780);
+var _getCallStackFrames = __webpack_require__(1779);
 
 var _getCallStackFrames2 = _interopRequireDefault(_getCallStackFrames);
 
-var _visibleSelectedFrame = __webpack_require__(1781);
+var _visibleSelectedFrame = __webpack_require__(1780);
 
 var _visibleSelectedFrame2 = _interopRequireDefault(_visibleSelectedFrame);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 /**
@@ -16936,17 +16936,17 @@ var loadSourceText = _interopRequireWild
 var _debuggee = __webpack_require__(1533);
 
 var debuggee = _interopRequireWildcard(_debuggee);
 
 var _toolbox = __webpack_require__(1534);
 
 var toolbox = _interopRequireWildcard(_toolbox);
 
-var _preview = __webpack_require__(1787);
+var _preview = __webpack_require__(1786);
 
 var preview = _interopRequireWildcard(_preview);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 exports.default = _extends({}, navigation, breakpoints, expressions, eventListeners, sources, pause, ui, fileSearch, ast, coverage, projectTextSearch, quickOpen, sourceTree, loadSourceText, debuggee, toolbox, preview);
 
 /***/ }),
@@ -16967,17 +16967,17 @@ module.exports = feature;
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.isLoading = exports.isLoaded = exports.getMode = exports.getSourceLineCount = exports.getSourcePath = exports.getFileURL = exports.getFilenameFromURL = exports.getFilename = exports.getRawSourceURL = exports.getPrettySourceURL = exports.shouldPrettyPrint = exports.isThirdParty = exports.isPretty = exports.isJavaScript = exports.isMinified = undefined;
 
-var _isMinified = __webpack_require__(1779);
+var _isMinified = __webpack_require__(1778);
 
 Object.defineProperty(exports, "isMinified", {
   enumerable: true,
   get: function () {
     return _isMinified.isMinified;
   }
 });
 
@@ -17239,16 +17239,17 @@ function getMode(source, sourceMetaData)
   }
 
   return { name: "text" };
 }
 
 function isLoaded(source) {
   return source.get("loadedState") === "loaded";
 }
+
 function isLoading(source) {
   return source.get("loadedState") === "loading";
 }
 
 exports.isJavaScript = isJavaScript;
 exports.isPretty = isPretty;
 exports.isThirdParty = isThirdParty;
 exports.shouldPrettyPrint = shouldPrettyPrint;
@@ -17296,17 +17297,17 @@ var _extends = Object.assign || function
 var _devtoolsConfig = __webpack_require__(1355);
 
 var _sourceDocuments = __webpack_require__(1436);
 
 var sourceDocumentUtils = _interopRequireWildcard(_sourceDocuments);
 
 var _source = __webpack_require__(1356);
 
-var _getTokenLocation = __webpack_require__(1784);
+var _getTokenLocation = __webpack_require__(1783);
 
 var _sourceSearch = __webpack_require__(1526);
 
 var sourceSearchUtils = _interopRequireWildcard(_sourceSearch);
 
 var _wasm = __webpack_require__(1401);
 
 var _ui = __webpack_require__(1439);
@@ -17373,31 +17374,35 @@ function createEditor() {
       Esc: false,
       "Cmd-F": false,
       "Cmd-G": false
     }
   });
 }
 
 function toEditorLine(sourceId, lineOrOffset) {
-  return (0, _wasm.isWasm)(sourceId) ? (0, _wasm.wasmOffsetToLine)(sourceId, lineOrOffset) : lineOrOffset - 1;
-}
-
-function toEditorPosition(sourceId, location) {
-  return {
-    line: toEditorLine(sourceId, location.line),
-    column: (0, _wasm.isWasm)(sourceId) ? 0 : location.column
+  if ((0, _wasm.isWasm)(sourceId)) {
+    return (0, _wasm.wasmOffsetToLine)(sourceId, lineOrOffset);
+  }
+
+  return lineOrOffset ? lineOrOffset - 1 : 1;
+}
+
+function toEditorPosition(location) {
+  return {
+    line: toEditorLine(location.sourceId, location.line),
+    column: (0, _wasm.isWasm)(location.sourceId) || !location.column ? 0 : location.column
   };
 }
 
 function toEditorRange(sourceId, location) {
   const { start, end } = location;
   return {
-    start: toEditorPosition(sourceId, start),
-    end: toEditorPosition(sourceId, end)
+    start: toEditorPosition(_extends({}, start, { sourceId })),
+    end: toEditorPosition(_extends({}, end, { sourceId }))
   };
 }
 
 function toSourceLine(sourceId, line) {
   return (0, _wasm.isWasm)(sourceId) ? (0, _wasm.lineToWasmOffset)(sourceId, line) : line + 1;
 }
 
 function scrollToColumn(codeMirror, line, column) {
@@ -18337,20 +18342,16 @@ function update(state = initialState(), 
       }
 
     case "ADD_SOURCES":
       {
         return action.sources.reduce((newState, source) => updateSource(newState, source), state);
       }
 
     case "SELECT_SOURCE":
-      if (action.status != "start") {
-        return state;
-      }
-
       location = _extends({}, action.location, {
         url: action.source.url
       });
 
       _prefs.prefs.pendingSelectedLocation = location;
       return state.set("selectedLocation", _extends({
         sourceId: action.source.id
       }, action.location)).set("pendingSelectedLocation", location);
@@ -18549,17 +18550,17 @@ function getSource(state, id) {
   return getSourceInSources(getSources(state), id);
 }
 
 function getSourceByURL(state, url) {
   return getSourceByUrlInSources(state.sources.sources, url);
 }
 
 function getGeneratedSource(state, source) {
-  if (!(0, _devtoolsSourceMap.isOriginalId)(source.id)) {
+  if (!source || !(0, _devtoolsSourceMap.isOriginalId)(source.id)) {
     return null;
   }
   return getSource(state, (0, _devtoolsSourceMap.originalToGeneratedId)(source.id));
 }
 
 function getPendingSelectedLocation(state) {
   return state.sources.pendingSelectedLocation;
 }
@@ -18905,17 +18906,17 @@ var _breakpoints = __webpack_require__(1
 var _ast = __webpack_require__(1399);
 
 var _projectTextSearch = __webpack_require__(1433);
 
 var _ui = __webpack_require__(1385);
 
 var _source2 = __webpack_require__(1356);
 
-var _location = __webpack_require__(1783);
+var _location = __webpack_require__(1782);
 
 var _createPrettySource = __webpack_require__(1523);
 
 var _loadSourceText = __webpack_require__(1435);
 
 var _prefs = __webpack_require__(226);
 
 var _editor = __webpack_require__(1358);
@@ -19083,17 +19084,17 @@ function selectSource(sourceId, tabIndex
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 function selectLocation(location, tabIndex = "") {
-  return ({ dispatch, getState, client }) => {
+  return async ({ dispatch, getState, client }) => {
     if (!client) {
       // No connection, do nothing. This happens when the debugger is
       // shut down too fast and it tries to display a default source.
       return;
     }
 
     const source = (0, _selectors.getSource)(getState(), location.sourceId);
     if (!source) {
@@ -19103,31 +19104,30 @@ function selectLocation(location, tabInd
 
     const activeSearch = (0, _selectors.getActiveSearch)(getState());
     if (activeSearch !== "file") {
       dispatch((0, _ui.closeActiveSearch)());
     }
 
     dispatch(addTab(source.toJS(), 0));
 
-    return dispatch({
+    dispatch({
       type: "SELECT_SOURCE",
       source: source.toJS(),
       tabIndex,
-      location,
-      [_promise.PROMISE]: (async () => {
-        await dispatch((0, _loadSourceText.loadSourceText)(source));
-        dispatch((0, _ast.setOutOfScopeLocations)());
-        const src = (0, _selectors.getSource)(getState(), location.sourceId);
-        const { autoPrettyPrint } = _prefs.prefs;
-        if (autoPrettyPrint && (0, _source2.shouldPrettyPrint)(src) && (0, _source2.isMinified)(src)) {
-          await dispatch(togglePrettyPrint(src.get("id")));
-        }
-      })()
-    });
+      location
+    });
+
+    await dispatch((0, _loadSourceText.loadSourceText)(source));
+    dispatch((0, _ast.setOutOfScopeLocations)());
+    const src = (0, _selectors.getSource)(getState(), location.sourceId);
+    const { autoPrettyPrint } = _prefs.prefs;
+    if (autoPrettyPrint && (0, _source2.shouldPrettyPrint)(src) && (0, _source2.isMinified)(src)) {
+      await dispatch(togglePrettyPrint(src.get("id")));
+    }
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 function jumpToMappedLocation(sourceLocation) {
@@ -20341,17 +20341,17 @@ function update(state = initialState(), 
           location,
           result,
           tokenPos,
           cursorPos,
           extra
         });
       }
 
-    case "RESUMED":
+    case "RESUME":
       {
         return state.set("outOfScopeLocations", null);
       }
 
     case "NAVIGATE":
       {
         return initialState();
       }
@@ -21878,17 +21878,17 @@ Object.defineProperty(exports, "__esModu
 });
 exports.setSourceMetaData = setSourceMetaData;
 exports.setSymbols = setSymbols;
 exports.setEmptyLines = setEmptyLines;
 exports.setOutOfScopeLocations = setOutOfScopeLocations;
 
 var _selectors = __webpack_require__(1352);
 
-var _setInScopeLines = __webpack_require__(1782);
+var _setInScopeLines = __webpack_require__(1781);
 
 var _parser = __webpack_require__(1365);
 
 function setSourceMetaData(sourceId) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
     if (!sourceRecord) {
       return;
@@ -22699,17 +22699,17 @@ var _react2 = _interopRequireDefault(_re
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
-var _CommandBarButton = __webpack_require__(1765);
+var _CommandBarButton = __webpack_require__(1764);
 
 var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);
 
 __webpack_require__(1321);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 class PaneToggleButton extends _react.Component {
@@ -23100,17 +23100,17 @@ module.exports = Grip;
 "use strict";
 
 
 /* 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/. */
 
 const React = __webpack_require__(0);
-const InlineSVG = __webpack_require__(1790);
+const InlineSVG = __webpack_require__(1789);
 
 const svg = {
   "arrow": __webpack_require__(1152),
   "open-inspector": __webpack_require__(1153)
 };
 
 Svg.propTypes = {
   className: React.PropTypes.string
@@ -23168,17 +23168,17 @@ module.exports = function defer() {
 
 "use strict";
 
 
 /* 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/. */
 
-const { Menu, MenuItem } = __webpack_require__(1768);
+const { Menu, MenuItem } = __webpack_require__(1767);
 
 function inToolbox() {
   return window.parent.document.documentURI == "about:devtools-toolbox";
 }
 
 if (!inToolbox()) {
   __webpack_require__(1301);
 }
@@ -23696,17 +23696,16 @@ function update(state = initialState(), 
 
   return state;
 }
 
 function addBreakpoint(state, action) {
   if (action.status !== "done") {
     return state;
   }
-
   // when the action completes, we can commit the breakpoint
   const { value: { breakpoint } } = action;
   const locationId = (0, _breakpoint.makePendingLocationId)(breakpoint.location);
   const pendingBreakpoint = (0, _breakpoint.createPendingBreakpoint)(breakpoint);
 
   return state.setIn(["pendingBreakpoints", locationId], pendingBreakpoint);
 }
 
@@ -24216,17 +24215,18 @@ function InitialState() {
     results: I.List(),
     status: statusType.initial
   })();
 }
 
 function update(state = InitialState(), action) {
   switch (action.type) {
     case "ADD_QUERY":
-      return state.update("query", value => action.query);
+      const actionCopy = action;
+      return state.update("query", value => actionCopy.query);
 
     case "CLEAR_QUERY":
       return state.remove("query");
 
     case "ADD_SEARCH_RESULT":
       const results = state.get("results");
       return state.merge({ results: results.push(action.result) });
 
@@ -24909,32 +24909,36 @@ function loadSourceText(source) {
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.showLoading = exports.showSourceText = exports.updateDocument = exports.updateLineNumberFormat = exports.resetLineNumberFormat = exports.clearDocuments = exports.removeDocument = exports.setDocument = exports.getDocument = undefined;
+exports.showLoading = exports.showSourceText = exports.updateDocument = exports.updateLineNumberFormat = exports.resetLineNumberFormat = exports.clearDocuments = exports.removeDocument = exports.hasDocument = exports.setDocument = exports.getDocument = undefined;
 
 var _source = __webpack_require__(1356);
 
 var _wasm = __webpack_require__(1401);
 
 var _ui = __webpack_require__(1439);
 
 let sourceDocs = {}; /* 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/>. */
 
 function getDocument(key) {
   return sourceDocs[key];
 }
 
+function hasDocument(key) {
+  return !!getDocument(key);
+}
+
 function setDocument(key, doc) {
   sourceDocs[key] = doc;
 }
 
 function removeDocument(key) {
   delete sourceDocs[key];
 }
 
@@ -24955,29 +24959,30 @@ function updateLineNumberFormat(editor, 
   }
   const cm = editor.codeMirror;
   const lineNumberFormatter = (0, _wasm.getWasmLineNumberFormatter)(sourceId);
   cm.setOption("lineNumberFormatter", lineNumberFormatter);
   (0, _ui.resizeBreakpointGutter)(cm);
   (0, _ui.resizeToggleButton)(cm);
 }
 
-function updateDocument(editor, sourceId) {
-  if (!sourceId) {
+function updateDocument(editor, source) {
+  if (!source) {
     return;
   }
 
+  const sourceId = source.get("id");
   const doc = getDocument(sourceId) || editor.createDocument();
   editor.replaceDocument(doc);
 
   updateLineNumberFormat(editor, sourceId);
 }
 
 function showLoading(editor) {
-  if (getDocument("loading")) {
+  if (hasDocument("loading")) {
     return;
   }
 
   const doc = editor.createDocument();
   setDocument("loading", doc);
   editor.replaceDocument(doc);
   editor.setText(L10N.getStr("loadingText"));
   editor.setMode({ name: "text" });
@@ -24999,40 +25004,41 @@ function setEditorText(editor, source) {
  * Handle getting the source document or creating a new
  * document with the correct mode and text.
  */
 function showSourceText(editor, source, sourceMetaData) {
   if (!source) {
     return;
   }
 
-  let doc = getDocument(source.id);
-  if (editor.codeMirror.doc === doc) {
-    editor.setMode((0, _source.getMode)(source, sourceMetaData));
-    return;
-  }
-
-  if (doc) {
+  if (hasDocument(source.id)) {
+    const doc = getDocument(source.id);
+    if (editor.codeMirror.doc === doc) {
+      editor.setMode((0, _source.getMode)(source, sourceMetaData));
+      return;
+    }
+
     editor.replaceDocument(doc);
     updateLineNumberFormat(editor, source.id);
     editor.setMode((0, _source.getMode)(source, sourceMetaData));
     return doc;
   }
 
-  doc = editor.createDocument();
+  const doc = editor.createDocument();
   setDocument(source.id, doc);
   editor.replaceDocument(doc);
 
   setEditorText(editor, source);
   editor.setMode((0, _source.getMode)(source, sourceMetaData));
   updateLineNumberFormat(editor, source.id);
 }
 
 exports.getDocument = getDocument;
 exports.setDocument = setDocument;
+exports.hasDocument = hasDocument;
 exports.removeDocument = removeDocument;
 exports.clearDocuments = clearDocuments;
 exports.resetLineNumberFormat = resetLineNumberFormat;
 exports.updateLineNumberFormat = updateLineNumberFormat;
 exports.updateDocument = updateDocument;
 exports.showSourceText = showSourceText;
 exports.showLoading = showLoading;
 
@@ -27020,17 +27026,17 @@ var _reactDom2 = _interopRequireDefault(
 var _devtoolsLaunchpad = __webpack_require__(1362);
 
 var _devtoolsConfig = __webpack_require__(1355);
 
 var _client = __webpack_require__(1499);
 
 var _bootstrap = __webpack_require__(1430);
 
-var _sourceQueue = __webpack_require__(1762);
+var _sourceQueue = __webpack_require__(1795);
 
 var _sourceQueue2 = _interopRequireDefault(_sourceQueue);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 if (false) {
   window.Perf = require("react-addons-perf");
 } /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -29296,17 +29302,17 @@ module.exports = connect(mapStateToProps
 
 "use strict";
 
 
 /* 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/. */
 
-const { score } = __webpack_require__(1775);
+const { score } = __webpack_require__(1774);
 
 function getTabs(state) {
   let tabs = state.tabs.get("tabs");
   let filterString = getFilterString(state);
 
   if (filterString === "") {
     return tabs;
   }
@@ -29701,17 +29707,17 @@ module.exports = Sidebar;
 /***/ }),
 /* 1494 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 const React = __webpack_require__(0);
-const { default: InlineSVG } = __webpack_require__(1764);
+const { default: InlineSVG } = __webpack_require__(1763);
 const { isDevelopment } = __webpack_require__(1355);
 
 const svg = {
   rocket: __webpack_require__(1126)
 };
 
 function Svg({ name, className, onClick, "aria-label": ariaLabel }) {
   className = `${name} ${className || ""}`;
@@ -30687,33 +30693,34 @@ function isSelectedFrameVisible(state) {
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.clientEvents = exports.setupEvents = undefined;
 
 var _create = __webpack_require__(1428);
 
-var _sourceQueue = __webpack_require__(1762);
+var _sourceQueue = __webpack_require__(1795);
 
 var _sourceQueue2 = _interopRequireDefault(_sourceQueue);
 
 var _devtoolsConfig = __webpack_require__(1355);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/>. */
 
 const CALL_STACK_PAGE_SIZE = 1000;
 
 let threadClient;
 let actions;
 let supportsWasm;
+let isInterrupted;
 
 function setupEvents(dependencies) {
   threadClient = dependencies.threadClient;
   actions = dependencies.actions;
   supportsWasm = dependencies.supportsWasm;
   _sourceQueue2.default.initialize({ actions, supportsWasm, createSource: _create.createSource });
 
   if (threadClient) {
@@ -30724,30 +30731,39 @@ function setupEvents(dependencies) {
 }
 
 async function paused(_, packet) {
   // If paused by an explicit interrupt, which are generated by the
   // slow script dialog and internal events such as setting
   // breakpoints, ignore the event.
   const { why } = packet;
   if (why.type === "interrupted" && !packet.why.onNext) {
+    isInterrupted = true;
     return;
   }
 
   // Eagerly fetch the frames
   const response = await threadClient.getFrames(0, CALL_STACK_PAGE_SIZE);
 
   if (why.type != "alreadyPaused") {
     const pause = (0, _create.createPause)(packet, response);
     _sourceQueue2.default.flush();
     actions.paused(pause);
   }
 }
 
 function resumed(_, packet) {
+  // NOTE: the client suppresses resumed events while interrupted
+  // to prevent unintentional behavior.
+  // see [client docs](../README.md#interrupted) for more information.
+  if (isInterrupted) {
+    isInterrupted = false;
+    return;
+  }
+
   actions.resumed(packet);
 }
 
 function newSource(_, { source }) {
   _sourceQueue2.default.queue(source);
 
   if ((0, _devtoolsConfig.isEnabled)("eventListeners")) {
     actions.fetchEventListeners();
@@ -32336,17 +32352,17 @@ Object.defineProperty(exports, "__esModu
 });
 exports.willNavigate = willNavigate;
 exports.navigate = navigate;
 exports.connect = connect;
 exports.navigated = navigated;
 
 var _editor = __webpack_require__(1358);
 
-var _sourceQueue = __webpack_require__(1762);
+var _sourceQueue = __webpack_require__(1795);
 
 var _sourceQueue2 = _interopRequireDefault(_sourceQueue);
 
 var _sources = __webpack_require__(1369);
 
 var _utils = __webpack_require__(1366);
 
 var _sources2 = __webpack_require__(1373);
@@ -32511,17 +32527,16 @@ function traverseResults(rev, editor) {
     if (query === "") {
       dispatch((0, _ui.setActiveSearch)("file"));
     }
 
     if (modifiers) {
       const matchedLocations = matches || [];
       const { ch, line } = rev ? (0, _editor.findPrev)(ctx, query, true, modifiers.toJS()) : (0, _editor.findNext)(ctx, query, true, modifiers.toJS());
 
-      console.log(line);
       dispatch(updateSearchResults(ch, line, matchedLocations));
     }
   };
 }
 
 function closeFileSearch(editor) {
   return ({ getState, dispatch }) => {
     const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
@@ -33473,17 +33488,17 @@ TextSearch.contextTypes = {
 
 /***/ }),
 /* 1540 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-var _svgInlineReact = __webpack_require__(1764);
+var _svgInlineReact = __webpack_require__(1763);
 
 var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 const React = __webpack_require__(0);
 
 const { isDevelopment } = __webpack_require__(1355);
@@ -33498,17 +33513,17 @@ const svg = {
   "column-breakpoint": __webpack_require__(998),
   "case-match": __webpack_require__(351),
   close: __webpack_require__(352),
   choo: __webpack_require__(1290),
   dojo: __webpack_require__(806),
   domain: __webpack_require__(353),
   file: __webpack_require__(354),
   folder: __webpack_require__(355),
-  function: __webpack_require__(1788),
+  function: __webpack_require__(1787),
   globe: __webpack_require__(356),
   jquery: __webpack_require__(999),
   underscore: __webpack_require__(1117),
   lodash: __webpack_require__(1118),
   ember: __webpack_require__(1119),
   vuejs: __webpack_require__(1174),
   "magnifying-glass": __webpack_require__(357),
   "arrow-up": __webpack_require__(919),
@@ -35227,16 +35242,20 @@ var _HitMarker2 = _interopRequireDefault
 var _CallSites = __webpack_require__(1591);
 
 var _CallSites2 = _interopRequireDefault(_CallSites);
 
 var _DebugLine = __webpack_require__(1593);
 
 var _DebugLine2 = _interopRequireDefault(_DebugLine);
 
+var _HighlightLine = __webpack_require__(1796);
+
+var _HighlightLine2 = _interopRequireDefault(_HighlightLine);
+
 var _EmptyLines = __webpack_require__(1594);
 
 var _EmptyLines2 = _interopRequireDefault(_EmptyLines);
 
 var _GutterMenu = __webpack_require__(1595);
 
 var _GutterMenu2 = _interopRequireDefault(_GutterMenu);
 
@@ -35253,24 +35272,28 @@ var _editor = __webpack_require__(1358);
 var _ui = __webpack_require__(1439);
 
 __webpack_require__(1332);
 
 __webpack_require__(1333);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-// Redux actions
+/* 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/>. */
+
 const cssVars = {
   searchbarHeight: "var(--editor-searchbar-height)",
   secondSearchbarHeight: "var(--editor-second-searchbar-height)",
   footerHeight: "var(--editor-footer-height)"
-}; /* 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/>. */
+};
+
+// Redux actions
+
 
 class Editor extends _react.PureComponent {
 
   constructor() {
     super();
 
     this.onToggleBreakpoint = (key, e) => {
       e.preventDefault();
@@ -35366,34 +35389,37 @@ class Editor extends _react.PureComponen
 
       return openConditionalPanel(line);
     };
 
     this.closeConditionalPanel = () => {
       return this.props.closeConditionalPanel();
     };
 
-    this.pendingJumpLocation = null;
-    this.lastJumpLine = null;
-
     this.state = {
       highlightedLineRange: null,
       editor: null
     };
   }
 
   componentWillReceiveProps(nextProps) {
     if (!this.state.editor) {
       return;
     }
 
     (0, _editor.resizeBreakpointGutter)(this.state.editor.codeMirror);
     (0, _ui.resizeToggleButton)(this.state.editor.codeMirror);
   }
 
+  componentWillUpdate(nextProps) {
+    this.setText(nextProps);
+    this.setSize(nextProps);
+    this.scrollToLocation(nextProps);
+  }
+
   setupEditor() {
     const editor = (0, _editor.createEditor)();
 
     // disables the default search shortcuts
 
     editor._initShortcuts = () => {};
 
     const node = _reactDom2.default.findDOMNode(this);
@@ -35437,80 +35463,45 @@ class Editor extends _react.PureComponen
 
     this.setState({ editor });
     return editor;
   }
 
   componentDidMount() {
     const editor = this.setupEditor();
 
-    const { selectedSource, selectedLocation } = this.props;
+    const { selectedSource } = this.props;
     const { shortcuts } = this.context;
 
     const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
     const searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");
 
     shortcuts.on(L10N.getStr("toggleBreakpoint.key"), this.onToggleBreakpoint);
     shortcuts.on(L10N.getStr("toggleCondPanel.key"), this.toggleConditionalPanel);
     shortcuts.on("Esc", this.onEscape);
     shortcuts.on(searchAgainPrevKey, this.onSearchAgain);
     shortcuts.on(searchAgainKey, this.onSearchAgain);
 
-    if (selectedLocation && !!selectedLocation.line) {
-      this.pendingJumpLocation = selectedLocation;
-    }
-
-    const sourceId = selectedSource ? selectedSource.get("id") : undefined;
-    (0, _editor.updateDocument)(editor, sourceId);
+    (0, _editor.updateDocument)(editor, selectedSource);
   }
 
   componentWillUnmount() {
     this.state.editor.destroy();
     this.setState({ editor: null });
 
     const searchAgainKey = L10N.getStr("sourceSearch.search.again.key2");
     const searchAgainPrevKey = L10N.getStr("sourceSearch.search.againPrev.key2");
     const shortcuts = this.context.shortcuts;
     shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
     shortcuts.off(L10N.getStr("toggleCondPanel.key"));
     shortcuts.off(searchAgainPrevKey);
     shortcuts.off(searchAgainKey);
   }
 
-  componentWillUpdate(nextProps) {
-    this.setText(nextProps);
-    this.setSize(nextProps);
-  }
-
   componentDidUpdate(prevProps, prevState) {
-    // This is in `componentDidUpdate` so helper functions can expect
-    // `this.props` to be the current props. This lifecycle method is
-    // responsible for updating the editor annotations.
-    const { selectedLocation, selectedSource } = this.props;
-
-    // If the location is different and a new line is requested,
-    // update the pending jump line. Note that if jumping to a line in
-    // a source where the text hasn't been loaded yet, we will set the
-    // line here but not jump until rendering the actual source.
-
-    if (prevProps.selectedLocation !== selectedLocation) {
-      if (selectedLocation && selectedLocation.line != undefined) {
-        this.pendingJumpLocation = selectedLocation;
-      } else {
-        this.pendingJumpLocation = null;
-      }
-    }
-
-    // Only update and jump around in real source texts. This will
-    // keep the jump state around until the real source text is
-    // loaded.
-    if (selectedSource && (0, _source.isLoaded)(selectedSource)) {
-      this.highlightLine();
-    }
-
     // NOTE: when devtools are opened, the editor is not set when
     // the source loads so we need to wait until the editor is
     // set to update the text and size.
     if (!prevState.editor && this.state.editor) {
       this.setText(this.props);
       this.setSize(this.props);
     }
   }
@@ -35564,87 +35555,77 @@ class Editor extends _react.PureComponen
     const { selectedLocation, jumpToMappedLocation } = this.props;
 
     if (e.metaKey && e.altKey) {
       const sourceLocation = (0, _editor.getSourceLocationFromMouseEvent)(this.state.editor, selectedLocation, e);
       jumpToMappedLocation(sourceLocation);
     }
   }
 
-  // If the location has changed and a specific line is requested,
-  // move to that line and flash it.
-  highlightLine() {
-    const { selectedLocation, selectedFrame } = this.props;
-    if (!selectedLocation) {
-      return;
-    }
-
-    // Make sure to clean up after ourselves. Not only does this
-    // cancel any existing animation, but it avoids it from
-    // happening ever again (in case CodeMirror re-applies the
-    // class, etc).
-    if (this.lastJumpLine !== null) {
-      (0, _editor.clearLineClass)(this.state.editor.codeMirror, "highlight-line");
-    }
-
-    let line = null;
-    if (selectedLocation.line >= 0) {
-      line = this.scrollToPosition();
-    }
-
-    // We only want to do the flashing animation if it's not a debug
-    // line, which has it's own styling.
-    // Also, if it the first time the debugger is being loaded, we don't want
-    // to flash the previously saved selected line.
-    if (line !== null && this.lastJumpLine !== null && (!selectedFrame || selectedFrame.location.line !== line)) {
-      this.state.editor.codeMirror.addLineClass(line, "line", "highlight-line");
-    }
-
-    this.lastJumpLine = line;
-    this.pendingJumpLocation = null;
-  }
-
-  scrollToPosition() {
-    const { sourceId, line, column } = this.props.selectedLocation;
-    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
-    (0, _editor.scrollToColumn)(this.state.editor.codeMirror, editorLine, column);
-    return editorLine;
+  shouldScrollToLocation(nextProps) {
+    const { selectedLocation, selectedSource } = this.props;
+    const { editor } = this.state;
+
+    if (!nextProps.selectedSource || !editor || !nextProps.selectedLocation) {
+      return false;
+    }
+
+    if (!(0, _source.isLoaded)(nextProps.selectedSource)) {
+      return false;
+    }
+
+    if (!nextProps.selectedLocation.line) {
+      return false;
+    }
+
+    const isFirstLoad = (!selectedSource || !(0, _source.isLoaded)(selectedSource)) && (0, _source.isLoaded)(nextProps.selectedSource);
+
+    const locationChanged = selectedLocation !== nextProps.selectedLocation;
+    return isFirstLoad || locationChanged;
+  }
+
+  scrollToLocation(nextProps) {
+    const { editor } = this.state;
+
+    if (this.shouldScrollToLocation(nextProps)) {
+      const { line, column } = (0, _editor.toEditorPosition)(nextProps.selectedLocation);
+      (0, _editor.scrollToColumn)(editor.codeMirror, line, column);
+    }
   }
 
   setSize(nextProps) {
     if (!this.state.editor) {
       return;
     }
 
     if (nextProps.startPanelSize !== this.props.startPanelSize || nextProps.endPanelSize !== this.props.endPanelSize) {
       this.state.editor.codeMirror.setSize();
     }
   }
 
   setText(props) {
     const { selectedSource, sourceMetaData } = props;
+
     if (!this.state.editor) {
       return;
     }
 
     if (!selectedSource) {
       return this.showMessage("");
     }
 
     if (!(0, _source.isLoaded)(selectedSource)) {
       return (0, _editor.showLoading)(this.state.editor);
     }
 
     if (selectedSource.get("error")) {
       return this.showMessage(selectedSource.get("error"));
     }
 
-    if (selectedSource) {
-      return (0, _editor.showSourceText)(this.state.editor, selectedSource.toJS(), sourceMetaData);
-    }
+    return (0, _editor.showSourceText)(this.state.editor, selectedSource.toJS(), sourceMetaData);
   }
 
   showMessage(msg) {
     const { editor } = this.state;
     if (!editor) {
       return;
     }
 
@@ -35680,27 +35661,28 @@ class Editor extends _react.PureComponen
     return hitCount.filter(marker => marker.get("count") > 0).map(marker => _react2.default.createElement(_HitMarker2.default, {
       key: marker.get("line"),
       hitData: marker.toJS(),
       editor: this.state.editor.codeMirror
     }));
   }
 
   renderItems() {
-    const { selectedSource, horizontal } = this.props;
+    const { horizontal, selectedSource } = this.props;
     const { editor } = this.state;
 
-    if (!editor || !selectedSource || !(0, _source.isLoaded)(selectedSource)) {
+    if (!editor || !selectedSource) {
       return null;
     }
 
     return _react2.default.createElement(
       "div",
       null,
-      _react2.default.createElement(_DebugLine2.default, { editor: editor }),
+      _react2.default.createElement(_DebugLine2.default, null),
+      _react2.default.createElement(_HighlightLine2.default, null),
       _react2.default.createElement(_EmptyLines2.default, { editor: editor }),
       _react2.default.createElement(_Breakpoints2.default, { editor: editor }),
       _react2.default.createElement(_Preview2.default, { editor: editor }),
       ";",
       _react2.default.createElement(_Footer2.default, { editor: editor, horizontal: horizontal }),
       _react2.default.createElement(_HighlightLines2.default, { editor: editor }),
       _react2.default.createElement(_EditorMenu2.default, { editor: editor }),
       _react2.default.createElement(_GutterMenu2.default, { editor: editor }),
@@ -35747,17 +35729,16 @@ Editor.contextTypes = {
 const mapStateToProps = state => {
   const selectedSource = (0, _selectors.getSelectedSource)(state);
   const sourceId = selectedSource ? selectedSource.get("id") : "";
   return {
     selectedLocation: (0, _selectors.getSelectedLocation)(state),
     selectedSource,
     searchOn: (0, _selectors.getActiveSearch)(state) === "file",
     hitCount: (0, _selectors.getHitCountForSource)(state, sourceId),
-    selectedFrame: (0, _selectors.getSelectedFrame)(state),
     coverageOn: (0, _selectors.getCoverageEnabled)(state),
     conditionalPanelLine: (0, _selectors.getConditionalPanelLine)(state),
     sourceMetaData: (0, _selectors.getSourceMetaData)(state, sourceId)
   };
 };
 
 exports.default = (0, _reactRedux.connect)(mapStateToProps, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Editor);
 
@@ -35967,17 +35948,17 @@ class SourceFooter extends _react.PureCo
       this.renderSourceSummary(),
       this.renderToggleButton()
     );
   }
 }
 
 exports.default = (0, _reactRedux.connect)(state => {
   const selectedSource = (0, _selectors.getSelectedSource)(state);
-  const selectedId = selectedSource && selectedSource.get("id");
+  const selectedId = selectedSource.get("id");
   const source = selectedSource.toJS();
   return {
     selectedSource,
     mappedSource: (0, _sources.getGeneratedSource)(state, source),
     prettySource: (0, _selectors.getPrettySource)(state, selectedId),
     endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
   };
 }, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourceFooter);
@@ -40021,112 +40002,103 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.DebugLine = undefined;
 
 var _react = __webpack_require__(0);
 
 var _editor = __webpack_require__(1358);
 
-var _sourceDocuments = __webpack_require__(1436);
+var _source = __webpack_require__(1356);
 
 var _reactRedux = __webpack_require__(1189);
 
 var _selectors = __webpack_require__(1352);
 
+function isException(pauseInfo) {
+  return pauseInfo && pauseInfo.why.type === "exception";
+} /* 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/>. */
+
+function isDocumentReady(selectedSource, selectedFrame) {
+  return selectedFrame && (0, _source.isLoaded)(selectedSource) && (0, _editor.hasDocument)(selectedFrame.location.sourceId);
+}
+
 class DebugLine extends _react.Component {
-  constructor() {
-    super();
-    this.state = { debugExpression: { clear: () => {} } };
+
+  componentDidUpdate(prevProps) {
+    const { pauseInfo, selectedFrame, selectedSource } = this.props;
+    this.setDebugLine(pauseInfo, selectedFrame, selectedSource);
+  }
+
+  componentWillUpdate() {
+    const { pauseInfo, selectedFrame, selectedSource } = this.props;
+    this.clearDebugLine(selectedFrame, selectedSource, pauseInfo);
   }
 
   componentDidMount() {
-    this.setDebugLine(this.props.pauseInfo, this.props.selectedFrame, this.props.editor);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    this.clearDebugLine(this.props.selectedFrame, this.props.editor);
-    this.setDebugLine(nextProps.pauseInfo, nextProps.selectedFrame, nextProps.editor);
-  }
-
-  componentWillUnmount() {
-    this.clearDebugLine(this.props.selectedFrame, this.props.editor);
-  }
-
-  setDebugLine(pauseInfo, selectedFrame, editor) {
-    if (!selectedFrame) {
-      return;
+    const { pauseInfo, selectedFrame, selectedSource } = this.props;
+    this.setDebugLine(pauseInfo, selectedFrame, selectedSource);
+  }
+
+  setDebugLine(pauseInfo, selectedFrame, selectedSource) {
+    if (!isDocumentReady(selectedSource, selectedFrame)) {
+      return;
+    }
+    const sourceId = selectedFrame.location.sourceId;
+    const doc = (0, _editor.getDocument)(sourceId);
+
+    const { line, column } = (0, _editor.toEditorPosition)(selectedFrame.location);
+    const { markTextClass, lineClass } = this.getTextClasses(pauseInfo);
+    doc.addLineClass(line, "line", lineClass);
+
+    this.debugExpression = doc.markText({ ch: column, line }, { ch: null, line }, { className: markTextClass });
+  }
+
+  clearDebugLine(selectedFrame, selectedSource, pause) {
+    if (!isDocumentReady(selectedSource, selectedFrame)) {
+      return;
+    }
+
+    if (this.debugExpression) {
+      this.debugExpression.clear();
     }
 
     const sourceId = selectedFrame.location.sourceId;
-    const doc = (0, _sourceDocuments.getDocument)(sourceId);
-    if (!doc) {
-      return;
-    }
-
-    const { line, column } = (0, _editor.toEditorPosition)(sourceId, selectedFrame.location);
-
-    // make sure the line is visible
-    if (editor && editor.alignLine) {
-      editor.alignLine(line);
-    }
-
-    const { markTextClass, lineClass } = this.getTextClasses(pauseInfo);
-    doc.addLineClass(line, "line", lineClass);
-
-    const debugExpression = (0, _editor.markText)(editor, markTextClass, {
-      start: { line, column },
-      end: { line, column: null }
-    });
-    this.setState({ debugExpression });
-  }
-
-  clearDebugLine(selectedFrame, editor) {
-    if (!selectedFrame) {
-      return;
-    }
-    const { line, sourceId } = selectedFrame.location;
-    const { debugExpression } = this.state;
-    if (debugExpression) {
-      debugExpression.clear();
-    }
-
-    const editorLine = line - 1;
-    const doc = (0, _sourceDocuments.getDocument)(sourceId);
-    if (!doc) {
-      return;
-    }
-
-    doc.removeLineClass(editorLine, "line", "new-debug-line");
-    doc.removeLineClass(editorLine, "line", "new-debug-line-error");
-  }
-
-  getTextClasses(pauseInfo) {
-    if (pauseInfo && pauseInfo.why.type === "exception") {
+    const { line } = (0, _editor.toEditorPosition)(selectedFrame.location);
+    const doc = (0, _editor.getDocument)(sourceId);
+    const { lineClass } = this.getTextClasses(pause);
+    doc.removeLineClass(line, "line", lineClass);
+  }
+
+  getTextClasses(pause) {
+    if (isException(pause)) {
       return {
         markTextClass: "debug-expression-error",
         lineClass: "new-debug-line-error"
       };
     }
+
     return { markTextClass: "debug-expression", lineClass: "new-debug-line" };
   }
 
   render() {
     return null;
   }
 }
 
-exports.DebugLine = DebugLine; /* 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/>. */
-
-exports.default = (0, _reactRedux.connect)(state => ({
-  selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
-  pauseInfo: (0, _selectors.getPause)(state)
-}))(DebugLine);
+exports.DebugLine = DebugLine;
+exports.default = (0, _reactRedux.connect)(state => {
+  return {
+    selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
+    selectedSource: (0, _selectors.getSelectedSource)(state),
+    pauseInfo: (0, _selectors.getPause)(state)
+  };
+})(DebugLine);
 
 /***/ }),
 /* 1594 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -40898,17 +40870,17 @@ var _Accordion2 = _interopRequireDefault
 var _CommandBar = __webpack_require__(1608);
 
 var _CommandBar2 = _interopRequireDefault(_CommandBar);
 
 var _UtilsBar = __webpack_require__(1609);
 
 var _UtilsBar2 = _interopRequireDefault(_UtilsBar);
 
-var _BreakpointsDropdown = __webpack_require__(1791);
+var _BreakpointsDropdown = __webpack_require__(1790);
 
 var _BreakpointsDropdown2 = _interopRequireDefault(_BreakpointsDropdown);
 
 var _ChromeScopes = __webpack_require__(1610);
 
 var _ChromeScopes2 = _interopRequireDefault(_ChromeScopes);
 
 var _Scopes2 = __webpack_require__(1611);
@@ -40979,17 +40951,16 @@ class SecondaryPanes extends _react.Comp
     return [debugBtn(evt => {
       evt.stopPropagation();
       this.props.evaluateExpressions();
     }, "refresh", "refresh", L10N.getStr("watchExpressions.refreshButton"))];
   }
 
   getScopeItem() {
     const isPaused = () => !!this.props.pauseData;
-
     return {
       header: L10N.getStr("scopes.header"),
       className: "scopes-pane",
       component: Scopes,
       opened: _prefs.prefs.scopesVisible,
       onToggle: opened => {
         _prefs.prefs.scopesVisible = opened;
       },
@@ -42489,17 +42460,17 @@ var _prefs = __webpack_require__(226);
 var _selectors = __webpack_require__(1352);
 
 var _text = __webpack_require__(1387);
 
 var _actions = __webpack_require__(1354);
 
 var _actions2 = _interopRequireDefault(_actions);
 
-var _CommandBarButton = __webpack_require__(1765);
+var _CommandBarButton = __webpack_require__(1764);
 
 var _CommandBarButton2 = _interopRequireDefault(_CommandBarButton);
 
 __webpack_require__(1295);
 
 var _devtoolsModules = __webpack_require__(1376);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -43008,17 +42979,17 @@ var _redux = __webpack_require__(3);
 var _reactRedux = __webpack_require__(1189);
 
 var _actions = __webpack_require__(1354);
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _selectors = __webpack_require__(1352);
 
-var _scopes = __webpack_require__(1793);
+var _scopes = __webpack_require__(1792);
 
 var _devtoolsReps = __webpack_require__(1408);
 
 __webpack_require__(1296);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
@@ -45011,17 +44982,17 @@ var _selectors = __webpack_require__(135
 var _prefs = __webpack_require__(226);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _loadSourceText = __webpack_require__(1435);
 
 var _parser = __webpack_require__(1365);
 
-var _updateScopeBindings = __webpack_require__(1785);
+var _updateScopeBindings = __webpack_require__(1784);
 
 /* 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/>. */
 
 function mapScopes(scopes, frame) {
   return async function ({ dispatch, getState, client, sourceMaps }) {
     const mappedScopes = await (0, _updateScopeBindings.updateScopeBindings)(scopes, frame.generatedLocation, frame.location, {
@@ -45838,17 +45809,17 @@ module.exports = "<!-- This Source Code 
 
 /* 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/. */
 
 var EventEmitter = function EventEmitter() {};
 module.exports = EventEmitter;
 
-const promise = __webpack_require__(1770);
+const promise = __webpack_require__(1769);
 
 /**
  * Decorate an object with event emitter functionality.
  *
  * @param Object aObjectToDecorate
  *        Bind all public methods of EventEmitter to
  *        the aObjectToDecorate object.
  */
@@ -46416,59 +46387,16 @@ EventEmitter.prototype = {
 
 /***/ }),
 /* 1762 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-
-var _lodash = __webpack_require__(2);
-
-let newSources;
-let createSource;
-let queuedSources;
-let supportsWasm = false;
-
-const queue = (0, _lodash.throttle)(() => {
-  if (!newSources || !createSource) {
-    return;
-  }
-  newSources(queuedSources.map(source => {
-    return createSource(source, { supportsWasm });
-  }));
-  queuedSources = [];
-}, 100);
-
-exports.default = {
-  initialize: options => {
-    newSources = options.actions.newSources;
-    createSource = options.createSource;
-    supportsWasm = options.supportsWasm;
-    queuedSources = [];
-  },
-  queue: source => {
-    queuedSources.push(source);
-    queue();
-  },
-  flush: () => queue.flush(),
-  clear: () => queue.cancel()
-};
-
-/***/ }),
-/* 1763 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
 (function () {
   var Query, coreChars, countDir, getCharCodes, getExtension, opt_char_re, truncatedUpperCase, _ref;
 
   _ref = __webpack_require__(1761), countDir = _ref.countDir, getExtension = _ref.getExtension;
 
   module.exports = Query = function () {
     function Query(query, _arg) {
       var optCharRegEx, pathSeparator, _ref1;
@@ -46516,17 +46444,17 @@ exports.default = {
     while (++i < len) {
       charCodes[str.charCodeAt(i)] = true;
     }
     return charCodes;
   };
 }).call(undefined);
 
 /***/ }),
-/* 1764 */
+/* 1763 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
@@ -46536,17 +46464,17 @@ var _extends = Object.assign || function
 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _propTypes = __webpack_require__(20);
 
-var _util = __webpack_require__(1778);
+var _util = __webpack_require__(1777);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
@@ -46614,17 +46542,17 @@ InlineSVG.defaultProps = {
 
 InlineSVG.propTypes = {
     src: _propTypes.string.isRequired,
     element: _propTypes.string,
     raw: _propTypes.bool
 };
 
 /***/ }),
-/* 1765 */
+/* 1764 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -46634,17 +46562,17 @@ var _extends = Object.assign || function
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
-__webpack_require__(1789);
+__webpack_require__(1788);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 const CommandBarButton = props => {
   const { children, className, pressed = false } = props,
         rest = _objectWithoutProperties(props, ["children", "className", "pressed"]);
@@ -46657,17 +46585,17 @@ const CommandBarButton = props => {
     }, rest),
     children
   );
 };
 
 exports.default = CommandBarButton;
 
 /***/ }),
-/* 1766 */
+/* 1765 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -46728,17 +46656,17 @@ function getBindingVariables(bindings, p
       name,
       path: `${parentName}/${name}`,
       contents
     };
   });
 }
 
 /***/ }),
-/* 1767 */
+/* 1766 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -46791,46 +46719,46 @@ function getThisVariable(frame, path) {
   return {
     name: "<this>",
     path: `${path}/<this>`,
     contents: { value: this_ }
   };
 }
 
 /***/ }),
-/* 1768 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-const Menu = __webpack_require__(1769);
-const MenuItem = __webpack_require__(1771);
-const { PrefsHelper } = __webpack_require__(1772);
+/* 1767 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/. */
+
+const Menu = __webpack_require__(1768);
+const MenuItem = __webpack_require__(1770);
+const { PrefsHelper } = __webpack_require__(1771);
 const Services = __webpack_require__(22);
-const KeyShortcuts = __webpack_require__(1773);
-const { ZoomKeys } = __webpack_require__(1774);
+const KeyShortcuts = __webpack_require__(1772);
+const { ZoomKeys } = __webpack_require__(1773);
 const EventEmitter = __webpack_require__(1759);
 
 module.exports = {
   KeyShortcuts,
   Menu,
   MenuItem,
   PrefsHelper,
   Services,
   ZoomKeys,
   EventEmitter
 };
 
 /***/ }),
-/* 1769 */
+/* 1768 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -47014,17 +46942,17 @@ Menu.sendActionToFirstResponder = () => 
 
 Menu.buildFromTemplate = () => {
   throw Error("Not implemented");
 };
 
 module.exports = Menu;
 
 /***/ }),
-/* 1770 */
+/* 1769 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -47051,17 +46979,17 @@ p.defer = function defer() {
     reject: reject,
     promise: promise
   };
 };
 
 module.exports = p;
 
 /***/ }),
-/* 1771 */
+/* 1770 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -47120,17 +47048,17 @@ function MenuItem({
   this.submenu = submenu;
   this.type = type;
   this.visible = visible;
 }
 
 module.exports = MenuItem;
 
 /***/ }),
-/* 1772 */
+/* 1771 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -47308,17 +47236,17 @@ function makeObserver(self, cache, prefs
       self.emit("pref-changed", accessorName, self[accessorName]);
     }
   };
 }
 
 exports.PrefsHelper = PrefsHelper;
 
 /***/ }),
-/* 1773 */
+/* 1772 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -47555,17 +47483,17 @@ KeyShortcuts.prototype = {
 
   off(key, listener) {
     this.eventEmitter.off(key, listener);
   }
 };
 module.exports = KeyShortcuts;
 
 /***/ }),
-/* 1774 */
+/* 1773 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* 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/. */
 
 
@@ -47575,34 +47503,34 @@ module.exports = KeyShortcuts;
  *
  * Based on nsIMarkupDocumentViewer.fullZoom API
  * https://developer.mozilla.org/en-US/Firefox/Releases/3/Full_page_zoom
  */
 
 exports.register = function (window) {};
 
 /***/ }),
-/* 1775 */
+/* 1774 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(process) {
 
 (function () {
   var Query, defaultPathSeparator, filter, matcher, parseOptions, pathScorer, preparedQueryCache, scorer;
 
-  filter = __webpack_require__(1776);
-
-  matcher = __webpack_require__(1777);
+  filter = __webpack_require__(1775);
+
+  matcher = __webpack_require__(1776);
 
   scorer = __webpack_require__(1760);
 
   pathScorer = __webpack_require__(1761);
 
-  Query = __webpack_require__(1763);
+  Query = __webpack_require__(1762);
 
   preparedQueryCache = null;
 
   defaultPathSeparator = (typeof process !== "undefined" && process !== null ? process.platform : void 0) === "win32" ? '\\' : '/';
 
   module.exports = {
     filter: function (candidates, query, options) {
       if (options == null) {
@@ -47696,30 +47624,30 @@ exports.register = function (window) {};
       options.preparedQuery = preparedQueryCache && preparedQueryCache.query === query ? preparedQueryCache : preparedQueryCache = new Query(query, options);
     }
     return options;
   };
 }).call(undefined);
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(120)))
 
 /***/ }),
-/* 1776 */
+/* 1775 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 (function () {
   var Query, pathScorer, pluckCandidates, scorer, sortCandidates;
 
   scorer = __webpack_require__(1760);
 
   pathScorer = __webpack_require__(1761);
 
-  Query = __webpack_require__(1763);
+  Query = __webpack_require__(1762);
 
   pluckCandidates = function (a) {
     return a.candidate;
   };
 
   sortCandidates = function (a, b) {
     return b.score - a.score;
   };
@@ -47753,17 +47681,17 @@ exports.register = function (window) {};
     if (maxResults != null) {
       candidates = candidates.slice(0, maxResults);
     }
     return candidates;
   };
 }).call(undefined);
 
 /***/ }),
-/* 1777 */
+/* 1776 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 (function () {
   var basenameMatch, computeMatch, isMatch, isWordStart, match, mergeMatches, scoreAcronyms, scoreCharacter, scoreConsecutives, _ref;
 
@@ -47975,17 +47903,17 @@ exports.register = function (window) {};
       }
     }
     matches.reverse();
     return matches;
   };
 }).call(undefined);
 
 /***/ }),
-/* 1778 */
+/* 1777 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
@@ -48030,17 +47958,17 @@ function getSVGFromSource(src) {
 
 // get <svg /> element props
 function extractSVGProps(src) {
     var map = getSVGFromSource(src).attributes;
     return map.length > 0 ? serializeAttrs(map) : null;
 }
 
 /***/ }),
-/* 1779 */
+/* 1778 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48093,17 +48021,17 @@ function isMinified(source) {
 
   const minified = indentCount / lines * 100 < INDENT_COUNT_THRESHOLD || overCharLimit;
 
   _minifiedCache.set(source.id, minified);
   return minified;
 }
 
 /***/ }),
-/* 1780 */
+/* 1779 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48154,55 +48082,59 @@ function getCallStackFrames(state) {
   const selectedSource = (0, _sources.getSelectedSource)(state);
   const sources = (0, _sources.getSources)(state);
   const frames = (0, _pause.getFrames)(state);
 
   return formatCallStackFrames(frames, sources, selectedSource);
 }
 
 /***/ }),
-/* 1781 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-Object.defineProperty(exports, "__esModule", {
-  value: true
-});
-exports.default = getVisibleSelectedFrame;
+/* 1780 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
 
 var _sources = __webpack_require__(1369);
 
 var _pause = __webpack_require__(1394);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
-function getLocation(frame, isGeneratedSource) {
-  return isGeneratedSource ? frame.generatedLocation || frame.location : frame.location;
-} /* 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/>. */
-
-function getVisibleSelectedFrame(state) {
-  const selectedLocation = (0, _sources.getSelectedLocation)(state);
-  const isGeneratedSource = !(0, _devtoolsSourceMap.isOriginalId)(selectedLocation.sourceId);
-  const selectedFrame = (0, _pause.getSelectedFrame)(state);
-
-  if (!selectedFrame) {
-    return;
-  }
-
-  return {
-    location: getLocation(selectedFrame, isGeneratedSource)
-  };
-}
-
-/***/ }),
-/* 1782 */
+var _reselect = __webpack_require__(993);
+
+/* 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/>. */
+
+function getLocation(frame, location) {
+  return !(0, _devtoolsSourceMap.isOriginalId)(location.sourceId) ? frame.generatedLocation || frame.location : frame.location;
+}
+
+const getVisibleSelectedFrame = (0, _reselect.createSelector)(_sources.getSelectedLocation, _pause.getSelectedFrame, (selectedLocation, selectedFrame) => {
+  if (!selectedFrame || !selectedLocation) {
+    return null;
+  }
+
+  const { id } = selectedFrame;
+
+  return {
+    id,
+    location: getLocation(selectedFrame, selectedLocation)
+  };
+});
+
+exports.default = getVisibleSelectedFrame;
+
+/***/ }),
+/* 1781 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48245,17 +48177,17 @@ function setInScopeLines() {
     dispatch({
       type: "IN_SCOPE_LINES",
       lines: inScopeLines
     });
   };
 }
 
 /***/ }),
-/* 1783 */
+/* 1782 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48270,17 +48202,17 @@ function createLocation({
     sourceId,
     line,
     column,
     sourceUrl: sourceUrl || null
   };
 }
 
 /***/ }),
-/* 1784 */
+/* 1783 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48298,17 +48230,17 @@ function getTokenLocation(codeMirror, to
 
   return {
     line: line + 1,
     column: ch
   };
 }
 
 /***/ }),
-/* 1785 */
+/* 1784 */
 /***/ (function(module, exports, __webpack_require__) {
 
 let updateScopeBindings = (() => {
   var _ref = _asyncToGenerator(function* (scope, location, originalLocation, scopesDataSource) {
     const generatedScopes = yield scopesDataSource.getSourceMapsScopes(location);
     if (!generatedScopes) {
       return scope;
     }
@@ -48323,17 +48255,17 @@ let updateScopeBindings = (() => {
 })();
 
 function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
 
 /* 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/. */
 
-const { remapScopes } = __webpack_require__(1786);
+const { remapScopes } = __webpack_require__(1785);
 
 function extendScope(scope, generatedScopes, index, remapedScopes, remapedScopesIndex) {
   if (!scope) {
     return undefined;
   }
   if (index >= generatedScopes.length) {
     return scope;
   }
@@ -48361,17 +48293,17 @@ function extendScope(scope, generatedSco
   });
 }
 
 module.exports = {
   updateScopeBindings
 };
 
 /***/ }),
-/* 1786 */
+/* 1785 */
 /***/ (function(module, exports) {
 
 
 
 // Chunk split source scopes on function/closure boundary
 /* 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/. */
@@ -48444,17 +48376,17 @@ function remapScopes(scopes, generatedSc
   return assigned;
 }
 
 module.exports = {
   remapScopes
 };
 
 /***/ }),
-/* 1787 */
+/* 1786 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48470,16 +48402,18 @@ var _ast = __webpack_require__(1638);
 var _editor = __webpack_require__(1358);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _promise = __webpack_require__(1653);
 
 var _selectors = __webpack_require__(1352);
 
+var _expressions = __webpack_require__(1398);
+
 var _lodash = __webpack_require__(2);
 
 const extraProps = {
   react: { displayName: "this._reactInternalInstance.getName()" },
   immutable: {
     isImmutable: exp => `Immutable.Iterable.isIterable(${exp})`,
     entries: exp => `${exp}.toJS()`,
     type: exp => `${exp}.constructor.name`
@@ -48501,17 +48435,19 @@ function updatePreview(target, editor) {
 
       // We are mousing over a new token that is not in the preview
       if (!target.classList.contains("debug-expression")) {
         dispatch(clearPreview());
       }
     }
 
     const source = (0, _selectors.getSelectedSource)(getState());
-    if ((0, _selectors.getSymbols)(getState(), source.toJS()).functions.length == 0) {
+
+    const symbols = (0, _selectors.getSymbols)(getState(), source.toJS());
+    if (symbols.functions.length == 0) {
       return;
     }
 
     const invalidToken = tokenText === "" || tokenText.match(/[(){}\|&%,.;=<>\+-/\*\s]/);
 
     const invalidTarget = target.parentElement && !target.parentElement.closest(".CodeMirror-line") || cursorPos.top == 0;
 
     const isUpdating = preview && preview.updating;
@@ -48555,17 +48491,17 @@ function setPreview(token, tokenPos, cur
         if (!expression) {
           return;
         }
 
         const sourceId = source.get("id");
         if (location && !(0, _devtoolsSourceMap.isGeneratedId)(sourceId)) {
           const generatedLocation = await sourceMaps.getGeneratedLocation(_extends({}, location.start, { sourceId }), source.toJS());
 
-          expression = await (0, _selectors.getMappedExpression)({ sourceMaps }, generatedLocation, expression);
+          expression = await (0, _expressions.getMappedExpression)({ sourceMaps }, generatedLocation, expression);
         }
 
         const selectedFrame = (0, _selectors.getSelectedFrame)(getState());
         const { result } = await client.evaluate(expression, {
           frameId: selectedFrame.id
         });
 
         const reactDisplayName = await client.evaluate(extraProps.react.displayName, {
@@ -48623,29 +48559,29 @@ function clearPreview() {
 
     return dispatch({
       type: "CLEAR_SELECTION"
     });
   };
 }
 
 /***/ }),
-/* 1788 */
+/* 1787 */
 /***/ (function(module, exports) {
 
 module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 9 15\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><title>Group 2</title><desc>Created with Sketch.</desc><g id=\"Symbols\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\"><g id=\"Group-2\" transform=\"translate(-3.000000, 0.000000)\" fill-rule=\"nonzero\" fill=\"#000000\"><g transform=\"translate(2.000000, 0.000000)\" id=\"path10\"><path d=\"M8.66478978,0.152329483 L6.24859226,7.23234215 L6.24859226,7.23234215 C6.22451137,7.30290469 6.14778771,7.34058551 6.07722517,7.31650462 C6.0437665,7.30508617 6.01612208,7.2809984 6.00023206,7.24941723 L2.49196511,0.276788279 L2.49196511,0.276788279 C2.4537519,0.200840088 2.3759926,0.152917255 2.29097274,0.152917255 L1.08420686,0.152917255 L1.08420686,0.152917255 C1.02897839,0.152917255 0.984206865,0.19768878 0.984206865,0.252917255 C0.984206865,0.268916365 0.988045664,0.284681937 0.99540091,0.2988901 L5.47310495,8.94849143 L5.47310495,8.94849143 C5.53346173,9.06508289 5.54553974,9.2007357 5.50673082,9.3261565 L4.0917648,13.8989752 L4.0917648,13.8989752 C4.06260539,13.9932111 3.97546399,14.0574648 3.87681975,14.0574648 L1.45804052,14.0574648 L1.45804052,14.0574648 C1.33377645,14.0574648 1.23304051,14.1582007 1.23304051,14.2824648 L1.23304051,14.775 L1.23304051,14.775 C1.23304051,14.8992641 1.33377645,15 1.45804052,15 L4.50020953,15 L4.50020953,15 C4.71271509,15 4.90200445,14.8656767 4.97216898,14.6650887 L10.0088745,0.266035474 L10.0088745,0.266035474 C10.0453448,0.161773071 9.99038856,0.0476866038 9.88612615,0.0112162185 C9.86490135,0.00379190571 9.84257651,2.78889645e-14 9.82009068,2.5895952e-14 L8.87773108,-4.83289971e-14 L8.87773108,-4.79061235e-14 C8.78147405,-5.47279145e-14 8.69587888,0.061231278 8.66478978,0.152329483 Z\" transform=\"translate(5.502176, 7.500000) scale(1, -1) translate(-5.502176, -7.500000) \"></path></g></g></g></svg>"
 
 /***/ }),
+/* 1788 */
+/***/ (function(module, exports) {
+
+// removed by extract-text-webpack-plugin
+
+/***/ }),
 /* 1789 */
-/***/ (function(module, exports) {
-
-// removed by extract-text-webpack-plugin
-
-/***/ }),
-/* 1790 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, '__esModule', {
     value: true
 });
@@ -48836,17 +48772,17 @@ var InlineSVG = function (_React$Compone
 
     return InlineSVG;
 }(_react2['default'].Component);
 
 exports['default'] = InlineSVG;
 module.exports = exports['default'];
 
 /***/ }),
-/* 1791 */
+/* 1790 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -48863,17 +48799,17 @@ var _Svg2 = _interopRequireDefault(_Svg)
 var _Dropdown = __webpack_require__(1615);
 
 var _Dropdown2 = _interopRequireDefault(_Dropdown);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
-__webpack_require__(1792);
+__webpack_require__(1791);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function renderPause(isWaitingOnBreak) {
   const active = isWaitingOnBreak;
   return _react2.default.createElement(
     "div",
     {
@@ -48997,36 +48933,36 @@ function renderBreakpointsDropdown(break
           inactive: !active
         })
       })
     })
   );
 }
 
 /***/ }),
+/* 1791 */
+/***/ (function(module, exports) {
+
+// removed by extract-text-webpack-plugin
+
+/***/ }),
 /* 1792 */
-/***/ (function(module, exports) {
-
-// removed by extract-text-webpack-plugin
-
-/***/ }),
-/* 1793 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.getScopes = getScopes;
 
-var _synthesizeScopes = __webpack_require__(1794);
-
-var _getScope = __webpack_require__(1795);
+var _synthesizeScopes = __webpack_require__(1793);
+
+var _getScope = __webpack_require__(1794);
 
 /* 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/>. */
 
 function getScopes(pauseInfo, selectedFrame, frameScopes) {
   if (!pauseInfo || !selectedFrame) {
     return null;
@@ -49067,32 +49003,32 @@ function getScopes(pauseInfo, selectedFr
     }
     scope = scope.parent;
   }
 
   return scopes;
 }
 
 /***/ }),
-/* 1794 */
+/* 1793 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.synthesizeScopes = synthesizeScopes;
 
-var _getVariables = __webpack_require__(1766);
+var _getVariables = __webpack_require__(1765);
 
 var _frame = __webpack_require__(1380);
 
-var _utils = __webpack_require__(1767);
+var _utils = __webpack_require__(1766);
 
 function getSynteticScopeTitle(type, generatedScopes) {
   if (type === "function") {
     // FIXME Use original function name here
     const lastGeneratedScope = generatedScopes[generatedScopes.length - 1];
     const isLastGeneratedScopeFn = lastGeneratedScope && lastGeneratedScope.type === "function";
     return isLastGeneratedScopeFn && lastGeneratedScope.function.displayName ? (0, _frame.simplifyDisplayName)(lastGeneratedScope.function.displayName) : L10N.getStr("anonymous");
   }
@@ -49212,35 +49148,35 @@ function synthesizeScopes(scope, selecte
         contents: bindings
       });
     }
     return result;
   }, []);
 }
 
 /***/ }),
-/* 1795 */
+/* 1794 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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/>. */
 
 exports.getScope = getScope;
 
-var _getVariables = __webpack_require__(1766);
-
-var _utils = __webpack_require__(1767);
+var _getVariables = __webpack_require__(1765);
+
+var _utils = __webpack_require__(1766);
 
 var _frame = __webpack_require__(1380);
 
 function getScopeTitle(type, scope) {
   if (type === "function") {
     return scope.function.displayName ? (0, _frame.simplifyDisplayName)(scope.function.displayName) : L10N.getStr("anonymous");
   }
   return L10N.getStr("scopes.block");
@@ -49290,11 +49226,142 @@ function getScope(scope, selectedFrame, 
       path: key,
       contents: { value }
     };
   }
 
   return null;
 }
 
+/***/ }),
+/* 1795 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+
+var _lodash = __webpack_require__(2);
+
+let newSources;
+let createSource;
+let queuedSources;
+let supportsWasm = false;
+
+const queue = (0, _lodash.throttle)(() => {
+  if (!newSources || !createSource) {
+    return;
+  }
+  newSources(queuedSources.map(source => {
+    return createSource(source, { supportsWasm });
+  }));
+  queuedSources = [];
+}, 100);
+
+exports.default = {
+  initialize: options => {
+    newSources = options.actions.newSources;
+    createSource = options.createSource;
+    supportsWasm = options.supportsWasm;
+    queuedSources = [];
+  },
+  queue: source => {
+    queuedSources.push(source);
+    queue();
+  },
+  flush: () => queue.flush(),
+  clear: () => queue.cancel()
+};
+
+/***/ }),
+/* 1796 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.HighlightLine = undefined;
+
+var _react = __webpack_require__(0);
+
+var _editor = __webpack_require__(1358);
+
+var _sourceDocuments = __webpack_require__(1436);
+
+var _source = __webpack_require__(1356);
+
+var _reactRedux = __webpack_require__(1189);
+
+var _selectors = __webpack_require__(1352);
+
+/* 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/>. */
+
+function isDebugLine(selectedFrame, selectedLocation) {
+  if (!selectedFrame) {
+    return;
+  }
+
+  return selectedFrame.location.sourceId == selectedLocation.sourceId && selectedFrame.location.line == selectedLocation.line;
+}
+
+function isDocumentReady(selectedSource, selectedLocation) {
+  return selectedLocation && (0, _source.isLoaded)(selectedSource) && (0, _sourceDocuments.hasDocument)(selectedLocation.sourceId);
+}
+
+class HighlightLine extends _react.PureComponent {
+  componentDidUpdate(prevProps) {
+    const { selectedLocation, selectedFrame, selectedSource } = this.props;
+
+    this.clearHighlightLine(prevProps.selectedLocation, prevProps.selectedSource);
+
+    this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
+  }
+
+  setHighlightLine(selectedLocation, selectedFrame, selectedSource) {
+    if (!isDocumentReady(selectedSource, selectedLocation)) {
+      return;
+    }
+
+    const { sourceId, line } = selectedLocation;
+
+    if (!line || isDebugLine(selectedFrame, selectedLocation)) {
+      return;
+    }
+
+    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
+    const doc = (0, _sourceDocuments.getDocument)(sourceId);
+    doc.addLineClass(editorLine, "line", "highlight-line");
+  }
+
+  clearHighlightLine(selectedLocation, selectedSource) {
+    if (!isDocumentReady(selectedSource, selectedLocation)) {
+      return;
+    }
+
+    const { line, sourceId } = selectedLocation;
+    const editorLine = (0, _editor.toEditorLine)(sourceId, line);
+    const doc = (0, _sourceDocuments.getDocument)(sourceId);
+    doc.removeLineClass(editorLine, "line", "highlight-line");
+  }
+
+  render() {
+    return null;
+  }
+}
+
+exports.HighlightLine = HighlightLine;
+exports.default = (0, _reactRedux.connect)(state => ({
+  selectedFrame: (0, _selectors.getVisibleSelectedFrame)(state),
+  selectedLocation: (0, _selectors.getSelectedLocation)(state),
+  selectedSource: (0, _selectors.getSelectedSource)(state)
+}))(HighlightLine);
+
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -91,23 +91,26 @@ skip-if = !e10s # This test is only vali
 [browser_dbg-iframes.js]
 [browser_dbg_keyboard_navigation.js]
 skip-if = true # regular failures during release in Bug 1415300
 [browser_dbg_keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 [browser_dbg-pause-exceptions.js]
+[browser_dbg-pause-ux.js]
+skip-if = os == "win"
 [browser_dbg-navigation.js]
 [browser_dbg-minified.js]
 [browser_dbg-pretty-print.js]
 [browser_dbg-pretty-print-console.js]
 [browser_dbg-pretty-print-paused.js]
 [browser_dbg-preview.js]
 skip-if = true # regular failures during release in Bug 1415300
+[browser_dbg-preview-source-maps.js]
 [browser_dbg-returnvalues.js]
 [browser_dbg-scopes-mutations.js]
 [browser_dbg-search-file.js]
 skip-if = os == "win" # Bug 1393121
 [browser_dbg-quick-open.js]
 skip-if = true # regular failures during release in Bug 1415300
 [browser_dbg-search-project.js]
 [browser_dbg-sourcemaps.js]
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breaking.js
@@ -6,19 +6,21 @@
 add_task(async function() {
   const dbg = await initDebugger("doc-scripts.html");
   const { selectors: { getSelectedSource }, getState } = dbg;
 
   // Make sure we can set a top-level breakpoint and it will be hit on
   // reload.
   await addBreakpoint(dbg, "scripts.html", 18);
   reload(dbg);
+
+  await waitForDispatch(dbg, "NAVIGATE");
+  await waitForSelectedSource(dbg, "doc-scripts.html");
   await waitForPaused(dbg);
 
-  await waitForLoadedSource(dbg, "doc-scripts.html");
   assertPausedLocation(dbg);
   await resume(dbg);
 
   // Create an eval script that pauses itself.
   invokeInTab("doEval");
   await waitForPaused(dbg);
 
   await resume(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-highlight.js
@@ -12,37 +12,38 @@ add_task(async function() {
 
   // The source itself doesn't even exist yet, and using
   // `selectSourceURL` will set a pending request to load this source
   // and highlight a specific line.
   dbg.actions.selectSourceURL(sourceUrl, { location: { line: 66 } });
 
   // Wait for the source text to load and make sure we're in the right
   // place.
-  await waitForLoadedSource(dbg, sourceUrl);
+  await waitForSelectedSource(dbg, sourceUrl);
+  log(`loaded source`);
 
   // TODO: revisit highlighting lines when the debugger opens
   // assertHighlightLocation(dbg, "long.js", 66);
 
-  // Jump to line 16 and make sure the editor scrolled.
+  log(`Select line 16 and make sure the editor scrolled.`);
   await selectSource(dbg, "long.js", 16);
   await waitForElement(dbg, ".CodeMirror-code > .highlight-line");
   assertHighlightLocation(dbg, "long.js", 16);
 
-  // Make sure only one line is ever highlighted and the flash
-  // animation is cancelled on old lines.
+  log(`Select several locations and check that we have one highlight`);
   await selectSource(dbg, "long.js", 17);
   await selectSource(dbg, "long.js", 18);
   assertHighlightLocation(dbg, "long.js", 18);
 
   // Test jumping to a line in a source that exists but hasn't been
   // loaded yet.
+  log(`Select an unloaded source`);
   selectSource(dbg, "simple1.js", 6);
 
   // Make sure the source is in the loading state, wait for it to be
   // fully loaded, and check the highlighted line.
   const simple1 = findSource(dbg, "simple1.js");
   is(getSource(getState(), simple1.id).get("loadedState"), "loading");
 
-  await waitForLoadedSource(dbg, "simple1.js");
+  await waitForSelectedSource(dbg, "simple1.js");
   ok(getSource(getState(), simple1.id).get("text"));
   assertHighlightLocation(dbg, "simple1.js", 6);
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pause-ux.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getScrollTop(dbg) {
+  return getCM(dbg).doc.scrollTop;
+}
+
+async function waitForMatch(dbg, { matchIndex, count }) {
+  await waitForState(
+    dbg,
+    state => {
+      const result = dbg.selectors.getFileSearchResults(state);
+      return result.matchIndex == matchIndex && result.count == count;
+    },
+    "wait for match"
+  );
+}
+
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html");
+
+  // Make sure that we can set a breakpoint on a line out of the
+  // viewport, and that pausing there scrolls the editor to it.
+  let longSrc = findSource(dbg, "long.js");
+  await addBreakpoint(dbg, longSrc, 66);
+  invokeInTab("testModel");
+  await waitForPaused(dbg);
+
+  const pauseScrollTop = getScrollTop(dbg);
+
+  log("1. adding a breakpoint should not scroll the editor");
+  getCM(dbg).scrollTo(0, 0);
+  await addBreakpoint(dbg, longSrc, 11);
+  is(getScrollTop(dbg), 0, "scroll position");
+
+  log("2. searching should jump to the match");
+  pressKey(dbg, "fileSearch");
+  type(dbg, "check");
+  await waitForMatch(dbg, { matchIndex: 0, count: 2 });
+  const matchScrollTop = getScrollTop(dbg);
+  ok(pauseScrollTop != matchScrollTop, "did not jump to debug line");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getCoordsFromPosition(cm, { line, ch }) {
+  return cm.charCoords({ line: ~~line, ch: ~~ch });
+}
+
+function hoverAtPos(dbg, { line, ch }) {
+  const cm = getCM(dbg);
+  const coords = getCoordsFromPosition(cm, { line: line - 1, ch });
+  const tokenEl = dbg.win.document.elementFromPoint(coords.left, coords.top);
+  tokenEl.dispatchEvent(
+    new MouseEvent("mouseover", {
+      bubbles: true,
+      cancelable: true,
+      view: dbg.win
+    })
+  );
+}
+
+function assertTooltip(dbg, { result, expression }) {
+  const previewEl = findElement(dbg, "tooltip");
+  is(previewEl.innerText, result, "Preview text shown to user");
+
+  const preview = dbg.selectors.getPreview(dbg.getState());
+  is(`${preview.result}`, result, "Preview.result");
+  is(preview.updating, false, "Preview.updating");
+  is(preview.expression, expression, "Preview.expression");
+}
+
+function assertPopup(dbg, { field, value, expression }) {
+  const previewEl = findElement(dbg, "popup");
+  is(previewEl.innerText, "", "Preview text shown to user");
+
+  const preview = dbg.selectors.getPreview(dbg.getState());
+
+  is(
+    `${preview.result.preview.ownProperties[field].value}`,
+    value,
+    "Preview.result"
+  );
+  is(preview.updating, false, "Preview.updating");
+  is(preview.expression, expression, "Preview.expression");
+}
+
+add_task(async function() {
+  const dbg = await initDebugger("doc-sourcemaps.html");
+  const { selectors: { getSelectedSource }, getState } = dbg;
+
+  await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
+  await selectSource(dbg, "times2");
+  await addBreakpoint(dbg, "times2", 2);
+
+  invokeInTab("keepMeAlive");
+  await waitForPaused(dbg);
+  await waitForSelectedSource(dbg, "times2");
+
+  const tooltipPreviewed = waitForDispatch(dbg, "SET_PREVIEW");
+  hoverAtPos(dbg, { line: 2, ch: 9 });
+
+  await tooltipPreviewed;
+  assertTooltip(dbg, { result: 4, expression: "x" });
+});
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -203,17 +203,17 @@ function waitForSources(dbg, ...sources)
     sources.map(url => {
       function sourceExists(state) {
         return getSources(state).some(s => {
           return (s.get("url") || "").includes(url);
         });
       }
 
       if (!sourceExists(store.getState())) {
-        return waitForState(dbg, sourceExists);
+        return waitForState(dbg, sourceExists, `source ${url}`);
       }
     })
   );
 }
 
 /**
  * Waits for a source to be loaded.
  *
@@ -308,18 +308,24 @@ function assertDebugLine(dbg, line) {
   }
 
   ok(
     lineInfo.wrapClass.includes("new-debug-line"),
     "Line is highlighted as paused"
   );
 
   const debugLine =
-    findElementWithSelector(dbg, ".new-debug-line") ||
-    findElementWithSelector(dbg, ".new-debug-line-error");
+    findElement(dbg, "debugLine") || findElement(dbg, "debugErrorLine");
+
+  is(
+    findAllElements(dbg, "debugLine").length +
+      findAllElements(dbg, "debugErrorLine").length,
+    1,
+    "There is only one line"
+  );
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
     const marker = markedSpans[0].marker;
     ok(
       marker.className.includes("debug-expression"),
@@ -930,16 +936,18 @@ const selectors = {
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
   frame: i => `.frames ul li:nth-child(${i})`,
   frames: ".frames ul li",
   gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
   menuitem: i => `menupopup menuitem:nth-child(${i})`,
   pauseOnExceptions: ".pause-exceptions",
   breakpoint: ".CodeMirror-code > .new-breakpoint",
   highlightLine: ".CodeMirror-code > .highlight-line",
+  debugLine: ".new-debug-line",
+  debugErrorLine: ".new-debug-line-error",
   codeMirror: ".CodeMirror",
   resume: ".resume.active",
   sourceTabs: ".source-tabs",
   stepOver: ".stepOver.active",
   stepOut: ".stepOut.active",
   stepIn: ".stepIn.active",
   toggleBreakpoints: ".breakpoints-toggle",
   prettyPrintButton: ".source-footer .prettyPrint",
--- a/devtools/client/inspector/grids/components/GridItem.js
+++ b/devtools/client/inspector/grids/components/GridItem.js
@@ -29,16 +29,17 @@ class GridItem extends PureComponent {
     };
   }
 
   constructor(props) {
     super(props);
     this.setGridColor = this.setGridColor.bind(this);
     this.translateNodeFrontToGrip = this.translateNodeFrontToGrip.bind(this);
     this.onGridCheckboxClick = this.onGridCheckboxClick.bind(this);
+    this.onGridInspectIconClick = this.onGridInspectIconClick.bind(this);
   }
 
   componentDidMount() {
     let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
     let tooltip = this.props.getSwatchColorPickerTooltip();
 
     let previousColor;
     tooltip.addSwatch(swatchEl, {
@@ -109,20 +110,25 @@ class GridItem extends PureComponent {
     let {
       grid,
       onToggleGridHighlighter,
     } = this.props;
 
     onToggleGridHighlighter(grid.nodeFront);
   }
 
+  onGridInspectIconClick(nodeFront) {
+    let { setSelectedNode } = this.props;
+    setSelectedNode(nodeFront, "layout-panel").catch(e => console.error(e));
+    nodeFront.scrollIntoView().catch(e => console.error(e));
+  }
+
   render() {
     let {
       grid,
-      setSelectedNode,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
     } = this.props;
     let { nodeFront } = grid;
 
     return dom.li(
       {},
       dom.label(
@@ -137,17 +143,17 @@ class GridItem extends PureComponent {
         ),
         Rep(
           {
             defaultRep: ElementNode,
             mode: MODE.TINY,
             object: this.translateNodeFrontToGrip(nodeFront),
             onDOMNodeMouseOut: () => onHideBoxModelHighlighter(),
             onDOMNodeMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
-            onInspectIconClick: () => setSelectedNode(nodeFront, "layout-panel"),
+            onInspectIconClick: () => this.onGridInspectIconClick(nodeFront),
           }
         )
       ),
       dom.div(
         {
           className: "grid-color-swatch",
           style: {
             backgroundColor: grid.color,
--- a/devtools/client/shared/vendor/WASMPARSER_UPGRADING
+++ b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
@@ -1,14 +1,14 @@
 # wasmparser version
 
-Current vesion is: 0.4.11
+Current vesion is: 0.5.2
 
 # Upgrade process
 
 1. Pull latest release from npm and extract WasmDis.js and WasmParser.js, e.g.
 
 ```
-curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.4.11.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
+curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.5.2.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
 ```
 
 2. Remove reference to source maps (last line)
 
--- a/devtools/client/shared/vendor/WasmDis.js
+++ b/devtools/client/shared/vendor/WasmDis.js
@@ -64,41 +64,107 @@ function formatFloat64(n) {
 }
 function memoryAddressToString(address, code) {
     var defaultAlignFlags;
     switch (code) {
         case 41 /* i64_load */:
         case 55 /* i64_store */:
         case 43 /* f64_load */:
         case 57 /* f64_store */:
+        case 65026 /* i64_atomic_wait */:
+        case 65041 /* i64_atomic_load */:
+        case 65048 /* i64_atomic_store */:
+        case 65055 /* i64_atomic_rmw_add */:
+        case 65062 /* i64_atomic_rmw_sub */:
+        case 65069 /* i64_atomic_rmw_and */:
+        case 65076 /* i64_atomic_rmw_or */:
+        case 65083 /* i64_atomic_rmw_xor */:
+        case 65090 /* i64_atomic_rmw_xchg */:
+        case 65097 /* i64_atomic_rmw_cmpxchg */:
             defaultAlignFlags = 3;
             break;
         case 40 /* i32_load */:
         case 52 /* i64_load32_s */:
         case 53 /* i64_load32_u */:
         case 54 /* i32_store */:
         case 62 /* i64_store32 */:
         case 42 /* f32_load */:
         case 56 /* f32_store */:
+        case 65024 /* atomic_wake */:
+        case 65025 /* i32_atomic_wait */:
+        case 65040 /* i32_atomic_load */:
+        case 65046 /* i64_atomic_load32_u */:
+        case 65047 /* i32_atomic_store */:
+        case 65053 /* i64_atomic_store32 */:
+        case 65054 /* i32_atomic_rmw_add */:
+        case 65060 /* i64_atomic_rmw32_u_add */:
+        case 65061 /* i32_atomic_rmw_sub */:
+        case 65067 /* i64_atomic_rmw32_u_sub */:
+        case 65068 /* i32_atomic_rmw_and */:
+        case 65074 /* i64_atomic_rmw32_u_and */:
+        case 65075 /* i32_atomic_rmw_or */:
+        case 65081 /* i64_atomic_rmw32_u_or */:
+        case 65082 /* i32_atomic_rmw_xor */:
+        case 65088 /* i64_atomic_rmw32_u_xor */:
+        case 65089 /* i32_atomic_rmw_xchg */:
+        case 65095 /* i64_atomic_rmw32_u_xchg */:
+        case 65096 /* i32_atomic_rmw_cmpxchg */:
+        case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
             defaultAlignFlags = 2;
             break;
         case 46 /* i32_load16_s */:
         case 47 /* i32_load16_u */:
         case 50 /* i64_load16_s */:
         case 51 /* i64_load16_u */:
         case 59 /* i32_store16 */:
         case 61 /* i64_store16 */:
+        case 65043 /* i32_atomic_load16_u */:
+        case 65045 /* i64_atomic_load16_u */:
+        case 65050 /* i32_atomic_store16 */:
+        case 65052 /* i64_atomic_store16 */:
+        case 65057 /* i32_atomic_rmw16_u_add */:
+        case 65059 /* i64_atomic_rmw16_u_add */:
+        case 65064 /* i32_atomic_rmw16_u_sub */:
+        case 65066 /* i64_atomic_rmw16_u_sub */:
+        case 65071 /* i32_atomic_rmw16_u_and */:
+        case 65073 /* i64_atomic_rmw16_u_and */:
+        case 65078 /* i32_atomic_rmw16_u_or */:
+        case 65080 /* i64_atomic_rmw16_u_or */:
+        case 65085 /* i32_atomic_rmw16_u_xor */:
+        case 65087 /* i64_atomic_rmw16_u_xor */:
+        case 65092 /* i32_atomic_rmw16_u_xchg */:
+        case 65094 /* i64_atomic_rmw16_u_xchg */:
+        case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
+        case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
             defaultAlignFlags = 1;
             break;
         case 44 /* i32_load8_s */:
         case 45 /* i32_load8_u */:
         case 48 /* i64_load8_s */:
         case 49 /* i64_load8_u */:
         case 58 /* i32_store8 */:
         case 60 /* i64_store8 */:
+        case 65042 /* i32_atomic_load8_u */:
+        case 65044 /* i64_atomic_load8_u */:
+        case 65049 /* i32_atomic_store8 */:
+        case 65051 /* i64_atomic_store8 */:
+        case 65056 /* i32_atomic_rmw8_u_add */:
+        case 65058 /* i64_atomic_rmw8_u_add */:
+        case 65063 /* i32_atomic_rmw8_u_sub */:
+        case 65065 /* i64_atomic_rmw8_u_sub */:
+        case 65070 /* i32_atomic_rmw8_u_and */:
+        case 65072 /* i64_atomic_rmw8_u_and */:
+        case 65077 /* i32_atomic_rmw8_u_or */:
+        case 65079 /* i64_atomic_rmw8_u_or */:
+        case 65084 /* i32_atomic_rmw8_u_xor */:
+        case 65086 /* i64_atomic_rmw8_u_xor */:
+        case 65091 /* i32_atomic_rmw8_u_xchg */:
+        case 65093 /* i64_atomic_rmw8_u_xchg */:
+        case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
+        case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
             defaultAlignFlags = 0;
             break;
     }
     if (address.flags == defaultAlignFlags)
         return !address.offset ? null : "offset=" + address.offset;
     if (!address.offset)
         return "align=" + (1 << address.flags);
     return "offset=" + (address.offset | 0) + " align=" + (1 << address.flags);
@@ -379,17 +445,17 @@ var WasmDisassembler = (function () {
             var line = this._lines[backrefLabel.line];
             this._lines[backrefLabel.line] = line.substring(0, backrefLabel.position) +
                 ' ' + backrefLabel.label + line.substring(backrefLabel.position);
             this._labelIndex++;
         }
         return backrefLabel.label || '' + depth;
     };
     WasmDisassembler.prototype.printOperator = function (operator) {
-        var code = operator.code;
+        var code = operator.code | 0;
         this.appendBuffer(getOperatorName(code));
         switch (code) {
             case 2 /* block */:
             case 3 /* loop */:
             case 4 /* if */:
                 if (this._labelMode !== LabelMode.Depth) {
                     var backrefLabel_1 = {
                         line: this._lines.length,
@@ -471,16 +537,82 @@ var WasmDisassembler = (function () {
             case 55 /* i64_store */:
             case 56 /* f32_store */:
             case 57 /* f64_store */:
             case 58 /* i32_store8 */:
             case 59 /* i32_store16 */:
             case 60 /* i64_store8 */:
             case 61 /* i64_store16 */:
             case 62 /* i64_store32 */:
+            case 65024 /* atomic_wake */:
+            case 65025 /* i32_atomic_wait */:
+            case 65026 /* i64_atomic_wait */:
+            case 65040 /* i32_atomic_load */:
+            case 65041 /* i64_atomic_load */:
+            case 65042 /* i32_atomic_load8_u */:
+            case 65043 /* i32_atomic_load16_u */:
+            case 65044 /* i64_atomic_load8_u */:
+            case 65045 /* i64_atomic_load16_u */:
+            case 65046 /* i64_atomic_load32_u */:
+            case 65047 /* i32_atomic_store */:
+            case 65048 /* i64_atomic_store */:
+            case 65049 /* i32_atomic_store8 */:
+            case 65050 /* i32_atomic_store16 */:
+            case 65051 /* i64_atomic_store8 */:
+            case 65052 /* i64_atomic_store16 */:
+            case 65053 /* i64_atomic_store32 */:
+            case 65054 /* i32_atomic_rmw_add */:
+            case 65055 /* i64_atomic_rmw_add */:
+            case 65056 /* i32_atomic_rmw8_u_add */:
+            case 65057 /* i32_atomic_rmw16_u_add */:
+            case 65058 /* i64_atomic_rmw8_u_add */:
+            case 65059 /* i64_atomic_rmw16_u_add */:
+            case 65060 /* i64_atomic_rmw32_u_add */:
+            case 65061 /* i32_atomic_rmw_sub */:
+            case 65062 /* i64_atomic_rmw_sub */:
+            case 65063 /* i32_atomic_rmw8_u_sub */:
+            case 65064 /* i32_atomic_rmw16_u_sub */:
+            case 65065 /* i64_atomic_rmw8_u_sub */:
+            case 65066 /* i64_atomic_rmw16_u_sub */:
+            case 65067 /* i64_atomic_rmw32_u_sub */:
+            case 65068 /* i32_atomic_rmw_and */:
+            case 65069 /* i64_atomic_rmw_and */:
+            case 65070 /* i32_atomic_rmw8_u_and */:
+            case 65071 /* i32_atomic_rmw16_u_and */:
+            case 65072 /* i64_atomic_rmw8_u_and */:
+            case 65073 /* i64_atomic_rmw16_u_and */:
+            case 65074 /* i64_atomic_rmw32_u_and */:
+            case 65075 /* i32_atomic_rmw_or */:
+            case 65076 /* i64_atomic_rmw_or */:
+            case 65077 /* i32_atomic_rmw8_u_or */:
+            case 65078 /* i32_atomic_rmw16_u_or */:
+            case 65079 /* i64_atomic_rmw8_u_or */:
+            case 65080 /* i64_atomic_rmw16_u_or */:
+            case 65081 /* i64_atomic_rmw32_u_or */:
+            case 65082 /* i32_atomic_rmw_xor */:
+            case 65083 /* i64_atomic_rmw_xor */:
+            case 65084 /* i32_atomic_rmw8_u_xor */:
+            case 65085 /* i32_atomic_rmw16_u_xor */:
+            case 65086 /* i64_atomic_rmw8_u_xor */:
+            case 65087 /* i64_atomic_rmw16_u_xor */:
+            case 65088 /* i64_atomic_rmw32_u_xor */:
+            case 65089 /* i32_atomic_rmw_xchg */:
+            case 65090 /* i64_atomic_rmw_xchg */:
+            case 65091 /* i32_atomic_rmw8_u_xchg */:
+            case 65092 /* i32_atomic_rmw16_u_xchg */:
+            case 65093 /* i64_atomic_rmw8_u_xchg */:
+            case 65094 /* i64_atomic_rmw16_u_xchg */:
+            case 65095 /* i64_atomic_rmw32_u_xchg */:
+            case 65096 /* i32_atomic_rmw_cmpxchg */:
+            case 65097 /* i64_atomic_rmw_cmpxchg */:
+            case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
+            case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
+            case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
+            case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
+            case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
                 var memoryAddress = memoryAddressToString(operator.memoryAddress, operator.code);
                 if (memoryAddress !== null) {
                     this.appendBuffer(' ');
                     this.appendBuffer(memoryAddress);
                 }
                 break;
             case 63 /* current_memory */:
             case 64 /* grow_memory */:
@@ -615,19 +747,22 @@ var WasmDisassembler = (function () {
                         default:
                             reader.skipSection();
                             break;
                     }
                     break;
                 case 15 /* MEMORY_SECTION_ENTRY */:
                     var memoryInfo = reader.result;
                     var memoryName = this._nameResolver.getMemoryName(this._memoryCount++, false);
-                    this.appendBuffer("  (memory " + memoryName + " " + memoryInfo.limits.initial);
-                    if (memoryInfo.limits.maximum !== undefined) {
-                        this.appendBuffer(" " + memoryInfo.limits.maximum);
+                    this.appendBuffer("  (memory " + memoryName + " ");
+                    if (memoryInfo.shared) {
+                        this.appendBuffer("(shared " + limitsToString(memoryInfo.limits) + ")");
+                    }
+                    else {
+                        this.appendBuffer(limitsToString(memoryInfo.limits));
                     }
                     this.appendBuffer(')');
                     this.newLine();
                     break;
                 case 14 /* TABLE_SECTION_ENTRY */:
                     var tableInfo = reader.result;
                     var tableName = this._nameResolver.getTableName(this._tableCount++, false);
                     this.appendBuffer("  (table " + tableName + " " + limitsToString(tableInfo.limits) + " " + typeToString(tableInfo.elementType) + ")");
@@ -676,17 +811,24 @@ var WasmDisassembler = (function () {
                         case 1 /* Table */:
                             var tableImportInfo = importInfo.type;
                             var tableName = this._nameResolver.getTableName(this._tableCount++, false);
                             this.appendBuffer(" (table " + tableName + " " + limitsToString(tableImportInfo.limits) + " " + typeToString(tableImportInfo.elementType) + ")");
                             break;
                         case 2 /* Memory */:
                             var memoryImportInfo = importInfo.type;
                             var memoryName = this._nameResolver.getMemoryName(this._memoryCount++, false);
-                            this.appendBuffer(" (memory " + memoryName + " " + limitsToString(memoryImportInfo.limits) + ")");
+                            this.appendBuffer(" (memory " + memoryName + " ");
+                            if (memoryImportInfo.shared) {
+                                this.appendBuffer("(shared " + limitsToString(memoryImportInfo.limits) + ")");
+                            }
+                            else {
+                                this.appendBuffer(limitsToString(memoryImportInfo.limits));
+                            }
+                            this.appendBuffer(')');
                             break;
                         case 3 /* Global */:
                             var globalImportInfo = importInfo.type;
                             var globalName = this._nameResolver.getGlobalName(this._globalCount++, false);
                             this.appendBuffer(" (global " + globalName + " " + globalTypeToString(globalImportInfo) + ")");
                             break;
                         default:
                             throw new Error("NYI other import types: " + importInfo.kind);
--- a/devtools/client/shared/vendor/WasmParser.js
+++ b/devtools/client/shared/vendor/WasmParser.js
@@ -203,21 +203,108 @@ var OperatorCode;
     OperatorCode[OperatorCode["f64_convert_u_i32"] = 184] = "f64_convert_u_i32";
     OperatorCode[OperatorCode["f64_convert_s_i64"] = 185] = "f64_convert_s_i64";
     OperatorCode[OperatorCode["f64_convert_u_i64"] = 186] = "f64_convert_u_i64";
     OperatorCode[OperatorCode["f64_promote_f32"] = 187] = "f64_promote_f32";
     OperatorCode[OperatorCode["i32_reinterpret_f32"] = 188] = "i32_reinterpret_f32";
     OperatorCode[OperatorCode["i64_reinterpret_f64"] = 189] = "i64_reinterpret_f64";
     OperatorCode[OperatorCode["f32_reinterpret_i32"] = 190] = "f32_reinterpret_i32";
     OperatorCode[OperatorCode["f64_reinterpret_i64"] = 191] = "f64_reinterpret_i64";
+    OperatorCode[OperatorCode["i32_extend8_s"] = 192] = "i32_extend8_s";
+    OperatorCode[OperatorCode["i32_extend16_s"] = 193] = "i32_extend16_s";
+    OperatorCode[OperatorCode["i64_extend8_s"] = 194] = "i64_extend8_s";
+    OperatorCode[OperatorCode["i64_extend16_s"] = 195] = "i64_extend16_s";
+    OperatorCode[OperatorCode["i64_extend32_s"] = 196] = "i64_extend32_s";
+    OperatorCode[OperatorCode["prefix_0xfc"] = 252] = "prefix_0xfc";
+    OperatorCode[OperatorCode["prefix_0xfe"] = 254] = "prefix_0xfe";
+    OperatorCode[OperatorCode["i32_trunc_s_sat_f32"] = 64512] = "i32_trunc_s_sat_f32";
+    OperatorCode[OperatorCode["i32_trunc_u_sat_f32"] = 64513] = "i32_trunc_u_sat_f32";
+    OperatorCode[OperatorCode["i32_trunc_s_sat_f64"] = 64514] = "i32_trunc_s_sat_f64";
+    OperatorCode[OperatorCode["i32_trunc_u_sat_f64"] = 64515] = "i32_trunc_u_sat_f64";
+    OperatorCode[OperatorCode["i64_trunc_s_sat_f32"] = 64516] = "i64_trunc_s_sat_f32";
+    OperatorCode[OperatorCode["i64_trunc_u_sat_f32"] = 64517] = "i64_trunc_u_sat_f32";
+    OperatorCode[OperatorCode["i64_trunc_s_sat_f64"] = 64518] = "i64_trunc_s_sat_f64";
+    OperatorCode[OperatorCode["i64_trunc_u_sat_f64"] = 64519] = "i64_trunc_u_sat_f64";
+    OperatorCode[OperatorCode["atomic_wake"] = 65024] = "atomic_wake";
+    OperatorCode[OperatorCode["i32_atomic_wait"] = 65025] = "i32_atomic_wait";
+    OperatorCode[OperatorCode["i64_atomic_wait"] = 65026] = "i64_atomic_wait";
+    OperatorCode[OperatorCode["i32_atomic_load"] = 65040] = "i32_atomic_load";
+    OperatorCode[OperatorCode["i64_atomic_load"] = 65041] = "i64_atomic_load";
+    OperatorCode[OperatorCode["i32_atomic_load8_u"] = 65042] = "i32_atomic_load8_u";
+    OperatorCode[OperatorCode["i32_atomic_load16_u"] = 65043] = "i32_atomic_load16_u";
+    OperatorCode[OperatorCode["i64_atomic_load8_u"] = 65044] = "i64_atomic_load8_u";
+    OperatorCode[OperatorCode["i64_atomic_load16_u"] = 65045] = "i64_atomic_load16_u";
+    OperatorCode[OperatorCode["i64_atomic_load32_u"] = 65046] = "i64_atomic_load32_u";
+    OperatorCode[OperatorCode["i32_atomic_store"] = 65047] = "i32_atomic_store";
+    OperatorCode[OperatorCode["i64_atomic_store"] = 65048] = "i64_atomic_store";
+    OperatorCode[OperatorCode["i32_atomic_store8"] = 65049] = "i32_atomic_store8";
+    OperatorCode[OperatorCode["i32_atomic_store16"] = 65050] = "i32_atomic_store16";
+    OperatorCode[OperatorCode["i64_atomic_store8"] = 65051] = "i64_atomic_store8";
+    OperatorCode[OperatorCode["i64_atomic_store16"] = 65052] = "i64_atomic_store16";
+    OperatorCode[OperatorCode["i64_atomic_store32"] = 65053] = "i64_atomic_store32";
+    OperatorCode[OperatorCode["i32_atomic_rmw_add"] = 65054] = "i32_atomic_rmw_add";
+    OperatorCode[OperatorCode["i64_atomic_rmw_add"] = 65055] = "i64_atomic_rmw_add";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_add"] = 65056] = "i32_atomic_rmw8_u_add";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_add"] = 65057] = "i32_atomic_rmw16_u_add";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_add"] = 65058] = "i64_atomic_rmw8_u_add";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_add"] = 65059] = "i64_atomic_rmw16_u_add";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_add"] = 65060] = "i64_atomic_rmw32_u_add";
+    OperatorCode[OperatorCode["i32_atomic_rmw_sub"] = 65061] = "i32_atomic_rmw_sub";
+    OperatorCode[OperatorCode["i64_atomic_rmw_sub"] = 65062] = "i64_atomic_rmw_sub";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_sub"] = 65063] = "i32_atomic_rmw8_u_sub";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_sub"] = 65064] = "i32_atomic_rmw16_u_sub";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_sub"] = 65065] = "i64_atomic_rmw8_u_sub";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_sub"] = 65066] = "i64_atomic_rmw16_u_sub";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_sub"] = 65067] = "i64_atomic_rmw32_u_sub";
+    OperatorCode[OperatorCode["i32_atomic_rmw_and"] = 65068] = "i32_atomic_rmw_and";
+    OperatorCode[OperatorCode["i64_atomic_rmw_and"] = 65069] = "i64_atomic_rmw_and";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_and"] = 65070] = "i32_atomic_rmw8_u_and";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_and"] = 65071] = "i32_atomic_rmw16_u_and";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_and"] = 65072] = "i64_atomic_rmw8_u_and";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_and"] = 65073] = "i64_atomic_rmw16_u_and";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_and"] = 65074] = "i64_atomic_rmw32_u_and";
+    OperatorCode[OperatorCode["i32_atomic_rmw_or"] = 65075] = "i32_atomic_rmw_or";
+    OperatorCode[OperatorCode["i64_atomic_rmw_or"] = 65076] = "i64_atomic_rmw_or";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_or"] = 65077] = "i32_atomic_rmw8_u_or";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_or"] = 65078] = "i32_atomic_rmw16_u_or";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_or"] = 65079] = "i64_atomic_rmw8_u_or";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_or"] = 65080] = "i64_atomic_rmw16_u_or";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_or"] = 65081] = "i64_atomic_rmw32_u_or";
+    OperatorCode[OperatorCode["i32_atomic_rmw_xor"] = 65082] = "i32_atomic_rmw_xor";
+    OperatorCode[OperatorCode["i64_atomic_rmw_xor"] = 65083] = "i64_atomic_rmw_xor";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_xor"] = 65084] = "i32_atomic_rmw8_u_xor";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_xor"] = 65085] = "i32_atomic_rmw16_u_xor";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_xor"] = 65086] = "i64_atomic_rmw8_u_xor";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_xor"] = 65087] = "i64_atomic_rmw16_u_xor";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_xor"] = 65088] = "i64_atomic_rmw32_u_xor";
+    OperatorCode[OperatorCode["i32_atomic_rmw_xchg"] = 65089] = "i32_atomic_rmw_xchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw_xchg"] = 65090] = "i64_atomic_rmw_xchg";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_xchg"] = 65091] = "i32_atomic_rmw8_u_xchg";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_xchg"] = 65092] = "i32_atomic_rmw16_u_xchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_xchg"] = 65093] = "i64_atomic_rmw8_u_xchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_xchg"] = 65094] = "i64_atomic_rmw16_u_xchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_xchg"] = 65095] = "i64_atomic_rmw32_u_xchg";
+    OperatorCode[OperatorCode["i32_atomic_rmw_cmpxchg"] = 65096] = "i32_atomic_rmw_cmpxchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw_cmpxchg"] = 65097] = "i64_atomic_rmw_cmpxchg";
+    OperatorCode[OperatorCode["i32_atomic_rmw8_u_cmpxchg"] = 65098] = "i32_atomic_rmw8_u_cmpxchg";
+    OperatorCode[OperatorCode["i32_atomic_rmw16_u_cmpxchg"] = 65099] = "i32_atomic_rmw16_u_cmpxchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw8_u_cmpxchg"] = 65100] = "i64_atomic_rmw8_u_cmpxchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw16_u_cmpxchg"] = 65101] = "i64_atomic_rmw16_u_cmpxchg";
+    OperatorCode[OperatorCode["i64_atomic_rmw32_u_cmpxchg"] = 65102] = "i64_atomic_rmw32_u_cmpxchg";
 })(OperatorCode = exports.OperatorCode || (exports.OperatorCode = {}));
 ;
 exports.OperatorCodeNames = [
-    "unreachable", "nop", "block", "loop", "if", "else", undefined, undefined, undefined, undefined, undefined, "end", "br", "br_if", "br_table", "return", "call", "call_indirect", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "drop", "select", undefined, undefined, undefined, undefined, "get_local", "set_local", "tee_local", "get_global", "set_global", undefined, undefined, undefined, "i32.load", "i64.load", "f32.load", "f64.load", "i32.load8_s", "i32.load8_u", "i32.load16_s", "i32.load16_u", "i64.load8_s", "i64.load8_u", "i64.load16_s", "i64.load16_u", "i64.load32_s", "i64.load32_u", "i32.store", "i64.store", "f32.store", "f64.store", "i32.store8", "i32.store16", "i64.store8", "i64.store16", "i64.store32", "current_memory", "grow_memory", "i32.const", "i64.const", "f32.const", "f64.const", "i32.eqz", "i32.eq", "i32.ne", "i32.lt_s", "i32.lt_u", "i32.gt_s", "i32.gt_u", "i32.le_s", "i32.le_u", "i32.ge_s", "i32.ge_u", "i64.eqz", "i64.eq", "i64.ne", "i64.lt_s", "i64.lt_u", "i64.gt_s", "i64.gt_u", "i64.le_s", "i64.le_u", "i64.ge_s", "i64.ge_u", "f32.eq", "f32.ne", "f32.lt", "f32.gt", "f32.le", "f32.ge", "f64.eq", "f64.ne", "f64.lt", "f64.gt", "f64.le", "f64.ge", "i32.clz", "i32.ctz", "i32.popcnt", "i32.add", "i32.sub", "i32.mul", "i32.div_s", "i32.div_u", "i32.rem_s", "i32.rem_u", "i32.and", "i32.or", "i32.xor", "i32.shl", "i32.shr_s", "i32.shr_u", "i32.rotl", "i32.rotr", "i64.clz", "i64.ctz", "i64.popcnt", "i64.add", "i64.sub", "i64.mul", "i64.div_s", "i64.div_u", "i64.rem_s", "i64.rem_u", "i64.and", "i64.or", "i64.xor", "i64.shl", "i64.shr_s", "i64.shr_u", "i64.rotl", "i64.rotr", "f32.abs", "f32.neg", "f32.ceil", "f32.floor", "f32.trunc", "f32.nearest", "f32.sqrt", "f32.add", "f32.sub", "f32.mul", "f32.div", "f32.min", "f32.max", "f32.copysign", "f64.abs", "f64.neg", "f64.ceil", "f64.floor", "f64.trunc", "f64.nearest", "f64.sqrt", "f64.add", "f64.sub", "f64.mul", "f64.div", "f64.min", "f64.max", "f64.copysign", "i32.wrap/i64", "i32.trunc_s/f32", "i32.trunc_u/f32", "i32.trunc_s/f64", "i32.trunc_u/f64", "i64.extend_s/i32", "i64.extend_u/i32", "i64.trunc_s/f32", "i64.trunc_u/f32", "i64.trunc_s/f64", "i64.trunc_u/f64", "f32.convert_s/i32", "f32.convert_u/i32", "f32.convert_s/i64", "f32.convert_u/i64", "f32.demote/f64", "f64.convert_s/i32", "f64.convert_u/i32", "f64.convert_s/i64", "f64.convert_u/i64", "f64.promote/f32", "i32.reinterpret/f32", "i64.reinterpret/f64", "f32.reinterpret/i32", "f64.reinterpret/i64", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined
+    "unreachable", "nop", "block", "loop", "if", "else", undefined, undefined, undefined, undefined, undefined, "end", "br", "br_if", "br_table", "return", "call", "call_indirect", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "drop", "select", undefined, undefined, undefined, undefined, "get_local", "set_local", "tee_local", "get_global", "set_global", undefined, undefined, undefined, "i32.load", "i64.load", "f32.load", "f64.load", "i32.load8_s", "i32.load8_u", "i32.load16_s", "i32.load16_u", "i64.load8_s", "i64.load8_u", "i64.load16_s", "i64.load16_u", "i64.load32_s", "i64.load32_u", "i32.store", "i64.store", "f32.store", "f64.store", "i32.store8", "i32.store16", "i64.store8", "i64.store16", "i64.store32", "current_memory", "grow_memory", "i32.const", "i64.const", "f32.const", "f64.const", "i32.eqz", "i32.eq", "i32.ne", "i32.lt_s", "i32.lt_u", "i32.gt_s", "i32.gt_u", "i32.le_s", "i32.le_u", "i32.ge_s", "i32.ge_u", "i64.eqz", "i64.eq", "i64.ne", "i64.lt_s", "i64.lt_u", "i64.gt_s", "i64.gt_u", "i64.le_s", "i64.le_u", "i64.ge_s", "i64.ge_u", "f32.eq", "f32.ne", "f32.lt", "f32.gt", "f32.le", "f32.ge", "f64.eq", "f64.ne", "f64.lt", "f64.gt", "f64.le", "f64.ge", "i32.clz", "i32.ctz", "i32.popcnt", "i32.add", "i32.sub", "i32.mul", "i32.div_s", "i32.div_u", "i32.rem_s", "i32.rem_u", "i32.and", "i32.or", "i32.xor", "i32.shl", "i32.shr_s", "i32.shr_u", "i32.rotl", "i32.rotr", "i64.clz", "i64.ctz", "i64.popcnt", "i64.add", "i64.sub", "i64.mul", "i64.div_s", "i64.div_u", "i64.rem_s", "i64.rem_u", "i64.and", "i64.or", "i64.xor", "i64.shl", "i64.shr_s", "i64.shr_u", "i64.rotl", "i64.rotr", "f32.abs", "f32.neg", "f32.ceil", "f32.floor", "f32.trunc", "f32.nearest", "f32.sqrt", "f32.add", "f32.sub", "f32.mul", "f32.div", "f32.min", "f32.max", "f32.copysign", "f64.abs", "f64.neg", "f64.ceil", "f64.floor", "f64.trunc", "f64.nearest", "f64.sqrt", "f64.add", "f64.sub", "f64.mul", "f64.div", "f64.min", "f64.max", "f64.copysign", "i32.wrap/i64", "i32.trunc_s/f32", "i32.trunc_u/f32", "i32.trunc_s/f64", "i32.trunc_u/f64", "i64.extend_s/i32", "i64.extend_u/i32", "i64.trunc_s/f32", "i64.trunc_u/f32", "i64.trunc_s/f64", "i64.trunc_u/f64", "f32.convert_s/i32", "f32.convert_u/i32", "f32.convert_s/i64", "f32.convert_u/i64", "f32.demote/f64", "f64.convert_s/i32", "f64.convert_u/i32", "f64.convert_s/i64", "f64.convert_u/i64", "f64.promote/f32", "i32.reinterpret/f32", "i64.reinterpret/f64", "f32.reinterpret/i32", "f64.reinterpret/i64", "i32.extend8_s", "i32.extend16_s", "i64.extend8_s", "i64.extend16_s", "i64.extend32_s", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined
 ];
+["i32.trunc_s:sat/f32", "i32.trunc_u:sat/f32", "i32.trunc_s:sat/f64", "i32.trunc_u:sat/f64", "i64.trunc_s:sat/f32", "i64.trunc_u:sat/f32", "i64.trunc_s:sat/f64", "i64.trunc_u:sat/f64"].forEach(function (s, i) {
+    exports.OperatorCodeNames[0xfc00 | i] = s;
+});
+["atomic.wake", "i32.atomic.wait", "i64.atomic.wait", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "i32.atomic.load", "i64.atomic.load", "i32.atomic.load8_u", "i32.atomic.load16_u", "i64.atomic.load8_u", "i64.atomic.load16_u", "i64.atomic.load32_u", "i32.atomic.store", "i64.atomic.store", "i32.atomic.store8", "i32.atomic.store16", "i64.atomic.store8", "i64.atomic.store16", "i64.atomic.store32", "i32.atomic.rmw.add", "i64.atomic.rmw.add", "i32.atomic.rmw8_u.add", "i32.atomic.rmw16_u.add", "i64.atomic.rmw8_u.add", "i64.atomic.rmw16_u.add", "i64.atomic.rmw32_u.add", "i32.atomic.rmw.sub", "i64.atomic.rmw.sub", "i32.atomic.rmw8_u.sub", "i32.atomic.rmw16_u.sub", "i64.atomic.rmw8_u.sub", "i64.atomic.rmw16_u.sub", "i64.atomic.rmw32_u.sub", "i32.atomic.rmw.and", "i64.atomic.rmw.and", "i32.atomic.rmw8_u.and", "i32.atomic.rmw16_u.and", "i64.atomic.rmw8_u.and", "i64.atomic.rmw16_u.and", "i64.atomic.rmw32_u.and", "i32.atomic.rmw.or", "i64.atomic.rmw.or", "i32.atomic.rmw8_u.or", "i32.atomic.rmw16_u.or", "i64.atomic.rmw8_u.or", "i64.atomic.rmw16_u.or", "i64.atomic.rmw32_u.or", "i32.atomic.rmw.xor", "i64.atomic.rmw.xor", "i32.atomic.rmw8_u.xor", "i32.atomic.rmw16_u.xor", "i64.atomic.rmw8_u.xor", "i64.atomic.rmw16_u.xor", "i64.atomic.rmw32_u.xor", "i32.atomic.rmw.xchg", "i64.atomic.rmw.xchg", "i32.atomic.rmw8_u.xchg", "i32.atomic.rmw16_u.xchg", "i64.atomic.rmw8_u.xchg", "i64.atomic.rmw16_u.xchg", "i64.atomic.rmw32_u.xchg", "i32.atomic.rmw.cmpxchg", "i64.atomic.rmw.cmpxchg", "i32.atomic.rmw8_u.cmpxchg", "i32.atomic.rmw16_u.cmpxchg", "i64.atomic.rmw8_u.cmpxchg", "i64.atomic.rmw16_u.cmpxchg", "i64.atomic.rmw32_u.cmpxchg"].forEach(function (s, i) {
+    exports.OperatorCodeNames[0xfe00 | i] = s;
+});
 var ExternalKind;
 (function (ExternalKind) {
     ExternalKind[ExternalKind["Function"] = 0] = "Function";
     ExternalKind[ExternalKind["Table"] = 1] = "Table";
     ExternalKind[ExternalKind["Memory"] = 2] = "Memory";
     ExternalKind[ExternalKind["Global"] = 3] = "Global";
 })(ExternalKind = exports.ExternalKind || (exports.ExternalKind = {}));
 var Type;
@@ -532,32 +619,34 @@ var BinaryReader = (function () {
         for (var i = 0; i < returnCount; i++)
             returnTypes[i] = this.readVarInt7();
         return {
             form: form,
             params: paramTypes,
             returns: returnTypes
         };
     };
-    BinaryReader.prototype.readResizableLimits = function () {
-        var flags = this.readVarUint32() >>> 0;
+    BinaryReader.prototype.readResizableLimits = function (maxPresent) {
         var initial = this.readVarUint32() >>> 0;
         var maximum;
-        if (flags & 0x1) {
+        if (maxPresent) {
             maximum = this.readVarUint32() >>> 0;
         }
-        return { flags: flags, initial: initial, maximum: maximum };
+        return { initial: initial, maximum: maximum };
     };
     BinaryReader.prototype.readTableType = function () {
         var elementType = this.readVarInt7();
-        var limits = this.readResizableLimits();
+        var flags = this.readVarUint32() >>> 0;
+        var limits = this.readResizableLimits(!!(flags & 0x01));
         return { elementType: elementType, limits: limits };
     };
     BinaryReader.prototype.readMemoryType = function () {
-        return { limits: this.readResizableLimits() };
+        var flags = this.readVarUint32() >>> 0;
+        var shared = !!(flags & 0x02);
+        return { limits: this.readResizableLimits(!!(flags & 0x01)), shared: shared };
     };
     BinaryReader.prototype.readGlobalType = function () {
         if (!this.hasVarIntBytes()) {
             return null;
         }
         var pos = this._pos;
         var contentType = this.readVarInt7();
         if (!this.hasVarIntBytes()) {
@@ -915,16 +1004,127 @@ var BinaryReader = (function () {
             type: type,
             offset: offset,
             index: index,
             addend: addend
         };
         this._sectionEntriesLeft--;
         return true;
     };
+    BinaryReader.prototype.readCodeOperator_0xfc = function () {
+        var code = this._data[this._pos++] | 0xfc00;
+        switch (code) {
+            case 64512 /* i32_trunc_s_sat_f32 */:
+            case 64513 /* i32_trunc_u_sat_f32 */:
+            case 64514 /* i32_trunc_s_sat_f64 */:
+            case 64515 /* i32_trunc_u_sat_f64 */:
+            case 64516 /* i64_trunc_s_sat_f32 */:
+            case 64517 /* i64_trunc_u_sat_f32 */:
+            case 64518 /* i64_trunc_s_sat_f64 */:
+            case 64519 /* i64_trunc_u_sat_f64 */:
+                break;
+            default:
+                this.error = new Error("Unknown operator: " + code);
+                this.state = -1 /* ERROR */;
+                return true;
+        }
+        this.result = { code: code,
+            blockType: undefined, brDepth: undefined, brTable: undefined,
+            funcIndex: undefined, typeIndex: undefined, localIndex: undefined,
+            globalIndex: undefined, memoryAddress: undefined, literal: undefined };
+        return true;
+    };
+    BinaryReader.prototype.readCodeOperator_0xfe = function () {
+        var MAX_CODE_OPERATOR_0XFE_SIZE = 11;
+        var pos = this._pos;
+        if (!this._eof && pos + MAX_CODE_OPERATOR_0XFE_SIZE > this._length) {
+            return false;
+        }
+        var code = this._data[this._pos++] | 0xfe00;
+        var memoryAddress;
+        switch (code) {
+            case 65024 /* atomic_wake */:
+            case 65025 /* i32_atomic_wait */:
+            case 65026 /* i64_atomic_wait */:
+            case 65040 /* i32_atomic_load */:
+            case 65041 /* i64_atomic_load */:
+            case 65042 /* i32_atomic_load8_u */:
+            case 65043 /* i32_atomic_load16_u */:
+            case 65044 /* i64_atomic_load8_u */:
+            case 65045 /* i64_atomic_load16_u */:
+            case 65046 /* i64_atomic_load32_u */:
+            case 65047 /* i32_atomic_store */:
+            case 65048 /* i64_atomic_store */:
+            case 65049 /* i32_atomic_store8 */:
+            case 65050 /* i32_atomic_store16 */:
+            case 65051 /* i64_atomic_store8 */:
+            case 65052 /* i64_atomic_store16 */:
+            case 65053 /* i64_atomic_store32 */:
+            case 65054 /* i32_atomic_rmw_add */:
+            case 65055 /* i64_atomic_rmw_add */:
+            case 65056 /* i32_atomic_rmw8_u_add */:
+            case 65057 /* i32_atomic_rmw16_u_add */:
+            case 65058 /* i64_atomic_rmw8_u_add */:
+            case 65059 /* i64_atomic_rmw16_u_add */:
+            case 65060 /* i64_atomic_rmw32_u_add */:
+            case 65061 /* i32_atomic_rmw_sub */:
+            case 65062 /* i64_atomic_rmw_sub */:
+            case 65063 /* i32_atomic_rmw8_u_sub */:
+            case 65064 /* i32_atomic_rmw16_u_sub */:
+            case 65065 /* i64_atomic_rmw8_u_sub */:
+            case 65066 /* i64_atomic_rmw16_u_sub */:
+            case 65067 /* i64_atomic_rmw32_u_sub */:
+            case 65068 /* i32_atomic_rmw_and */:
+            case 65069 /* i64_atomic_rmw_and */:
+            case 65070 /* i32_atomic_rmw8_u_and */:
+            case 65071 /* i32_atomic_rmw16_u_and */:
+            case 65072 /* i64_atomic_rmw8_u_and */:
+            case 65073 /* i64_atomic_rmw16_u_and */:
+            case 65074 /* i64_atomic_rmw32_u_and */:
+            case 65075 /* i32_atomic_rmw_or */:
+            case 65076 /* i64_atomic_rmw_or */:
+            case 65077 /* i32_atomic_rmw8_u_or */:
+            case 65078 /* i32_atomic_rmw16_u_or */:
+            case 65079 /* i64_atomic_rmw8_u_or */:
+            case 65080 /* i64_atomic_rmw16_u_or */:
+            case 65081 /* i64_atomic_rmw32_u_or */:
+            case 65082 /* i32_atomic_rmw_xor */:
+            case 65083 /* i64_atomic_rmw_xor */:
+            case 65084 /* i32_atomic_rmw8_u_xor */:
+            case 65085 /* i32_atomic_rmw16_u_xor */:
+            case 65086 /* i64_atomic_rmw8_u_xor */:
+            case 65087 /* i64_atomic_rmw16_u_xor */:
+            case 65088 /* i64_atomic_rmw32_u_xor */:
+            case 65089 /* i32_atomic_rmw_xchg */:
+            case 65090 /* i64_atomic_rmw_xchg */:
+            case 65091 /* i32_atomic_rmw8_u_xchg */:
+            case 65092 /* i32_atomic_rmw16_u_xchg */:
+            case 65093 /* i64_atomic_rmw8_u_xchg */:
+            case 65094 /* i64_atomic_rmw16_u_xchg */:
+            case 65095 /* i64_atomic_rmw32_u_xchg */:
+            case 65096 /* i32_atomic_rmw_cmpxchg */:
+            case 65097 /* i64_atomic_rmw_cmpxchg */:
+            case 65098 /* i32_atomic_rmw8_u_cmpxchg */:
+            case 65099 /* i32_atomic_rmw16_u_cmpxchg */:
+            case 65100 /* i64_atomic_rmw8_u_cmpxchg */:
+            case 65101 /* i64_atomic_rmw16_u_cmpxchg */:
+            case 65102 /* i64_atomic_rmw32_u_cmpxchg */:
+                memoryAddress = this.readMemoryImmediate();
+                break;
+            default:
+                this.error = new Error("Unknown operator: " + code);
+                this.state = -1 /* ERROR */;
+                return true;
+        }
+        this.result = { code: code,
+            blockType: undefined, brDepth: undefined, brTable: undefined,
+            funcIndex: undefined, typeIndex: undefined, localIndex: undefined,
+            globalIndex: undefined, memoryAddress: memoryAddress, literal: undefined };
+        return true;
+    };
     BinaryReader.prototype.readCodeOperator = function () {
         if (this.state === 30 /* CODE_OPERATOR */ &&
             this._pos >= this._functionRange.end) {
             this.skipFunctionBody();
             return this.read();
         }
         else if (this.state === 26 /* INIT_EXPRESSION_OPERATOR */ &&
             this.result &&
@@ -933,17 +1133,17 @@ var BinaryReader = (function () {
             this.result = null;
             return true;
         }
         var MAX_CODE_OPERATOR_SIZE = 11; // i64.const or load/store
         var pos = this._pos;
         if (!this._eof && pos + MAX_CODE_OPERATOR_SIZE > this._length) {
             return false;
         }
-        var code = this._data[this._pos++];
+        var code = this._data[this._pos++] | 0;
         var blockType, brDepth, brTable, funcIndex, typeIndex, localIndex, globalIndex, memoryAddress, literal, reserved;
         switch (code) {
             case 2 /* block */:
             case 3 /* loop */:
             case 4 /* if */:
                 blockType = this.readVarInt7();
                 break;
             case 12 /* br */:
@@ -1020,16 +1220,168 @@ var BinaryReader = (function () {
             case 67 /* f32_const */:
                 literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat32(this._pos, true);
                 this._pos += 4;
                 break;
             case 68 /* f64_const */:
                 literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat64(this._pos, true);
                 this._pos += 8;
                 break;
+            case 252 /* prefix_0xfc */:
+                if (this.readCodeOperator_0xfc()) {
+                    return true;
+                }
+                this._pos = pos;
+                return false;
+            case 254 /* prefix_0xfe */:
+                if (this.readCodeOperator_0xfe()) {
+                    return true;
+                }
+                this._pos = pos;
+                return false;
+            case 0 /* unreachable */:
+            case 1 /* nop */:
+            case 5 /* else */:
+            case 11 /* end */:
+            case 15 /* return */:
+            case 26 /* drop */:
+            case 27 /* select */:
+            case 69 /* i32_eqz */:
+            case 70 /* i32_eq */:
+            case 71 /* i32_ne */:
+            case 72 /* i32_lt_s */:
+            case 73 /* i32_lt_u */:
+            case 74 /* i32_gt_s */:
+            case 75 /* i32_gt_u */:
+            case 76 /* i32_le_s */:
+            case 77 /* i32_le_u */:
+            case 78 /* i32_ge_s */:
+            case 79 /* i32_ge_u */:
+            case 80 /* i64_eqz */:
+            case 81 /* i64_eq */:
+            case 82 /* i64_ne */:
+            case 83 /* i64_lt_s */:
+            case 84 /* i64_lt_u */:
+            case 85 /* i64_gt_s */:
+            case 86 /* i64_gt_u */:
+            case 87 /* i64_le_s */:
+            case 88 /* i64_le_u */:
+            case 89 /* i64_ge_s */:
+            case 90 /* i64_ge_u */:
+            case 91 /* f32_eq */:
+            case 92 /* f32_ne */:
+            case 93 /* f32_lt */:
+            case 94 /* f32_gt */:
+            case 95 /* f32_le */:
+            case 96 /* f32_ge */:
+            case 97 /* f64_eq */:
+            case 98 /* f64_ne */:
+            case 99 /* f64_lt */:
+            case 100 /* f64_gt */:
+            case 101 /* f64_le */:
+            case 102 /* f64_ge */:
+            case 103 /* i32_clz */:
+            case 104 /* i32_ctz */:
+            case 105 /* i32_popcnt */:
+            case 106 /* i32_add */:
+            case 107 /* i32_sub */:
+            case 108 /* i32_mul */:
+            case 109 /* i32_div_s */:
+            case 110 /* i32_div_u */:
+            case 111 /* i32_rem_s */:
+            case 112 /* i32_rem_u */:
+            case 113 /* i32_and */:
+            case 114 /* i32_or */:
+            case 115 /* i32_xor */:
+            case 116 /* i32_shl */:
+            case 117 /* i32_shr_s */:
+            case 118 /* i32_shr_u */:
+            case 119 /* i32_rotl */:
+            case 120 /* i32_rotr */:
+            case 121 /* i64_clz */:
+            case 122 /* i64_ctz */:
+            case 123 /* i64_popcnt */:
+            case 124 /* i64_add */:
+            case 125 /* i64_sub */:
+            case 126 /* i64_mul */:
+            case 127 /* i64_div_s */:
+            case 128 /* i64_div_u */:
+            case 129 /* i64_rem_s */:
+            case 130 /* i64_rem_u */:
+            case 131 /* i64_and */:
+            case 132 /* i64_or */:
+            case 133 /* i64_xor */:
+            case 134 /* i64_shl */:
+            case 135 /* i64_shr_s */:
+            case 136 /* i64_shr_u */:
+            case 137 /* i64_rotl */:
+            case 138 /* i64_rotr */:
+            case 139 /* f32_abs */:
+            case 140 /* f32_neg */:
+            case 141 /* f32_ceil */:
+            case 142 /* f32_floor */:
+            case 143 /* f32_trunc */:
+            case 144 /* f32_nearest */:
+            case 145 /* f32_sqrt */:
+            case 146 /* f32_add */:
+            case 147 /* f32_sub */:
+            case 148 /* f32_mul */:
+            case 149 /* f32_div */:
+            case 150 /* f32_min */:
+            case 151 /* f32_max */:
+            case 152 /* f32_copysign */:
+            case 153 /* f64_abs */:
+            case 154 /* f64_neg */:
+            case 155 /* f64_ceil */:
+            case 156 /* f64_floor */:
+            case 157 /* f64_trunc */:
+            case 158 /* f64_nearest */:
+            case 159 /* f64_sqrt */:
+            case 160 /* f64_add */:
+            case 161 /* f64_sub */:
+            case 162 /* f64_mul */:
+            case 163 /* f64_div */:
+            case 164 /* f64_min */:
+            case 165 /* f64_max */:
+            case 166 /* f64_copysign */:
+            case 167 /* i32_wrap_i64 */:
+            case 168 /* i32_trunc_s_f32 */:
+            case 169 /* i32_trunc_u_f32 */:
+            case 170 /* i32_trunc_s_f64 */:
+            case 171 /* i32_trunc_u_f64 */:
+            case 172 /* i64_extend_s_i32 */:
+            case 173 /* i64_extend_u_i32 */:
+            case 174 /* i64_trunc_s_f32 */:
+            case 175 /* i64_trunc_u_f32 */:
+            case 176 /* i64_trunc_s_f64 */:
+            case 177 /* i64_trunc_u_f64 */:
+            case 178 /* f32_convert_s_i32 */:
+            case 179 /* f32_convert_u_i32 */:
+            case 180 /* f32_convert_s_i64 */:
+            case 181 /* f32_convert_u_i64 */:
+            case 182 /* f32_demote_f64 */:
+            case 183 /* f64_convert_s_i32 */:
+            case 184 /* f64_convert_u_i32 */:
+            case 185 /* f64_convert_s_i64 */:
+            case 186 /* f64_convert_u_i64 */:
+            case 187 /* f64_promote_f32 */:
+            case 188 /* i32_reinterpret_f32 */:
+            case 189 /* i64_reinterpret_f64 */:
+            case 190 /* f32_reinterpret_i32 */:
+            case 191 /* f64_reinterpret_i64 */:
+            case 192 /* i32_extend8_s */:
+            case 193 /* i32_extend16_s */:
+            case 194 /* i64_extend8_s */:
+            case 195 /* i64_extend16_s */:
+            case 196 /* i64_extend32_s */:
+                break;
+            default:
+                this.error = new Error("Unknown operator: " + code);
+                this.state = -1 /* ERROR */;
+                return true;
         }
         this.result = { code: code,
             blockType: blockType, brDepth: brDepth, brTable: brTable,
             funcIndex: funcIndex, typeIndex: typeIndex, localIndex: localIndex,
             globalIndex: globalIndex, memoryAddress: memoryAddress, literal: literal };
         return true;
     };
     BinaryReader.prototype.readFunctionBody = function () {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -186,16 +186,17 @@
 #include "nsNetUtil.h"
 #include "nsObjectLoadingContent.h"
 #include "nsPingListener.h"
 #include "nsPoint.h"
 #include "nsQueryObject.h"
 #include "nsRect.h"
 #include "nsRefreshTimer.h"
 #include "nsSandboxFlags.h"
+#include "nsIServiceWorkerManager.h"
 #include "nsSHistory.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsSubDocumentFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsViewSourceHandler.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsWidgetsCID.h"
@@ -2787,17 +2788,17 @@ nsDocShell::MaybeCreateInitialClientSour
   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
   if (mInitialClientSource) {
     return;
   }
 
   // Don't pre-allocate the client when we are sandboxed.  The inherited
   // principal does not take sandboxing into account.
   // TODO: Refactor sandboxing principal code out so we can use it here.
-  if (!aPrincipal && (mSandboxFlags & SANDBOXED_ORIGIN)) {
+  if (!aPrincipal && mSandboxFlags) {
     return;
   }
 
   nsIPrincipal* principal = aPrincipal ? aPrincipal
                                        : GetInheritedPrincipal(false);
 
   // Sometimes there is no principal available when we are called from
   // CreateAboutBlankContentViewer.  For example, sometimes the principal
@@ -2829,28 +2830,44 @@ nsDocShell::MaybeCreateInitialClientSour
   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
   nsPIDOMWindowInner* parentInner =
     parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
   if (!parentInner) {
     return;
   }
 
+  // We're done if there is no parent controller or if this docshell
+  // is not permitted to control for some reason.
   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
-  if (controller.isNothing()) {
+  if (controller.isNothing() || !ServiceWorkerAllowedToControlWindow(nullptr)) {
+    return;
+  }
+
+  nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
+  if (!swm) {
     return;
   }
 
   // If the parent is controlled then propagate that controller to the
   // initial about:blank client as well.  This will set the controller
   // in the ClientManagerService in the parent.
-  RefPtr<ClientHandle> handle =
-    ClientManager::CreateHandle(mInitialClientSource->Info(),
-                                parentInner->EventTargetFor(TaskCategory::Other));
-  handle->Control(controller.ref());
+  //
+  // Note: If the registration is missing from the SWM we avoid setting
+  //       the controller on the client.  We can do this synchronously
+  //       for now since SWM is in the child process.  In the future
+  //       when SWM is in the parent process we will probably have to
+  //       always set the initial client source and then somehow clear
+  //       it if we find the registration is acutally gone.  Its also
+  //       possible this race only occurs in cases where the resulting
+  //       window is no longer exposed.  For example, in theory the SW
+  //       should not go away if our parent window is controlled.
+  if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
+    return;
+  }
 
   // Also mark the ClientSource as controlled directly in case script
   // immediately accesses navigator.serviceWorker.controller.
   mInitialClientSource->SetController(controller.ref());
 }
 
 Maybe<ClientInfo>
 nsDocShell::GetInitialClientInfo() const
@@ -14080,16 +14097,64 @@ nsDocShell::CanSetOriginAttributes()
         return false;
       }
     }
   }
 
   return true;
 }
 
+bool
+nsDocShell::ServiceWorkerAllowedToControlWindow(nsIURI* aURI)
+{
+  // NOTE: Ideally this method would call one of the
+  //       nsContentUtils::StorageAllowed*() methods to determine if the
+  //       interception is allowed.  Unfortunately we cannot safely do this
+  //       before the first window loads in the child process because the
+  //       permission manager might not have all its data yet.  Therefore,
+  //       we use this somewhat lame alternate implementation here.  Once
+  //       interception is moved to the parent process we should switch
+  //       to calling nsContentUtils::StorageAllowed*().  See bug 1428130.
+
+  if (UsePrivateBrowsing() || mSandboxFlags) {
+    return false;
+  }
+
+  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
+  uint32_t lifetimePolicy = nsContentUtils::CookiesLifetimePolicy();
+  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT ||
+      lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
+    return false;
+  }
+
+  if (!aURI || cookieBehavior == nsICookieService::BEHAVIOR_ACCEPT) {
+    return true;
+  }
+
+  nsCOMPtr<nsIDocShellTreeItem> parent;
+  GetSameTypeParent(getter_AddRefs(parent));
+  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
+                                                     : nullptr;
+  if (parentWindow) {
+    nsresult rv = NS_OK;
+    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+      do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+    if (thirdPartyUtil) {
+      bool isThirdPartyURI = true;
+      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI,
+                                              &isThirdPartyURI);
+      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
 nsresult
 nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs)
 {
   if (!CanSetOriginAttributes()) {
     return NS_ERROR_FAILURE;
   }
 
   AssertOriginAttributesMatchPrivateBrowsing();
@@ -14284,73 +14349,49 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
 #endif
 }
 
 NS_IMETHODIMP
 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
-  // No in private browsing
-  if (UsePrivateBrowsing()) {
-    return NS_OK;
-  }
-
-  if (mSandboxFlags) {
-    // If we're sandboxed, don't intercept.
-    return NS_OK;
-  }
-
-  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
-  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
-    // If cookies are disabled, don't intercept.
-    return NS_OK;
-  }
-
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (!swm) {
-    return NS_OK;
-  }
-
+
+  // For subresource requests we base our decision solely on the client's
+  // controller value.  Any settings that would have blocked service worker
+  // access should have been set before the initial navigation created the
+  // window.
   if (!aIsNonSubresourceRequest) {
     nsCOMPtr<nsIDocument> doc = GetDocument();
     if (!doc) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     ErrorResult rv;
     *aShouldIntercept = doc->GetController().isSome();
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
 
     return NS_OK;
   }
 
-  // If the user has set a cookie policy that restricts cookies, then
-  // avoid intercepting 3rd party iframes.
-  if (cookieBehavior != nsICookieService::BEHAVIOR_ACCEPT) {
-    nsCOMPtr<nsIDocShellTreeItem> parent;
-    GetSameTypeParent(getter_AddRefs(parent));
-    nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
-                                                       : nullptr;
-    if (parentWindow) {
-      nsresult rv = NS_OK;
-      nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-        do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool isThirdPartyURI = true;
-      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI, &isThirdPartyURI);
-      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
-        return NS_OK;
-      }
-    }
-  }
-
+  // For navigations, first check to see if we are allowed to control a
+  // window with the given URL.
+  if (!ServiceWorkerAllowedToControlWindow(aURI)) {
+    return NS_OK;
+  }
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    return NS_OK;
+  }
+
+  // We're allowed to control a window, so check with the ServiceWorkerManager
+  // for a matching service worker.
   nsCOMPtr<nsIPrincipal> principal =
     BasePrincipal::CreateCodebasePrincipal(aURI, mOriginAttributes);
   *aShouldIntercept = swm->IsAvailable(principal, aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -347,16 +347,29 @@ public:
    *
    * This method steals the data from the passed-in array.
    */
   void SetAncestorOuterWindowIDs(nsTArray<uint64_t>&& aAncestorOuterWindowIDs)
   {
     mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
   }
 
+  bool CanSetOriginAttributes();
+
+  // Determine if a service worker is allowed to control a window in this
+  // docshell with the given URL.  If there are any reasons it should not,
+  // this will return false.  If true is returned then the window *may* be
+  // controlled.  The caller must still consult either the parent controller
+  // or the ServiceWorkerManager to determine if a service worker should
+  // actually control the window.
+  //
+  // A nullptr URL is considered to be an about:blank window and will not
+  // trigger 3rd party iframe checks.
+  bool ServiceWorkerAllowedToControlWindow(nsIURI* aURI);
+
   const mozilla::OriginAttributes& GetOriginAttributes()
   {
     return mOriginAttributes;
   }
 
   // Determine whether this docshell corresponds to the given history entry,
   // via having a pointer to it in mOSHE or mLSHE.
   bool HasHistoryEntry(nsISHEntry* aEntry) const
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2932,16 +2932,21 @@ public:
 
   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
 
   static uint32_t CookiesBehavior()
   {
     return sCookiesBehavior;
   }
 
+  static uint32_t CookiesLifetimePolicy()
+  {
+    return sCookiesLifetimePolicy;
+  }
+
   // The order of these entries matters, as we use std::min for total ordering
   // of permissions. Private Browsing is considered to be more limiting
   // then session scoping
   enum class StorageAccess {
     // Don't allow access to the storage
     eDeny = 0,
     // Allow access to the storage, but only if it is secure to do so in a
     // private browsing context.
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5594,17 +5594,20 @@ nsDocument::DispatchContentLoadedEvents(
                                         NS_LITERAL_STRING("MozApplicationManifest"),
                                         true, true);
   }
 
   if (mMaybeServiceWorkerControlled) {
     using mozilla::dom::workers::ServiceWorkerManager;
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     if (swm) {
-      swm->MaybeCheckNavigationUpdate(this);
+      Maybe<ClientInfo> clientInfo = GetClientInfo();
+      if (clientInfo.isSome()) {
+        swm->MaybeCheckNavigationUpdate(clientInfo.ref());
+      }
     }
   }
 
   UnblockOnload(true);
 }
 
 void
 nsDocument::EndLoad()
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1781,41 +1781,74 @@ nsGlobalWindowInner::EnsureClientSource(
   // In this case we want to inherit this placeholder Client here.
   if (!mClientSource) {
     mClientSource = Move(initialClientSource);
     if (mClientSource) {
       newClientSource = true;
     }
   }
 
+  // Verify the final ClientSource principal matches the final document
+  // principal.  The ClientChannelHelper handles things like network
+  // redirects, but there are other ways the document principal can change.
+  // For example, if something sets the nsIChannel.owner property, then
+  // the final channel principal can be anything.  Unfortunately there is
+  // no good way to detect this until after the channel completes loading.
+  //
+  // For now we handle this just by reseting the ClientSource.  This will
+  // result in a new ClientSource with the correct principal being created.
+  // To APIs like ServiceWorker and Clients API it will look like there was
+  // an initial content page created that was then immediately replaced.
+  // This is pretty close to what we are actually doing.
+  if (mClientSource) {
+    nsCOMPtr<nsIPrincipal> clientPrincipal(mClientSource->Info().GetPrincipal());
+    if (!clientPrincipal || !clientPrincipal->Equals(mDoc->NodePrincipal())) {
+      mClientSource.reset();
+    }
+  }
+
   // If we don't have a reserved client or an initial client, then create
   // one now.  This can happen in certain cases where we avoid preallocating
   // the client in the docshell.  This mainly occurs in situations where
   // the principal is not clearly inherited from the parent; e.g. sandboxed
   // iframes, window.open(), etc.
+  //
+  // We also do this late ClientSource creation if the final document ended
+  // up with a different principal.
+  //
   // TODO: We may not be marking initial about:blank documents created
   //       this way as controlled by a service worker properly.  The
   //       controller should be coming from the same place as the inheritted
   //       principal.  We do this in docshell, but as mentioned we aren't
   //       smart enough to handle all cases yet.  For example, a
   //       window.open() with new URL should inherit the controller from
   //       the opener, but we probably don't handle that yet.
   if (!mClientSource) {
     mClientSource = ClientManager::CreateSource(ClientType::Window,
                                                 EventTargetFor(TaskCategory::Other),
                                                 mDoc->NodePrincipal());
     MOZ_DIAGNOSTIC_ASSERT(mClientSource);
     newClientSource = true;
+
+    // Note, we don't apply the loadinfo controller below if we create
+    // the ClientSource here.
   }
 
   // The load may have started controlling the Client as well.  If
   // so, mark it as controlled immediately here.  The actor may
   // or may not have been notified by the parent side about being
   // controlled yet.
-  if (loadInfo) {
+  //
+  // Note: We should be careful not to control a client that was created late.
+  //       These clients were not seen by the ServiceWorkerManager when it
+  //       marked the LoadInfo controlled and it won't know about them.  Its
+  //       also possible we are creating the client late due to the final
+  //       principal changing and these clients should definitely not be
+  //       controlled by a service worker with a different principal.
+  else if (loadInfo) {
     const Maybe<ServiceWorkerDescriptor> controller = loadInfo->GetController();
     if (controller.isSome()) {
       mClientSource->SetController(controller.ref());
     }
 
     // We also have to handle the case where te initial about:blank is
     // controlled due to inheritting the service worker from its parent,
     // but the actual nsIChannel load is not covered by any service worker.
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3112,21 +3112,20 @@ nsGlobalWindowOuter::GetStatusOuter(nsAS
 }
 
 void
 nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus)
 {
   mStatus = aStatus;
 
   /*
-   * If caller is not chrome and dom.disable_window_status_change is true,
-   * prevent propagating window.status to the UI by exiting early
+   * If caller is not chrome, prevent propagating window.status to the UI by
+   * exiting early.
    */
-
-  if (!CanSetProperty("dom.disable_window_status_change")) {
+  if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return;
   }
 
   nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
   if (browserChrome) {
     browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
                              PromiseFlatString(aStatus).get());
   }
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -574,16 +574,17 @@ skip-if = toolkit == 'android' #bug 6870
 [test_bug976673.html]
 [test_bug982153.html]
 [test_bug999456.html]
 [test_bug1022229.html]
 [test_bug1025933.html]
 skip-if = stylo # bug 1293844
 [test_bug1037687.html]
 skip-if = stylo # bug 1293844
+support-files = test_bug1037687_subframe.html
 [test_bug1043106.html]
 [test_bug1057176.html]
 [test_bug1060938.html]
 [test_bug1064481.html]
 [test_bug1070015.html]
 [test_bug1075702.html]
 [test_bug1091883.html]
 [test_bug1101364.html]
--- a/dom/base/test/test_bug1025933.html
+++ b/dom/base/test/test_bug1025933.html
@@ -10,28 +10,38 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1025933 **/
 
   SimpleTest.waitForExplicitFinish();
 
   function test() {
-    var s = document.getElementById("host").createShadowRoot();
-    s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
-    var el = s.firstElementChild;
-    is(el.clientWidth, 100);
-    is(el.clientHeight, 100);
-    SimpleTest.finish();
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, function() {
+      var iframe = document.createElement('iframe');
+      iframe.srcdoc = '<div id="content"> <div id="host"></div </div>';
+
+      iframe.onload = function() {
+        var s = iframe.contentDocument.getElementById("host").attachShadow({mode: 'open'});
+        s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
+        var el = s.firstElementChild;
+        is(el.clientWidth, 100);
+        is(el.clientHeight, 100);
+        SimpleTest.finish();
+      }
+
+      document.body.appendChild(iframe);
+    });
   }
 
   </script>
 </head>
 <body onload="test()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1025933">Mozilla Bug 1025933</a>
 <p id="display"></p>
-<div id="content">
-  <div id="host"></div>
-</div>
 <pre id="test">
 </pre>
 </body>
 </html>
--- a/dom/base/test/test_bug1037687.html
+++ b/dom/base/test/test_bug1037687.html
@@ -3,61 +3,34 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1037687
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1037687</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 1037687 **/
-
-  SimpleTest.waitForExplicitFinish();
-
-  var host;
-  var sr;
-  var embed;
-  var object;
-  var iframe;
-  var resourceLoadCount = 0;
-
-  function resourceLoaded(event) {
-    ++resourceLoadCount;
-    ok(true, event.target + " got " + event.load);
-    if (resourceLoadCount == 3) {
-      SimpleTest.finish();
-    }
-  }
-
-  function createResource(sr, type) {
-    var el = document.createElement(type);
-    var attrName = type == "object" ? "data" : "src";
-    el.setAttribute(attrName, "file_mozfiledataurl_img.jpg");
-    el.onload = resourceLoaded;
-    var info = document.createElement("div");
-    info.textContent = type;
-    sr.appendChild(info);
-    sr.appendChild(el);
-  }
-
-  function test() {
-    host = document.getElementById("host");
-    sr = host.createShadowRoot();
-    embed = createResource(sr, "embed");
-    object = createResource(sr, "object");
-    iframe = createResource(sr, "iframe");
-  }
-
-  </script>
 </head>
-<body onload="test()">
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1037687">Mozilla Bug 1037687</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
-<div id="host"></div>
+<script type="application/javascript">
+  /** Test for Bug 1037687 **/
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.enabled", true]
+    ]
+  }, function() {
+    // This test loads in an iframe, to ensure that the element instance is
+    // loaded with the correct value of the preference.
+    let iframe = document.createElement("iframe");
+    iframe.src = "test_bug1037687_subframe.html";
+    document.body.appendChild(iframe);
+  });
+</script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1037687_subframe.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+  var SimpleTest = window.parent.SimpleTest;
+  var ok = window.parent.ok;
+  var is = window.parent.is;
+
+  var host;
+  var sr;
+  var embed;
+  var object;
+  var iframe;
+  var resourceLoadCount = 0;
+
+  function resourceLoaded(event) {
+    ++resourceLoadCount;
+    ok(true, event.target + " got " + event.load);
+    if (resourceLoadCount == 3) {
+      SimpleTest.finish();
+    }
+  }
+
+  function createResource(sr, type) {
+    var el = document.createElement(type);
+    var attrName = type == "object" ? "data" : "src";
+    el.setAttribute(attrName, "file_mozfiledataurl_img.jpg");
+    el.onload = resourceLoaded;
+    var info = document.createElement("div");
+    info.textContent = type;
+    sr.appendChild(info);
+    sr.appendChild(el);
+  }
+
+  function test() {
+    host = document.getElementById("host");
+    sr = host.attachShadow({mode: 'open'});
+    embed = createResource(sr, "embed");
+    object = createResource(sr, "object");
+    iframe = createResource(sr, "iframe");
+  }
+</script>
+</head>
+<body onload="test()">
+<div id="host"></div>
+</body>
+</html>
--- a/dom/clients/manager/ClientHandle.cpp
+++ b/dom/clients/manager/ClientHandle.cpp
@@ -60,16 +60,26 @@ ClientHandle::StartOp(const ClientOpCons
   }, [promise] {
     promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
   });
 
   RefPtr<ClientOpPromise> ref = promise.get();
   return ref.forget();
 }
 
+void
+ClientHandle::OnShutdownThing()
+{
+  NS_ASSERT_OWNINGTHREAD(ClientHandle);
+  if (!mDetachPromise) {
+    return;
+  }
+  mDetachPromise->Resolve(true, __func__);
+}
+
 ClientHandle::ClientHandle(ClientManager* aManager,
                            nsISerialEventTarget* aSerialEventTarget,
                            const ClientInfo& aClientInfo)
   : mManager(aManager)
   , mSerialEventTarget(aSerialEventTarget)
   , mClientInfo(aClientInfo)
 {
   MOZ_DIAGNOSTIC_ASSERT(mManager);
@@ -177,10 +187,26 @@ ClientHandle::PostMessage(StructuredClon
     }, [outerPromise](const ClientOpResult& aResult) {
       outerPromise->Reject(aResult.get_nsresult(), __func__);
     });
 
   ref = outerPromise.get();
   return ref.forget();
 }
 
+RefPtr<GenericPromise>
+ClientHandle::OnDetach()
+{
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+
+  if (!mDetachPromise) {
+    mDetachPromise = new GenericPromise::Private(__func__);
+    if (IsShutdown()) {
+      mDetachPromise->Resolve(true, __func__);
+    }
+  }
+
+  RefPtr<GenericPromise> ref(mDetachPromise);
+  return Move(ref);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientHandle.h
+++ b/dom/clients/manager/ClientHandle.h
@@ -37,26 +37,31 @@ class StructuredCloneData;
 // is destroyed, but this could be added in the future.
 class ClientHandle final : public ClientThing<ClientHandleChild>
 {
   friend class ClientManager;
   friend class ClientHandleChild;
 
   RefPtr<ClientManager> mManager;
   nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
+  RefPtr<GenericPromise::Private> mDetachPromise;
   ClientInfo mClientInfo;
 
   ~ClientHandle();
 
   void
   Shutdown();
 
   already_AddRefed<ClientOpPromise>
   StartOp(const ClientOpConstructorArgs& aArgs);
 
+  // ClientThing interface
+  void
+  OnShutdownThing() override;
+
   // Private methods called by ClientHandleChild
   void
   ExecutionReady(const ClientInfo& aClientInfo);
 
   // Private methods called by ClientManager
   ClientHandle(ClientManager* aManager,
                nsISerialEventTarget* aSerialEventTarget,
                const ClientInfo& aClientInfo);
@@ -85,15 +90,26 @@ public:
   // dispatched to the Client's navigator.serviceWorker event target.  The
   // returned promise will resolve if the MessageEvent is dispatched or if
   // it triggers an error handled in the Client's context.  Other errors
   // will result in the promise rejecting.
   RefPtr<GenericPromise>
   PostMessage(ipc::StructuredCloneData& aData,
               const ServiceWorkerDescriptor& aSource);
 
+  // Return a Promise that resolves when the ClientHandle object is detached
+  // from its remote actors.  This will happen if the ClientSource is destroyed
+  // and triggers the cleanup of the handle actors.  It will also naturally
+  // happen when the ClientHandle is de-referenced and tears down its own
+  // actors.
+  //
+  // Note: This method can only be called on the ClientHandle owning thread,
+  //       but the MozPromise lets you Then() to another thread.
+  RefPtr<GenericPromise>
+  OnDetach();
+
   NS_INLINE_DECL_REFCOUNTING(ClientHandle);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientHandle_h
--- a/dom/clients/manager/ClientInfo.cpp
+++ b/dom/clients/manager/ClientInfo.cpp
@@ -2,21 +2,23 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientInfo.h"
 
 #include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/ipc/BackgroundUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::PrincipalInfo;
+using mozilla::ipc::PrincipalInfoToPrincipal;
 
 ClientInfo::ClientInfo(const nsID& aId,
                        ClientType aType,
                        const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                        const TimeStamp& aCreationTime)
   : mData(MakeUnique<IPCClientInfo>(aId, aType, aPrincipalInfo, aCreationTime,
                                     EmptyCString(),
                                     mozilla::dom::FrameType::None))
@@ -133,10 +135,18 @@ ClientInfo::IsPrivateBrowsing() const
     default:
     {
       // clients should never be expanded principals
       MOZ_CRASH("unexpected principal type!");
     }
   }
 }
 
+nsCOMPtr<nsIPrincipal>
+ClientInfo::GetPrincipal() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIPrincipal> ref = PrincipalInfoToPrincipal(PrincipalInfo());
+  return Move(ref);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientInfo.h
+++ b/dom/clients/manager/ClientInfo.h
@@ -90,14 +90,19 @@ public:
 
   // Convert to the ipdl generated type.
   const IPCClientInfo&
   ToIPC() const;
 
   // Determine if the client is in private browsing mode.
   bool
   IsPrivateBrowsing() const;
+
+  // Get a main-thread nsIPrincipal for the client.  This may return nullptr
+  // if the PrincipalInfo() fails to deserialize for some reason.
+  nsCOMPtr<nsIPrincipal>
+  GetPrincipal() const;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientInfo_h
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -201,16 +201,24 @@ ClientSource::WorkerExecutionReady(Worke
 {
   MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   if (IsShutdown()) {
     return;
   }
 
+  // A client without access to storage should never be controlled by
+  // a service worker.  Check this here in case we were controlled before
+  // execution ready.  We can't reliably determine what our storage policy
+  // is before execution ready, unfortunately.
+  if (mController.isSome()) {
+    MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate->IsStorageAllowed());
+  }
+
   // Its safe to store the WorkerPrivate* here because the ClientSource
   // is explicitly destroyed by WorkerPrivate before exiting its run loop.
   MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
   mOwner = AsVariant(aWorkerPrivate);
 
   ClientSourceExecutionReadyArgs args(
     aWorkerPrivate->GetLocationInfo().mHref,
     FrameType::None);
@@ -230,16 +238,25 @@ ClientSource::WindowExecutionReady(nsPID
     return NS_OK;
   }
 
   nsIDocument* doc = aInnerWindow->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  // A client without access to storage should never be controlled by
+  // a service worker.  Check this here in case we were controlled before
+  // execution ready.  We can't reliably determine what our storage policy
+  // is before execution ready, unfortunately.
+  if (mController.isSome()) {
+    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(aInnerWindow) ==
+                          nsContentUtils::StorageAccess::eAllow);
+  }
+
   // Don't use nsAutoCString here since IPC requires a full nsCString anyway.
   nsCString spec;
 
   nsIURI* uri = doc->GetOriginalURI();
   if (uri) {
     nsresult rv = uri->GetSpec(spec);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -285,16 +302,20 @@ ClientSource::DocShellExecutionReady(nsI
     return NS_OK;
   }
 
   nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
   if (NS_WARN_IF(!outer)) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  // Note: We don't assert storage access for a controlled client.  If
+  // the about:blank actually gets used then WindowExecutionReady() will
+  // get called which asserts storage access.
+
   // TODO: dedupe this with WindowExecutionReady
   FrameType frameType = FrameType::Top_level;
   if (!outer->IsTopLevelWindow()) {
     frameType = FrameType::Nested;
   } else if(outer->HadOriginalOpener()) {
     frameType = FrameType::Auxiliary;
   }
 
@@ -355,16 +376,26 @@ ClientSource::SetController(const Servic
 {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
 
   // A client in private browsing mode should never be controlled by
   // a service worker.  The principal origin attributes should guarantee
   // this invariant.
   MOZ_DIAGNOSTIC_ASSERT(!mClientInfo.IsPrivateBrowsing());
 
+  // A client without access to storage should never be controlled a
+  // a service worker.  If we are already execution ready with a real
+  // window or worker, then verify assert the storage policy is correct.
+  if (GetInnerWindow()) {
+    MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::StorageAllowedForWindow(GetInnerWindow()) ==
+                          nsContentUtils::StorageAccess::eAllow);
+  } else if (GetWorkerPrivate()) {
+    MOZ_DIAGNOSTIC_ASSERT(GetWorkerPrivate()->IsStorageAllowed());
+  }
+
   if (mController.isSome() && mController.ref() == aServiceWorker) {
     return;
   }
 
   mController.reset();
   mController.emplace(aServiceWorker);
 
   RefPtr<ServiceWorkerContainer> swc;
--- a/dom/clients/manager/ClientThing.h
+++ b/dom/clients/manager/ClientThing.h
@@ -85,31 +85,42 @@ protected:
 
     // If we are shutdown before the actor, then clear the weak references
     // between the actor and the thing.
     if (mActor) {
       mActor->RevokeOwner(this);
       mActor->MaybeStartTeardown();
       mActor = nullptr;
     }
+
+    OnShutdownThing();
+  }
+
+  // Allow extending classes to take action when shutdown.
+  virtual void
+  OnShutdownThing()
+  {
+    // by default do nothing
   }
 
 public:
   // Clear the weak references between the thing and its IPC actor.
   void
   RevokeActor(ActorType* aActor)
   {
     MOZ_DIAGNOSTIC_ASSERT(mActor);
     MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
     mActor->RevokeOwner(this);
     mActor = nullptr;
 
     // Also consider the ClientThing shutdown.  We simply set the flag
     // instead of calling ShutdownThing() to avoid calling MaybeStartTeardown()
     // on the destroyed actor.
     mShutdown = true;
+
+    OnShutdownThing();
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientThing_h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1258,33 +1258,16 @@ IMEContentObserver::ClearAddedNodesDurin
 {
   mFirstAddedContainer = mLastAddedContainer = nullptr;
   mFirstAddedContent = mLastAddedContent = nullptr;
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
      ", finished storing consecutive nodes", this));
 }
 
-// static
-nsIContent*
-IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
-{
-  if (!aParent->HasChildren() || aOffset < 0 ||
-      aOffset >= static_cast<int32_t>(aParent->Length())) {
-    return nullptr;
-  }
-  if (!aOffset) {
-    return aParent->GetFirstChild();
-  }
-  if (aOffset == static_cast<int32_t>(aParent->Length() - 1)) {
-    return aParent->GetLastChild();
-  }
-  return aParent->GetChildAt_Deprecated(aOffset);
-}
-
 bool
 IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
                                               nsIContent* aChild) const
 {
   MOZ_ASSERT(aParent);
   MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
   MOZ_ASSERT(mRootContent);
   MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -183,22 +183,16 @@ private:
   void OnIMEReceivedFocus();
   void Clear();
   bool IsObservingContent(nsPresContext* aPresContext,
                           nsIContent* aContent) const;
   bool IsReflowLocked() const;
   bool IsSafeToNotifyIME() const;
   bool IsEditorComposing() const;
 
-  /**
-   * nsINode::GetChildAt() is slow.  So, this avoids to use it if it's
-   * first child or last child of aParent.
-   */
-  static nsIContent* GetChildNode(nsINode* aParent, int32_t aOffset);
-
   // Following methods are called by DocumentObserver when
   // beginning to update the contents and ending updating the contents.
   void BeginDocumentUpdate();
   void EndDocumentUpdate();
 
   // Following methods manages added nodes during a document change.
 
   /**
--- a/dom/events/test/test_bug1079236.html
+++ b/dom/events/test/test_bug1079236.html
@@ -8,48 +8,58 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 1079236</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1079236 **/
 
-SimpleTest.waitForExplicitFinish();
-SimpleTest.waitForFocus(runTests);
+  function runTests() {
+    var iframe = document.createElement('iframe');
+    document.body.appendChild(iframe);
+    iframe.contentDocument.body.innerHTML = '<div id="content"></div>';
 
-function runTests() {
-  var c = document.getElementById("content");
-  var sr = c.createShadowRoot();
-  sr.innerHTML = "<input type='file'" + ">";
-  var file = sr.firstChild;
-  is(file.type, "file");
-  file.offsetLeft; // Flush layout because dispatching mouse events.
-  document.body.onmousemove = function(e) {
-    is(e.target, c, "Event target should be the element in non-Shadow DOM");
-    if (e.originalTarget == file) {
-      is(e.originalTarget, file,
-         "type='file' implementation doesn't seem to have native anonymous content");
-    } else {
-      var wrapped = SpecialPowers.wrap(e.originalTarget);
-      isnot(wrapped, file, "Shouldn't have the same event.target and event.originalTarget");
+    var c = iframe.contentDocument.getElementById("content");
+    var sr = c.attachShadow({mode: 'open'});
+    sr.innerHTML = "<input type='file'" + ">";
+    var file = sr.firstChild;
+    is(file.type, "file");
+    file.offsetLeft; // Flush layout because dispatching mouse events.
+    iframe.contentDocument.body.onmousemove = function(e) {
+      is(e.target, c, "Event target should be the element in non-Shadow DOM");
+      if (e.originalTarget == file) {
+        is(e.originalTarget, file,
+           "type='file' implementation doesn't seem to have native anonymous content");
+      } else {
+        var wrapped = SpecialPowers.wrap(e.originalTarget);
+        isnot(wrapped, file, "Shouldn't have the same event.target and event.originalTarget");
+      }
+
+      ok(!("composedTarget" in e), "Events shouldn't have composedTarget in non-chrome context!");
+      e = SpecialPowers.wrap(e);
+      var composedTarget = SpecialPowers.unwrap(e.composedTarget);
+      ok(composedTarget, file, "composedTarget should be the file object.");
+
+      SimpleTest.finish();
     }
 
-    ok(!("composedTarget" in e), "Events shouldn't have composedTarget in non-chrome context!");
-    e = SpecialPowers.wrap(e);
-    var composedTarget = SpecialPowers.unwrap(e.composedTarget);
-    ok(composedTarget, file, "composedTarget should be the file object.");
-
-    SimpleTest.finish();
+    var r = file.getBoundingClientRect();
+    synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"}, iframe.contentWindow);
+    iframe.contentDocument.body.onmousemove = null;
   }
 
-  var r = file.getBoundingClientRect();
-  synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"} );
-  document.body.onmousemove = null;
-}
+  SimpleTest.waitForExplicitFinish();
+  SimpleTest.waitForFocus(() => {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, runTests);
+  });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079236">Mozilla Bug 1079236</a>
 <p id="display"></p>
 <div id="content">
 
--- a/dom/events/test/test_bug1145910.html
+++ b/dom/events/test/test_bug1145910.html
@@ -5,44 +5,54 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for Bug 1145910</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<style>
-div:active {
-  color: rgb(0, 255, 0);
-}
-</style>
-<div id="host">Foo</div>
+
 <script type="application/javascript">
 
 /** Test for Bug 1145910 **/
-SimpleTest.waitForExplicitFinish();
+
+function runTests() {
 
-SimpleTest.waitForFocus(function() {
-  var host = document.getElementById("host");
-  var shadow = host.createShadowRoot();
+  var iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  iframe.contentDocument.body.innerHTML =
+    '<style> div:active { color: rgb(0, 255, 0); } </style> <div id="host">Foo</div>';
+
+  var host = iframe.contentDocument.getElementById("host");
+  var shadow = host.attachShadow({mode: 'open'});
   shadow.innerHTML = '<style>div:active { color: rgb(0, 255, 0); }</style><div id="inner">Bar</div>';
   var inner = shadow.getElementById("inner");
+  var iframeWin = iframe.contentWindow;
 
-  is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+  is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
 
-  synthesizeMouseAtCenter(host, { type: "mousedown" });
+  synthesizeMouseAtCenter(host, { type: "mousedown" }, iframeWin);
 
-  is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
-  is(window.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+  is(iframeWin.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
 
-  synthesizeMouseAtCenter(host, { type: "mouseup" });
+  synthesizeMouseAtCenter(host, { type: "mouseup" }, iframeWin);
 
-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
-  is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+  is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
 
   SimpleTest.finish();
+};
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.enabled", true]
+    ]
+  }, runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/events/test/test_bug1150308.html
+++ b/dom/events/test/test_bug1150308.html
@@ -5,37 +5,49 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for Bug 1150308</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<div id="host"><span id="distributeme">Foo</span></div>
 <script type="application/javascript">
 
 /** Test for Bug 1150308 **/
-SimpleTest.waitForExplicitFinish();
 
-SimpleTest.waitForFocus(function() {
-  var host = document.getElementById("host");
-  var shadow = host.createShadowRoot();
-  shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><content></content></div>';
-  var inner = shadow.getElementById("inner");
-  var distributed = document.getElementById("distributeme");
+function runTests() {
+  var iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  iframe.contentDocument.body.innerHTML =
+    '<div id="host"><span id="distributeme">Foo</span></div>';
 
-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
-
-  synthesizeMouseAtCenter(distributed, { type: "mousedown" });
+  var host = iframe.contentDocument.getElementById("host");
+  var shadow = host.attachShadow({mode: 'open'});
+  shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><slot></slot></div>';
+  var inner = shadow.getElementById("inner");
+  var distributed = iframe.contentDocument.getElementById("distributeme");
+  var iframeWin = iframe.contentWindow;
 
-  is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+
+  synthesizeMouseAtCenter(distributed, { type: "mousedown" }, iframeWin);
 
-  synthesizeMouseAtCenter(distributed, { type: "mouseup" });
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
 
-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+  synthesizeMouseAtCenter(distributed, { type: "mouseup" }, iframeWin);
+
+  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
 
   SimpleTest.finish();
+};
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.enabled", true]
+    ]
+  }, runTests);
 });
-
 </script>
 </body>
 </html>
--- a/dom/events/test/test_bug1264380.html
+++ b/dom/events/test/test_bug1264380.html
@@ -1,54 +1,64 @@
 <html>
 <head>
   <title>Test the dragstart event on the anchor in side shadow DOM</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 <script>
 
-SimpleTest.waitForExplicitFinish();
-
-SpecialPowers.pushPrefEnv({"set": [
-  ["dom.webcomponents.enabled", true]
-]});
-
 function runTests()
 {
   let dragService = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
     getService(SpecialPowers.Ci.nsIDragService);
 
-  let shadow = document.querySelector('#outter').createShadowRoot();
-  let target = document.createElement('a');
-  let linkText = document.createTextNode("Drag me if you can!");
+  let iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  iframe.contentDocument.body.innerHTML = '<div id="outter"/>';
+
+  let iframeDoc = iframe.contentDocument;
+  let iframeWin = iframe.contentWindow;
+
+  let shadow = iframeDoc.querySelector('#outter').attachShadow({mode: 'open'});
+  let target = iframeDoc.createElement('a');
+  let linkText = iframeDoc.createTextNode("Drag me if you can!");
   target.appendChild(linkText);
   target.href = "http://www.mozilla.org/";
   shadow.appendChild(target);
 
   let dataTransfer;
   let trapDrag = function(event) {
     ok(true, "Got dragstart event");
     dataTransfer = event.dataTransfer;
     ok(dataTransfer, "DataTransfer object is available.");
     is(dataTransfer.mozItemCount, 1, "initial link item count");
     is(dataTransfer.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
     is(dataTransfer.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
   }
 
   ok(!dragService.getCurrentSession(), "There shouldn't be a drag session!");
-  window.addEventListener("dragstart", trapDrag, true);
-  synthesizeMouse(target, 2, 2, { type: "mousedown" });
-  synthesizeMouse(target, 11, 11, { type: "mousemove" });
-  synthesizeMouse(target, 20, 20, { type: "mousemove" });
-  window.removeEventListener("dragstart", trapDrag, true);
+  iframeWin.addEventListener("dragstart", trapDrag, true);
+  synthesizeMouse(target, 2, 2, { type: "mousedown" }, iframeWin);
+  synthesizeMouse(target, 11, 11, { type: "mousemove" }, iframeWin);
+  synthesizeMouse(target, 20, 20, { type: "mousemove" }, iframeWin);
+  iframeWin.removeEventListener("dragstart", trapDrag, true);
   ok(dragService.getCurrentSession(), "Drag session is available.");
   dragService.endDragSession(false);
   ok(!dragService.getCurrentSession(), "There shouldn't be a drag session anymore!");
   SimpleTest.finish();
 }
 
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.enabled", true]
+    ]
+  }, runTests);
+});
+
 </script>
 
-<body onload="window.setTimeout(runTests, 0);">
-<div id="outter"/>
+<body>
 </body>
 </html>
--- a/dom/file/BlobSet.cpp
+++ b/dom/file/BlobSet.cpp
@@ -32,17 +32,17 @@ BlobSet::AppendVoidPtr(const void* aData
   mBlobImpls.AppendElement(blobImpl);
 
   return NS_OK;
 }
 
 nsresult
 BlobSet::AppendString(const nsAString& aString, bool nativeEOL)
 {
-  nsAutoCString utf8Str;
+  nsCString utf8Str;
   if (NS_WARN_IF(!AppendUTF16toUTF8(aString, utf8Str, mozilla::fallible))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (nativeEOL) {
     if (utf8Str.Contains('\r')) {
       utf8Str.ReplaceSubstring("\r\n", "\n");
       utf8Str.ReplaceSubstring("\r", "\n");
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -10,16 +10,27 @@ interface mozIDOMWindow;
 interface nsPIDOMWindowInner;
 interface mozIDOMWindowProxy;
 interface nsIArray;
 interface nsIDocument;
 interface nsIInterceptedChannel;
 interface nsIPrincipal;
 interface nsIRunnable;
 interface nsIURI;
+%{C++
+namespace mozilla {
+namespace dom {
+class ClientInfo;
+class ServiceWorkerDescriptor;
+} // namespace dom
+} // namespace mozilla
+%}
+
+[ref] native const_ClientInfoRef(const mozilla::dom::ClientInfo);
+[ref] native const_ServiceWorkerDescriptorRef(const mozilla::dom::ServiceWorkerDescriptor);
 
 [scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)]
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
@@ -145,16 +156,19 @@ interface nsIServiceWorkerManager : nsIS
   /**
    * Call this to request that document `aDoc` be controlled by a ServiceWorker
    * if a registration exists for it's scope.
    *
    * This MUST only be called once per document!
    */
   [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
 
+  [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
+                                              in const_ServiceWorkerDescriptorRef aServiceWorker);
+
   /**
    * Documents that have called MaybeStartControlling() should call this when
    * they are destroyed. This function may be called multiple times, and is
    * idempotent.
    */
   [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
 
   /*
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -502,17 +502,17 @@ var interfaceNamesInGlobalScope =
     "HTMLProgressElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLQuoteElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLScriptElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSelectElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "HTMLSlotElement",
+    {name: "HTMLSlotElement", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSourceElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLSpanElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLStyleElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLTableCaptionElement",
@@ -838,17 +838,17 @@ var interfaceNamesInGlobalScope =
     "ServiceWorkerContainer",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerRegistration",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ScopedCredential", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ScopedCredentialInfo", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
+    {name: "ShadowRoot", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SharedWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleGestureEvent", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleTest", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SourceBuffer",
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/head.js
@@ -0,0 +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/. */
+
+"use strict";
+
+/**
+ * Set dom.webcomponents.enabled pref to true and loads an iframe, to ensure
+ * that the Element instance is loaded with the correct value of the
+ * preference.
+ *
+ * @return {Promise} promise that resolves when iframe is loaded.
+ */
+function setWebComponentsPrefAndCreateIframe(aSrcDoc) {
+  return new Promise(function (aResolve, aReject) {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.enabled", true]
+      ]
+    }, () => {
+      let iframe = document.createElement("iframe");
+      iframe.onload = function () { aResolve(iframe.contentDocument); }
+      iframe.onerror = function () { aReject('Failed to load iframe'); }
+      if (aSrcDoc) {
+        iframe.srcdoc = aSrcDoc;
+      }
+      document.body.appendChild(iframe);
+    });
+  });
+}
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -1,19 +1,18 @@
 [DEFAULT]
 support-files =
   inert_style.css
   dummy_page.html
+  head.js
 
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_bug1176757.html]
 [test_bug1276240.html]
-[test_content_element.html]
-skip-if = true # Triggers assertions about flattened tree inconsistencies and it's going away in bug 1418002.
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_htmlconstructor.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
 [test_custom_element_in_shadow.html]
 [test_custom_element_throw_on_dynamic_markup_insertion.html]
--- a/dom/tests/mochitest/webcomponents/test_bug1176757.html
+++ b/dom/tests/mochitest/webcomponents/test_bug1176757.html
@@ -1,40 +1,46 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1176757
 -->
 <head>
   <title>Test for Bug 1176757</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank"
-	href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176757">Mozilla Bug 1176757</a>
+  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176757">Mozilla Bug 1176757</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 1176757 **/
-var element = document.createElement("a");
-var shadowRoot = element.createShadowRoot();
-var thrownException = false;
+SimpleTest.waitForExplicitFinish();
+setWebComponentsPrefAndCreateIframe()
+  .then((aDocument) => {
+    var element = aDocument.createElement("div");
+    var shadowRoot = element.attachShadow({mode: "open"});
+    var thrownException = false;
 
-try {
-  shadowRoot.cloneNode();
-} catch(err) {
-  thrownException = err;
-}
+    try {
+      shadowRoot.cloneNode();
+    } catch(err) {
+      thrownException = err;
+    }
 
-ok(thrownException !== false, "An exception should've been thrown");
-is(thrownException.name, "DataCloneError", "A DataCloneError exception should've been thrown");
+    ok(thrownException !== false, "An exception should've been thrown");
+    is(thrownException.name, "DataCloneError", "A DataCloneError exception should've been thrown");
 
+    SimpleTest.finish();
+  });
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/tests/mochitest/webcomponents/test_bug1269155.html
+++ b/dom/tests/mochitest/webcomponents/test_bug1269155.html
@@ -1,89 +1,95 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1269155
 -->
 <head>
   <title>Test for Bug 1269155</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank"
-	href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269155">Mozilla Bug 1269155</a>
+  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269155">Mozilla Bug 1269155</a>
 <p id="display"></p>
-<div id="content" style="display: none">
 
-</div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 1269155 **/
-var host = document.querySelector('#content');
- var root = host.createShadowRoot();
+SimpleTest.waitForExplicitFinish();
 
- var header1 = document.createElement('h1');
- header1.textContent = 'Shadow Header1';
+var content = '<div id="content" style="display: none"> </div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    var host = aDocument.querySelector('#content');
+    var root = host.attachShadow({mode: "open"});
 
- var paragraph1 = document.createElement('p');
- paragraph1.textContent = 'shadow text paragraph1';
+    var header1 = aDocument.createElement('h1');
+    header1.textContent = 'Shadow Header1';
 
- root.appendChild(header1);
- root.appendChild(paragraph1);
+    var paragraph1 = aDocument.createElement('p');
+    paragraph1.textContent = 'shadow text paragraph1';
+
+    root.appendChild(header1);
+    root.appendChild(paragraph1);
 
-var root2 = paragraph1.createShadowRoot();
-var header2 = document.createElement('h2');
-header2.textContent = 'Shadow Header2';
+    var root2 = paragraph1.attachShadow({mode: "open"});
+    var header2 = aDocument.createElement('h2');
+    header2.textContent = 'Shadow Header2';
 
-var paragraph2 = document.createElement('p');
-paragraph2.textContent = 'shadow text paragraph2';
-root2.appendChild(header2);
-root2.appendChild(paragraph2);
+    var paragraph2 = aDocument.createElement('p');
+    paragraph2.textContent = 'shadow text paragraph2';
+    root2.appendChild(header2);
+    root2.appendChild(paragraph2);
 
 
-var frag = document.createDocumentFragment();
-var paragraph3 = document.createElement('p');
-paragraph3.textContent = 'fragment paragraph3';
-frag.appendChild(paragraph3);
+    var frag = aDocument.createDocumentFragment();
+    var paragraph3 = aDocument.createElement('p');
+    paragraph3.textContent = 'fragment paragraph3';
+    frag.appendChild(paragraph3);
 
-var root3 = paragraph3.createShadowRoot();
-var header4 = document.createElement('h2');
-header4.textContent = 'Shadow Header3';
+    var root3 = paragraph3.attachShadow({mode: "open"});
+    var header4 = aDocument.createElement('h2');
+    header4.textContent = 'Shadow Header3';
 
-var paragraph4 = document.createElement('p');
-paragraph4.textContent = 'shadow text paragraph4';
+    var paragraph4 = aDocument.createElement('p');
+    paragraph4.textContent = 'shadow text paragraph4';
 
-root3.appendChild(header4);
-root3.appendChild(paragraph4);
+    root3.appendChild(header4);
+    root3.appendChild(paragraph4);
 
-//shadow dom without compose
-is(root.getRootNode(), root, "root.getRootNode() should be root.");
-is(root2.getRootNode(), root2, "root2.getRootNode() should be root.");
-is(root3.getRootNode(), root3, "root3.getRootNode() should be root.");
-is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
-is(header2.getRootNode(), root2, "header1.getRootNode() should be root2.");
-is(header4.getRootNode(), root3, "header1.getRootNode() should be root3.");
-//shadow dom with compose
-is(root.getRootNode({ composed: true }), document, "root.getRootNode() with composed flag should be document.");
-is(root2.getRootNode({ composed: true }), document, "root2.getRootNode() with composed flag should be document.");
-is(root3.getRootNode({ composed: true }), frag, "root3.getRootNode() with composed flag should be frag.");
-is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
-is(header2.getRootNode({ composed: true }) , document, "header2.getRootNode() with composed flag should be document.");
-is(header4.getRootNode({ composed: true }) , frag, "head4.getRootNode() with composed flag should be frag.");
-//dom without compose
-is(host.getRootNode(), document, "host.getRootNode() should be document.");
-is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
-is(paragraph1.getRootNode(), root, "paragraph1.getRootNode() should be root.");
-is(frag.getRootNode(), frag, "frag.getRootNode() should be frag.");
-//dom with compose
-is(host.getRootNode({ composed: true }) , document, "host.getRootNode() with composed flag should be document.");
-is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
-is(paragraph1.getRootNode({ composed: true }) , document, "paragraph1.getRootNode() with composed flag should be document.");
-is(frag.getRootNode({ composed: true }) , frag, "frag.getRootNode() with composed flag should be frag.");
+    //shadow dom without compose
+    is(root.getRootNode(), root, "root.getRootNode() should be root.");
+    is(root2.getRootNode(), root2, "root2.getRootNode() should be root.");
+    is(root3.getRootNode(), root3, "root3.getRootNode() should be root.");
+    is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+    is(header2.getRootNode(), root2, "header1.getRootNode() should be root2.");
+    is(header4.getRootNode(), root3, "header1.getRootNode() should be root3.");
+    //shadow dom with compose
+    is(root.getRootNode({ composed: true }), aDocument, "root.getRootNode() with composed flag should be document.");
+    is(root2.getRootNode({ composed: true }), aDocument, "root2.getRootNode() with composed flag should be document.");
+    is(root3.getRootNode({ composed: true }), frag, "root3.getRootNode() with composed flag should be frag.");
+    is(header1.getRootNode({ composed: true }) , aDocument, "header1.getRootNode() with composed flag should be document.");
+    is(header2.getRootNode({ composed: true }) , aDocument, "header2.getRootNode() with composed flag should be document.");
+    is(header4.getRootNode({ composed: true }) , frag, "head4.getRootNode() with composed flag should be frag.");
+    //dom without compose
+    is(host.getRootNode(), aDocument, "host.getRootNode() should be document.");
+    is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+    is(paragraph1.getRootNode(), root, "paragraph1.getRootNode() should be root.");
+    is(frag.getRootNode(), frag, "frag.getRootNode() should be frag.");
+    //dom with compose
+    is(host.getRootNode({ composed: true }) , aDocument, "host.getRootNode() with composed flag should be document.");
+    is(header1.getRootNode({ composed: true }) , aDocument, "header1.getRootNode() with composed flag should be document.");
+    is(paragraph1.getRootNode({ composed: true }) , aDocument, "paragraph1.getRootNode() with composed flag should be document.");
+    is(frag.getRootNode({ composed: true }) , frag, "frag.getRootNode() with composed flag should be frag.");
 
+    SimpleTest.finish();
+  });
 </script>
 </pre>
 </body>
 </html>
 
deleted file mode 100644
--- a/dom/tests/mochitest/webcomponents/test_content_element.html
+++ /dev/null
@@ -1,131 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=806506
--->
-<head>
-  <title>Test for HTMLContent element</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<div id="grabme"></div>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
-<script>
-// Create a ShadowRoot and append some nodes, containing an insertion point with a universal selector.
-var shadow = $("grabme").createShadowRoot();
-shadow.innerHTML = '<span><content id="point"></content></span>';
-
-// Get the insertion point from the ShadowRoot and check that child of host is distributed.
-// Insertion point should match everything because the selector set is empty.
-var insertionPoint = shadow.getElementById("point");
-$("grabme").innerHTML = '<div id="distme"></div>';
-var distNodes = insertionPoint.getDistributedNodes();
-is(distNodes[0], $("distme"), "Child of bound content should be distributed into insertion point with universal selector.");
-is(distNodes.length, 1, "Should only have one child distributed into insertion point.");
-
-// Add another node to bound content and make sure that the node list is static and does not change.
-var someSpan = document.createElement("span");
-$("grabme").appendChild(someSpan);
-is(distNodes.length, 1, "NodeList from getDistributedNodes should be static.");
-
-// Test content select.
-$("grabme").innerHTML = '<div id="first" class="tall"></div><div id="second" class="skinny"></div>';
-shadow.innerHTML = '<span><content select=".tall" id="point"></content></span>';
-var insertionPoint = shadow.getElementById("point");
-distNodes = insertionPoint.getDistributedNodes();
-is(distNodes.length, 1, "Insertion point should only match element with the 'tall' class.");
-is(distNodes[0], $("first"), "Insertion point should only match element with the 'tall' class.");
-
-// Get rid of the select attribute and check that the insertion point matches everything.
-insertionPoint.removeAttribute("select");
-is(insertionPoint.getDistributedNodes().length, 2, "After removing the 'select' attribute, the insertion point should match everything.");
-
-// Set an invalid selector and make sure that nothing is matched.
-insertionPoint.setAttribute("select", "div:first-child");
-is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
-
-// all compound selectors must only be permitted simple selectors.
-insertionPoint.setAttribute("select", "div:first-child, span");
-is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
-
-// Test multiple compound selectors.
-$("grabme").innerHTML = '<div id="first"></div><span id="second"></span><span data-match-me="pickme" id="third"></span>';
-insertionPoint.setAttribute("select", "span[data-match-me=pickme], div");
-distNodes = insertionPoint.getDistributedNodes();
-is(distNodes.length, 2, "Insertion point selector should only match two nodes.");
-is(distNodes[0], $("first"), "First child node should match selector.");
-is(distNodes[1], $("third"), "Third child node should match selector.");
-
-// Test select property
-insertionPoint.select = "#second, #third";
-distNodes = insertionPoint.getDistributedNodes();
-is(distNodes.length, 2, "Insertion point selector (set using property) should only match two nodes.");
-is(distNodes[0], $("second"), "First child node should match selector.");
-is(distNodes[1], $("third"), "Third child node should match selector.");
-is(insertionPoint.select, "#second, #third", "select property should be transparent.");
-
-// Empty set of selectors should match everything.
-insertionPoint.select = "";
-is(insertionPoint.getDistributedNodes().length, 3, "Empty set of selectors (set using property) should match everything.");
-
-// Remove insertion point and make sure that the point does not have any nodes distributed.
-$("grabme").innerHTML = '<div></div><span></span>';
-insertionPoint.removeAttribute("select");
-is(insertionPoint.getDistributedNodes().length, 2, "Insertion point with univeral selector should match two nodes.");
-var insertionParent = insertionPoint.parentNode;
-insertionParent.removeChild(insertionPoint);
-is(insertionPoint.getDistributedNodes().length, 0, "Insertion point should match no nodes after removal.");
-insertionParent.appendChild(insertionPoint);
-is(insertionPoint.getDistributedNodes().length, 2, "Insertion point should match two nodes after appending.");
-
-// Test multiple insertion points and check tree order distribution of points.
-// Append two divs and three spans into host.
-$("grabme").innerHTML = '<div></div><span></span><div></div><span></span><span></span>';
-shadow.innerHTML = '<content select="div" id="divpoint"></content><content select="div, span" id="allpoint"></content>';
-// Insertion point matching div
-var divPoint = shadow.getElementById("divpoint");
-// Insertion point matching span and div
-var allPoint = shadow.getElementById("allpoint");
-
-is(divPoint.getDistributedNodes().length, 2, "Two div nodes should be distributed into divPoint.");
-is(allPoint.getDistributedNodes().length, 3, "Remaining nodes should be distributed into allPoint.");
-
-shadow.removeChild(allPoint);
-is(divPoint.getDistributedNodes().length, 2, "Number of div distributed into insertion point should not change.");
-is(allPoint.getDistributedNodes().length, 0, "Removed insertion point should not have any nodes.");
-
-shadow.insertBefore(allPoint, divPoint);
-is(allPoint.getDistributedNodes().length, 5, "allPoint should have nodes distributed before divPoint.");
-is(divPoint.getDistributedNodes().length, 0, "divPoint should have no distributed nodes because they are all distributed to allPoint.");
-
-// Make sure that fallback content are in the distributed nodes.
-$("grabme").innerHTML = '<div id="one"></div><div id="two"></div>';
-shadow.innerHTML = '<content select="#nothing" id="point"><span id="fallback"></span></content>';
-insertionPoint = shadow.getElementById("point");
-is(insertionPoint.getDistributedNodes().length, 1, "There should be one distributed node from fallback content.");
-is(insertionPoint.getDistributedNodes()[0].id, "fallback", "Distributed node should be fallback content.");
-
-$("grabme").innerHTML = '';
-shadow.innerHTML = '<content select="div" id="point"><span id="one"></span><span id="two"></span></content>';
-insertionPoint = shadow.getElementById("point");
-// Make sure that two fallback nodes are distributed into the insertion point.
-is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
-is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
-is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
-
-// Append a node that gets matched by the insertion point, thus causing the fallback content to be removed.
-var matchingDiv = document.createElement("div");
-matchingDiv.id = "three";
-$("grabme").appendChild(matchingDiv);
-is(insertionPoint.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
-is(insertionPoint.getDistributedNodes()[0].id, "three", "Node distriubted from host should have id of three.");
-
-// Remove the matching node from the host and make sure that the fallback content gets distributed.
-$("grabme").removeChild(matchingDiv);
-is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
-is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
-is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
-</script>
-</body>
-</html>
--- a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
@@ -1,120 +1,129 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1087460
 -->
 <head>
   <title>Test for custom element callbacks in shadow DOM.</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
-<div id="container"></div>
 
 <script>
 
-// Test callback for custom element when used after registration.
+SimpleTest.waitForExplicitFinish();
 
-var connectedCallbackCount = 0;
-var disconnectedCallbackCount = 0;
-var attributeChangedCallbackCount = 0;
+var content = '<div id="container"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+
+    // Test callback for custom element when used after registration.
 
-class Foo extends HTMLElement
-{
-  connectedCallback() {
-    connectedCallbackCount++;
-  }
+    var iframeWin = aDocument.defaultView;
+    var connectedCallbackCount = 0;
+    var disconnectedCallbackCount = 0;
+    var attributeChangedCallbackCount = 0;
 
-  disconnectedCallback() {
-    disconnectedCallbackCount++;
-  }
+    class Foo extends iframeWin.HTMLElement
+    {
+      connectedCallback() {
+        connectedCallbackCount++;
+      }
 
-  attributeChangedCallback(aName, aOldValue, aNewValue) {
-    attributeChangedCallbackCount++;
-  }
+      disconnectedCallback() {
+        disconnectedCallbackCount++;
+      }
 
-  static get observedAttributes() {
-    return ["data-foo"];
-  }
-}
+      attributeChangedCallback(aName, aOldValue, aNewValue) {
+        attributeChangedCallbackCount++;
+      }
 
-customElements.define("x-foo", Foo);
+      static get observedAttributes() {
+        return ["data-foo"];
+      }
+    }
 
-var container = document.getElementById("container");
-var shadow = container.createShadowRoot();
-var customElem = document.createElement("x-foo");
+    iframeWin.customElements.define("x-foo", Foo);
 
-is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
-customElem.setAttribute("data-foo", "bar");
-is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
+    var container = aDocument.getElementById("container");
+    var shadow = container.attachShadow({mode: "open"});
+    var customElem = aDocument.createElement("x-foo");
 
-is(connectedCallbackCount, 0, "connectedCallback should not be called on an element that is not in a document/composed document.");
-shadow.appendChild(customElem);
-is(connectedCallbackCount, 1, "connectedCallback should be called after attaching custom element to the composed document.");
+    is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
+    customElem.setAttribute("data-foo", "bar");
+    is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
 
-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called without detaching custom element.");
-shadow.removeChild(customElem);
-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
+    is(connectedCallbackCount, 0, "connectedCallback should not be called on an element that is not in a document/composed document.");
+    shadow.appendChild(customElem);
+    is(connectedCallbackCount, 1, "connectedCallback should be called after attaching custom element to the composed document.");
 
-// Test callback for custom element already in the composed doc when created.
+    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called without detaching custom element.");
+    shadow.removeChild(customElem);
+    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
 
-connectedCallbackCount = 0;
-disconnectedCallbackCount = 0;
-attributeChangedCallbackCount = 0;
+    // Test callback for custom element already in the composed doc when created.
 
-shadow.innerHTML = "<x-foo></x-foo>";
-is(connectedCallbackCount, 1, "connectedCallback should be called after creating an element in the composed document.");
-
-shadow.innerHTML = "";
-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
+    connectedCallbackCount = 0;
+    disconnectedCallbackCount = 0;
+    attributeChangedCallbackCount = 0;
 
-// Test callback for custom element in shadow DOM when host attached/detached to/from document.
+    shadow.innerHTML = "<x-foo></x-foo>";
+    is(connectedCallbackCount, 1, "connectedCallback should be called after creating an element in the composed document.");
 
-connectedCallbackCount = 0;
-disconnectedCallbackCount = 0;
-attributeChangedCallbackCount = 0;
+    shadow.innerHTML = "";
+    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
+
+    // Test callback for custom element in shadow DOM when host attached/detached to/from document.
 
-var host = document.createElement("div");
-shadow = host.createShadowRoot();
-customElem = document.createElement("x-foo");
+    connectedCallbackCount = 0;
+    disconnectedCallbackCount = 0;
+    attributeChangedCallbackCount = 0;
+
+    var host = aDocument.createElement("div");
+    shadow = host.attachShadow({mode: "open"});
+    customElem = aDocument.createElement("x-foo");
 
-is(connectedCallbackCount, 0, "connectedCallback should not be called on newly created element.");
-shadow.appendChild(customElem);
-is(connectedCallbackCount, 0, "connectedCallback should not be called on attaching to a tree that is not in the composed document.");
+    is(connectedCallbackCount, 0, "connectedCallback should not be called on newly created element.");
+    shadow.appendChild(customElem);
+    is(connectedCallbackCount, 0, "connectedCallback should not be called on attaching to a tree that is not in the composed document.");
 
-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called.");
-shadow.removeChild(customElem);
-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called when detaching from a tree that is not in the composed document.");
+    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called.");
+    shadow.removeChild(customElem);
+    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called when detaching from a tree that is not in the composed document.");
 
-shadow.appendChild(customElem);
-is(connectedCallbackCount, 0, "connectedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
+    shadow.appendChild(customElem);
+    is(connectedCallbackCount, 0, "connectedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
+
+    container.appendChild(host);
+    is(connectedCallbackCount, 1, "connectedCallback should be called after host is inserted into document.");
 
-container.appendChild(host);
-is(connectedCallbackCount, 1, "connectedCallback should be called after host is inserted into document.");
+    container.removeChild(host);
+    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after host is removed from document.");
 
-container.removeChild(host);
-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after host is removed from document.");
+    // Test callback for custom element for upgraded element.
 
-// Test callback for custom element for upgraded element.
+    connectedCallbackCount = 0;
+    disconnectedCallbackCount = 0;
+    attributeChangedCallbackCount = 0;
 
-connectedCallbackCount = 0;
-disconnectedCallbackCount = 0;
-attributeChangedCallbackCount = 0;
-
-shadow = container.shadowRoot;
-shadow.innerHTML = "<x-bar></x-bar>";
+    shadow = container.shadowRoot;
+    shadow.innerHTML = "<x-bar></x-bar>";
 
-class Bar extends HTMLElement {
-  connectedCallback() {
-    connectedCallbackCount++;
-  }
-};
+    class Bar extends iframeWin.HTMLElement {
+      connectedCallback() {
+        connectedCallbackCount++;
+      }
+    };
 
-customElements.define("x-bar", Bar);
-is(connectedCallbackCount, 1, "connectedCallback should be called after upgrading element in composed document.");
+    iframeWin.customElements.define("x-bar", Bar);
+    is(connectedCallbackCount, 1, "connectedCallback should be called after upgrading element in composed document.");
 
+    SimpleTest.finish();
+  });
 </script>
 
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_detached_style.html
+++ b/dom/tests/mochitest/webcomponents/test_detached_style.html
@@ -1,25 +1,35 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1062578
 -->
 <head>
   <title>Test for creating style in shadow root of host not in document.</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<div id="grabme"></div>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1062578">Bug 1062578</a>
 <script>
-var host = document.createElement("div");
-var shadow = host.createShadowRoot();
-shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
+
+SimpleTest.waitForExplicitFinish();
 
-grabme.appendChild(host);
+var content = '<div id="grabme"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    var host = aDocument.createElement("div");
+    var shadow = host.attachShadow({mode: "open"});
+    shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
 
-var inner = shadow.getElementById("inner");
-is(getComputedStyle(inner, null).getPropertyValue("height"), "200px", "Style in shadow root should take effect.");
+    var iframeWin = aDocument.defaultView;
+    iframeWin.grabme.appendChild(host);
+
+    var inner = shadow.getElementById("inner");
+    is(iframeWin.getComputedStyle(inner, null).getPropertyValue("height"), "200px", "Style in shadow root should take effect.");
+
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+++ b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
@@ -1,36 +1,42 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1177991
 -->
 <head>
   <title>Test for Bug 1177991</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1177991">Mozilla Bug 1177991</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var thrownException = false;
-var shadowRoot = document.createElement('a').createShadowRoot();
+SimpleTest.waitForExplicitFinish();
+setWebComponentsPrefAndCreateIframe()
+  .then((aDocument) => {
+    var thrownException = false;
+    var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
 
-try {
-  document.adoptNode(shadowRoot);
-} catch(err) {
-  thrownException = err;
-}
+    try {
+      aDocument.adoptNode(shadowRoot);
+    } catch(err) {
+      thrownException = err;
+    }
 
-ok(thrownException !== false, "A HierarchyRequestError");
-is(thrownException.name, "HierarchyRequestError", "A HierarchyRequestError should've been thrown");
+    ok(thrownException !== false, "A HierarchyRequestError");
+    is(thrownException.name, "HierarchyRequestError", "A HierarchyRequestError should've been thrown");
 
+    SimpleTest.finish();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_document_importnode.html
+++ b/dom/tests/mochitest/webcomponents/test_document_importnode.html
@@ -1,37 +1,42 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1177914
 -->
 <head>
   <title>Test for Bug 1177914</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1177914">Mozilla Bug 1177914</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-var thrownException = false;
-var shadowRoot = document.createElement('a').createShadowRoot();
+SimpleTest.waitForExplicitFinish();
+setWebComponentsPrefAndCreateIframe()
+  .then((aDocument) => {
+    var thrownException = false;
+    var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
 
-try {
-  document.importNode(shadowRoot);
-} catch(err) {
-  thrownException = err;
-}
+    try {
+      aDocument.importNode(shadowRoot);
+    } catch(err) {
+      thrownException = err;
+    }
 
+    ok(thrownException !== false, "A HierarchyRequestError");
+    is(thrownException.name, "NotSupportedError", "A NotSupportedError exception should've been thrown");
 
-ok(thrownException !== false, "An exception should've been thrown");
-is(thrownException.name, "NotSupportedError", "A NotSupportedError exception should've been thrown");
-
+    SimpleTest.finish();
+  });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_event_retarget.html
+++ b/dom/tests/mochitest/webcomponents/test_event_retarget.html
@@ -1,147 +1,153 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=887541
 -->
 <head>
   <title>Test for event retargeting in web components</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
 <script>
 
-/*
- * Creates an event listener with an expected event target.
- */
-function createEventListener(expectedTarget, msg) {
-  return function(e) {
-    is(e.target, expectedTarget, msg);
-  };
-}
+SimpleTest.waitForExplicitFinish();
+setWebComponentsPrefAndCreateIframe()
+  .then((aDocument) => {
+    /*
+     * Creates an event listener with an expected event target.
+     */
+    function createEventListener(expectedTarget, msg) {
+      return function(e) {
+        is(e.target, expectedTarget, msg);
+      };
+    }
 
-/*
- * Test of event retargeting through a basic ShadowRoot with a content insertion point.
- *
- * <div elemThree> ---- <shadow-root shadowOne>
- *        |                        |
- * <div elemOne>            <content elemTwo>
- *
- * Dispatch event on elemOne
- */
+    /*
+     * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+     *
+     * <div elemThree> ---- <shadow-root shadowOne>
+     *        |                        |
+     * <div elemOne>            <content elemTwo>
+     *
+     * Dispatch event on elemOne
+     */
 
-var elemOne = document.createElement("div");
-var elemTwo = document.createElement("content");
-var elemThree = document.createElement("div");
-var shadowOne = elemThree.createShadowRoot();
+    var elemOne = aDocument.createElement("div");
+    var elemTwo = aDocument.createElement("content");
+    var elemThree = aDocument.createElement("div");
+    var shadowOne = elemThree.attachShadow({mode: "open"});
 
-elemThree.appendChild(elemOne);
-shadowOne.appendChild(elemTwo);
+    elemThree.appendChild(elemOne);
+    shadowOne.appendChild(elemTwo);
 
-elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
-elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
-elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
-shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+    elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+    elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+    elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+    shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
 
-var customEvent = new CustomEvent("custom", { "bubbles" : true });
-elemOne.dispatchEvent(customEvent);
+    var customEvent = new CustomEvent("custom", { "bubbles" : true });
+    elemOne.dispatchEvent(customEvent);
 
-/*
- * Test of event retargeting through a basic ShadowRoot with a content insertion point.
- *
- * <div elemThree> ---- <shadow-root shadowOne>
- *        |                        |
- * <div elemOne>            <content elemTwo>
- *
- * Dispatch event on elemTwo
- */
+    /*
+     * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+     *
+     * <div elemThree> ---- <shadow-root shadowOne>
+     *        |                        |
+     * <div elemOne>            <content elemTwo>
+     *
+     * Dispatch event on elemTwo
+     */
 
-elemOne = document.createElement("div");
-elemTwo = document.createElement("content");
-elemThree = document.createElement("div");
-shadowOne = elemThree.createShadowRoot();
+    elemOne = aDocument.createElement("div");
+    elemTwo = aDocument.createElement("content");
+    elemThree = aDocument.createElement("div");
+    shadowOne = elemThree.attachShadow({mode: "open"});
 
-elemThree.appendChild(elemOne);
-shadowOne.appendChild(elemTwo);
+    elemThree.appendChild(elemOne);
+    shadowOne.appendChild(elemTwo);
 
-elemTwo.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of elemTwo."));
-elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
-shadowOne.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of shadowOne."));
+    elemTwo.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of elemTwo."));
+    elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+    shadowOne.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of shadowOne."));
 
-customEvent = new CustomEvent("custom", { "bubbles" : true });
-elemTwo.dispatchEvent(customEvent);
+    customEvent = new CustomEvent("custom", { "bubbles" : true });
+    elemTwo.dispatchEvent(customEvent);
 
-/*
- * Test of event retargeting through a nested ShadowRoots with content insertion points.
- *
- * <div elemFive> --- <shadow-root shadowTwo>
- *       |                       |
- * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
- *                               |                        |
- *                       <content elemTwo>       <content elemThree>
- *
- * Dispatch custom event on elemOne.
- */
+    /*
+     * Test of event retargeting through a nested ShadowRoots with content insertion points.
+     *
+     * <div elemFive> --- <shadow-root shadowTwo>
+     *       |                       |
+     * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+     *                               |                        |
+     *                       <content elemTwo>       <content elemThree>
+     *
+     * Dispatch custom event on elemOne.
+     */
 
-elemOne = document.createElement("div");
-elemTwo = document.createElement("content");
-elemThree = document.createElement("content");
-var elemFour = document.createElement("div");
-var elemFive = document.createElement("div");
-var shadowTwo = elemFive.createShadowRoot();
-shadowOne = elemFour.createShadowRoot();
+    elemOne = aDocument.createElement("div");
+    elemTwo = aDocument.createElement("content");
+    elemThree = aDocument.createElement("content");
+    var elemFour = aDocument.createElement("div");
+    var elemFive = aDocument.createElement("div");
+    var shadowTwo = elemFive.attachShadow({mode: "open"});
+    shadowOne = elemFour.attachShadow({mode: "open"});
 
-elemFive.appendChild(elemOne);
-shadowTwo.appendChild(elemFour);
-elemFour.appendChild(elemTwo);
-shadowOne.appendChild(elemThree);
+    elemFive.appendChild(elemOne);
+    shadowTwo.appendChild(elemFour);
+    elemFour.appendChild(elemTwo);
+    shadowOne.appendChild(elemThree);
 
-elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
-elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
-elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
-elemFour.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFour."));
-elemFive.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFive."));
-shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
-shadowTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowTwo."));
+    elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+    elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+    elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+    elemFour.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFour."));
+    elemFive.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFive."));
+    shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+    shadowTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowTwo."));
+
+    customEvent = new CustomEvent("custom", { "bubbles" : true });
+    elemOne.dispatchEvent(customEvent);
 
-customEvent = new CustomEvent("custom", { "bubbles" : true });
-elemOne.dispatchEvent(customEvent);
-
-/*
- * Test of event retargeting through a nested ShadowRoots with content insertion points.
- *
- * <div elemFive> --- <shadow-root shadowTwo>
- *       |                       |
- * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
- *                               |                        |
- *                       <content elemTwo>       <content elemThree>
- *
- * Dispatch custom event on elemThree.
- */
+    /*
+     * Test of event retargeting through a nested ShadowRoots with content insertion points.
+     *
+     * <div elemFive> --- <shadow-root shadowTwo>
+     *       |                       |
+     * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+     *                               |                        |
+     *                       <content elemTwo>       <content elemThree>
+     *
+     * Dispatch custom event on elemThree.
+     */
 
-elemOne = document.createElement("div");
-elemTwo = document.createElement("content");
-elemThree = document.createElement("content");
-elemFour = document.createElement("div");
-elemFive = document.createElement("div");
-shadowTwo = elemFive.createShadowRoot();
-shadowOne = elemFour.createShadowRoot();
+    elemOne = aDocument.createElement("div");
+    elemTwo = aDocument.createElement("content");
+    elemThree = aDocument.createElement("content");
+    elemFour = aDocument.createElement("div");
+    elemFive = aDocument.createElement("div");
+    shadowTwo = elemFive.attachShadow({mode: "open"});
+    shadowOne = elemFour.attachShadow({mode: "open"});
 
-elemFive.appendChild(elemOne);
-shadowTwo.appendChild(elemFour);
-elemFour.appendChild(elemTwo);
-shadowOne.appendChild(elemThree);
+    elemFive.appendChild(elemOne);
+    shadowTwo.appendChild(elemFour);
+    elemFour.appendChild(elemTwo);
+    shadowOne.appendChild(elemThree);
 
-elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
-elemFour.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of elemFour."));
-elemFive.addEventListener("custom", createEventListener(elemFive, "elemFive is in common ancestor tree of elemFive."));
-shadowOne.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of shadowOne."));
-shadowTwo.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of shadowTwo."));
+    elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+    elemFour.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of elemFour."));
+    elemFive.addEventListener("custom", createEventListener(elemFive, "elemFive is in common ancestor tree of elemFive."));
+    shadowOne.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of shadowOne."));
+    shadowTwo.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of shadowTwo."));
 
-customEvent = new CustomEvent("custom", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
+    customEvent = new CustomEvent("custom", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
 
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
+++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
@@ -1,168 +1,174 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=887541
 -->
 <head>
   <title>Test for event model in web components</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
 <script>
 
 var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
             .getService(SpecialPowers.Ci.nsIEventListenerService);
 
-function eventListener(e) {
-  eventChain.push(this);
-}
+SimpleTest.waitForExplicitFinish();
+setWebComponentsPrefAndCreateIframe()
+  .then((aDocument) => {
+    function eventListener(e) {
+      eventChain.push(this);
+    }
 
-function isEventChain(actual, expected, msg) {
-  is(actual.length, expected.length, msg);
-  for (var i = 0; i < expected.length; i++) {
-    is(actual[i], expected[i], msg + " at " + i);
-  }
-
-  if (0 < actual.length) {
-    var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
-    ok(expected.length < chain.length, "There should be additional chrome event targets.");
-  }
-}
+    function isEventChain(actual, expected, msg) {
+      is(actual.length, expected.length, msg);
+      for (var i = 0; i < expected.length; i++) {
+        is(actual[i], expected[i], msg + " at " + i);
+      }
 
-/*
- * <div elemOne> ------ <shadow-root shadowOne>
- *                                 |
- *                          <span elemTwo>
- *                                 |
- *                         <span elemThree>
- */
+      if (0 < actual.length) {
+        var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
+        ok(expected.length < chain.length, "There should be additional chrome event targets.");
+      }
+    }
 
-var elemOne = document.createElement("div");
-var elemTwo = document.createElement("span");
-var elemThree = document.createElement("span");
-var shadowOne = elemOne.createShadowRoot();
+    /*
+     * <div elemOne> ------ <shadow-root shadowOne>
+     *                                 |
+     *                          <span elemTwo>
+     *                                 |
+     *                         <span elemThree>
+     */
 
-shadowOne.appendChild(elemTwo);
-elemTwo.appendChild(elemThree);
+    var elemOne = aDocument.createElement("div");
+    var elemTwo = aDocument.createElement("span");
+    var elemThree = aDocument.createElement("span");
+    var shadowOne = elemOne.attachShadow({mode: "open"});
 
-// Test stopping "abort" event.
+    shadowOne.appendChild(elemTwo);
+    elemTwo.appendChild(elemThree);
+
+    // Test stopping "abort" event.
 
-elemOne.addEventListener("abort", eventListener);
-elemTwo.addEventListener("abort", eventListener);
-elemThree.addEventListener("abort", eventListener);
-shadowOne.addEventListener("abort", eventListener);
+    elemOne.addEventListener("abort", eventListener);
+    elemTwo.addEventListener("abort", eventListener);
+    elemThree.addEventListener("abort", eventListener);
+    shadowOne.addEventListener("abort", eventListener);
 
-var eventChain = [];
+    var eventChain = [];
 
-var customEvent = new CustomEvent("abort", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that abort event is stopped at shadow root.");
+    var customEvent = new CustomEvent("abort", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that abort event is stopped at shadow root.");
 
-// Test stopping "error" event.
+    // Test stopping "error" event.
 
-elemOne.addEventListener("error", eventListener);
-elemTwo.addEventListener("error", eventListener);
-elemThree.addEventListener("error", eventListener);
-shadowOne.addEventListener("error", eventListener);
+    elemOne.addEventListener("error", eventListener);
+    elemTwo.addEventListener("error", eventListener);
+    elemThree.addEventListener("error", eventListener);
+    shadowOne.addEventListener("error", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("error", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that error event is stopped at shadow root.");
+    customEvent = new CustomEvent("error", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that error event is stopped at shadow root.");
 
-// Test stopping "select" event.
+    // Test stopping "select" event.
 
-elemOne.addEventListener("select", eventListener);
-elemTwo.addEventListener("select", eventListener);
-elemThree.addEventListener("select", eventListener);
-shadowOne.addEventListener("select", eventListener);
+    elemOne.addEventListener("select", eventListener);
+    elemTwo.addEventListener("select", eventListener);
+    elemThree.addEventListener("select", eventListener);
+    shadowOne.addEventListener("select", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("select", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that select event is stopped at shadow root.");
+    customEvent = new CustomEvent("select", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that select event is stopped at shadow root.");
 
-// Test stopping "change" event.
+    // Test stopping "change" event.
 
-elemOne.addEventListener("change", eventListener);
-elemTwo.addEventListener("change", eventListener);
-elemThree.addEventListener("change", eventListener);
-shadowOne.addEventListener("change", eventListener);
+    elemOne.addEventListener("change", eventListener);
+    elemTwo.addEventListener("change", eventListener);
+    elemThree.addEventListener("change", eventListener);
+    shadowOne.addEventListener("change", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("change", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
+    customEvent = new CustomEvent("change", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
 
-// Test stopping "reset" event.
+    // Test stopping "reset" event.
 
-elemOne.addEventListener("reset", eventListener);
-elemTwo.addEventListener("reset", eventListener);
-elemThree.addEventListener("reset", eventListener);
-shadowOne.addEventListener("reset", eventListener);
+    elemOne.addEventListener("reset", eventListener);
+    elemTwo.addEventListener("reset", eventListener);
+    elemThree.addEventListener("reset", eventListener);
+    shadowOne.addEventListener("reset", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("reset", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that reset event is stopped at shadow root.");
+    customEvent = new CustomEvent("reset", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that reset event is stopped at shadow root.");
 
-// Test stopping "load" event.
+    // Test stopping "load" event.
 
-elemOne.addEventListener("load", eventListener);
-elemTwo.addEventListener("load", eventListener);
-elemThree.addEventListener("load", eventListener);
-shadowOne.addEventListener("load", eventListener);
+    elemOne.addEventListener("load", eventListener);
+    elemTwo.addEventListener("load", eventListener);
+    elemThree.addEventListener("load", eventListener);
+    shadowOne.addEventListener("load", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("load", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that load event is stopped at shadow root.");
+    customEvent = new CustomEvent("load", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that load event is stopped at shadow root.");
 
-// Test stopping "resize" event.
+    // Test stopping "resize" event.
 
-elemOne.addEventListener("resize", eventListener);
-elemTwo.addEventListener("resize", eventListener);
-elemThree.addEventListener("resize", eventListener);
-shadowOne.addEventListener("resize", eventListener);
+    elemOne.addEventListener("resize", eventListener);
+    elemTwo.addEventListener("resize", eventListener);
+    elemThree.addEventListener("resize", eventListener);
+    shadowOne.addEventListener("resize", eventListener);
 
-eventChain = [];
+    eventChain = [];
 
-customEvent = new CustomEvent("resize", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that resize event is stopped at shadow root.");
+    customEvent = new CustomEvent("resize", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that resize event is stopped at shadow root.");
+
+    // Test stopping "scroll" event.
 
-// Test stopping "scroll" event.
+    elemOne.addEventListener("scroll", eventListener);
+    elemTwo.addEventListener("scroll", eventListener);
+    elemThree.addEventListener("scroll", eventListener);
+    shadowOne.addEventListener("scroll", eventListener);
 
-elemOne.addEventListener("scroll", eventListener);
-elemTwo.addEventListener("scroll", eventListener);
-elemThree.addEventListener("scroll", eventListener);
-shadowOne.addEventListener("scroll", eventListener);
+    eventChain = [];
 
-eventChain = [];
+    customEvent = new CustomEvent("scroll", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that scroll event is stopped at shadow root.");
 
-customEvent = new CustomEvent("scroll", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that scroll event is stopped at shadow root.");
-
-// Test stopping "selectstart" event.
+    // Test stopping "selectstart" event.
 
-elemOne.addEventListener("selectstart", eventListener);
-elemTwo.addEventListener("selectstart", eventListener);
-elemThree.addEventListener("selectstart", eventListener);
-shadowOne.addEventListener("selectstart", eventListener);
+    elemOne.addEventListener("selectstart", eventListener);
+    elemTwo.addEventListener("selectstart", eventListener);
+    elemThree.addEventListener("selectstart", eventListener);
+    shadowOne.addEventListener("selectstart", eventListener);
+
+    eventChain = [];
 
-eventChain = [];
+    customEvent = new CustomEvent("selectstart", { "bubbles" : true });
+    elemThree.dispatchEvent(customEvent);
+    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that selectstart event is stopped at shadow root.");
 
-customEvent = new CustomEvent("selectstart", { "bubbles" : true });
-elemThree.dispatchEvent(customEvent);
-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that selectstart event is stopped at shadow root.");
-
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_shadowroot.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
@@ -1,83 +1,91 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=806506
 -->
 <head>
   <title>Test for ShadowRoot</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<div id="movedtoshadow" class="testclass"></div>
-<svg id="svgmovedtoshadow"></svg>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
-// Create ShadowRoot.
-var element = document.createElement("div");
-ok(!element.shadowRoot, "div element should not have a shadow root.");
-var shadow = element.createShadowRoot();
-is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
+
+SimpleTest.waitForExplicitFinish();
 
-// Move an element from the document to the ShadowRoot.
-var inShadowEl = document.getElementById("movedtoshadow");
-var inShadowSVGEl = document.getElementById("svgmovedtoshadow");
+var content = '<div id="movedtoshadow" class="testclass"></div>' +
+              '<svg id="svgmovedtoshadow"></svg>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    // Create ShadowRoot.
+    var element = aDocument.createElement("div");
+    ok(!element.shadowRoot, "div element should not have a shadow root.");
+    var shadow = element.attachShadow({mode: "open"});
+    is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
 
-// Test getElementById
-ok(!shadow.getElementById("movedtoshadow"), "Element not in ShadowRoot should not be accessible from ShadowRoot API.");
-ok(!shadow.getElementById("svgmovedtoshadow"), "SVG element not in ShadowRoot should not be accessible from ShadowRoot API.");
-shadow.appendChild(inShadowEl);
-shadow.appendChild(inShadowSVGEl);
-is(shadow.getElementById("movedtoshadow"), inShadowEl, "Element appended to a ShadowRoot should be accessible from ShadowRoot API.");
-ok(!document.getElementById("movedtoshadow"), "Element appended to a ShadowRoot should not be accessible from document.");
-is(shadow.getElementById("svgmovedtoshadow"), inShadowSVGEl, "SVG element appended to a ShadowRoot should be accessible from ShadowRoot API.");
-ok(!document.getElementById("svgmovedtoshadow"), "SVG element appended to a ShadowRoot should not be accessible from document.");
+    // Move an element from the document to the ShadowRoot.
+    var inShadowEl = aDocument.getElementById("movedtoshadow");
+    var inShadowSVGEl = aDocument.getElementById("svgmovedtoshadow");
 
-// Test getElementsByClassName
-is(document.getElementsByClassName("testclass").length, 0, "Element removed from DOM should not be accessible by DOM accessors.");
-is(shadow.getElementsByClassName("testclass").length, 1, "Element added to ShadowRoot should be accessible by ShadowRoot API.");
+    // Test getElementById
+    ok(!shadow.getElementById("movedtoshadow"), "Element not in ShadowRoot should not be accessible from ShadowRoot API.");
+    ok(!shadow.getElementById("svgmovedtoshadow"), "SVG element not in ShadowRoot should not be accessible from ShadowRoot API.");
+    shadow.appendChild(inShadowEl);
+    shadow.appendChild(inShadowSVGEl);
+    is(shadow.getElementById("movedtoshadow"), inShadowEl, "Element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+    ok(!aDocument.getElementById("movedtoshadow"), "Element appended to a ShadowRoot should not be accessible from document.");
+    is(shadow.getElementById("svgmovedtoshadow"), inShadowSVGEl, "SVG element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+    ok(!aDocument.getElementById("svgmovedtoshadow"), "SVG element appended to a ShadowRoot should not be accessible from document.");
 
-// Test getElementsByTagName{NS}
-is(document.getElementsByTagName("div").length, 0, "Element removed from DOM should not be accessible from DOM accessors.");
-is(shadow.getElementsByTagName("div").length, 1, "Elements in the ShadowRoot should be accessible from the ShadowRoot API.");
-is(document.getElementsByTagName("svg").length, 0, "SVG elements removed from DOM should not be accessible from DOM accessors.");
-is(shadow.getElementsByTagName("svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
-is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+    // Test getElementsByClassName
+    is(aDocument.getElementsByClassName("testclass").length, 0, "Element removed from DOM should not be accessible by DOM accessors.");
+    is(shadow.getElementsByClassName("testclass").length, 1, "Element added to ShadowRoot should be accessible by ShadowRoot API.");
 
-// Remove elements from ShadowRoot and make sure that they are no longer accessible via the ShadowRoot API.
-shadow.removeChild(inShadowEl);
-shadow.removeChild(inShadowSVGEl);
-ok(!shadow.getElementById("movedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
-ok(!shadow.getElementById("svgmovedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
-is(shadow.getElementsByClassName("testclass").length, 0, "ShadowRoot getElementsByClassName should not be able to access elements removed from ShadowRoot.");
-is(shadow.getElementsByTagName("svg").length, 0, "ShadowRoot getElementsByTagName should not be able to access elements removed from ShadowRoot.");
-is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 0, "ShadowRoot getElementsByTagNameNS should not be able to access elements removed from ShadowRoot.");
+    // Test getElementsByTagName{NS}
+    is(aDocument.getElementsByTagName("div").length, 0, "Element removed from DOM should not be accessible from DOM accessors.");
+    is(shadow.getElementsByTagName("div").length, 1, "Elements in the ShadowRoot should be accessible from the ShadowRoot API.");
+    is(aDocument.getElementsByTagName("svg").length, 0, "SVG elements removed from DOM should not be accessible from DOM accessors.");
+    is(shadow.getElementsByTagName("svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+    is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+
+    // Remove elements from ShadowRoot and make sure that they are no longer accessible via the ShadowRoot API.
+    shadow.removeChild(inShadowEl);
+    shadow.removeChild(inShadowSVGEl);
+    ok(!shadow.getElementById("movedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+    ok(!shadow.getElementById("svgmovedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+    is(shadow.getElementsByClassName("testclass").length, 0, "ShadowRoot getElementsByClassName should not be able to access elements removed from ShadowRoot.");
+    is(shadow.getElementsByTagName("svg").length, 0, "ShadowRoot getElementsByTagName should not be able to access elements removed from ShadowRoot.");
+    is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 0, "ShadowRoot getElementsByTagNameNS should not be able to access elements removed from ShadowRoot.");
 
-// Test querySelector on element in a ShadowRoot.
-element = document.createElement("div");
-shadow = element.createShadowRoot();
-var parentDiv = document.createElement("div");
-var childSpan = document.createElement("span");
-childSpan.id = "innerdiv";
-parentDiv.appendChild(childSpan);
-is(parentDiv.querySelector("#innerdiv"), childSpan, "ID query selector should work on element in ShadowRoot.");
-is(parentDiv.querySelector("span"), childSpan, "Tag query selector should work on element in ShadowRoot.");
+    // Test querySelector on element in a ShadowRoot.
+    element = aDocument.createElement("div");
+    shadow = element.attachShadow({mode: "open"});
+    var parentDiv = aDocument.createElement("div");
+    var childSpan = aDocument.createElement("span");
+    childSpan.id = "innerdiv";
+    parentDiv.appendChild(childSpan);
+    is(parentDiv.querySelector("#innerdiv"), childSpan, "ID query selector should work on element in ShadowRoot.");
+    is(parentDiv.querySelector("span"), childSpan, "Tag query selector should work on element in ShadowRoot.");
 
-// Test that exception is thrown when trying to create a cycle with host node.
-element = document.createElement("div");
-shadow = element.createShadowRoot();
-try {
-  shadow.appendChild(element);
-  ok(false, "Excpetion should be thrown when creating a cycle with host content.");
-} catch (ex) {
-  ok(true, "Excpetion should be thrown when creating a cycle with host content.");
-}
+    // Test that exception is thrown when trying to create a cycle with host node.
+    element = aDocument.createElement("div");
+    shadow = element.attachShadow({mode: "open"});
+    try {
+      shadow.appendChild(element);
+      ok(false, "Excpetion should be thrown when creating a cycle with host content.");
+    } catch (ex) {
+      ok(true, "Excpetion should be thrown when creating a cycle with host content.");
+    }
 
-// Basic innerHTML tests.
-shadow.innerHTML = '<span id="first"></span><div id="second"></div>';
-is(shadow.childNodes.length, 2, "There should be two children in the ShadowRoot.");
-is(shadow.getElementById("second").tagName, "DIV", "Elements created by innerHTML should be accessible by ShadowRoot API.");
+    // Basic innerHTML tests.
+    shadow.innerHTML = '<span id="first"></span><div id="second"></div>';
+    is(shadow.childNodes.length, 2, "There should be two children in the ShadowRoot.");
+    is(shadow.getElementById("second").tagName, "DIV", "Elements created by innerHTML should be accessible by ShadowRoot API.");
 
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
@@ -1,44 +1,49 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=806506
 -->
 <head>
   <title>Test for inert elements in ShadowRoot</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
-<body onload="runChecks();">
-<div id="grabme"></div>
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
-var element = document.getElementById("grabme");
-var shadow = element.createShadowRoot();
-
-// Check that <base> is inert.
-shadow.innerHTML = '<base href="http://www.example.org/" />';
-isnot(document.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
-
 SimpleTest.waitForExplicitFinish();
 
-// Check that <link> is inert.
-var numStyleBeforeLoad = document.styleSheets.length;
+var content = '<div id="grabme"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    function runChecks() {
+      isnot(aDocument.defaultView.getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
+      is(aDocument.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
+      is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
+      // Remove link to make sure we don't get assertions.
+      shadow.removeChild(shadowStyle);
+      SimpleTest.finish();
+    };
 
-shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
-shadow.applyAuthorStyles = true;
-var shadowSpan = shadow.getElementById("shadowspan");
-var shadowStyle = shadow.getElementById("shadowlink");
+    var element = aDocument.getElementById("grabme");
+    var shadow = element.attachShadow({mode: "open"});
 
-function runChecks() {
-  isnot(getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
-  is(document.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
-  is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
-  // Remove link to make sure we don't get assertions.
-  shadow.removeChild(shadowStyle);
-  SimpleTest.finish();
-};
+    // Check that <base> is inert.
+    shadow.innerHTML = '<base href="http://www.example.org/" />';
+    isnot(aDocument.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
+
+    // Check that <link> is inert.
+    var numStyleBeforeLoad = aDocument.styleSheets.length;
 
+    shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
+    shadow.applyAuthorStyles = true;
+    var shadowSpan = shadow.getElementById("shadowspan");
+    var shadowStyle = shadow.getElementById("shadowlink");
+
+    runChecks();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
@@ -1,88 +1,97 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=806506
 -->
 <head>
   <title>Test for ShadowRoot styling</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<div class="tall" id="bodydiv"></div>
-<div id="container"></div>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 if (!SpecialPowers.DOMWindowUtils.isStyledByServo) {
   SimpleTest.expectAssertions(3, 3); // GeckoRestyleManager stuff.
 }
 
-// Create ShadowRoot.
-var container = document.getElementById("container");
-var elem = document.createElement("div");
-container.appendChild(elem); // Put ShadowRoot host in document.
-var root = elem.createShadowRoot();
+SimpleTest.waitForExplicitFinish();
+
+var content = '<div class="tall" id="bodydiv"></div>' +
+              '<div id="container"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    var iframeWin = aDocument.defaultView;
 
-// A style element that will be appended into the ShadowRoot.
-var shadowStyle = document.createElement("style");
-shadowStyle.innerHTML = ".tall { height: 100px; } .fat { padding-left: inherit; }";
+    // Create ShadowRoot.
+    var container = aDocument.getElementById("container");
+    var elem = aDocument.createElement("div");
+    container.appendChild(elem); // Put ShadowRoot host in document.
+    var root = elem.attachShadow({mode: "open"});
 
-root.innerHTML = '<div id="divtostyle" class="tall fat"></div>';
-var divToStyle = root.getElementById("divtostyle");
+    // A style element that will be appended into the ShadowRoot.
+    var shadowStyle = aDocument.createElement("style");
+    shadowStyle.innerHTML = ".tall { height: 100px; } .fat { padding-left: inherit; }";
 
-// Make sure styleSheet counts are correct after appending a style to the ShadowRoot.
-is(document.styleSheets.length, 1, "There should only be one style sheet on the document from the test style sheet.");
-is(root.styleSheets.length, 0, "The ShadowRoot should have no style sheets.");
-root.appendChild(shadowStyle);
-is(document.styleSheets.length, 1, "Styles in the ShadowRoot element should not be accessible from the document.");
-is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet from the appened style.");
-is(root.styleSheets[0].ownerNode, shadowStyle, "First style in ShadowRoot should match the style that was just appended.");
+    root.innerHTML = '<div id="divtostyle" class="tall fat"></div>';
+    var divToStyle = root.getElementById("divtostyle");
 
-var dummyStyle = document.createElement("style");
-root.appendChild(dummyStyle);
-is(root.styleSheets.length, 2, "ShadowRoot should have an additional style from appending dummyStyle.");
-is(root.styleSheets[1].ownerNode, dummyStyle, "Second style in ShadowRoot should be the dummyStyle.");
-root.removeChild(dummyStyle);
-is(root.styleSheets.length, 1, "Removing dummyStyle should remove it from the ShadowRoot style sheets.");
-is(root.styleSheets[0].ownerNode, shadowStyle, "The style sheet remaining in the ShadowRoot should be shadowStyle.");
+    // Make sure styleSheet counts are correct after appending a style to the ShadowRoot.
+    is(aDocument.styleSheets.length, 0, "There shouldn't be any style sheet in the test frame document.");
+    is(root.styleSheets.length, 0, "The ShadowRoot should have no style sheets.");
+    root.appendChild(shadowStyle);
+    is(aDocument.styleSheets.length, 0, "Styles in the ShadowRoot element should not be accessible from the document.");
+    is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet from the appened style.");
+    is(root.styleSheets[0].ownerNode, shadowStyle, "First style in ShadowRoot should match the style that was just appended.");
 
-// Make sure that elements outside of the ShadowRoot are not affected by the ShadowRoot style.
-isnot(getComputedStyle(document.getElementById("bodydiv"), null).getPropertyValue("height"), "100px", "Style sheets in ShadowRoot should not apply to elements no in the ShadowRoot.");
+    var dummyStyle = aDocument.createElement("style");
+    root.appendChild(dummyStyle);
+    is(root.styleSheets.length, 2, "ShadowRoot should have an additional style from appending dummyStyle.");
+    is(root.styleSheets[1].ownerNode, dummyStyle, "Second style in ShadowRoot should be the dummyStyle.");
+    root.removeChild(dummyStyle);
+    is(root.styleSheets.length, 1, "Removing dummyStyle should remove it from the ShadowRoot style sheets.");
+    is(root.styleSheets[0].ownerNode, shadowStyle, "The style sheet remaining in the ShadowRoot should be shadowStyle.");
 
-// Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
+    // Make sure that elements outside of the ShadowRoot are not affected by the ShadowRoot style.
+    isnot(iframeWin.getComputedStyle(aDocument.getElementById("bodydiv"), null).getPropertyValue("height"), "100px", "Style sheets in ShadowRoot should not apply to elements no in the ShadowRoot.");
 
-// Tests for applyAuthorStyles.
-var authorStyle = document.createElement("style");
-authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
-document.body.appendChild(authorStyle);
+    // Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
+    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
+
+    // Tests for applyAuthorStyles.
+    var authorStyle = aDocument.createElement("style");
+    authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
+    aDocument.body.appendChild(authorStyle);
 
-is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
-isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
-root.applyAuthorStyles = true;
-is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
-is(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
-root.applyAuthorStyles = false;
-is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
-isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+    is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
+    isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+    root.applyAuthorStyles = true;
+    is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
+    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
+    root.applyAuthorStyles = false;
+    is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
+    isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
 
-// Test dynamic changes to style in ShadowRoot.
-root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
-divToStyle = root.getElementById("divtostyle");
-var dummyShadowStyle = document.createElement("style");
-dummyShadowStyle.innerHTML = ".dummy { height: 300px; }";
-root.appendChild(dummyShadowStyle);
-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "300px", "Dummy element in ShadowRoot should be styled by style in ShadowRoot.");
-dummyShadowStyle.innerHTML = ".dummy { height: 200px; }";
-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Dynamic changes to styles in ShadowRoot should change style of affected elements.");
+    // Test dynamic changes to style in ShadowRoot.
+    root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
+    divToStyle = root.getElementById("divtostyle");
+    var dummyShadowStyle = aDocument.createElement("style");
+    dummyShadowStyle.innerHTML = ".dummy { height: 300px; }";
+    root.appendChild(dummyShadowStyle);
+    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "300px", "Dummy element in ShadowRoot should be styled by style in ShadowRoot.");
+    dummyShadowStyle.innerHTML = ".dummy { height: 200px; }";
+    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Dynamic changes to styles in ShadowRoot should change style of affected elements.");
 
-// Test id selector in ShadowRoot style.
-root.innerHTML = '<style>#divtostyle { padding-top: 10px; }</style><div id="divtostyle"></div>';
-divToStyle = root.getElementById("divtostyle");
-is(getComputedStyle(divToStyle, null).getPropertyValue("padding-top"), "10px", "ID selector in style selector should match element.");
+    // Test id selector in ShadowRoot style.
+    root.innerHTML = '<style>#divtostyle { padding-top: 10px; }</style><div id="divtostyle"></div>';
+    divToStyle = root.getElementById("divtostyle");
+    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-top"), "10px", "ID selector in style selector should match element.");
 
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
 
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
@@ -1,42 +1,52 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=806506
 -->
 <head>
   <title>Test for ShadowRoot style order</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<div id="container"></div>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
-// Create ShadowRoot.
-var container = document.getElementById("container");
-var elem = document.createElement("div");
-container.appendChild(elem); // Put ShadowRoot host in document.
-var root = elem.createShadowRoot();
+
+SimpleTest.waitForExplicitFinish();
+
+var content = '<div id="container"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    var iframeWin = aDocument.defaultView;
 
-// Style elements that will be appended into the ShadowRoot.
-var tallShadowStyle = document.createElement("style");
-tallShadowStyle.innerHTML = ".tall { height: 100px; }";
+    // Create ShadowRoot.
+    var container = aDocument.getElementById("container");
+    var elem = aDocument.createElement("div");
+    container.appendChild(elem); // Put ShadowRoot host in document.
+    var root = elem.attachShadow({mode: "open"});
 
-var veryTallShadowStyle = document.createElement("style");
-veryTallShadowStyle.innerHTML = ".tall { height: 200px; }";
+    // Style elements that will be appended into the ShadowRoot.
+    var tallShadowStyle = aDocument.createElement("style");
+    tallShadowStyle.innerHTML = ".tall { height: 100px; }";
 
-var divToStyle = document.createElement("div");
-divToStyle.setAttribute("class", "tall");
-root.appendChild(divToStyle);
+    var veryTallShadowStyle = aDocument.createElement("style");
+    veryTallShadowStyle.innerHTML = ".tall { height: 200px; }";
+
+    var divToStyle = aDocument.createElement("div");
+    divToStyle.setAttribute("class", "tall");
+    root.appendChild(divToStyle);
 
-// Make sure the styles are applied in tree order.
-root.appendChild(tallShadowStyle);
-is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet.");
-is(window.getComputedStyle(divToStyle).getPropertyValue("height"), "100px", "Style in ShadowRoot should apply to elements in ShadowRoot.");
-root.appendChild(veryTallShadowStyle);
-is(root.styleSheets.length, 2, "ShadowRoot should have two style sheets.");
-is(window.getComputedStyle(divToStyle).getPropertyValue("height"), "200px", "Style in ShadowRoot should apply to elements in ShadowRoot in tree order.");
+    // Make sure the styles are applied in tree order.
+    root.appendChild(tallShadowStyle);
+    is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet.");
+    is(iframeWin.getComputedStyle(divToStyle).getPropertyValue("height"), "100px", "Style in ShadowRoot should apply to elements in ShadowRoot.");
+    root.appendChild(veryTallShadowStyle);
+    is(root.styleSheets.length, 2, "ShadowRoot should have two style sheets.");
+    is(iframeWin.getComputedStyle(divToStyle).getPropertyValue("height"), "200px", "Style in ShadowRoot should apply to elements in ShadowRoot in tree order.");
 
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+++ b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
@@ -1,28 +1,39 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=806506
 -->
 <head>
   <title>Test for styling fallback content</title>
+  <script type="text/javascript" src="head.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
-<div id="grabme"></div>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
-var host = document.getElementById("grabme");
-var shadow = host.createShadowRoot();
-shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><content><span id="innerspan">Hello</span></content></span>';
-var innerStyle = shadow.getElementById("innerstyle");
+
+SimpleTest.waitForExplicitFinish();
+
+var content = '<div id="grabme"></div>';
+setWebComponentsPrefAndCreateIframe(content)
+  .then((aDocument) => {
+    var iframeWin = aDocument.defaultView;
 
-innerStyle.innerHTML = '#innerspan { margin-top: 10px; }';
-var innerSpan = shadow.getElementById("innerspan");
-is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "10px", "Default content should be style by id selector.");
+    var host = aDocument.getElementById("grabme");
+    var shadow = host.attachShadow({mode: "open"});
+    shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><slot><span id="innerspan">Hello</span></slot></span>';
+    var innerStyle = shadow.getElementById("innerstyle");
 
-innerStyle.innerHTML = '#container > content > #innerspan { margin-top: 30px; }';
-is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "30px", "Default content should be style by child combinators.");
+    innerStyle.innerHTML = '#innerspan { margin-top: 10px; }';
+    var innerSpan = shadow.getElementById("innerspan");
+    is(iframeWin.getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "10px", "Default content should be style by id selector.");
+
+    innerStyle.innerHTML = '#container > slot > #innerspan { margin-top: 30px; }';
+    is(iframeWin.getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "30px", "Default content should be style by child combinators.");
+
+    SimpleTest.finish();
+  });
 </script>
 </body>
 </html>
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -308,16 +308,85 @@ ServiceWorkerManager::Init(ServiceWorker
   if (!actor) {
     MaybeStartShutdown();
     return;
   }
 
   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
 }
 
+RefPtr<GenericPromise>
+ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
+                                             ServiceWorkerRegistrationInfo* aRegistrationInfo)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
+
+  RefPtr<GenericPromise> ref;
+
+  const ServiceWorkerDescriptor& active =
+    aRegistrationInfo->GetActive()->Descriptor();
+
+  auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
+  if (entry) {
+    RefPtr<ServiceWorkerRegistrationInfo> old =
+      entry.Data()->mRegistrationInfo.forget();
+
+    ref = Move(entry.Data()->mClientHandle->Control(active));
+    entry.Data()->mRegistrationInfo = aRegistrationInfo;
+
+    if (old != aRegistrationInfo) {
+      StopControllingRegistration(old);
+      aRegistrationInfo->StartControllingClient();
+    }
+
+    Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+
+    return Move(ref);
+  }
+
+  RefPtr<ClientHandle> clientHandle =
+    ClientManager::CreateHandle(aClientInfo,
+                                SystemGroup::EventTargetFor(TaskCategory::Other));
+
+  ref = Move(clientHandle->Control(active));
+
+  aRegistrationInfo->StartControllingClient();
+
+  entry.OrInsert([&] {
+    return new ControlledClientData(clientHandle, aRegistrationInfo);
+  });
+
+  RefPtr<ServiceWorkerManager> self(this);
+  clientHandle->OnDetach()->Then(
+    SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
+    [self = Move(self), aClientInfo] {
+      self->StopControllingClient(aClientInfo);
+    });
+
+  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
+
+  return Move(ref);
+}
+
+void
+ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
+{
+  auto entry = mControlledClients.Lookup(aClientInfo.Id());
+  if (!entry) {
+    return;
+  }
+
+  RefPtr<ServiceWorkerRegistrationInfo> reg =
+    entry.Data()->mRegistrationInfo.forget();
+
+  entry.Remove();
+
+  StopControllingRegistration(reg);
+}
+
 void
 ServiceWorkerManager::MaybeStartShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown) {
     return;
   }
@@ -1427,18 +1496,23 @@ ServiceWorkerManager::GetActiveWorkerInf
   return registration->GetActive();
 }
 
 ServiceWorkerInfo*
 ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
 {
   AssertIsOnMainThread();
 
+  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
+  if (clientInfo.isNothing()) {
+    return nullptr;
+  }
+
   RefPtr<ServiceWorkerRegistrationInfo> registration;
-  GetDocumentRegistration(aDocument, getter_AddRefs(registration));
+  GetClientRegistration(clientInfo.ref(), getter_AddRefs(registration));
 
   if (!registration) {
     return nullptr;
   }
 
   return registration->GetActive();
 }
 
@@ -1573,17 +1647,17 @@ ServiceWorkerManager::WorkerIsIdle(Servi
   if (!reg) {
     return;
   }
 
   if (reg->GetActive() != aWorker) {
     return;
   }
 
-  if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
+  if (!reg->IsControllingClients() && reg->mPendingUninstall) {
     RemoveRegistration(reg);
     return;
   }
 
   reg->TryToActivateAsync();
 }
 
 already_AddRefed<ServiceWorkerJobQueue>
@@ -2264,25 +2338,35 @@ ServiceWorkerManager::RemoveScopeAndRegi
     return;
   }
 
   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
     entry.Data()->Cancel();
     entry.Remove();
   }
 
-  // The registration should generally only be removed if there are no controlled
-  // documents, but mControlledDocuments can contain references to potentially
-  // controlled docs.  This happens when the service worker is not active yet.
-  // We must purge these references since we are evicting the registration.
+  // Verify there are no controlled clients for the purged registration.
+  for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
+    auto& reg = iter.UserData()->mRegistrationInfo;
+    if (reg->mScope.Equals(aRegistration->mScope)) {
+      MOZ_DIAGNOSTIC_ASSERT(false,
+                            "controlled client when removing registration");
+      iter.Remove();
+      break;
+    }
+  }
+
+  // Registration lifecycle is managed via mControlledClients now.  Do not
+  // assert on on mControlledDocuments as races may cause this to still be
+  // set when the registration is destroyed.
   for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
     ServiceWorkerRegistrationInfo* reg = iter.UserData();
-    MOZ_ASSERT(reg);
     if (reg->mScope.Equals(aRegistration->mScope)) {
       iter.Remove();
+      break;
     }
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
@@ -2303,97 +2387,89 @@ ServiceWorkerManager::MaybeRemoveRegistr
 
 void
 ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aDoc);
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aDoc);
-  if (registration) {
+  if (registration && registration->GetActive() &&
+      aDoc->GetSandboxFlags() == 0) {
     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
     StartControllingADocument(registration, aDoc);
   }
 }
 
+bool
+ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
+                                       const ServiceWorkerDescriptor& aServiceWorker)
+{
+  AssertIsOnMainThread();
+
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
+  NS_ENSURE_TRUE(principal, false);
+
+  nsCOMPtr<nsIURI> scope;
+  nsresult rv =
+    NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  RefPtr<ServiceWorkerRegistrationInfo> registration =
+    GetServiceWorkerRegistrationInfo(principal, scope);
+  NS_ENSURE_TRUE(registration, false);
+
+  StartControllingClient(aClientInfo, registration);
+
+  return true;
+}
+
 void
 ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aDoc);
-  RefPtr<ServiceWorkerRegistrationInfo> registration;
-  mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
-  // A document which was uncontrolled does not maintain that state itself, so
-  // it will always call MaybeStopControlling() even if there isn't an
-  // associated registration. So this check is required.
-  if (registration) {
-    StopControllingADocument(registration);
-  }
+  mControlledDocuments.Remove(aDoc);
 }
 
 void
-ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
+ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aDoc);
   // We perform these success path navigation update steps when the
   // document tells us its more or less done loading.  This avoids
   // slowing down page load and also lets pages consistently get
   // updatefound events when they fire.
   //
   // 9.8.20 If respondWithEntered is false, then:
   // 9.8.22 Else: (respondWith was entered and succeeded)
   //    If request is a non-subresource request, then: Invoke Soft Update
   //    algorithm.
-  RefPtr<ServiceWorkerRegistrationInfo> registration;
-  mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
-  if (registration) {
-    registration->MaybeScheduleUpdate();
+  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
+  if (data && data->mRegistrationInfo) {
+    data->mRegistrationInfo->MaybeScheduleUpdate();
   }
 }
 
-RefPtr<GenericPromise>
+void
 ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                                                 nsIDocument* aDoc)
 {
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(aDoc);
 
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-  auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
-  MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
-#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
-
-  RefPtr<GenericPromise> ref = GenericPromise::CreateAndResolve(true, __func__);
-
-  aRegistration->StartControllingADocument();
   mControlledDocuments.Put(aDoc, aRegistration);
-
-  // Mark the document's ClientSource as controlled using the ClientHandle
-  // interface.  While we could get at the ClientSource directly from the
-  // document here, our goal is to move ServiceWorkerManager to a separate
-  // process.  Using the ClientHandle supports this remote operation.
-  ServiceWorkerInfo* activeWorker = aRegistration->GetActive();
-  Maybe<ClientInfo> clientInfo = aDoc->GetClientInfo();
-  if (activeWorker && clientInfo.isSome()) {
-    RefPtr<ClientHandle> clientHandle =
-      ClientManager::CreateHandle(clientInfo.ref(),
-                                  SystemGroup::EventTargetFor(TaskCategory::Other));
-    ref = Move(clientHandle->Control(activeWorker->Descriptor()));
-  }
-
-  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
-  return Move(ref);
 }
 
 void
-ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
+ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
-  aRegistration->StopControllingADocument();
-  if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
+  aRegistration->StopControllingClient();
+  if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
     return;
   }
 
   if (aRegistration->mPendingUninstall) {
     RemoveRegistration(aRegistration);
     return;
   }
 
@@ -2713,20 +2789,17 @@ ServiceWorkerManager::DispatchFetchEvent
         //       initial about:blank global.  See bug 1419620 and the spec
         //       issue here: https://github.com/w3c/ServiceWorker/issues/1232
       }
 
       if (clientInfo.isSome()) {
         // First, attempt to mark the reserved client controlled directly.  This
         // will update the controlled status in the ClientManagerService in the
         // parent.  It will also eventually propagate back to the ClientSource.
-        RefPtr<ClientHandle> clientHandle =
-          ClientManager::CreateHandle(clientInfo.ref(),
-                                      SystemGroup::EventTargetFor(TaskCategory::Other));
-        clientHandle->Control(serviceWorker->Descriptor());
+        StartControllingClient(clientInfo.ref(), registration);
       }
 
       // But we also note the reserved state on the LoadInfo.  This allows the
       // ClientSource to be updated immediately after the nsIChannel starts.
       // This is necessary to have the correct controller in place for immediate
       // follow-on requests.
       loadInfo->SetController(serviceWorker->Descriptor());
     }
@@ -2780,30 +2853,31 @@ ServiceWorkerManager::IsAvailable(nsIPri
   MOZ_ASSERT(aURI);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
   return registration && registration->GetActive();
 }
 
 nsresult
-ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
-                                              ServiceWorkerRegistrationInfo** aRegistrationInfo)
+ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
+                                            ServiceWorkerRegistrationInfo** aRegistrationInfo)
 {
-  RefPtr<ServiceWorkerRegistrationInfo> registration;
-  if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
+  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
+  if (!data || !data->mRegistrationInfo) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // If the document is controlled, the current worker MUST be non-null.
-  if (!registration->GetActive()) {
+  if (!data->mRegistrationInfo->GetActive()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  registration.forget(aRegistrationInfo);
+  RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
+  ref.forget(aRegistrationInfo);
   return NS_OK;
 }
 
 /*
  * The .controller is for the registration associated with the document when
  * the document was loaded.
  */
 NS_IMETHODIMP
@@ -3164,35 +3238,40 @@ ServiceWorkerManager::MaybeClaimClient(n
   RefPtr<GenericPromise> ref;
 
   // Same origin check
   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
     ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
     return ref.forget();
   }
 
+  Maybe<ClientInfo> clientInfo(aDocument->GetClientInfo());
+  if (NS_WARN_IF(clientInfo.isNothing())) {
+    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                                          __func__);
+    return ref.forget();
+  }
+
   // The registration that should be controlling the client
   RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
     GetServiceWorkerRegistrationInfo(aDocument);
 
   // The registration currently controlling the client
   RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
-  GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
+  GetClientRegistration(clientInfo.ref(),
+                        getter_AddRefs(controllingRegistration));
 
   if (aWorkerRegistration != matchingRegistration ||
       aWorkerRegistration == controllingRegistration) {
     ref = GenericPromise::CreateAndResolve(true, __func__);
     return ref.forget();
   }
 
-  if (controllingRegistration) {
-    StopControllingADocument(controllingRegistration);
-  }
-
-  ref = StartControllingADocument(aWorkerRegistration, aDocument);
+  StartControllingADocument(aWorkerRegistration, aDocument);
+  ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
   return ref.forget();
 }
 
 already_AddRefed<GenericPromise>
 ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
                                        const ServiceWorkerDescriptor& aServiceWorker)
 {
   RefPtr<GenericPromise> ref;
@@ -3243,41 +3322,29 @@ ServiceWorkerManager::SetSkipWaitingFlag
 void
 ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
 {
   AssertIsOnMainThread();
 
   RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
   MOZ_DIAGNOSTIC_ASSERT(activeWorker);
 
-  AutoTArray<nsCOMPtr<nsIDocument>, 16> docList;
-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
-    if (iter.UserData() != aRegistration) {
-      continue;
-    }
-
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
-    if (NS_WARN_IF(!doc)) {
+  AutoTArray<RefPtr<ClientHandle>, 16> handleList;
+  for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
+    if (iter.UserData()->mRegistrationInfo != aRegistration) {
       continue;
     }
 
-    docList.AppendElement(doc.forget());
+    handleList.AppendElement(iter.UserData()->mClientHandle);
   }
 
-  // Fire event after iterating mControlledDocuments is done to prevent
+  // Fire event after iterating mControlledClients is done to prevent
   // modification by reentering from the event handlers during iteration.
-  for (auto& doc : docList) {
-    Maybe<ClientInfo> clientInfo = doc->GetClientInfo();
-    if (clientInfo.isNothing()) {
-      continue;
-    }
-    RefPtr<ClientHandle> clientHandle =
-      ClientManager::CreateHandle(clientInfo.ref(),
-                                  SystemGroup::EventTargetFor(TaskCategory::Other));
-    clientHandle->Control(activeWorker->Descriptor());
+  for (auto& handle : handleList) {
+    handle->Control(activeWorker->Descriptor());
   }
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
                                       const nsACString& aScope) const
 {
   MOZ_ASSERT(aPrincipal);
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -16,16 +16,17 @@
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ClientHandle.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerCommon.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
 #include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
@@ -100,16 +101,31 @@ public:
 
   struct RegistrationDataPerPrincipal;
   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
 
   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
 
   nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
 
+  struct ControlledClientData
+  {
+    RefPtr<ClientHandle> mClientHandle;
+    RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
+
+    ControlledClientData(ClientHandle* aClientHandle,
+                         ServiceWorkerRegistrationInfo* aRegistrationInfo)
+      : mClientHandle(aClientHandle)
+      , mRegistrationInfo(aRegistrationInfo)
+    {
+    }
+  };
+
+  nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
+
   // Track all documents that have attempted to register a service worker for a
   // given scope.
   typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
   nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
 
   // Track all intercepted navigation channels for a given scope.  Channels are
   // placed in the appropriate list before dispatch the FetchEvent to the worker
   // thread and removed once FetchEvent processing dispatches back to the main
@@ -299,17 +315,17 @@ public:
   AddRegistrationEventListener(const nsAString& aScope,
                                ServiceWorkerRegistrationListener* aListener);
 
   NS_IMETHOD
   RemoveRegistrationEventListener(const nsAString& aScope,
                                   ServiceWorkerRegistrationListener* aListener);
 
   void
-  MaybeCheckNavigationUpdate(nsIDocument* aDoc);
+  MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
 
   nsresult
   SendPushEvent(const nsACString& aOriginAttributes,
                 const nsACString& aScope,
                 const nsAString& aMessageId,
                 const Maybe<nsTArray<uint8_t>>& aData);
 
   nsresult
@@ -323,16 +339,23 @@ public:
 
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   Init(ServiceWorkerRegistrar* aRegistrar);
 
+  RefPtr<GenericPromise>
+  StartControllingClient(const ClientInfo& aClientInfo,
+                         ServiceWorkerRegistrationInfo* aRegistrationInfo);
+
+  void
+  StopControllingClient(const ClientInfo& aClientInfo);
+
   void
   MaybeStartShutdown();
 
   already_AddRefed<ServiceWorkerJobQueue>
   GetOrCreateJobQueue(const nsACString& aOriginSuffix,
                       const nsACString& aScope);
 
   void
@@ -344,18 +367,18 @@ private:
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   Update(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
-  GetDocumentRegistration(nsIDocument* aDoc,
-                          ServiceWorkerRegistrationInfo** aRegistrationInfo);
+  GetClientRegistration(const ClientInfo& aClientInfo,
+                        ServiceWorkerRegistrationInfo** aRegistrationInfo);
 
   nsresult
   GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
                            const nsAString& aScope,
                            WhichServiceWorker aWhichWorker,
                            nsISupports** aServiceWorker);
 
   ServiceWorkerInfo*
@@ -370,22 +393,22 @@ private:
                                             WhichServiceWorker aWhichOne);
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
 
-  RefPtr<GenericPromise>
+  void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
 
   void
-  StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
+  StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
@@ -76,32 +76,30 @@ ServiceWorkerRegistrationInfo::Clear()
 
   NotifyChromeRegistrationListeners();
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
     const nsACString& aScope,
     nsIPrincipal* aPrincipal,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
-  : mControlledDocumentsCounter(0)
+  : mControlledClientsCounter(0)
   , mUpdateState(NoUpdate)
   , mCreationTime(PR_Now())
   , mCreationTimeStamp(TimeStamp::Now())
   , mLastUpdateTime(0)
   , mUpdateViaCache(aUpdateViaCache)
   , mScope(aScope)
   , mPrincipal(aPrincipal)
   , mPendingUninstall(false)
 {}
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
-  if (IsControllingDocuments()) {
-    NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
-  }
+  MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
 }
 
 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
 
 NS_IMETHODIMP
 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
 {
   AssertIsOnMainThread();
@@ -243,17 +241,17 @@ ServiceWorkerRegistrationInfo::TryToActi
 
 /*
  * TryToActivate should not be called directly, use TryToActivateAsync instead.
  */
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   AssertIsOnMainThread();
-  bool controlling = IsControllingDocuments();
+  bool controlling = IsControllingClients();
   bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
   bool idle = IsIdle();
   if (idle && (!controlling || skipWaiting)) {
     Activate();
   }
 }
 
 void
--- a/dom/workers/ServiceWorkerRegistrationInfo.h
+++ b/dom/workers/ServiceWorkerRegistrationInfo.h
@@ -12,17 +12,17 @@
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 class ServiceWorkerRegistrationInfo final
   : public nsIServiceWorkerRegistrationInfo
 {
-  uint32_t mControlledDocumentsCounter;
+  uint32_t mControlledClientsCounter;
 
   enum
   {
     NoUpdate,
     NeedTimeCheckAndUpdate,
     NeedUpdate
   } mUpdateState;
 
@@ -74,32 +74,32 @@ public:
 
     return newest.forget();
   }
 
   already_AddRefed<ServiceWorkerInfo>
   GetServiceWorkerInfoById(uint64_t aId);
 
   void
-  StartControllingADocument()
+  StartControllingClient()
   {
-    ++mControlledDocumentsCounter;
+    ++mControlledClientsCounter;
   }
 
   void
-  StopControllingADocument()
+  StopControllingClient()
   {
-    MOZ_ASSERT(mControlledDocumentsCounter);
-    --mControlledDocumentsCounter;
+    MOZ_ASSERT(mControlledClientsCounter);
+    --mControlledClientsCounter;
   }
 
   bool
-  IsControllingDocuments() const
+  IsControllingClients() const
   {
-    return mActiveWorker && mControlledDocumentsCounter;
+    return mActiveWorker && mControlledClientsCounter;
   }
 
   void
   Clear();
 
   void
   TryToActivateAsync();
 
--- a/dom/workers/ServiceWorkerUnregisterJob.cpp
+++ b/dom/workers/ServiceWorkerUnregisterJob.cpp
@@ -133,17 +133,17 @@ ServiceWorkerUnregisterJob::Unregister()
   // "Set registration's uninstalling flag."
   registration->mPendingUninstall = true;
 
   // "Resolve promise with true"
   mResult = true;
   InvokeResultCallbacks(NS_OK);
 
   // "If no service worker client is using registration..."
-  if (!registration->IsControllingDocuments() && registration->IsIdle()) {
+  if (!registration->IsControllingClients() && registration->IsIdle()) {
     // "Invoke [[Clear Registration]]..."
     swm->RemoveRegistration(registration);
   }
 
   Finish(NS_OK);
 }
 
 } // namespace workers
--- a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
+++ b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
@@ -14,20 +14,16 @@
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   if (!parent) {
     info("skip_waiting_scope/index.html shouldn't be launched directly!");
   }
 
-  navigator.serviceWorker.ready.then(function() {
-    parent.postMessage("READY", "*");
-  });
-
   navigator.serviceWorker.oncontrollerchange = function() {
     parent.postMessage({
       event: "controllerchange",
       controllerScriptURL: navigator.serviceWorker.controller &&
                            navigator.serviceWorker.controller.scriptURL
     }, "*");
   }
 
--- a/dom/workers/test/serviceworkers/test_skip_waiting.html
+++ b/dom/workers/test/serviceworkers/test_skip_waiting.html
@@ -8,45 +8,36 @@
   <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
+<script src="utils.js"></script>
 <script class="testbody" type="text/javascript">
   var registration, iframe, content;
 
   function start() {
     return navigator.serviceWorker.register("worker.js",
                                             {scope: "./skip_waiting_scope/"});
   }
 
-  function waitForActivated(swr) {
+  async function waitForActivated(swr) {
     registration = swr;
-    var promise = new Promise(function(resolve, reject) {
-      window.onmessage = function(e) {
-        if (e.data === "READY") {
-          ok(true, "Active worker is activated now");
-          resolve();
-        } else {
-          ok(false, "Wrong value. Somenting went wrong");
-          resolve();
-        }
-      }
-    });
+    await waitForState(registration.installing, "activated")
 
     iframe = document.createElement("iframe");
     iframe.setAttribute("src", "skip_waiting_scope/index.html");
 
     content = document.getElementById("content");
     content.appendChild(iframe);
 
-    return promise;
+    await new Promise(resolve => iframe.onload = resolve);
   }
 
   function checkWhetherItSkippedWaiting() {
     var promise = new Promise(function(resolve, reject) {
       window.onmessage = function (evt) {
         if (evt.data.event === "controllerchange") {
           ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"),
              "The controller changed after skiping the waiting step");
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -77,19 +77,20 @@ function runTest(aExpectedResponses) {
       ok(false, "Expected " + expected.status + " got " + status);
     }
     responsesIndex++;
   };
 }
 
 // Verify that we can register and intercept a 3rd party iframe with
 // the given cookie policy.
-function testShouldIntercept(policy, done) {
+function testShouldIntercept(behavior, lifetime, done) {
   SpecialPowers.pushPrefEnv({"set": [
-      ["network.cookie.cookieBehavior", policy]
+      ["network.cookie.cookieBehavior", behavior],
+      ["network.cookie.lifetimePolicy", lifetime],
   ]}, function() {
     runTest([{
       status: "ok"
     }, {
       status: "registrationdone",
       next: function() {
         iframe.addEventListener("load", testIframeLoaded);
         iframe.src = origin + basePath + "iframe1.html";
@@ -116,19 +117,20 @@ function testShouldIntercept(policy, don
         done();
       }
     }]);
   });
 }
 
 // Verify that we cannot register a service worker in a 3rd party
 // iframe with the given cookie policy.
-function testShouldNotRegister(policy, done) {
+function testShouldNotRegister(behavior, lifetime, done) {
   SpecialPowers.pushPrefEnv({"set": [
-      ["network.cookie.cookieBehavior", policy]
+      ["network.cookie.cookieBehavior", behavior],
+      ["network.cookie.lifetimePolicy", lifetime],
   ]}, function() {
     runTest([{
       status: "registrationfailed",
       next: function() {
         iframe.addEventListener("load", testIframeLoaded);
         iframe.src = origin + basePath + "iframe1.html";
       }
     }, {
@@ -147,28 +149,30 @@ function testShouldNotRegister(policy, d
       }
     }]);
   });
 }
 
 // Verify that if a service worker is already registered a 3rd
 // party iframe will still not be intercepted with the given cookie
 // policy.
-function testShouldNotIntercept(policy, done) {
+function testShouldNotIntercept(behavior, lifetime, done) {
   SpecialPowers.pushPrefEnv({"set": [
-      ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
+    ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
+    ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
   ]}, function() {
     runTest([{
       status: "ok"
     }, {
       status: "registrationdone",
       next: function() {
         iframe.addEventListener("load", testIframeLoaded);
         SpecialPowers.pushPrefEnv({"set": [
-            ["network.cookie.cookieBehavior", policy],
+            ["network.cookie.cookieBehavior", behavior],
+            ["network.cookie.lifetimePolicy", lifetime],
           ]}, function() {
             iframe.src = origin + basePath + "iframe1.html";
           });
       }
     }, {
       status: "networkresponse",
     }, {
       status: "worker-networkresponse",
@@ -181,17 +185,18 @@ function testShouldNotIntercept(policy, 
         iframe.src = thirdPartyOrigin + basePath + "unregister.html";
       }
     }, {
       status: "uncontrolled",
     }, {
       status: "getregistrationfailed",
       next: function() {
         SpecialPowers.pushPrefEnv({"set": [
-            ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT],
+            ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
+            ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
           ]}, function() {
             iframe.src = thirdPartyOrigin + basePath + "unregister.html";
           });
       }
     }, {
       status: "controlled",
     }, {
       status: "unregistrationdone",
@@ -199,41 +204,59 @@ function testShouldNotIntercept(policy, 
         window.onmessage = null;
         ok(true, "Test finished successfully");
         done();
       }
     }]);
   });
 }
 
-const COOKIE_BEHAVIOR_ACCEPT        = 0;
-const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
-const COOKIE_BEHAVIOR_REJECT        = 2;
-const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
+const BEHAVIOR_ACCEPT        = 0;
+const BEHAVIOR_REJECTFOREIGN = 1;
+const BEHAVIOR_REJECT        = 2;
+const BEHAVIOR_LIMITFOREIGN  = 3;
+
+const LIFETIME_EXPIRE        = 0;
+const LIFETIME_SESSION       = 2;
 
 let steps = [() => {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["browser.dom.window.dump.enabled", true],
-    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
+    ["network.cookie.cookieBehavior", BEHAVIOR_ACCEPT],
+    ["network.cookie.lifetimePolicy", LIFETIME_EXPIRE],
   ]}, next);
 }, () => {
-  testShouldIntercept(COOKIE_BEHAVIOR_ACCEPT, next);
+  testShouldIntercept(BEHAVIOR_ACCEPT, LIFETIME_EXPIRE, next);
+}, () => {
+  testShouldNotRegister(BEHAVIOR_REJECTFOREIGN, LIFETIME_EXPIRE, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_REJECTFOREIGN, LIFETIME_EXPIRE, next);
 }, () => {
-  testShouldNotRegister(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
+  testShouldNotRegister(BEHAVIOR_REJECT, LIFETIME_EXPIRE, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_REJECT, LIFETIME_EXPIRE, next);
 }, () => {
-  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
+  testShouldNotRegister(BEHAVIOR_LIMITFOREIGN, LIFETIME_EXPIRE, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_LIMITFOREIGN, LIFETIME_EXPIRE, next);
 }, () => {
-  testShouldNotRegister(COOKIE_BEHAVIOR_REJECT, next);
+  testShouldNotIntercept(BEHAVIOR_ACCEPT, LIFETIME_SESSION, next);
 }, () => {
-  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECT, next);
+  testShouldNotRegister(BEHAVIOR_REJECTFOREIGN, LIFETIME_SESSION, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_REJECTFOREIGN, LIFETIME_SESSION, next);
 }, () => {
-  testShouldNotRegister(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
+  testShouldNotRegister(BEHAVIOR_REJECT, LIFETIME_SESSION, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_REJECT, LIFETIME_SESSION, next);
 }, () => {
-  testShouldNotIntercept(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
+  testShouldNotRegister(BEHAVIOR_LIMITFOREIGN, LIFETIME_SESSION, next);
+}, () => {
+  testShouldNotIntercept(BEHAVIOR_LIMITFOREIGN, LIFETIME_SESSION, next);
 }];
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
@@ -8,24 +8,25 @@
   <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content"></div>
 <pre id="test"></pre>
+<script src="utils.js"></script>
 <script class="testbody" type="text/javascript">
   var registration;
   var promise;
 
-  function start() {
-    return navigator.serviceWorker.register("worker_updatefoundevent.js",
-                                            { scope: "./updatefoundevent.html" })
-      .then((swr) => registration = swr);
+  async function start() {
+    registration = await navigator.serviceWorker.register("worker_updatefoundevent.js",
+                                                          { scope: "./updatefoundevent.html" })
+    await waitForState(registration.installing, 'activated');
   }
 
   function startWaitForUpdateFound() {
     registration.onupdatefound = function(e) {
     }
 
     promise = new Promise(function(resolve, reject) {
       window.onmessage = function(e) {
--- a/dom/workers/test/serviceworkers/worker_updatefoundevent.js
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
@@ -1,23 +1,18 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-onactivate = function(e) {
-  e.waitUntil(new Promise(function(resolve, reject) {
-    registration.onupdatefound = function(e) {
-      clients.matchAll().then(function(clients) {
-        if (!clients.length) {
-          reject("No clients found");
-        }
+registration.onupdatefound = function(e) {
+  clients.matchAll().then(function(clients) {
+    if (!clients.length) {
+      reject("No clients found");
+    }
 
-        if (registration.scope.match(/updatefoundevent\.html$/)) {
-          clients[0].postMessage("finish");
-          resolve();
-        } else {
-          dump("Scope did not match");
-        }
-      }, reject);
+    if (registration.scope.match(/updatefoundevent\.html$/)) {
+      clients[0].postMessage("finish");
+    } else {
+      dump("Scope did not match");
     }
-  }));
+  });
 }
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -16,29 +16,16 @@
 #include "mozilla/layers/WebRenderLayersLogging.h"
 #include "nsAString.h"
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsRegion.h"                   // for nsRegion, nsIntRegion
 #include "nscore.h"                     // for nsACString, etc
 
 namespace mozilla {
 
-namespace wr {
-struct ColorF;
-
-struct TypedSize2D_f32__LayerPixel;
-typedef TypedSize2D_f32__LayerPixel LayerSize;
-typedef LayerSize LayoutSize;
-
-struct TypedRect_f32__LayerPixel;
-typedef TypedRect_f32__LayerPixel LayerRect;
-typedef LayerRect LayoutRect;
-
-} // namespace wr
-
 namespace gfx {
 template <class units, class F> struct RectTyped;
 } // namespace gfx
 
 enum class ImageFormat;
 
 namespace layers {
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -361,16 +361,33 @@ typedef GenericFlingAnimation FlingAnima
  * Units: screen pixels (for distance)
  *        screen pixels per millisecond (for velocity)
  *
  * \li\b apz.paint_skipping.enabled
  * When APZ is scrolling and sending repaint requests to the main thread, often
  * the main thread doesn't actually need to do a repaint. This pref allows the
  * main thread to skip doing those repaints in cases where it doesn't need to.
  *
+ * \li\b apz.pinch_lock.mode
+ * The preferred pinch locking style. See PinchLockMode for possible values.
+ *
+ * \li\b apz.pinch_lock.scroll_lock_threshold
+ * Pinch locking is triggered if the user scrolls more than this distance
+ * and pinches less than apz.pinch_lock.span_lock_threshold.\n
+ * Units: (real-world, i.e. screen) inches
+ *
+ * \li\b apz.pinch_lock.span_breakout_threshold
+ * Distance in inches the user must pinch before lock can be broken.\n
+ * Units: (real-world, i.e. screen) inches measured between two touch points
+ *
+ * \li\b apz.pinch_lock.span_lock_threshold
+ * Pinch locking is triggered if the user pinches less than this distance
+ * and scrolls more than apz.pinch_lock.scroll_lock_threshold.\n
+ * Units: (real-world, i.e. screen) inches measured between two touch points
+ *
  * \li\b apz.popups.enabled
  * Determines whether APZ is used for XUL popup widgets with remote content.
  * Ideally, this should always be true, but it is currently not well tested, and
  * has known issues, so needs to be prefable.
  *
  * \li\b apz.record_checkerboarding
  * Whether or not to record detailed info on checkerboarding events.
  *
@@ -754,16 +771,17 @@ AsyncPanZoomController::AsyncPanZoomCont
      // mTreeManager must be initialized before GetFrameTime() is called
      mTreeManager(aTreeManager),
      mFrameMetrics(mScrollMetadata.GetMetrics()),
      mRecursiveMutex("AsyncPanZoomController"),
      mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
      mX(this),
      mY(this),
      mPanDirRestricted(false),
+     mPinchLocked(false),
      mZoomConstraints(false, false,
         mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1),
         mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)),
      mLastSampleTime(GetFrameTime()),
      mLastCheckerboardReport(GetFrameTime()),
      mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
      mState(NOTHING),
      mNotificationBlockers(0),
@@ -859,16 +877,21 @@ AsyncPanZoomController::GetSecondTapTole
   return (gfxPrefs::APZSecondTapTolerance() * APZCTreeManager::GetDPI());
 }
 
 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
 {
   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
 }
 
+/* static */AsyncPanZoomController::PinchLockMode AsyncPanZoomController::GetPinchLockMode()
+{
+  return static_cast<PinchLockMode>(gfxPrefs::APZPinchLockMode());
+}
+
 bool
 AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint32_t aTouchPoints) {
   if (aTouchPoints == 0) {
     // Cant' do anything with zero touch points
     return false;
   }
 
   // This logic is simplified, erring on the side of returning true
@@ -1323,16 +1346,17 @@ nsEventStatus AsyncPanZoomController::On
   OnTouchEndOrCancel();
   CancelAnimationAndGestureState();
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
 
+  mPinchLocked = false;
   mPinchPaintTimerSet = false;
   // Note that there may not be a touch block at this point, if we received the
   // PinchGestureEvent directly from widget code without any touch events.
   if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   // If zooming is not allowed, this is a two-finger pan.
@@ -1364,19 +1388,36 @@ nsEventStatus AsyncPanZoomController::On
   if (HasReadyTouchBlock() && !GetCurrentTouchBlock()->TouchActionAllowsPinchZoom()) {
     return nsEventStatus_eIgnore;
   }
 
   if (mState != PINCHING) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
+  ParentLayerCoord spanDistance = fabsf(aEvent.mPreviousSpan - aEvent.mCurrentSpan);
+  ParentLayerPoint focusPoint, focusChange;
+  {
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
+
+    focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
+    focusChange = mLastZoomFocus - focusPoint;
+    mLastZoomFocus = focusPoint;
+  }
+
+  HandlePinchLocking(
+    ToScreenCoordinates(ParentLayerPoint(0, spanDistance), focusPoint).Length(),
+    ToScreenCoordinates(focusChange, focusPoint));
+  bool allowZoom = mZoomConstraints.mAllowZoom && !mPinchLocked;
+
   // If zooming is not allowed, this is a two-finger pan.
   // Tracking panning distance and velocity.
-  if (!mZoomConstraints.mAllowZoom) {
+  // UpdateWithTouchAtDevicePoint() acquires the tree lock, so
+  // it cannot be called while the mRecursiveMutex lock is held.
+  if (!allowZoom) {
     mX.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.x, 0, aEvent.mTime);
     mY.UpdateWithTouchAtDevicePoint(aEvent.mLocalFocusPoint.y, 0, aEvent.mTime);
   }
 
   if (!gfxPrefs::APZAllowZooming()) {
     if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
       controller->NotifyPinchGesture(aEvent.mType, GetGuid(),
           ViewAs<LayoutDevicePixel>(aEvent.mCurrentSpan - aEvent.mPreviousSpan,
@@ -1392,21 +1433,18 @@ nsEventStatus AsyncPanZoomController::On
   // page rect and the composition bounds).
   MOZ_ASSERT(mFrameMetrics.IsRootContent());
   MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame());
 
   {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
 
     CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom().ToScaleFactor();
-    ParentLayerPoint focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft();
     CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom();
 
-    ParentLayerPoint focusChange = mLastZoomFocus - focusPoint;
-    mLastZoomFocus = focusPoint;
     // If displacing by the change in focus point will take us off page bounds,
     // then reduce the displacement such that it doesn't.
     focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
     focusChange.y -= mY.DisplacementWillOverscrollAmount(focusChange.y);
     ScrollBy(focusChange / userZoom);
 
     // If the span is zero or close to it, we don't want to process this zoom
     // change because we're going to get wonky numbers for the spanRatio. So
@@ -1434,22 +1472,19 @@ nsEventStatus AsyncPanZoomController::On
     realMinZoom.scale = std::max(realMinZoom.scale,
                                  mFrameMetrics.GetCompositionBounds().Width() / mFrameMetrics.GetScrollableRect().Width());
     realMinZoom.scale = std::max(realMinZoom.scale,
                                  mFrameMetrics.GetCompositionBounds().Height() / mFrameMetrics.GetScrollableRect().Height());
     if (realMaxZoom < realMinZoom) {
       realMaxZoom = realMinZoom;
     }
 
-    bool doScale = (spanRatio > 1.0 && userZoom < realMaxZoom) ||
-                   (spanRatio < 1.0 && userZoom > realMinZoom);
-
-    if (!mZoomConstraints.mAllowZoom) {
-      doScale = false;
-    }
+    bool doScale = allowZoom && (
+                   (spanRatio > 1.0 && userZoom < realMaxZoom) ||
+                   (spanRatio < 1.0 && userZoom > realMinZoom));
 
     if (doScale) {
       spanRatio = clamped(spanRatio,
                           realMinZoom.scale / userZoom.scale,
                           realMaxZoom.scale / userZoom.scale);
 
       // Note that the spanRatio here should never put us into OVERSCROLL_BOTH because
       // up above we clamped it.
@@ -2596,16 +2631,34 @@ void AsyncPanZoomController::HandlePanni
           mX.SetAxisLocked(false);
           SetState(PANNING);
         }
       }
     }
   }
 }
 
+void AsyncPanZoomController::HandlePinchLocking(ScreenCoord spanDistance, ScreenPoint focusChange) {
+  if (mPinchLocked) {
+    if (GetPinchLockMode() == PINCH_STICKY) {
+      ScreenCoord spanBreakoutThreshold = gfxPrefs::APZPinchLockSpanBreakoutThreshold() * APZCTreeManager::GetDPI();
+      mPinchLocked = !(spanDistance > spanBreakoutThreshold);
+    }
+  } else {
+    if (GetPinchLockMode() != PINCH_FREE) {
+      ScreenCoord spanLockThreshold = gfxPrefs::APZPinchLockSpanLockThreshold() * APZCTreeManager::GetDPI();
+      ScreenCoord scrollLockThreshold = gfxPrefs::APZPinchLockScrollLockThreshold() * APZCTreeManager::GetDPI();
+
+      if (spanDistance < spanLockThreshold && focusChange.Length() > scrollLockThreshold) {
+        mPinchLocked = true;
+      }
+    }
+  }
+}
+
 nsEventStatus
 AsyncPanZoomController::StartPanning(const ParentLayerPoint& aStartPoint) {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   float dx = mX.PanDistance(aStartPoint.x);
   float dy = mY.PanDistance(aStartPoint.y);
 
   double angle = atan2(dy, dx); // range [-pi, pi]
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -651,16 +651,21 @@ protected:
   void HandlePanning(double angle);
 
   /**
    * Update the panning state and axis locks.
    */
   void HandlePanningUpdate(const ScreenPoint& aDelta);
 
   /**
+   * Set and update the pinch lock
+   */
+  void HandlePinchLocking(ScreenCoord spanDistance, ScreenPoint focusChange);
+
+  /**
    * Sets up anything needed for panning. This takes us out of the "TOUCHING"
    * state and starts actually panning us.
    */
   nsEventStatus StartPanning(const ParentLayerPoint& aStartPoint);
 
   /**
    * Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for
    * both axes and factors in the time delta from the last update.
@@ -719,16 +724,24 @@ protected:
   enum AxisLockMode {
     FREE,     /* No locking at all */
     STANDARD, /* Default axis locking mode that remains locked until pan ends*/
     STICKY,   /* Allow lock to be broken, with hysteresis */
   };
 
   static AxisLockMode GetAxisLockMode();
 
+  enum PinchLockMode {
+    PINCH_FREE,     /* No locking at all */
+    PINCH_STANDARD, /* Default pinch locking mode that remains locked until pinch gesture ends*/
+    PINCH_STICKY,   /* Allow lock to be broken, with hysteresis */
+  };
+
+  static PinchLockMode GetPinchLockMode();
+
   // Helper function for OnSingleTapUp(), OnSingleTapConfirmed(), and
   // OnLongPressUp().
   nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType,
                                   const ScreenIntPoint& aPoint,
                                   mozilla::Modifiers aModifiers);
 
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
@@ -795,16 +808,20 @@ private:
 
   AxisX mX;
   AxisY mY;
 
   // This flag is set to true when we are in a axis-locked pan as a result of
   // the touch-action CSS property.
   bool mPanDirRestricted;
 
+  // This flag is set to true when we are in a pinch-locked state. ie: user
+  // is performing a two-finger pan rather than a pinch gesture
+  bool mPinchLocked;
+
   // Most up-to-date constraints on zooming. These should always be reasonable
   // values; for example, allowing a min zoom of 0.0 can cause very bad things
   // to happen.
   ZoomConstraints mZoomConstraints;
 
   // The last time the compositor has sampled the content transform for this
   // frame.
   TimeStamp mLastSampleTime;
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -132,24 +132,34 @@ public:
     bool found = false;
     auto i = mKeys.Length();
     while (i > 0) {
       --i;
       ImageKeyData& entry = mKeys[i];
       if (entry.mManager->IsDestroyed()) {
         mKeys.RemoveElementAt(i);
       } else if (entry.mManager == aManager) {
-        found = true;
-        if (entry.mInvalidations != aInvalidations) {
-          aManager->AddImageKeyForDiscard(entry.mImageKey);
+        WebRenderBridgeChild* wrBridge = aManager->WrBridge();
+        MOZ_ASSERT(wrBridge);
+
+        // Even if the manager is the same, its underlying WebRenderBridgeChild
+        // can change state. If our namespace differs, then our old key has
+        // already been discarded.
+        bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
+        if (!ownsKey || entry.mInvalidations != aInvalidations) {
+          if (ownsKey) {
+            aManager->AddImageKeyForDiscard(entry.mImageKey);
+          }
           entry.mInvalidations = aInvalidations;
-          entry.mImageKey = aManager->WrBridge()->GetNextImageKey();
+          entry.mImageKey = wrBridge->GetNextImageKey();
           aResources.AddExternalImage(mId, entry.mImageKey);
         }
+
         key = entry.mImageKey;
+        found = true;
       }
     }
 
     if (!found) {
       key = aManager->WrBridge()->GetNextImageKey();
       ImageKeyData data(aManager, key, aInvalidations);
       mKeys.AppendElement(Move(data));
       aResources.AddExternalImage(mId, key);
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -247,17 +247,17 @@ AsyncImagePipelineManager::UpdateWithout
     NS_ERROR("DataSourceSurface failed to map");
     return Nothing();
   }
 
   gfx::IntSize size = dSurf->GetSize();
   wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
 
   // Costly copy right here...
-  wr::Vec_u8 bytes;
+  wr::Vec<uint8_t> bytes;
   bytes.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
 
   if (aOp == TextureHost::UPDATE_IMAGE) {
     aResources.UpdateImageBuffer(aKey, descriptor, bytes);
   } else {
     aResources.AddImage(aKey, descriptor, bytes);
   }
 
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -168,17 +168,17 @@ ShmSegmentsReader::ShmSegmentsReader(con
         || shm.get<uint8_t>() == nullptr) {
       mChunkSize = 0;
       return;
     }
   }
 }
 
 bool
-ShmSegmentsReader::ReadLarge(const layers::OffsetRange& aRange, wr::Vec_u8& aInto)
+ShmSegmentsReader::ReadLarge(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto)
 {
   // source = zero is for small allocs.
   MOZ_RELEASE_ASSERT(aRange.source() != 0);
   if (aRange.source() > mLargeAllocs.Length()) {
     return false;
   }
   size_t id = aRange.source() - 1;
   const ipc::Shmem& shm = mLargeAllocs[id];
@@ -188,17 +188,17 @@ ShmSegmentsReader::ReadLarge(const layer
 
   uint8_t* srcPtr = shm.get<uint8_t>();
   aInto.PushBytes(Range<uint8_t>(srcPtr, aRange.length()));
 
   return true;
 }
 
 bool
-ShmSegmentsReader::Read(const layers::OffsetRange& aRange, wr::Vec_u8& aInto)
+ShmSegmentsReader::Read(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto)
 {
   if (aRange.length() == 0) {
     return true;
   }
 
   if (aRange.source() != 0) {
     return ReadLarge(aRange, aInto);
   }
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -46,20 +46,20 @@ protected:
   size_t mChunkSize;
 };
 
 class ShmSegmentsReader {
 public:
   ShmSegmentsReader(const nsTArray<ipc::Shmem>& aSmallShmems,
                     const nsTArray<ipc::Shmem>& aLargeShmems);
 
-  bool Read(const layers::OffsetRange& aRange, wr::Vec_u8& aInto);
+  bool Read(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto);
 
 protected:
-  bool ReadLarge(const layers::OffsetRange& aRange, wr::Vec_u8& aInto);
+  bool ReadLarge(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto);
 
   const nsTArray<ipc::Shmem>& mSmallAllocs;
   const nsTArray<ipc::Shmem>& mLargeAllocs;
   size_t mChunkSize;
 };
 
 class IpcResourceUpdateQueue {
 public:
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -284,78 +284,78 @@ WebRenderBridgeParent::UpdateResources(c
                                        wr::ResourceUpdateQueue& aUpdates)
 {
   wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
 
   for (const auto& cmd : aResourceUpdates) {
     switch (cmd.type()) {
       case OpUpdateResource::TOpAddImage: {
         const auto& op = cmd.get_OpAddImage();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.AddImage(op.key(), op.descriptor(), bytes);
         break;
       }
       case OpUpdateResource::TOpUpdateImage: {
         const auto& op = cmd.get_OpUpdateImage();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.UpdateImageBuffer(op.key(), op.descriptor(), bytes);
         break;
       }
       case OpUpdateResource::TOpAddBlobImage: {
         const auto& op = cmd.get_OpAddBlobImage();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.AddBlobImage(op.key(), op.descriptor(), bytes);
         break;
       }
       case OpUpdateResource::TOpUpdateBlobImage: {
         const auto& op = cmd.get_OpUpdateBlobImage();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.UpdateBlobImage(op.key(), op.descriptor(), bytes, wr::ToDeviceUintRect(op.dirtyRect()));
         break;
       }
       case OpUpdateResource::TOpAddExternalImage: {
         const auto& op = cmd.get_OpAddExternalImage();
         if (!AddExternalImage(op.externalImageId(), op.key(), aUpdates)) {
           return false;
         }
         break;
       }
       case OpUpdateResource::TOpAddRawFont: {
         const auto& op = cmd.get_OpAddRawFont();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.AddRawFont(op.key(), bytes, op.fontIndex());
         break;
       }
       case OpUpdateResource::TOpAddFontDescriptor: {
         const auto& op = cmd.get_OpAddFontDescriptor();
-        wr::Vec_u8 bytes;
+        wr::Vec<uint8_t> bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.AddFontDescriptor(op.key(), bytes, op.fontIndex());
         break;
       }
       case OpUpdateResource::TOpAddFontInstance: {
         const auto& op = cmd.get_OpAddFontInstance();
-        wr::Vec_u8 variations;
+        wr::Vec<uint8_t> variations;
         if (!reader.Read(op.variations(), variations)) {
             return false;
         }
         aUpdates.AddFontInstance(op.instanceKey(), op.fontKey(),
                                  op.glyphSize(),
                                  op.options().ptrOr(nullptr),
                                  op.platformOptions().ptrOr(nullptr),
                                  variations);
@@ -435,17 +435,17 @@ WebRenderBridgeParent::AddExternalImage(
   DataSourceSurface::MappedSurface map;
   if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
     NS_ERROR("DataSourceSurface failed to map");
     return false;
   }
 
   IntSize size = dSurf->GetSize();
   wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
-  wr::Vec_u8 data;
+  wr::Vec<uint8_t> data;
   data.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
   aResources.AddImage(keys[0], descriptor, data);
   dSurf->Unmap();
 
   return true;
 }
 
 mozilla::ipc::IPCResult
@@ -606,17 +606,17 @@ WebRenderBridgeParent::RecvSetDisplayLis
   ProcessWebRenderParentCommands(aCommands);
 
   wr::ResourceUpdateQueue resources;
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
 
-  wr::Vec_u8 dlData(Move(dl));
+  wr::Vec<uint8_t> dlData(Move(dl));
 
   // If id namespaces do not match, it means the command is obsolete, probably
   // because the tab just moved to a new window.
   // In that case do not send the commands to webrender.
   if (mIdNamespace == aIdNamespace) {
     if (mWidget) {
       LayoutDeviceIntSize size = mWidget->GetClientSize();
       mApi->SetWindowParameters(size);
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -328,16 +328,20 @@ private:
   DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled",        APZMinimapVisibilityEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.one_touch_pinch.enabled",           APZOneTouchPinchEnabled, bool, true);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.overscroll.spring_stiffness",       APZOverscrollSpringStiffness, float, 0.001f);
   DECL_GFX_PREF(Live, "apz.overscroll.stop_distance_threshold", APZOverscrollStopDistanceThreshold, float, 5.0f);
   DECL_GFX_PREF(Live, "apz.paint_skipping.enabled",            APZPaintSkipping, bool, true);
   DECL_GFX_PREF(Live, "apz.peek_messages.enabled",             APZPeekMessages, bool, true);
+  DECL_GFX_PREF(Live, "apz.pinch_lock.mode",                   APZPinchLockMode, int32_t, 1);
+  DECL_GFX_PREF(Live, "apz.pinch_lock.scroll_lock_threshold",  APZPinchLockScrollLockThreshold, float, 1.0f / 32.0f);
+  DECL_GFX_PREF(Live, "apz.pinch_lock.span_breakout_threshold", APZPinchLockSpanBreakoutThreshold, float, 1.0f / 32.0f);
+  DECL_GFX_PREF(Live, "apz.pinch_lock.span_lock_threshold",    APZPinchLockSpanLockThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.popups.enabled",                    APZPopupsEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.printtree",                         APZPrintTree, bool, false);
   DECL_GFX_PREF(Live, "apz.record_checkerboarding",            APZRecordCheckerboarding, bool, false);
   DECL_GFX_PREF(Live, "apz.second_tap_tolerance",              APZSecondTapTolerance, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.test.fails_with_native_injection",  APZTestFailsWithNativeInjection, bool, false);
   DECL_GFX_PREF(Live, "apz.test.logging_enabled",              APZTestLoggingEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.touch_move_tolerance",              APZTouchMoveTolerance, float, 0.1f);
   DECL_GFX_PREF(Live, "apz.touch_start_tolerance",             APZTouchStartTolerance, float, 1.0f/4.5f);
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -260,17 +260,17 @@ WebRenderAPI::GenerateFrame(const nsTArr
 
 void
 WebRenderAPI::SetDisplayList(gfx::Color aBgColor,
                              Epoch aEpoch,
                              mozilla::LayerSize aViewportSize,
                              wr::WrPipelineId pipeline_id,
                              const LayoutSize& content_size,
                              wr::BuiltDisplayListDescriptor dl_descriptor,
-                             wr::Vec_u8& dl_data,
+                             wr::Vec<uint8_t>& dl_data,
                              ResourceUpdateQueue& aResources)
 {
   wr_api_set_display_list(mDocHandle,
                           ToColorF(aBgColor),
                           aEpoch,
                           aViewportSize.width, aViewportSize.height,
                           pipeline_id,
                           content_size,
@@ -500,27 +500,27 @@ ResourceUpdateQueue::~ResourceUpdateQueu
 void
 ResourceUpdateQueue::Clear()
 {
   wr_resource_updates_clear(mUpdates);
 }
 
 void
 ResourceUpdateQueue::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
-                              wr::Vec_u8& aBytes)
+                              wr::Vec<uint8_t>& aBytes)
 {
   wr_resource_updates_add_image(mUpdates,
                                 key,
                                 &aDescriptor,
                                 &aBytes.inner);
 }
 
 void
 ResourceUpdateQueue::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor,
-                                  wr::Vec_u8& aBytes)
+                                  wr::Vec<uint8_t>& aBytes)
 {
   wr_resource_updates_add_blob_image(mUpdates,
                                      key,
                                      &aDescriptor,
                                      &aBytes.inner);
 }
 
 void
@@ -547,28 +547,28 @@ ResourceUpdateQueue::AddExternalImageBuf
   AddExternalImage(aKey, aDescriptor, aHandle,
                    wr::WrExternalImageBufferType::ExternalBuffer,
                    channelIndex);
 }
 
 void
 ResourceUpdateQueue::UpdateImageBuffer(ImageKey aKey,
                                        const ImageDescriptor& aDescriptor,
-                                       wr::Vec_u8& aBytes)
+                                       wr::Vec<uint8_t>& aBytes)
 {
   wr_resource_updates_update_image(mUpdates,
                                    aKey,
                                    &aDescriptor,
                                    &aBytes.inner);
 }
 
 void
 ResourceUpdateQueue::UpdateBlobImage(ImageKey aKey,
                                      const ImageDescriptor& aDescriptor,
-                                     wr::Vec_u8& aBytes,
+                                     wr::Vec<uint8_t>& aBytes,
                                      const wr::DeviceUintRect& aDirtyRect)
 {
   wr_resource_updates_update_blob_image(mUpdates,
                                         aKey,
                                         &aDescriptor,
                                         &aBytes.inner,
                                         aDirtyRect);
 }
@@ -590,40 +590,40 @@ ResourceUpdateQueue::UpdateExternalImage
 
 void
 ResourceUpdateQueue::DeleteImage(ImageKey aKey)
 {
   wr_resource_updates_delete_image(mUpdates, aKey);
 }
 
 void
-ResourceUpdateQueue::AddRawFont(wr::FontKey aKey, wr::Vec_u8& aBytes, uint32_t aIndex)
+ResourceUpdateQueue::AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
 {
   wr_resource_updates_add_raw_font(mUpdates, aKey, &aBytes.inner, aIndex);
 }
 
 void
-ResourceUpdateQueue::AddFontDescriptor(wr::FontKey aKey, wr::Vec_u8& aBytes, uint32_t aIndex)
+ResourceUpdateQueue::AddFontDescriptor(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
 {
   wr_resource_updates_add_font_descriptor(mUpdates, aKey, &aBytes.inner, aIndex);
 }
 
 void
 ResourceUpdateQueue::DeleteFont(wr::FontKey aKey)
 {
   wr_resource_updates_delete_font(mUpdates, aKey);
 }
 
 void
 ResourceUpdateQueue::AddFontInstance(wr::FontInstanceKey aKey,
                                      wr::FontKey aFontKey,
                                      float aGlyphSize,
                                      const wr::FontInstanceOptions* aOptions,
                                      const wr::FontInstancePlatformOptions* aPlatformOptions,
-                                     wr::Vec_u8& aVariations)
+                                     wr::Vec<uint8_t>& aVariations)
 {
   wr_resource_updates_add_font_instance(mUpdates, aKey, aFontKey, aGlyphSize,
                                         aOptions, aPlatformOptions,
                                         &aVariations.inner);
 }
 
 void
 ResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey)
@@ -1126,17 +1126,17 @@ DisplayListBuilder::PushBorder(const wr:
 
 void
 DisplayListBuilder::PushBorderImage(const wr::LayoutRect& aBounds,
                                     const wr::LayoutRect& aClip,
                                     bool aIsBackfaceVisible,
                                     const wr::BorderWidths& aWidths,
                                     wr::ImageKey aImage,
                                     const wr::NinePatchDescriptor& aPatch,
-                                    const wr::SideOffsets2D_f32& aOutset,
+                                    const wr::SideOffsets2D<float>& aOutset,
                                     const wr::RepeatMode& aRepeatHorizontal,
                                     const wr::RepeatMode& aRepeatVertical)
 {
   wr_dp_push_border_image(mWrState, aBounds, aClip, aIsBackfaceVisible,
                           aWidths, aImage, aPatch, aOutset,
                           aRepeatHorizontal, aRepeatVertical);
 }
 
@@ -1144,34 +1144,34 @@ void
 DisplayListBuilder::PushBorderGradient(const wr::LayoutRect& aBounds,
                                        const wr::LayoutRect& aClip,
                                        bool aIsBackfaceVisible,
                                        const wr::BorderWidths& aWidths,
                                        const wr::LayoutPoint& aStartPoint,
                                        const wr::LayoutPoint& aEndPoint,
                                        const nsTArray<wr::GradientStop>& aStops,
                                        wr::ExtendMode aExtendMode,
-                                       const wr::SideOffsets2D_f32& aOutset)
+                                       const wr::SideOffsets2D<float>& aOutset)
 {
   wr_dp_push_border_gradient(mWrState, aBounds, aClip, aIsBackfaceVisible,
                              aWidths, aStartPoint, aEndPoint,
                              aStops.Elements(), aStops.Length(),
                              aExtendMode, aOutset);
 }
 
 void
 DisplayListBuilder::PushBorderRadialGradient(const wr::LayoutRect& aBounds,
                                              const wr::LayoutRect& aClip,
                                              bool aIsBackfaceVisible,
                                              const wr::BorderWidths& aWidths,
                                              const wr::LayoutPoint& aCenter,
                                              const wr::LayoutSize& aRadius,
                                              const nsTArray<wr::GradientStop>& aStops,
                                              wr::ExtendMode aExtendMode,
-                                             const wr::SideOffsets2D_f32& aOutset)
+                                             const wr::SideOffsets2D<float>& aOutset)
 {
   wr_dp_push_border_radial_gradient(
     mWrState, aBounds, aClip, aIsBackfaceVisible, aWidths, aCenter,
     aRadius, aStops.Elements(), aStops.Length(),
     aExtendMode, aOutset);
 }
 
 void
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -58,61 +58,61 @@ public:
   ~ResourceUpdateQueue();
   ResourceUpdateQueue(ResourceUpdateQueue&&);
   ResourceUpdateQueue(const ResourceUpdateQueue&) = delete;
   ResourceUpdateQueue& operator=(ResourceUpdateQueue&&);
   ResourceUpdateQueue& operator=(const ResourceUpdateQueue&) = delete;
 
   void AddImage(wr::ImageKey aKey,
                 const ImageDescriptor& aDescriptor,
-                wr::Vec_u8& aBytes);
+                wr::Vec<uint8_t>& aBytes);
 
   void AddBlobImage(wr::ImageKey aKey,
                     const ImageDescriptor& aDescriptor,
-                    wr::Vec_u8& aBytes);
+                    wr::Vec<uint8_t>& aBytes);
 
   void AddExternalImageBuffer(ImageKey key,
                               const ImageDescriptor& aDescriptor,
                               ExternalImageId aHandle);
 
   void AddExternalImage(ImageKey key,
                         const ImageDescriptor& aDescriptor,
                         ExternalImageId aExtID,
                         WrExternalImageBufferType aBufferType,
                         uint8_t aChannelIndex = 0);
 
   void UpdateImageBuffer(wr::ImageKey aKey,
                          const ImageDescriptor& aDescriptor,
-                         wr::Vec_u8& aBytes);
+                         wr::Vec<uint8_t>& aBytes);
 
   void UpdateBlobImage(wr::ImageKey aKey,
                        const ImageDescriptor& aDescriptor,
-                       wr::Vec_u8& aBytes,
+                       wr::Vec<uint8_t>& aBytes,
                        const wr::DeviceUintRect& aDirtyRect);
 
   void UpdateExternalImage(ImageKey aKey,
                            const ImageDescriptor& aDescriptor,
                            ExternalImageId aExtID,
                            wr::WrExternalImageBufferType aBufferType,
                            uint8_t aChannelIndex = 0);
 
   void DeleteImage(wr::ImageKey aKey);
 
-  void AddRawFont(wr::FontKey aKey, wr::Vec_u8& aBytes, uint32_t aIndex);
+  void AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex);
 
-  void AddFontDescriptor(wr::FontKey aKey, wr::Vec_u8& aBytes, uint32_t aIndex);
+  void AddFontDescriptor(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex);
 
   void DeleteFont(wr::FontKey aKey);
 
   void AddFontInstance(wr::FontInstanceKey aKey,
                        wr::FontKey aFontKey,
                        float aGlyphSize,
                        const wr::FontInstanceOptions* aOptions,
                        const wr::FontInstancePlatformOptions* aPlatformOptions,
-                       wr::Vec_u8& aVariations);
+                       wr::Vec<uint8_t>& aVariations);
 
   void DeleteFontInstance(wr::FontInstanceKey aKey);
 
   void Clear();
 
   // Try to avoid using this when possible.
   wr::ResourceUpdates* Raw() { return mUpdates; }
 
@@ -156,17 +156,17 @@ public:
   void SetWindowParameters(LayoutDeviceIntSize size);
 
   void SetDisplayList(gfx::Color aBgColor,
                       Epoch aEpoch,
                       mozilla::LayerSize aViewportSize,
                       wr::WrPipelineId pipeline_id,
                       const wr::LayoutSize& content_size,
                       wr::BuiltDisplayListDescriptor dl_descriptor,
-                      wr::Vec_u8& dl_data,
+                      wr::Vec<uint8_t>& dl_data,
                       ResourceUpdateQueue& aResources);
 
   void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId pipeline_id);
 
   void SetRootPipeline(wr::PipelineId aPipeline);
 
   void RemovePipeline(wr::PipelineId aPipeline);
 
@@ -356,39 +356,39 @@ public:
                   const wr::BorderRadius& aRadius);
 
   void PushBorderImage(const wr::LayoutRect& aBounds,
                        const wr::LayoutRect& aClip,
                        bool aIsBackfaceVisible,
                        const wr::BorderWidths& aWidths,
                        wr::ImageKey aImage,
                        const wr::NinePatchDescriptor& aPatch,
-                       const wr::SideOffsets2D_f32& aOutset,
+                       const wr::SideOffsets2D<float>& aOutset,
                        const wr::RepeatMode& aRepeatHorizontal,
                        const wr::RepeatMode& aRepeatVertical);
 
   void PushBorderGradient(const wr::LayoutRect& aBounds,
                           const wr::LayoutRect& aClip,
                           bool aIsBackfaceVisible,
                           const wr::BorderWidths& aWidths,
                           const wr::LayoutPoint& aStartPoint,
                           const wr::LayoutPoint& aEndPoint,
                           const nsTArray<wr::GradientStop>& aStops,
                           wr::ExtendMode aExtendMode,
-                          const wr::SideOffsets2D_f32& aOutset);
+                          const wr::SideOffsets2D<float>& aOutset);
 
   void PushBorderRadialGradient(const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 bool aIsBackfaceVisible,
                                 const wr::BorderWidths& aWidths,
                                 const wr::LayoutPoint& aCenter,
                                 const wr::LayoutSize& aRadius,
                                 const nsTArray<wr::GradientStop>& aStops,
                                 wr::ExtendMode aExtendMode,
-                                const wr::SideOffsets2D_f32& aOutset);
+                                const wr::SideOffsets2D<float>& aOutset);
 
   void PushText(const wr::LayoutRect& aBounds,
                 const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible,
                 const wr::ColorF& aColor,
                 wr::FontInstanceKey aFontKey,
                 Range<const wr::GlyphInstance> aGlyphBuffer,
                 const wr::GlyphOptions* aGlyphOptions = nullptr);
--- a/gfx/webrender_bindings/WebRenderTypes.cpp
+++ b/gfx/webrender_bindings/WebRenderTypes.cpp
@@ -6,19 +6,21 @@
 
 #include "WebRenderTypes.h"
 
 #include "mozilla/ipc/ByteBuf.h"
 
 namespace mozilla {
 namespace wr {
 
-Vec_u8::Vec_u8(mozilla::ipc::ByteBuf&& aSrc) {
-  inner.data = aSrc.mData;
-  inner.length = aSrc.mLen;
-  inner.capacity = aSrc.mCapacity;
-  aSrc.mData = nullptr;
-  aSrc.mLen = 0;
-  aSrc.mCapacity = 0;
+void
+Assign_WrVecU8(wr::WrVecU8& aVec, mozilla::ipc::ByteBuf&& aOther)
+{
+  aVec.data = aOther.mData;
+  aVec.length = aOther.mLen;
+  aVec.capacity = aOther.mCapacity;
+  aOther.mData = nullptr;
+  aOther.mLen = 0;
+  aOther.mCapacity = 0;
 }
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -456,38 +456,38 @@ static inline wr::BorderWidths ToBorderW
   bw.top = top;
   bw.right = right;
   bw.bottom = bottom;
   bw.left = left;
   return bw;
 }
 
 static inline wr::NinePatchDescriptor ToNinePatchDescriptor(uint32_t width, uint32_t height,
-                                                            const wr::SideOffsets2D_u32& slice)
+                                                            const wr::SideOffsets2D<uint32_t>& slice)
 {
   NinePatchDescriptor patch;
   patch.width = width;
   patch.height = height;
   patch.slice = slice;
   return patch;
 }
 
-static inline wr::SideOffsets2D_u32 ToSideOffsets2D_u32(uint32_t top, uint32_t right, uint32_t bottom, uint32_t left)
+static inline wr::SideOffsets2D<uint32_t> ToSideOffsets2D_u32(uint32_t top, uint32_t right, uint32_t bottom, uint32_t left)
 {
-  SideOffsets2D_u32 offset;
+  SideOffsets2D<uint32_t> offset;
   offset.top = top;
   offset.right = right;
   offset.bottom = bottom;
   offset.left = left;
   return offset;
 }
 
-static inline wr::SideOffsets2D_f32 ToSideOffsets2D_f32(float top, float right, float bottom, float left)
+static inline wr::SideOffsets2D<float> ToSideOffsets2D_f32(float top, float right, float bottom, float left)
 {
-  SideOffsets2D_f32 offset;
+  SideOffsets2D<float> offset;
   offset.top = top;
   offset.right = right;
   offset.bottom = bottom;
   offset.left = left;
   return offset;
 }
 
 static inline wr::RepeatMode ToRepeatMode(mozilla::StyleBorderImageRepeat repeatMode)
@@ -566,31 +566,39 @@ inline wr::ByteSlice RangeToByteSlice(mo
 inline mozilla::Range<const uint8_t> ByteSliceToRange(wr::ByteSlice aWrSlice) {
   return mozilla::Range<const uint8_t>(aWrSlice.buffer, aWrSlice.len);
 }
 
 inline mozilla::Range<uint8_t> MutByteSliceToRange(wr::MutByteSlice aWrSlice) {
   return mozilla::Range<uint8_t>(aWrSlice.buffer, aWrSlice.len);
 }
 
-struct Vec_u8 {
+void Assign_WrVecU8(wr::WrVecU8& aVec, mozilla::ipc::ByteBuf&& aOther);
+
+template<typename T>
+struct Vec;
+
+template<>
+struct Vec<uint8_t> {
   wr::WrVecU8 inner;
-  Vec_u8() {
+  Vec() {
     SetEmpty();
   }
-  Vec_u8(Vec_u8&) = delete;
-  Vec_u8(Vec_u8&& src) {
+  Vec(Vec&) = delete;
+  Vec(Vec&& src) {
     inner = src.inner;
     src.SetEmpty();
   }
 
-  explicit Vec_u8(mozilla::ipc::ByteBuf&& aSrc);
+  explicit Vec(mozilla::ipc::ByteBuf&& aSrc) {
+    Assign_WrVecU8(inner, std::move(aSrc));
+  }
 
-  Vec_u8&
-  operator=(Vec_u8&& src) {
+  Vec&
+  operator=(Vec&& src) {
     inner = src.inner;
     src.SetEmpty();
     return *this;
   }
 
   wr::WrVecU8
   Extract() {
     wr::WrVecU8 ret = inner;
@@ -607,17 +615,17 @@ struct Vec_u8 {
 
   size_t Length() { return inner.length; }
 
   void PushBytes(Range<uint8_t> aBytes)
   {
     wr_vec_u8_push_bytes(&inner, RangeToByteSlice(aBytes));
   }
 
-  ~Vec_u8() {
+  ~Vec() {
     if (inner.data) {
       wr_vec_u8_free(inner);
     }
   }
 };
 
 struct ByteBuffer
 {
--- a/gfx/webrender_bindings/cbindgen.toml
+++ b/gfx/webrender_bindings/cbindgen.toml
@@ -22,17 +22,16 @@ expand = ["euclid"]
 [fn]
 prefix = "WR_INLINE"
 postfix = "WR_FUNC"
 args = "Vertical"
 rename_args = "GeckoCase"
 
 [struct]
 derive_eq = true
-generic_template_specialization = false
 
 [enum]
 add_sentinel = true
 
 [defines]
 "target_os = windows" = "XP_WIN"
 "target_os = macos" = "XP_MACOSX"
 
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,26 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.2.2 */
+/* Generated with cbindgen:0.3.3 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/rlhunt/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
 
 #include <cstdint>
 #include <cstdlib>
 
-extern "C" {
-
 namespace mozilla {
 namespace wr {
 
 enum class BorderStyle : uint32_t {
   None = 0,
   Solid = 1,
   Double = 2,
   Dotted = 3,
@@ -232,28 +230,46 @@ enum class WrFilterOpType : uint32_t {
 
 enum class YuvColorSpace : uint32_t {
   Rec601 = 0,
   Rec709 = 1,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
-struct Arc_VecU8;
+template<typename T>
+struct Arc;
+
+// Geometry in the coordinate system of the render target (screen or intermediate
+// surface) in physical pixels.
+struct DevicePixel;
 
 struct DocumentHandle;
 
+// Geometry in a layer's local coordinate space (logical pixels).
+struct LayerPixel;
+
 // The renderer is responsible for submitting to the GPU the work prepared by the
 // RenderBackend.
 struct Renderer;
 
 // The resource updates for a given transaction (they must be applied in the same frame).
 struct ResourceUpdates;
 
-struct Vec_u8;
+// Offset in number of tiles.
+struct Tiles;
+
+// The default unit.
+struct UnknownUnit;
+
+template<typename T>
+struct Vec;
+
+// Geometry in the document's coordinate space (logical pixels).
+struct WorldPixel;
 
 struct WrProgramCache;
 
 struct WrRenderedEpochs;
 
 struct WrState;
 
 struct WrThreadPool;
@@ -280,71 +296,72 @@ struct FontKey {
   uint32_t mHandle;
 
   bool operator==(const FontKey& aOther) const {
     return mNamespace == aOther.mNamespace &&
            mHandle == aOther.mHandle;
   }
 };
 
-typedef FontKey WrFontKey;
+using WrFontKey = FontKey;
 
-typedef Arc_VecU8 ArcVecU8;
+using VecU8 = Vec<uint8_t>;
 
-typedef Vec_u8 VecU8;
+using ArcVecU8 = Arc<VecU8>;
 
 struct Epoch {
   uint32_t mHandle;
 
   bool operator==(const Epoch& aOther) const {
     return mHandle == aOther.mHandle;
   }
   bool operator<(const Epoch& aOther) const {
     return mHandle < aOther.mHandle;
   }
   bool operator<=(const Epoch& aOther) const {
     return mHandle <= aOther.mHandle;
   }
 };
 
-typedef Epoch WrEpoch;
+using WrEpoch = Epoch;
 
 // This type carries no valuable semantics for WR. However, it reflects the fact that
 // clients (Servo) may generate pipelines by different semi-independent sources.
 // These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
 // Having this extra Id field enables them to generate `PipelineId` without collision.
-typedef uint32_t PipelineSourceId;
+using PipelineSourceId = uint32_t;
 
 // From the point of view of WR, `PipelineId` is completely opaque and generic as long as
 // it's clonable, serializable, comparable, and hashable.
 struct PipelineId {
   PipelineSourceId mNamespace;
   uint32_t mHandle;
 
   bool operator==(const PipelineId& aOther) const {
     return mNamespace == aOther.mNamespace &&
            mHandle == aOther.mHandle;
   }
 };
 
-typedef PipelineId WrPipelineId;
+using WrPipelineId = PipelineId;
 
-struct TypedSize2D_f32__LayerPixel {
-  float width;
-  float height;
+template<typename T, typename U>
+struct TypedSize2D {
+  T width;
+  T height;
 
-  bool operator==(const TypedSize2D_f32__LayerPixel& aOther) const {
+  bool operator==(const TypedSize2D& aOther) const {
     return width == aOther.width &&
            height == aOther.height;
   }
 };
 
-typedef TypedSize2D_f32__LayerPixel LayerSize;
+using LayerSize = TypedSize2D<float, LayerPixel>;
 
-typedef LayerSize LayoutSize;
+using LayoutSize = LayerSize;
 
 // Describes the memory layout of a display list.
 //
 // A display list consists of some number of display list items, followed by a number of display
 // items.
 struct BuiltDisplayListDescriptor {
   // The first IPC time stamp: before any work has been done
   uint64_t builder_start_time;
@@ -377,46 +394,52 @@ struct WrOpacityProperty {
   float opacity;
 
   bool operator==(const WrOpacityProperty& aOther) const {
     return id == aOther.id &&
            opacity == aOther.opacity;
   }
 };
 
+// Geometry in a stacking context's local coordinate space (logical pixels).
+//
+// For now layout pixels are equivalent to layer pixels, but it may change.
+using LayoutPixel = LayerPixel;
+
 // A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
 //
 // Transforms can be parametrized over the source and destination units, to describe a
 // transformation from a space to another.
 // For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
 // takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
 //
 // Transforms expose a set of convenience methods for pre- and post-transformations.
 // A pre-transformation corresponds to adding an operation that is applied before
 // the rest of the transformation, while a post-transformation adds an operation
 // that is applied after.
-struct TypedTransform3D_f32__LayoutPixel__LayoutPixel {
-  float m11;
-  float m12;
-  float m13;
-  float m14;
-  float m21;
-  float m22;
-  float m23;
-  float m24;
-  float m31;
-  float m32;
-  float m33;
-  float m34;
-  float m41;
-  float m42;
-  float m43;
-  float m44;
+template<typename T, typename Src, typename Dst>
+struct TypedTransform3D {
+  T m11;
+  T m12;
+  T m13;
+  T m14;
+  T m21;
+  T m22;
+  T m23;
+  T m24;
+  T m31;
+  T m32;
+  T m33;
+  T m34;
+  T m41;
+  T m42;
+  T m43;
+  T m44;
 
-  bool operator==(const TypedTransform3D_f32__LayoutPixel__LayoutPixel& aOther) const {
+  bool operator==(const TypedTransform3D& aOther) const {
     return m11 == aOther.m11 &&
            m12 == aOther.m12 &&
            m13 == aOther.m13 &&
            m14 == aOther.m14 &&
            m21 == aOther.m21 &&
            m22 == aOther.m22 &&
            m23 == aOther.m23 &&
            m24 == aOther.m24 &&
@@ -426,37 +449,38 @@ struct TypedTransform3D_f32__LayoutPixel
            m34 == aOther.m34 &&
            m41 == aOther.m41 &&
            m42 == aOther.m42 &&
            m43 == aOther.m43 &&
            m44 == aOther.m44;
   }
 };
 
-typedef TypedTransform3D_f32__LayoutPixel__LayoutPixel LayoutTransform;
+using LayoutTransform = TypedTransform3D<float, LayoutPixel, LayoutPixel>;
 
 struct WrTransformProperty {
   uint64_t id;
   LayoutTransform transform;
 };
 
-typedef IdNamespace WrIdNamespace;
+using WrIdNamespace = IdNamespace;
 
 // A 2d Point tagged with a unit.
-struct TypedPoint2D_f32__WorldPixel {
-  float x;
-  float y;
+template<typename T, typename U>
+struct TypedPoint2D {
+  T x;
+  T y;
 
-  bool operator==(const TypedPoint2D_f32__WorldPixel& aOther) const {
+  bool operator==(const TypedPoint2D& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
 };
 
-typedef TypedPoint2D_f32__WorldPixel WorldPoint;
+using WorldPoint = TypedPoint2D<float, WorldPixel>;
 
 // Represents RGBA screen colors with floating point numbers.
 //
 // All components must be between 0.0 and 1.0.
 // An alpha value of 1.0 is opaque while 0.0 is fully transparent.
 struct ColorF {
   float r;
   float g;
@@ -466,41 +490,31 @@ struct ColorF {
   bool operator==(const ColorF& aOther) const {
     return r == aOther.r &&
            g == aOther.g &&
            b == aOther.b &&
            a == aOther.a;
   }
 };
 
-// A 2d Point tagged with a unit.
-struct TypedPoint2D_f32__LayerPixel {
-  float x;
-  float y;
+// A 2d Rectangle optionally tagged with a unit.
+template<typename T, typename U>
+struct TypedRect {
+  TypedPoint2D<T, U> origin;
+  TypedSize2D<T, U> size;
 
-  bool operator==(const TypedPoint2D_f32__LayerPixel& aOther) const {
-    return x == aOther.x &&
-           y == aOther.y;
-  }
-};
-
-// A 2d Rectangle optionally tagged with a unit.
-struct TypedRect_f32__LayerPixel {
-  TypedPoint2D_f32__LayerPixel origin;
-  TypedSize2D_f32__LayerPixel size;
-
-  bool operator==(const TypedRect_f32__LayerPixel& aOther) const {
+  bool operator==(const TypedRect& aOther) const {
     return origin == aOther.origin &&
            size == aOther.size;
   }
 };
 
-typedef TypedRect_f32__LayerPixel LayerRect;
+using LayerRect = TypedRect<float, LayerPixel>;
 
-typedef LayerRect LayoutRect;
+using LayoutRect = LayerRect;
 
 struct BorderRadius {
   LayoutSize top_left;
   LayoutSize top_right;
   LayoutSize bottom_left;
   LayoutSize bottom_right;
 
   bool operator==(const BorderRadius& aOther) const {
@@ -536,17 +550,17 @@ struct ImageKey {
            mHandle == aOther.mHandle;
   }
   bool operator!=(const ImageKey& aOther) const {
     return mNamespace != aOther.mNamespace ||
            mHandle != aOther.mHandle;
   }
 };
 
-typedef ImageKey WrImageKey;
+using WrImageKey = ImageKey;
 
 struct WrImageMask {
   WrImageKey image;
   LayoutRect rect;
   bool repeat;
 
   bool operator==(const WrImageMask& aOther) const {
     return image == aOther.image &&
@@ -568,29 +582,30 @@ struct StickyOffsetBounds {
 
   bool operator==(const StickyOffsetBounds& aOther) const {
     return min == aOther.min &&
            max == aOther.max;
   }
 };
 
 // A 2d Vector tagged with a unit.
-struct TypedVector2D_f32__LayerPixel {
-  float x;
-  float y;
+template<typename T, typename U>
+struct TypedVector2D {
+  T x;
+  T y;
 
-  bool operator==(const TypedVector2D_f32__LayerPixel& aOther) const {
+  bool operator==(const TypedVector2D& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
 };
 
-typedef TypedVector2D_f32__LayerPixel LayerVector2D;
+using LayerVector2D = TypedVector2D<float, LayerPixel>;
 
-typedef LayerVector2D LayoutVector2D;
+using LayoutVector2D = LayerVector2D;
 
 struct BorderWidths {
   float left;
   float top;
   float right;
   float bottom;
 
   bool operator==(const BorderWidths& aOther) const {
@@ -606,64 +621,53 @@ struct BorderSide {
   BorderStyle style;
 
   bool operator==(const BorderSide& aOther) const {
     return color == aOther.color &&
            style == aOther.style;
   }
 };
 
-typedef TypedPoint2D_f32__LayerPixel LayerPoint;
+using LayerPoint = TypedPoint2D<float, LayerPixel>;
 
-typedef LayerPoint LayoutPoint;
+using LayoutPoint = LayerPoint;
 
 struct GradientStop {
   float offset;
   ColorF color;
 
   bool operator==(const GradientStop& aOther) const {
     return offset == aOther.offset &&
            color == aOther.color;
   }
 };
 
-// The default side offset type with no unit.
-struct SideOffsets2D_f32 {
-  float top;
-  float right;
-  float bottom;
-  float left;
+template<typename T, typename U>
+struct TypedSideOffsets2D {
+  T top;
+  T right;
+  T bottom;
+  T left;
 
-  bool operator==(const SideOffsets2D_f32& aOther) const {
+  bool operator==(const TypedSideOffsets2D& aOther) const {
     return top == aOther.top &&
            right == aOther.right &&
            bottom == aOther.bottom &&
            left == aOther.left;
   }
 };
 
 // The default side offset type with no unit.
-struct SideOffsets2D_u32 {
-  uint32_t top;
-  uint32_t right;
-  uint32_t bottom;
-  uint32_t left;
-
-  bool operator==(const SideOffsets2D_u32& aOther) const {
-    return top == aOther.top &&
-           right == aOther.right &&
-           bottom == aOther.bottom &&
-           left == aOther.left;
-  }
-};
+template<typename T>
+using SideOffsets2D = TypedSideOffsets2D<T, UnknownUnit>;
 
 struct NinePatc