Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Thu, 08 Feb 2018 21:02:00 +0200
changeset 402990 c5120bcaf7bdcb5cdb06a02b60bd5bfe6a867d06
parent 402989 992a8e2e042b2b7b3bc3e270901c0aacc3413b5f (current diff)
parent 402934 c82860cdbeafc9b267be7adb206cbaae85647845 (diff)
child 402991 a427450a2cc92727561a847babb57a005b7d3e49
child 403036 366477a25fd7eeab9d3838ad6f1f223008ed0260
push id99696
push usernbeleuzu@mozilla.com
push dateThu, 08 Feb 2018 19:36:57 +0000
treeherdermozilla-inbound@a427450a2cc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
c5120bcaf7bd / 60.0a1 / 20180208220029 / files
nightly linux64
c5120bcaf7bd / 60.0a1 / 20180208220029 / files
nightly mac
c5120bcaf7bd / 60.0a1 / 20180208220029 / files
nightly win32
c5120bcaf7bd / 60.0a1 / 20180208220029 / files
nightly win64
c5120bcaf7bd / 60.0a1 / 20180208220029 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/base/nsGkAtomList.h
toolkit/content/widgets/menu.xml
--- a/accessible/base/XULMap.h
+++ b/accessible/base/XULMap.h
@@ -1,14 +1,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+XULMAP_TYPE(checkbox, XULCheckboxAccessible)
+XULMAP_TYPE(dropMarker, XULDropmarkerAccessible)
+XULMAP_TYPE(findbar, XULToolbarAccessible)
+XULMAP_TYPE(groupbox, XULGroupboxAccessible)
+XULMAP_TYPE(listbox, XULListboxAccessibleWrap)
+XULMAP_TYPE(listhead, XULColumAccessible)
+XULMAP_TYPE(listheader, XULColumnItemAccessible)
+XULMAP_TYPE(listitem, XULListitemAccessible)
+XULMAP_TYPE(menu, XULMenuitemAccessibleWrap)
+XULMAP_TYPE(menubar, XULMenubarAccessible)
+XULMAP_TYPE(menucaption, XULMenuitemAccessibleWrap)
+XULMAP_TYPE(menuitem, XULMenuitemAccessibleWrap)
+XULMAP_TYPE(menulist, XULComboboxAccessible)
 XULMAP_TYPE(menuseparator, XULMenuSeparatorAccessible)
+XULMAP_TYPE(notification, XULAlertAccessible)
+XULMAP_TYPE(progressmeter, XULProgressMeterAccessible)
+XULMAP_TYPE(radio, XULRadioButtonAccessible)
+XULMAP_TYPE(radiogroup, XULRadioGroupAccessible)
+XULMAP_TYPE(richlistbox, XULListboxAccessibleWrap)
+XULMAP_TYPE(richlistitem, XULListitemAccessible)
 XULMAP_TYPE(statusbar, XULStatusBarAccessible)
+XULMAP_TYPE(tab, XULTabAccessible)
+XULMAP_TYPE(tabpanels, XULTabpanelsAccessible)
+XULMAP_TYPE(tabs, XULTabsAccessible)
+XULMAP_TYPE(toolbarseparator, XULToolbarSeparatorAccessible)
+XULMAP_TYPE(toolbarspacer, XULToolbarSeparatorAccessible)
+XULMAP_TYPE(toolbarspring, XULToolbarSeparatorAccessible)
+XULMAP_TYPE(treecol, XULColumnItemAccessible)
+XULMAP_TYPE(treecolpicker, XULButtonAccessible)
+XULMAP_TYPE(treecols, XULTreeColumAccessible)
+XULMAP_TYPE(toolbar, XULToolbarAccessible)
+XULMAP_TYPE(tooltip, XULTooltipAccessible)
 
 XULMAP(
   image,
   [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
     if (aContent->IsElement() &&
         aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
       return new XULToolbarButtonAccessible(aContent, aContext->Document());
     }
@@ -17,8 +47,54 @@ XULMAP(
     if (!aContent->IsElement() ||
         !aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext)) {
       return nullptr;
     }
 
     return new ImageAccessibleWrap(aContent, aContext->Document());
   }
 )
+
+XULMAP(
+  listcell,
+  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+    // Only create cells if there's more than one per row.
+    nsIContent* listItem = aContent->GetParent();
+    if (!listItem) {
+      return nullptr;
+    }
+
+    for (nsIContent* child = listItem->GetFirstChild(); child;
+         child = child->GetNextSibling()) {
+      if (child->IsXULElement(nsGkAtoms::listcell) && child != aContent) {
+        return new XULListCellAccessibleWrap(aContent, aContext->Document());
+      }
+    }
+
+    return nullptr;
+  }
+)
+
+XULMAP(
+  tree,
+  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+    nsIContent* child = nsTreeUtils::GetDescendantChild(aContent,
+                                                        nsGkAtoms::treechildren);
+    if (!child)
+      return nullptr;
+
+    nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
+    if (!treeFrame)
+      return nullptr;
+
+    RefPtr<nsTreeColumns> treeCols = treeFrame->Columns();
+    int32_t count = 0;
+    treeCols->GetCount(&count);
+
+    // Outline of list accessible.
+    if (count == 1) {
+      return new XULTreeAccessible(aContent, aContext->Document(), treeFrame);
+    }
+
+    // Table or tree table accessible.
+    return new XULTreeGridAccessibleWrap(aContent, aContext->Document(), treeFrame);
+  }
+)
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1455,81 +1455,31 @@ nsAccessibilityService::CreateAccessible
   if (role.EqualsLiteral("outerdoc")) {
     RefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
     return accessible.forget();
   }
 
   RefPtr<Accessible> accessible;
 #ifdef MOZ_XUL
   // XUL controls
-  if (role.EqualsLiteral("xul:alert")) {
-    accessible = new XULAlertAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:button")) {
+  if (role.EqualsLiteral("xul:button")) {
     accessible = new XULButtonAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:checkbox")) {
-    accessible = new XULCheckboxAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:colorpicker")) {
     accessible = new XULColorPickerAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:colorpickertile")) {
     accessible = new XULColorPickerTileAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:combobox")) {
     accessible = new XULComboboxAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:tabpanels")) {
-      accessible = new XULTabpanelsAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:dropmarker")) {
-      accessible = new XULDropmarkerAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:groupbox")) {
-      accessible = new XULGroupboxAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:link")) {
     accessible = new XULLinkAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:listbox")) {
-      accessible = new XULListboxAccessibleWrap(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:listcell")) {
-    // Only create cells if there's more than one per row.
-    nsIContent* listItem = aContent->GetParent();
-    if (!listItem)
-      return nullptr;
-
-    for (nsIContent* child = listItem->GetFirstChild(); child;
-         child = child->GetNextSibling()) {
-      if (child->IsXULElement(nsGkAtoms::listcell) && child != aContent) {
-        accessible = new XULListCellAccessibleWrap(aContent, aDoc);
-        break;
-      }
-    }
-
-  } else if (role.EqualsLiteral("xul:listhead")) {
-    accessible = new XULColumAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:listheader")) {
-    accessible = new XULColumnItemAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:listitem")) {
-    accessible = new XULListitemAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:menubar")) {
-    accessible = new XULMenubarAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:menulist")) {
-    accessible = new XULComboboxAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:menuitem")) {
-    accessible = new XULMenuitemAccessibleWrap(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:menupopup")) {
 #ifdef MOZ_ACCESSIBILITY_ATK
     // ATK considers this node to be redundant when within menubars, and it makes menu
     // navigation with assistive technologies more difficult
     // XXX In the future we will should this for consistency across the nsIAccessible
     // implementations on each platform for a consistent scripting environment, but
     // then strip out redundant accessibles in the AccessibleWrap class for each platform.
     nsIContent *parent = aContent->GetParent();
@@ -1546,61 +1496,28 @@ nsAccessibilityService::CreateAccessible
     if (aContent->IsElement() &&
         aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
                                            nsGkAtoms::noautofocus,
                                            nsGkAtoms::_true, eCaseMatters))
       accessible = new XULAlertAccessible(aContent, aDoc);
     else
       accessible = new EnumRoleAccessible<roles::PANE>(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:progressmeter")) {
-    accessible = new XULProgressMeterAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:scale")) {
     accessible = new XULSliderAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:radiobutton")) {
-    accessible = new XULRadioButtonAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:radiogroup")) {
-    accessible = new XULRadioGroupAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:tab")) {
-    accessible = new XULTabAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:tabs")) {
-    accessible = new XULTabsAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:text")) {
     accessible = new XULLabelAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:textbox")) {
     accessible = new EnumRoleAccessible<roles::SECTION>(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:thumb")) {
     accessible = new XULThumbAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:tree")) {
-    accessible = CreateAccessibleForXULTree(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:treecolumns")) {
-    accessible = new XULTreeColumAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:treecolumnitem")) {
-    accessible = new XULColumnItemAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:toolbar")) {
-    accessible = new XULToolbarAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:toolbarseparator")) {
-    accessible = new XULToolbarSeparatorAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:tooltip")) {
-    accessible = new XULTooltipAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:toolbarbutton")) {
     accessible = new XULToolbarButtonAccessible(aContent, aDoc);
 
   }
 #endif // MOZ_XUL
 
   return accessible.forget();
 }
@@ -1826,48 +1743,16 @@ nsAccessibilityService::HasAccessible(ns
     return false;
 
   return document->HasAccessible(node);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService private (DON'T put methods here)
 
-#ifdef MOZ_XUL
-already_AddRefed<Accessible>
-nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
-                                                   DocAccessible* aDoc)
-{
-  nsIContent* child = nsTreeUtils::GetDescendantChild(aContent,
-                                                      nsGkAtoms::treechildren);
-  if (!child)
-    return nullptr;
-
-  nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
-  if (!treeFrame)
-    return nullptr;
-
-  RefPtr<nsTreeColumns> treeCols = treeFrame->Columns();
-  int32_t count = 0;
-  treeCols->GetCount(&count);
-
-  // Outline of list accessible.
-  if (count == 1) {
-    RefPtr<Accessible> accessible =
-      new XULTreeAccessible(aContent, aDoc, treeFrame);
-    return accessible.forget();
-  }
-
-  // Table or tree table accessible.
-  RefPtr<Accessible> accessible =
-    new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame);
-  return accessible.forget();
-}
-#endif
-
 void
 nsAccessibilityService::SetConsumers(uint32_t aConsumers) {
   if (gConsumers & aConsumers) {
     return;
   }
 
   gConsumers |= aConsumers;
   NotifyOfConsumersChange();
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -319,24 +319,16 @@ private:
    */
   void SetConsumers(uint32_t aConsumers);
 
   /**
    * Unset accessibility service consumers.
    */
   void UnsetConsumers(uint32_t aConsumers);
 
-#ifdef MOZ_XUL
-  /**
-   * Create accessible for XUL tree element.
-   */
-  already_AddRefed<Accessible>
-    CreateAccessibleForXULTree(nsIContent* aContent, DocAccessible* aDoc);
-#endif
-
   /**
    * Reference for accessibility service instance.
    */
   static nsAccessibilityService* gAccessibilityService;
 
   /**
    * Reference for application accessible instance.
    */
--- a/browser/base/content/safeMode.js
+++ b/browser/base/content/safeMode.js
@@ -1,14 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* import-globals-from ../../../toolkit/content/resetProfile.js */
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const appStartup = Services.startup;
 
 ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");
 
 var defaultToReset = false;
 
 function restartApp() {
--- a/browser/base/content/safeMode.xul
+++ b/browser/base/content/safeMode.xul
@@ -23,17 +23,16 @@
             buttonlabelaccept="&startSafeMode.label;"
             buttonlabelextra1="&refreshProfile.label;"
             maxwidth="&window.maxWidth;"
             ondialogaccept="return onDefaultButton()"
             ondialogcancel="onCancel();"
             ondialogextra1="return onExtra1()"
             onload="onLoad()">
 
-  <script type="application/javascript" src="chrome://global/content/resetProfile.js"/>
   <script type="application/javascript" src="chrome://browser/content/safeMode.js"/>
 
   <vbox id="autoSafeMode" hidden="true">
     <description>&autoSafeModeDescription3.label;</description>
   </vbox>
 
   <vbox id="safeMode">
     <label>&safeModeDescription3.label;</label>
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -53,17 +53,16 @@ const startupPhases = {
     ])
   }},
 
   // We reach this phase right after showing the first browser window.
   // This means that anything already loaded at this point has been loaded
   // before first paint and delayed it.
   "before first paint": {blacklist: {
     components: new Set([
-      "UnifiedComplete.js",
       "nsSearchService.js",
     ]),
     modules: new Set([
       "chrome://webcompat-reporter/content/WebCompatReporter.jsm",
       "chrome://webcompat/content/data/ua_overrides.jsm",
       "chrome://webcompat/content/lib/ua_overrider.jsm",
       "resource:///modules/AboutNewTab.jsm",
       "resource:///modules/BrowserUITelemetry.jsm",
@@ -108,16 +107,19 @@ const startupPhases = {
       "@mozilla.org/browser/nav-bookmarks-service;1",
     ])
   }},
 
   // Things that are expected to be completely out of the startup path
   // and loaded lazily when used for the first time by the user should
   // be blacklisted here.
   "before becoming idle": {blacklist: {
+    components: new Set([
+      "UnifiedComplete.js",
+    ]),
     modules: new Set([
       "resource://gre/modules/AsyncPrefs.jsm",
       "resource://gre/modules/LoginManagerContextMenu.jsm",
       "resource://gre/modules/Task.jsm",
     ]),
   }},
 };
 
--- a/browser/components/customizableui/content/toolbar.xml
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -3,17 +3,17 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <bindings id="browserToolbarBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="toolbar" role="xul:toolbar">
+  <binding id="toolbar">
     <resources>
       <stylesheet src="chrome://global/skin/toolbar.css"/>
     </resources>
     <implementation>
       <field name="overflowedDuringConstruction">null</field>
 
       <constructor><![CDATA[
           let scope = {};
--- a/browser/components/translation/translation-infobar.xml
+++ b/browser/components/translation/translation-infobar.xml
@@ -9,17 +9,17 @@
 <!ENTITY % translationDTD SYSTEM "chrome://browser/locale/translation.dtd" >
 %translationDTD;
 ]>
 
 <bindings id="translationBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
-  <binding id="translationbar" extends="chrome://global/content/bindings/notification.xml#notification" role="xul:alert">
+  <binding id="translationbar" extends="chrome://global/content/bindings/notification.xml#notification">
     <resources>
       <stylesheet src="chrome://global/skin/notification.css"/>
     </resources>
     <content>
       <xul:hbox anonid="details" align="center" flex="1">
         <xul:image class="translate-infobar-element messageImage"
                    anonid="messageImage"/>
         <xul:panel anonid="welcomePanel" class="translation-welcome-panel"
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -458,16 +458,17 @@ GK_ATOM(face, "face")
 GK_ATOM(fallback, "fallback")
 GK_ATOM(_false, "false")
 GK_ATOM(farthest, "farthest")
 GK_ATOM(field, "field")
 GK_ATOM(fieldset, "fieldset")
 GK_ATOM(file, "file")
 GK_ATOM(figcaption, "figcaption")
 GK_ATOM(figure, "figure")
+GK_ATOM(findbar, "findbar")
 GK_ATOM(fixed, "fixed")
 GK_ATOM(flags, "flags")
 GK_ATOM(flex, "flex")
 GK_ATOM(flexgroup, "flexgroup")
 GK_ATOM(flip, "flip")
 GK_ATOM(floating, "floating")
 GK_ATOM(floor, "floor")
 GK_ATOM(flowlength, "flowlength")
@@ -504,16 +505,17 @@ GK_ATOM(getter, "getter")
 GK_ATOM(glyphchar, "glyphchar")
 GK_ATOM(glyphid, "glyphid")
 GK_ATOM(graphicsDocument, "graphics-document")
 GK_ATOM(graphicsObject, "graphics-object")
 GK_ATOM(graphicsSymbol, "graphics-symbol")
 GK_ATOM(grid, "grid")
 GK_ATOM(grippy, "grippy")
 GK_ATOM(group, "group")
+GK_ATOM(groupbox, "groupbox")
 GK_ATOM(groupingSeparator, "grouping-separator")
 GK_ATOM(groupingSize, "grouping-size")
 GK_ATOM(grow, "grow")
 GK_ATOM(gutter, "gutter")
 GK_ATOM(h1, "h1")
 GK_ATOM(h2, "h2")
 GK_ATOM(h3, "h3")
 GK_ATOM(h4, "h4")
@@ -683,16 +685,17 @@ GK_ATOM(maxwidth, "maxwidth")
 GK_ATOM(mayscript, "mayscript")
 GK_ATOM(media, "media")
 GK_ATOM(mediaType, "media-type")
 GK_ATOM(member, "member")
 GK_ATOM(menu, "menu")
 GK_ATOM(menubar, "menubar")
 GK_ATOM(menubutton, "menubutton")
 GK_ATOM(menuButton, "menu-button")
+GK_ATOM(menucaption, "menucaption")
 GK_ATOM(menugroup, "menugroup")
 GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
 GK_ATOM(menuseparator, "menuseparator")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
 GK_ATOM(referrer, "referrer")
@@ -760,16 +763,17 @@ GK_ATOM(noisolation, "noisolation")
 GK_ATOM(nomodule, "nomodule")
 GK_ATOM(nonce, "nonce")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
 GK_ATOM(normal, "normal")
 GK_ATOM(normalizeSpace, "normalize-space")
 GK_ATOM(noscript, "noscript")
 GK_ATOM(noshade, "noshade")
+GK_ATOM(notification, "notification")
 GK_ATOM(novalidate, "novalidate")
 GK_ATOM(_not, "not")
 GK_ATOM(nowrap, "nowrap")
 GK_ATOM(number, "number")
 GK_ATOM(null, "null")
 GK_ATOM(object, "object")
 GK_ATOM(objectType, "object-type")
 GK_ATOM(observer, "observer")
--- a/dom/fetch/FetchStream.cpp
+++ b/dom/fetch/FetchStream.cpp
@@ -3,19 +3,18 @@
 /* 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 "FetchStream.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/dom/WorkerPrivate.h"
-#include "nsITransport.h"
-#include "nsIStreamTransportService.h"
 #include "nsProxyRelease.h"
+#include "nsStreamUtils.h"
 
 #define FETCH_STREAM_FLAG 0
 
 static NS_DEFINE_CID(kStreamTransportServiceCID,
                      NS_STREAMTRANSPORTSERVICE_CID);
 
 namespace mozilla {
 namespace dom {
@@ -199,55 +198,25 @@ FetchStream::RequestDataCallback(JSConte
 
   stream->mState = eReading;
 
   if (!stream->mInputStream) {
     // This is the first use of the stream. Let's convert the
     // mOriginalInputStream into an nsIAsyncInputStream.
     MOZ_ASSERT(stream->mOriginalInputStream);
 
-    bool nonBlocking = false;
-    nsresult rv = stream->mOriginalInputStream->IsNonBlocking(&nonBlocking);
+    nsCOMPtr<nsIAsyncInputStream> asyncStream;
+    nsresult rv =
+      NS_MakeAsyncNonBlockingInputStream(stream->mOriginalInputStream.forget(),
+                                         getter_AddRefs(asyncStream));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       stream->ErrorPropagation(aCx, aStream, rv);
       return;
     }
 
-    nsCOMPtr<nsIAsyncInputStream> asyncStream =
-      do_QueryInterface(stream->mOriginalInputStream);
-    if (!nonBlocking || !asyncStream) {
-      nsCOMPtr<nsIStreamTransportService> sts =
-        do_GetService(kStreamTransportServiceCID, &rv);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        stream->ErrorPropagation(aCx, aStream, rv);
-        return;
-      }
-
-      nsCOMPtr<nsITransport> transport;
-      rv = sts->CreateInputTransport(stream->mOriginalInputStream,
-                                     /* aCloseWhenDone */ true,
-                                     getter_AddRefs(transport));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        stream->ErrorPropagation(aCx, aStream, rv);
-        return;
-      }
-
-      nsCOMPtr<nsIInputStream> wrapper;
-      rv = transport->OpenInputStream(/* aFlags */ 0,
-                                       /* aSegmentSize */ 0,
-                                       /* aSegmentCount */ 0,
-                                       getter_AddRefs(wrapper));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        stream->ErrorPropagation(aCx, aStream, rv);
-        return;
-      }
-
-      asyncStream = do_QueryInterface(wrapper);
-    }
-
     stream->mInputStream = asyncStream;
     stream->mOriginalInputStream = nullptr;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
   MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream);
 
   nsresult rv =
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -4,50 +4,45 @@
  * 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 "FileReader.h"
 
 #include "nsIEventTarget.h"
 #include "nsIGlobalObject.h"
 #include "nsITimer.h"
-#include "nsITransport.h"
-#include "nsIStreamTransportService.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileReaderBinding.h"
 #include "mozilla/dom/ProgressEvent.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/Encoding.h"
 #include "nsAlgorithm.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMJSUtils.h"
 #include "nsError.h"
-#include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 #define ABORT_STR "abort"
 #define LOAD_STR "load"
 #define LOADSTART_STR "loadstart"
 #define LOADEND_STR "loadend"
 #define ERROR_STR "error"
 #define PROGRESS_STR "progress"
 
 const uint64_t kUnknownSize = uint64_t(-1);
 
-static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(FileReader)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileReader,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -405,58 +400,28 @@ FileReader::ReadFileContent(Blob& aBlob,
   mTotal = 0;
   mReadyState = EMPTY;
   FreeFileData();
 
   mBlob = &aBlob;
   mDataFormat = aDataFormat;
   CopyUTF16toUTF8(aCharset, mCharset);
 
-  nsCOMPtr<nsIInputStream> stream;
-  mBlob->CreateInputStream(getter_AddRefs(stream), aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-
-  bool nonBlocking = false;
-  aRv = stream->IsNonBlocking(&nonBlocking);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-
-  mAsyncStream = do_QueryInterface(stream);
-
-  // We want to have a non-blocking nsIAsyncInputStream.
-  if (!mAsyncStream || !nonBlocking) {
-    nsresult rv;
-    nsCOMPtr<nsIStreamTransportService> sts =
-      do_GetService(kStreamTransportServiceCID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRv.Throw(rv);
-      return;
-    }
-
-    nsCOMPtr<nsITransport> transport;
-    aRv = sts->CreateInputTransport(stream,
-                                    /* aCloseWhenDone */ true,
-                                    getter_AddRefs(transport));
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    mBlob->CreateInputStream(getter_AddRefs(stream), aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
-    nsCOMPtr<nsIInputStream> wrapper;
-    aRv = transport->OpenInputStream(/* aFlags */ 0,
-                                     /* aSegmentSize */ 0,
-                                     /* aSegmentCount */ 0,
-                                     getter_AddRefs(wrapper));
+    aRv = NS_MakeAsyncNonBlockingInputStream(stream.forget(),
+                                             getter_AddRefs(mAsyncStream));
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
-
-    mAsyncStream = do_QueryInterface(wrapper);
   }
 
   MOZ_ASSERT(mAsyncStream);
 
   mTotal = mBlob->GetSize(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IPCBlobInputStream.h"
 #include "IPCBlobInputStreamChild.h"
 #include "IPCBlobInputStreamStorage.h"
 #include "mozilla/ipc/InputStreamParams.h"
 #include "mozilla/SlicedInputStream.h"
+#include "mozilla/NonBlockingAsyncInputStream.h"
 #include "IPCBlobInputStreamThread.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIPipe.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 
 namespace mozilla {
@@ -640,17 +641,29 @@ IPCBlobInputStream::EnsureAsyncRemoteStr
   // If the stream is blocking, we want to make it unblocking using a pipe.
   bool nonBlocking = false;
   nsresult rv = mRemoteStream->IsNonBlocking(&nonBlocking);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
-  if (!asyncStream || !nonBlocking) {
+
+  // If non-blocking and non-async, let's use NonBlockingAsyncInputStream.
+  if (nonBlocking && !asyncStream) {
+    rv = NonBlockingAsyncInputStream::Create(mRemoteStream.forget(),
+                                             getter_AddRefs(asyncStream));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(asyncStream);
+  }
+
+  if (!asyncStream) {
     // Let's make the stream async using the DOMFile thread.
     nsCOMPtr<nsIAsyncInputStream> pipeIn;
     nsCOMPtr<nsIAsyncOutputStream> pipeOut;
     rv = NS_NewPipe2(getter_AddRefs(pipeIn),
                      getter_AddRefs(pipeOut),
                      true, true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
--- a/dom/file/ipc/moz.build
+++ b/dom/file/ipc/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "DOM: File")
+
 EXPORTS.mozilla.dom.ipc += [
     'IPCBlobInputStream.h',
     'IPCBlobInputStreamChild.h',
     'IPCBlobInputStreamParent.h',
     'IPCBlobInputStreamStorage.h',
     'PendingIPCBlobChild.h',
     'PendingIPCBlobParent.h',
     'TemporaryIPCBlobChild.h',
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -1,16 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
-    BUG_COMPONENT = ("Core", "DOM")
+    BUG_COMPONENT = ("Core", "DOM: File")
 
 DIRS += ['ipc']
 
 XPIDL_SOURCES += [
     'nsIDOMBlob.idl',
     'nsIDOMFileList.idl',
 ]
 
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -1,16 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
-    BUG_COMPONENT = ("Core", "DOM")
+    BUG_COMPONENT = ("Core", "DOM: File")
 
 DIRS += ['compat']
 
 TEST_DIRS += ['tests']
 
 EXPORTS.mozilla.dom += [
     'Directory.h',
     'FileSystemBase.h',
--- a/dom/media/webvtt/moz.build
+++ b/dom/media/webvtt/moz.build
@@ -14,8 +14,10 @@ XPIDL_MODULE = 'webvtt'
 EXTRA_COMPONENTS += [
   'WebVTT.manifest',
   'WebVTTParserWrapper.js',
 ]
 
 EXTRA_JS_MODULES += [
   'vtt.jsm',
 ]
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
new file mode 100644
--- /dev/null
+++ b/dom/media/webvtt/tests/test_parser.js
@@ -0,0 +1,77 @@
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/vtt.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+let fakeWindow = {
+  VTTCue: function() {},
+  VTTRegion: function() {},
+};
+
+// We have a better parser check in WPT. Here I want to check that incomplete
+// lines are correctly parsable.
+let tests = [
+  // Signature
+  { input: [ "WEBVTT" ], cue: 0, region: 0 },
+  { input: [ "", "WE", "BVT", "T" ], cue: 0, region: 0 },
+  { input: [ "WEBVTT - This file has no cues." ], cue: 0, region: 0 },
+  { input: [ "WEBVTT", " - ", "This file has no cues." ], cue: 0, region: 0 },
+
+  // Body with IDs
+  { input: [ "WEB", "VTT - This file has cues.\n", "\n", "14\n",
+             "00:01:14", ".815 --> 00:0", "1:18.114\n", "- What?\n", "- Where are we now?\n", "\n",
+             "15\n", "00:01:18.171 --> 00:01:20.991\n", "- T", "his is big bat country.\n", "\n",
+             "16\n", "00:01:21.058 --> 00:01:23.868\n", "- [ Bat", "s Screeching ]\n",
+             "- They won't get in your hair. They're after the bug", "s.\n", ],
+    cue: 3, region: 0 },
+
+  // Body without IDs
+  { input: [ "WEBVTT - This file has c", "ues.\n", "\n",
+             "00:01:14.815 --> 00:01:18.114\n", "- What?\n", "- Where are we now?\n", "\n",
+             "00:01:18.171 --> 00:01:2", "0.991\n", "- ", "This is big bat country.\n", "\n",
+             "00:01:21.058 --> 00:01:23.868\n", "- [ Bats S", "creeching ]\n",
+             "- They won't get in your hair. They're after the bugs.\n", ],
+    cue: 3, region: 0 },
+
+  // Note
+  { input: [ "WEBVTT - This file has no cues.\n", "\n", "NOTE what" ],
+    cue: 0, region: 0 },
+
+  // Regions - This vtt is taken from a WPT
+  { input: [ "WE", "BVTT\n", "\n", "REGION\n", "id:0\n", "\n", "REGION\n", "id:1\n",
+             "region", "an", "chor:0%,0%\n", "\n", "R", "EGION\n", "id:2\n",
+             "regionanchor:18446744073709552000%,18446744", "073709552000%\n", "\n",
+             "REGION\n", "id:3\n", "regionanchor: 100%,100%\n", "regio", "nanchor :100%,100%\n",
+             "regionanchor:100% ,100%\n", "regionanchor:100%, 100%\n",
+             "regionanchor:100 %,100%\n", "regionanchor:10", "0%,100 %\n", "\n",
+             "00:00:00.000 --> 00:00:01.000", " region:0\n", "text\n", "\n",
+             "00:00:00.000 --> 00:00:01.000 region:1\n", "text\n", "\n",
+             "00:00:00.000 --> 00:00:01.000 region:3\n", "text\n" ], cue: 3, region: 4 },
+];
+
+function run_test() {
+
+  tests.forEach(test => {
+    let parser = new WebVTT.Parser(fakeWindow, null);
+    ok(!!parser, "Ok... this is a good starting point");
+
+    let cue = 0;
+    parser.oncue = () => { ++cue; };
+
+    let region = 0;
+    parser.onregion = () => { ++region; };
+
+    parser.onparsingerror = () => {
+      ok(false, "No error accepted");
+    }
+
+    test.input.forEach(input => {
+      parser.parse(new TextEncoder().encode(input));
+    });
+
+    parser.flush();
+
+    equal(cue, test.cue, "Cue value matches");
+    equal(region, test.region, "Region value matches");
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/webvtt/tests/xpcshell.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_parser.js]
--- a/dom/media/webvtt/vtt.jsm
+++ b/dom/media/webvtt/vtt.jsm
@@ -23,18 +23,17 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 ChromeUtils.import('resource://gre/modules/Services.jsm');
-const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 (function(global) {
 
   var _objCreate = Object.create || (function() {
     function F() {}
     return function(o) {
       if (arguments.length !== 1) {
         throw new Error('Object.create shim only accepts one parameter.');
@@ -270,18 +269,18 @@ const { XPCOMUtils } = require("resource
     skipWhitespace();
     cue.endTime = consumeTimeStamp();     // (5) collect cue end time
 
     // 4.1 WebVTT cue settings list.
     skipWhitespace();
     consumeCueSettings(input, cue);
   }
 
-  function onlyContainsWhiteSpaces(input) {
-    return /^[ \f\n\r\t]+$/.test(input);
+  function emptyOrOnlyContainsWhiteSpaces(input) {
+    return input == "" || /^[ \f\n\r\t]+$/.test(input);
   }
 
   function containsTimeDirectionSymbol(input) {
     return input.includes("-->");
   }
 
   function maybeIsTimeStampFormat(input) {
     return /^\s*(\d+:)?(\d{2}):(\d{2})\.(\d+)\s*-->\s*(\d+:)?(\d{2}):(\d{2})\.(\d+)\s*/.test(input);
@@ -1122,66 +1121,72 @@ const { XPCOMUtils } = require("resource
         }
       }
     })();
   };
 
   WebVTT.Parser = function(window, decoder) {
     this.window = window;
     this.state = "INITIAL";
+    this.substate = "";
+    this.substatebuffer = "";
     this.buffer = "";
     this.decoder = decoder || new TextDecoder("utf8");
     this.regionList = [];
   };
 
   WebVTT.Parser.prototype = {
     // If the error is a ParsingError then report it to the consumer if
     // possible. If it's not a ParsingError then throw it like normal.
     reportOrThrowError: function(e) {
       if (e instanceof ParsingError) {
         this.onparsingerror && this.onparsingerror(e);
       } else {
         throw e;
       }
     },
     parse: function (data) {
-      var self = this;
-
       // If there is no data then we won't decode it, but will just try to parse
       // whatever is in buffer already. This may occur in circumstances, for
       // example when flush() is called.
       if (data) {
         // Try to decode the data that we received.
-        self.buffer += self.decoder.decode(data, {stream: true});
+        this.buffer += this.decoder.decode(data, {stream: true});
       }
 
-      function collectNextLine() {
-        var buffer = self.buffer;
+      // This parser is line-based. Let's see if we have a line to parse.
+      while (/\r\n|\n|\r/.test(this.buffer)) {
+        var buffer = this.buffer;
         var pos = 0;
-        while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
+        while (buffer[pos] !== '\r' && buffer[pos] !== '\n') {
           ++pos;
         }
         var line = buffer.substr(0, pos);
         // Advance the buffer early in case we fail below.
         if (buffer[pos] === '\r') {
           ++pos;
         }
         if (buffer[pos] === '\n') {
           ++pos;
         }
-        self.buffer = buffer.substr(pos);
+        this.buffer = buffer.substr(pos);
+
         // Spec defined replacement.
         line = line.replace(/[\u0000]/g, "\uFFFD");
 
-        if (/^NOTE($|[ \t])/.test(line)) {
-          line = null;
+        if (!/^NOTE($|[ \t])/.test(line)) {
+          this.parseLine(line);
         }
-        return line;
       }
 
+      return this;
+    },
+    parseLine: function(line) {
+      var self = this;
+
       function createCueIfNeeded() {
         if (!self.cue) {
           self.cue = new self.window.VTTCue(0, 0, "");
         }
       }
 
       // Parsing cue identifier and the identifier should be unique.
       // Return true if the input is a cue identifier.
@@ -1277,18 +1282,17 @@ const { XPCOMUtils } = require("resource
             var regionPref = Services.prefs.getBoolPref("media.webvtt.regions.enabled");
             dump("regionPref " + regionPref + "\n");
           }
         }
       }
 
       // Parsing the WebVTT signature, it contains parsing algo step1 to step9.
       // See spec, https://w3c.github.io/webvtt/#file-parsing
-      function parseSignatureMayThrow(input) {
-        let signature = collectNextLine();
+      function parseSignatureMayThrow(signature) {
         if (!/^WEBVTT([ \t].*)?$/.test(signature)) {
           throw new ParsingError(ParsingError.Errors.BadSignature);
         } else {
           self.state = "HEADER";
         }
       }
 
       function parseRegionOrStyle(input) {
@@ -1305,128 +1309,127 @@ const { XPCOMUtils } = require("resource
       // See spec, https://w3c.github.io/webvtt/#collect-a-webvtt-block
       //
       // There are sereval things would appear in header,
       //   1. Region or Style setting
       //   2. Garbage (meaningless string)
       //   3. Empty line
       //   4. Cue's timestamp
       // The case 4 happens when there is no line interval between the header
-      // and the cue blocks. In this case, we should preserve the line and
-      // return it for the next phase parsing.
-      function parseHeader() {
-        let line = null;
-        while (self.buffer && self.state === "HEADER") {
-          line = collectNextLine();
-          var tempStr = "";
-          if (/^REGION|^STYLE/.test(line)) {
-            self.substate = /^REGION/.test(line) ? "REGION" : "STYLE";
+      // and the cue blocks. In this case, we should preserve the line for the
+      // next phase parsing, returning "true".
+      function parseHeader(line) {
+        if (!self.substate && /^REGION|^STYLE/.test(line)) {
+          self.substate = /^REGION/.test(line) ? "REGION" : "STYLE";
+          return false;
+        }
 
-            while (true) {
-              line = collectNextLine();
-              if (!line || maybeIsTimeStampFormat(line) || onlyContainsWhiteSpaces(line) || containsTimeDirectionSymbol(line)) {
-                // parse the tempStr and break the while loop.
-                parseRegionOrStyle(tempStr);
-                break;
-              } else if (/^REGION|^STYLE/.test(line)) {
-                // The line is another REGION/STYLE, parse tempStr then reset tempStr.
-                // Don't break the while loop to parse the next REGION/STYLE.
-                parseRegionOrStyle(tempStr);
-                self.substate = /^REGION/.test(line) ? "REGION" : "STYLE";
-                tempStr = "";
-              } else {
-                tempStr = tempStr + " " + line;
-              }
-            }
+        if (self.substate === "REGION" || self.substate === "STYLE") {
+          if (maybeIsTimeStampFormat(line) ||
+              emptyOrOnlyContainsWhiteSpaces(line) ||
+              containsTimeDirectionSymbol(line)) {
+            parseRegionOrStyle(self.substatebuffer);
+            self.substatebuffer = "";
+            self.substate = null;
+
+            // This is the end of the region or style state.
+            return parseHeader(line);
           }
 
-          if (!line || onlyContainsWhiteSpaces(line)) {
-            // empty line, whitespaces
-            continue;
-          } else if (maybeIsTimeStampFormat(line)) {
-            self.state = "CUE";
-            break;
-          } else if (containsTimeDirectionSymbol(line)) {
-            // string contains "-->"
-            break;
-          } else {
-            //It is an ID.
-            break;
+          if (/^REGION|^STYLE/.test(line)) {
+            // The line is another REGION/STYLE, parse and reset substatebuffer.
+            // Don't break the while loop to parse the next REGION/STYLE.
+            parseRegionOrStyle(self.substatebuffer);
+            self.substatebuffer = "";
+            self.substate = /^REGION/.test(line) ? "REGION" : "STYLE";
+            return false;
           }
-        } // self.state === "HEADER"
 
-        // End parsing header part and doesn't see the timestamp.
-        if (self.state === "HEADER") {
-          self.state = "ID";
+          // We weren't able to parse the line as a header. Accumulate and
+          // return.
+          self.substatebuffer += " " + line;
+          return false;
         }
-        return line;
-      }
 
-      // 5.1 WebVTT file parsing.
-      try {
-        if (self.state === "INITIAL") {
-          parseSignatureMayThrow();
+        if (emptyOrOnlyContainsWhiteSpaces(line)) {
+          // empty line, whitespaces, nothing to do.
+          return false;
+        }
+
+        if (maybeIsTimeStampFormat(line)) {
+          self.state = "CUE";
+          // We want to process the same line again.
+          return true;
         }
 
-        var line;
-        if (self.state === "HEADER") {
-          line = parseHeader();
+        // string contains "-->" or an ID
+        self.state = "ID";
+        return true;
+      }
+
+      try {
+        // 5.1 WebVTT file parsing.
+        if (self.state === "INITIAL") {
+          parseSignatureMayThrow(line);
+          return;
         }
 
-        var nextIteration = false;
-        while (nextIteration || self.buffer) {
-          nextIteration = false;
-          if (!line) {
-            // Since the data receiving is async, we need to wait until the
-            // buffer gets the full line.
-            if (!/\r\n|\n|\r/.test(self.buffer)) {
-              return this;
-            }
-            line = collectNextLine();
+        if (self.state === "HEADER") {
+          // parseHeader returns false if the same line doesn't need to be
+          // parsed again.
+          if (!parseHeader(line)) {
+            return;
+          }
+        }
+
+        if (self.state === "ID") {
+          // If there is no cue identifier, read the next line. 
+          if (line == "") {
+            return;
           }
 
-          switch (self.state) {
-          case "ID":
-            // If there is no cue identifier, keep the line and reuse this line
-            // in next iteration.
-            if (!line || !parseCueIdentifier(line)) {
-              nextIteration = true;
-              continue;
+          // If there is no cue identifier, parse the line again.
+          if (!parseCueIdentifier(line)) {
+            return self.parseLine(line);
+          }
+          return;
+        }
+
+        if (self.state === "CUE") {
+          parseCueMayThrow(line);
+          return;
+        }
+
+        if (self.state === "CUETEXT") {
+          // Report the cue when (1) get an empty line (2) get the "-->""
+          if (emptyOrOnlyContainsWhiteSpaces(line) ||
+              containsTimeDirectionSymbol(line)) {
+            // We are done parsing self cue.
+            self.oncue && self.oncue(self.cue);
+            self.cue = null;
+            self.state = "ID";
+
+            if (emptyOrOnlyContainsWhiteSpaces(line)) {
+              return;
             }
-            break;
-          case "CUE":
-            parseCueMayThrow(line);
-            break;
-          case "CUETEXT":
-            // Report the cue when (1) get an empty line (2) get the "-->""
-            if (!line || containsTimeDirectionSymbol(line)) {
-              // We are done parsing self cue.
-              self.oncue && self.oncue(self.cue);
-              self.cue = null;
-              self.state = "ID";
-              // Keep the line and reuse this line in next iteration.
-              nextIteration = true;
-              continue;
-            }
-            if (self.cue.text) {
-              self.cue.text += "\n";
-            }
-            self.cue.text += line;
-            break;
-          case "BADCUE": // BADCUE
-            // 54-62 - Collect and discard the remaining cue.
-            self.state = "ID";
-            if (line) { // keep this line to ID state.
-              continue;
-            }
-            break;
+
+            // Reuse the same line.
+            return self.parseLine(line);
+          }
+          if (self.cue.text) {
+            self.cue.text += "\n";
           }
-          // The line was already parsed, empty it to ensure we can get the
-          // new line in next iteration.
-          line = null;
+          self.cue.text += line;
+          return;
+        }
+
+        if (self.state === "BADCUE") {
+          // 54-62 - Collect and discard the remaining cue.
+          self.state = "ID";
+          return self.parseLine(line);
         }
       } catch (e) {
         self.reportOrThrowError(e);
 
         // If we are currently parsing a cue, report what we have.
         if (self.state === "CUETEXT" && self.cue && self.oncue) {
           self.oncue(self.cue);
         }
@@ -1437,27 +1440,18 @@ const { XPCOMUtils } = require("resource
       }
       return this;
     },
     flush: function () {
       var self = this;
       try {
         // Finish decoding the stream.
         self.buffer += self.decoder.decode();
-        // Synthesize the end of the current cue or region.
-        if (self.cue || self.state === "HEADER") {
-          self.buffer += "\n\n";
-          self.parse();
-        }
-        // If we've flushed, parsed, and we're still on the INITIAL state then
-        // that means we don't have enough of the stream to parse the first
-        // line.
-        if (self.state === "INITIAL") {
-          throw new ParsingError(ParsingError.Errors.BadSignature);
-        }
+        self.buffer += "\n\n";
+        self.parse();
       } catch(e) {
         self.reportOrThrowError(e);
       }
       self.onflush && self.onflush();
       return this;
     }
   };
 
--- a/dom/webauthn/U2FSoftTokenManager.cpp
+++ b/dom/webauthn/U2FSoftTokenManager.cpp
@@ -711,16 +711,23 @@ U2FSoftTokenManager::Register(const nsTA
 //
 RefPtr<U2FSignPromise>
 U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredential>& aCredentials,
                           const nsTArray<uint8_t>& aApplication,
                           const nsTArray<uint8_t>& aChallenge,
                           bool aRequireUserVerification,
                           uint32_t aTimeoutMS)
 {
+  if (!mInitialized) {
+    nsresult rv = Init();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return U2FSignPromise::CreateAndReject(rv, __func__);
+    }
+  }
+
   // The U2F softtoken doesn't support user verification.
   if (aRequireUserVerification) {
     return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
   }
 
   nsTArray<uint8_t> keyHandle;
   for (auto cred: aCredentials) {
     bool isRegistered = false;
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -534,23 +534,16 @@ WebAuthnManager::GetAssertion(const Publ
   }
 
   srv = HashCString(hashService, clientDataJSON, clientDataHash);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
-  // Note: we only support U2F-style authentication for now, so we effectively
-  // require an AllowList.
-  if (aOptions.mAllowCredentials.Length() < 1) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
   nsTArray<WebAuthnScopedCredential> allowList;
   for (const auto& s: aOptions.mAllowCredentials) {
     if (s.mType == PublicKeyCredentialType::Public_key) {
       WebAuthnScopedCredential c;
       CryptoBuffer cb;
       cb.Assign(s.mId);
       c.id() = cb;
 
--- a/dom/webauthn/tests/test_webauthn_get_assertion.html
+++ b/dom/webauthn/tests/test_webauthn_get_assertion.html
@@ -1,129 +1,167 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <head>
   <title>Tests for GetAssertion for W3C Web Authentication</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
   <script type="text/javascript" src="u2futil.js"></script>
   <script type="text/javascript" src="pkijs/common.js"></script>
   <script type="text/javascript" src="pkijs/asn1.js"></script>
   <script type="text/javascript" src="pkijs/x509_schema.js"></script>
   <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
   <h1>Tests for GetAssertion for W3C Web Authentication</h1>
   <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
 
   <script class="testbody" type="text/javascript">
     "use strict";
 
-    // Execute the full-scope test
-    SimpleTest.waitForExplicitFinish();
+    is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+    isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+    isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+    let gAssertionChallenge = new Uint8Array(16);
+    window.crypto.getRandomValues(gAssertionChallenge);
+
+    let invalidCred = {type: "Magic", id: base64ToBytes("AAA=")};
+    let unknownCred = {type: "public-key", id: base64ToBytes("AAA=")};
+
+    function requestGetAssertion(params) {
+      return navigator.credentials.get(params);
+    }
 
     function arrivingHereIsBad(aResult) {
       ok(false, "Bad result! Received a: " + aResult);
-      return Promise.resolve();
     }
 
     function expectNotAllowedError(aResult) {
       ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
-      return Promise.resolve();
     }
 
     function expectTypeError(aResult) {
       ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
-      return Promise.resolve();
+    }
+
+    function expectAbortError(aResult) {
+      is(aResult.code, DOMException.ABORT_ERR, "Expecting an AbortError");
     }
 
-    SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
-                                       ["security.webauth.webauthn_enable_softtoken", true],
-                                       ["security.webauth.webauthn_enable_usbtoken", false]]},
-                              runTests);
+    add_task(() => {
+      return SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn", true],
+        ["security.webauth.webauthn_enable_softtoken", true],
+        ["security.webauth.webauthn_enable_usbtoken", false]
+      ]});
+    });
+
+    // Test basic good call, but without giving a credential so expect failures
+    // this is OK by the standard, but not supported by U2F-backed authenticators
+    // like the soft token in use here.
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    });
 
-    function runTests() {
-      is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
-      isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
-      isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
-      isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+    // Test with an unexpected option
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi"
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    });
 
-      let credm = navigator.credentials;
+    // Test with an invalid credential
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: [invalidCred]
+      };
 
-      let gAssertionChallenge = new Uint8Array(16);
-      window.crypto.getRandomValues(gAssertionChallenge);
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectTypeError);
+    });
 
-      let invalidCred = {type: "Magic", id: base64ToBytes("AAA=")};
-      let unknownCred = {type: "public-key", id: base64ToBytes("AAA=")};
+    // Test with an unknown credential
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: [unknownCred]
+      };
 
-      var testFuncs = [
-        function () {
-          // Test basic good call, but without giving a credential so expect failures
-          // this is OK by the standard, but not supported by U2F-backed authenticators
-          // like the soft token in use here.
-          let publicKeyCredentialRequestOptions = {
-            challenge: gAssertionChallenge
-          };
-          return credm.get({publicKey: publicKeyCredentialRequestOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectNotAllowedError);
-        },
-        function () {
-          // Test with an unexpected option
-          let publicKeyCredentialRequestOptions = {
-            challenge: gAssertionChallenge,
-            unknownValue: "hi"
-          };
-          return credm.get({publicKey: publicKeyCredentialRequestOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectNotAllowedError);
-        },
-        function () {
-          // Test with an invalid credential
-          let publicKeyCredentialRequestOptions = {
-            challenge: gAssertionChallenge,
-            allowCredentials: [invalidCred]
-          };
-          return credm.get({publicKey: publicKeyCredentialRequestOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectTypeError);
-        },
-        function () {
-          // Test with an unknown credential
-          let publicKeyCredentialRequestOptions = {
-            challenge: gAssertionChallenge,
-            allowCredentials: [unknownCred]
-          };
-          return credm.get({publicKey: publicKeyCredentialRequestOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectNotAllowedError);
-        },
-        function () {
-          // Test with an unexpected option and an invalid credential
-          let publicKeyCredentialRequestOptions = {
-            challenge: gAssertionChallenge,
-            unknownValue: "hi",
-            allowCredentials: [invalidCred]
-          };
-          return credm.get({publicKey: publicKeyCredentialRequestOptions})
-                      .then(arrivingHereIsBad)
-                      .catch(expectTypeError);
-        }
-      ];
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    });
+
+    // Test with an unexpected option and an invalid credential
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi",
+        allowCredentials: [invalidCred]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectTypeError);
+    });
+
+    // Test with an empty credential list
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: []
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    });
 
-      var i = 0;
-      var runNextTest = () => {
-        if (i == testFuncs.length) {
-          SimpleTest.finish();
-          return;
-        }
-        testFuncs[i]().then(() => { runNextTest(); });
-        i = i + 1;
+    add_task(() => {
+      // Enable USB tokens.
+      return SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn", true],
+        ["security.webauth.webauthn_enable_softtoken", false],
+        ["security.webauth.webauthn_enable_usbtoken", true]
+      ]});
+    });
+
+    // Test with an empty credential list
+    add_task(async () => {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: []
       };
-      runNextTest();
+
+      let ctrl = new AbortController();
+      let request = requestGetAssertion({publicKey, signal: ctrl.signal})
+                         .then(arrivingHereIsBad)
+                         .catch(expectAbortError);
 
-    }
+      // Wait a tick for the statemachine to start.
+      await Promise.resolve();
 
+      // The request should time out. We'll abort it here and will fail or
+      // succeed upon resolution, when the error code is checked.
+      ctrl.abort();
+      await request;
+    });
   </script>
 
 </body>
 </html>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -959,37 +959,35 @@ public:
   virtual bool Notify(WorkerStatus aStatus) override { return true; }
 };
 
 } /* anonymous namespace */
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, Runnable)
 
 
-template <class Derived>
-class WorkerPrivateParent<Derived>::EventTarget final
-  : public nsISerialEventTarget
+class WorkerPrivate::EventTarget final : public nsISerialEventTarget
 {
   // This mutex protects mWorkerPrivate and must be acquired *before* the
   // WorkerPrivate's mutex whenever they must both be held.
   mozilla::Mutex mMutex;
   WorkerPrivate* mWorkerPrivate;
   nsIEventTarget* mWeakNestedEventTarget;
   nsCOMPtr<nsIEventTarget> mNestedEventTarget;
 
 public:
   explicit EventTarget(WorkerPrivate* aWorkerPrivate)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+  : mMutex("WorkerPrivate::EventTarget::mMutex"),
     mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
   {
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+  : mMutex("WorkerPrivate::EventTarget::mMutex"),
     mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
     mNestedEventTarget(aNestedEventTarget)
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aNestedEventTarget);
   }
 
   void
@@ -1435,19 +1433,18 @@ WorkerPrivate::MemoryReporter::FinishCol
 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
 : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
 #ifdef DEBUG
   , mHasRun(false)
 #endif
 {
 }
 
-template <class Derived>
 nsIDocument*
-WorkerPrivateParent<Derived>::GetDocument() const
+WorkerPrivate::GetDocument() const
 {
   AssertIsOnMainThread();
   if (mLoadInfo.mWindow) {
     return mLoadInfo.mWindow->GetExtantDoc();
   }
   // if we don't have a document, we should query the document
   // from the parent in case of a nested worker
   WorkerPrivate* parent = mParent;
@@ -1456,48 +1453,44 @@ WorkerPrivateParent<Derived>::GetDocumen
       return parent->mLoadInfo.mWindow->GetExtantDoc();
     }
     parent = parent->GetParent();
   }
   // couldn't query a document, give up and return nullptr
   return nullptr;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::SetCSP(nsIContentSecurityPolicy* aCSP)
+WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP)
 {
   AssertIsOnMainThread();
   if (!aCSP) {
     return;
   }
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-  aCSP->EnsureEventTarget(self->mMainThreadEventTarget);
+  aCSP->EnsureEventTarget(mMainThreadEventTarget);
   mLoadInfo.mCSP = aCSP;
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
-                                                     const nsACString& aCSPReportOnlyHeaderValue)
+WorkerPrivate::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
+                                      const nsACString& aCSPReportOnlyHeaderValue)
 {
   AssertIsOnMainThread();
   MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
 
   NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
   if (!csp) {
     return NS_OK;
   }
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-  csp->EnsureEventTarget(self->mMainThreadEventTarget);
+  csp->EnsureEventTarget(mMainThreadEventTarget);
 
   // If there's a CSP header, apply it.
   if (!cspHeaderValue.IsEmpty()) {
     rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   // If there's a report-only CSP header, apply it.
   if (!cspROHeaderValue.IsEmpty()) {
@@ -1523,283 +1516,178 @@ WorkerPrivateParent<Derived>::SetCSPFrom
 
   if (hasReferrerPolicy) {
     mLoadInfo.mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
   }
 
   return NS_OK;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(
-                                  const nsACString& aReferrerPolicyHeaderValue)
+WorkerPrivate::SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue)
 {
   NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
 
   if (headerValue.IsEmpty()) {
     return;
   }
 
   net::ReferrerPolicy policy =
     nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
   if (policy == net::RP_Unset) {
     return;
   }
 
   SetReferrerPolicy(policy);
 }
 
-template <class Derived>
-WorkerPrivateParent<Derived>::WorkerPrivateParent(
-                                           WorkerPrivate* aParent,
-                                           const nsAString& aScriptURL,
-                                           bool aIsChromeWorker,
-                                           WorkerType aWorkerType,
-                                           const nsAString& aWorkerName,
-                                           const nsACString& aServiceWorkerScope,
-                                           WorkerLoadInfo& aLoadInfo)
-: mMutex("WorkerPrivateParent Mutex"),
-  mCondVar(mMutex, "WorkerPrivateParent CondVar"),
-  mParent(aParent), mScriptURL(aScriptURL),
-  mWorkerName(aWorkerName),
-  mLoadingWorkerScript(false), mBusyCount(0), mParentWindowPausedDepth(0),
-  mParentStatus(Pending), mParentFrozen(false),
-  mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
-  mIsSecureContext(false), mWorkerType(aWorkerType),
-  mCreationTimeStamp(TimeStamp::Now()),
-  mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
-{
-  MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
-
-  mLoadInfo.StealFrom(aLoadInfo);
-
-  if (aParent) {
-    aParent->AssertIsOnWorkerThread();
-
-    // Note that this copies our parent's secure context state into mJSSettings.
-    aParent->CopyJSSettings(mJSSettings);
-
-    // And manually set our mIsSecureContext, though it's not really relevant to
-    // dedicated workers...
-    mIsSecureContext = aParent->IsSecureContext();
-    MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
-
-    MOZ_ASSERT(IsDedicatedWorker());
-
-    if (aParent->mParentFrozen) {
-      Freeze(nullptr);
-    }
-  }
-  else {
-    AssertIsOnMainThread();
-
-    RuntimeService::GetDefaultJSSettings(mJSSettings);
-
-    // Our secure context state depends on the kind of worker we have.
-    if (UsesSystemPrincipal() || IsServiceWorker()) {
-      mIsSecureContext = true;
-    } else if (mLoadInfo.mWindow) {
-      // Shared and dedicated workers both inherit the loading window's secure
-      // context state.  Shared workers then prevent windows with a different
-      // secure context state from attaching to them.
-      mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
-    } else {
-      MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
-                             "that has no parent and no associated window");
-    }
-
-    if (mIsSecureContext) {
-      mJSSettings.chrome.compartmentOptions
-                 .creationOptions().setSecureContext(true);
-      mJSSettings.content.compartmentOptions
-                 .creationOptions().setSecureContext(true);
-    }
-
-    // Our parent can get suspended after it initiates the async creation
-    // of a new worker thread.  In this case suspend the new worker as well.
-    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
-      ParentWindowPaused();
-    }
-
-    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
-      Freeze(mLoadInfo.mWindow);
-    }
-  }
-}
-
-template <class Derived>
-WorkerPrivateParent<Derived>::~WorkerPrivateParent()
-{
-  DropJSObjects(this);
-}
-
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::Traverse(nsCycleCollectionTraversalCallback& aCb)
+WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb)
 {
   AssertIsOnParentThread();
 
   // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
   // Worker object, which is really held by the worker thread.  We traverse this
   // reference if and only if our busy count is zero and we have not released
   // the main thread reference.  We do not unlink it.  This allows the CC to
   // break cycles involving the Worker and begin shutting it down (which does
   // happen in unlink) but ensures that the WorkerPrivate won't be deleted
   // before we're done shutting down the thread.
   if (!mBusyCount && !mMainThreadObjectsForgotten) {
     nsCycleCollectionTraversalCallback& cb = aCb;
-    WorkerPrivateParent<Derived>* tmp = this;
+    WorkerPrivate* tmp = this;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
   }
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable,
-                                              nsIEventTarget* aSyncLoopTarget)
+WorkerPrivate::DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable,
+                               nsIEventTarget* aSyncLoopTarget)
 {
   // May be called on any thread!
   RefPtr<WorkerRunnable> runnable(aRunnable);
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-
   {
     MutexAutoLock lock(mMutex);
 
-    MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread);
-
-    if (!self->mThread) {
-      if (ParentStatus() == Pending || self->mStatus == Pending) {
+    MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
+
+    if (!mThread) {
+      if (ParentStatus() == Pending || mStatus == Pending) {
         mPreStartRunnables.AppendElement(runnable);
         return NS_OK;
       }
 
       NS_WARNING("Using a worker event target after the thread has already"
                  "been released!");
       return NS_ERROR_UNEXPECTED;
     }
 
-    if (self->mStatus == Dead ||
+    if (mStatus == Dead ||
         (!aSyncLoopTarget && ParentStatus() > Running)) {
       NS_WARNING("A runnable was posted to a worker that is already shutting "
                  "down!");
       return NS_ERROR_UNEXPECTED;
     }
 
     nsresult rv;
     if (aSyncLoopTarget) {
       rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
     } else {
-      rv = self->mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
+      rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
     }
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     mCondVar.Notify();
   }
 
   return NS_OK;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::EnableDebugger()
+WorkerPrivate::EnableDebugger()
 {
   AssertIsOnParentThread();
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-
-  if (NS_FAILED(RegisterWorkerDebugger(self))) {
+  if (NS_FAILED(RegisterWorkerDebugger(this))) {
     NS_WARNING("Failed to register worker debugger!");
     return;
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::DisableDebugger()
+WorkerPrivate::DisableDebugger()
 {
   AssertIsOnParentThread();
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-
-  if (NS_FAILED(UnregisterWorkerDebugger(self))) {
+  if (NS_FAILED(UnregisterWorkerDebugger(this))) {
     NS_WARNING("Failed to unregister worker debugger!");
   }
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::DispatchControlRunnable(
-  already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
+WorkerPrivate::DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
 {
   // May be called on any thread!
   RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
   MOZ_ASSERT(runnable);
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-
   {
     MutexAutoLock lock(mMutex);
 
-    if (self->mStatus == Dead) {
+    if (mStatus == Dead) {
       return NS_ERROR_UNEXPECTED;
     }
 
     // Transfer ownership to the control queue.
-    self->mControlQueue.Push(runnable.forget().take());
-
-    if (JSContext* cx = self->mJSContext) {
-      MOZ_ASSERT(self->mThread);
+    mControlQueue.Push(runnable.forget().take());
+
+    if (JSContext* cx = mJSContext) {
+      MOZ_ASSERT(mThread);
       JS_RequestInterruptCallback(cx);
     }
 
     mCondVar.Notify();
   }
 
   return NS_OK;
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::DispatchDebuggerRunnable(
-  already_AddRefed<WorkerRunnable> aDebuggerRunnable)
+WorkerPrivate::DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable)
 {
   // May be called on any thread!
 
   RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
 
   MOZ_ASSERT(runnable);
 
-  WorkerPrivate* self = ParentAsWorkerPrivate();
-
   {
     MutexAutoLock lock(mMutex);
 
-    if (self->mStatus == Dead) {
+    if (mStatus == Dead) {
       NS_WARNING("A debugger runnable was posted to a worker that is already "
                  "shutting down!");
       return NS_ERROR_UNEXPECTED;
     }
 
     // Transfer ownership to the debugger queue.
-    self->mDebuggerQueue.Push(runnable.forget().take());
+    mDebuggerQueue.Push(runnable.forget().take());
 
     mCondVar.Notify();
   }
 
   return NS_OK;
 }
 
-template <class Derived>
 already_AddRefed<WorkerRunnable>
-WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
+WorkerPrivate::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
 {
   // May be called on any thread!
 
   nsCOMPtr<nsIRunnable> runnable(aRunnable);
   MOZ_ASSERT(runnable);
 
   RefPtr<WorkerRunnable> workerRunnable =
     WorkerRunnable::FromRunnable(runnable);
@@ -1807,44 +1695,40 @@ WorkerPrivateParent<Derived>::MaybeWrapA
     return workerRunnable.forget();
   }
 
   nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
   if (!cancelable) {
     MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
   }
 
-  workerRunnable =
-    new ExternalRunnableWrapper(ParentAsWorkerPrivate(), runnable);
+  workerRunnable = new ExternalRunnableWrapper(this, runnable);
   return workerRunnable.forget();
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::Start()
+WorkerPrivate::Start()
 {
   // May be called on any thread!
   {
     MutexAutoLock lock(mMutex);
-
     NS_ASSERTION(mParentStatus != Running, "How can this be?!");
 
     if (mParentStatus == Pending) {
       mParentStatus = Running;
       return true;
     }
   }
 
   return false;
 }
 
 // aCx is null when called from the finalizer
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::NotifyPrivate(WorkerStatus aStatus)
+WorkerPrivate::NotifyPrivate(WorkerStatus aStatus)
 {
   AssertIsOnParentThread();
 
   bool pending;
   {
     MutexAutoLock lock(mMutex);
 
     if (mParentStatus >= aStatus) {
@@ -1854,54 +1738,50 @@ WorkerPrivateParent<Derived>::NotifyPriv
     pending = mParentStatus == Pending;
     mParentStatus = aStatus;
   }
 
   if (IsSharedWorker()) {
     RuntimeService* runtime = RuntimeService::GetService();
     MOZ_ASSERT(runtime);
 
-    runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
+    runtime->ForgetSharedWorker(this);
   }
 
   if (pending) {
-    WorkerPrivate* self = ParentAsWorkerPrivate();
-
 #ifdef DEBUG
     {
       // Fake a thread here just so that our assertions don't go off for no
       // reason.
       nsIThread* currentThread = NS_GetCurrentThread();
       MOZ_ASSERT(currentThread);
 
-      MOZ_ASSERT(!self->mPRThread);
-      self->mPRThread = PRThreadFromThread(currentThread);
-      MOZ_ASSERT(self->mPRThread);
+      MOZ_ASSERT(!mPRThread);
+      mPRThread = PRThreadFromThread(currentThread);
+      MOZ_ASSERT(mPRThread);
     }
 #endif
 
     // Worker never got a chance to run, go ahead and delete it.
-    self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
+    ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
     return true;
   }
 
   NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
                "Shouldn't have anything queued!");
 
   // Anything queued will be discarded.
   mQueuedRunnables.Clear();
 
-  RefPtr<NotifyRunnable> runnable =
-    new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
+  RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
   return runnable->Dispatch();
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::Freeze(nsPIDOMWindowInner* aWindow)
+WorkerPrivate::Freeze(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnParentThread();
 
   // Shared workers are only frozen if all of their owning documents are
   // frozen. It can happen that mSharedWorkers is empty but this thread has
   // not been unregistered yet.
   if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
     AssertIsOnMainThread();
@@ -1912,18 +1792,17 @@ WorkerPrivateParent<Derived>::Freeze(nsP
       if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
         // Calling Freeze() may change the refcount, ensure that the worker
         // outlives this call.
         RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
 
         kungFuDeathGrip->Freeze();
       } else {
         MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
-                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
-                                       aWindow));
+                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
         if (!mSharedWorkers[i]->IsFrozen()) {
           allFrozen = false;
         }
       }
     }
 
     if (!allFrozen || mParentFrozen) {
       return true;
@@ -1937,28 +1816,26 @@ WorkerPrivateParent<Derived>::Freeze(nsP
 
     if (mParentStatus >= Terminating) {
       return true;
     }
   }
 
   DisableDebugger();
 
-  RefPtr<FreezeRunnable> runnable =
-    new FreezeRunnable(ParentAsWorkerPrivate());
+  RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
   if (!runnable->Dispatch()) {
     return false;
   }
 
   return true;
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::Thaw(nsPIDOMWindowInner* aWindow)
+WorkerPrivate::Thaw(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnParentThread();
 
   MOZ_ASSERT(mParentFrozen);
 
   // Shared workers are resumed if any of their owning documents are thawed.
   // It can happen that mSharedWorkers is empty but this thread has not been
   // unregistered yet.
@@ -1972,18 +1849,17 @@ WorkerPrivateParent<Derived>::Thaw(nsPID
         // Calling Thaw() may change the refcount, ensure that the worker
         // outlives this call.
         RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
 
         kungFuDeathGrip->Thaw();
         anyRunning = true;
       } else {
         MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
-                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
-                                       aWindow));
+                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
         if (!mSharedWorkers[i]->IsFrozen()) {
           anyRunning = true;
         }
       }
     }
 
     if (!anyRunning || !mParentFrozen) {
       return true;
@@ -2012,37 +1888,34 @@ WorkerPrivateParent<Derived>::Thaw(nsPID
     nsTArray<nsCOMPtr<nsIRunnable>> runnables;
     mQueuedRunnables.SwapElements(runnables);
 
     for (uint32_t index = 0; index < runnables.Length(); index++) {
       runnables[index]->Run();
     }
   }
 
-  RefPtr<ThawRunnable> runnable =
-    new ThawRunnable(ParentAsWorkerPrivate());
+  RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
   if (!runnable->Dispatch()) {
     return false;
   }
 
   return true;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::ParentWindowPaused()
+WorkerPrivate::ParentWindowPaused()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 0);
   mParentWindowPausedDepth += 1;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::ParentWindowResumed()
+WorkerPrivate::ParentWindowResumed()
 {
   AssertIsOnMainThread();
 
   MOZ_ASSERT(mParentWindowPausedDepth > 0);
   MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 1);
   mParentWindowPausedDepth -= 1;
   if (mParentWindowPausedDepth > 0) {
     return;
@@ -2065,32 +1938,29 @@ WorkerPrivateParent<Derived>::ParentWind
     mQueuedRunnables.SwapElements(runnables);
 
     for (uint32_t index = 0; index < runnables.Length(); index++) {
       runnables[index]->Run();
     }
   }
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::Close()
+WorkerPrivate::Close()
 {
   mMutex.AssertCurrentThreadOwns();
-
   if (mParentStatus < Closing) {
     mParentStatus = Closing;
   }
 
   return true;
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::ModifyBusyCount(bool aIncrease)
+WorkerPrivate::ModifyBusyCount(bool aIncrease)
 {
   AssertIsOnParentThread();
 
   NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
 
   if (aIncrease) {
     mBusyCount++;
     return true;
@@ -2107,151 +1977,137 @@ WorkerPrivateParent<Derived>::ModifyBusy
     if (shouldCancel && !Cancel()) {
       return false;
     }
   }
 
   return true;
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects()
+WorkerPrivate::ProxyReleaseMainThreadObjects()
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(!mMainThreadObjectsForgotten);
 
   nsCOMPtr<nsILoadGroup> loadGroupToCancel;
   // If we're not overriden, then do nothing here.  Let the load group get
   // handled in ForgetMainThreadObjects().
   if (mLoadInfo.mInterfaceRequestor) {
     mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
   }
 
-  bool result = mLoadInfo.ProxyReleaseMainThreadObjects(ParentAsWorkerPrivate(),
-                                                        loadGroupToCancel);
+  bool result = mLoadInfo.ProxyReleaseMainThreadObjects(this, loadGroupToCancel);
 
   mMainThreadObjectsForgotten = true;
 
   return result;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateContextOptions(
-                                    const JS::ContextOptions& aContextOptions)
+WorkerPrivate::UpdateContextOptions(const JS::ContextOptions& aContextOptions)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     mJSSettings.contextOptions = aContextOptions;
   }
 
   RefPtr<UpdateContextOptionsRunnable> runnable =
-    new UpdateContextOptionsRunnable(ParentAsWorkerPrivate(), aContextOptions);
+    new UpdateContextOptionsRunnable(this, aContextOptions);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to update worker context options!");
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateLanguages(const nsTArray<nsString>& aLanguages)
+WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages)
 {
   AssertIsOnParentThread();
 
   RefPtr<UpdateLanguagesRunnable> runnable =
-    new UpdateLanguagesRunnable(ParentAsWorkerPrivate(), aLanguages);
+    new UpdateLanguagesRunnable(this, aLanguages);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to update worker languages!");
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
-                                                            uint32_t aValue)
+WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue)
 {
   AssertIsOnParentThread();
 
   bool found = false;
 
   {
     MutexAutoLock lock(mMutex);
     found = mJSSettings.ApplyGCSetting(aKey, aValue);
   }
 
   if (found) {
     RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
-      new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey,
-                                                aValue);
+      new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
     if (!runnable->Dispatch()) {
       NS_WARNING("Failed to update memory parameter!");
     }
   }
 }
 
 #ifdef JS_GC_ZEAL
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
+WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     mJSSettings.gcZeal = aGCZeal;
     mJSSettings.gcZealFrequency = aFrequency;
   }
 
   RefPtr<UpdateGCZealRunnable> runnable =
-    new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
+    new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to update worker gczeal!");
   }
 }
 #endif
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::GarbageCollect(bool aShrinking)
+WorkerPrivate::GarbageCollect(bool aShrinking)
 {
   AssertIsOnParentThread();
 
   RefPtr<GarbageCollectRunnable> runnable =
-    new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking,
-                               /* collectChildren = */ true);
+    new GarbageCollectRunnable(this, aShrinking, /* collectChildren = */ true);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to GC worker!");
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::CycleCollect(bool aDummy)
+WorkerPrivate::CycleCollect(bool aDummy)
 {
   AssertIsOnParentThread();
 
   RefPtr<CycleCollectRunnable> runnable =
-    new CycleCollectRunnable(ParentAsWorkerPrivate(),
-                             /* collectChildren = */ true);
+    new CycleCollectRunnable(this, /* collectChildren = */ true);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to CC worker!");
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(bool aIsOffline)
+WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline)
 {
   AssertIsOnParentThread();
 
   RefPtr<OfflineStatusChangeRunnable> runnable =
-    new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
+    new OfflineStatusChangeRunnable(this, aIsOffline);
   if (!runnable->Dispatch()) {
     NS_WARNING("Failed to dispatch offline status change event!");
   }
 }
 
 void
 WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline)
 {
@@ -2284,62 +2140,57 @@ WorkerPrivate::OfflineStatusChangeEventI
 
   event->InitEvent(eventType, false, false);
   event->SetTrusted(true);
 
   bool dummy;
   globalScope->DispatchEvent(event, &dummy);
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::MemoryPressure(bool aDummy)
+WorkerPrivate::MemoryPressure(bool aDummy)
 {
   AssertIsOnParentThread();
 
-  RefPtr<MemoryPressureRunnable> runnable =
-    new MemoryPressureRunnable(ParentAsWorkerPrivate());
+  RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
   Unused << NS_WARN_IF(!runnable->Dispatch());
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::RegisterSharedWorker(SharedWorker* aSharedWorker,
-                                                   MessagePort* aPort)
+WorkerPrivate::RegisterSharedWorker(SharedWorker* aSharedWorker,
+                                    MessagePort* aPort)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
   MOZ_ASSERT(IsSharedWorker());
   MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
 
   if (IsSharedWorker()) {
-    RefPtr<MessagePortRunnable> runnable =
-      new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
+    RefPtr<MessagePortRunnable> runnable = new MessagePortRunnable(this, aPort);
     if (!runnable->Dispatch()) {
       return false;
     }
   }
 
   mSharedWorkers.AppendElement(aSharedWorker);
 
   // If there were other SharedWorker objects attached to this worker then they
   // may all have been frozen and this worker would need to be thawed.
   if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
     return false;
   }
 
   return true;
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
-                                                    JSContext* aCx,
-                                                    const WorkerErrorReport* aReport,
-                                                    bool aIsErrorEvent)
+WorkerPrivate::BroadcastErrorToSharedWorkers(
+                                     JSContext* aCx,
+                                     const WorkerErrorReport* aReport,
+                                     bool aIsErrorEvent)
 {
   AssertIsOnMainThread();
 
   if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
     // Don't fire any events anywhere.  Just log to console.
     // XXXbz should we log to all the consoles of all the relevant windows?
     MOZ_ASSERT(aReport);
     WorkerErrorReport::LogErrorToConsole(*aReport, 0);
@@ -2460,52 +2311,47 @@ WorkerPrivateParent<Derived>::BroadcastE
 
   // Finally log a warning in the console if no window tried to prevent it.
   if (shouldLogErrorToConsole) {
     MOZ_ASSERT(aReport);
     WorkerErrorReport::LogErrorToConsole(*aReport, 0);
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::GetAllSharedWorkers(
-                               nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
+WorkerPrivate::GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
 
   if (!aSharedWorkers.IsEmpty()) {
     aSharedWorkers.Clear();
   }
 
   for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
     aSharedWorkers.AppendElement(mSharedWorkers[i]);
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
-                                                    nsPIDOMWindowInner* aWindow)
+WorkerPrivate::CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(aWindow);
 
   bool someRemoved = false;
 
   for (uint32_t i = 0; i < mSharedWorkers.Length();) {
     if (mSharedWorkers[i]->GetOwner() == aWindow) {
       mSharedWorkers[i]->Close();
       mSharedWorkers.RemoveElementAt(i);
       someRemoved = true;
     } else {
-      MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
-                                  aWindow));
+      MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
       ++i;
     }
   }
 
   if (!someRemoved) {
     return;
   }
 
@@ -2515,48 +2361,45 @@ WorkerPrivateParent<Derived>::CloseShare
 
   if (!mSharedWorkers.IsEmpty()) {
     Freeze(nullptr);
   } else {
     Cancel();
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::CloseAllSharedWorkers()
+WorkerPrivate::CloseAllSharedWorkers()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
 
   for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
     mSharedWorkers[i]->Close();
   }
 
   mSharedWorkers.Clear();
 
   Cancel();
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::WorkerScriptLoaded()
+WorkerPrivate::WorkerScriptLoaded()
 {
   AssertIsOnMainThread();
 
   if (IsSharedWorker() || IsServiceWorker()) {
     // No longer need to hold references to the window or document we came from.
     mLoadInfo.mWindow = nullptr;
     mLoadInfo.mScriptContext = nullptr;
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
+WorkerPrivate::SetBaseURI(nsIURI* aBaseURI)
 {
   AssertIsOnMainThread();
 
   if (!mLoadInfo.mBaseURI) {
     NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
     mLoadInfo.mResolvedScriptURI = aBaseURI;
   }
 
@@ -2607,61 +2450,54 @@ WorkerPrivateParent<Derived>::SetBaseURI
   }
   else {
     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
   }
 
   nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
-                                                       nsILoadGroup* aLoadGroup)
+WorkerPrivate::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
+                                        nsILoadGroup* aLoadGroup)
 {
   return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
 }
 
-template <class Derived>
 nsresult
-WorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel* aChannel)
+WorkerPrivate::SetPrincipalFromChannel(nsIChannel* aChannel)
 {
   return mLoadInfo.SetPrincipalFromChannel(aChannel);
 }
 
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
+WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
 {
   return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
 }
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::PrincipalURIMatchesScriptURL()
+WorkerPrivate::PrincipalURIMatchesScriptURL()
 {
   return mLoadInfo.PrincipalURIMatchesScriptURL();
 }
 #endif
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
+WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
 {
   AssertIsOnMainThread();
 
   // The load group should have been overriden at init time.
   mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(
-                                           nsIConsoleReportCollector* aReporter)
+WorkerPrivate::FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter)
 {
   AssertIsOnMainThread();
 
   AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
   AutoTArray<WindowAction, 10> windowActions;
   GetAllSharedWorkers(sharedWorkers);
 
   // First find out all the shared workers' window.
@@ -2696,31 +2532,28 @@ WorkerPrivateParent<Derived>::FlushRepor
     return;
   }
 
   aReporter->ClearConsoleReports();
 }
 
 #ifdef DEBUG
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
+WorkerPrivate::AssertIsOnParentThread() const
 {
   if (GetParent()) {
     GetParent()->AssertIsOnWorkerThread();
-  }
-  else {
+  } else {
     AssertIsOnMainThread();
   }
 }
 
-template <class Derived>
 void
-WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
+WorkerPrivate::AssertInnerWindowIsCorrect() const
 {
   AssertIsOnParentThread();
 
   // Only care about top level workers from windows.
   if (mParent || !mLoadInfo.mWindow) {
     return;
   }
 
@@ -2729,65 +2562,127 @@ WorkerPrivateParent<Derived>::AssertInne
   nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
   NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
                "Inner window no longer correct!");
 }
 
 #endif
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-template <class Derived>
 bool
-WorkerPrivateParent<Derived>::PrincipalIsValid() const
+WorkerPrivate::PrincipalIsValid() const
 {
   return mLoadInfo.PrincipalIsValid();
 }
 #endif
 
 WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsAString& aWorkerName,
                              const nsACString& aServiceWorkerScope,
                              WorkerLoadInfo& aLoadInfo)
-  : WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
-                                       aIsChromeWorker, aWorkerType,
-                                       aWorkerName, aServiceWorkerScope,
-                                       aLoadInfo)
-  , mDebuggerRegistered(false)
+  : mMutex("WorkerPrivate Mutex")
+  , mCondVar(mMutex, "WorkerPrivate CondVar")
+  , mParent(aParent)
+  , mScriptURL(aScriptURL)
+  , mWorkerName(aWorkerName)
+  , mWorkerType(aWorkerType)
   , mDebugger(nullptr)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
-  , mNumHoldersPreventingShutdownStart(0)
-  , mDebuggerEventLoopLevel(0)
   , mMainThreadEventTarget(GetMainThreadEventTarget())
   , mWorkerControlEventTarget(new WorkerEventTarget(this,
                                                     WorkerEventTarget::Behavior::ControlOnly))
   , mWorkerHybridEventTarget(new WorkerEventTarget(this,
                                                    WorkerEventTarget::Behavior::Hybrid))
+  , mParentStatus(Pending)
+  , mStatus(Pending)
+  , mBusyCount(0)
+  , mLoadingWorkerScript(false)
+  , mCreationTimeStamp(TimeStamp::Now())
+  , mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
+  , mNumHoldersPreventingShutdownStart(0)
+  , mDebuggerEventLoopLevel(0)
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
-  , mStatus(Pending)
+  , mParentWindowPausedDepth(0)
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
   , mPendingEventQueueClearing(false)
   , mCancelAllPendingRunnables(false)
   , mPeriodicGCTimerRunning(false)
   , mIdleGCTimerRunning(false)
   , mWorkerScriptExecutedSuccessfully(false)
   , mFetchHandlerWasAdded(false)
   , mOnLine(false)
+  , mMainThreadObjectsForgotten(false)
+  , mIsChromeWorker(aIsChromeWorker)
+  , mParentFrozen(false)
+  , mIsSecureContext(false)
+  , mDebuggerRegistered(false)
 {
+  MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
+  mLoadInfo.StealFrom(aLoadInfo);
+
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
+
+    // Note that this copies our parent's secure context state into mJSSettings.
+    aParent->CopyJSSettings(mJSSettings);
+
+    // And manually set our mIsSecureContext, though it's not really relevant to
+    // dedicated workers...
+    mIsSecureContext = aParent->IsSecureContext();
+    MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
+
+    MOZ_ASSERT(IsDedicatedWorker());
+
+    if (aParent->mParentFrozen) {
+      Freeze(nullptr);
+    }
+
     mOnLine = aParent->OnLine();
   }
   else {
     AssertIsOnMainThread();
+
+    RuntimeService::GetDefaultJSSettings(mJSSettings);
+
+    // Our secure context state depends on the kind of worker we have.
+    if (UsesSystemPrincipal() || IsServiceWorker()) {
+      mIsSecureContext = true;
+    } else if (mLoadInfo.mWindow) {
+      // Shared and dedicated workers both inherit the loading window's secure
+      // context state.  Shared workers then prevent windows with a different
+      // secure context state from attaching to them.
+      mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
+    } else {
+      MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
+                             "that has no parent and no associated window");
+    }
+
+    if (mIsSecureContext) {
+      mJSSettings.chrome.compartmentOptions
+                 .creationOptions().setSecureContext(true);
+      mJSSettings.content.compartmentOptions
+                 .creationOptions().setSecureContext(true);
+    }
+
+    // Our parent can get suspended after it initiates the async creation
+    // of a new worker thread.  In this case suspend the new worker as well.
+    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
+      ParentWindowPaused();
+    }
+
+    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
+      Freeze(mLoadInfo.mWindow);
+    }
+
     mOnLine = !NS_IsOffline();
   }
 
   nsCOMPtr<nsISerialEventTarget> target;
 
   // A child worker just inherits the parent workers ThrottledEventQueue
   // and main thread target for now.  This is mainly due to the restriction
   // that ThrottledEventQueue can only be created on the main thread at the
@@ -2817,16 +2712,18 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
     mMainThreadEventTarget = mMainThreadThrottledEventQueue;
   } else {
     mMainThreadEventTarget = target.forget();
   }
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
+  DropJSObjects(this);
+
   mWorkerControlEventTarget->ForgetWorkerPrivate(this);
 
   // We force the hybrid event target to forget the thread when we
   // enter the Killing state, but we do it again here to be safe.
   // Its possible that we may be created and destroyed without progressing
   // to Killing via some obscure code path.
   mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
 }
@@ -3983,17 +3880,17 @@ WorkerPrivate::ModifyBusyCountFromWorker
   }
 
   RefPtr<ModifyBusyCountRunnable> runnable =
     new ModifyBusyCountRunnable(this, aIncrease);
   return runnable->Dispatch();
 }
 
 bool
-WorkerPrivate::AddChildWorker(ParentType* aChildWorker)
+WorkerPrivate::AddChildWorker(WorkerPrivate* aChildWorker)
 {
   AssertIsOnWorkerThread();
 
 #ifdef DEBUG
   {
     WorkerStatus currentStatus;
     {
       MutexAutoLock lock(mMutex);
@@ -4009,17 +3906,17 @@ WorkerPrivate::AddChildWorker(ParentType
   mChildWorkers.AppendElement(aChildWorker);
 
   return mChildWorkers.Length() == 1 ?
          ModifyBusyCountFromWorker(true) :
          true;
 }
 
 void
-WorkerPrivate::RemoveChildWorker(ParentType* aChildWorker)
+WorkerPrivate::RemoveChildWorker(WorkerPrivate* aChildWorker)
 {
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
                "Didn't know about this one!");
   mChildWorkers.RemoveElement(aChildWorker);
 
   if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
@@ -4085,17 +3982,17 @@ WorkerPrivate::NotifyHolders(JSContext* 
   while (iter.HasMore()) {
     WorkerHolder* holder = iter.GetNext();
     if (!holder->Notify(aStatus)) {
       NS_WARNING("Failed to notify holder!");
     }
     MOZ_ASSERT(!JS_IsExceptionPending(aCx));
   }
 
-  AutoTArray<ParentType*, 10> children;
+  AutoTArray<WorkerPrivate*, 10> children;
   children.AppendElements(mChildWorkers);
 
   for (uint32_t index = 0; index < children.Length(); index++) {
     if (!children[index]->Notify(aStatus)) {
       NS_WARNING("Failed to notify child worker!");
     }
   }
 }
@@ -5349,51 +5246,45 @@ WorkerPrivate::GetPerformanceStorage()
     mPerformanceStorage = PerformanceStorageWorker::Create(this);
   }
 
   return mPerformanceStorage;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)
 
-template <class Derived>
-NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
-
-template <class Derived>
-NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
-
-template <class Derived>
-NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
+NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
+NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
+
+NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 #ifdef DEBUG
   // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
   // result.
   if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
     *aInstancePtr = this;
     return NS_OK;
   }
   else
 #endif
 NS_INTERFACE_MAP_END
 
-template <class Derived>
 NS_IMETHODIMP
-WorkerPrivateParent<Derived>::
-EventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
+WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
+                                               uint32_t aFlags)
 {
   nsCOMPtr<nsIRunnable> event(aRunnable);
   return Dispatch(event.forget(), aFlags);
 }
 
-template <class Derived>
 NS_IMETHODIMP
-WorkerPrivateParent<Derived>::
-EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
+WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+                                     uint32_t aFlags)
 {
   // May be called on any thread!
   nsCOMPtr<nsIRunnable> event(aRunnable);
 
   // Workers only support asynchronous dispatch for now.
   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
     return NS_ERROR_UNEXPECTED;
   }
@@ -5416,58 +5307,51 @@ EventTarget::Dispatch(already_AddRefed<n
     mWorkerPrivate->DispatchPrivate(workerRunnable.forget(), mNestedEventTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-template <class Derived>
 NS_IMETHODIMP
-WorkerPrivateParent<Derived>::
-EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
+                                            uint32_t)
+
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-template <class Derived>
 NS_IMETHODIMP
-WorkerPrivateParent<Derived>::
-EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
+WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
 {
   // May be called on any thread!
 
   MOZ_ASSERT(aIsOnCurrentThread);
 
   MutexAutoLock lock(mMutex);
 
   if (!mWorkerPrivate) {
     NS_WARNING("A worker's event target was used after the worker has !");
     return NS_ERROR_UNEXPECTED;
   }
 
   *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
   return NS_OK;
 }
 
-template <class Derived>
 NS_IMETHODIMP_(bool)
-WorkerPrivateParent<Derived>::
-EventTarget::IsOnCurrentThreadInfallible()
+WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible()
 {
   // May be called on any thread!
 
   MutexAutoLock lock(mMutex);
 
   if (!mWorkerPrivate) {
     NS_WARNING("A worker's event target was used after the worker has !");
     return false;
   }
 
   return mWorkerPrivate->IsOnCurrentThread();
 }
 
-// Force instantiation.
-template class WorkerPrivateParent<WorkerPrivate>;
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -95,175 +95,65 @@ public:
 
   void
   AssertCurrentThreadOwns() const
   {
     mMutex->AssertCurrentThreadOwns();
   }
 };
 
-template <class Derived>
-class WorkerPrivateParent
+class WorkerPrivate
 {
-protected:
-  class EventTarget;
-  friend class EventTarget;
-
-  typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
-
 public:
   struct LocationInfo
   {
     nsCString mHref;
     nsCString mProtocol;
     nsCString mHost;
     nsCString mHostname;
     nsCString mPort;
     nsCString mPathname;
     nsCString mSearch;
     nsCString mHash;
     nsString mOrigin;
   };
 
-protected:
-  typedef mozilla::ErrorResult ErrorResult;
-
-  SharedMutex mMutex;
-  mozilla::CondVar mCondVar;
-
-  // Protected by mMutex.
-  nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables;
+  NS_INLINE_DECL_REFCOUNTING(WorkerPrivate)
 
-private:
-  WorkerPrivate* mParent;
-  nsString mScriptURL;
-  // This is the worker name for shared workers and dedicated workers.
-  nsString mWorkerName;
-  LocationInfo mLocationInfo;
-  // The lifetime of these objects within LoadInfo is managed explicitly;
-  // they do not need to be cycle collected.
-  WorkerLoadInfo mLoadInfo;
-
-  Atomic<bool> mLoadingWorkerScript;
-
-  // Only used for top level workers.
-  nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
-
-  // Protected by mMutex.
-  workerinternals::JSSettings mJSSettings;
-
-  // Only touched on the parent thread (currently this is always the main
-  // thread as SharedWorkers are always top-level).
-  nsTArray<RefPtr<SharedWorker>> mSharedWorkers;
-
-  // This is touched on parent thread only, but it can be read on a different
-  // thread before crashing because hanging.
-  Atomic<uint64_t> mBusyCount;
+  static already_AddRefed<WorkerPrivate>
+  Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
+              WorkerType aWorkerType, const nsAString& aWorkerName,
+              const nsACString& aServiceWorkerScope,
+              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
 
-  // SharedWorkers may have multiple windows paused, so this must be
-  // a count instead of just a boolean.
-  uint32_t mParentWindowPausedDepth;
-  WorkerStatus mParentStatus;
-  bool mParentFrozen;
-  bool mIsChromeWorker;
-  bool mMainThreadObjectsForgotten;
-  // mIsSecureContext is set once in our constructor; after that it can be read
-  // from various threads.  We could make this const if we were OK with setting
-  // it in the initializer list via calling some function that takes all sorts
-  // of state (loadinfo, worker type, parent).
-  //
-  // It's a bit unfortunate that we have to have an out-of-band boolean for
-  // this, but we need access to this state from the parent thread, and we can't
-  // use our global object's secure state there.
-  bool mIsSecureContext;
-  WorkerType mWorkerType;
-  TimeStamp mCreationTimeStamp;
-  DOMHighResTimeStamp mCreationTimeHighRes;
+  enum LoadGroupBehavior
+  {
+    InheritLoadGroup,
+    OverrideLoadGroup
+  };
 
-protected:
-  // The worker is owned by its thread, which is represented here.  This is set
-  // in Constructor() and emptied by WorkerFinishedRunnable, and conditionally
-  // traversed by the cycle collector if the busy count is zero.
-  //
-  // There are 4 ways a worker can be terminated:
-  // 1. GC/CC - When the worker is in idle state (busycount == 0), it allows to
-  //    traverse the 'hidden' mParentEventTargetRef pointer. This is the exposed
-  //    Worker webidl object. Doing this, CC will be able to detect a cycle and
-  //    Unlink is called. In Unlink, Worker calls Terminate().
-  // 2. Worker::Terminate() is called - the shutdown procedure starts
-  //    immediately.
-  // 3. WorkerScope::Close() is called - Similar to point 2.
-  // 4. xpcom-shutdown notification - We call Kill().
-  RefPtr<Worker> mParentEventTargetRef;
-  RefPtr<WorkerPrivate> mSelfRef;
-
-  WorkerPrivateParent(WorkerPrivate* aParent,
-                      const nsAString& aScriptURL, bool aIsChromeWorker,
-                      WorkerType aWorkerType,
-                      const nsAString& aWorkerName,
-                      const nsACString& aServiceWorkerScope,
-                      WorkerLoadInfo& aLoadInfo);
-
-  virtual ~WorkerPrivateParent();
-
-private:
-  Derived*
-  ParentAsWorkerPrivate() const
-  {
-    return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
-  }
-
-  bool
-  NotifyPrivate(WorkerStatus aStatus);
-
-  bool
-  TerminatePrivate()
-  {
-    return NotifyPrivate(Terminating);
-  }
-
-  nsresult
-  DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget);
-
-public:
-  NS_INLINE_DECL_REFCOUNTING(WorkerPrivateParent)
+  static nsresult
+  GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+              WorkerPrivate* aParent,
+              const nsAString& aScriptURL, bool aIsChromeWorker,
+              LoadGroupBehavior aLoadGroupBehavior, WorkerType aWorkerType,
+              WorkerLoadInfo* aLoadInfo);
 
   void
   Traverse(nsCycleCollectionTraversalCallback& aCb);
 
   void
-  EnableDebugger();
-
-  void
-  DisableDebugger();
-
-  void
   ClearSelfAndParentEventTargetRef()
   {
     AssertIsOnParentThread();
     MOZ_ASSERT(mSelfRef);
     mParentEventTargetRef = nullptr;
     mSelfRef = nullptr;
   }
 
-  nsresult
-  Dispatch(already_AddRefed<WorkerRunnable> aRunnable)
-  {
-    return DispatchPrivate(Move(aRunnable), nullptr);
-  }
-
-  nsresult
-  DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable);
-
-  nsresult
-  DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable);
-
-  already_AddRefed<WorkerRunnable>
-  MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable);
-
   // May be called on any thread...
   bool
   Start();
 
   // Called on the parent thread.
   bool
   Notify(WorkerStatus aStatus)
   {
@@ -277,721 +167,26 @@ public:
   }
 
   bool
   Kill()
   {
     return Notify(Killing);
   }
 
-  // We can assume that an nsPIDOMWindow will be available for Freeze, Thaw
-  // as these are only used for globals going in and out of the bfcache.
-  //
-  // XXXbz: This is a bald-faced lie given the uses in RegisterSharedWorker and
-  // CloseSharedWorkersForWindow, which pass null for aWindow to Thaw and Freeze
-  // respectively.  See bug 1251722.
-  bool
-  Freeze(nsPIDOMWindowInner* aWindow);
-
-  bool
-  Thaw(nsPIDOMWindowInner* aWindow);
-
-  // When we debug a worker, we want to disconnect the window and the worker
-  // communication. This happens calling this method.
-  // Note: this method doesn't suspend the worker! Use Freeze/Thaw instead.
-  void
-  ParentWindowPaused();
-
-  void
-  ParentWindowResumed();
-
   bool
   Terminate()
   {
     AssertIsOnParentThread();
     return TerminatePrivate();
   }
 
   bool
   Close();
 
-  bool
-  ModifyBusyCount(bool aIncrease);
-
-  bool
-  ProxyReleaseMainThreadObjects();
-
-  void
-  UpdateContextOptions(const JS::ContextOptions& aContextOptions);
-
-  void
-  UpdateLanguages(const nsTArray<nsString>& aLanguages);
-
-  void
-  UpdateJSWorkerMemoryParameter(JSGCParamKey key, uint32_t value);
-
-#ifdef JS_GC_ZEAL
-  void
-  UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency);
-#endif
-
-  void
-  GarbageCollect(bool aShrinking);
-
-  void
-  CycleCollect(bool aDummy);
-
-  void
-  OfflineStatusChangeEvent(bool aIsOffline);
-
-  void
-  MemoryPressure(bool aDummy);
-
-  bool
-  RegisterSharedWorker(SharedWorker* aSharedWorker, MessagePort* aPort);
-
-  void
-  BroadcastErrorToSharedWorkers(JSContext* aCx,
-                                const WorkerErrorReport* aReport,
-                                bool aIsErrorEvent);
-
-  void
-  WorkerScriptLoaded();
-
-  void
-  QueueRunnable(nsIRunnable* aRunnable)
-  {
-    AssertIsOnParentThread();
-    mQueuedRunnables.AppendElement(aRunnable);
-  }
-
-  WorkerPrivate*
-  GetParent() const
-  {
-    return mParent;
-  }
-
-  bool
-  IsFrozen() const
-  {
-    AssertIsOnParentThread();
-    return mParentFrozen;
-  }
-
-  bool
-  IsParentWindowPaused() const
-  {
-    AssertIsOnParentThread();
-    return mParentWindowPausedDepth > 0;
-  }
-
-  bool
-  IsAcceptingEvents()
-  {
-    AssertIsOnParentThread();
-
-    MutexAutoLock lock(mMutex);
-    return mParentStatus < Terminating;
-  }
-
-  WorkerStatus
-  ParentStatusProtected()
-  {
-    AssertIsOnParentThread();
-    MutexAutoLock lock(mMutex);
-    return mParentStatus;
-  }
-
-  WorkerStatus
-  ParentStatus() const
-  {
-    mMutex.AssertCurrentThreadOwns();
-    return mParentStatus;
-  }
-
-  nsIScriptContext*
-  GetScriptContext() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mScriptContext;
-  }
-
-  const nsString&
-  ScriptURL() const
-  {
-    return mScriptURL;
-  }
-
-  const nsCString&
-  Domain() const
-  {
-    return mLoadInfo.mDomain;
-  }
-
-  bool
-  IsFromWindow() const
-  {
-    return mLoadInfo.mFromWindow;
-  }
-
-  nsLoadFlags
-  GetLoadFlags() const
-  {
-    return mLoadInfo.mLoadFlags;
-  }
-
-  uint64_t
-  WindowID() const
-  {
-    return mLoadInfo.mWindowID;
-  }
-
-  uint64_t
-  ServiceWorkerID() const
-  {
-    return GetServiceWorkerDescriptor().Id();
-  }
-
-  const nsCString&
-  ServiceWorkerScope() const
-  {
-    return GetServiceWorkerDescriptor().Scope();
-  }
-
-  nsIURI*
-  GetBaseURI() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mBaseURI;
-  }
-
-  void
-  SetBaseURI(nsIURI* aBaseURI);
-
-  nsIURI*
-  GetResolvedScriptURI() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mResolvedScriptURI;
-  }
-
-  const nsString&
-  ServiceWorkerCacheName() const
-  {
-    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
-    AssertIsOnMainThread();
-    return mLoadInfo.mServiceWorkerCacheName;
-  }
-
-  const ServiceWorkerDescriptor&
-  GetServiceWorkerDescriptor() const
-  {
-    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
-    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
-    return mLoadInfo.mServiceWorkerDescriptor.ref();
-  }
-
-  void
-  UpdateServiceWorkerState(ServiceWorkerState aState)
-  {
-    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
-    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
-    return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState);
-  }
-
-  const Maybe<ServiceWorkerDescriptor>&
-  GetParentController() const
-  {
-    return mLoadInfo.mParentController;
-  }
-
-  const ChannelInfo&
-  GetChannelInfo() const
-  {
-    return mLoadInfo.mChannelInfo;
-  }
-
-  void
-  SetChannelInfo(const ChannelInfo& aChannelInfo)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
-    MOZ_ASSERT(aChannelInfo.IsInitialized());
-    mLoadInfo.mChannelInfo = aChannelInfo;
-  }
-
-  void
-  InitChannelInfo(nsIChannel* aChannel)
-  {
-    mLoadInfo.mChannelInfo.InitFromChannel(aChannel);
-  }
-
-  void
-  InitChannelInfo(const ChannelInfo& aChannelInfo)
-  {
-    mLoadInfo.mChannelInfo = aChannelInfo;
-  }
-
-  // This is used to handle importScripts(). When the worker is first loaded
-  // and executed, it happens in a sync loop. At this point it sets
-  // mLoadingWorkerScript to true. importScripts() calls that occur during the
-  // execution run in nested sync loops and so this continues to return true,
-  // leading to these scripts being cached offline.
-  // mLoadingWorkerScript is set to false when the top level loop ends.
-  // importScripts() in function calls or event handlers are always fetched
-  // from the network.
-  bool
-  LoadScriptAsPartOfLoadingServiceWorkerScript()
-  {
-    MOZ_ASSERT(IsServiceWorker());
-    return mLoadingWorkerScript;
-  }
-
-  void
-  SetLoadingWorkerScript(bool aLoadingWorkerScript)
-  {
-    // any thread
-    MOZ_ASSERT(IsServiceWorker());
-    mLoadingWorkerScript = aLoadingWorkerScript;
-  }
-
-  TimeStamp CreationTimeStamp() const
-  {
-    return mCreationTimeStamp;
-  }
-
-  DOMHighResTimeStamp CreationTime() const
-  {
-    return mCreationTimeHighRes;
-  }
-
-  DOMHighResTimeStamp TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const
-  {
-    MOZ_ASSERT(!aTimeStamp.IsNull());
-    TimeDuration duration = aTimeStamp - mCreationTimeStamp;
-    return duration.ToMilliseconds();
-  }
-
-  nsIPrincipal*
-  GetPrincipal() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mPrincipal;
-  }
-
-  nsIPrincipal*
-  GetLoadingPrincipal() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mLoadingPrincipal;
-  }
-
-  const nsAString& Origin() const
-  {
-    return mLoadInfo.mOrigin;
-  }
-
-  nsILoadGroup*
-  GetLoadGroup() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mLoadGroup;
-  }
-
-  // This method allows the principal to be retrieved off the main thread.
-  // Principals are main-thread objects so the caller must ensure that all
-  // access occurs on the main thread.
-  nsIPrincipal*
-  GetPrincipalDontAssertMainThread() const
-  {
-      return mLoadInfo.mPrincipal;
-  }
-
-  nsresult
-  SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
-
-  nsresult
-  SetPrincipalFromChannel(nsIChannel* aChannel);
-
-  bool
-  FinalChannelPrincipalIsValid(nsIChannel* aChannel);
-
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-  bool
-  PrincipalURIMatchesScriptURL();
-#endif
-
-  bool
-  UsesSystemPrincipal() const
-  {
-    return mLoadInfo.mPrincipalIsSystem;
-  }
-
-  const PrincipalInfo&
-  GetPrincipalInfo() const
-  {
-    return *mLoadInfo.mPrincipalInfo;
-  }
-
-  already_AddRefed<nsIChannel>
-  ForgetWorkerChannel()
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mChannel.forget();
-  }
-
-  nsIDocument* GetDocument() const;
-
-  nsPIDOMWindowInner*
-  GetWindow()
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mWindow;
-  }
-
-  nsIContentSecurityPolicy*
-  GetCSP() const
-  {
-    AssertIsOnMainThread();
-    return mLoadInfo.mCSP;
-  }
-
-  void
-  SetCSP(nsIContentSecurityPolicy* aCSP);
-
-  nsresult
-  SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
-                         const nsACString& aCSPReportOnlyHeaderValue);
-
-  void
-  SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue);
-
-  net::ReferrerPolicy
-  GetReferrerPolicy() const
-  {
-    return mLoadInfo.mReferrerPolicy;
-  }
-
-  void
-  SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
-  {
-    mLoadInfo.mReferrerPolicy = aReferrerPolicy;
-  }
-
-  bool
-  IsEvalAllowed() const
-  {
-    return mLoadInfo.mEvalAllowed;
-  }
-
-  void
-  SetEvalAllowed(bool aEvalAllowed)
-  {
-    mLoadInfo.mEvalAllowed = aEvalAllowed;
-  }
-
-  bool
-  GetReportCSPViolations() const
-  {
-    return mLoadInfo.mReportCSPViolations;
-  }
-
-  void
-  SetReportCSPViolations(bool aReport)
-  {
-    mLoadInfo.mReportCSPViolations = aReport;
-  }
-
-  bool
-  XHRParamsAllowed() const
-  {
-    return mLoadInfo.mXHRParamsAllowed;
-  }
-
-  void
-  SetXHRParamsAllowed(bool aAllowed)
-  {
-    mLoadInfo.mXHRParamsAllowed = aAllowed;
-  }
-
-  LocationInfo&
-  GetLocationInfo()
-  {
-    return mLocationInfo;
-  }
-
-  void
-  CopyJSSettings(workerinternals::JSSettings& aSettings)
-  {
-    mozilla::MutexAutoLock lock(mMutex);
-    aSettings = mJSSettings;
-  }
-
-  void
-  CopyJSCompartmentOptions(JS::CompartmentOptions& aOptions)
-  {
-    mozilla::MutexAutoLock lock(mMutex);
-    aOptions = IsChromeWorker() ? mJSSettings.chrome.compartmentOptions
-                                : mJSSettings.content.compartmentOptions;
-  }
-
-  // The ability to be a chrome worker is orthogonal to the type of
-  // worker [Dedicated|Shared|Service].
-  bool
-  IsChromeWorker() const
-  {
-    return mIsChromeWorker;
-  }
-
-  WorkerType
-  Type() const
-  {
-    return mWorkerType;
-  }
-
-  bool
-  IsDedicatedWorker() const
-  {
-    return mWorkerType == WorkerTypeDedicated;
-  }
-
-  bool
-  IsSharedWorker() const
-  {
-    return mWorkerType == WorkerTypeShared;
-  }
-
-  bool
-  IsServiceWorker() const
-  {
-    return mWorkerType == WorkerTypeService;
-  }
-
-  nsContentPolicyType
-  ContentPolicyType() const
-  {
-    return ContentPolicyType(mWorkerType);
-  }
-
-  static nsContentPolicyType
-  ContentPolicyType(WorkerType aWorkerType)
-  {
-    switch (aWorkerType) {
-    case WorkerTypeDedicated:
-      return nsIContentPolicy::TYPE_INTERNAL_WORKER;
-    case WorkerTypeShared:
-      return nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
-    case WorkerTypeService:
-      return nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
-    default:
-      MOZ_ASSERT_UNREACHABLE("Invalid worker type");
-      return nsIContentPolicy::TYPE_INVALID;
-    }
-  }
-
-  const nsString&
-  WorkerName() const
-  {
-    return mWorkerName;
-  }
-
-  bool
-  IsStorageAllowed() const
-  {
-    return mLoadInfo.mStorageAllowed;
-  }
-
-  const OriginAttributes&
-  GetOriginAttributes() const
-  {
-    return mLoadInfo.mOriginAttributes;
-  }
-
-  // Determine if the SW testing per-window flag is set by devtools
-  bool
-  ServiceWorkersTestingInWindow() const
-  {
-    return mLoadInfo.mServiceWorkersTestingInWindow;
-  }
-
-  void
-  GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers);
-
-  void
-  CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow);
-
-  void
-  CloseAllSharedWorkers();
-
-  void
-  UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
-
-  already_AddRefed<nsIRunnable>
-  StealLoadFailedAsyncRunnable()
-  {
-    return mLoadInfo.mLoadFailedAsyncRunnable.forget();
-  }
-
-  void
-  FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter);
-
-  // Check whether this worker is a secure context.  For use from the parent
-  // thread only; the canonical "is secure context" boolean is stored on the
-  // compartment of the worker global.  The only reason we don't
-  // AssertIsOnParentThread() here is so we can assert that this value matches
-  // the one on the compartment, which has to be done from the worker thread.
-  bool IsSecureContext() const
-  {
-    return mIsSecureContext;
-  }
-
-#ifdef DEBUG
-  void
-  AssertIsOnParentThread() const;
-
-  void
-  AssertInnerWindowIsCorrect() const;
-#else
-  void
-  AssertIsOnParentThread() const
-  { }
-
-  void
-  AssertInnerWindowIsCorrect() const
-  { }
-#endif
-
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-  bool
-  PrincipalIsValid() const;
-#endif
-
-  // This method is used by RuntimeService to know what is going wrong the
-  // shutting down.
-  uint32_t
-  BusyCount()
-  {
-    return mBusyCount;
-  }
-};
-
-class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
-{
-  friend class mozilla::dom::WorkerHolder;
-  friend class WorkerPrivateParent<WorkerPrivate>;
-  typedef WorkerPrivateParent<WorkerPrivate> ParentType;
-  friend class AutoSyncLoopHolder;
-
-  struct TimeoutInfo;
-
-  class MemoryReporter;
-  friend class MemoryReporter;
-
-  friend class mozilla::dom::WorkerThread;
-
-  enum GCTimerMode
-  {
-    PeriodicTimer = 0,
-    IdleTimer,
-    NoTimer
-  };
-
-  bool mDebuggerRegistered;
-  WorkerDebugger* mDebugger;
-
-  workerinternals::Queue<WorkerControlRunnable*, 4> mControlQueue;
-  workerinternals::Queue<WorkerRunnable*, 4> mDebuggerQueue;
-
-  // Touched on multiple threads, protected with mMutex.
-  JSContext* mJSContext;
-  RefPtr<WorkerThread> mThread;
-  PRThread* mPRThread;
-
-  // Things touched on worker thread only.
-  RefPtr<WorkerGlobalScope> mScope;
-  RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
-  nsTArray<ParentType*> mChildWorkers;
-  nsTObserverArray<WorkerHolder*> mHolders;
-  uint32_t mNumHoldersPreventingShutdownStart;
-  nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
-  uint32_t mDebuggerEventLoopLevel;
-  RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;
-  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
-  RefPtr<WorkerEventTarget> mWorkerControlEventTarget;
-  RefPtr<WorkerEventTarget> mWorkerHybridEventTarget;
-
-  struct SyncLoopInfo
-  {
-    explicit SyncLoopInfo(EventTarget* aEventTarget);
-
-    RefPtr<EventTarget> mEventTarget;
-    bool mCompleted;
-    bool mResult;
-#ifdef DEBUG
-    bool mHasRun;
-#endif
-  };
-
-  // This is only modified on the worker thread, but in DEBUG builds
-  // AssertValidSyncLoop function iterates it on other threads. Therefore
-  // modifications are done with mMutex held *only* in DEBUG builds.
-  nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
-
-  nsCOMPtr<nsITimer> mTimer;
-  nsCOMPtr<nsITimerCallback> mTimerRunnable;
-
-  nsCOMPtr<nsITimer> mGCTimer;
-
-  RefPtr<MemoryReporter> mMemoryReporter;
-
-  // fired on the main thread if the worker script fails to load
-  nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
-
-  RefPtr<PerformanceStorage> mPerformanceStorage;
-
-  JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
-  TimeStamp mKillTime;
-  uint32_t mErrorHandlerRecursionCount;
-  uint32_t mNextTimeoutId;
-  WorkerStatus mStatus;
-  UniquePtr<ClientSource> mClientSource;
-  bool mFrozen;
-  bool mTimerRunning;
-  bool mRunningExpiredTimeouts;
-  bool mPendingEventQueueClearing;
-  bool mCancelAllPendingRunnables;
-  bool mPeriodicGCTimerRunning;
-  bool mIdleGCTimerRunning;
-  bool mWorkerScriptExecutedSuccessfully;
-  bool mFetchHandlerWasAdded;
-  bool mOnLine;
-
-protected:
-  ~WorkerPrivate();
-
-public:
-  static already_AddRefed<WorkerPrivate>
-  Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
-              WorkerType aWorkerType, const nsAString& aWorkerName,
-              const nsACString& aServiceWorkerScope,
-              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
-
-  enum LoadGroupBehavior
-  {
-    InheritLoadGroup,
-    OverrideLoadGroup
-  };
-
-  static nsresult
-  GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
-              WorkerPrivate* aParent,
-              const nsAString& aScriptURL, bool aIsChromeWorker,
-              LoadGroupBehavior aLoadGroupBehavior, WorkerType aWorkerType,
-              WorkerLoadInfo* aLoadInfo);
-
   // The passed principal must be the Worker principal in case of a
   // ServiceWorker and the loading principal for any other type.
   static void
   OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
                             nsIPrincipal* aPrincipal);
 
   bool
   IsDebuggerRegistered()
@@ -1083,20 +278,20 @@ public:
 
   void
   UnlinkTimeouts();
 
   bool
   ModifyBusyCountFromWorker(bool aIncrease);
 
   bool
-  AddChildWorker(ParentType* aChildWorker);
+  AddChildWorker(WorkerPrivate* aChildWorker);
 
   void
-  RemoveChildWorker(ParentType* aChildWorker);
+  RemoveChildWorker(WorkerPrivate* aChildWorker);
 
   void
   PostMessageToParent(JSContext* aCx,
                       JS::Handle<JS::Value> aMessage,
                       const Sequence<JSObject*>& aTransferable,
                       ErrorResult& aRv)
   {
     PostMessageToParentInternal(aCx, aMessage, aTransferable, aRv);
@@ -1373,38 +568,654 @@ public:
   Control(const ServiceWorkerDescriptor& aServiceWorker);
 
   void
   ExecutionReady();
 
   PerformanceStorage*
   GetPerformanceStorage();
 
+  bool
+  IsAcceptingEvents()
+  {
+    AssertIsOnParentThread();
+
+    MutexAutoLock lock(mMutex);
+    return mParentStatus < Terminating;
+  }
+
+  WorkerStatus
+  ParentStatusProtected()
+  {
+    AssertIsOnParentThread();
+    MutexAutoLock lock(mMutex);
+    return mParentStatus;
+  }
+
+  WorkerStatus
+  ParentStatus() const
+  {
+    mMutex.AssertCurrentThreadOwns();
+    return mParentStatus;
+  }
+
   Worker*
   ParentEventTargetRef() const
   {
     MOZ_DIAGNOSTIC_ASSERT(mParentEventTargetRef);
     return mParentEventTargetRef;
   }
 
   void
   SetParentEventTargetRef(Worker* aParentEventTargetRef)
   {
     MOZ_DIAGNOSTIC_ASSERT(aParentEventTargetRef);
     MOZ_DIAGNOSTIC_ASSERT(!mParentEventTargetRef);
     mParentEventTargetRef = aParentEventTargetRef;
   }
 
+  bool
+  ModifyBusyCount(bool aIncrease);
+
+  // This method is used by RuntimeService to know what is going wrong the
+  // shutting down.
+  uint32_t
+  BusyCount()
+  {
+    return mBusyCount;
+  }
+
+  // Check whether this worker is a secure context.  For use from the parent
+  // thread only; the canonical "is secure context" boolean is stored on the
+  // compartment of the worker global.  The only reason we don't
+  // AssertIsOnParentThread() here is so we can assert that this value matches
+  // the one on the compartment, which has to be done from the worker thread.
+  bool IsSecureContext() const
+  {
+    return mIsSecureContext;
+  }
+
+  TimeStamp CreationTimeStamp() const
+  {
+    return mCreationTimeStamp;
+  }
+
+  DOMHighResTimeStamp CreationTime() const
+  {
+    return mCreationTimeHighRes;
+  }
+
+  DOMHighResTimeStamp TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const
+  {
+    MOZ_ASSERT(!aTimeStamp.IsNull());
+    TimeDuration duration = aTimeStamp - mCreationTimeStamp;
+    return duration.ToMilliseconds();
+  }
+
+  LocationInfo&
+  GetLocationInfo()
+  {
+    return mLocationInfo;
+  }
+
+  void
+  CopyJSSettings(workerinternals::JSSettings& aSettings)
+  {
+    mozilla::MutexAutoLock lock(mMutex);
+    aSettings = mJSSettings;
+  }
+
+  void
+  CopyJSCompartmentOptions(JS::CompartmentOptions& aOptions)
+  {
+    mozilla::MutexAutoLock lock(mMutex);
+    aOptions = IsChromeWorker() ? mJSSettings.chrome.compartmentOptions
+                                : mJSSettings.content.compartmentOptions;
+  }
+
+  // The ability to be a chrome worker is orthogonal to the type of
+  // worker [Dedicated|Shared|Service].
+  bool
+  IsChromeWorker() const
+  {
+    return mIsChromeWorker;
+  }
+
+  WorkerPrivate*
+  GetParent() const
+  {
+    return mParent;
+  }
+
+  bool
+  IsFrozen() const
+  {
+    AssertIsOnParentThread();
+    return mParentFrozen;
+  }
+
+  bool
+  IsParentWindowPaused() const
+  {
+    AssertIsOnParentThread();
+    return mParentWindowPausedDepth > 0;
+  }
+
+  // When we debug a worker, we want to disconnect the window and the worker
+  // communication. This happens calling this method.
+  // Note: this method doesn't suspend the worker! Use Freeze/Thaw instead.
+  void
+  ParentWindowPaused();
+
+  void
+  ParentWindowResumed();
+
+  const nsString&
+  ScriptURL() const
+  {
+    return mScriptURL;
+  }
+
+  const nsString&
+  WorkerName() const
+  {
+    return mWorkerName;
+  }
+
+  WorkerType
+  Type() const
+  {
+    return mWorkerType;
+  }
+
+  bool
+  IsDedicatedWorker() const
+  {
+    return mWorkerType == WorkerTypeDedicated;
+  }
+
+  bool
+  IsSharedWorker() const
+  {
+    return mWorkerType == WorkerTypeShared;
+  }
+
+  bool
+  IsServiceWorker() const
+  {
+    return mWorkerType == WorkerTypeService;
+  }
+
+  nsContentPolicyType
+  ContentPolicyType() const
+  {
+    return ContentPolicyType(mWorkerType);
+  }
+
+  static nsContentPolicyType
+  ContentPolicyType(WorkerType aWorkerType)
+  {
+    switch (aWorkerType) {
+    case WorkerTypeDedicated:
+      return nsIContentPolicy::TYPE_INTERNAL_WORKER;
+    case WorkerTypeShared:
+      return nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+    case WorkerTypeService:
+      return nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Invalid worker type");
+      return nsIContentPolicy::TYPE_INVALID;
+    }
+  }
+
+  nsIScriptContext*
+  GetScriptContext() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mScriptContext;
+  }
+
+  const nsCString&
+  Domain() const
+  {
+    return mLoadInfo.mDomain;
+  }
+
+  bool
+  IsFromWindow() const
+  {
+    return mLoadInfo.mFromWindow;
+  }
+
+  nsLoadFlags
+  GetLoadFlags() const
+  {
+    return mLoadInfo.mLoadFlags;
+  }
+
+  uint64_t
+  WindowID() const
+  {
+    return mLoadInfo.mWindowID;
+  }
+
+  uint64_t
+  ServiceWorkerID() const
+  {
+    return GetServiceWorkerDescriptor().Id();
+  }
+
+  const nsCString&
+  ServiceWorkerScope() const
+  {
+    return GetServiceWorkerDescriptor().Scope();
+  }
+
+  nsIURI*
+  GetBaseURI() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mBaseURI;
+  }
+
+  void
+  SetBaseURI(nsIURI* aBaseURI);
+
+  nsIURI*
+  GetResolvedScriptURI() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mResolvedScriptURI;
+  }
+
+  const nsString&
+  ServiceWorkerCacheName() const
+  {
+    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+    AssertIsOnMainThread();
+    return mLoadInfo.mServiceWorkerCacheName;
+  }
+
+  const ServiceWorkerDescriptor&
+  GetServiceWorkerDescriptor() const
+  {
+    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
+    return mLoadInfo.mServiceWorkerDescriptor.ref();
+  }
+
+  void
+  UpdateServiceWorkerState(ServiceWorkerState aState)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(IsServiceWorker());
+    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mServiceWorkerDescriptor.isSome());
+    return mLoadInfo.mServiceWorkerDescriptor.ref().SetState(aState);
+  }
+
+  const Maybe<ServiceWorkerDescriptor>&
+  GetParentController() const
+  {
+    return mLoadInfo.mParentController;
+  }
+
+  const ChannelInfo&
+  GetChannelInfo() const
+  {
+    return mLoadInfo.mChannelInfo;
+  }
+
+  void
+  SetChannelInfo(const ChannelInfo& aChannelInfo)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
+    MOZ_ASSERT(aChannelInfo.IsInitialized());
+    mLoadInfo.mChannelInfo = aChannelInfo;
+  }
+
+  void
+  InitChannelInfo(nsIChannel* aChannel)
+  {
+    mLoadInfo.mChannelInfo.InitFromChannel(aChannel);
+  }
+
+  void
+  InitChannelInfo(const ChannelInfo& aChannelInfo)
+  {
+    mLoadInfo.mChannelInfo = aChannelInfo;
+  }
+
+  nsIPrincipal*
+  GetPrincipal() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mPrincipal;
+  }
+
+  nsIPrincipal*
+  GetLoadingPrincipal() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mLoadingPrincipal;
+  }
+
+  const nsAString& Origin() const
+  {
+    return mLoadInfo.mOrigin;
+  }
+
+  nsILoadGroup*
+  GetLoadGroup() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mLoadGroup;
+  }
+
+  // This method allows the principal to be retrieved off the main thread.
+  // Principals are main-thread objects so the caller must ensure that all
+  // access occurs on the main thread.
+  nsIPrincipal*
+  GetPrincipalDontAssertMainThread() const
+  {
+      return mLoadInfo.mPrincipal;
+  }
+
+  bool
+  UsesSystemPrincipal() const
+  {
+    return mLoadInfo.mPrincipalIsSystem;
+  }
+
+  const mozilla::ipc::PrincipalInfo&
+  GetPrincipalInfo() const
+  {
+    return *mLoadInfo.mPrincipalInfo;
+  }
+
+  already_AddRefed<nsIChannel>
+  ForgetWorkerChannel()
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mChannel.forget();
+  }
+
+  nsPIDOMWindowInner*
+  GetWindow()
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mWindow;
+  }
+
+  nsIContentSecurityPolicy*
+  GetCSP() const
+  {
+    AssertIsOnMainThread();
+    return mLoadInfo.mCSP;
+  }
+
+  void
+  SetCSP(nsIContentSecurityPolicy* aCSP);
+
+  nsresult
+  SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
+                         const nsACString& aCSPReportOnlyHeaderValue);
+
+  void
+  SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue);
+
+  net::ReferrerPolicy
+  GetReferrerPolicy() const
+  {
+    return mLoadInfo.mReferrerPolicy;
+  }
+
+  void
+  SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
+  {
+    mLoadInfo.mReferrerPolicy = aReferrerPolicy;
+  }
+
+  bool
+  IsEvalAllowed() const
+  {
+    return mLoadInfo.mEvalAllowed;
+  }
+
+  void
+  SetEvalAllowed(bool aEvalAllowed)
+  {
+    mLoadInfo.mEvalAllowed = aEvalAllowed;
+  }
+
+  bool
+  GetReportCSPViolations() const
+  {
+    return mLoadInfo.mReportCSPViolations;
+  }
+
+  void
+  SetReportCSPViolations(bool aReport)
+  {
+    mLoadInfo.mReportCSPViolations = aReport;
+  }
+
+  bool
+  XHRParamsAllowed() const
+  {
+    return mLoadInfo.mXHRParamsAllowed;
+  }
+
+  void
+  SetXHRParamsAllowed(bool aAllowed)
+  {
+    mLoadInfo.mXHRParamsAllowed = aAllowed;
+  }
+
+  bool
+  IsStorageAllowed() const
+  {
+    return mLoadInfo.mStorageAllowed;
+  }
+
+  const OriginAttributes&
+  GetOriginAttributes() const
+  {
+    return mLoadInfo.mOriginAttributes;
+  }
+
+  // Determine if the SW testing per-window flag is set by devtools
+  bool
+  ServiceWorkersTestingInWindow() const
+  {
+    return mLoadInfo.mServiceWorkersTestingInWindow;
+  }
+
+  already_AddRefed<nsIRunnable>
+  StealLoadFailedAsyncRunnable()
+  {
+    return mLoadInfo.mLoadFailedAsyncRunnable.forget();
+  }
+
+  // This is used to handle importScripts(). When the worker is first loaded
+  // and executed, it happens in a sync loop. At this point it sets
+  // mLoadingWorkerScript to true. importScripts() calls that occur during the
+  // execution run in nested sync loops and so this continues to return true,
+  // leading to these scripts being cached offline.
+  // mLoadingWorkerScript is set to false when the top level loop ends.
+  // importScripts() in function calls or event handlers are always fetched
+  // from the network.
+  bool
+  LoadScriptAsPartOfLoadingServiceWorkerScript()
+  {
+    MOZ_ASSERT(IsServiceWorker());
+    return mLoadingWorkerScript;
+  }
+
+  void
+  SetLoadingWorkerScript(bool aLoadingWorkerScript)
+  {
+    // any thread
+    MOZ_ASSERT(IsServiceWorker());
+    mLoadingWorkerScript = aLoadingWorkerScript;
+  }
+
+  void
+  QueueRunnable(nsIRunnable* aRunnable)
+  {
+    AssertIsOnParentThread();
+    mQueuedRunnables.AppendElement(aRunnable);
+  }
+
+  bool
+  RegisterSharedWorker(SharedWorker* aSharedWorker, MessagePort* aPort);
+
+  void
+  BroadcastErrorToSharedWorkers(JSContext* aCx,
+                                const WorkerErrorReport* aReport,
+                                bool aIsErrorEvent);
+
+  void
+  GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers);
+
+  void
+  CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow);
+
+  void
+  CloseAllSharedWorkers();
+
+  void
+  FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter);
+
+  // We can assume that an nsPIDOMWindow will be available for Freeze, Thaw
+  // as these are only used for globals going in and out of the bfcache.
+  //
+  // XXXbz: This is a bald-faced lie given the uses in RegisterSharedWorker and
+  // CloseSharedWorkersForWindow, which pass null for aWindow to Thaw and Freeze
+  // respectively.  See bug 1251722.
+  bool
+  Freeze(nsPIDOMWindowInner* aWindow);
+
+  bool
+  Thaw(nsPIDOMWindowInner* aWindow);
+
+  void
+  EnableDebugger();
+
+  void
+  DisableDebugger();
+
+  already_AddRefed<WorkerRunnable>
+  MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable);
+
+  bool
+  ProxyReleaseMainThreadObjects();
+
+  void
+  GarbageCollect(bool aShrinking);
+
+  void
+  CycleCollect(bool aDummy);
+
+  nsresult
+  SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
+
+  nsresult
+  SetPrincipalFromChannel(nsIChannel* aChannel);
+
+  bool
+  FinalChannelPrincipalIsValid(nsIChannel* aChannel);
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  bool
+  PrincipalURIMatchesScriptURL();
+#endif
+
+  void
+  UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
+
+  void
+  WorkerScriptLoaded();
+
+  nsIDocument* GetDocument() const;
+
+  void
+  MemoryPressure(bool aDummy);
+
+  void
+  UpdateContextOptions(const JS::ContextOptions& aContextOptions);
+
+  void
+  UpdateLanguages(const nsTArray<nsString>& aLanguages);
+
+  void
+  UpdateJSWorkerMemoryParameter(JSGCParamKey key, uint32_t value);
+
+#ifdef JS_GC_ZEAL
+  void
+  UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency);
+#endif
+
+  void
+  OfflineStatusChangeEvent(bool aIsOffline);
+
+  nsresult
+  Dispatch(already_AddRefed<WorkerRunnable> aRunnable)
+  {
+    return DispatchPrivate(Move(aRunnable), nullptr);
+  }
+
+  nsresult
+  DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable);
+
+  nsresult
+  DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable);
+
+#ifdef DEBUG
+  void
+  AssertIsOnParentThread() const;
+
+  void
+  AssertInnerWindowIsCorrect() const;
+#else
+  void
+  AssertIsOnParentThread() const
+  { }
+
+  void
+  AssertInnerWindowIsCorrect() const
+  { }
+#endif
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  bool
+  PrincipalIsValid() const;
+#endif
+
 private:
   WorkerPrivate(WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsAString& aWorkerName,
                 const nsACString& aServiceWorkerScope,
                 WorkerLoadInfo& aLoadInfo);
 
+  ~WorkerPrivate();
+
+  nsresult
+  DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable,
+                  nsIEventTarget* aSyncLoopTarget);
+
+  bool
+  NotifyPrivate(WorkerStatus aStatus);
+
+  bool
+  TerminatePrivate()
+  {
+    return NotifyPrivate(Terminating);
+  }
+
   bool
   MayContinueRunning()
   {
     AssertIsOnWorkerThread();
 
     WorkerStatus status;
     {
       MutexAutoLock lock(mMutex);
@@ -1467,16 +1278,23 @@ private:
   RunCurrentSyncLoop();
 
   bool
   DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread = nullptr);
 
   void
   InitializeGCTimers();
 
+  enum GCTimerMode
+  {
+    PeriodicTimer = 0,
+    IdleTimer,
+    NoTimer
+  };
+
   void
   SetGCTimerMode(GCTimerMode aMode);
 
   void
   ShutdownGCTimers();
 
   bool
   AddHolder(WorkerHolder* aHolder, WorkerStatus aFailStatus);
@@ -1488,16 +1306,176 @@ private:
   NotifyHolders(JSContext* aCx, WorkerStatus aStatus);
 
   bool
   HasActiveHolders()
   {
     return !(mChildWorkers.IsEmpty() && mTimeouts.IsEmpty() &&
              mHolders.IsEmpty());
   }
+
+  class EventTarget;
+  friend class EventTarget;
+  friend class mozilla::dom::WorkerHolder;
+  friend class AutoSyncLoopHolder;
+
+  struct TimeoutInfo;
+
+  class MemoryReporter;
+  friend class MemoryReporter;
+
+  friend class mozilla::dom::WorkerThread;
+
+  SharedMutex mMutex;
+  mozilla::CondVar mCondVar;
+
+  WorkerPrivate* mParent;
+
+  nsString mScriptURL;
+
+  // This is the worker name for shared workers and dedicated workers.
+  nsString mWorkerName;
+
+  WorkerType mWorkerType;
+
+  // The worker is owned by its thread, which is represented here.  This is set
+  // in Constructor() and emptied by WorkerFinishedRunnable, and conditionally
+  // traversed by the cycle collector if the busy count is zero.
+  //
+  // There are 4 ways a worker can be terminated:
+  // 1. GC/CC - When the worker is in idle state (busycount == 0), it allows to
+  //    traverse the 'hidden' mParentEventTargetRef pointer. This is the exposed
+  //    Worker webidl object. Doing this, CC will be able to detect a cycle and
+  //    Unlink is called. In Unlink, Worker calls Terminate().
+  // 2. Worker::Terminate() is called - the shutdown procedure starts
+  //    immediately.
+  // 3. WorkerScope::Close() is called - Similar to point 2.
+  // 4. xpcom-shutdown notification - We call Kill().
+  RefPtr<Worker> mParentEventTargetRef;
+  RefPtr<WorkerPrivate> mSelfRef;
+
+  // The lifetime of these objects within LoadInfo is managed explicitly;
+  // they do not need to be cycle collected.
+  WorkerLoadInfo mLoadInfo;
+  LocationInfo mLocationInfo;
+
+  // Protected by mMutex.
+  workerinternals::JSSettings mJSSettings;
+
+  WorkerDebugger* mDebugger;
+
+  workerinternals::Queue<WorkerControlRunnable*, 4> mControlQueue;
+  workerinternals::Queue<WorkerRunnable*, 4> mDebuggerQueue;
+
+  // Touched on multiple threads, protected with mMutex.
+  JSContext* mJSContext;
+  RefPtr<WorkerThread> mThread;
+  PRThread* mPRThread;
+
+  // Things touched on worker thread only.
+  RefPtr<WorkerGlobalScope> mScope;
+  RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
+  nsTArray<WorkerPrivate*> mChildWorkers;
+  nsTObserverArray<WorkerHolder*> mHolders;
+  nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
+  RefPtr<ThrottledEventQueue> mMainThreadThrottledEventQueue;
+  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
+  RefPtr<WorkerEventTarget> mWorkerControlEventTarget;
+  RefPtr<WorkerEventTarget> mWorkerHybridEventTarget;
+
+  struct SyncLoopInfo
+  {
+    explicit SyncLoopInfo(EventTarget* aEventTarget);
+
+    RefPtr<EventTarget> mEventTarget;
+    bool mCompleted;
+    bool mResult;
+#ifdef DEBUG
+    bool mHasRun;
+#endif
+  };
+
+  // This is only modified on the worker thread, but in DEBUG builds
+  // AssertValidSyncLoop function iterates it on other threads. Therefore
+  // modifications are done with mMutex held *only* in DEBUG builds.
+  nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
+
+  nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsITimerCallback> mTimerRunnable;
+
+  nsCOMPtr<nsITimer> mGCTimer;
+
+  RefPtr<MemoryReporter> mMemoryReporter;
+
+  // fired on the main thread if the worker script fails to load
+  nsCOMPtr<nsIRunnable> mLoadFailedRunnable;
+
+  RefPtr<PerformanceStorage> mPerformanceStorage;
+
+  // Only used for top level workers.
+  nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
+
+  // Protected by mMutex.
+  nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables;
+
+  // Only touched on the parent thread (currently this is always the main
+  // thread as SharedWorkers are always top-level).
+  nsTArray<RefPtr<SharedWorker>> mSharedWorkers;
+
+  JS::UniqueChars mDefaultLocale; // nulled during worker JSContext init
+  TimeStamp mKillTime;
+  WorkerStatus mParentStatus;
+  WorkerStatus mStatus;
+  UniquePtr<ClientSource> mClientSource;
+
+  // This is touched on parent thread only, but it can be read on a different
+  // thread before crashing because hanging.
+  Atomic<uint64_t> mBusyCount;
+
+  Atomic<bool> mLoadingWorkerScript;
+
+  TimeStamp mCreationTimeStamp;
+  DOMHighResTimeStamp mCreationTimeHighRes;
+
+  // Things touched on worker thread only.
+  uint32_t mNumHoldersPreventingShutdownStart;
+  uint32_t mDebuggerEventLoopLevel;
+
+  uint32_t mErrorHandlerRecursionCount;
+  uint32_t mNextTimeoutId;
+
+  // SharedWorkers may have multiple windows paused, so this must be
+  // a count instead of just a boolean.
+  uint32_t mParentWindowPausedDepth;
+
+  bool mFrozen;
+  bool mTimerRunning;
+  bool mRunningExpiredTimeouts;
+  bool mPendingEventQueueClearing;
+  bool mCancelAllPendingRunnables;
+  bool mPeriodicGCTimerRunning;
+  bool mIdleGCTimerRunning;
+  bool mWorkerScriptExecutedSuccessfully;
+  bool mFetchHandlerWasAdded;
+  bool mOnLine;
+  bool mMainThreadObjectsForgotten;
+  bool mIsChromeWorker;
+  bool mParentFrozen;
+
+  // mIsSecureContext is set once in our constructor; after that it can be read
+  // from various threads.  We could make this const if we were OK with setting
+  // it in the initializer list via calling some function that takes all sorts
+  // of state (loadinfo, worker type, parent).
+  //
+  // It's a bit unfortunate that we have to have an out-of-band boolean for
+  // this, but we need access to this state from the parent thread, and we can't
+  // use our global object's secure state there.
+  bool mIsSecureContext;
+
+  bool mDebuggerRegistered;
 };
 
 class AutoSyncLoopHolder
 {
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mTarget;
   uint32_t mIndex;
 
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -458,16 +458,22 @@ WebRenderProgramCache::~WebRenderProgram
 extern "C" {
 
 static void NewFrameReady(mozilla::wr::WrWindowId aWindowId)
 {
   mozilla::wr::RenderThread::Get()->IncRenderingFrameCount(aWindowId);
   mozilla::wr::RenderThread::Get()->NewFrameReady(mozilla::wr::WindowId(aWindowId));
 }
 
+void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId)
+{
+  //TODO?
+  mozilla::Unused << aWindowId;
+}
+
 void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId)
 {
   NewFrameReady(aWindowId);
 }
 
 void wr_notifier_new_scroll_frame_ready(mozilla::wr::WrWindowId aWindowId, bool aCompositeNeeded)
 {
   // If we sent a transaction that contained both scrolling updates and a
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -534,17 +534,16 @@ WebRenderAPI::WaitFlushed()
 void
 WebRenderAPI::Capture()
 {
   uint8_t bits = 3; //TODO: get from JavaScript
   const char* path = "wr-capture"; //TODO: get from JavaScript
   const char* border = "--------------------------\n";
   printf("%s Capturing WR state to: %s\n%s", border, path, border);
   wr_api_capture(mDocHandle, path, bits);
-  RenderThread::Get()->IncPendingFrameCount(GetId());
 }
 
 
 void
 TransactionBuilder::Clear()
 {
   wr_resource_updates_clear(mResourceUpdates);
 }
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -449,33 +449,34 @@ extern "C" {
 
 struct CppNotifier {
     window_id: WrWindowId,
 }
 
 unsafe impl Send for CppNotifier {}
 
 extern "C" {
+    fn wr_notifier_wake_up(window_id: WrWindowId);
     fn wr_notifier_new_frame_ready(window_id: WrWindowId);
     fn wr_notifier_new_scroll_frame_ready(window_id: WrWindowId,
                                           composite_needed: bool);
     fn wr_notifier_external_event(window_id: WrWindowId,
                                   raw_event: usize);
 }
 
 impl RenderNotifier for CppNotifier {
     fn clone(&self) -> Box<RenderNotifier> {
         Box::new(CppNotifier {
             window_id: self.window_id,
         })
     }
 
     fn wake_up(&self) {
         unsafe {
-            wr_notifier_new_frame_ready(self.window_id);
+            wr_notifier_wake_up(self.window_id);
         }
     }
 
     fn new_document_ready(&self,
                           _: DocumentId,
                           scrolled: bool,
                           composite_needed: bool) {
         unsafe {
@@ -1158,30 +1159,36 @@ pub extern "C" fn wr_resource_updates_ad
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_capture(
     dh: &mut DocumentHandle,
     path: *const c_char,
     bits_raw: u32,
 ) {
-    use std::fs::File;
+    use std::fs::{File, create_dir_all};
     use std::io::Write;
 
     let cstr = unsafe { CStr::from_ptr(path) };
     let path = PathBuf::from(&*cstr.to_string_lossy());
-    let revision_path = path.join("wr.txt");
+
+    let _ = create_dir_all(&path);
+    match File::create(path.join("wr.txt")) {
+        Ok(mut file) => {
+            let revision = include_bytes!("../revision.txt");
+            file.write(revision).unwrap();
+        }
+        Err(e) => {
+            println!("Unable to create path '{:?}' for capture: {:?}", path, e);
+            return
+        }
+    }
+
     let bits = CaptureBits::from_bits(bits_raw as _).unwrap();
     dh.api.save_capture(path, bits);
-
-    let revision = include_bytes!("../revision.txt");
-    File::create(revision_path)
-        .unwrap()
-        .write(revision)
-        .unwrap();
 }
 
 #[cfg(target_os = "windows")]
 fn read_font_descriptor(
     bytes: &mut WrVecU8,
     index: u32
 ) -> NativeFontHandle {
     let wchars = bytes.convert_into_vec::<u16>();
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1351,16 +1351,18 @@ extern bool wr_moz2d_render_cb(ByteSlice
 extern void wr_notifier_external_event(WrWindowId aWindowId,
                                        size_t aRawEvent);
 
 extern void wr_notifier_new_frame_ready(WrWindowId aWindowId);
 
 extern void wr_notifier_new_scroll_frame_ready(WrWindowId aWindowId,
                                                bool aCompositeNeeded);
 
+extern void wr_notifier_wake_up(WrWindowId aWindowId);
+
 WR_INLINE
 void wr_program_cache_delete(WrProgramCache *aProgramCache)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 WrProgramCache *wr_program_cache_new()
 WR_FUNC;
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -273,16 +273,19 @@ class GCSchedulingTunables
     MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock);
     void resetParameter(JSGCParamKey key, const AutoLockGC& lock);
 
     void setMaxMallocBytes(size_t value);
 
 private:
     void setHighFrequencyLowLimit(uint64_t value);
     void setHighFrequencyHighLimit(uint64_t value);
+    void setHighFrequencyHeapGrowthMin(double value);
+    void setHighFrequencyHeapGrowthMax(double value);
+    void setLowFrequencyHeapGrowth(double value);
     void setMinEmptyChunkCount(uint32_t value);
     void setMaxEmptyChunkCount(uint32_t value);
 };
 
 /*
  * GC Scheduling Overview
  * ======================
  *
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1404710.js
@@ -0,0 +1,12 @@
+// |jit-test| error:InternalError
+if (!('stackTest' in this))
+    throw InternalError();
+stackTest(new Function(`
+    var g = newGlobal();
+    var dbg = new Debugger(g);
+    dbg.onDebuggerStatement = function (frame) {
+        frame.evalWithBindings("x", {x: 2}).return;
+    };
+    g.eval("function f(y) { debugger; }");
+    g.f(3);
+`), false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1435321.js
@@ -0,0 +1,30 @@
+// Check that corresponding parameters are updated to ensure that invariants are
+// preserved when updating various GC parameters.
+
+gcparam('highFrequencyHeapGrowthMin', 200);
+gcparam('highFrequencyHeapGrowthMax', 400);
+assertEq(gcparam('highFrequencyHeapGrowthMin'), 200);
+assertEq(gcparam('highFrequencyHeapGrowthMax'), 400);
+
+gcparam('highFrequencyHeapGrowthMax', 150);
+assertEq(gcparam('highFrequencyHeapGrowthMin'), 150);
+assertEq(gcparam('highFrequencyHeapGrowthMax'), 150);
+
+gcparam('highFrequencyHeapGrowthMin', 300);
+assertEq(gcparam('highFrequencyHeapGrowthMin'), 300);
+assertEq(gcparam('highFrequencyHeapGrowthMax'), 300);
+
+// The following parameters are stored in bytes but specified/retrieved in MiB.
+
+gcparam('highFrequencyLowLimit', 200);
+gcparam('highFrequencyHighLimit', 500);
+assertEq(gcparam('highFrequencyLowLimit'), 200);
+assertEq(gcparam('highFrequencyHighLimit'), 500);
+
+gcparam('highFrequencyHighLimit', 100);
+assertEq(gcparam('highFrequencyLowLimit'), 99);
+assertEq(gcparam('highFrequencyHighLimit'), 100);
+
+gcparam('highFrequencyLowLimit', 300);
+assertEq(gcparam('highFrequencyLowLimit'), 300);
+assertEq(gcparam('highFrequencyHighLimit'), 300);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1319,34 +1319,31 @@ GCSchedulingTunables::setParameter(JSGCP
             return false;
         setHighFrequencyHighLimit(newLimit);
         break;
       }
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: {
         double newGrowth = value / 100.0;
         if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor)
             return false;
-        highFrequencyHeapGrowthMax_ = newGrowth;
-        MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
+        setHighFrequencyHeapGrowthMax(newGrowth);
         break;
       }
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN: {
         double newGrowth = value / 100.0;
         if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor)
             return false;
-        highFrequencyHeapGrowthMin_ = newGrowth;
-        MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
+        setHighFrequencyHeapGrowthMin(newGrowth);
         break;
       }
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH: {
         double newGrowth = value / 100.0;
         if (newGrowth <= 0.9 || newGrowth > MaxHeapGrowthFactor)
             return false;
-        lowFrequencyHeapGrowth_ = newGrowth;
-        MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
+        setLowFrequencyHeapGrowth(newGrowth);
         break;
       }
       case JSGC_DYNAMIC_HEAP_GROWTH:
         dynamicHeapGrowthEnabled_ = value != 0;
         break;
       case JSGC_DYNAMIC_MARK_SLICE:
         dynamicMarkSliceEnabled_ = value != 0;
         break;
@@ -1400,16 +1397,43 @@ GCSchedulingTunables::setHighFrequencyHi
 {
     highFrequencyHighLimitBytes_ = newLimit;
     if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_)
         highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1;
     MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_);
 }
 
 void
+GCSchedulingTunables::setHighFrequencyHeapGrowthMin(double value)
+{
+    highFrequencyHeapGrowthMin_ = value;
+    if (highFrequencyHeapGrowthMin_ > highFrequencyHeapGrowthMax_)
+        highFrequencyHeapGrowthMax_ = highFrequencyHeapGrowthMin_;
+    MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
+    MOZ_ASSERT(highFrequencyHeapGrowthMin_ <= highFrequencyHeapGrowthMax_);
+}
+
+void
+GCSchedulingTunables::setHighFrequencyHeapGrowthMax(double value)
+{
+    highFrequencyHeapGrowthMax_ = value;
+    if (highFrequencyHeapGrowthMax_ < highFrequencyHeapGrowthMin_)
+        highFrequencyHeapGrowthMin_ = highFrequencyHeapGrowthMax_;
+    MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
+    MOZ_ASSERT(highFrequencyHeapGrowthMin_ <= highFrequencyHeapGrowthMax_);
+}
+
+void
+GCSchedulingTunables::setLowFrequencyHeapGrowth(double value)
+{
+    lowFrequencyHeapGrowth_ = value;
+    MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
+}
+
+void
 GCSchedulingTunables::setMinEmptyChunkCount(uint32_t value)
 {
     minEmptyChunkCount_ = value;
     if (minEmptyChunkCount_ > maxEmptyChunkCount_)
         maxEmptyChunkCount_ = minEmptyChunkCount_;
     MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
 }
 
@@ -1486,28 +1510,23 @@ GCSchedulingTunables::resetParameter(JSG
         break;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         setHighFrequencyLowLimit(TuningDefaults::HighFrequencyLowLimitBytes);
         break;
       case JSGC_HIGH_FREQUENCY_HIGH_LIMIT:
         setHighFrequencyHighLimit(TuningDefaults::HighFrequencyHighLimitBytes);
         break;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX:
-        highFrequencyHeapGrowthMax_ =
-            TuningDefaults::HighFrequencyHeapGrowthMax;
-        MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0);
+        setHighFrequencyHeapGrowthMax(TuningDefaults::HighFrequencyHeapGrowthMax);
         break;
       case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN:
-        highFrequencyHeapGrowthMin_ =
-            TuningDefaults::HighFrequencyHeapGrowthMin;
-        MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0);
+        setHighFrequencyHeapGrowthMin(TuningDefaults::HighFrequencyHeapGrowthMin);
         break;
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
-        lowFrequencyHeapGrowth_ = TuningDefaults::LowFrequencyHeapGrowth;
-        MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0);
+        setLowFrequencyHeapGrowth(TuningDefaults::LowFrequencyHeapGrowth);
         break;
       case JSGC_DYNAMIC_HEAP_GROWTH:
         dynamicHeapGrowthEnabled_ = TuningDefaults::DynamicHeapGrowthEnabled;
         break;
       case JSGC_DYNAMIC_MARK_SLICE:
         dynamicMarkSliceEnabled_ = TuningDefaults::DynamicMarkSliceEnabled;
         break;
       case JSGC_ALLOCATION_THRESHOLD:
@@ -1833,30 +1852,33 @@ ZoneHeapThreshold::computeZoneHeapGrowth
     // The heap growth factor depends on the heap size after a GC and the GC
     // frequency. For low frequency GCs (more than 1sec between GCs) we let
     // the heap grow to 150%. For high frequency GCs we let the heap grow
     // depending on the heap size:
     //   lastBytes < highFrequencyLowLimit: 300%
     //   lastBytes > highFrequencyHighLimit: 150%
     //   otherwise: linear interpolation between 300% and 150% based on lastBytes
 
-    // Use shorter names to make the operation comprehensible.
     double minRatio = tunables.highFrequencyHeapGrowthMin();
     double maxRatio = tunables.highFrequencyHeapGrowthMax();
     double lowLimit = tunables.highFrequencyLowLimitBytes();
     double highLimit = tunables.highFrequencyHighLimitBytes();
 
+    MOZ_ASSERT(minRatio <= maxRatio);
+    MOZ_ASSERT(lowLimit < highLimit);
+
     if (lastBytes <= lowLimit)
         return maxRatio;
 
     if (lastBytes >= highLimit)
         return minRatio;
 
     double factor = maxRatio - ((maxRatio - minRatio) * ((lastBytes - lowLimit) /
                                                          (highLimit - lowLimit)));
+
     MOZ_ASSERT(factor >= minRatio);
     MOZ_ASSERT(factor <= maxRatio);
     return factor;
 }
 
 /* static */ size_t
 ZoneHeapThreshold::computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
                                            JSGCInvocationKind gckind,
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2710,17 +2710,18 @@ DebugEnvironments::takeFrameSnapshot(JSC
         return;
 
     /*
      * Use a dense array as storage (since proxies do not have trace
      * hooks). This array must not escape into the wild.
      */
     RootedArrayObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
     if (!snapshot) {
-        cx->recoverFromOutOfMemory();
+        MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed());
+        cx->clearPendingException();
         return;
     }
 
     debugEnv->initSnapshot(*snapshot);
 }
 
 /* static */ void
 DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3941,19 +3941,18 @@ nsDocumentViewer::Print(nsIPrintSettings
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::PrintPreview(nsIPrintSettings* aPrintSettings,
                                mozIDOMWindowProxy* aChildDOMWin,
                                nsIWebProgressListener* aWebProgressListener)
 {
 #if defined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
-  NS_WARNING_ASSERTION(
-    IsInitializedForPrintPreview(),
-    "Using docshell.printPreview is the preferred way for print previewing!");
+  MOZ_ASSERT(IsInitializedForPrintPreview(),
+    "For print preview nsIWebBrowserPrint must be from docshell.printPreview!");
 
   NS_ENSURE_ARG_POINTER(aChildDOMWin);
   nsresult rv = NS_OK;
 
   if (GetIsPrinting()) {
     nsPrintJob::CloseProgressDialog(aWebProgressListener);
     return NS_ERROR_FAILURE;
   }
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -12,18 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-             .getInterface(Components.interfaces.nsIWebBrowserPrint);
+  gWbp = window.frames[1].document.docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -39,18 +38,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-   .getInterface(Components.interfaces.nsIWebBrowserPrint).exitPrintPreview(); 
+  window.frames[1].document.docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run()
@@ -86,38 +84,37 @@ function run2() {
     document.getElementById("i").removeEventListener("load", arguments.callee, true);
     setTimeout(run3, 0);
   };
   document.getElementById("i").addEventListener("load", loadhandler, true);
   window.frames[0].location.reload();
 }
 
 function run3() {
-  gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-             .getInterface(Components.interfaces.nsIWebBrowserPrint);
+  gWbp = window.frames[1].document.docShell.printPreview;
   ok(gWbp.doingPrintPreview, "Should be doing print preview");
   exitprintpreview();
   setTimeout(run4, 0);
 }
 
 function run4() {
   var i = document.getElementById("i");
   i.remove();
   var loadhandler = function() {
     document.getElementById("i").removeEventListener("load", loadhandler, true);
     setTimeout(run5, 0);
   };
   i.addEventListener("load", loadhandler, true);
   document.documentElement.getBoundingClientRect();
   document.documentElement.appendChild(i);
-  gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-             .getInterface(Components.interfaces.nsIWebBrowserPrint);
-  ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
 }
 
 function run5() {
+  gWbp = window.frames[0].document.docShell.printPreview;
+  ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
+
   //XXX this shouldn't be necessary, see bug 405555
   printpreview();
   exitprintpreview();
   finish(); //should not have crashed after all of this
 }
 ]]></script>
 </window>
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -12,18 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-             .getInterface(Components.interfaces.nsIWebBrowserPrint);
+  gWbp = window.frames[1].document.docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -39,18 +38,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-   .getInterface(Components.interfaces.nsIWebBrowserPrint).exitPrintPreview(); 
+  window.frames[1].document.docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run1()
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -19,18 +19,17 @@ var ctx2;
 var counter = 0;
 
 var file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("TmpD", Components.interfaces.nsIFile);
 filePath = file.path;
 
 function printpreview() {
-  gWbp = window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-             .getInterface(Components.interfaces.nsIWebBrowserPrint);
+  gWbp = window.frames[1].document.docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -56,18 +55,17 @@ function printpreview() {
   is(before, 1, "Should have called beforeprint listener!");
   is(after, 1, "Should have called afterprint listener!");
   window.frames[0].removeEventListener("beforeprint", beforeprint, true);
   window.frames[0].removeEventListener("afterprint", afterprint, true);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-   .getInterface(Components.interfaces.nsIWebBrowserPrint).exitPrintPreview(); 
+  window.frames[1].document.docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function runTests()
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -611,40 +611,28 @@ nsCSSProps::LookupPropertyByIDLName(cons
 nsCSSFontDesc
 nsCSSProps::LookupFontDesc(const nsACString& aFontDesc)
 {
   MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref");
   nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc));
 
   if (which == eCSSFontDesc_Display && !StylePrefs::sFontDisplayEnabled) {
     which = eCSSFontDesc_UNKNOWN;
-  } else if (which == eCSSFontDesc_UNKNOWN) {
-    // check for unprefixed font-feature-settings/font-language-override
-    nsAutoCString prefixedProp;
-    prefixedProp.AppendLiteral("-moz-");
-    prefixedProp.Append(aFontDesc);
-    which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp));
   }
   return which;
 }
 
 nsCSSFontDesc
 nsCSSProps::LookupFontDesc(const nsAString& aFontDesc)
 {
   MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref");
   nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc));
 
   if (which == eCSSFontDesc_Display && !StylePrefs::sFontDisplayEnabled) {
     which = eCSSFontDesc_UNKNOWN;
-  } else if (which == eCSSFontDesc_UNKNOWN) {
-    // check for unprefixed font-feature-settings/font-language-override
-    nsAutoString prefixedProp;
-    prefixedProp.AppendLiteral("-moz-");
-    prefixedProp.Append(aFontDesc);
-    which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp));
   }
   return which;
 }
 
 nsCSSCounterDesc
 nsCSSProps::LookupCounterDesc(const nsAString& aProperty)
 {
   MOZ_ASSERT(gCounterDescTable, "no lookup table, needs addref");
--- a/netwerk/base/nsIStreamTransportService.idl
+++ b/netwerk/base/nsIStreamTransportService.idl
@@ -7,16 +7,19 @@
 interface nsITransport;
 interface nsIInputStream;
 interface nsIOutputStream;
 interface nsIInputAvailableCallback;
 
 /**
  * This service read/writes a stream on a background thread.
  *
+ * Note: instead of using this interface, probably you want to use
+ * NS_MakeAsyncNonBlockingInputStream.
+ *
  * Use this service to transform any blocking stream (e.g., file stream)
  * into a fully asynchronous stream that can be read/written without 
  * blocking the main thread.
  */
 [builtinclass, scriptable, uuid(5e0adf7d-9785-45c3-a193-04f25a75da8f)]
 interface nsIStreamTransportService : nsISupports
 {
     /**
--- a/netwerk/base/nsInputStreamPump.cpp
+++ b/netwerk/base/nsInputStreamPump.cpp
@@ -8,16 +8,18 @@
 #include "nsInputStreamPump.h"
 #include "nsIStreamTransportService.h"
 #include "nsISeekableStream.h"
 #include "nsITransport.h"
 #include "nsIThreadRetargetableStreamListener.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Logging.h"
+#include "mozilla/NonBlockingAsyncInputStream.h"
+#include "mozilla/SlicedInputStream.h"
 #include "GeckoProfiler.h"
 #include "nsIStreamListener.h"
 #include "nsILoadGroup.h"
 #include "nsNetCID.h"
 #include "nsStreamUtils.h"
 #include <algorithm>
 
 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
@@ -327,16 +329,22 @@ nsInputStreamPump::AsyncRead(nsIStreamLi
     //
 
     bool nonBlocking;
     nsresult rv = mStream->IsNonBlocking(&nonBlocking);
     if (NS_FAILED(rv)) return rv;
 
     if (nonBlocking) {
         mAsyncStream = do_QueryInterface(mStream);
+        if (!mAsyncStream) {
+            rv = NonBlockingAsyncInputStream::Create(mStream.forget(),
+                                                     getter_AddRefs(mAsyncStream));
+            if (NS_WARN_IF(NS_FAILED(rv))) return rv;
+        }
+        MOZ_ASSERT(mAsyncStream);
     }
 
     if (!mAsyncStream) {
         // ok, let's use the stream transport service to read this stream.
         nsCOMPtr<nsIStreamTransportService> sts =
             do_GetService(kStreamTransportServiceCID, &rv);
         if (NS_FAILED(rv)) return rv;
 
--- a/testing/config/tooltool-manifests/linux32/hostutils.manifest
+++ b/testing/config/tooltool-manifests/linux32/hostutils.manifest
@@ -1,10 +1,10 @@
 [
   {
-    "size": 76928680,
+    "size": 76254067,
     "visibility": "public",
-    "digest": "406551c8f13d597f89d262734f50bad8a6ccc38148bef57888994904910f655dbb96c964ea38bfe1d3dcef5a20a9953bb41beb3213f218cb7aec7acd5860e835",
+    "digest": "4371be5328a0bc01ff98aeb753e8d70677712fabd748aa871402152bf8416e703fbaec9bbe1b6e7cb21d30ff5e7071441b5783264df01de77f83a86e95c25348",
     "algorithm": "sha512",
-    "filename": "host-utils-60.0a1.en-US-linux-i686.tar.gz",
+    "filename": "host-utils-60.0a2.en-US.linux-i686.tar.gz",
     "unpack": true
   }
 ]
--- a/testing/config/tooltool-manifests/linux64/hostutils.manifest
+++ b/testing/config/tooltool-manifests/linux64/hostutils.manifest
@@ -1,10 +1,10 @@
 [
   {
-    "size": 75720275,
+    "size": 74983178,
     "visibility": "public",
-    "digest": "5931dea406d6e126a7cab95859c250c94727d83f586ea0a28075e13aeb8eed91f5a7528b3c54320bbb7cc55a673f5145cd5584dba0f602340f1c17d15e527c14",
+    "digest": "800acc77951dbd47880ded6ebfb70b2b6a1e034a52cb0e84fda1931e1825152853c0b5cfa4d05afdc2038f30c1f077fd037d9a49751505d98cbeb2b7e0377328",
     "algorithm": "sha512",
-    "filename": "host-utils-60.0a1.en-US-linux-x86_64.tar.gz",
+    "filename": "host-utils-60.0a2.en-US.linux-x86_64.tar.gz",
     "unpack": true
   }
 ]
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -149,57 +149,19 @@ nsAutoCompleteController::SetInput(nsIAu
   // Reset the current search string.
   nsAutoString value;
   input->GetTextValue(value);
   SetSearchStringInternal(value);
 
   // Clear out this reference in case the new input's popup has no tree
   mTree = nullptr;
 
-  // Initialize our list of search objects
-  uint32_t searchCount;
-  input->GetSearchCount(&searchCount);
-  mResults.SetCapacity(searchCount);
-  mSearches.SetCapacity(searchCount);
-  mImmediateSearchesCount = 0;
-
-  const char *searchCID = kAutoCompleteSearchCID;
-
   // Since the controller can be used as a service it's important to reset this.
   mClearingAutoFillSearchesAgain = false;
 
-  for (uint32_t i = 0; i < searchCount; ++i) {
-    // Use the search name to create the contract id string for the search service
-    nsAutoCString searchName;
-    input->GetSearchAt(i, searchName);
-    nsAutoCString cid(searchCID);
-    cid.Append(searchName);
-
-    // Use the created cid to get a pointer to the search service and store it for later
-    nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get());
-    if (search) {
-      mSearches.AppendObject(search);
-
-      // Count immediate searches.
-      nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
-        do_QueryInterface(search);
-      if (searchDesc) {
-        uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
-        if (NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) &&
-            searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) {
-          mImmediateSearchesCount++;
-        }
-
-        if (!mClearingAutoFillSearchesAgain) {
-          searchDesc->GetClearingAutoFillSearchesAgain(&mClearingAutoFillSearchesAgain);
-        }
-      }
-    }
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAutoCompleteController::ResetInternalState()
 {
   // Clear out the current search context
   if (mInput) {
@@ -1375,22 +1337,62 @@ nsresult
 nsAutoCompleteController::StartSearches()
 {
   // Don't create a new search timer if we're already waiting for one to fire.
   // If we don't check for this, we won't be able to cancel the original timer
   // and may crash when it fires (bug 236659).
   if (mTimer || !mInput)
     return NS_OK;
 
+  nsCOMPtr<nsIAutoCompleteInput> input(mInput);
+
+  if (!mSearches.Length()) {
+    // Initialize our list of search objects
+    uint32_t searchCount;
+    input->GetSearchCount(&searchCount);
+    mResults.SetCapacity(searchCount);
+    mSearches.SetCapacity(searchCount);
+    mImmediateSearchesCount = 0;
+
+    const char *searchCID = kAutoCompleteSearchCID;
+
+    for (uint32_t i = 0; i < searchCount; ++i) {
+      // Use the search name to create the contract id string for the search service
+      nsAutoCString searchName;
+      input->GetSearchAt(i, searchName);
+      nsAutoCString cid(searchCID);
+      cid.Append(searchName);
+
+      // Use the created cid to get a pointer to the search service and store it for later
+      nsCOMPtr<nsIAutoCompleteSearch> search = do_GetService(cid.get());
+      if (search) {
+        mSearches.AppendObject(search);
+
+        // Count immediate searches.
+        nsCOMPtr<nsIAutoCompleteSearchDescriptor> searchDesc =
+          do_QueryInterface(search);
+        if (searchDesc) {
+          uint16_t searchType = nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_DELAYED;
+          if (NS_SUCCEEDED(searchDesc->GetSearchType(&searchType)) &&
+              searchType == nsIAutoCompleteSearchDescriptor::SEARCH_TYPE_IMMEDIATE) {
+            mImmediateSearchesCount++;
+          }
+
+          if (!mClearingAutoFillSearchesAgain) {
+            searchDesc->GetClearingAutoFillSearchesAgain(&mClearingAutoFillSearchesAgain);
+          }
+        }
+      }
+    }
+  }
+
   // Check if the current input should be completed with the placeholder string
   // from the last completion until the actual search results come back.
   MaybeCompletePlaceholder();
 
-  nsCOMPtr<nsIAutoCompleteInput> input(mInput);
-
   // Get the timeout for delayed searches.
   uint32_t timeout;
   input->GetTimeout(&timeout);
 
   uint32_t immediateSearchesCount = mImmediateSearchesCount;
   if (timeout == 0) {
     // All the searches should be executed immediately.
     immediateSearchesCount = mSearches.Length();
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -128,55 +128,16 @@ var PrintUtils = {
     mm.sendAsyncMessage("Printing:Print", {
       windowID: aWindowID,
       simplifiedMode: this._shouldSimplify,
       defaultPrinterName,
     });
   },
 
   /**
-   * Deprecated.
-   *
-   * Starts the process of printing the contents of window.content.
-   *
-   */
-  print() {
-    if (gBrowser) {
-      return this.printWindow(gBrowser.selectedBrowser.outerWindowID,
-                              gBrowser.selectedBrowser);
-    }
-
-    if (this.usingRemoteTabs) {
-      throw new Error("PrintUtils.print cannot be run in windows running with " +
-                      "remote tabs. Use PrintUtils.printWindow instead.");
-    }
-
-    let domWindow = window.content;
-    let ifReq = domWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
-    let browser = ifReq.getInterface(Components.interfaces.nsIWebNavigation)
-                       .QueryInterface(Components.interfaces.nsIDocShell)
-                       .chromeEventHandler;
-    if (!browser) {
-      throw new Error("PrintUtils.print could not resolve content window " +
-                      "to a browser.");
-    }
-
-    let windowID = ifReq.getInterface(Components.interfaces.nsIDOMWindowUtils)
-                        .outerWindowID;
-
-    let Deprecated = ChromeUtils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
-    let msg = "PrintUtils.print is now deprecated. Please use PrintUtils.printWindow.";
-    let url = "https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Printing";
-    Deprecated.warning(msg, url);
-
-    this.printWindow(windowID, browser);
-    return undefined;
-  },
-
-  /**
    * Initializes print preview.
    *
    * @param aListenerObj
    *        An object that defines the following functions:
    *
    *        getPrintPreviewBrowser:
    *          Returns the <xul:browser> to display the print preview in. This
    *          <xul:browser> must have its type attribute set to "content".
@@ -268,86 +229,26 @@ var PrintUtils = {
       if (!notifyOnOpen.value.valueOf() || this._webProgressPP.value == null) {
         this.enterPrintPreview();
       }
     } catch (e) {
       this.enterPrintPreview();
     }
   },
 
-  /**
-   * Returns the nsIWebBrowserPrint associated with some content window.
-   * This method is being kept here for compatibility reasons, but should not
-   * be called by code hoping to support e10s / remote browsers.
-   *
-   * @param aWindow
-   *        The window from which to get the nsIWebBrowserPrint from.
-   * @return nsIWebBrowserPrint
-   */
-  getWebBrowserPrint(aWindow) {
-    let Deprecated = ChromeUtils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
-    let text = "getWebBrowserPrint is now deprecated, and fully unsupported for " +
-               "multi-process browsers. Please use a frame script to get " +
-               "access to nsIWebBrowserPrint from content.";
-    let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App";
-    Deprecated.warning(text, url);
-
-    if (this.usingRemoteTabs) {
-      return {};
-    }
-
-    var contentWindow = aWindow || window.content;
-    return contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                        .getInterface(Components.interfaces.nsIWebBrowserPrint);
-  },
-
-  /**
-   * Returns the nsIWebBrowserPrint from the print preview browser's docShell.
-   * This method is being kept here for compatibility reasons, but should not
-   * be called by code hoping to support e10s / remote browsers.
-   *
-   * @return nsIWebBrowserPrint
-   */
-  getPrintPreview() {
-    let Deprecated = ChromeUtils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated;
-    let text = "getPrintPreview is now deprecated, and fully unsupported for " +
-               "multi-process browsers. Please use a frame script to get " +
-               "access to nsIWebBrowserPrint from content.";
-    let url = "https://developer.mozilla.org/en-US/docs/Printing_from_a_XUL_App";
-    Deprecated.warning(text, url);
-
-    if (this.usingRemoteTabs) {
-      return {};
-    }
-
-    return this._currentPPBrowser.docShell.printPreview;
-  },
-
   // "private" methods and members. Don't use them.
 
   _listener: null,
   _closeHandlerPP: null,
   _webProgressPP: null,
   _sourceBrowser: null,
   _originalTitle: "",
   _originalURL: "",
   _shouldSimplify: false,
 
-  get usingRemoteTabs() {
-    // We memoize this, since it's highly unlikely to change over the lifetime
-    // of the window.
-    let usingRemoteTabs =
-      window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-            .getInterface(Components.interfaces.nsIWebNavigation)
-            .QueryInterface(Components.interfaces.nsILoadContext)
-            .useRemoteTabs;
-    delete this.usingRemoteTabs;
-    return this.usingRemoteTabs = usingRemoteTabs;
-  },
-
   displayPrintingError(nsresult, isPrinting) {
     // The nsresults from a printing error are mapped to strings that have
     // similar names to the errors themselves. For example, for error
     // NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, the name of the string
     // for the error message is: PERR_GFX_PRINTER_NO_PRINTER_AVAILABLE. What's
     // more, if we're in the process of doing a print preview, it's possible
     // that there are strings specific for print preview for these errors -
     // if so, the names of those strings have _PP as a suffix. It's possible
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -20,18 +20,16 @@
           href="chrome://branding/content/icon32.png"/>
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css"
           type="text/css"/>
     <link rel="stylesheet" href="chrome://global/skin/aboutSupport.css"
           type="text/css"/>
 
     <script type="application/javascript"
             src="chrome://global/content/aboutSupport.js"/>
-    <script type="application/javascript"
-            src="chrome://global/content/resetProfile.js"/>
   </head>
 
   <body dir="&locale.dir;">
 
 #ifndef ANDROID
     <div id="action-box">
       <div id="reset-box">
         <h3>&refreshProfile.title;</h3>
--- a/toolkit/content/resetProfile.js
+++ b/toolkit/content/resetProfile.js
@@ -1,20 +1,10 @@
 /* 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";
 
-// NB: this file can be loaded from aboutSupport.xhtml or from the
-// resetProfile.xul dialog, and so Cu may or may not exist already.
-// Proceed with caution:
-if (!("Cu" in window)) {
-  window.Cu = Components.utils;
-}
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");
-
 function onResetProfileAccepted() {
   let retVals = window.arguments[0];
   retVals.reset = true;
 }
--- a/toolkit/content/widgets/checkbox.xml
+++ b/toolkit/content/widgets/checkbox.xml
@@ -4,17 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 
 <bindings id="checkboxBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="checkbox" role="xul:checkbox"
+  <binding id="checkbox"
     extends="chrome://global/content/bindings/general.xml#basetext">
     <resources>
       <stylesheet src="chrome://global/skin/checkbox.css"/>
     </resources>
 
     <content>
       <xul:image class="checkbox-check" xbl:inherits="checked,disabled"/>
       <xul:hbox class="checkbox-label-box" flex="1">
--- a/toolkit/content/widgets/general.xml
+++ b/toolkit/content/widgets/general.xml
@@ -127,17 +127,17 @@
             this.selectedIndex = selectedIndex;
             return val;
           ]]>
         </setter>
       </property>
     </implementation>
   </binding>
 
-  <binding id="dropmarker" extends="xul:button" role="xul:dropmarker">
+  <binding id="dropmarker" extends="xul:button">
     <resources>
       <stylesheet src="chrome://global/skin/dropmarker.css"/>
     </resources>
 
     <content>
       <xul:image class="dropmarker-icon"/>
     </content>
   </binding>
--- a/toolkit/content/widgets/groupbox.xml
+++ b/toolkit/content/widgets/groupbox.xml
@@ -10,17 +10,17 @@
    xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="groupbox-base">
     <resources>
       <stylesheet src="chrome://global/skin/groupbox.css"/>
     </resources>
   </binding>
 
-  <binding id="groupbox" role="xul:groupbox"
+  <binding id="groupbox"
     extends="chrome://global/content/bindings/groupbox.xml#groupbox-base">
     <content>
       <xul:hbox class="groupbox-title" align="center" pack="start">
         <children includes="caption"/>
       </xul:hbox>
       <xul:box flex="1" class="groupbox-body" xbl:inherits="orient,align,pack">
         <children/>
       </xul:box>
--- a/toolkit/content/widgets/listbox.xml
+++ b/toolkit/content/widgets/listbox.xml
@@ -70,17 +70,17 @@
      * @param aDirection - specifies scrolling direction, should be either -1 or 1
      * @return the number of elements the selection scrolled
      */
     scrollOnePage(aDirection)
 
     /** Fire "select" event */
     _fireOnSelect()
    -->
-   <binding id="listbox-base" role="xul:listbox"
+   <binding id="listbox-base"
             extends="chrome://global/content/bindings/general.xml#basecontrol">
 
     <implementation implements="nsIDOMXULMultiSelectControlElement">
       <field name="_lastKeyTime">0</field>
       <field name="_incrementalString">""</field>
 
     <!-- nsIDOMXULSelectControlElement -->
       <property name="selectedItem"
@@ -943,17 +943,17 @@
         // shouldn't be scrolled by pixel scrolling events before a line/page
         // scrolling event.
         event.preventDefault();
       ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="listitem" role="xul:listitem"
+  <binding id="listitem"
            extends="chrome://global/content/bindings/general.xml#basetext">
     <resources>
       <stylesheet src="chrome://global/skin/listbox.css"/>
     </resources>
 
     <content>
       <children>
         <xul:listcell xbl:inherits="label,crop,disabled,flexlabel"/>
@@ -1108,17 +1108,17 @@
           this.checked = !this.checked;
           this.doCommand();
         }
       ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="listcell" role="xul:listcell"
+  <binding id="listcell"
            extends="chrome://global/content/bindings/general.xml#basecontrol">
 
     <resources>
       <stylesheet src="chrome://global/skin/listbox.css"/>
     </resources>
 
     <content>
       <children>
@@ -1142,30 +1142,30 @@
     <content>
       <children>
         <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
         <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
       </children>
     </content>
   </binding>
 
-  <binding id="listhead" role="xul:listhead">
+  <binding id="listhead">
 
     <resources>
       <stylesheet src="chrome://global/skin/listbox.css"/>
     </resources>
 
     <content>
       <xul:listheaditem>
         <children includes="listheader"/>
       </xul:listheaditem>
     </content>
   </binding>
 
-  <binding id="listheader" display="xul:button" role="xul:listheader">
+  <binding id="listheader" display="xul:button">
 
     <resources>
       <stylesheet src="chrome://global/skin/listbox.css"/>
     </resources>
 
     <content>
       <xul:image class="listheader-icon"/>
       <xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
--- a/toolkit/content/widgets/menu.xml
+++ b/toolkit/content/widgets/menu.xml
@@ -4,17 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 
 <bindings id="menuitemBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="menuitem-base" role="xul:menuitem"
+  <binding id="menuitem-base"
            extends="chrome://global/content/bindings/general.xml#basetext">
     <implementation implements="nsIDOMXULSelectControlItemElement, nsIDOMXULContainerItemElement">
       <property name="value" onset="this.setAttribute('value', val); return val;"
                              onget="return this.getAttribute('value');"/>
       <!-- nsIDOMXULSelectControlItemElement -->
       <property name="selected" readonly="true"
                 onget="return this.getAttribute('selected') == 'true';"/>
       <property name="control" readonly="true">
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -12,17 +12,17 @@
 
   <binding id="menulist-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <resources>
       <stylesheet src="chrome://global/content/menulist.css"/>
       <stylesheet src="chrome://global/skin/menulist.css"/>
     </resources>
   </binding>
 
-  <binding id="menulist" display="xul:menu" role="xul:menulist"
+  <binding id="menulist" display="xul:menu"
            extends="chrome://global/content/bindings/menulist.xml#menulist-base">
     <content sizetopopup="pref">
       <xul:hbox class="menulist-label-box" flex="1">
         <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
         <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey,highlightable" crop="right" flex="1"/>
         <xul:label class="menulist-highlightable-label" xbl:inherits="xbl:text=label,crop,accesskey,highlightable" crop="right" flex="1"/>
       </xul:hbox>
       <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -393,17 +393,17 @@
         if (event.target.localName == "notification" &&
             event.propertyName == "margin-top")
           this._finishAnimation();
       ]]></handler>
     </handlers>
 
   </binding>
 
-  <binding id="notification" role="xul:alert">
+  <binding id="notification">
     <content>
       <xul:hbox anonid="details" align="center" flex="1"
                 oncommand="this.parentNode._doButtonCommand(event);">
         <xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
         <xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
         <xul:spacer flex="1"/>
         <children/>
       </xul:hbox>
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -536,17 +536,17 @@
         }
       </handler>
       <handler event="popuppositioned" phase="target">
         this.adjustArrowPosition();
       </handler>
     </handlers>
   </binding>
 
-  <binding id="tooltip" role="xul:tooltip"
+  <binding id="tooltip"
            extends="chrome://global/content/bindings/popup.xml#popup-base">
     <content>
       <children>
         <xul:label class="tooltip-label" xbl:inherits="xbl:text=label" flex="1"/>
       </children>
     </content>
 
     <implementation>
--- a/toolkit/content/widgets/progressmeter.xml
+++ b/toolkit/content/widgets/progressmeter.xml
@@ -4,17 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 
 <bindings id="progressmeterBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="progressmeter" role="xul:progressmeter">
+  <binding id="progressmeter">
     <resources>
       <stylesheet src="chrome://global/skin/progressmeter.css"/>
     </resources>
 
     <content>
       <xul:spacer class="progress-bar" xbl:inherits="mode"/>
       <xul:spacer class="progress-remainder" xbl:inherits="mode"/>
     </content>
--- a/toolkit/content/widgets/radio.xml
+++ b/toolkit/content/widgets/radio.xml
@@ -4,17 +4,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 
 <bindings id="radioBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="radiogroup" role="xul:radiogroup"
+  <binding id="radiogroup"
            extends="chrome://global/content/bindings/general.xml#basecontrol">
     <resources>
       <stylesheet src="chrome://global/skin/radio.css"/>
     </resources>
 
     <implementation implements="nsIDOMXULSelectControlElement">
       <constructor>
         <![CDATA[
@@ -422,17 +422,17 @@
       </handler>
       <handler event="blur" phase="target">
         this.removeAttribute("focused");
         this.focusedItem = null;
       </handler>
     </handlers>
   </binding>
 
-  <binding id="radio" role="xul:radiobutton"
+  <binding id="radio"
     extends="chrome://global/content/bindings/general.xml#basetext">
     <resources>
       <stylesheet src="chrome://global/skin/radio.css"/>
     </resources>
 
     <content>
       <xul:image class="radio-check" xbl:inherits="disabled,selected"/>
       <xul:hbox class="radio-label-box" align="center" flex="1">
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -217,17 +217,17 @@
           Components.interfaces.nsIEventListenerService;
         let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
                             .getService(nsIEventListenerService);
         els.removeSystemEventListener(this._eventNode, "keydown", this, false);
       </destructor>
     </implementation>
   </binding>
 
-  <binding id="tabs" role="xul:tabs"
+  <binding id="tabs"
            extends="chrome://global/content/bindings/general.xml#basecontrol">
     <resources>
       <stylesheet src="chrome://global/skin/tabbox.css"/>
     </resources>
 
     <content>
       <xul:spacer class="tabs-left"/>
       <children/>
@@ -576,17 +576,17 @@
 
         event.stopPropagation();
       ]]>
       </handler>
     </handlers>
 #endif
   </binding>
 
-  <binding id="tabpanels" role="xul:tabpanels"
+  <binding id="tabpanels"
            extends="chrome://global/content/bindings/tabbox.xml#tab-base">
     <implementation implements="nsIDOMXULRelatedElement">
       <!-- nsIDOMXULRelatedElement -->
       <method name="getRelatedElement">
         <parameter name="aTabPanelElm"/>
         <body>
         <![CDATA[
           if (!aTabPanelElm)
@@ -687,17 +687,17 @@
             this.selectedIndex = selectedIndex;
             return val;
           ]]>
         </setter>
       </property>
     </implementation>
   </binding>
 
-  <binding id="tab" display="xul:button" role="xul:tab"
+  <binding id="tab" display="xul:button"
            extends="chrome://global/content/bindings/general.xml#basetext">
     <resources>
       <stylesheet src="chrome://global/skin/tabbox.css"/>
     </resources>
 
     <content>
       <xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected,visuallyselected" flex="1">
         <xul:image class="tab-icon"
--- a/toolkit/content/widgets/toolbar.xml
+++ b/toolkit/content/widgets/toolbar.xml
@@ -10,17 +10,17 @@
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="toolbar-base">
     <resources>
       <stylesheet src="chrome://global/skin/toolbar.css"/>
     </resources>
   </binding>
 
-  <binding id="toolbar" role="xul:toolbar"
+  <binding id="toolbar"
            extends="chrome://global/content/bindings/toolbar.xml#toolbar-base" />
 
   <binding id="toolbar-menubar-autohide"
            extends="chrome://global/content/bindings/toolbar.xml#toolbar">
     <implementation>
       <constructor>
         this._setInactive();
       </constructor>
@@ -131,17 +131,17 @@
                      this.getAttribute("customizing") != "true";
             };
           } catch (e) {}
         }
       ]]></constructor>
     </implementation>
   </binding>
 
-  <binding id="menubar" role="xul:menubar"
+  <binding id="menubar"
            extends="chrome://global/content/bindings/toolbar.xml#toolbar-base" display="xul:menubar">
     <implementation>
        <field name="_active">false</field>
        <field name="_statusbar">null</field>
        <field name="_originalStatusText">null</field>
        <property name="statusbar" onget="return this.getAttribute('statusbar');"
                                   onset="this.setAttribute('statusbar', val); return val;"/>
        <method name="_updateStatusText">
@@ -176,17 +176,17 @@
             this._statusbar.label = this._originalStatusText;
           ]]>
         </handler>
         <handler event="DOMMenuItemActive">this._updateStatusText(event.target.statusText);</handler>
         <handler event="DOMMenuItemInactive">this._updateStatusText("");</handler>
     </handlers>
   </binding>
 
-  <binding id="toolbardecoration" role="xul:toolbarseparator" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base">
+  <binding id="toolbardecoration" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base">
   </binding>
 
   <binding id="toolbarpaletteitem" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base" display="xul:button">
     <content>
       <xul:hbox class="toolbarpaletteitem-box" flex="1" xbl:inherits="type,place">
         <children/>
       </xul:hbox>
     </content>
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -23,17 +23,17 @@
         <parameter name="aEvent"/>
         <body><![CDATA[
           return aEvent.getModifierState("Accel");
         ]]></body>
       </method>
     </implementation>
   </binding>
 
-  <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base" role="xul:tree">
+  <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base">
     <content hidevscroll="true" hidehscroll="true" clickthrough="never">
       <children includes="treecols"/>
       <xul:stack class="tree-stack" flex="1">
         <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll">
           <children/>
         </xul:treerows>
         <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/>
       </xul:stack>
@@ -1020,17 +1020,17 @@
            }
            event.preventDefault();
          }
          ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="treecols" role="xul:treecolumns">
+  <binding id="treecols">
     <resources>
       <stylesheet src="chrome://global/skin/tree.css"/>
     </resources>
     <content orient="horizontal">
       <xul:hbox class="tree-scrollable-columns" flex="1">
         <children includes="treecol|splitter"/>
       </xul:hbox>
       <xul:treecolpicker class="treecol-image" fixed="true" xbl:inherits="tooltiptext=pickertooltiptext"/>
@@ -1264,17 +1264,17 @@
         if (cell.col && !cell.col.cycler && cell.childElt != "twisty")
           this.parentNode.changeOpenState(row);
       ]]>
       </handler>
 
     </handlers>
   </binding>
 
-  <binding id="treecol-base" role="xul:treecolumnitem"
+  <binding id="treecol-base"
            extends="chrome://global/content/bindings/tree.xml#tree-base">
     <implementation>
       <constructor>
         this.parentNode.parentNode._columnsDirty = true;
       </constructor>
 
       <property name="ordinal">
         <getter><![CDATA[
@@ -1482,17 +1482,17 @@
   </binding>
 
   <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base">
     <content>
       <xul:image class="treecol-icon" xbl:inherits="src"/>
     </content>
   </binding>
 
-  <binding id="columnpicker" display="xul:button" role="xul:button"
+  <binding id="columnpicker" display="xul:button"
            extends="chrome://global/content/bindings/tree.xml#tree-base">
     <content>
       <xul:image class="tree-columnpicker-icon"/>
       <xul:menupopup anonid="popup">
         <xul:menuseparator anonid="menuseparator"/>
         <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/>
       </xul:menupopup>
     </content>
new file mode 100644
--- /dev/null
+++ b/xpcom/io/NonBlockingAsyncInputStream.cpp
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NonBlockingAsyncInputStream.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+class NonBlockingAsyncInputStream::AsyncWaitRunnable final : public CancelableRunnable
+{
+  RefPtr<NonBlockingAsyncInputStream> mStream;
+  nsCOMPtr<nsIInputStreamCallback> mCallback;
+
+public:
+  AsyncWaitRunnable(NonBlockingAsyncInputStream* aStream,
+                    nsIInputStreamCallback* aCallback)
+    : CancelableRunnable("AsyncWaitRunnable")
+    , mStream(aStream)
+    , mCallback(aCallback)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    mStream->RunAsyncWaitCallback(this, mCallback.forget());
+    return NS_OK;
+  }
+};
+
+NS_IMPL_ADDREF(NonBlockingAsyncInputStream);
+NS_IMPL_RELEASE(NonBlockingAsyncInputStream);
+
+NonBlockingAsyncInputStream::WaitClosureOnly::WaitClosureOnly(AsyncWaitRunnable* aRunnable,
+                                                              nsIEventTarget* aEventTarget)
+  : mRunnable(aRunnable)
+  , mEventTarget(aEventTarget)
+{}
+
+NS_INTERFACE_MAP_BEGIN(NonBlockingAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
+                                     mWeakCloneableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
+                                     mWeakIPCSerializableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
+                                     mWeakSeekableInputStream)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+/* static */ nsresult
+NonBlockingAsyncInputStream::Create(already_AddRefed<nsIInputStream> aInputStream,
+                                    nsIAsyncInputStream** aResult)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aResult);
+
+  nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
+
+  bool nonBlocking = false;
+  nsresult rv = inputStream->IsNonBlocking(&nonBlocking);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(nonBlocking);
+
+  nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+    do_QueryInterface(inputStream);
+  MOZ_DIAGNOSTIC_ASSERT(!asyncInputStream);
+
+  RefPtr<NonBlockingAsyncInputStream> stream =
+    new NonBlockingAsyncInputStream(inputStream.forget());
+
+  stream.forget(aResult);
+  return NS_OK;
+}
+
+NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(already_AddRefed<nsIInputStream> aInputStream)
+  : mInputStream(Move(aInputStream))
+  , mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
+  , mLock("NonBlockingAsyncInputStream::mLock")
+  , mClosed(false)
+{
+  MOZ_ASSERT(mInputStream);
+
+  nsCOMPtr<nsICloneableInputStream> cloneableStream =
+    do_QueryInterface(mInputStream);
+  if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
+    mWeakCloneableInputStream = cloneableStream;
+  }
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+    do_QueryInterface(mInputStream);
+  if (serializableStream &&
+      SameCOMIdentity(mInputStream, serializableStream)) {
+    mWeakIPCSerializableInputStream = serializableStream;
+  }
+
+  nsCOMPtr<nsISeekableStream> seekableStream =
+    do_QueryInterface(mInputStream);
+  if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
+    mWeakSeekableInputStream = seekableStream;
+  }
+}
+
+NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream()
+{}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Close()
+{
+  RefPtr<AsyncWaitRunnable> waitClosureOnlyRunnable;
+  nsCOMPtr<nsIEventTarget> waitClosureOnlyEventTarget;
+
+  {
+    MutexAutoLock lock(mLock);
+
+    if (mClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    mClosed = true;
+
+    NS_ENSURE_STATE(mInputStream);
+    nsresult rv = mInputStream->Close();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mWaitClosureOnly.reset();
+      return rv;
+    }
+
+    // If we have a WaitClosureOnly runnable, it's time to use it.
+    if (mWaitClosureOnly.isSome()) {
+      waitClosureOnlyRunnable = Move(mWaitClosureOnly->mRunnable);
+      waitClosureOnlyEventTarget = Move(mWaitClosureOnly->mEventTarget);
+
+      mWaitClosureOnly.reset();
+
+      // Now we want to dispatch the asyncWaitCallback.
+      mAsyncWaitCallback = waitClosureOnlyRunnable;
+    }
+  }
+
+  if (waitClosureOnlyRunnable) {
+    if (waitClosureOnlyEventTarget) {
+      waitClosureOnlyEventTarget->Dispatch(waitClosureOnlyRunnable,
+                                           NS_DISPATCH_NORMAL);
+    } else {
+      waitClosureOnlyRunnable->Run();
+    }
+  }
+
+  return NS_OK;
+}
+
+// nsIInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Available(uint64_t* aLength)
+{
+  return mInputStream->Available(aLength);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Read(char* aBuffer, uint32_t aCount,
+                                  uint32_t* aReadCount)
+{
+  return mInputStream->Read(aBuffer, aCount, aReadCount);
+}
+
+namespace {
+
+class MOZ_RAII ReadSegmentsData
+{
+public:
+  ReadSegmentsData(NonBlockingAsyncInputStream* aStream,
+                   nsWriteSegmentFun aFunc,
+                   void* aClosure)
+    : mStream(aStream)
+    , mFunc(aFunc)
+    , mClosure(aClosure)
+  {}
+
+  NonBlockingAsyncInputStream* mStream;
+  nsWriteSegmentFun mFunc;
+  void* mClosure;
+};
+
+nsresult
+ReadSegmentsWriter(nsIInputStream* aInStream,
+                   void* aClosure,
+                   const char* aFromSegment,
+                   uint32_t aToOffset,
+                   uint32_t aCount,
+                   uint32_t* aWriteCount)
+{
+  ReadSegmentsData* data = static_cast<ReadSegmentsData*>(aClosure);
+  return data->mFunc(data->mStream, data->mClosure, aFromSegment, aToOffset,
+                     aCount, aWriteCount);
+}
+
+} // anonymous
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+                                          void* aClosure, uint32_t aCount,
+                                          uint32_t* aResult)
+{
+  ReadSegmentsData data(this, aWriter, aClosure);
+  return mInputStream->ReadSegments(ReadSegmentsWriter, &data, aCount, aResult);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+  *aNonBlocking = true;
+  return NS_OK;
+}
+
+// nsICloneableInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::GetCloneable(bool* aCloneable)
+{
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+  return mWeakCloneableInputStream->GetCloneable(aCloneable);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Clone(nsIInputStream** aResult)
+{
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+
+  nsCOMPtr<nsIInputStream> clonedStream;
+  nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIAsyncInputStream> asyncStream;
+  rv = Create(clonedStream.forget(), getter_AddRefs(asyncStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  asyncStream.forget(aResult);
+  return NS_OK;
+}
+
+// nsIAsyncInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::CloseWithStatus(nsresult aStatus)
+{
+  return Close();
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+                                       uint32_t aFlags,
+                                       uint32_t aRequestedCount,
+                                       nsIEventTarget* aEventTarget)
+{
+  RefPtr<AsyncWaitRunnable> runnable;
+  {
+    MutexAutoLock lock(mLock);
+
+    if (aCallback && (mWaitClosureOnly.isSome() || mAsyncWaitCallback)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (!aCallback) {
+      // Canceling previous callbacks.
+      mWaitClosureOnly.reset();
+      mAsyncWaitCallback = nullptr;
+      return NS_OK;
+    }
+
+    // Maybe the stream is already closed.
+    if (!mClosed) {
+      uint64_t length;
+      nsresult rv = mInputStream->Available(&length);
+      if (NS_SUCCEEDED(rv) && length == 0) {
+        mInputStream->Close();
+        mClosed = true;
+      }
+    }
+
+    runnable = new AsyncWaitRunnable(this, aCallback);
+    if ((aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) && !mClosed) {
+      mWaitClosureOnly.emplace(runnable, aEventTarget);
+      return NS_OK;
+    }
+
+    mAsyncWaitCallback = runnable;
+  }
+
+  MOZ_ASSERT(runnable);
+
+  if (aEventTarget) {
+    return aEventTarget->Dispatch(runnable.forget());
+  }
+
+  return runnable->Run();
+}
+
+// nsIIPCSerializableInputStream
+
+void
+NonBlockingAsyncInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
+                                       FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(mWeakIPCSerializableInputStream);
+  InputStreamHelper::SerializeInputStream(mInputStream, aParams,
+                                          aFileDescriptors);
+}
+
+bool
+NonBlockingAsyncInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+                                         const FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_CRASH("NonBlockingAsyncInputStream cannot be deserialized!");
+  return true;
+}
+
+Maybe<uint64_t>
+NonBlockingAsyncInputStream::ExpectedSerializedLength()
+{
+  NS_ENSURE_TRUE(mWeakIPCSerializableInputStream, Nothing());
+  return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
+}
+
+// nsISeekableStream
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  return mWeakSeekableInputStream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Tell(int64_t* aResult)
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  return mWeakSeekableInputStream->Tell(aResult);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::SetEOF()
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+NonBlockingAsyncInputStream::RunAsyncWaitCallback(NonBlockingAsyncInputStream::AsyncWaitRunnable* aRunnable,
+                                                  already_AddRefed<nsIInputStreamCallback> aCallback)
+{
+  nsCOMPtr<nsIInputStreamCallback> callback = Move(aCallback);
+
+  {
+    MutexAutoLock lock(mLock);
+    if (mAsyncWaitCallback != aRunnable) {
+      // The callback has been canceled in the meantime.
+      return;
+    }
+
+    mAsyncWaitCallback = nullptr;
+  }
+
+  callback->OnInputStreamReady(this);
+}
+
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/xpcom/io/NonBlockingAsyncInputStream.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NonBlockingAsyncInputStream_h
+#define NonBlockingAsyncInputStream_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsICloneableInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
+
+// This class aims to wrap a non-blocking and non-async inputStream and expose
+// it as nsIAsyncInputStream.
+// Probably you don't want to use this class directly. Instead use
+// NS_MakeAsyncNonBlockingInputStream() as it will handle different stream
+// variants without requiring you to special-case them yourself.
+
+namespace mozilla {
+
+class NonBlockingAsyncInputStream final : public nsIAsyncInputStream
+                                        , public nsICloneableInputStream
+                                        , public nsIIPCSerializableInputStream
+                                        , public nsISeekableStream
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
+  NS_DECL_NSICLONEABLEINPUTSTREAM
+  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+  NS_DECL_NSISEEKABLESTREAM
+
+  // |aInputStream| must be a non-blocking, non-async inputSteam.
+  static nsresult
+  Create(already_AddRefed<nsIInputStream> aInputStream,
+         nsIAsyncInputStream** aAsyncInputStream);
+
+private:
+  explicit NonBlockingAsyncInputStream(already_AddRefed<nsIInputStream> aInputStream);
+  ~NonBlockingAsyncInputStream();
+
+  class AsyncWaitRunnable;
+
+  void
+  RunAsyncWaitCallback(AsyncWaitRunnable* aRunnable,
+                       already_AddRefed<nsIInputStreamCallback> aCallback);
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+
+  // Raw pointers because these are just QI of mInputStream.
+  nsICloneableInputStream* MOZ_NON_OWNING_REF mWeakCloneableInputStream;
+  nsIIPCSerializableInputStream* MOZ_NON_OWNING_REF mWeakIPCSerializableInputStream;
+  nsISeekableStream* MOZ_NON_OWNING_REF mWeakSeekableInputStream;
+
+  Mutex mLock;
+
+  struct WaitClosureOnly
+  {
+    WaitClosureOnly(AsyncWaitRunnable* aRunnable, nsIEventTarget* aEventTarget);
+
+    RefPtr<AsyncWaitRunnable> mRunnable;
+    nsCOMPtr<nsIEventTarget> mEventTarget;
+  };
+
+  // This is set when AsyncWait is called with a callback and with
+  // WAIT_CLOSURE_ONLY as flag.
+  // This is protected by mLock.
+  Maybe<WaitClosureOnly> mWaitClosureOnly;
+
+  // This is protected by mLock.
+  RefPtr<AsyncWaitRunnable> mAsyncWaitCallback;
+
+  // This is protected by mLock.
+  bool mClosed;
+};
+
+} // mozilla namespace
+
+#endif // NonBlockingAsyncInputStream_h
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -78,26 +78,28 @@ EXPORTS += [
     'nsStringStream.h',
     'nsUnicharInputStream.h',
     'nsWildCard.h',
     'SpecialSystemDirectory.h',
 ]
 
 EXPORTS.mozilla += [
     'Base64.h',
+    'NonBlockingAsyncInputStream.h',
     'SlicedInputStream.h',
     'SnappyCompressOutputStream.h',
     'SnappyFrameUtils.h',
     'SnappyUncompressInputStream.h',
 ]
 
 UNIFIED_SOURCES += [
     'Base64.cpp',
     'crc32c.c',
     'FileDescriptorFile.cpp',
+    'NonBlockingAsyncInputStream.cpp',
     'nsAnonymousTemporaryFile.cpp',
     'nsAppFileLocationProvider.cpp',
     'nsBinaryStream.cpp',
     'nsDirectoryService.cpp',
     'nsEscape.cpp',
     'nsInputStreamTee.cpp',
     'nsIOUtil.cpp',
     'nsLinebreakConverter.cpp',
--- a/xpcom/io/nsStreamUtils.cpp
+++ b/xpcom/io/nsStreamUtils.cpp
@@ -16,19 +16,24 @@
 #include "nsISafeOutputStream.h"
 #include "nsString.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIBufferedStreams.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
+#include "nsITransport.h"
+#include "nsIStreamTransportService.h"
+#include "NonBlockingAsyncInputStream.h"
 
 using namespace mozilla;
 
+static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
+
 //-----------------------------------------------------------------------------
 
 // This is a nsICancelableRunnable because we can dispatch it to Workers and
 // those can be shut down at any time, and in these cases, Cancel() is called
 // instead of Run().
 class nsInputStreamReadyEvent final
   : public CancelableRunnable
   , public nsIInputStreamCallback
@@ -957,8 +962,67 @@ NS_CloneInputStream(nsIInputStream* aSou
   rv = NS_AsyncCopy(aSource, writer, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   readerClone.forget(aCloneOut);
   reader.forget(aReplacementOut);
 
   return NS_OK;
 }
+
+nsresult
+NS_MakeAsyncNonBlockingInputStream(already_AddRefed<nsIInputStream> aSource,
+                                   nsIAsyncInputStream** aAsyncInputStream)
+{
+  nsCOMPtr<nsIInputStream> source = Move(aSource);
+  if (NS_WARN_IF(!aAsyncInputStream)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool nonBlocking = false;
+  nsresult rv = source->IsNonBlocking(&nonBlocking);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(source);
+
+  if (nonBlocking && asyncStream) {
+    // This stream is perfect!
+    asyncStream.forget(aAsyncInputStream);
+    return NS_OK;
+  }
+
+  if (nonBlocking) {
+    // If the stream is non-blocking but not async, we wrap it.
+    return NonBlockingAsyncInputStream::Create(source.forget(),
+                                               aAsyncInputStream);
+  }
+
+  nsCOMPtr<nsIStreamTransportService> sts =
+    do_GetService(kStreamTransportServiceCID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsITransport> transport;
+  rv = sts->CreateInputTransport(source,
+                                 /* aCloseWhenDone */ true,
+                                 getter_AddRefs(transport));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIInputStream> wrapper;
+  rv = transport->OpenInputStream(/* aFlags */ 0,
+                                  /* aSegmentSize */ 0,
+                                  /* aSegmentCount */ 0,
+                                  getter_AddRefs(wrapper));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  asyncStream = do_QueryInterface(wrapper);
+  MOZ_ASSERT(asyncStream);
+
+  asyncStream.forget(aAsyncInputStream);
+  return NS_OK;
+}
--- a/xpcom/io/nsStreamUtils.h
+++ b/xpcom/io/nsStreamUtils.h
@@ -7,16 +7,17 @@
 #ifndef nsStreamUtils_h__
 #define nsStreamUtils_h__
 
 #include "nsCOMPtr.h"
 #include "nsStringFwd.h"
 #include "nsIInputStream.h"
 #include "nsTArray.h"
 
+class nsIAsyncInputStream;
 class nsIOutputStream;
 class nsIInputStreamCallback;
 class nsIOutputStreamCallback;
 class nsIEventTarget;
 
 /**
  * A "one-shot" proxy of the OnInputStreamReady callback.  The resulting
  * proxy object's OnInputStreamReady function may only be called once!  The
@@ -288,9 +289,30 @@ NS_InputStreamIsCloneable(nsIInputStream
  *                        supported and a non-cloneable source will result
  *                        in failure.  Replacement streams are non-blocking.
  * @return NS_OK on successful clone.  Error otherwise.
  */
 extern nsresult
 NS_CloneInputStream(nsIInputStream* aSource, nsIInputStream** aCloneOut,
                     nsIInputStream** aReplacementOut = nullptr);
 
+/*
+ * This function returns a non-blocking nsIAsyncInputStream. Internally,
+ * different approaches are used based on what |aSource| is and what it
+ * implements.
+ *
+ * Note that this component takes the owninship of aSource.
+ *
+ * If the |aSource| is already a non-blocking and async stream,
+ * |aAsyncInputStream| will be equal to |aSource|.
+ *
+ * Otherwise, if |aSource| is just non-blocking, NonBlockingAsyncInputStream
+ * class is used in order to make it async.
+ *
+ * The last step is to use nsIStreamTransportService and create a pipe in order
+ * to expose a non-blocking async inputStream and read |aSource| data from
+ * a separate thread.
+ */
+extern nsresult
+NS_MakeAsyncNonBlockingInputStream(already_AddRefed<nsIInputStream> aSource,
+                                   nsIAsyncInputStream** aAsyncInputStream);
+
 #endif // !nsStreamUtils_h__
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/gtest/TestNonBlockingAsyncInputStream.cpp
@@ -0,0 +1,367 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/NonBlockingAsyncInputStream.h"
+#include "nsIAsyncInputStream.h"
+#include "nsStreamUtils.h"
+#include "nsString.h"
+#include "nsStringStream.h"
+#include "Helpers.h"
+
+TEST(TestNonBlockingAsyncInputStream, Simple) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  // It should not be async.
+  bool nonBlocking = false;
+  nsCOMPtr<nsIAsyncInputStream> async;
+
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    async = do_QueryInterface(stream);
+    ASSERT_EQ(nullptr, async);
+
+    // It must be non-blocking
+    ASSERT_EQ(NS_OK, stream->IsNonBlocking(&nonBlocking));
+    ASSERT_TRUE(nonBlocking);
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+  ASSERT_TRUE(!!async);
+
+  // Still non-blocking
+  ASSERT_EQ(NS_OK, async->IsNonBlocking(&nonBlocking));
+  ASSERT_TRUE(nonBlocking);
+
+  // Testing ::Available()
+  uint64_t length;
+  ASSERT_EQ(NS_OK, async->Available(&length));
+  ASSERT_EQ(data.Length(), length);
+
+  // Read works fine.
+  char buffer[1024];
+  uint32_t read = 0;
+  ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read));
+  ASSERT_EQ(data.Length(), read);
+  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
+}
+
+class ReadSegmentsData
+{
+public:
+  ReadSegmentsData(nsIInputStream* aStream, char* aBuffer)
+    : mStream(aStream)
+    , mBuffer(aBuffer)
+  {}
+
+  nsIInputStream* mStream;
+  char* mBuffer;
+};
+
+nsresult
+ReadSegmentsFunction(nsIInputStream* aInStr,
+                     void* aClosure,
+                     const char* aBuffer,
+                     uint32_t aOffset,
+                     uint32_t aCount,
+                     uint32_t* aCountWritten)
+{
+  ReadSegmentsData* data = static_cast<ReadSegmentsData*>(aClosure);
+  if (aInStr != data->mStream) return NS_ERROR_FAILURE;
+  memcpy(&data->mBuffer[aOffset], aBuffer, aCount);
+  *aCountWritten = aCount;
+  return NS_OK;
+}
+
+TEST(TestNonBlockingAsyncInputStream, ReadSegments) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+
+  // Read works fine.
+  char buffer[1024];
+  uint32_t read = 0;
+  ReadSegmentsData closure(async, buffer);
+  ASSERT_EQ(NS_OK, async->ReadSegments(ReadSegmentsFunction, &closure,
+                                       sizeof(buffer), &read));
+  ASSERT_EQ(data.Length(), read);
+  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
+}
+
+TEST(TestNonBlockingAsyncInputStream, AsyncWait_Simple) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+  ASSERT_TRUE(!!async);
+
+  // Testing ::Available()
+  uint64_t length;
+  ASSERT_EQ(NS_OK, async->Available(&length));
+  ASSERT_EQ(data.Length(), length);
+
+  // Testing ::AsyncWait - without EventTarget
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+
+  ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, nullptr));
+  ASSERT_TRUE(cb->Called());
+
+  // Testing ::AsyncWait - with EventTarget
+  cb = new testing::InputStreamCallback();
+  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+  ASSERT_EQ(NS_OK, async->AsyncWait(cb, 0, 0, thread));
+  ASSERT_FALSE(cb->Called());
+  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+
+  // Read works fine.
+  char buffer[1024];
+  uint32_t read = 0;
+  ASSERT_EQ(NS_OK, async->Read(buffer, sizeof(buffer), &read));
+  ASSERT_EQ(data.Length(), read);
+  ASSERT_TRUE(data.Equals(nsCString(buffer, read)));
+}
+
+TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withoutEventTarget) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+  ASSERT_TRUE(!!async);
+
+  // Testing ::AsyncWait - no eventTarget
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+
+  ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, nullptr));
+
+  ASSERT_FALSE(cb->Called());
+  ASSERT_EQ(NS_OK, async->Close());
+  ASSERT_TRUE(cb->Called());
+}
+
+TEST(TestNonBlockingAsyncInputStream, AsyncWait_ClosureOnly_withEventTarget) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+  ASSERT_TRUE(!!async);
+
+  // Testing ::AsyncWait - with EventTarget
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+  ASSERT_EQ(NS_OK, async->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, thread));
+
+  ASSERT_FALSE(cb->Called());
+  ASSERT_EQ(NS_OK, async->Close());
+  ASSERT_FALSE(cb->Called());
+
+  MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+}
+
+TEST(TestNonBlockingAsyncInputStream, Helper) {
+  nsCString data;
+  data.Assign("Hello world!");
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    // Let's create a test string inputStream
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+
+    // Here the non-blocking stream.
+    ASSERT_EQ(NS_OK,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+  ASSERT_TRUE(!!async);
+
+  // This should return the same object because async is already non-blocking
+  // and async.
+  nsCOMPtr<nsIAsyncInputStream> result;
+  nsCOMPtr<nsIAsyncInputStream> asyncTmp = async;
+  ASSERT_EQ(NS_OK,
+            NS_MakeAsyncNonBlockingInputStream(asyncTmp.forget(),
+                                               getter_AddRefs(result)));
+  ASSERT_EQ(async, result);
+
+  // This will use NonBlockingAsyncInputStream wrapper.
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    ASSERT_EQ(NS_OK, NS_NewCStringInputStream(getter_AddRefs(stream), data));
+    ASSERT_EQ(NS_OK,
+              NS_MakeAsyncNonBlockingInputStream(stream.forget(),
+                                                 getter_AddRefs(result)));
+  }
+  ASSERT_TRUE(async != result);
+  ASSERT_TRUE(async);
+}
+
+class QIInputStream final : public nsIInputStream
+                          , public nsICloneableInputStream
+                          , public nsIIPCSerializableInputStream
+                          , public nsISeekableStream
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  QIInputStream(bool aNonBlockingError, bool aCloneable,
+                bool aIPCSerializable, bool aSeekable)
+    : mNonBlockingError(aNonBlockingError)
+    , mCloneable(aCloneable)
+    , mIPCSerializable(aIPCSerializable)
+    , mSeekable(aSeekable)
+  {}
+
+  // nsIInputStream
+  NS_IMETHOD Close() override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD Available(uint64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD Read(char*, uint32_t, uint32_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD ReadSegments(nsWriteSegmentFun, void*, uint32_t, uint32_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD IsNonBlocking(bool* aNonBlocking) override
+  {
+    *aNonBlocking = true;
+    return mNonBlockingError ? NS_ERROR_FAILURE : NS_OK;
+  }
+
+  // nsICloneableInputStream
+  NS_IMETHOD GetCloneable(bool*) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD Clone(nsIInputStream**) override { return NS_ERROR_NOT_IMPLEMENTED; }
+
+  // nsIIPCSerializableInputStream
+  void Serialize(mozilla::ipc::InputStreamParams&, FileDescriptorArray&) override {}
+  bool Deserialize(const mozilla::ipc::InputStreamParams&,
+                   const FileDescriptorArray&) override { return false; }
+  mozilla::Maybe<uint64_t> ExpectedSerializedLength() override { return mozilla::Nothing(); }
+
+  // nsISeekableStream
+  NS_IMETHOD Seek(int32_t, int64_t) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD Tell(int64_t*) override { return NS_ERROR_NOT_IMPLEMENTED; }
+  NS_IMETHOD SetEOF() override { return NS_ERROR_NOT_IMPLEMENTED; }
+
+private:
+  ~QIInputStream() = default;
+
+  bool mNonBlockingError;
+  bool mCloneable;
+  bool mIPCSerializable;
+  bool mSeekable;
+};
+
+NS_IMPL_ADDREF(QIInputStream);
+NS_IMPL_RELEASE(QIInputStream);
+
+NS_INTERFACE_MAP_BEGIN(QIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, mCloneable)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, mIPCSerializable)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mSeekable)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+TEST(TestNonBlockingAsyncInputStream, QI) {
+  // Let's test ::Create() returning error.
+
+  nsCOMPtr<nsIAsyncInputStream> async;
+  {
+    nsCOMPtr<nsIInputStream> stream = new QIInputStream(true, true, true, true);
+
+    ASSERT_EQ(NS_ERROR_FAILURE,
+              NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                  getter_AddRefs(async)));
+  }
+
+  // Let's test the QIs
+  for (int i = 0; i < 8; ++i) {
+    bool shouldBeCloneable = !!(i & 0x01);
+    bool shouldBeSerializable = !!(i & 0x02);
+    bool shouldBeSeekable = !!(i & 0x04);
+
+    nsCOMPtr<nsICloneableInputStream> cloneable;
+    nsCOMPtr<nsIIPCSerializableInputStream> ipcSerializable;
+    nsCOMPtr<nsISeekableStream> seekable;
+
+    {
+      nsCOMPtr<nsIInputStream> stream =
+        new QIInputStream(false, shouldBeCloneable, shouldBeSerializable, shouldBeSeekable);
+
+      cloneable = do_QueryInterface(stream);
+      ASSERT_EQ(shouldBeCloneable, !!cloneable);
+
+      ipcSerializable = do_QueryInterface(stream);
+      ASSERT_EQ(shouldBeSerializable, !!ipcSerializable);
+
+      seekable = do_QueryInterface(stream);
+      ASSERT_EQ(shouldBeSeekable, !!seekable);
+
+      ASSERT_EQ(NS_OK, NonBlockingAsyncInputStream::Create(stream.forget(),
+                                                           getter_AddRefs(async)));
+    }
+
+    // The returned async stream should be cloneable only if the underlying
+    // stream is.
+    cloneable = do_QueryInterface(async);
+    ASSERT_EQ(shouldBeCloneable, !!cloneable);
+
+    // The returned async stream should be serializable only if the underlying
+    // stream is.
+    ipcSerializable = do_QueryInterface(async);
+    ASSERT_EQ(shouldBeSerializable, !!ipcSerializable);
+
+    // The returned async stream should be seekable only if the underlying
+    // stream is.
+    seekable = do_QueryInterface(async);
+    ASSERT_EQ(shouldBeSeekable, !!seekable);
+  }
+}
--- a/xpcom/tests/gtest/TestThreadManager.cpp
+++ b/xpcom/tests/gtest/TestThreadManager.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIThreadManager.h"
 #include "nsCOMPtr.h"
 #include "nsXPCOM.h"
 #include "nsThreadUtils.h"
+#include "nsServiceManagerUtils.h"
 #include "mozilla/Atomics.h"
 #include "gtest/gtest.h"
 
 using mozilla::Atomic;
 using mozilla::Runnable;
 
 class WaitCondition final : public nsINestedEventLoopCondition
 {
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -23,16 +23,17 @@ UNIFIED_SOURCES += [
     'TestEventTargetQI.cpp',
     'TestExpirationTracker.cpp',
     'TestFile.cpp',
     'TestGCPostBarriers.cpp',
     'TestID.cpp',
     'TestMoveString.cpp',
     'TestMozPromise.cpp',
     'TestMultiplexInputStream.cpp',
+    'TestNonBlockingAsyncInputStream.cpp',
     'TestNsDeque.cpp',
     'TestNSPRLogModulesParser.cpp',
     'TestObserverArray.cpp',
     'TestObserverService.cpp',
     'TestPipes.cpp',
     'TestPLDHash.cpp',
     'TestPriorityQueue.cpp',
     'TestRacingServiceManager.cpp',
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -461,42 +461,36 @@ NS_IMETHODIMP
 nsChromeTreeOwner::OnStateChange(nsIWebProgress* aWebProgress,
                                  nsIRequest* aRequest,
                                  uint32_t aProgressStateFlags,
                                  nsresult aStatus)
 {
    return NS_OK;
 }
 
-NS_IMETHODIMP nsChromeTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
-                                                  nsIRequest* aRequest,
-                                                  nsIURI* aLocation,
-                                                  uint32_t aFlags)
+NS_IMETHODIMP
+nsChromeTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress,
+                                    nsIRequest* aRequest,
+                                    nsIURI* aLocation,
+                                    uint32_t aFlags)
 {
-  bool itsForYou = true;
+  NS_ENSURE_STATE(mXULWindow);
 
+  // If loading a new root .xul document, then redo chrome.
   if (aWebProgress) {
-    NS_ENSURE_STATE(mXULWindow);
-    nsCOMPtr<mozIDOMWindowProxy> progressWin;
-    aWebProgress->GetDOMWindow(getter_AddRefs(progressWin));
-
     nsCOMPtr<nsIDocShell> docshell;
     mXULWindow->GetDocShell(getter_AddRefs(docshell));
-    // XXXkhuey this is totally wrong, bug 1223303.
-    nsCOMPtr<mozIDOMWindowProxy> ourWin(do_QueryInterface(docshell));
 
-    if (ourWin != progressWin)
-      itsForYou = false;
+    nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(docshell));
+    if (webProgress != aWebProgress) {
+      return NS_OK;
+    }
   }
 
-   // If loading a new root .xul document, then redo chrome.
-  if (itsForYou) {
-    NS_ENSURE_STATE(mXULWindow);
-    mXULWindow->mChromeLoaded = false;
-  }
+  mXULWindow->mChromeLoaded = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsChromeTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress,
                                   nsIRequest* aRequest,
                                   nsresult aStatus,
                                   const char16_t* aMessage)
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -1362,16 +1362,21 @@ bool nsXULWindow::LoadMiscPersistentAttr
   }
 
   if (sizeMode == nsSizeMode_Fullscreen) {
     nsCOMPtr<mozIDOMWindowProxy> ourWindow;
     GetWindowDOMWindow(getter_AddRefs(ourWindow));
     auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
     piWindow->SetFullScreen(true);
   } else {
+    // For maximized windows, ignore the XUL size attributes, as setting the
+    // size would set the window back to the normal sizemode.
+    if (sizeMode == nsSizeMode_Maximized) {
+      mIgnoreXULSize = true;
+    }
     mWindow->SetSizeMode(sizeMode);
   }
   gotState = true;
 
   // zlevel
   windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString);
   if (!stateString.IsEmpty()) {
     nsresult errorCode;