Merge mozilla-central to autoland. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Tue, 25 Jun 2019 12:42:21 +0300
changeset 542849 6972687fbe5a50899bc3d90439a6fe71d9bd3bc8
parent 542848 feb980ef7f050e76b921be9016d8390d56f35767 (current diff)
parent 542835 207bcf72dac70e275daee08aebfbb5df0900c9d0 (diff)
child 542850 89664bfdb628a012b717ecee5427495f3fea528c
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
js/xpconnect/tests/unit/subScriptWithEarlyError.js
js/xpconnect/tests/unit/test_asyncLoadSubScriptError.js
js/xpconnect/tests/unit/test_bug1150106.js
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -131,17 +131,18 @@ function Inspector(toolbox) {
   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
   this.onShowBoxModelHighlighterForNode =
     this.onShowBoxModelHighlighterForNode.bind(this);
   this.onSidebarHidden = this.onSidebarHidden.bind(this);
   this.onSidebarResized = this.onSidebarResized.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
   this.onSidebarShown = this.onSidebarShown.bind(this);
   this.onSidebarToggle = this.onSidebarToggle.bind(this);
-  this.handleThreadState = this.handleThreadState.bind(this);
+  this.handleThreadPaused = this.handleThreadPaused.bind(this);
+  this.handleThreadResumed = this.handleThreadResumed.bind(this);
 
   this._target.on("will-navigate", this._onBeforeNavigate);
 }
 
 Inspector.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
@@ -152,18 +153,18 @@ Inspector.prototype = {
     // When replaying, we need to listen to changes in the target's pause state.
     if (this._target.isReplayEnabled()) {
       let dbg = this._toolbox.getPanel("jsdebugger");
       if (!dbg) {
         dbg = await this._toolbox.loadTool("jsdebugger");
       }
       this._replayResumed = !dbg.isPaused();
 
-      this._target.threadClient.on("paused", this.handleThreadState);
-      this._target.threadClient.on("resumed", this.handleThreadState);
+      this._target.threadClient.on("paused", this.handleThreadPaused);
+      this._target.threadClient.on("resumed", this.handleThreadResumed);
     }
 
     await Promise.all([
       this._getCssProperties(),
       this._getPageStyle(),
       this._getDefaultSelection(),
       this._getAccessibilityFront(),
       this._getChangesFront(),
@@ -1133,20 +1134,28 @@ Inspector.prototype = {
       this.setupToolbar();
     };
     this._pendingSelection = onNodeSelected;
     this._getDefaultNodeForSelection()
         .then(onNodeSelected, this._handleRejectionIfNotDestroyed);
   },
 
   /**
-   * When replaying, reset the inspector whenever the target paused or unpauses.
+   * When replaying, reset the inspector whenever the target pauses.
    */
-  handleThreadState(packet) {
-    this._replayResumed = packet.type != "paused";
+  handleThreadPaused() {
+    this._replayResumed = false;
+    this.onNewRoot();
+  },
+
+  /**
+   * When replaying, reset the inspector whenever the target resumes.
+   */
+  handleThreadResumed() {
+    this._replayResumed = true;
     this.onNewRoot();
   },
 
   /**
    * Handler for "markuploaded" event fired on a new root mutation and after the markup
    * view is initialized. Expands the current selected node and restores the saved
    * highlighter state.
    */
@@ -1374,18 +1383,18 @@ Inspector.prototype = {
   /**
    * Destroy the inspector.
    */
   destroy: function() {
     if (this._panelDestroyer) {
       return this._panelDestroyer;
     }
 
-    this._target.threadClient.off("paused", this.handleThreadState);
-    this._target.threadClient.off("resumed", this.handleThreadState);
+    this._target.threadClient.off("paused", this.handleThreadPaused);
+    this._target.threadClient.off("resumed", this.handleThreadResumed);
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
 
     this.cancelUpdate();
 
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
@@ -11,16 +11,18 @@
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_rr_basic.html",
     { waitForRecording: true }
   );
 
   const {threadClient, tab, toolbox, target} = dbg;
 
+  await threadClient.interrupt();
+
   // Rewind to the beginning of the recording.
   await rewindToLine(threadClient, undefined);
 
   const bp = await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
   await resumeToLine(threadClient, 21);
   await checkEvaluateInTopFrame(target, "number", 1);
   await resumeToLine(threadClient, 21);
   await checkEvaluateInTopFrame(target, "number", 2);
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-01.js
+++ b/devtools/client/webreplay/mochitest/browser_rr_inspector-01.js
@@ -18,16 +18,18 @@ function getContainerForNodeFront(nodeFr
 // Test basic inspector functionality in web replay: the inspector is able to
 // show contents when paused according to the child's current position.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_inspector_basic.html",
     { waitForRecording: true }
   );
   const {threadClient, tab, toolbox} = dbg;
+
+  await threadClient.interrupt();
   await threadClient.resume();
 
   const {inspector} = await openInspector();
 
   let nodeFront = await getNodeFront("#maindiv", inspector);
   let container = getContainerForNodeFront(nodeFront, inspector);
   ok(!container, "No node container while unpaused");
 
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-02.js
+++ b/devtools/client/webreplay/mochitest/browser_rr_inspector-02.js
@@ -12,16 +12,18 @@ Services.scriptloader.loadSubScript(
 
 // Test that the element highlighter works when paused and replaying.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_inspector_basic.html",
     { waitForRecording: true }
   );
   const {threadClient, tab, toolbox} = dbg;
+
+  await threadClient.interrupt();
   await threadClient.resume();
 
   await threadClient.interrupt();
   const bp = await setBreakpoint(threadClient, "doc_inspector_basic.html", 9);
   await rewindToLine(threadClient, 9);
 
   const {inspector, testActor} = await openInspector();
 
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-03.js
+++ b/devtools/client/webreplay/mochitest/browser_rr_inspector-03.js
@@ -28,16 +28,18 @@ function getComputedViewProperty(view, n
 
 // Test that styles for elements can be viewed when using web replay.
 add_task(async function() {
   const dbg = await attachRecordingDebugger(
     "doc_inspector_styles.html",
     { waitForRecording: true }
   );
   const {threadClient, tab, toolbox} = dbg;
+
+  await threadClient.interrupt();
   await threadClient.resume();
 
   await threadClient.interrupt();
 
   const {inspector, view} = await openComputedView();
   await checkBackgroundColor("body", "rgb(0, 128, 0)");
   await checkBackgroundColor("#maindiv", "rgb(0, 0, 255)");
 
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -1089,29 +1089,17 @@ const ThreadActor = ActorClassWithSpec(t
 
     try {
       if (resumeLimit) {
         await this._handleResumeLimit({resumeLimit, rewind});
       } else {
         this._clearSteppingHooks();
       }
 
-      // When replaying execution in a separate process we need to explicitly
-      // notify that process when to resume execution.
-      if (this.dbg.replaying) {
-        if (resumeLimit && resumeLimit.type == "warp") {
-          this.dbg.replayTimeWarp(resumeLimit.target);
-        } else if (rewind) {
-          this.dbg.replayResumeBackward();
-        } else {
-          this.dbg.replayResumeForward();
-        }
-      }
-
-      this.doResume();
+      this.doResume({ resumeLimit, rewind });
       return {};
     } catch (error) {
       return error instanceof Error
         ? {
             error: "unknownError",
             message: DevToolsUtils.safeErrorString(error) }
         // It is a known error, and the promise was rejected with an error
         // packet.
@@ -1119,17 +1107,29 @@ const ThreadActor = ActorClassWithSpec(t
     }
   },
 
   /**
    * Only resume and notify necessary observers. This should be used in cases
    * when we do not want to notify the front end of a resume, for example when
    * we are shutting down.
    */
-  doResume() {
+  doResume({ resumeLimit, rewind } = {}) {
+    // When replaying execution in a separate process we need to explicitly
+    // notify that process when to resume execution.
+    if (this.dbg.replaying) {
+      if (resumeLimit && resumeLimit.type == "warp") {
+        this.dbg.replayTimeWarp(resumeLimit.target);
+      } else if (rewind) {
+        this.dbg.replayResumeBackward();
+      } else {
+        this.dbg.replayResumeForward();
+      }
+    }
+
     this.maybePauseOnExceptions();
     this._state = "running";
 
     // Drop the actors in the pause actor pool.
     this.conn.removeActorPool(this._pausePool);
 
     this._pausePool = null;
     this._pauseActor = null;
--- a/devtools/shared/specs/thread.js
+++ b/devtools/shared/specs/thread.js
@@ -19,16 +19,18 @@ const threadSpec = generateActorSpec({
 
   events: {
     paused: {
       actor: Option(0, "nullable:string"),
       frame: Option(0, "nullable:json"),
       why: Option(0, "nullable:json"),
       poppedFrames: Option(0, "nullable:json"),
       error: Option(0, "nullable:json"),
+      recordingEndpoint: Option(0, "nullable:json"),
+      executionPoint: Option(0, "nullable:json"),
     },
     resumed: {},
     detached: {},
     willInterrupt: {},
     newSource: {
       source: Option(0, "json"),
     },
     progress: {
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -174,18 +174,17 @@ already_AddRefed<BrowsingContext> Browsi
 
 BrowsingContext::BrowsingContext(BrowsingContext* aParent,
                                  BrowsingContextGroup* aGroup,
                                  uint64_t aBrowsingContextId, Type aType)
     : mType(aType),
       mBrowsingContextId(aBrowsingContextId),
       mGroup(aGroup),
       mParent(aParent),
-      mIsInProcess(false),
-      mIsDiscarded(false) {
+      mIsInProcess(false) {
   MOZ_RELEASE_ASSERT(!mParent || mParent->Group() == mGroup);
   MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
   MOZ_RELEASE_ASSERT(mGroup);
 }
 
 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
   // XXX(nika): We should communicate that we are now an active BrowsingContext
   // process to the parent & do other validation here.
@@ -206,17 +205,17 @@ void BrowsingContext::SetEmbedderElement
     //
     // XXX: This is a workaround to some parent edges not being immutable in the
     // parent process. It can be fixed once bug 1539979 has been fixed.
     if (mParent && mEmbedderElement && mEmbedderElement != aEmbedder) {
       NS_WARNING("Non root content frameLoader swap! This will crash soon!");
 
       MOZ_DIAGNOSTIC_ASSERT(mType == Type::Chrome, "must be chrome");
       MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "must be in parent");
-      MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this),
+      MOZ_DIAGNOSTIC_ASSERT(!Group()->IsContextCached(this),
                             "cannot be in bfcache");
 
       RefPtr<BrowsingContext> kungFuDeathGrip(this);
       RefPtr<BrowsingContext> newParent;
       container->GetBrowsingContext(getter_AddRefs(newParent));
       mParent->mChildren.RemoveElement(this);
       if (newParent) {
         newParent->mChildren.AppendElement(this);
@@ -244,98 +243,81 @@ void BrowsingContext::SetEmbedderElement
 }
 
 void BrowsingContext::Attach(bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64,
            XRE_IsParentProcess() ? "Parent" : "Child", Id(),
            mParent ? mParent->Id() : 0));
 
-  MOZ_DIAGNOSTIC_ASSERT(mGroup);
-  MOZ_DIAGNOSTIC_ASSERT(!mGroup->IsContextCached(this));
-  MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
+  MOZ_DIAGNOSTIC_ASSERT(!Group()->IsContextCached(this));
 
   auto* children = mParent ? &mParent->mChildren : &mGroup->Toplevels();
   MOZ_DIAGNOSTIC_ASSERT(!children->Contains(this));
 
   children->AppendElement(this);
 
   if (!aFromIPC) {
     // Send attach to our parent if we need to.
     if (XRE_IsContentProcess()) {
       ContentChild::GetSingleton()->SendAttachBrowsingContext(
           GetIPCInitializer());
     } else if (IsContent()) {
       MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
-      mGroup->EachParent([&](ContentParent* aParent) {
+      Group()->EachParent([&](ContentParent* aParent) {
         Unused << aParent->SendAttachBrowsingContext(GetIPCInitializer());
       });
     }
   }
 }
 
 void BrowsingContext::Detach(bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
            XRE_IsParentProcess() ? "Parent" : "Child", Id(),
            mParent ? mParent->Id() : 0));
 
-  // Unlinking might remove our group before Detach gets called.
-  if (NS_WARN_IF(!mGroup)) {
-    return;
-  }
-
   RefPtr<BrowsingContext> kungFuDeathGrip(this);
 
-  if (!mGroup->EvictCachedContext(this)) {
+  if (Group() && !Group()->EvictCachedContext(this)) {
     Children* children = nullptr;
     if (mParent) {
       children = &mParent->mChildren;
-    } else {
+    } else if (mGroup) {
       children = &mGroup->Toplevels();
     }
 
-    children->RemoveElement(this);
+    if (children) {
+      // TODO(farre): This assert looks extremely fishy, I know, but
+      // what we're actually saying is this: if we're detaching, but our
+      // parent doesn't have any children, it is because we're being
+      // detached by the cycle collector destroying docshells out of
+      // order.
+      MOZ_DIAGNOSTIC_ASSERT(children->IsEmpty() || children->Contains(this));
+
+      children->RemoveElement(this);
+    }
   }
 
-  Unregister();
+  if (mGroup) {
+    mGroup->Unregister(this);
+  }
+
+  // As our nsDocShell is going away, this should implicitly mark us as closed.
+  // We directly set our member, rather than using a transaction as we're going
+  // to send a `Detach` message to other processes either way.
+  mClosed = true;
 
   if (!aFromIPC && XRE_IsContentProcess()) {
     auto cc = ContentChild::GetSingleton();
     MOZ_DIAGNOSTIC_ASSERT(cc);
     cc->SendDetachBrowsingContext(this);
   }
 }
 
-void BrowsingContext::DetachChildren(bool aFromIPC) {
-  if (mChildren.IsEmpty()) {
-    return;
-  }
-
-  MOZ_LOG(GetLog(), LogLevel::Debug,
-          ("%s: Detaching all children of 0x%08" PRIx64 "",
-           XRE_IsParentProcess() ? "Parent" : "Child", Id()));
-
-  // TODO(farre). Watch out! Notice the missing call to
-  // BrowsingContextGroup::Unregister here, this is intentional. If we
-  // unregister and set the detached flag here, tests will fail
-  // because not being able to look up Window.top. (Un-)Fortunately
-  // nsDocShell::Destroy will still call BrowsingContext::Detach,
-  // which will set the detached flag at that point. Bug 1558176 would
-  // clean this up.
-
-  mChildren.Clear();
-
-  if (!aFromIPC && XRE_IsContentProcess()) {
-    auto cc = ContentChild::GetSingleton();
-    MOZ_DIAGNOSTIC_ASSERT(cc);
-    cc->SendDetachBrowsingContextChildren(this);
-  }
-}
-
 void BrowsingContext::PrepareForProcessChange() {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Preparing 0x%08" PRIx64 " for a process change",
            XRE_IsParentProcess() ? "Parent" : "Child", Id()));
 
   MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
   MOZ_ASSERT(!mClosed, "We're already closed?");
 
@@ -351,46 +333,46 @@ void BrowsingContext::PrepareForProcessC
   mDocShell = nullptr;
 }
 
 void BrowsingContext::CacheChildren(bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Caching children of 0x%08" PRIx64 "",
            XRE_IsParentProcess() ? "Parent" : "Child", Id()));
 
-  mGroup->CacheContexts(mChildren);
+  Group()->CacheContexts(mChildren);
   mChildren.Clear();
 
   if (!aFromIPC && XRE_IsContentProcess()) {
     auto cc = ContentChild::GetSingleton();
     MOZ_DIAGNOSTIC_ASSERT(cc);
     cc->SendCacheBrowsingContextChildren(this);
   }
 }
 
 void BrowsingContext::RestoreChildren(Children&& aChildren, bool aFromIPC) {
   MOZ_LOG(GetLog(), LogLevel::Debug,
           ("%s: Restoring children of 0x%08" PRIx64 "",
            XRE_IsParentProcess() ? "Parent" : "Child", Id()));
 
   for (BrowsingContext* child : aChildren) {
     MOZ_DIAGNOSTIC_ASSERT(child->GetParent() == this);
-    Unused << mGroup->EvictCachedContext(child);
+    Unused << Group()->EvictCachedContext(child);
   }
 
   mChildren.AppendElements(aChildren);
 
   if (!aFromIPC && XRE_IsContentProcess()) {
     auto cc = ContentChild::GetSingleton();
     MOZ_DIAGNOSTIC_ASSERT(cc);
     cc->SendRestoreBrowsingContextChildren(this, aChildren);
   }
 }
 
-bool BrowsingContext::IsCached() { return mGroup->IsContextCached(this); }
+bool BrowsingContext::IsCached() { return Group()->IsContextCached(this); }
 
 bool BrowsingContext::HasOpener() const {
   return sBrowsingContexts->Contains(mOpenerId);
 }
 
 void BrowsingContext::GetChildren(Children& aChildren) {
   MOZ_ALWAYS_TRUE(aChildren.AppendElements(mChildren));
 }
@@ -544,22 +526,16 @@ bool BrowsingContext::IsActive() const {
     if (!win->GetClosedOuter()) {
       return true;
     }
   }
 
   return false;
 }
 
-void BrowsingContext::Unregister() {
-  MOZ_DIAGNOSTIC_ASSERT(mGroup);
-  mGroup->Unregister(this);
-  mIsDiscarded = true;
-}
-
 BrowsingContext::~BrowsingContext() {
   MOZ_DIAGNOSTIC_ASSERT(!mParent || !mParent->mChildren.Contains(this));
   MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
   MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->IsContextCached(this));
 
   if (sBrowsingContexts) {
     sBrowsingContexts->Remove(Id());
   }
@@ -689,17 +665,17 @@ void BrowsingContext::Blur(ErrorResult& 
   if (ContentChild* cc = ContentChild::GetSingleton()) {
     cc->SendWindowBlur(this);
   } else if (ContentParent* cp = Canonical()->GetContentParent()) {
     Unused << cp->SendWindowBlur(this);
   }
 }
 
 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
-  if (mIsDiscarded) {
+  if (mClosed) {
     return nullptr;
   }
 
   // We never return null or throw an error, but the implementation in
   // nsGlobalWindow does and we need to use the same signature.
   return WindowProxyHolder(Top());
 }
 
@@ -713,17 +689,17 @@ void BrowsingContext::GetOpener(JSContex
   }
 
   if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
     aError.NoteJSContextException(aCx);
   }
 }
 
 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
-  if (mIsDiscarded) {
+  if (mClosed) {
     return nullptr;
   }
 
   // We never throw an error, but the implementation in nsGlobalWindow does and
   // we need to use the same signature.
   if (!mParent) {
     return WindowProxyHolder(this);
   }
--- a/docshell/base/BrowsingContext.h
+++ b/docshell/base/BrowsingContext.h
@@ -143,20 +143,16 @@ class BrowsingContext : public nsWrapper
 
   // Detach the current BrowsingContext from its parent, in both the
   // child and the parent process.
   void Detach(bool aFromIPC = false);
 
   // Prepare this BrowsingContext to leave the current process.
   void PrepareForProcessChange();
 
-  // Detach the current BrowsingContext's children, in both the child
-  // and the parent process.
-  void DetachChildren(bool aFromIPC = false);
-
   // Remove all children from the current BrowsingContext and cache
   // them to allow them to be attached again.
   void CacheChildren(bool aFromIPC = false);
 
   // Restore cached browsing contexts.
   void RestoreChildren(Children&& aChildren, bool aFromIPC = false);
 
   // Determine if the current BrowsingContext was 'cached' by the logic in
@@ -379,19 +375,16 @@ class BrowsingContext : public nsWrapper
   BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
                                          BrowsingContext* aRequestingContext);
 
   // Performs access control to check that 'this' can access 'aContext'.
   bool CanAccess(BrowsingContext* aContext);
 
   bool IsActive() const;
 
-  // Removes the context from its group and sets mIsDetached to true.
-  void Unregister();
-
   friend class ::nsOuterWindowProxy;
   friend class ::nsGlobalWindowOuter;
   // Update the window proxy object that corresponds to this browsing context.
   // This should be called from the window proxy object's objectMoved hook, if
   // the object mWindowProxy points to was moved by the JS GC.
   void UpdateWindowProxy(JSObject* obj, JSObject* old) {
     if (mWindowProxy) {
       MOZ_ASSERT(mWindowProxy == old);
@@ -463,20 +456,16 @@ class BrowsingContext : public nsWrapper
   LocationProxy mLocation;
 
   FieldEpochs mFieldEpochs;
 
   // Is the most recent Document in this BrowsingContext loaded within this
   // process? This may be true with a null mDocShell after the Window has been
   // closed.
   bool mIsInProcess : 1;
-
-  // Has this browsing context been detached? BrowsingContexts should
-  // only be detached once.
-  bool mIsDiscarded : 1;
 };
 
 /**
  * Gets a WindowProxy object for a BrowsingContext that lives in a different
  * process (creating the object if it doesn't already exist). The WindowProxy
  * object will be in the compartment that aCx is currently in. This should only
  * be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
  * lives in this process, and a same-process WindowProxy should be used (see
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -536,18 +536,16 @@ void nsDocShell::DestroyChildren() {
     NS_ASSERTION(shell, "docshell has null child");
 
     if (shell) {
       shell->SetTreeOwner(nullptr);
     }
   }
 
   nsDocLoader::DestroyChildren();
-
-  mBrowsingContext->DetachChildren();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell, nsDocLoader,
                                    mSessionStorageManager, mScriptGlobal,
                                    mInitialClientSource, mSessionHistory,
                                    mBrowsingContext, mChromeEventHandler)
 
 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3869,25 +3869,16 @@ mozilla::ipc::IPCResult ContentChild::Re
     BrowsingContext* aContext) {
   MOZ_RELEASE_ASSERT(aContext);
 
   aContext->Detach(/* aFromIPC */ true);
 
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult ContentChild::RecvDetachBrowsingContextChildren(
-    BrowsingContext* aContext) {
-  MOZ_RELEASE_ASSERT(aContext);
-
-  aContext->DetachChildren(/* aFromIPC */ true);
-
-  return IPC_OK();
-}
-
 mozilla::ipc::IPCResult ContentChild::RecvCacheBrowsingContextChildren(
     BrowsingContext* aContext) {
   MOZ_RELEASE_ASSERT(aContext);
 
   aContext->CacheChildren(/* aFromIPC */ true);
 
   return IPC_OK();
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -712,19 +712,16 @@ class ContentChild final : public PConte
 
   virtual void OnChannelReceivedMessage(const Message& aMsg) override;
 
   mozilla::ipc::IPCResult RecvAttachBrowsingContext(
       BrowsingContext::IPCInitializer&& aInit);
 
   mozilla::ipc::IPCResult RecvDetachBrowsingContext(BrowsingContext* aContext);
 
-  mozilla::ipc::IPCResult RecvDetachBrowsingContextChildren(
-      BrowsingContext* aContext);
-
   mozilla::ipc::IPCResult RecvCacheBrowsingContextChildren(
       BrowsingContext* aContext);
 
   mozilla::ipc::IPCResult RecvRestoreBrowsingContextChildren(
       BrowsingContext* aContext, BrowsingContext::Children&& aChildren);
 
   mozilla::ipc::IPCResult RecvRegisterBrowsingContextGroup(
       nsTArray<BrowsingContext::IPCInitializer>&& aInits);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5828,43 +5828,16 @@ mozilla::ipc::IPCResult ContentParent::R
 
   aContext->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
     Unused << aParent->SendCacheBrowsingContextChildren(aContext);
   });
 
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult ContentParent::RecvDetachBrowsingContextChildren(
-    BrowsingContext* aContext) {
-  if (!aContext) {
-    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
-            ("ParentIPC: Trying to detach from already detached context"));
-    return IPC_OK();
-  }
-
-  if (!aContext->Canonical()->IsOwnedByProcess(ChildID())) {
-    // We're trying to detach the children of a BrowsingContext in
-    // another child process. This is illegal since the owner of the
-    // BrowsingContext is the proccess with the in-process docshell,
-    // which is tracked by OwnerProcessId.
-    MOZ_DIAGNOSTIC_ASSERT(false,
-                          "Trying to detach from out of process context");
-    return IPC_OK();
-  }
-
-  aContext->DetachChildren(/* aFromIPC */ true);
-
-  aContext->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
-    Unused << aParent->SendDetachBrowsingContextChildren(aContext);
-  });
-
-  return IPC_OK();
-}
-
 mozilla::ipc::IPCResult ContentParent::RecvRestoreBrowsingContextChildren(
     BrowsingContext* aContext, BrowsingContext::Children&& aChildren) {
   if (!aContext) {
     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
             ("ParentIPC: Trying to restore already detached"));
     return IPC_OK();
   }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -622,19 +622,16 @@ class ContentParent final : public PCont
 
   static bool IsInputEventQueueSupported();
 
   mozilla::ipc::IPCResult RecvAttachBrowsingContext(
       BrowsingContext::IPCInitializer&& aInit);
 
   mozilla::ipc::IPCResult RecvDetachBrowsingContext(BrowsingContext* aContext);
 
-  mozilla::ipc::IPCResult RecvDetachBrowsingContextChildren(
-      BrowsingContext* aContext);
-
   mozilla::ipc::IPCResult RecvCacheBrowsingContextChildren(
       BrowsingContext* aContext);
 
   mozilla::ipc::IPCResult RecvRestoreBrowsingContextChildren(
       BrowsingContext* aContext, BrowsingContext::Children&& aChildren);
 
   mozilla::ipc::IPCResult RecvWindowClose(BrowsingContext* aContext,
                                           bool aTrustedCaller);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1423,21 +1423,16 @@ both:
      * BrowsingContext belonging to an already detached subtree. The
      * 'aMoveToBFCache' paramater controls if detaching a BrowsingContext
      * should move it to the bfcache allowing it to be re-attached if navigated
      * to.
      */
     async DetachBrowsingContext(BrowsingContext aContext);
 
     /**
-     * Removes all children from 'aContext'.
-     */
-    async DetachBrowsingContextChildren(BrowsingContext aContext);
-
-    /**
      * Removes all of 'aContext'\'s children, and caches them in the
      * BrowsingContextGroup.
      */
     async CacheBrowsingContextChildren(BrowsingContext aContext);
 
     /**
      * Re-attach all BrowsingContexts in a 'aContext'.
      */
--- a/js/xpconnect/idl/mozIJSSubScriptLoader.idl
+++ b/js/xpconnect/idl/mozIJSSubScriptLoader.idl
@@ -5,16 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIPrincipal;
 interface nsIObserver;
 
+/**
+ * Interface for synchronous script loads from local file: or jar: sources.
+ * For asynchronous script loads, ChromeUtils.compileScript() should be used
+ * instead.
+ */
 [scriptable, builtinclass, uuid(19533e7b-f321-4ef1-bc59-6e812dc2a733)]
 interface mozIJSSubScriptLoader : nsISupports
 {
     /**
      * This method should only be called from JS!
      * In JS, the signature looks like:
      * rv loadSubScript (url [, obj] [, charset]);
      * @param url the url of the UTF-8-encoded sub-script, it MUST be either a
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -9,67 +9,59 @@
 #include "mozJSLoaderUtils.h"
 
 #include "nsIURI.h"
 #include "nsIIOService.h"
 #include "nsIChannel.h"
 #include "nsIInputStream.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
-#include "nsIFileURL.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"                   // xpc::OptionsBase
 #include "js/CompilationAndEvaluation.h"  // JS::Compile{,ForNonSyntacticScope}DontInflate
 #include "js/SourceText.h"                // JS::Source{Ownership,Text}
 #include "js/Wrapper.h"
 
 #include "mozilla/ContentPrincipal.h"
-#include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/ScriptLoader.h"
-#include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/SystemPrincipal.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 #include "nsContentUtils.h"
 #include "nsString.h"
-#include "nsCycleCollectionParticipant.h"
 #include "GeckoProfiler.h"
 
 using namespace mozilla::scache;
 using namespace JS;
 using namespace xpc;
 using namespace mozilla;
 using namespace mozilla::dom;
 
 class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
  public:
   explicit LoadSubScriptOptions(JSContext* cx = xpc_GetSafeJSContext(),
                                 JSObject* options = nullptr)
       : OptionsBase(cx, options),
         target(cx),
         ignoreCache(false),
-        async(false),
         wantReturnValue(false) {}
 
   virtual bool Parse() override {
     return ParseObject("target", &target) &&
            ParseBoolean("ignoreCache", &ignoreCache) &&
-           ParseBoolean("async", &async) &&
            ParseBoolean("wantReturnValue", &wantReturnValue);
   }
 
   RootedObject target;
   bool ignoreCache;
-  bool async;
   bool wantReturnValue;
 };
 
 /* load() error msgs, XXX localize? */
 #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
 #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
 #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme.  This is bad."
 #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
@@ -245,214 +237,16 @@ static bool EvalScript(JSContext* cx, Ha
       JSAutoRealm ar(cx, script);
       WriteCachedScript(StartupCache::GetSingleton(), cachePath, cx, script);
     }
   }
 
   return true;
 }
 
-class AsyncScriptLoader : public nsIIncrementalStreamLoaderObserver {
- public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER
-
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
-
-  AsyncScriptLoader(nsIChannel* aChannel, bool aWantReturnValue,
-                    JSObject* aTargetObj, JSObject* aLoadScope, bool aCache,
-                    Promise* aPromise)
-      : mChannel(aChannel),
-        mTargetObj(aTargetObj),
-        mLoadScope(aLoadScope),
-        mPromise(aPromise),
-        mWantReturnValue(aWantReturnValue),
-        mCache(aCache) {
-    // Needed for the cycle collector to manage mTargetObj.
-    mozilla::HoldJSObjects(this);
-  }
-
- private:
-  virtual ~AsyncScriptLoader() { mozilla::DropJSObjects(this); }
-
-  RefPtr<nsIChannel> mChannel;
-  Heap<JSObject*> mTargetObj;
-  Heap<JSObject*> mLoadScope;
-  RefPtr<Promise> mPromise;
-  bool mWantReturnValue;
-  bool mCache;
-};
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader)
-  NS_INTERFACE_MAP_ENTRY(nsIIncrementalStreamLoaderObserver)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
-  tmp->mTargetObj = nullptr;
-  tmp->mLoadScope = nullptr;
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLoadScope)
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader)
-
-class MOZ_STACK_CLASS AutoRejectPromise {
- public:
-  AutoRejectPromise(AutoEntryScript& aAutoEntryScript, Promise* aPromise,
-                    nsIGlobalObject* aGlobalObject)
-      : mAutoEntryScript(aAutoEntryScript),
-        mPromise(aPromise),
-        mGlobalObject(aGlobalObject) {}
-
-  ~AutoRejectPromise() {
-    if (mPromise) {
-      JSContext* cx = mAutoEntryScript.cx();
-      RootedValue rejectionValue(cx, JS::UndefinedValue());
-      if (mAutoEntryScript.HasException()) {
-        Unused << mAutoEntryScript.PeekException(&rejectionValue);
-      }
-      mPromise->MaybeReject(cx, rejectionValue);
-    }
-  }
-
-  void ResolvePromise(HandleValue aResolveValue) {
-    mPromise->MaybeResolve(aResolveValue);
-    mPromise = nullptr;
-  }
-
- private:
-  AutoEntryScript& mAutoEntryScript;
-  RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIGlobalObject> mGlobalObject;
-};
-
-NS_IMETHODIMP
-AsyncScriptLoader::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
-                                     nsISupports* aContext,
-                                     uint32_t aDataLength, const uint8_t* aData,
-                                     uint32_t* aConsumedData) {
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AsyncScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
-                                    nsISupports* aContext, nsresult aStatus,
-                                    uint32_t aLength, const uint8_t* aBuf) {
-  nsCOMPtr<nsIURI> uri;
-  mChannel->GetURI(getter_AddRefs(uri));
-
-  nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(mTargetObj);
-  AutoEntryScript aes(globalObject, "async loadSubScript");
-  AutoRejectPromise autoPromise(aes, mPromise, globalObject);
-  JSContext* cx = aes.cx();
-
-  if (NS_FAILED(aStatus)) {
-    ReportError(cx, "Unable to load script.", uri);
-  }
-  // Just notify that we are done with this load.
-  NS_ENSURE_SUCCESS(aStatus, NS_OK);
-
-  if (aLength == 0) {
-    ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
-    return NS_OK;
-  }
-
-  if (aLength > INT32_MAX) {
-    ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
-    return NS_OK;
-  }
-
-  RootedScript script(cx);
-  nsAutoCString spec;
-  nsresult rv = uri->GetSpec(spec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  RootedObject targetObj(cx, mTargetObj);
-  RootedObject loadScope(cx, mLoadScope);
-
-  script = PrepareScript(uri, cx, JS_IsGlobalObject(targetObj), spec.get(),
-                         reinterpret_cast<const char*>(aBuf), aLength,
-                         mWantReturnValue);
-  if (!script) {
-    return NS_OK;
-  }
-
-  JS::Rooted<JS::Value> retval(cx);
-  if (EvalScript(cx, targetObj, loadScope, &retval, uri, mCache,
-                 mCache && !mWantReturnValue, &script)) {
-    autoPromise.ResolvePromise(retval);
-  }
-
-  return NS_OK;
-}
-
-nsresult mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri,
-                                               HandleObject targetObj,
-                                               HandleObject loadScope,
-                                               nsIIOService* serv,
-                                               bool wantReturnValue, bool cache,
-                                               MutableHandleValue retval) {
-  nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(targetObj);
-  ErrorResult result;
-
-  AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.Init(globalObject))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  RefPtr<Promise> promise = Promise::Create(globalObject, result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
-
-  DebugOnly<bool> asJS = ToJSValue(jsapi.cx(), promise, retval);
-  MOZ_ASSERT(asJS, "Should not fail to convert the promise to a JS value");
-
-  // We create a channel and call SetContentType, to avoid expensive MIME type
-  // lookups (bug 632490).
-  nsCOMPtr<nsIChannel> channel;
-  nsresult rv;
-  rv = NS_NewChannel(getter_AddRefs(channel), uri,
-                     nsContentUtils::GetSystemPrincipal(),
-                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                     nsIContentPolicy::TYPE_OTHER,
-                     nullptr,  // nsICookieSettings
-                     nullptr,  // aPerformanceStorage
-                     nullptr,  // aLoadGroup
-                     nullptr,  // aCallbacks
-                     nsIRequest::LOAD_NORMAL, serv);
-
-  if (!NS_SUCCEEDED(rv)) {
-    return rv;
-  }
-
-  channel->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
-
-  RefPtr<AsyncScriptLoader> loadObserver = new AsyncScriptLoader(
-      channel, wantReturnValue, targetObj, loadScope, cache, promise);
-
-  nsCOMPtr<nsIIncrementalStreamLoader> loader;
-  rv = NS_NewIncrementalStreamLoader(getter_AddRefs(loader), loadObserver);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIStreamListener> listener = loader.get();
-  return channel->AsyncOpen(listener);
-}
-
 JSScript* mozJSSubScriptLoader::ReadScript(
     nsIURI* uri, JSContext* cx, HandleObject targetObj, const char* uriStr,
     nsIIOService* serv, bool wantReturnValue, bool useCompilationScope) {
   // We create a channel and call SetContentType, to avoid expensive MIME type
   // lookups (bug 632490).
   nsCOMPtr<nsIChannel> chan;
   nsCOMPtr<nsIInputStream> instream;
   nsresult rv;
@@ -511,17 +305,18 @@ JSScript* mozJSSubScriptLoader::ReadScri
                        len, wantReturnValue);
 }
 
 NS_IMETHODIMP
 mozJSSubScriptLoader::LoadSubScript(const nsAString& url, HandleValue target,
                                     JSContext* cx, MutableHandleValue retval) {
   /*
    * Loads a local url, referring to UTF-8-encoded data, and evals it into the
-   * current cx.  Synchronous (an async version would be cool too.)
+   * current cx.  Synchronous. ChromeUtils.compileScript() should be used for
+   * async loads.
    *   url: The url to load.  Must be local so that it can be loaded
    *        synchronously.
    *   targetObj: Optional object to eval the script onto (defaults to context
    *              global)
    *   returns: Whatever jsval the script pointed to by the url returns.
    * Should ONLY (O N L Y !) be called from JavaScript code.
    */
   LoadSubScriptOptions options(cx);
@@ -660,22 +455,16 @@ nsresult mozJSSubScriptLoader::DoLoadSub
       rv = ReadCachedScript(cache, cachePath, cx, &script);
     }
     if (NS_FAILED(rv) || !script) {
       // ReadCachedScript may have set a pending exception.
       JS_ClearPendingException(cx);
     }
   }
 
-  // If we are doing an async load, trigger it and bail out.
-  if (!script && options.async) {
-    return ReadScriptAsync(uri, targetObj, loadScope, serv,
-                           options.wantReturnValue, !!cache, retval);
-  }
-
   if (script) {
     // |script| came from the cache, so don't bother writing it
     // |back there.
     cache = nullptr;
   } else {
     script =
         ReadScript(uri, cx, targetObj, static_cast<const char*>(uriStr.get()),
                    serv, options.wantReturnValue, useCompilationScope);
deleted file mode 100644
--- a/js/xpconnect/tests/unit/subScriptWithEarlyError.js
+++ /dev/null
@@ -1,1 +0,0 @@
-var ;
deleted file mode 100644
--- a/js/xpconnect/tests/unit/test_asyncLoadSubScriptError.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var srvScope = {};
-
-function success(result) {
-  ok(false, "script should not have loaded successfully");
-  do_test_finished();
-}
-
-function error(err) {
-  ok(err instanceof SyntaxError, "loading script with early error asynchronously resulted in error");
-  do_test_finished();
-}
-
-function run_test() {
-  do_test_pending();
-
-  var file = do_get_file("subScriptWithEarlyError.js");
-  var ios = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService);
-  var uri = ios.newFileURI(file);
-  var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-                       .getService(Ci.mozIJSSubScriptLoader);
-  var p = scriptLoader.loadSubScriptWithOptions(uri.spec,
-                                                { target: srvScope,
-                                                  async: true });
-  p.then(success, error);
-}
deleted file mode 100644
--- a/js/xpconnect/tests/unit/test_bug1150106.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var srvScope = {};
-
-function success(result) {
-  equal(result, 42, "Result of script is correct");
-  ok('makeTags' in srvScope && srvScope.makeTags instanceof Function,
-     "makeTags is a function.");
-  do_test_finished();
-}
-
-function error() {
-  ok(false, "error loading the script asynchronously.");
-  do_test_finished();
-}
-
-function run_test() {
-  do_test_pending();
-
-  var file = do_get_file("bug451678_subscript.js");
-  var ios = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService);
-  var uri = ios.newFileURI(file);
-  var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-                       .getService(Ci.mozIJSSubScriptLoader);
-  var p = scriptLoader.loadSubScriptWithOptions(uri.spec,
-                                                { target: srvScope,
-                                                  async: true,
-                                                  wantReturnValue: true });
-  p.then(success, error);
-}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -13,17 +13,16 @@ support-files =
   component_import.manifest
   environment_script.js
   environment_loadscript.jsm
   environment_checkscript.jsm
   file_simple_script.js
   importer.jsm
   recursive_importA.jsm
   recursive_importB.jsm
-  subScriptWithEarlyError.js
   syntax_error.jsm
 
 [test_allowWaivers.js]
 [test_bogus_files.js]
 [test_bug408412.js]
 [test_bug451678.js]
 [test_bug604362.js]
 [test_bug677864.js]
@@ -52,17 +51,16 @@ support-files =
 [test_bug1033253.js]
 [test_bug1033920.js]
 [test_bug1033927.js]
 [test_bug1034262.js]
 [test_bug1082450.js]
 [test_bug1081990.js]
 [test_bug1110546.js]
 [test_bug1131707.js]
-[test_bug1150106.js]
 [test_bug1150771.js]
 [test_bug1151385.js]
 [test_bug1170311.js]
 [test_bug1244222.js]
 [test_bug_442086.js]
 [test_callFunctionWithAsyncStack.js]
 [test_cenums.js]
 [test_compileScript.js]
@@ -141,16 +139,15 @@ head = head_watchdog.js
 [test_xrayed_arguments.js]
 [test_xrayed_iterator.js]
 [test_xray_instanceof.js]
 [test_xray_named_element_access.js]
 [test_xray_SavedFrame.js]
 [test_xray_SavedFrame-02.js]
 [test_xray_regexp.js]
 [test_resolve_dead_promise.js]
-[test_asyncLoadSubScriptError.js]
 [test_function_names.js]
 [test_FrameScriptEnvironment.js]
 [test_SubscriptLoaderEnvironment.js]
 [test_SubscriptLoaderSandboxEnvironment.js]
 [test_SubscriptLoaderJSMEnvironment.js]
 [test_ComponentEnvironment.js]
 [test_wrapped_js_enumerator.js]
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -96,30 +96,16 @@ static gfxRect AppUnitsToFloatCSSPixels(
                                         const nsPresContext* aContext) {
   return gfxRect(nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
                  nsPresContext::AppUnitsToFloatCSSPixels(aRect.y),
                  nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
                  nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
 }
 
 /**
- * Scales a gfxRect around a given point.
- *
- * @param aRect The rectangle to scale.
- * @param aPoint The point around which to scale.
- * @param aScale The scale amount.
- */
-static void ScaleAround(gfxRect& aRect, const gfxPoint& aPoint, double aScale) {
-  aRect.x = aPoint.x - aScale * (aPoint.x - aRect.x);
-  aRect.y = aPoint.y - aScale * (aPoint.y - aRect.y);
-  aRect.width *= aScale;
-  aRect.height *= aScale;
-}
-
-/**
  * Returns whether a gfxPoint lies within a gfxRect.
  */
 static bool Inside(const gfxRect& aRect, const gfxPoint& aPoint) {
   return aPoint.x >= aRect.x && aPoint.x < aRect.XMost() &&
          aPoint.y >= aRect.y && aPoint.y < aRect.YMost();
 }
 
 /**
@@ -900,24 +886,18 @@ SVGBBox TextRenderedRun::GetRunUserSpace
   }
 
   // Convert the app units rectangle to user units.
   gfxRect fill = AppUnitsToFloatCSSPixels(
       gfxRect(fillInAppUnits.x, fillInAppUnits.y, fillInAppUnits.width,
               fillInAppUnits.height),
       aContext);
 
-  // Scale the rectangle up due to any mFontSizeScaleFactor.  We scale
-  // it around the text's origin.
-  ScaleAround(
-      fill,
-      textRun->IsVertical()
-          ? gfxPoint(nsPresContext::AppUnitsToFloatCSSPixels(baseline), 0.0)
-          : gfxPoint(0.0, nsPresContext::AppUnitsToFloatCSSPixels(baseline)),
-      1.0 / mFontSizeScaleFactor);
+  // Scale the rectangle up due to any mFontSizeScaleFactor.
+  fill.Scale(1.0 / mFontSizeScaleFactor);
 
   // Include the fill if requested.
   if (aFlags & eIncludeFill) {
     r = fill;
   }
 
   // Include the stroke if requested.
   if ((aFlags & eIncludeStroke) && !fill.IsEmpty() &&
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/coordinate-systems/support/viewBox-scaling-text-001-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <title>Reference case for text scaled via SVG viewBox</title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <style>
+    body { margin: 0; }
+    svg {
+      width: 100px;
+      height: 100px;
+      background: red;
+    }
+    rect {
+      fill: lime;
+    }
+  </style>
+</head>
+<body>
+  <svg>
+    <rect height="100%" width="100%"></rect>
+  </svg>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/coordinate-systems/viewBox-scaling-text-001.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <title>Testcase for text scaled via SVG viewBox</title>
+  <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
+  <link rel="help" href="https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute">
+  <link rel="match" href="support/viewBox-scaling-text-001-ref.html">
+  <style>
+    body { margin: 0; }
+    svg {
+      width: 100px;
+      height: 100px;
+      background: red;
+    }
+    text {
+      fill: lime;
+      font: 1px/1 Ahem;
+    }
+  </style>
+</head>
+<body>
+  <!-- We position the <text> at y=0.8px, which is the alphabetic baseline for
+       the Ahem font. This puts the bottom of the rendered square glyph at
+       y=1px, i.e. the bottom of the SVG viewport. With that, the 1px-tall Ahem
+       square 'X' character should fully fill the SVG viewport (which is then
+       scaled up from 1x1 to 100x100). -->
+  <svg viewBox="0 0 1 1">
+    <text x="0" y="0.8">X̂̂̂̂̂̂</text>
+  </svg>
+</body>