Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
Brindusan Cristian 2019-04-17 12:49:37 +03:00
Родитель 7b41d44de7 ee3905439a
Коммит 19b5d2e16c
29 изменённых файлов: 409 добавлений и 211 удалений

Просмотреть файл

@ -1436,9 +1436,6 @@ var gMainPane = {
handleEvent(aEvent) {
if (aEvent.type == "unload") {
this.destroy();
if (AppConstants.MOZ_UPDATER) {
onUnload();
}
}
},

Просмотреть файл

@ -114,7 +114,8 @@ bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
if (!(tag == SCTAG_DOM_NULL_PRINCIPAL || tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
tag == SCTAG_DOM_EXPANDED_PRINCIPAL)) {
tag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
tag == SCTAG_DOM_WORKER_PRINCIPAL)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
@ -286,6 +287,19 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
return true;
}
static StaticRefPtr<nsIPrincipal> sActiveWorkerPrincipal;
nsJSPrincipals::AutoSetActiveWorkerPrincipal::AutoSetActiveWorkerPrincipal
(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(!sActiveWorkerPrincipal);
sActiveWorkerPrincipal = aPrincipal;
}
nsJSPrincipals::AutoSetActiveWorkerPrincipal::~AutoSetActiveWorkerPrincipal() {
sActiveWorkerPrincipal = nullptr;
}
/* static */
bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
JSStructuredCloneReader* aReader,
@ -294,13 +308,26 @@ bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
aTag == SCTAG_DOM_EXPANDED_PRINCIPAL);
aTag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
aTag == SCTAG_DOM_WORKER_PRINCIPAL);
if (NS_WARN_IF(!NS_IsMainThread())) {
xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
return false;
}
if (aTag == SCTAG_DOM_WORKER_PRINCIPAL) {
// When reading principals which were written on a worker thread, we need to
// know the principal of the worker which did the write.
if (!sActiveWorkerPrincipal) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
RefPtr<nsJSPrincipals> retval = get(sActiveWorkerPrincipal);
retval.forget(aOutPrincipals);
return true;
}
PrincipalInfo info;
if (!ReadPrincipalInfo(aReader, aTag, info)) {
return false;

Просмотреть файл

@ -24,6 +24,17 @@ class nsJSPrincipals : public nsIPrincipal, public JSPrincipals {
uint32_t aTag,
JSPrincipals** aOutPrincipals);
// This class is used on the main thread to specify which principal to use
// when reading principals data that was set on a DOM worker thread.
// DOM workers do not use principals from Gecko's point of view, and any
// JSPrincipals used internally will be a shared singleton object. When that
// singleton is written out and later read on the main thread, we substitute
// the principal specified with this class.
struct MOZ_RAII AutoSetActiveWorkerPrincipal {
explicit AutoSetActiveWorkerPrincipal(nsIPrincipal* aPrincipal);
~AutoSetActiveWorkerPrincipal();
};
bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) final;
/*

Просмотреть файл

@ -37,7 +37,7 @@ export function removeEditor() {
}
function getCodeMirror() {
return editor && editor.codeMirror;
return editor && editor.hasCodeMirror ? editor.codeMirror : null;
}
export function startOperation() {

Просмотреть файл

@ -9,6 +9,9 @@ import { isDevelopment } from "devtools-environment";
import Services from "devtools-services";
import { asyncStoreHelper } from "./asyncStoreHelper";
// Schema version to bump when the async store format has changed incompatibly
// and old stores should be cleared. This needs to match the prefs schema
// version in devtools/client/preferences/debugger.js.
const prefsSchemaVersion = "1.0.9";
const pref = Services.pref;
@ -43,7 +46,7 @@ if (isDevelopment()) {
pref("devtools.debugger.file-search-regex-match", false);
pref("devtools.debugger.project-directory-root", "");
pref("devtools.debugger.map-scopes-enabled", false);
pref("devtools.debugger.prefs-schema-version", "1.0.1");
pref("devtools.debugger.prefs-schema-version", prefsSchemaVersion);
pref("devtools.debugger.skip-pausing", false);
pref("devtools.debugger.features.workers", true);
pref("devtools.debugger.features.async-stepping", true);

Просмотреть файл

@ -789,3 +789,4 @@ skip-if = true
[browser_dbg-event-handler.js]
[browser_dbg-eval-throw.js]
[browser_dbg-sourceURL-breakpoint.js]
[browser_dbg-old-breakpoint.js]

Просмотреть файл

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that we show a breakpoint in the UI when there is an old pending
// breakpoint with an invalid original location.
add_task(async function() {
clearDebuggerPreferences();
const pending = {
bp1: {
location: {
sourceId: "",
sourceUrl: EXAMPLE_URL + "nowhere2.js",
line: 5
},
generatedLocation: {
sourceUrl: EXAMPLE_URL + "simple1.js",
line: 4
},
options: {},
disabled: false
},
bp2: {
location: {
sourceId: "",
sourceUrl: EXAMPLE_URL + "nowhere.js",
line: 5
},
generatedLocation: {
sourceUrl: EXAMPLE_URL + "simple3.js",
line: 2
},
options: {},
disabled: false
},
};
asyncStorage.setItem("debugger.pending-breakpoints", pending);
const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + "doc-scripts.html", "jsdebugger");
const dbg = createDebuggerContext(toolbox);
// Pending breakpoints are installed asynchronously, keep invoking the entry
// function until the debugger pauses.
await waitUntil(() => {
invokeInTab("main");
return isPaused(dbg);
});
ok(true, "paused at unmapped breakpoint");
await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 2);
ok(true, "unmapped breakpoints shown in UI");
});
// Test that if we show a breakpoint with an old generated location, it is
// removed after we load the original source and find the new generated
// location.
add_task(async function() {
clearDebuggerPreferences();
const pending = {
bp1: {
location: {
sourceId: "",
sourceUrl: "webpack:///entry.js",
line: 15,
column: 0
},
generatedLocation: {
sourceUrl: EXAMPLE_URL + "sourcemaps/bundle.js",
line: 47,
column: 16
},
astLocation: {},
options: {},
disabled: false
},
};
asyncStorage.setItem("debugger.pending-breakpoints", pending);
const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + "doc-sourcemaps.html", "jsdebugger");
const dbg = createDebuggerContext(toolbox);
await waitForState(dbg, state => {
const bps = dbg.selectors.getBreakpointsList(state);
return bps.length == 1
&& bps[0].location.sourceUrl.includes("entry.js")
&& bps[0].location.line == 15;
});
ok(true, "removed old breakpoint during sync");
});

Просмотреть файл

@ -19,7 +19,8 @@ pref("devtools.debugger.auto-black-box", true);
pref("devtools.debugger.workers", false);
// The default Debugger UI settings
pref("devtools.debugger.prefs-schema-version", "1.0.0");
// This schema version needs to match that in devtools/client/debugger/src/utils/prefs.js.
pref("devtools.debugger.prefs-schema-version", "1.0.9");
pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
pref("devtools.debugger.ui.panes-instruments-width", 300);
pref("devtools.debugger.ui.panes-visible-on-startup", false);

Просмотреть файл

@ -262,6 +262,13 @@ Editor.prototype = {
return editors.get(this);
},
/**
* Return whether there is a CodeMirror instance associated with this Editor.
*/
get hasCodeMirror() {
return editors.has(this);
},
/**
* Appends the current Editor instance to the element specified by
* 'el'. You can also provide your own iframe to host the editor as

Просмотреть файл

@ -205,7 +205,7 @@ stubPreparedMessages.set(`throw ""`, new ConsoleMessage({
"category": "content javascript",
"messageText": "uncaught exception: ",
"parameters": null,
"repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: \",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source24\",\"lineNumber\":1,\"columnNumber\":1,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":null,\"line\":1,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: \",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source24\",\"lineNumber\":1,\"columnNumber\":1,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@ -229,7 +229,12 @@ stubPreparedMessages.set(`throw ""`, new ConsoleMessage({
"functionName": null
}
],
"frame": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
"sourceId": null,
"line": 1,
"column": 1
},
"groupId": null,
"errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
"userProvidedStyles": null,
@ -252,7 +257,7 @@ stubPreparedMessages.set(`throw "tomato"`, new ConsoleMessage({
"category": "content javascript",
"messageText": "uncaught exception: tomato",
"parameters": null,
"repeatId": "{\"frame\":null,\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: tomato\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source24\",\"lineNumber\":1,\"columnNumber\":1,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
"repeatId": "{\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":null,\"line\":1,\"column\":1},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"uncaught exception: tomato\",\"parameters\":null,\"source\":\"javascript\",\"type\":\"log\",\"userProvidedStyles\":null,\"private\":false,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html\",\"sourceId\":\"server1.conn0.child1/source24\",\"lineNumber\":1,\"columnNumber\":1,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 59 > eval\",\"sourceId\":null,\"lineNumber\":7,\"columnNumber\":31,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"sourceId\":null,\"lineNumber\":60,\"columnNumber\":29,\"functionName\":null}]}",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
@ -276,7 +281,12 @@ stubPreparedMessages.set(`throw "tomato"`, new ConsoleMessage({
"functionName": null
}
],
"frame": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
"sourceId": null,
"line": 1,
"column": 1
},
"groupId": null,
"errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
"userProvidedStyles": null,
@ -459,11 +469,11 @@ stubPackets.set(`throw ""`, {
"pageError": {
"errorMessage": "uncaught exception: ",
"errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
"sourceName": "",
"sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
"sourceId": null,
"lineText": "",
"lineNumber": 0,
"columnNumber": 0,
"lineNumber": 1,
"columnNumber": 1,
"category": "content javascript",
"innerWindowID": 6442450949,
"timeStamp": 1517942398629,
@ -507,11 +517,11 @@ stubPackets.set(`throw "tomato"`, {
"pageError": {
"errorMessage": "uncaught exception: tomato",
"errorMessageName": "JSMSG_UNCAUGHT_EXCEPTION",
"sourceName": "",
"sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
"sourceId": null,
"lineText": "",
"lineNumber": 0,
"columnNumber": 0,
"lineNumber": 1,
"columnNumber": 1,
"category": "content javascript",
"innerWindowID": 6442450949,
"timeStamp": 1517942398637,

Просмотреть файл

@ -45,6 +45,9 @@ support-files =
test-dynamic-import.html
test-dynamic-import.js
test-error.html
test-error-worker.html
test-error-worker.js
test-error-worker2.js
test-eval-in-stackframe.html
test-eval-sources.html
test-external-script-errors.html
@ -408,3 +411,4 @@ tags = trackingprotection
[browser_webconsole_warning_groups_outside_console_group.js]
[browser_webconsole_warning_groups.js]
[browser_webconsole_websocket.js]
[browser_webconsole_worker_error.js]

Просмотреть файл

@ -15,19 +15,6 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
add_task(async function() {
const hud = await openNewTabAndConsole(TEST_URI);
await checkMessage("hello", 14, 3);
await checkMessage("1,2,3", 20, 1);
async function checkMessage(text, line, numFrames) {
const msgNode = await waitFor(() => findMessage(hud, text));
ok(!msgNode.classList.contains("open"), `Error logged not expanded`);
const button = msgNode.querySelector(".collapse-button");
button.click();
const framesNode = await waitFor(() => msgNode.querySelector(".frames"));
const frameNodes = framesNode.querySelectorAll(".frame");
ok(frameNodes.length == numFrames);
ok(frameNodes[0].querySelector(".line").textContent == "" + line);
}
await checkMessageStack(hud, "hello", [14, 10, 7]);
await checkMessageStack(hud, "1,2,3", [20]);
});

Просмотреть файл

@ -0,0 +1,21 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that throwing uncaught errors and primitive values in workers shows a
// stack in the console.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/mochitest/test-error-worker.html";
add_task(async function() {
const hud = await openNewTabAndConsole(TEST_URI);
await checkMessageStack(hud, "hello", [11, 3]);
await checkMessageStack(hud, "there", [14, 3]);
await checkMessageStack(hud, "dom", [16, 3]);
await checkMessageStack(hud, "worker2", [6, 3]);
});

Просмотреть файл

@ -1320,3 +1320,28 @@ function checkConsoleOutputForWarningGroup(hud, expectedMessages) {
`the expected "${expectedMessage}" content - "${message.textContent.trim()}"`);
});
}
/**
* Check that there is a message with the specified text that has the specified
* stack information.
* @param {WebConsole} hud
* @param {string} text
* message substring to look for
* @param {Array<number>} frameLines
* line numbers of the frames expected in the stack
*/
async function checkMessageStack(hud, text, frameLines) {
const msgNode = await waitFor(() => findMessage(hud, text));
ok(!msgNode.classList.contains("open"), `Error logged not expanded`);
const button = msgNode.querySelector(".collapse-button");
button.click();
const framesNode = await waitFor(() => msgNode.querySelector(".frames"));
const frameNodes = framesNode.querySelectorAll(".frame");
ok(frameNodes.length == frameLines.length, `Found ${frameLines.length} frames`);
for (let i = 0; i < frameLines.length; i++) {
ok(frameNodes[i].querySelector(".line").textContent == "" + frameLines[i],
`Found line ${frameLines[i]} for frame #${i}`);
}
}

Просмотреть файл

@ -0,0 +1,7 @@
<script>
"use strict";
var w = new Worker("test-error-worker.js");
w.postMessage(1);
w.postMessage(2);
w.postMessage(3);
</script>

Просмотреть файл

@ -0,0 +1,18 @@
"use strict";
self.addEventListener("message", ({ data }) => foo(data));
var w = new Worker("test-error-worker2.js");
w.postMessage({});
function foo(data) {
switch (data) {
case 1:
throw new Error("hello");
case 2:
/* eslint-disable */
throw "there";
case 3:
throw new DOMException("dom");
}
}

Просмотреть файл

@ -0,0 +1,7 @@
"use strict";
self.addEventListener("message", ({ data }) => foo(data));
function foo(data) {
throw new Error("worker2");
}

Просмотреть файл

@ -1515,15 +1515,30 @@ WebConsoleActor.prototype =
}
}
// If there is no location information in the error but we have a stack,
// fill in the location with the first frame on the stack.
let {
sourceName,
sourceId,
lineNumber,
columnNumber,
} = pageError;
if (!sourceName && !sourceId && !lineNumber && !columnNumber && stack) {
sourceName = stack[0].filename;
sourceId = stack[0].sourceId;
lineNumber = stack[0].lineNumber;
columnNumber = stack[0].columnNumber;
}
return {
errorMessage: this._createStringGrip(pageError.errorMessage),
errorMessageName: pageError.errorMessageName,
exceptionDocURL: ErrorDocs.GetURL(pageError),
sourceName: pageError.sourceName,
sourceId: this.getActorIdForInternalSourceId(pageError.sourceId),
lineText: lineText,
lineNumber: pageError.lineNumber,
columnNumber: pageError.columnNumber,
sourceName,
sourceId: this.getActorIdForInternalSourceId(sourceId),
lineText,
lineNumber,
columnNumber,
category: pageError.category,
innerWindowID: pageError.innerWindowID,
timeStamp: pageError.timeStamp,

Просмотреть файл

@ -72,7 +72,12 @@ enum StructuredCloneTags {
SCTAG_DOM_MAX,
SCTAG_DOM_STRUCTURED_CLONE_TESTER
SCTAG_DOM_STRUCTURED_CLONE_TESTER,
// Principal written out by worker threads when serializing objects. When
// reading on the main thread this principal will be converted to a normal
// principal object using nsJSPrincipals::AutoSetActiveWorkerPrincipal.
SCTAG_DOM_WORKER_PRINCIPAL
};
} // namespace dom

Просмотреть файл

@ -7687,7 +7687,7 @@ nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType,
}
}
MOZ_DIAGNOSTIC_ASSERT(usage >= 0);
MOZ_ASSERT(usage >= 0);
InitUsageForOrigin(aOrigin, usage);

Просмотреть файл

@ -528,7 +528,7 @@ void AutoJSAPI::ReportException() {
// because it may want to put it in its error events and has no other way
// to get hold of it. After we invoke ReportError, clear the exception on
// cx(), just in case ReportError didn't.
JS_SetPendingException(cx(), exn);
JS::SetPendingExceptionAndStack(cx(), exn, exnStack);
worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report());
ClearException();
}

Просмотреть файл

@ -8,14 +8,14 @@
#include "jsapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/StructuredCloneTags.h"
namespace mozilla {
namespace dom {
struct WorkerPrincipal final : public JSPrincipals {
bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override {
MOZ_CRASH("WorkerPrincipal::write not implemented");
return false;
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WORKER_PRINCIPAL, 0);
}
};

Просмотреть файл

@ -437,10 +437,16 @@ void WorkerDebugger::ReportErrorToDebuggerOnMainThread(
listeners[index]->OnError(aFilename, aLineno, aMessage);
}
WorkerErrorReport report;
// 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(nullptr);
report.mMessage = aMessage;
report.mFilename = aFilename;
WorkerErrorReport::LogErrorToConsole(report, 0);
WorkerErrorReport::LogErrorToConsole(jsapi.cx(), report, 0);
}
RefPtr<PerformanceInfoPromise> WorkerDebugger::ReportPerformanceInfo() {

Просмотреть файл

@ -28,135 +28,12 @@ namespace dom {
namespace {
class ReportErrorRunnable final : public WorkerDebuggeeRunnable {
WorkerErrorReport mReport;
UniquePtr<WorkerErrorReport> mReport;
public:
// 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,
DOMEventTargetHelper* aTarget, const WorkerErrorReport& aReport,
uint64_t aInnerWindowId,
JS::Handle<JS::Value> aException = JS::NullHandleValue) {
if (aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
} else {
AssertIsOnMainThread();
}
// We should not fire error events for warnings but instead make sure that
// they show up in the error console.
if (!JSREPORT_IS_WARNING(aReport.mFlags)) {
// First fire an ErrorEvent at the worker.
RootedDictionary<ErrorEventInit> init(aCx);
if (aReport.mMutedError) {
init.mMessage.AssignLiteral("Script error.");
} else {
init.mMessage = aReport.mMessage;
init.mFilename = aReport.mFilename;
init.mLineno = aReport.mLineNumber;
init.mError = aException;
}
init.mCancelable = true;
init.mBubbles = false;
if (aTarget) {
RefPtr<ErrorEvent> event =
ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
event->SetTrusted(true);
bool defaultActionEnabled =
aTarget->DispatchEvent(*event, CallerType::System, IgnoreErrors());
if (!defaultActionEnabled) {
return;
}
}
// Now fire an event at the global object, but don't do that if the error
// code is too much recursion and this is the same script threw the error.
// XXXbz the interaction of this with worker errors seems kinda broken.
// An overrecursion in the debugger or debugger sandbox will get turned
// into an error event on our parent worker!
// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this
// better.
if (aFireAtScope &&
(aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) {
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
NS_ASSERTION(global, "This should never be null!");
nsEventStatus status = nsEventStatus_eIgnore;
if (aWorkerPrivate) {
WorkerGlobalScope* globalScope = nullptr;
UNWRAP_OBJECT(WorkerGlobalScope, &global, globalScope);
if (!globalScope) {
WorkerDebuggerGlobalScope* globalScope = nullptr;
UNWRAP_OBJECT(WorkerDebuggerGlobalScope, &global, globalScope);
MOZ_ASSERT_IF(globalScope,
globalScope->GetWrapperPreserveColor() == global);
if (globalScope || IsWorkerDebuggerSandbox(global)) {
aWorkerPrivate->ReportErrorToDebugger(
aReport.mFilename, aReport.mLineNumber, aReport.mMessage);
return;
}
MOZ_ASSERT(SimpleGlobalObject::SimpleGlobalType(global) ==
SimpleGlobalObject::GlobalType::BindingDetail);
// XXXbz We should really log this to console, but unwinding out of
// this stuff without ending up firing any events is ... hard. Just
// return for now.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks
// making this better.
return;
}
MOZ_ASSERT(globalScope->GetWrapperPreserveColor() == global);
RefPtr<ErrorEvent> event = ErrorEvent::Constructor(
aTarget, NS_LITERAL_STRING("error"), init);
event->SetTrusted(true);
if (NS_FAILED(EventDispatcher::DispatchDOMEvent(
ToSupports(globalScope), nullptr, event, nullptr, &status))) {
NS_WARNING("Failed to dispatch worker thread error event!");
status = nsEventStatus_eIgnore;
}
} else if (nsGlobalWindowInner* win = xpc::WindowOrNull(global)) {
MOZ_ASSERT(NS_IsMainThread());
if (!win->HandleScriptError(init, &status)) {
NS_WARNING("Failed to dispatch main thread error event!");
status = nsEventStatus_eIgnore;
}
}
// Was preventDefault() called?
if (status == nsEventStatus_eConsumeNoDefault) {
return;
}
}
}
// Now fire a runnable to do the same on the parent's thread if we can.
if (aWorkerPrivate) {
RefPtr<ReportErrorRunnable> runnable =
new ReportErrorRunnable(aWorkerPrivate, aReport);
runnable->Dispatch();
return;
}
// Otherwise log an error to the error console.
WorkerErrorReport::LogErrorToConsole(aReport, aInnerWindowId);
}
ReportErrorRunnable(WorkerPrivate* aWorkerPrivate,
const WorkerErrorReport& aReport)
: WorkerDebuggeeRunnable(aWorkerPrivate), mReport(aReport) {}
UniquePtr<WorkerErrorReport> aReport)
: WorkerDebuggeeRunnable(aWorkerPrivate), mReport(std::move(aReport)) {}
private:
virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
@ -194,7 +71,7 @@ class ReportErrorRunnable final : public WorkerDebuggeeRunnable {
if (aWorkerPrivate->IsSharedWorker()) {
aWorkerPrivate->GetRemoteWorkerController()
->ErrorPropagationOnMainThread(&mReport,
->ErrorPropagationOnMainThread(mReport.get(),
/* isErrorEvent */ true);
return true;
}
@ -207,10 +84,10 @@ class ReportErrorRunnable final : public WorkerDebuggeeRunnable {
if (swm) {
swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
aWorkerPrivate->ServiceWorkerScope(),
aWorkerPrivate->ScriptURL(), mReport.mMessage,
mReport.mFilename, mReport.mLine,
mReport.mLineNumber, mReport.mColumnNumber,
mReport.mFlags, mReport.mExnType);
aWorkerPrivate->ScriptURL(), mReport->mMessage,
mReport->mFilename, mReport->mLine,
mReport->mLineNumber, mReport->mColumnNumber,
mReport->mFlags, mReport->mExnType);
}
return true;
}
@ -230,8 +107,9 @@ class ReportErrorRunnable final : public WorkerDebuggeeRunnable {
return true;
}
ReportError(aCx, parent, fireAtScope,
aWorkerPrivate->ParentEventTargetRef(), mReport, innerWindowId);
WorkerErrorReport::ReportError(aCx, parent, fireAtScope,
aWorkerPrivate->ParentEventTargetRef(),
std::move(mReport), innerWindowId);
return true;
}
};
@ -321,6 +199,19 @@ void WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote) {
xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage);
}
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);
@ -350,7 +241,7 @@ void WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport) {
/* static */
void WorkerErrorReport::ReportError(
JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aFireAtScope,
DOMEventTargetHelper* aTarget, const WorkerErrorReport& aReport,
DOMEventTargetHelper* aTarget, UniquePtr<WorkerErrorReport> aReport,
uint64_t aInnerWindowId, JS::Handle<JS::Value> aException) {
if (aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
@ -360,16 +251,16 @@ void WorkerErrorReport::ReportError(
// We should not fire error events for warnings but instead make sure that
// they show up in the error console.
if (!JSREPORT_IS_WARNING(aReport.mFlags)) {
if (!JSREPORT_IS_WARNING(aReport->mFlags)) {
// First fire an ErrorEvent at the worker.
RootedDictionary<ErrorEventInit> init(aCx);
if (aReport.mMutedError) {
if (aReport->mMutedError) {
init.mMessage.AssignLiteral("Script error.");
} else {
init.mMessage = aReport.mMessage;
init.mFilename = aReport.mFilename;
init.mLineno = aReport.mLineNumber;
init.mMessage = aReport->mMessage;
init.mFilename = aReport->mFilename;
init.mLineno = aReport->mLineNumber;
init.mError = aException;
}
@ -396,7 +287,7 @@ void WorkerErrorReport::ReportError(
// https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this
// better.
if (aFireAtScope &&
(aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) {
(aTarget || aReport->mErrorNumber != JSMSG_OVER_RECURSED)) {
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
NS_ASSERTION(global, "This should never be null!");
@ -414,7 +305,7 @@ void WorkerErrorReport::ReportError(
globalScope->GetWrapperPreserveColor() == global);
if (globalScope || IsWorkerDebuggerSandbox(global)) {
aWorkerPrivate->ReportErrorToDebugger(
aReport.mFilename, aReport.mLineNumber, aReport.mMessage);
aReport->mFilename, aReport->mLineNumber, aReport->mMessage);
return;
}
@ -458,17 +349,18 @@ void WorkerErrorReport::ReportError(
// Now fire a runnable to do the same on the parent's thread if we can.
if (aWorkerPrivate) {
RefPtr<ReportErrorRunnable> runnable =
new ReportErrorRunnable(aWorkerPrivate, aReport);
new ReportErrorRunnable(aWorkerPrivate, std::move(aReport));
runnable->Dispatch();
return;
}
// Otherwise log an error to the error console.
WorkerErrorReport::LogErrorToConsole(aReport, aInnerWindowId);
WorkerErrorReport::LogErrorToConsole(aCx, *aReport, aInnerWindowId);
}
/* static */
void WorkerErrorReport::LogErrorToConsole(const WorkerErrorReport& aReport,
void WorkerErrorReport::LogErrorToConsole(JSContext* aCx,
WorkerErrorReport& aReport,
uint64_t aInnerWindowId) {
nsTArray<ErrorDataNote> notes;
for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) {
@ -477,18 +369,42 @@ void WorkerErrorReport::LogErrorToConsole(const WorkerErrorReport& aReport,
note.mMessage, note.mFilename));
}
// 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);
LogErrorToConsole(errorData, aInnerWindowId, stack, stackGlobal);
}
/* static */
void WorkerErrorReport::LogErrorToConsole(const ErrorData& aReport,
uint64_t aInnerWindowId) {
uint64_t aInnerWindowId,
JS::HandleObject aStack,
JS::HandleObject aStackGlobal) {
AssertIsOnMainThread();
RefPtr<nsScriptErrorBase> scriptError = new nsScriptError();
RefPtr<nsScriptErrorBase> scriptError;
if (aStack) {
scriptError = new nsScriptErrorWithStack(aStack, aStackGlobal);
} else {
scriptError = new nsScriptError();
}
NS_WARNING_ASSERTION(scriptError, "Failed to create script error!");
if (scriptError) {

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/dom/WorkerCommon.h"
#include "jsapi.h"
#include "WorkerRef.h"
namespace mozilla {
@ -37,7 +38,9 @@ class WorkerErrorNote : public WorkerErrorBase {
class WorkerPrivate;
class WorkerErrorReport : public WorkerErrorBase {
// 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;
@ -45,7 +48,13 @@ class WorkerErrorReport : public WorkerErrorBase {
bool mMutedError;
nsTArray<WorkerErrorNote> mNotes;
WorkerErrorReport() : mFlags(0), mExnType(JSEXN_ERR), mMutedError(false) {}
// 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);
@ -54,15 +63,17 @@ class WorkerErrorReport : public WorkerErrorBase {
// (if any).
static void ReportError(
JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aFireAtScope,
DOMEventTargetHelper* aTarget, const WorkerErrorReport& aReport,
DOMEventTargetHelper* aTarget, UniquePtr<WorkerErrorReport> aReport,
uint64_t aInnerWindowId,
JS::Handle<JS::Value> aException = JS::NullHandleValue);
static void LogErrorToConsole(const WorkerErrorReport& aReport,
static void LogErrorToConsole(JSContext* aCx, WorkerErrorReport& aReport,
uint64_t aInnerWindowId);
static void LogErrorToConsole(const mozilla::dom::ErrorData& aReport,
uint64_t aInnerWindowId);
uint64_t aInnerWindowId,
JS::HandleObject aStack = nullptr,
JS::HandleObject aStackGlobal = nullptr);
static void CreateAndDispatchGenericErrorRunnableToParent(
WorkerPrivate* aWorkerPrivate);

Просмотреть файл

@ -4048,18 +4048,27 @@ void WorkerPrivate::ReportError(JSContext* aCx,
// for lack of anything better.
exn.setNull();
}
JS::RootedObject exnStack(aCx, JS::GetPendingExceptionStack(aCx));
JS_ClearPendingException(aCx);
WorkerErrorReport report;
UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>(this);
if (aReport) {
report.AssignErrorReport(aReport);
report->AssignErrorReport(aReport);
} else {
report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
report->mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
}
if (report.mMessage.IsEmpty() && aToStringResult) {
JS::RootedObject stack(aCx), stackGlobal(aCx);
xpc::FindExceptionStackForConsoleReport(nullptr, exn, exnStack, &stack, &stackGlobal);
if (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,
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.
@ -4070,7 +4079,7 @@ void WorkerPrivate::ReportError(JSContext* aCx,
nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
index);
AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
AppendUTF8toUTF16(truncatedToStringResult, report->mMessage);
}
}
@ -4079,11 +4088,11 @@ void WorkerPrivate::ReportError(JSContext* aCx,
// Don't want to run the scope's error handler if this is a recursive error or
// if we ran out of memory.
bool fireAtScope = data->mErrorHandlerRecursionCount == 1 &&
report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
report->mErrorNumber != JSMSG_OUT_OF_MEMORY &&
JS::CurrentGlobalOrNull(aCx);
WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
exn);
WorkerErrorReport::ReportError(aCx, this, fireAtScope,
nullptr, std::move(report), 0, exn);
data->mErrorHandlerRecursionCount--;
}

Просмотреть файл

@ -5008,6 +5008,22 @@ JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx) {
cx->clearPendingException();
}
JS_PUBLIC_API void
JS::SetPendingExceptionAndStack(JSContext* cx, HandleValue value,
HandleObject stack)
{
AssertHeapIsIdle();
CHECK_THREAD(cx);
cx->releaseCheck(value);
cx->releaseCheck(stack);
RootedSavedFrame nstack(cx);
if (stack) {
nstack = &UncheckedUnwrap(stack)->as<SavedFrame>();
}
cx->setPendingException(value, nstack);
}
JS_PUBLIC_API JSObject* JS::GetPendingExceptionStack(JSContext* cx) {
AssertHeapIsIdle();
CHECK_THREAD(cx);

Просмотреть файл

@ -2848,6 +2848,11 @@ class JS_PUBLIC_API AutoSaveExceptionState {
void restore();
};
// Set both the exception and its associated stack on the context. The stack
// must be a SavedFrame.
JS_PUBLIC_API void SetPendingExceptionAndStack(JSContext* cx, HandleValue value,
HandleObject stack);
/**
* Get the SavedFrame stack object captured when the pending exception was set
* on the JSContext. This fuzzily correlates with a `throw` statement in JS,

Просмотреть файл

@ -253,11 +253,10 @@ class DebugScript {
friend class JS::Realm;
/*
* When non-zero, compile script in single-step mode. The top bit is set and
* cleared by setStepMode, as used by JSD. The lower bits are a count,
* adjusted by changeStepModeCount, used by the Debugger object. Only
* when the bit is clear and the count is zero may we compile the script
* without single-step support.
* When greater than zero, compile script in single-step mode, with VM calls
* to HandleDebugTrap before each bytecode instruction's code. This is a
* counter, adjusted by the incrementStepModeCount and decrementStepModeCount
* methods.
*/
uint32_t stepMode;