Backed out 7 changesets (bug 1392408) for mochitest failures at test_xhr_withCredentials.html and xpcshell failures at test_startup_(no)session_async.js
authorDaniel Varga <dvarga@mozilla.com>
Fri, 03 May 2019 11:56:25 +0300
changeset 531274 9dbf4a541fff28274537c379f29b7bdb58cfd60d
parent 531273 f72b69b07dd65a242e08c1bdbc4a456969aab243
child 531275 c68ff92fe54c75338c3a937bc6c4e615d9b1d45a
child 531387 f8178fdb90bc7673d7079251a2e640cb701c94ba
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1392408
milestone68.0a1
backs out05656abf3e6c6862ba89990fd9ba3854fe5eb5f1
ac9c08f90cd070b591211a51419b8e301470a911
855b8dd227f92048824ff83ee46855f6f9bf010f
9c3b9f800a0ce8207d7716582206a2a4b2fe67e5
2a64ecfab240530e811d8123dcb1f0276716b0f8
b765eaf69bcb947cedacfefd424a43fa403f0aa4
337d93c03c2e540b3f1d220f4ccbd1f9deb37435
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
Backed out 7 changesets (bug 1392408) for mochitest failures at test_xhr_withCredentials.html and xpcshell failures at test_startup_(no)session_async.js Backed out changeset 05656abf3e6c (bug 1392408) Backed out changeset ac9c08f90cd0 (bug 1392408) Backed out changeset 855b8dd227f9 (bug 1392408) Backed out changeset 9c3b9f800a0c (bug 1392408) Backed out changeset 2a64ecfab240 (bug 1392408) Backed out changeset b765eaf69bcb (bug 1392408) Backed out changeset 337d93c03c2e (bug 1392408)
devtools/client/netmonitor/test/browser.ini
devtools/client/netmonitor/test/browser_net_cause.js
devtools/client/netmonitor/test/browser_net_worker_stacks.js
devtools/client/netmonitor/test/head.js
devtools/client/netmonitor/test/html_worker-test-page.html
devtools/client/netmonitor/test/js_worker-test.js
devtools/client/netmonitor/test/js_worker-test2.js
devtools/server/actors/network-monitor/stack-trace-collector.js
dom/base/SerializedStackHolder.cpp
dom/base/SerializedStackHolder.h
dom/base/moz.build
dom/base/nsContentPolicy.cpp
dom/base/nsFrameLoaderOwner.cpp
dom/bindings/Bindings.conf
dom/workers/ScriptLoader.cpp
dom/workers/ScriptLoader.h
dom/workers/WorkerDebugger.cpp
dom/workers/WorkerError.cpp
dom/workers/WorkerError.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
dom/xhr/XMLHttpRequestWorker.cpp
js/public/SavedFrameAPI.h
js/src/vm/SavedStacks.cpp
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -38,19 +38,16 @@ support-files =
   html_sorting-test-page.html
   html_statistics-test-page.html
   html_status-codes-test-page.html
   html_tracking-protection.html
   html_api-calls-test-page.html
   html_copy-as-curl.html
   html_curl-utils.html
   html_open-request-in-tab.html
-  html_worker-test-page.html
-  js_worker-test.js
-  js_worker-test2.js
   sjs_content-type-test-server.sjs
   sjs_cors-test-server.sjs
   sjs_https-redirect-test-server.sjs
   sjs_hsts-test-server.sjs
   sjs_json-test-server.sjs
   sjs_method-test-server.sjs
   sjs_set-cookie-same-site.sjs
   sjs_simple-test-server.sjs
@@ -216,9 +213,8 @@ skip-if = true # Bug 1373558
 [browser_net_timeline_ticks.js]
 skip-if = true # TODO: fix the test
 [browser_net_timing-division.js]
 [browser_net_tracking-resources.js]
 [browser_net_truncate-post-data.js]
 [browser_net_truncate.js]
 [browser_net_view-source-debugger.js]
 [browser_net_waterfall-click.js]
-[browser_net_worker_stacks.js]
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -88,33 +88,71 @@ add_task(async function() {
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
 
   const { document, store, windowRequire, connector } = monitor.panelWin;
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   const {
+    getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   const wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   BrowserTestUtils.loadURI(tab.linkedBrowser, CAUSE_URL);
   await wait;
 
   const requests = getSortedRequests(store.getState());
   await Promise.all(requests.map(requestItem =>
     connector.requestData(requestItem.id, "stackTrace")));
 
   is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
     "All the page events should be recorded.");
 
-  validateRequests(EXPECTED_REQUESTS, monitor);
+  EXPECTED_REQUESTS.forEach((spec, i) => {
+    const { method, url, causeType, causeUri, stack } = spec;
+
+    const requestItem = getSortedRequests(store.getState()).get(i);
+    verifyRequestItemTarget(
+      document,
+      getDisplayedRequests(store.getState()),
+      requestItem,
+      method,
+      url,
+      { cause: { type: causeType, loadingDocumentUri: causeUri } }
+    );
+
+    const stacktrace = requestItem.stacktrace;
+    const stackLen = stacktrace ? stacktrace.length : 0;
+
+    if (stack) {
+      ok(stacktrace, `Request #${i} has a stacktrace`);
+      ok(stackLen > 0,
+        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
+
+      // if "stack" is array, check the details about the top stack frames
+      if (Array.isArray(stack)) {
+        stack.forEach((frame, j) => {
+          is(stacktrace[j].functionName, frame.fn,
+            `Request #${i} has the correct function on JS stack frame #${j}`);
+          is(stacktrace[j].filename.split("/").pop(), frame.file,
+            `Request #${i} has the correct file on JS stack frame #${j}`);
+          is(stacktrace[j].lineNumber, frame.line,
+            `Request #${i} has the correct line number on JS stack frame #${j}`);
+          is(stacktrace[j].asyncCause, frame.asyncCause,
+            `Request #${i} has the correct async cause on JS stack frame #${j}`);
+        });
+      }
+    } else {
+      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
+    }
+  });
 
   // Sort the requests by cause and check the order
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#requests-list-cause-button"));
   const expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
   expectedOrder.forEach((expectedCause, i) => {
     const cause = getSortedRequests(store.getState()).get(i).cause.type;
     is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
deleted file mode 100644
--- a/devtools/client/netmonitor/test/browser_net_worker_stacks.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test that we get stack traces for the network requests made when starting or
-// running worker threads.
-
-const TOP_FILE_NAME = "html_worker-test-page.html";
-const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME;
-const WORKER_FILE_NAME = "js_worker-test.js";
-const WORKER_URL = EXAMPLE_URL + WORKER_FILE_NAME;
-
-const EXPECTED_REQUESTS = [
-  {
-    method: "GET",
-    url: TOP_URL,
-    causeType: "document",
-    causeUri: null,
-    stack: true,
-  },
-  {
-    method: "GET",
-    url: WORKER_URL,
-    causeType: "script",
-    causeUri: TOP_URL,
-    stack: [
-      { fn: "startWorkerInner", file: TOP_FILE_NAME, line: 11 },
-      { fn: "startWorker", file: TOP_FILE_NAME, line: 8 },
-      { file: TOP_FILE_NAME, line: 4 },
-    ],
-  },
-  {
-    method: "GET",
-    url: EXAMPLE_URL + "missing1.js",
-    causeType: "script",
-    causeUri: TOP_URL,
-    stack: [
-      { fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
-      { file: WORKER_FILE_NAME, line: 10 },
-    ],
-  },
-  {
-    method: "GET",
-    url: EXAMPLE_URL + "missing2.js",
-    causeType: "script",
-    causeUri: TOP_URL,
-    stack: [
-      { fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
-      { file: WORKER_FILE_NAME, line: 10 },
-    ],
-  },
-  {
-    method: "GET",
-    url: EXAMPLE_URL + "js_worker-test2.js",
-    causeType: "script",
-    causeUri: TOP_URL,
-    stack: [
-      { fn: "startWorkerFromWorker", file: WORKER_FILE_NAME, line: 7 },
-      { file: WORKER_FILE_NAME, line: 3 },
-    ],
-  },
-  {
-    method: "GET",
-    url: EXAMPLE_URL + "missing.json",
-    causeType: "xhr",
-    causeUri: TOP_URL,
-    stack: [
-      { fn: "createJSONRequest", file: WORKER_FILE_NAME, line: 22 },
-      { file: WORKER_FILE_NAME, line: 18 },
-    ],
-  },
-];
-
-add_task(async function() {
-  // Load a different URL first to instantiate the network monitor before we
-  // load the page we're really interested in.
-  const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
-
-  const { store, windowRequire, connector } = monitor.panelWin;
-  const {
-    getSortedRequests,
-  } = windowRequire("devtools/client/netmonitor/src/selectors/index");
-
-  BrowserTestUtils.loadURI(tab.linkedBrowser, TOP_URL);
-
-  await waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
-
-  is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
-    "All the page events should be recorded.");
-
-  // Wait for stack traces from all requests.
-  const requests = getSortedRequests(store.getState());
-  await Promise.all(requests.map(requestItem =>
-    connector.requestData(requestItem.id, "stackTrace")));
-
-  validateRequests(EXPECTED_REQUESTS, monitor);
-
-  await teardown(monitor);
-});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -862,55 +862,8 @@ function queryTelemetryEvents(query) {
     event[1] === category &&
     event[2] === query.method &&
     event[3] === object
   );
 
   // Return the `extra` field (which is event[5]e).
   return filtersChangedEvents.map(event => event[5]);
 }
-
-function validateRequests(requests, monitor) {
-  const { document, store, windowRequire } = monitor.panelWin;
-
-  const {
-    getDisplayedRequests,
-  } = windowRequire("devtools/client/netmonitor/src/selectors/index");
-
-  requests.forEach((spec, i) => {
-    const { method, url, causeType, causeUri, stack } = spec;
-
-    const requestItem = getSortedRequests(store.getState()).get(i);
-    verifyRequestItemTarget(
-      document,
-      getDisplayedRequests(store.getState()),
-      requestItem,
-      method,
-      url,
-      { cause: { type: causeType, loadingDocumentUri: causeUri } }
-    );
-
-    const stacktrace = requestItem.stacktrace;
-    const stackLen = stacktrace ? stacktrace.length : 0;
-
-    if (stack) {
-      ok(stacktrace, `Request #${i} has a stacktrace`);
-      ok(stackLen > 0,
-        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
-
-      // if "stack" is array, check the details about the top stack frames
-      if (Array.isArray(stack)) {
-        stack.forEach((frame, j) => {
-          is(stacktrace[j].functionName, frame.fn,
-            `Request #${i} has the correct function on JS stack frame #${j}`);
-          is(stacktrace[j].filename.split("/").pop(), frame.file,
-            `Request #${i} has the correct file on JS stack frame #${j}`);
-          is(stacktrace[j].lineNumber, frame.line,
-            `Request #${i} has the correct line number on JS stack frame #${j}`);
-          is(stacktrace[j].asyncCause, frame.asyncCause,
-            `Request #${i} has the correct async cause on JS stack frame #${j}`);
-        });
-      }
-    } else {
-      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
-    }
-  });
-}
deleted file mode 100644
--- a/devtools/client/netmonitor/test/html_worker-test-page.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<script>
-/* eslint-disable no-unused-vars */
-"use strict";
-startWorker();
-
-var w;
-function startWorker() {
-  startWorkerInner();
-}
-function startWorkerInner() {
-  w = new Worker("js_worker-test.js");
-}
-</script>
deleted file mode 100644
--- a/devtools/client/netmonitor/test/js_worker-test.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* eslint-disable no-unused-vars, no-undef */
-"use strict";
-startWorkerFromWorker();
-
-var w;
-function startWorkerFromWorker() {
-  w = new Worker("js_worker-test2.js");
-}
-
-importScriptsFromWorker();
-
-function importScriptsFromWorker() {
-  try {
-    importScripts("missing1.js", "missing2.js");
-  } catch (e) {}
-}
-
-createJSONRequest();
-
-function createJSONRequest() {
-  const request = new XMLHttpRequest();
-  request.open("GET", "missing.json", true);
-  request.send(null);
-}
deleted file mode 100644
--- a/devtools/client/netmonitor/test/js_worker-test2.js
+++ /dev/null
@@ -1,3 +0,0 @@
-"use strict";
-
-console.log("I AM A WORKER");
--- a/devtools/server/actors/network-monitor/stack-trace-collector.js
+++ b/devtools/server/actors/network-monitor/stack-trace-collector.js
@@ -20,110 +20,65 @@ function StackTraceCollector(filters, ne
   this.filters = filters;
   this.stacktracesById = new Map();
   this.netmonitors = netmonitors;
 }
 
 StackTraceCollector.prototype = {
   init() {
     Services.obs.addObserver(this, "http-on-opening-request");
-    Services.obs.addObserver(this, "network-monitor-alternate-stack");
     ChannelEventSinkFactory.getService().registerCollector(this);
     this.onGetStack = this.onGetStack.bind(this);
     for (const { messageManager } of this.netmonitors) {
       messageManager.addMessageListener("debug:request-stack:request", this.onGetStack);
     }
   },
 
   destroy() {
     Services.obs.removeObserver(this, "http-on-opening-request");
-    Services.obs.removeObserver(this, "network-monitor-alternate-stack");
     ChannelEventSinkFactory.getService().unregisterCollector(this);
     for (const { messageManager } of this.netmonitors) {
       messageManager.removeMessageListener("debug:request-stack:request",
         this.onGetStack);
     }
   },
 
   _saveStackTrace(channel, stacktrace) {
-    if (this.stacktracesById.has(channel.channelId)) {
-      // We can get up to two stack traces for the same channel: one each from
-      // the two observer topics we are listening to. Use the first stack trace
-      // which is specified, and ignore any later one.
-      return;
-    }
     for (const { messageManager } of this.netmonitors) {
       messageManager.sendAsyncMessage("debug:request-stack-available", {
         channelId: channel.channelId,
         stacktrace: stacktrace && stacktrace.length > 0,
       });
     }
     this.stacktracesById.set(channel.channelId, stacktrace);
   },
 
-  observe(subject, topic, data) {
+  observe(subject) {
     const channel = subject.QueryInterface(Ci.nsIHttpChannel);
 
     if (!matchRequest(channel, this.filters)) {
       return;
     }
 
+    // Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
+    // passed around through message managers etc.
+    let frame = components.stack;
     const stacktrace = [];
-    switch (topic) {
-      case "http-on-opening-request": {
-        // The channel is being opened on the main thread, associate the current
-        // stack with it.
-        //
-        // Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
-        // passed around through message managers etc.
-        let frame = components.stack;
-        if (frame && frame.caller) {
-          frame = frame.caller;
-          while (frame) {
-            stacktrace.push({
-              filename: frame.filename,
-              lineNumber: frame.lineNumber,
-              columnNumber: frame.columnNumber,
-              functionName: frame.name,
-              asyncCause: frame.asyncCause,
-            });
-            frame = frame.caller || frame.asyncCaller;
-          }
-        }
-        break;
+    if (frame && frame.caller) {
+      frame = frame.caller;
+      while (frame) {
+        stacktrace.push({
+          filename: frame.filename,
+          lineNumber: frame.lineNumber,
+          columnNumber: frame.columnNumber,
+          functionName: frame.name,
+          asyncCause: frame.asyncCause,
+        });
+        frame = frame.caller || frame.asyncCaller;
       }
-      case "network-monitor-alternate-stack": {
-        // An alternate stack trace is being specified for this channel.
-        // The topic data is the JSON for the saved frame stack we should use,
-        // so convert this into the expected format.
-        //
-        // This topic is used in the following cases:
-        //
-        // - The HTTP channel is opened asynchronously or on a different thread
-        //   from the code which triggered its creation, in which case the stack
-        //   from components.stack will be empty. The alternate stack will be
-        //   for the point we want to associate with the channel.
-        //
-        // - The channel is not a nsIHttpChannel, and we will receive no
-        //   opening request notification for it.
-        let frame = JSON.parse(data);
-        while (frame) {
-          stacktrace.push({
-            filename: frame.source,
-            lineNumber: frame.line,
-            columnNumber: frame.column,
-            functionName: frame.functionDisplayName,
-            asyncCause: frame.asyncCause,
-          });
-          frame = frame.parent || frame.asyncParent;
-        }
-        break;
-      }
-      default:
-        throw new Error("Unexpected observe() topic");
     }
 
     this._saveStackTrace(channel, stacktrace);
   },
 
   // eslint-disable-next-line no-shadow
   onChannelRedirect(oldChannel, newChannel, flags) {
     // We can be called with any nsIChannel, but are interested only in HTTP channels
deleted file mode 100644
--- a/dom/base/SerializedStackHolder.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "SerializedStackHolder.h"
-
-#include "js/SavedFrameAPI.h"
-#include "mozilla/dom/WorkerPrivate.h"
-
-namespace mozilla {
-namespace dom {
-
-SerializedStackHolder::SerializedStackHolder()
-  : mHolder(StructuredCloneHolder::CloningSupported,
-            StructuredCloneHolder::TransferringNotSupported,
-            StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread) {}
-
-void SerializedStackHolder::WriteStack(JSContext* aCx,
-                                       JS::HandleObject aStack) {
-  JS::RootedValue stackValue(aCx, JS::ObjectValue(*aStack));
-  mHolder.Write(aCx, stackValue, IgnoreErrors());
-
-  // StructuredCloneHolder::Write can leave a pending exception on the context.
-  JS_ClearPendingException(aCx);
-}
-
-void SerializedStackHolder::SerializeMainThreadStack(JSContext* aCx,
-                                                     JS::HandleObject aStack) {
-  MOZ_ASSERT(NS_IsMainThread());
-  WriteStack(aCx, aStack);
-}
-
-void SerializedStackHolder::SerializeWorkerStack(JSContext* aCx,
-                                                 WorkerPrivate* aWorkerPrivate,
-                                                 JS::HandleObject aStack) {
-  MOZ_ASSERT(aWorkerPrivate->IsOnCurrentThread());
-
-  RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
-      aWorkerPrivate, "WorkerErrorReport");
-  if (workerRef) {
-    mWorkerRef = new ThreadSafeWorkerRef(workerRef);
-  } else {
-    // Don't write the stack if we can't create a ref to the worker.
-    return;
-  }
-
-  WriteStack(aCx, aStack);
-}
-
-void SerializedStackHolder::SerializeCurrentStack(JSContext* aCx) {
-  JS::RootedObject stack(aCx);
-  if (JS::CurrentGlobalOrNull(aCx) && !JS::CaptureCurrentStack(aCx, &stack)) {
-    JS_ClearPendingException(aCx);
-    return;
-  }
-
-  if (stack) {
-    if (NS_IsMainThread()) {
-      SerializeMainThreadStack(aCx, stack);
-    } else {
-      WorkerPrivate* currentWorker = GetCurrentThreadWorkerPrivate();
-      SerializeWorkerStack(aCx, currentWorker, stack);
-    }
-  }
-}
-
-JSObject* SerializedStackHolder::ReadStack(JSContext* aCx) {
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mHolder.HasData()) {
-    return nullptr;
-  }
-
-  Maybe<nsJSPrincipals::AutoSetActiveWorkerPrincipal> set;
-  if (mWorkerRef) {
-    set.emplace(mWorkerRef->Private()->GetPrincipal());
-  }
-
-  JS::RootedValue stackValue(aCx);
-  mHolder.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackValue,
-               IgnoreErrors());
-  return stackValue.isObject() ? &stackValue.toObject() : nullptr;
-}
-
-UniquePtr<SerializedStackHolder> GetCurrentStackForNetMonitor(JSContext* aCx) {
-  UniquePtr<SerializedStackHolder> stack = MakeUnique<SerializedStackHolder>();
-  stack->SerializeCurrentStack(aCx);
-  return stack;
-}
-
-void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
-                                        UniquePtr<SerializedStackHolder> aStackHolder) {
-  if (!aStackHolder) {
-    return;
-  }
-
-  nsString stackString;
-  ConvertSerializedStackToJSON(std::move(aStackHolder), stackString);
-
-  if (!stackString.IsEmpty()) {
-    NotifyNetworkMonitorAlternateStack(aChannel, stackString);
-  }
-}
-
-void ConvertSerializedStackToJSON(UniquePtr<SerializedStackHolder> aStackHolder,
-                                  nsAString& aStackString) {
-  // We need a JSContext to be able to stringify the SavedFrame stack.
-  // This will not run any scripts. A privileged scope is needed to fully
-  // inspect all stack frames we find.
-  AutoJSAPI jsapi;
-  DebugOnly<bool> ok = jsapi.Init(xpc::PrivilegedJunkScope());
-  JSContext* cx = jsapi.cx();
-
-  JS::RootedObject savedFrame(cx, aStackHolder->ReadStack(cx));
-  if (!savedFrame) {
-    return;
-  }
-
-  JS::RootedObject converted(cx);
-  converted = JS::ConvertSavedFrameToPlainObject(cx, savedFrame,
-                                                 JS::SavedFrameSelfHosted::Exclude);
-  if (!converted) {
-    JS_ClearPendingException(cx);
-    return;
-  }
-
-  JS::RootedValue convertedValue(cx, JS::ObjectValue(*converted));
-  if (!nsContentUtils::StringifyJSON(cx, &convertedValue, aStackString)) {
-    JS_ClearPendingException(cx);
-    return;
-  }
-}
-
-void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
-                                        const nsAString& aStackJSON) {
-  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
-  if (!obsService) {
-    return;
-  }
-
-  obsService->NotifyObservers(aChannel, "network-monitor-alternate-stack",
-                              PromiseFlatString(aStackJSON).get());
-}
-
-}  // namespace dom
-}  // namespace mozilla
deleted file mode 100644
--- a/dom/base/SerializedStackHolder.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_SerializedStackHolder_h
-#define mozilla_dom_SerializedStackHolder_h
-
-#include "mozilla/dom/StructuredCloneHolder.h"
-#include "mozilla/dom/WorkerRef.h"
-
-namespace mozilla {
-namespace dom {
-
-// Information about a main or worker thread stack trace that can be accessed
-// from either kind of thread. When a worker thread stack is serialized, the
-// worker is held alive until this holder is destroyed.
-class SerializedStackHolder {
-  // Holds any encoded stack data.
-  StructuredCloneHolder mHolder;
-
-  // The worker associated with this stack, or null if this is a main thread
-  // stack.
-  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
-
-  // Write aStack's data into mHolder.
-  void WriteStack(JSContext* aCx, JS::HandleObject aStack);
-
- public:
-  SerializedStackHolder();
-
-  // Fill this holder with a main thread stack.
-  void SerializeMainThreadStack(JSContext* aCx, JS::HandleObject aStack);
-
-  // Fill this holder with a worker thread stack.
-  void SerializeWorkerStack(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-                            JS::HandleObject aStack);
-
-  // Fill this holder with the current thread's current stack.
-  void SerializeCurrentStack(JSContext* aCx);
-
-  // Read back a saved frame stack. This must be called on the main thread.
-  // This returns null on failure, and does not leave an exception on aCx.
-  JSObject* ReadStack(JSContext* aCx);
-};
-
-// Construct a stack for the current thread, which may be consumed by the net
-// monitor later on. This may be called on either the main or a worker thread.
-//
-// This always creates a stack, even if the net monitor isn't active for the
-// associated window. Ideally we would only create the stack if the net monitor
-// was active, but there doesn't seem to be an easy way to determine this.
-// The operations this is used with should be rare enough and/or have enough
-// other associated costs that the perf impact is low. See bug 1546736.
-UniquePtr<SerializedStackHolder> GetCurrentStackForNetMonitor(JSContext* aCx);
-
-// If aStackHolder is non-null, this notifies the net monitor that aStackHolder
-// is the stack from which aChannel originates. This must be called on the main
-// thread. This call is synchronous, and aChannel and aStackHolder will not be
-// used afterward. aChannel is an nsISupports object because this can be used
-// with either nsIChannel or nsIWebSocketChannel.
-void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
-                                        UniquePtr<SerializedStackHolder> aStackHolder);
-
-// Read back the saved frame stack and store it in a string as JSON.
-// This must be called on the main thread.
-void ConvertSerializedStackToJSON(UniquePtr<SerializedStackHolder> aStackHolder,
-                                  nsAString& aStackString);
-
-// As above, notify the net monitor for a stack that has already been converted
-// to JSON. This can be used with ConvertSerializedStackToJSON when multiple
-// notifications might be needed for a single stack.
-void NotifyNetworkMonitorAlternateStack(nsISupports* aChannel,
-                                        const nsAString& aStackJSON);
-
-}  // namespace dom
-}  // namespace mozilla
-
-#endif  // mozilla_dom_SerializedStackHolder_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -218,17 +218,16 @@ EXPORTS.mozilla.dom += [
     'ProcessMessageManager.h',
     'ResizeObserver.h',
     'ResizeObserverController.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenLuminance.h',
     'ScreenOrientation.h',
     'Selection.h',
-    'SerializedStackHolder.h',
     'ShadowIncludingTreeIterator.h',
     'ShadowRoot.h',
     'StructuredCloneBlob.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
     'StructuredCloneTester.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
@@ -388,17 +387,16 @@ UNIFIED_SOURCES += [
     'ResizeObserverController.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScreenLuminance.cpp',
     'ScreenOrientation.cpp',
     'ScriptableContentIterator.cpp',
     'Selection.cpp',
     'SelectionChangeEventDispatcher.cpp',
-    'SerializedStackHolder.cpp',
     'ShadowRoot.cpp',
     'StorageAccessPermissionRequest.cpp',
     'StructuredCloneBlob.cpp',
     'StructuredCloneHolder.cpp',
     'StructuredCloneTester.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'TabGroup.cpp',
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -94,17 +94,17 @@ inline nsresult nsContentPolicy::CheckPo
 #endif
 
   /*
    * There might not be a requestinglocation. This can happen for
    * iframes with an image as src. Get the uri from the dom node.
    * See bug 254510
    */
   if (!requestingLocation) {
-    nsCOMPtr<mozilla::dom::Document> doc;
+    nsCOMPtr<Document> doc;
     nsCOMPtr<nsIContent> node = do_QueryInterface(requestingContext);
     if (node) {
       doc = node->OwnerDoc();
     }
     if (!doc) {
       doc = do_QueryInterface(requestingContext);
     }
     if (doc) {
--- a/dom/base/nsFrameLoaderOwner.cpp
+++ b/dom/base/nsFrameLoaderOwner.cpp
@@ -7,17 +7,16 @@
 #include "nsFrameLoaderOwner.h"
 #include "nsFrameLoader.h"
 #include "nsFocusManager.h"
 #include "nsSubDocumentFrame.h"
 #include "nsQueryObject.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
-#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
 
 already_AddRefed<nsFrameLoader> nsFrameLoaderOwner::GetFrameLoader() {
   return do_AddRef(mFrameLoader);
 }
 
 void nsFrameLoaderOwner::SetFrameLoader(nsFrameLoader* aNewFrameLoader) {
   mFrameLoader = aNewFrameLoader;
 }
@@ -65,14 +64,12 @@ void nsFrameLoaderOwner::ChangeRemotenes
       fm->ActivateRemoteFrameIfNeeded(*owner);
     }
   }
 
   // Assuming this element is a XULFrameElement, once we've reset our
   // FrameLoader, fire an event to act like we've recreated ourselves, similar
   // to what XULFrameElement does after rebinding to the tree.
   // ChromeOnlyDispatch is turns on to make sure this isn't fired into content.
-  (new mozilla::AsyncEventDispatcher(owner,
-                                     NS_LITERAL_STRING("XULFrameLoaderCreated"),
-                                     mozilla::CanBubble::eYes,
-                                     mozilla::ChromeOnlyDispatch::eYes))
+  (new AsyncEventDispatcher(owner, NS_LITERAL_STRING("XULFrameLoaderCreated"),
+                            CanBubble::eYes, ChromeOnlyDispatch::eYes))
       ->RunDOMEventWhenSafe();
 }
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1551,17 +1551,17 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'implicitJSContext': [
         'dump', 'global', 'reportError', 'setConsoleEventHandler',
     ],
 },
 
 'WorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
-    'implicitJSContext': [ 'createImageBitmap', 'importScripts' ],
+    'implicitJSContext': [ 'createImageBitmap' ],
     'concrete': False,
     # Rename a few things so we don't have both classes and methods
     # with the same name
     'binaryNames': {
         'performance': 'getPerformance',
     },
 },
 
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -544,41 +544,37 @@ NS_IMPL_ISUPPORTS(LoaderListener, nsIStr
 
 class ScriptLoaderRunnable final : public nsIRunnable, public nsINamed {
   friend class ScriptExecutorRunnable;
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
   friend class LoaderListener;
 
   WorkerPrivate* mWorkerPrivate;
-  UniquePtr<SerializedStackHolder> mOriginStack;
-  nsString mOriginStackJSON;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   nsTArray<ScriptLoadInfo> mLoadInfos;
   RefPtr<CacheCreator> mCacheCreator;
   Maybe<ClientInfo> mClientInfo;
   Maybe<ServiceWorkerDescriptor> mController;
   bool mIsMainScript;
   WorkerScriptType mWorkerScriptType;
   bool mCanceledMainThread;
   ErrorResult& mRv;
 
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
-                       UniquePtr<SerializedStackHolder> aOriginStack,
                        nsIEventTarget* aSyncLoopTarget,
                        nsTArray<ScriptLoadInfo>& aLoadInfos,
                        const Maybe<ClientInfo>& aClientInfo,
                        const Maybe<ServiceWorkerDescriptor>& aController,
                        bool aIsMainScript, WorkerScriptType aWorkerScriptType,
                        ErrorResult& aRv)
       : mWorkerPrivate(aWorkerPrivate),
-        mOriginStack(std::move(aOriginStack)),
         mSyncLoopTarget(aSyncLoopTarget),
         mClientInfo(aClientInfo),
         mController(aController),
         mIsMainScript(aIsMainScript),
         mWorkerScriptType(aWorkerScriptType),
         mCanceledMainThread(false),
         mRv(aRv) {
     aWorkerPrivate->AssertIsOnWorkerThread();
@@ -835,24 +831,16 @@ class ScriptLoaderRunnable final : publi
 
   nsresult RunInternal() {
     AssertIsOnMainThread();
 
     if (IsMainWorkerScript()) {
       mWorkerPrivate->SetLoadingWorkerScript(true);
     }
 
-    // Convert the origin stack to JSON (which must be done on the main
-    // thread) explicitly, so that we can use the stack to notify the net
-    // monitor about every script we load.
-    if (mOriginStack) {
-      ConvertSerializedStackToJSON(std::move(mOriginStack),
-                                   mOriginStackJSON);
-    }
-
     if (!mWorkerPrivate->IsServiceWorker() || IsDebuggerScript()) {
       for (uint32_t index = 0, len = mLoadInfos.Length(); index < len;
            ++index) {
         nsresult rv = LoadScript(index);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           LoadingFinished(index, rv);
           return rv;
         }
@@ -969,21 +957,16 @@ class ScriptLoaderRunnable final : publi
           mClientInfo, mController, IsMainWorkerScript(), mWorkerScriptType,
           mWorkerPrivate->ContentPolicyType(), loadFlags,
           mWorkerPrivate->CookieSettings(), getter_AddRefs(channel));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    // Associate any originating stack with the channel.
-    if (!mOriginStackJSON.IsEmpty()) {
-      NotifyNetworkMonitorAlternateStack(channel, mOriginStackJSON);
-    }
-
     // We need to know which index we're on in OnStreamComplete so we know
     // where to put the result.
     RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
 
     // We don't care about progress so just use the simple stream loader for
     // OnStreamComplete notification only.
     nsCOMPtr<nsIStreamLoader> loader;
     rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
@@ -2093,17 +2076,16 @@ void ScriptExecutorRunnable::LogExceptio
   xpcReport->Init(report.report(), report.toStringResult().c_str(),
                   aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
 
   RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
   NS_DispatchToMainThread(r);
 }
 
 void LoadAllScripts(WorkerPrivate* aWorkerPrivate,
-                    UniquePtr<SerializedStackHolder> aOriginStack,
                     nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsMainScript,
                     WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
   aWorkerPrivate->AssertIsOnWorkerThread();
   NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
 
   AutoSyncLoopHolder syncLoop(aWorkerPrivate, Canceling);
   nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget();
   if (!syncLoopTarget) {
@@ -2114,18 +2096,18 @@ void LoadAllScripts(WorkerPrivate* aWork
   Maybe<ClientInfo> clientInfo;
   Maybe<ServiceWorkerDescriptor> controller;
   if (!aIsMainScript) {
     clientInfo = aWorkerPrivate->GetClientInfo();
     controller = aWorkerPrivate->GetController();
   }
 
   RefPtr<ScriptLoaderRunnable> loader = new ScriptLoaderRunnable(
-      aWorkerPrivate, std::move(aOriginStack), syncLoopTarget, aLoadInfos, clientInfo,
-      controller, aIsMainScript, aWorkerScriptType, aRv);
+      aWorkerPrivate, syncLoopTarget, aLoadInfos, clientInfo, controller,
+      aIsMainScript, aWorkerScriptType, aRv);
 
   NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
 
   RefPtr<StrongWorkerRef> workerRef =
       StrongWorkerRef::Create(aWorkerPrivate, "ScriptLoader", [loader]() {
         NS_DispatchToMainThread(NewRunnableMethod(
             "ScriptLoader::CancelMainThreadWithBindingAborted", loader,
             &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted));
@@ -2235,37 +2217,32 @@ void ReportLoadError(ErrorResult& aRv, n
   }
 
   aRv.ThrowDOMException(
       aLoadResult, NS_LITERAL_CSTRING("Failed to load worker script at \"") +
                        NS_ConvertUTF16toUTF8(aScriptURL) +
                        NS_LITERAL_CSTRING("\""));
 }
 
-void LoadMainScript(WorkerPrivate* aWorkerPrivate,
-                    UniquePtr<SerializedStackHolder> aOriginStack,
-                    const nsAString& aScriptURL,
+void LoadMainScript(WorkerPrivate* aWorkerPrivate, const nsAString& aScriptURL,
                     WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
   nsTArray<ScriptLoadInfo> loadInfos;
 
   ScriptLoadInfo* info = loadInfos.AppendElement();
   info->mURL = aScriptURL;
   info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
 
   // We are loading the main script, so the worker's Client must be
   // reserved.
   info->mReservedClientInfo = aWorkerPrivate->GetClientInfo();
 
-  LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), loadInfos, true,
-                 aWorkerScriptType, aRv);
+  LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
 }
 
-void Load(WorkerPrivate* aWorkerPrivate,
-          UniquePtr<SerializedStackHolder> aOriginStack,
-          const nsTArray<nsString>& aScriptURLs,
+void Load(WorkerPrivate* aWorkerPrivate, const nsTArray<nsString>& aScriptURLs,
           WorkerScriptType aWorkerScriptType, ErrorResult& aRv) {
   const uint32_t urlCount = aScriptURLs.Length();
 
   if (!urlCount) {
     return;
   }
 
   if (urlCount > MAX_CONCURRENT_SCRIPTS) {
@@ -2276,16 +2253,15 @@ void Load(WorkerPrivate* aWorkerPrivate,
   nsTArray<ScriptLoadInfo> loadInfos;
   loadInfos.SetLength(urlCount);
 
   for (uint32_t index = 0; index < urlCount; index++) {
     loadInfos[index].mURL = aScriptURLs[index];
     loadInfos[index].mLoadFlags = aWorkerPrivate->GetLoadFlags();
   }
 
-  LoadAllScripts(aWorkerPrivate, std::move(aOriginStack), loadInfos, false,
-                 aWorkerScriptType, aRv);
+  LoadAllScripts(aWorkerPrivate, loadInfos, false, aWorkerScriptType, aRv);
 }
 
 }  // namespace workerinternals
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/workers/ScriptLoader.h
+++ b/dom/workers/ScriptLoader.h
@@ -21,17 +21,16 @@ class nsICookieSettings;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 struct WorkerLoadInfo;
 class WorkerPrivate;
-class SerializedStackHolder;
 
 enum WorkerScriptType { WorkerScript, DebuggerScript };
 
 namespace workerinternals {
 
 nsresult ChannelFromScriptURLMainThread(
     nsIPrincipal* aPrincipal, Document* aParentDoc, nsILoadGroup* aLoadGroup,
     nsIURI* aScriptURL, const Maybe<ClientInfo>& aClientInfo,
@@ -41,24 +40,20 @@ nsresult ChannelFromScriptURLMainThread(
 nsresult ChannelFromScriptURLWorkerThread(JSContext* aCx,
                                           WorkerPrivate* aParent,
                                           const nsAString& aScriptURL,
                                           WorkerLoadInfo& aLoadInfo);
 
 void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
                      const nsAString& aScriptURL);
 
-void LoadMainScript(WorkerPrivate* aWorkerPrivate,
-                    UniquePtr<SerializedStackHolder> aOriginStack,
-                    const nsAString& aScriptURL,
+void LoadMainScript(WorkerPrivate* aWorkerPrivate, const nsAString& aScriptURL,
                     WorkerScriptType aWorkerScriptType, ErrorResult& aRv);
 
-void Load(WorkerPrivate* aWorkerPrivate,
-          UniquePtr<SerializedStackHolder> aOriginStack,
-          const nsTArray<nsString>& aScriptURLs,
+void Load(WorkerPrivate* aWorkerPrivate, const nsTArray<nsString>& aScriptURLs,
           WorkerScriptType aWorkerScriptType, ErrorResult& aRv);
 
 }  // namespace workerinternals
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif /* mozilla_dom_workers_scriptloader_h__ */
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -98,18 +98,18 @@ class CompileDebuggerScriptRunnable fina
     if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
       aWorkerPrivate->EnsurePerformanceCounter();
     }
 
     JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
 
     ErrorResult rv;
     JSAutoRealm ar(aCx, global);
-    workerinternals::LoadMainScript(aWorkerPrivate, nullptr, mScriptURL,
-                                    DebuggerScript, rv);
+    workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, DebuggerScript,
+                                    rv);
     rv.WouldReportJSException();
     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
     // case, but don't throw anything on aCx.  The idea is to not dispatch error
     // events if our load is canceled with that error code.
     if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
       rv.SuppressException();
       return false;
@@ -450,17 +450,17 @@ void WorkerDebugger::ReportErrorToDebugg
   }
 
   // We need a JSContext to be able to read any stack associated with the error.
   // This will not run any scripts.
   AutoJSAPI jsapi;
   DebugOnly<bool> ok = jsapi.Init(xpc::UnprivilegedJunkScope());
   MOZ_ASSERT(ok, "UnprivilegedJunkScope should exist");
 
-  WorkerErrorReport report;
+  WorkerErrorReport report(nullptr);
   report.mMessage = aMessage;
   report.mFilename = aFilename;
   WorkerErrorReport::LogErrorToConsole(jsapi.cx(), report, 0);
 }
 
 RefPtr<PerformanceInfoPromise> WorkerDebugger::ReportPerformanceInfo() {
   AssertIsOnMainThread();
   nsCOMPtr<nsPIDOMWindowOuter> top;
--- a/dom/workers/WorkerError.cpp
+++ b/dom/workers/WorkerError.cpp
@@ -194,18 +194,30 @@ void WorkerErrorBase::AssignErrorBase(JS
   mErrorNumber = aReport->errorNumber;
 }
 
 void WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote) {
   WorkerErrorBase::AssignErrorBase(aNote);
   xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage);
 }
 
-WorkerErrorReport::WorkerErrorReport()
-    : mFlags(0), mExnType(JSEXN_ERR), mMutedError(false) {}
+WorkerErrorReport::WorkerErrorReport(WorkerPrivate* aWorkerPrivate)
+    : StructuredCloneHolder(CloningSupported, TransferringNotSupported,
+                            StructuredCloneScope::SameProcessDifferentThread),
+      mFlags(0),
+      mExnType(JSEXN_ERR),
+      mMutedError(false) {
+  if (aWorkerPrivate) {
+    RefPtr<StrongWorkerRef> workerRef =
+        StrongWorkerRef::Create(aWorkerPrivate, "WorkerErrorReport");
+    if (workerRef) {
+      mWorkerRef = new ThreadSafeWorkerRef(workerRef);
+    }
+  }
+}
 
 void WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport) {
   WorkerErrorBase::AssignErrorBase(aReport);
   xpc::ErrorReport::ErrorReportToMessageString(aReport, mMessage);
 
   mLine.Assign(aReport->linebuf(), aReport->linebufLength());
   mFlags = aReport->flags;
   MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
@@ -354,18 +366,31 @@ void WorkerErrorReport::LogErrorToConsol
                                           uint64_t aInnerWindowId) {
   nsTArray<ErrorDataNote> notes;
   for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) {
     const WorkerErrorNote& note = aReport.mNotes.ElementAt(i);
     notes.AppendElement(ErrorDataNote(note.mLineNumber, note.mColumnNumber,
                                       note.mMessage, note.mFilename));
   }
 
-  JS::RootedObject stack(aCx, aReport.ReadStack(aCx));
-  JS::RootedObject stackGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
+  // Read any stack associated with the report.
+  JS::RootedValue stackValue(aCx);
+  if (aReport.HasData() && aReport.mWorkerRef) {
+    nsIPrincipal* principal = aReport.mWorkerRef->Private()->GetPrincipal();
+    nsJSPrincipals::AutoSetActiveWorkerPrincipal set(principal);
+    aReport.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackValue,
+                 IgnoreErrors());
+  }
+  JS::RootedObject stack(aCx);
+  JS::RootedObject stackGlobal(aCx);
+  if (stackValue.isObject()) {
+    stack = &stackValue.toObject();
+    stackGlobal = JS::CurrentGlobalOrNull(aCx);
+    MOZ_ASSERT(stackGlobal);
+  }
 
   ErrorData errorData(aReport.mLineNumber, aReport.mColumnNumber,
                       aReport.mFlags, aReport.mMessage, aReport.mFilename,
                       aReport.mLine, notes);
   LogErrorToConsole(errorData, aInnerWindowId, stack, stackGlobal);
 }
 
 /* static */
--- a/dom/workers/WorkerError.h
+++ b/dom/workers/WorkerError.h
@@ -2,19 +2,19 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_WorkerError_h
 #define mozilla_dom_workers_WorkerError_h
 
-#include "mozilla/dom/SerializedStackHolder.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "jsapi.h"
+#include "WorkerRef.h"
 
 namespace mozilla {
 
 class DOMEventTargetHelper;
 
 namespace dom {
 
 class ErrorData;
@@ -33,25 +33,33 @@ class WorkerErrorBase {
 
 class WorkerErrorNote : public WorkerErrorBase {
  public:
   void AssignErrorNote(JSErrorNotes::Note* aNote);
 };
 
 class WorkerPrivate;
 
-class WorkerErrorReport : public WorkerErrorBase, public SerializedStackHolder {
+// The StructuredCloneHolder superclass is used to encode the error's stack
+// data, if there is any.
+class WorkerErrorReport : public WorkerErrorBase, public StructuredCloneHolder {
  public:
   nsString mLine;
   uint32_t mFlags;
   JSExnType mExnType;
   bool mMutedError;
   nsTArray<WorkerErrorNote> mNotes;
 
-  WorkerErrorReport();
+  // Hold a reference on the originating worker until the error has been
+  // processed.
+  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
+
+  // Create a new error report. aWorkerPrivate represents the worker where the
+  // error originated.
+  explicit WorkerErrorReport(WorkerPrivate* aWorkerPrivate);
 
   void AssignErrorReport(JSErrorReport* aReport);
 
   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
   // aTarget is the worker object that we are going to fire an error at
   // (if any).
   static void ReportError(
       JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aFireAtScope,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -297,24 +297,22 @@ class ModifyBusyCountRunnable final : pu
     }
     // Don't do anything here as it's possible that aWorkerPrivate has been
     // deleted.
   }
 };
 
 class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
   nsString mScriptURL;
-  UniquePtr<SerializedStackHolder> mOriginStack;
 
  public:
   explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
-                                 UniquePtr<SerializedStackHolder> aOriginStack,
                                  const nsAString& aScriptURL)
       : WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-        mScriptURL(aScriptURL), mOriginStack(aOriginStack.release()) {}
+        mScriptURL(aScriptURL) {}
 
  private:
   // We can't implement PreRun effectively, because at the point when that would
   // run we have not yet done our load so don't know things like our final
   // principal and whatnot.
 
   virtual bool WorkerRun(JSContext* aCx,
                          WorkerPrivate* aWorkerPrivate) override {
@@ -334,18 +332,18 @@ class CompileScriptRunnable final : publ
     // content loading.
     aWorkerPrivate->EnsurePerformanceStorage();
 
     if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
       aWorkerPrivate->EnsurePerformanceCounter();
     }
 
     ErrorResult rv;
-    workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
-                                    mScriptURL, WorkerScript, rv);
+    workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript,
+                                    rv);
     rv.WouldReportJSException();
     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
     // case, but don't throw anything on aCx.  The idea is to not dispatch error
     // events if our load is canceled with that error code.
     if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
       rv.SuppressException();
       return false;
@@ -2253,20 +2251,18 @@ already_AddRefed<WorkerPrivate> WorkerPr
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   worker->EnableDebugger();
 
   MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
 
-  UniquePtr<SerializedStackHolder> stack = GetCurrentStackForNetMonitor(aCx);
-
   RefPtr<CompileScriptRunnable> compiler =
-      new CompileScriptRunnable(worker, std::move(stack), aScriptURL);
+      new CompileScriptRunnable(worker, aScriptURL);
   if (!compiler->Dispatch()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   worker->mSelfRef = worker;
 
   return worker.forget();
@@ -4052,29 +4048,30 @@ void WorkerPrivate::ReportError(JSContex
   if (!JS_GetPendingException(aCx, &exn)) {
     // Probably shouldn't actually happen?  But let's go ahead and just use null
     // for lack of anything better.
     exn.setNull();
   }
   JS::RootedObject exnStack(aCx, JS::GetPendingExceptionStack(aCx));
   JS_ClearPendingException(aCx);
 
-  UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>();
+  UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>(this);
   if (aReport) {
     report->AssignErrorReport(aReport);
   } else {
     report->mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
   }
 
   JS::RootedObject stack(aCx), stackGlobal(aCx);
   xpc::FindExceptionStackForConsoleReport(nullptr, exn, exnStack, &stack,
                                           &stackGlobal);
 
   if (stack) {
-    report->SerializeWorkerStack(aCx, this, stack);
+    JS::RootedValue stackValue(aCx, JS::ObjectValue(*stack));
+    report->Write(aCx, stackValue, IgnoreErrors());
   }
 
   if (report->mMessage.IsEmpty() && aToStringResult) {
     nsDependentCString toStringResult(aToStringResult.c_str());
     if (!AppendUTF8toUTF16(toStringResult, report->mMessage,
                            mozilla::fallible)) {
       // Try again, with only a 1 KB string. Do this infallibly this time.
       // If the user doesn't have 1 KB to spare we're done anyways.
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -230,25 +230,20 @@ void WorkerGlobalScope::SetOnerror(OnErr
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   EventListenerManager* elm = GetOrCreateListenerManager();
   if (elm) {
     elm->SetEventHandler(aHandler);
   }
 }
 
-void WorkerGlobalScope::ImportScripts(JSContext* aCx,
-                                      const Sequence<nsString>& aScriptURLs,
+void WorkerGlobalScope::ImportScripts(const Sequence<nsString>& aScriptURLs,
                                       ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
-
-  UniquePtr<SerializedStackHolder> stack = GetCurrentStackForNetMonitor(aCx);
-
-  workerinternals::Load(mWorkerPrivate, std::move(stack), aScriptURLs,
-                        WorkerScript, aRv);
+  workerinternals::Load(mWorkerPrivate, aScriptURLs, WorkerScript, aRv);
 }
 
 int32_t WorkerGlobalScope::SetTimeout(JSContext* aCx, Function& aHandler,
                                       const int32_t aTimeout,
                                       const Sequence<JS::Value>& aArguments,
                                       ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
@@ -906,17 +901,17 @@ void WorkerDebuggerGlobalScope::LoadSubS
       return;
     }
 
     ar.emplace(aCx, sandbox);
   }
 
   nsTArray<nsString> urls;
   urls.AppendElement(aURL);
-  workerinternals::Load(mWorkerPrivate, nullptr, urls, DebuggerScript, aRv);
+  workerinternals::Load(mWorkerPrivate, urls, DebuggerScript, aRv);
 }
 
 void WorkerDebuggerGlobalScope::EnterEventLoop() {
   // We're on the worker thread here, and WorkerPrivate's refcounting is
   // non-threadsafe: you can only do it on the parent thread.  What that
   // means in practice is that we're relying on it being kept alive while
   // we run.  Hopefully.
   MOZ_KnownLive(mWorkerPrivate)->EnterDebuggerEventLoop();
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -97,18 +97,17 @@ class WorkerGlobalScope : public DOMEven
 
   already_AddRefed<WorkerNavigator> Navigator();
 
   already_AddRefed<WorkerNavigator> GetExistingNavigator() const;
 
   OnErrorEventHandlerNonNull* GetOnerror();
   void SetOnerror(OnErrorEventHandlerNonNull* aHandler);
 
-  void ImportScripts(JSContext* aCx, const Sequence<nsString>& aScriptURLs,
-                     ErrorResult& aRv);
+  void ImportScripts(const Sequence<nsString>& aScriptURLs, ErrorResult& aRv);
 
   int32_t SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout,
                      const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      const int32_t aTimeout,
                      const Sequence<JS::Value>& /* unused */, ErrorResult& aRv);
   void ClearTimeout(int32_t aHandle);
   int32_t SetInterval(JSContext* aCx, Function& aHandler,
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -21,17 +21,16 @@
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/FormData.h"
 #include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "mozilla/dom/URLSearchParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
-#include "mozilla/dom/WorkerError.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/ProgressEvent.h"
@@ -2629,19 +2628,16 @@ nsresult XMLHttpRequestMainThread::Initi
   nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
 
   // Check if this XHR is created from a tracking script.
   // If yes, lower the channel's priority.
   if (StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
     MaybeLowerChannelPriority();
   }
 
-  // Associate any originating stack with the channel.
-  NotifyNetworkMonitorAlternateStack(mChannel, std::move(mOriginStack));
-
   // Start reading from the channel
   rv = mChannel->AsyncOpen(listener);
   listener = nullptr;
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // Drop our ref to the channel to avoid cycles. Also drop channel's
     // ref to us to be extra safe.
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
@@ -3137,21 +3133,16 @@ nsresult XMLHttpRequestMainThread::SetMo
 }
 
 void XMLHttpRequestMainThread::SetMozBackgroundRequest(
     bool aMozBackgroundRequest, ErrorResult& aRv) {
   // No errors for this webIDL method on main-thread.
   SetMozBackgroundRequest(aMozBackgroundRequest);
 }
 
-void XMLHttpRequestMainThread::SetOriginStack(
-    UniquePtr<SerializedStackHolder> aOriginStack) {
-  mOriginStack = std::move(aOriginStack);
-}
-
 bool XMLHttpRequestMainThread::WithCredentials() const {
   return mFlagACwithCredentials;
 }
 
 void XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials,
                                                   ErrorResult& aRv) {
   NOT_CALLABLE_IN_SYNC_SEND_RV
 
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -61,17 +61,16 @@ typedef __StatusTmp Status;
 class nsIJARChannel;
 class nsILoadGroup;
 
 namespace mozilla {
 namespace dom {
 
 class DOMString;
 class XMLHttpRequestUpload;
-class SerializedStackHolder;
 struct OriginAttributesDictionary;
 
 // A helper for building up an ArrayBuffer object's data
 // before creating the ArrayBuffer itself.  Will do doubling
 // based reallocation, up to an optional maximum growth given.
 //
 // When all the data has been appended, call getArrayBuffer,
 // passing in the JSContext* for which the ArrayBuffer object
@@ -388,18 +387,16 @@ class XMLHttpRequestMainThread final : p
 
   virtual bool MozBackgroundRequest() const override;
 
   nsresult SetMozBackgroundRequest(bool aMozBackgroundRequest);
 
   virtual void SetMozBackgroundRequest(bool aMozBackgroundRequest,
                                        ErrorResult& aRv) override;
 
-  void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack);
-
   virtual uint16_t ErrorCode() const override {
     return static_cast<uint16_t>(mErrorLoad);
   }
 
   virtual bool MozAnon() const override;
 
   virtual bool MozSystem() const override;
 
@@ -722,20 +719,16 @@ class XMLHttpRequestMainThread final : p
   bool mEofDecoded;
 
   // Our parse-end listener, if we are parsing.
   RefPtr<nsXHRParseEndListener> mParseEndListener;
 
   RefPtr<XMLHttpRequestDoneNotifier> mDelayedDoneNotifier;
   void DisconnectDoneNotifier();
 
-  // Any stack information for the point the XHR was opened. This is deleted
-  // after the XHR is opened, to avoid retaining references to the worker.
-  UniquePtr<SerializedStackHolder> mOriginStack;
-
   static bool sDontWarnAboutSyncXHR;
 };
 
 class MOZ_STACK_CLASS AutoDontWarnAboutSyncXHR {
  public:
   AutoDontWarnAboutSyncXHR()
       : mOldVal(XMLHttpRequestMainThread::DontWarnAboutSyncXHR()) {
     XMLHttpRequestMainThread::SetDontWarnAboutSyncXHR(true);
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -663,38 +663,32 @@ class OpenRunnable final : public Worker
   Optional<nsAString> mPassword;
   nsString mPasswordStr;
   bool mBackgroundRequest;
   bool mWithCredentials;
   uint32_t mTimeout;
   XMLHttpRequestResponseType mResponseType;
   const nsString mMimeTypeOverride;
 
-  // Remember the worker thread's stack when the XHR was opened, so that it can
-  // be passed on to the net monitor.
-  UniquePtr<SerializedStackHolder> mOriginStack;
-
  public:
   OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
                const nsACString& aMethod, const nsAString& aURL,
                const Optional<nsAString>& aUser,
                const Optional<nsAString>& aPassword, bool aBackgroundRequest,
                bool aWithCredentials, uint32_t aTimeout,
                XMLHttpRequestResponseType aResponseType,
-               const nsString& aMimeTypeOverride,
-               UniquePtr<SerializedStackHolder> aOriginStack)
+               const nsString& aMimeTypeOverride)
       : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
         mMethod(aMethod),
         mURL(aURL),
         mBackgroundRequest(aBackgroundRequest),
         mWithCredentials(aWithCredentials),
         mTimeout(aTimeout),
         mResponseType(aResponseType),
-        mMimeTypeOverride(aMimeTypeOverride),
-        mOriginStack(std::move(aOriginStack)) {
+        mMimeTypeOverride(aMimeTypeOverride) {
     if (aUser.WasPassed()) {
       mUserStr = aUser.Value();
       mUser = &mUserStr;
     }
     if (aPassword.WasPassed()) {
       mPasswordStr = aPassword.Value();
       mPassword = &mPasswordStr;
     }
@@ -1277,20 +1271,16 @@ nsresult OpenRunnable::MainThreadRunInte
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   if (mBackgroundRequest) {
     nsresult rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (mOriginStack) {
-    mProxy->mXHR->SetOriginStack(std::move(mOriginStack));
-  }
-
   ErrorResult rv;
 
   if (mWithCredentials) {
     mProxy->mXHR->SetWithCredentials(mWithCredentials, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
   }
@@ -1782,27 +1772,20 @@ void XMLHttpRequestWorker::Open(const ns
     }
     mProxy = new Proxy(this, clientInfo.ref(), mWorkerPrivate->GetController(),
                        mMozAnon, mMozSystem);
     alsoOverrideMimeType = true;
   }
 
   mProxy->mOuterEventStreamId++;
 
-  UniquePtr<SerializedStackHolder> stack;
-  if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
-    stack = GetCurrentStackForNetMonitor(cx);
-  }
-
   RefPtr<OpenRunnable> runnable = new OpenRunnable(
       mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
       mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
-      alsoOverrideMimeType ? mMimeTypeOverride : VoidString(),
-      std::move(stack));
-
+      alsoOverrideMimeType ? mMimeTypeOverride : VoidString());
   ++mProxy->mOpenCount;
   runnable->Dispatch(Canceling, aRv);
   if (aRv.Failed()) {
     if (mProxy && !--mProxy->mOpenCount) {
       ReleaseProxy();
     }
 
     return;
--- a/js/public/SavedFrameAPI.h
+++ b/js/public/SavedFrameAPI.h
@@ -123,27 +123,16 @@ extern JS_PUBLIC_API SavedFrameResult Ge
  * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
  * guaranteed to be in the cx's compartment. Defaults to nullptr.
  */
 extern JS_PUBLIC_API SavedFrameResult GetSavedFrameParent(
     JSContext* cx, JSPrincipals* principals, Handle<JSObject*> savedFrame,
     MutableHandle<JSObject*> parentp,
     SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
-/**
- * Given a SavedFrame object, convert it and its transitive parents to plain
- * objects. Because SavedFrame objects store their properties on the prototype,
- * they cannot be usefully stringified to JSON. Assigning their properties to
- * plain objects allow those objects to be stringified and the saved frame stack
- * can be encoded as a string.
- */
-JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject(
-    JSContext* cx, JS::HandleObject savedFrame,
-    JS::SavedFrameSelfHosted selfHosted);
-
 }  // namespace JS
 
 namespace js {
 
 /**
  * Get the first SavedFrame object in this SavedFrame stack whose principals are
  * subsumed by the given |principals|. If there is no such frame, return
  * nullptr.
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1083,71 +1083,16 @@ JS_PUBLIC_API bool IsMaybeWrappedSavedFr
   return obj->canUnwrapAs<js::SavedFrame>();
 }
 
 JS_PUBLIC_API bool IsUnwrappedSavedFrame(JSObject* obj) {
   MOZ_ASSERT(obj);
   return obj->is<js::SavedFrame>();
 }
 
-static bool AssignProperty(JSContext* cx, HandleObject dst, HandleObject src,
-                           const char* property) {
-  RootedValue v(cx);
-  return JS_GetProperty(cx, src, property, &v) &&
-         JS_DefineProperty(cx, dst, property, v, JSPROP_ENUMERATE);
-}
-
-JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject
-    (JSContext* cx, HandleObject savedFrameArg, SavedFrameSelfHosted selfHosted) {
-  MOZ_ASSERT(savedFrameArg);
-
-  RootedObject savedFrame(cx, savedFrameArg);
-  RootedObject baseConverted(cx), lastConverted(cx);
-  RootedValue v(cx);
-
-  baseConverted = lastConverted = JS_NewObject(cx, nullptr);
-  if (!baseConverted) {
-    return nullptr;
-  }
-
-  bool foundParent;
-  do {
-    if (!AssignProperty(cx, lastConverted, savedFrame, "source") ||
-        !AssignProperty(cx, lastConverted, savedFrame, "sourceId") ||
-        !AssignProperty(cx, lastConverted, savedFrame, "line") ||
-        !AssignProperty(cx, lastConverted, savedFrame, "column") ||
-        !AssignProperty(cx, lastConverted, savedFrame, "functionDisplayName") ||
-        !AssignProperty(cx, lastConverted, savedFrame, "asyncCause")) {
-      return nullptr;
-    }
-
-    const char* parentProperties[] = { "parent", "asyncParent" };
-    foundParent = false;
-    for (const char* prop : parentProperties) {
-      if (!JS_GetProperty(cx, savedFrame, prop, &v)) {
-        return nullptr;
-      }
-      if (v.isObject()) {
-        RootedObject nextConverted(cx, JS_NewObject(cx, nullptr));
-        if (!nextConverted ||
-            !JS_DefineProperty(cx, lastConverted, prop, nextConverted,
-                               JSPROP_ENUMERATE)) {
-          return nullptr;
-        }
-        lastConverted = nextConverted;
-        savedFrame = &v.toObject();
-        foundParent = true;
-        break;
-      }
-    }
-  } while (foundParent);
-
-  return baseConverted;
-}
-
 } /* namespace JS */
 
 namespace js {
 
 /* static */
 bool SavedFrame::sourceProperty(JSContext* cx, unsigned argc, Value* vp) {
   THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
   JSPrincipals* principals = cx->realm()->principals();