зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1680504 - [remote] Implement Runtime.exceptionThrown. r=remote-protocol-reviewers,jgraham
Differential Revision: https://phabricator.services.mozilla.com/D100026
This commit is contained in:
Родитель
9e229babfa
Коммит
016c762add
|
@ -46,29 +46,39 @@ class Log extends ContentProcessDomain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getLogCategory(category) {
|
||||||
|
if (category.startsWith("CORS")) {
|
||||||
|
return "network";
|
||||||
|
} else if (category.includes("javascript")) {
|
||||||
|
return "javascript";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "other";
|
||||||
|
}
|
||||||
|
|
||||||
// nsIObserver
|
// nsIObserver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes all script error messages belonging to the current window.
|
* Takes all script error messages that do not have an exception attached,
|
||||||
* and emits a "Log.entryAdded" event.
|
* and emits a "Log.entryAdded" event.
|
||||||
*
|
*
|
||||||
* @param {nsIConsoleMessage} message
|
* @param {nsIConsoleMessage} message
|
||||||
* Message originating from the nsIConsoleService.
|
* Message originating from the nsIConsoleService.
|
||||||
*/
|
*/
|
||||||
observe(message) {
|
observe(message) {
|
||||||
// Console messages will be handled via Runtime.consoleAPICalled
|
if (message instanceof Ci.nsIScriptError && !message.hasException) {
|
||||||
if (
|
let url;
|
||||||
message instanceof Ci.nsIScriptError &&
|
if (message.sourceName !== "debugger eval code") {
|
||||||
message.flags == Ci.nsIScriptError.errorFlag
|
url = message.sourceName;
|
||||||
) {
|
}
|
||||||
|
|
||||||
const entry = {
|
const entry = {
|
||||||
source: "javascript",
|
source: this._getLogCategory(message.category),
|
||||||
level: CONSOLE_MESSAGE_LEVEL_MAP[message.flags],
|
level: CONSOLE_MESSAGE_LEVEL_MAP[message.logLevel],
|
||||||
lineNumber: message.lineNumber,
|
|
||||||
stacktrace: message.stack,
|
|
||||||
text: message.errorMessage,
|
text: message.errorMessage,
|
||||||
timestamp: message.timeStamp,
|
timestamp: message.timeStamp,
|
||||||
url: message.sourceName,
|
url,
|
||||||
|
lineNumber: message.lineNumber,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.emit("Log.entryAdded", { entry });
|
this.emit("Log.entryAdded", { entry });
|
||||||
|
|
|
@ -25,6 +25,10 @@ addDebuggerToGlobal(Cu.getGlobalForObject(this));
|
||||||
|
|
||||||
const OBSERVER_CONSOLE_API = "console-api-log-event";
|
const OBSERVER_CONSOLE_API = "console-api-log-event";
|
||||||
|
|
||||||
|
const CONSOLE_API_LEVEL_MAP = {
|
||||||
|
warn: "warning",
|
||||||
|
};
|
||||||
|
|
||||||
class SetMap extends Map {
|
class SetMap extends Map {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -297,6 +301,29 @@ class Runtime extends ContentProcessDomain {
|
||||||
return this.__debugger;
|
return this.__debugger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_buildStackTrace(stack) {
|
||||||
|
const callFrames = [];
|
||||||
|
|
||||||
|
while (
|
||||||
|
stack &&
|
||||||
|
stack.source !== "debugger eval code" &&
|
||||||
|
!stack.source.startsWith("chrome://")
|
||||||
|
) {
|
||||||
|
callFrames.push({
|
||||||
|
functionName: stack.functionDisplayName,
|
||||||
|
scriptId: stack.sourceId,
|
||||||
|
url: stack.source,
|
||||||
|
lineNumber: stack.line,
|
||||||
|
columnNumber: stack.column,
|
||||||
|
});
|
||||||
|
stack = stack.parent || stack.asyncParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
callFrames,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
_getRemoteObject(objectId) {
|
_getRemoteObject(objectId) {
|
||||||
for (const ctx of this.contexts.values()) {
|
for (const ctx of this.contexts.values()) {
|
||||||
const debuggerObj = ctx.getRemoteObject(objectId);
|
const debuggerObj = ctx.getRemoteObject(objectId);
|
||||||
|
@ -380,7 +407,33 @@ class Runtime extends ContentProcessDomain {
|
||||||
executionContextId: context?.id || 0,
|
executionContextId: context?.id || 0,
|
||||||
timestamp: payload.timestamp,
|
timestamp: payload.timestamp,
|
||||||
type: payload.type,
|
type: payload.type,
|
||||||
stackTrace: payload.stacktrace,
|
stackTrace: this._buildStackTrace(payload.stack),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_emitExceptionThrown(payload) {
|
||||||
|
// Filter out messages that aren't coming from a valid inner window, or from
|
||||||
|
// a different browser tab. Also messages of type "time", which are not
|
||||||
|
// getting reported by Chrome.
|
||||||
|
const curBrowserId = this.session.browsingContext.browserId;
|
||||||
|
const win = Services.wm.getCurrentInnerWindowWithId(payload.innerWindowId);
|
||||||
|
if (!win || BrowsingContext.getFromWindow(win).browserId != curBrowserId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = this._getDefaultContextForWindow();
|
||||||
|
this.emit("Runtime.exceptionThrown", {
|
||||||
|
timestamp: payload.timestamp,
|
||||||
|
exceptionDetails: {
|
||||||
|
// Temporary placeholder to return a number.
|
||||||
|
exceptionId: 0,
|
||||||
|
text: payload.text,
|
||||||
|
lineNumber: payload.lineNumber,
|
||||||
|
columnNumber: payload.columnNumber,
|
||||||
|
url: payload.url,
|
||||||
|
stackTrace: this._buildStackTrace(payload.stack),
|
||||||
|
executionContextId: context?.id || undefined,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,8 +568,9 @@ class Runtime extends ContentProcessDomain {
|
||||||
// nsIObserver
|
// nsIObserver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a console message belonging to the current window that is not
|
* Takes a console message belonging to the current window and emits a
|
||||||
* a Javascript error, and emits a "consoleAPICalled" event.
|
* "exceptionThrown" event if it's a Javascript error, otherwise a
|
||||||
|
* "consoleAPICalled" event.
|
||||||
*
|
*
|
||||||
* @param {nsIConsoleMessage} message
|
* @param {nsIConsoleMessage} message
|
||||||
* Console message.
|
* Console message.
|
||||||
|
@ -528,20 +582,9 @@ class Runtime extends ContentProcessDomain {
|
||||||
const message = subject.wrappedJSObject;
|
const message = subject.wrappedJSObject;
|
||||||
entry = fromConsoleAPI(message);
|
entry = fromConsoleAPI(message);
|
||||||
this._emitConsoleAPICalled(entry);
|
this._emitConsoleAPICalled(entry);
|
||||||
return;
|
} else if (subject instanceof Ci.nsIScriptError && subject.hasException) {
|
||||||
}
|
entry = fromScriptError(subject);
|
||||||
|
this._emitExceptionThrown(entry);
|
||||||
if (subject instanceof Ci.nsIConsoleMessage) {
|
|
||||||
// Script errors will be handled through Log.entryAdded
|
|
||||||
if (
|
|
||||||
subject instanceof Ci.nsIScriptError &&
|
|
||||||
subject.flags == Ci.nsIScriptError.errorFlag
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = fromConsoleMessage(subject);
|
|
||||||
this._emitConsoleAPICalled(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,36 +596,26 @@ class Runtime extends ContentProcessDomain {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromConsoleAPI(message) {
|
function fromConsoleAPI(message) {
|
||||||
const CONSOLE_API_LEVEL_MAP = {
|
|
||||||
warn: "warning",
|
|
||||||
};
|
|
||||||
|
|
||||||
// From sendConsoleAPIMessage (toolkit/modules/Console.jsm)
|
// From sendConsoleAPIMessage (toolkit/modules/Console.jsm)
|
||||||
return {
|
return {
|
||||||
arguments: message.arguments,
|
arguments: message.arguments,
|
||||||
innerWindowId: message.innerID,
|
innerWindowId: message.innerID,
|
||||||
// TODO: Fetch the stack (Bug 1679981)
|
// TODO: Fetch the stack (Bug 1679981)
|
||||||
stacktrace: undefined,
|
stack: undefined,
|
||||||
timestamp: message.timeStamp,
|
timestamp: message.timeStamp,
|
||||||
type: CONSOLE_API_LEVEL_MAP[message.level] || message.level,
|
type: CONSOLE_API_LEVEL_MAP[message.level] || message.level,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromConsoleMessage(message) {
|
function fromScriptError(error) {
|
||||||
const CONSOLE_MESSAGE_LEVEL_MAP = {
|
// From dom/bindings/nsIScriptError.idl
|
||||||
[Ci.nsIConsoleMessage.debug]: "verbose",
|
|
||||||
[Ci.nsIConsoleMessage.info]: "info",
|
|
||||||
[Ci.nsIConsoleMessage.warn]: "warning",
|
|
||||||
[Ci.nsIConsoleMessage.error]: "error",
|
|
||||||
};
|
|
||||||
|
|
||||||
// From xpcom/base/nsIConsoleMessage.idl and dom/bindings/nsIScriptError.idl
|
|
||||||
return {
|
return {
|
||||||
arguments: [message.message],
|
innerWindowId: error.innerWindowID,
|
||||||
innerWindowId: message.innerWindowID,
|
columnNumber: error.columnNumber,
|
||||||
// TODO: Fetch the stack (Bug 1679981)
|
lineNumber: error.lineNumber,
|
||||||
stacktrace: undefined,
|
stack: error.stack,
|
||||||
timestamp: message.timeStamp,
|
text: error.errorMessage,
|
||||||
type: CONSOLE_MESSAGE_LEVEL_MAP[message.logLevel] || message.logLevel,
|
timestamp: error.timeStamp,
|
||||||
|
url: error.sourceName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1380,7 +1380,7 @@
|
||||||
"FAIL"
|
"FAIL"
|
||||||
],
|
],
|
||||||
"Page Page.Events.PageError should fire (page.spec.ts)": [
|
"Page Page.Events.PageError should fire (page.spec.ts)": [
|
||||||
"TIMEOUT"
|
"PASS"
|
||||||
],
|
],
|
||||||
"Page Page.setUserAgent should work (page.spec.ts)": [
|
"Page Page.setUserAgent should work (page.spec.ts)": [
|
||||||
"PASS"
|
"PASS"
|
||||||
|
|
|
@ -440,6 +440,43 @@ async function createFile(contents, options = {}) {
|
||||||
return { file, path };
|
return { file, path };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function throwScriptError(options = {}) {
|
||||||
|
const { inContent = true } = options;
|
||||||
|
|
||||||
|
const addScriptErrorInternal = ({ options }) => {
|
||||||
|
const {
|
||||||
|
flag = Ci.nsIScriptError.errorFlag,
|
||||||
|
innerWindowId = content.windowGlobalChild.innerWindowId,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(
|
||||||
|
Ci.nsIScriptError
|
||||||
|
);
|
||||||
|
scriptError.initWithWindowID(
|
||||||
|
options.text,
|
||||||
|
options.sourceName || "sourceName",
|
||||||
|
null,
|
||||||
|
options.lineNumber || 0,
|
||||||
|
options.columnNumber || 0,
|
||||||
|
flag,
|
||||||
|
options.category || "javascript",
|
||||||
|
innerWindowId
|
||||||
|
);
|
||||||
|
Services.console.logMessage(scriptError);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inContent) {
|
||||||
|
ContentTask.spawn(
|
||||||
|
gBrowser.selectedBrowser,
|
||||||
|
{ options },
|
||||||
|
addScriptErrorInternal
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
options.innerWindowId = window.windowGlobalChild.innerWindowId;
|
||||||
|
addScriptErrorInternal({ options });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class RecordEvents {
|
class RecordEvents {
|
||||||
/**
|
/**
|
||||||
* A timeline of events chosen by calls to `addRecorder`.
|
* A timeline of events chosen by calls to `addRecorder`.
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[DEFAULT]
|
||||||
|
tags = remote
|
||||||
|
subsuite = remote
|
||||||
|
prefs =
|
||||||
|
remote.enabled=true
|
||||||
|
support-files =
|
||||||
|
!/remote/test/browser/chrome-remote-interface.js
|
||||||
|
!/remote/test/browser/head.js
|
||||||
|
head.js
|
||||||
|
|
||||||
|
[browser_entryAdded.js]
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
add_task(async function noEventsWhenLogDomainDisabled({ client }) {
|
||||||
|
await runEntryAddedTest(client, 0, async () => {
|
||||||
|
await throwScriptError("foo");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function noEventsAfterLogDomainDisabled({ client }) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
await Log.enable();
|
||||||
|
await Log.disable();
|
||||||
|
|
||||||
|
await runEntryAddedTest(client, 0, async () => {
|
||||||
|
await throwScriptError("foo");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function noEventsForConsoleMessageWithException({ client }) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
await Log.enable();
|
||||||
|
|
||||||
|
const context = await enableRuntime(client);
|
||||||
|
await runEntryAddedTest(client, 0, async () => {
|
||||||
|
evaluate(client, context.id, () => {
|
||||||
|
const foo = {};
|
||||||
|
foo.bar();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function eventsForScriptErrorWithoutException({ client }) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
await Log.enable();
|
||||||
|
|
||||||
|
await enableRuntime(client);
|
||||||
|
const events = await runEntryAddedTest(client, 1, async () => {
|
||||||
|
throwScriptError({
|
||||||
|
text: "foo",
|
||||||
|
sourceName: "http://foo.bar",
|
||||||
|
lineNumber: 7,
|
||||||
|
category: "javascript",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
is(events[0].source, "javascript", "Got expected source");
|
||||||
|
is(events[0].level, "error", "Got expected level");
|
||||||
|
is(events[0].text, "foo", "Got expected text");
|
||||||
|
is(events[0].url, "http://foo.bar", "Got expected url");
|
||||||
|
is(events[0].lineNumber, 7, "Got expected line number");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function eventsForScriptErrorLevels({ client }) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
await Log.enable();
|
||||||
|
|
||||||
|
const flags = {
|
||||||
|
info: Ci.nsIScriptError.infoFlag,
|
||||||
|
warning: Ci.nsIScriptError.warningFlag,
|
||||||
|
error: Ci.nsIScriptError.errorFlag,
|
||||||
|
};
|
||||||
|
|
||||||
|
await enableRuntime(client);
|
||||||
|
for (const [level, flag] of Object.entries(flags)) {
|
||||||
|
const events = await runEntryAddedTest(client, 1, async () => {
|
||||||
|
throwScriptError({ text: level, flag });
|
||||||
|
});
|
||||||
|
|
||||||
|
is(events[0].text, level, "Got expected text");
|
||||||
|
is(events[0].level, level, "Got expected level");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function eventsForScriptErrorContent({ client }) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
await Log.enable();
|
||||||
|
|
||||||
|
const context = await enableRuntime(client);
|
||||||
|
const events = await runEntryAddedTest(client, 1, async () => {
|
||||||
|
evaluate(client, context.id, () => {
|
||||||
|
document.execCommand("copy");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
is(events[0].source, "other", "Got expected source");
|
||||||
|
is(events[0].level, "warning", "Got expected level");
|
||||||
|
ok(
|
||||||
|
events[0].text.includes("document.execCommand(‘cut’/‘copy’) was denied"),
|
||||||
|
"Got expected text"
|
||||||
|
);
|
||||||
|
is(events[0].url, undefined, "Got undefined url");
|
||||||
|
is(events[0].lineNumber, 2, "Got expected line number");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function runEntryAddedTest(client, eventCount, callback, options = {}) {
|
||||||
|
const { Log } = client;
|
||||||
|
|
||||||
|
const EVENT_ENTRY_ADDED = "Log.entryAdded";
|
||||||
|
|
||||||
|
const history = new RecordEvents(eventCount);
|
||||||
|
history.addRecorder({
|
||||||
|
event: Log.entryAdded,
|
||||||
|
eventName: EVENT_ENTRY_ADDED,
|
||||||
|
messageFn: payload => `Received "${EVENT_ENTRY_ADDED}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeBefore = Date.now();
|
||||||
|
await callback();
|
||||||
|
|
||||||
|
const entryAddedEvents = await history.record();
|
||||||
|
is(entryAddedEvents.length, eventCount, "Got expected amount of events");
|
||||||
|
|
||||||
|
if (eventCount == 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAfter = Date.now();
|
||||||
|
|
||||||
|
// Check basic details for entryAdded events
|
||||||
|
entryAddedEvents.forEach(event => {
|
||||||
|
const timestamp = event.payload.entry.timestamp;
|
||||||
|
|
||||||
|
ok(
|
||||||
|
timestamp >= timeBefore && timestamp <= timeAfter,
|
||||||
|
"Got valid timestamp"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return entryAddedEvents.map(event => event.payload.entry);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* import-globals-from ../head.js */
|
||||||
|
|
||||||
|
Services.scriptloader.loadSubScript(
|
||||||
|
"chrome://mochitests/content/browser/remote/test/browser/head.js",
|
||||||
|
this
|
||||||
|
);
|
|
@ -13,6 +13,7 @@ support-files =
|
||||||
[browser_callFunctionOn_returnByValue.js]
|
[browser_callFunctionOn_returnByValue.js]
|
||||||
[browser_consoleAPICalled.js]
|
[browser_consoleAPICalled.js]
|
||||||
[browser_evaluate.js]
|
[browser_evaluate.js]
|
||||||
|
[browser_exceptionThrown.js]
|
||||||
[browser_executionContextEvents.js]
|
[browser_executionContextEvents.js]
|
||||||
skip-if = os == "mac" || os == "win" # bug 1586503,1590930
|
skip-if = os == "mac" || os == "win" # bug 1586503,1590930
|
||||||
[browser_getProperties.js]
|
[browser_getProperties.js]
|
||||||
|
|
|
@ -23,6 +23,17 @@ add_task(async function noEventsAfterRuntimeDomainDisabled({ client }) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(async function noEventsForJavascriptErrors({ client }) {
|
||||||
|
await loadURL(PAGE_CONSOLE_EVENTS);
|
||||||
|
const context = await enableRuntime(client);
|
||||||
|
|
||||||
|
await runConsoleTest(client, 0, async () => {
|
||||||
|
evaluate(client, context.id, () => {
|
||||||
|
document.getElementById("js-error").click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
add_task(async function consoleAPI({ client }) {
|
add_task(async function consoleAPI({ client }) {
|
||||||
const context = await enableRuntime(client);
|
const context = await enableRuntime(client);
|
||||||
|
|
||||||
|
@ -138,29 +149,6 @@ add_task(async function consoleAPIByContentInteraction({ client }) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function consoleMessageByContent({ client }) {
|
|
||||||
await loadURL(PAGE_CONSOLE_EVENTS);
|
|
||||||
const context = await enableRuntime(client);
|
|
||||||
|
|
||||||
const events = await runConsoleTest(client, 1, async () => {
|
|
||||||
evaluate(client, context.id, () => {
|
|
||||||
document.execCommand("copy");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
is(events[0].type, "warning", "Got expected type");
|
|
||||||
Assert.equal(events[0].args.length, 1, "Got expected amount of argumnets");
|
|
||||||
ok(
|
|
||||||
events[0].args[0].value.includes("document.execCommand"),
|
|
||||||
"Got expected argument value"
|
|
||||||
);
|
|
||||||
is(
|
|
||||||
events[0].executionContextId,
|
|
||||||
context.id,
|
|
||||||
"Got event from current execution context"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function runConsoleTest(client, eventCount, callback, options = {}) {
|
async function runConsoleTest(client, eventCount, callback, options = {}) {
|
||||||
const { Runtime } = client;
|
const { Runtime } = client;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const PAGE_CONSOLE_EVENTS =
|
||||||
|
"http://example.com/browser/remote/test/browser/runtime/doc_console_events.html";
|
||||||
|
|
||||||
|
add_task(async function noEventsWhenRuntimeDomainDisabled({ client }) {
|
||||||
|
await runExceptionThrownTest(client, 0, async () => {
|
||||||
|
await throwScriptError({ text: "foo" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function noEventsAfterRuntimeDomainDisabled({ client }) {
|
||||||
|
const { Runtime } = client;
|
||||||
|
|
||||||
|
await Runtime.enable();
|
||||||
|
await Runtime.disable();
|
||||||
|
|
||||||
|
await runExceptionThrownTest(client, 0, async () => {
|
||||||
|
await throwScriptError({ text: "foo" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function noEventsForScriptErrorWithoutException({ client }) {
|
||||||
|
const { Runtime } = client;
|
||||||
|
|
||||||
|
await Runtime.enable();
|
||||||
|
|
||||||
|
await runExceptionThrownTest(client, 0, async () => {
|
||||||
|
await throwScriptError({ text: "foo" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function eventsForScriptErrorWithException({ client }) {
|
||||||
|
await loadURL(PAGE_CONSOLE_EVENTS);
|
||||||
|
|
||||||
|
const context = await enableRuntime(client);
|
||||||
|
|
||||||
|
const events = await runExceptionThrownTest(client, 1, async () => {
|
||||||
|
evaluate(client, context.id, () => {
|
||||||
|
document.getElementById("js-error").click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
is(
|
||||||
|
typeof events[0].exceptionId,
|
||||||
|
"number",
|
||||||
|
"Got expected type for exception id"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
events[0].text,
|
||||||
|
"TypeError: foo.click is not a function",
|
||||||
|
"Got expected text"
|
||||||
|
);
|
||||||
|
is(events[0].lineNumber, 9, "Got expected line number");
|
||||||
|
is(events[0].columnNumber, 11, "Got expected column number");
|
||||||
|
is(events[0].url, PAGE_CONSOLE_EVENTS, "Got expected url");
|
||||||
|
is(
|
||||||
|
events[0].executionContextId,
|
||||||
|
context.id,
|
||||||
|
"Got event from current execution context"
|
||||||
|
);
|
||||||
|
|
||||||
|
const callFrames = events[0].stackTrace.callFrames;
|
||||||
|
is(callFrames.length, 2, "Got expected amount of call frames");
|
||||||
|
|
||||||
|
is(callFrames[0].functionName, "throwError", "Got expected function name");
|
||||||
|
is(callFrames[0].url, PAGE_CONSOLE_EVENTS, "Got expected url");
|
||||||
|
is(callFrames[0].lineNumber, 9, "Got expected line number");
|
||||||
|
is(callFrames[0].columnNumber, 11, "Got expected column number");
|
||||||
|
|
||||||
|
is(callFrames[1].functionName, "onclick", "Got expected function name");
|
||||||
|
is(callFrames[1].url, PAGE_CONSOLE_EVENTS, "Got expected url");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function runExceptionThrownTest(
|
||||||
|
client,
|
||||||
|
eventCount,
|
||||||
|
callback,
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
|
const { Runtime } = client;
|
||||||
|
|
||||||
|
const EVENT_EXCEPTION_THROWN = "Runtime.exceptionThrown";
|
||||||
|
|
||||||
|
const history = new RecordEvents(eventCount);
|
||||||
|
history.addRecorder({
|
||||||
|
event: Runtime.exceptionThrown,
|
||||||
|
eventName: EVENT_EXCEPTION_THROWN,
|
||||||
|
messageFn: payload => `Received "${payload.name}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeBefore = Date.now();
|
||||||
|
await callback();
|
||||||
|
|
||||||
|
const exceptionThrownEvents = await history.record();
|
||||||
|
is(exceptionThrownEvents.length, eventCount, "Got expected amount of events");
|
||||||
|
|
||||||
|
if (eventCount == 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeAfter = Date.now();
|
||||||
|
|
||||||
|
// Check basic details for entryAdded events
|
||||||
|
exceptionThrownEvents.forEach(event => {
|
||||||
|
const details = event.payload.exceptionDetails;
|
||||||
|
const timestamp = event.payload.timestamp;
|
||||||
|
|
||||||
|
is(typeof details, "object", "Got expected 'exceptionDetails' property");
|
||||||
|
ok(
|
||||||
|
timestamp >= timeBefore && timestamp <= timeAfter,
|
||||||
|
"Got valid timestamp"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return exceptionThrownEvents.map(event => event.payload.exceptionDetails);
|
||||||
|
}
|
|
@ -2,10 +2,17 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Empty page</title>
|
<title>Empty page</title>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function throwError() {
|
||||||
|
let foo = {};
|
||||||
|
foo.click();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a id="console-log" href="javascript:console.log('foo')">console.log()</a><br/>
|
<a id="console-log" href="javascript:console.log('foo')">console.log()</a><br/>
|
||||||
<a id="console-error" href="javascript:console.error('foo')">console.error()</a><br/>
|
<a id="console-error" href="javascript:console.error('foo')">console.error()</a><br/>
|
||||||
<a id="js-error" href="javascript:foo.click()">Javascript Error</a><br/>
|
<a id="js-error" onclick="throwError()">Javascript Error</a><br/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -9,6 +9,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||||
"browser/emulation/browser.ini",
|
"browser/emulation/browser.ini",
|
||||||
"browser/input/browser.ini",
|
"browser/input/browser.ini",
|
||||||
"browser/io/browser.ini",
|
"browser/io/browser.ini",
|
||||||
|
"browser/log/browser.ini",
|
||||||
"browser/network/browser.ini",
|
"browser/network/browser.ini",
|
||||||
"browser/page/browser.ini",
|
"browser/page/browser.ini",
|
||||||
"browser/runtime/browser.ini",
|
"browser/runtime/browser.ini",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче