mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-18 23:15:38 +00:00
Bug 1112378 - Rewrite errors from call watcher to hide information of the error originating from call watcher, rather than content. r=vp
This commit is contained in:
parent
75cd780807
commit
e533052040
@ -13,6 +13,7 @@ support-files =
|
||||
doc_automation.html
|
||||
doc_bug_1125817.html
|
||||
doc_bug_1130901.html
|
||||
doc_bug_1112378.html
|
||||
440hz_sine.ogg
|
||||
head.js
|
||||
|
||||
@ -31,6 +32,7 @@ skip-if = true # bug 1092571
|
||||
[browser_audionode-actor-get-automation-data-02.js]
|
||||
[browser_audionode-actor-get-automation-data-03.js]
|
||||
[browser_callwatcher-01.js]
|
||||
[browser_callwatcher-02.js]
|
||||
[browser_webaudio-actor-simple.js]
|
||||
[browser_webaudio-actor-destroy-node.js]
|
||||
[browser_webaudio-actor-connect-param.js]
|
||||
|
@ -0,0 +1,44 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Bug 1112378
|
||||
* Tests to ensure that errors called on wrapped functions via call-watcher
|
||||
* correctly looks like the error comes from the content, not from within the devtools.
|
||||
*/
|
||||
|
||||
const BUG_1112378_URL = EXAMPLE_URL + "doc_bug_1112378.html";
|
||||
|
||||
add_task(function*() {
|
||||
let { target, panel } = yield initWebAudioEditor(BUG_1112378_URL);
|
||||
let { panelWin } = panel;
|
||||
let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
|
||||
|
||||
loadFrameScripts();
|
||||
|
||||
reload(target);
|
||||
|
||||
yield waitForGraphRendered(panelWin, 2, 0);
|
||||
|
||||
let error = yield evalInDebuggee("throwError()");
|
||||
is(error.lineNumber, 21, "error has correct lineNumber");
|
||||
is(error.columnNumber, 11, "error has correct columnNumber");
|
||||
is(error.name, "TypeError", "error has correct name");
|
||||
is(error.message, "Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error has correct message");
|
||||
is(error.stringified, "TypeError: Argument 1 is not valid for any of the 2-argument overloads of AudioNode.connect.", "error is stringified correctly");
|
||||
ise(error.instanceof, true, "error is correctly an instanceof TypeError");
|
||||
is(error.fileName, "http://example.com/browser/browser/devtools/webaudioeditor/test/doc_bug_1112378.html", "error has correct fileName");
|
||||
|
||||
error = yield evalInDebuggee("throwDOMException()");
|
||||
is(error.lineNumber, 37, "exception has correct lineNumber");
|
||||
is(error.columnNumber, 0, "exception has correct columnNumber");
|
||||
is(error.code, 9, "exception has correct code");
|
||||
is(error.result, 2152923145, "exception has correct result");
|
||||
is(error.name, "NotSupportedError", "exception has correct name");
|
||||
is(error.message, "Operation is not supported", "exception has correct message");
|
||||
is(error.stringified, "NotSupportedError: Operation is not supported", "exception is stringified correctly");
|
||||
ise(error.instanceof, true, "exception is correctly an instance of DOMException");
|
||||
is(error.filename, "http://example.com/browser/browser/devtools/webaudioeditor/test/doc_bug_1112378.html", "exception has correct filename");
|
||||
|
||||
yield teardown(target);
|
||||
});
|
57
browser/devtools/webaudioeditor/test/doc_bug_1112378.html
Normal file
57
browser/devtools/webaudioeditor/test/doc_bug_1112378.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Web Audio Editor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let ctx = new AudioContext();
|
||||
let osc = ctx.createOscillator();
|
||||
|
||||
function throwError () {
|
||||
try {
|
||||
osc.connect({});
|
||||
} catch (e) {
|
||||
return {
|
||||
lineNumber: e.lineNumber,
|
||||
fileName: e.fileName,
|
||||
columnNumber: e.columnNumber,
|
||||
message: e.message,
|
||||
instanceof: e instanceof TypeError,
|
||||
stringified: e.toString(),
|
||||
name: e.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function throwDOMException () {
|
||||
try {
|
||||
osc.frequency.setValueAtTime(0, -1);
|
||||
} catch (e) {
|
||||
return {
|
||||
lineNumber: e.lineNumber,
|
||||
columnNumber: e.columnNumber,
|
||||
filename: e.filename,
|
||||
message: e.message,
|
||||
code: e.code,
|
||||
result: e.result,
|
||||
instanceof: e instanceof DOMException,
|
||||
stringified: e.toString(),
|
||||
name: e.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -16,10 +16,13 @@ let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
|
||||
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
|
||||
let { WebAudioFront } = devtools.require("devtools/server/actors/webaudio");
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
let mm = null;
|
||||
|
||||
const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js";
|
||||
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/test/";
|
||||
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
|
||||
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
|
||||
@ -47,6 +50,15 @@ registerCleanupFunction(() => {
|
||||
Cu.forceGC();
|
||||
});
|
||||
|
||||
/**
|
||||
* Call manually in tests that use frame script utils after initializing
|
||||
* the web audio editor. Call after init but before navigating to a different page.
|
||||
*/
|
||||
function loadFrameScripts () {
|
||||
mm = gBrowser.selectedBrowser.messageManager;
|
||||
mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
|
||||
}
|
||||
|
||||
function addTab(aUrl, aWindow) {
|
||||
info("Adding tab: " + aUrl);
|
||||
|
||||
@ -440,6 +452,33 @@ function waitForInspectorRender (panelWin, EVENTS) {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string `script` and evaluates it directly in the content
|
||||
* in potentially a different process.
|
||||
*/
|
||||
function evalInDebuggee (script) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
if (!mm) {
|
||||
throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
|
||||
}
|
||||
|
||||
let id = generateUUID().toString();
|
||||
mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
|
||||
mm.addMessageListener("devtools:test:eval:response", handler);
|
||||
|
||||
function handler ({ data }) {
|
||||
if (id !== data.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
mm.removeMessageListener("devtools:test:eval:response", handler);
|
||||
deferred.resolve(data.value);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of audio node properties to test against expectations of the AudioNode actor
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@ const {Cc, Ci, Cu, Cr} = require("chrome");
|
||||
const events = require("sdk/event/core");
|
||||
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const {ContentObserver} = require("devtools/content-observer");
|
||||
const {serializeStack, parseStack} = require("toolkit/loader");
|
||||
|
||||
const {on, once, off, emit} = events;
|
||||
const {method, Arg, Option, RetVal} = protocol;
|
||||
@ -417,7 +417,12 @@ let CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass({
|
||||
let originalFunc = Cu.unwaiveXrays(target[name]);
|
||||
|
||||
Cu.exportFunction(function(...args) {
|
||||
let result = Cu.waiveXrays(originalFunc.apply(this, args));
|
||||
let result;
|
||||
try {
|
||||
result = Cu.waiveXrays(originalFunc.apply(this, args));
|
||||
} catch (e) {
|
||||
throw createContentError(e, unwrappedWindow);
|
||||
}
|
||||
|
||||
if (self._recording) {
|
||||
let stack = getStack(name);
|
||||
@ -693,3 +698,45 @@ function getBitToEnumValue(type, object, arg) {
|
||||
// Cache the combined bitmask value
|
||||
return table[arg] = flags.join(" | ") || arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new error from an error that originated from content but was called
|
||||
* from a wrapped overridden method. This is so we can make our own error
|
||||
* that does not look like it originated from the call watcher.
|
||||
*
|
||||
* We use toolkit/loader's parseStack and serializeStack rather than the
|
||||
* parsing done in the local `getStack` function, because it does not expose
|
||||
* column number, would have to change the protocol models `call-stack-items` and `call-details`
|
||||
* which hurts backwards compatibility, and the local `getStack` is an optimized, hot function.
|
||||
*/
|
||||
function createContentError (e, win) {
|
||||
let { message, name, stack } = e;
|
||||
let parsedStack = parseStack(stack);
|
||||
let { fileName, lineNumber, columnNumber } = parsedStack[parsedStack.length - 1];
|
||||
let error;
|
||||
|
||||
let isDOMException = e instanceof Ci.nsIDOMDOMException;
|
||||
let constructor = isDOMException ? win.DOMException : (win[e.name] || win.Error);
|
||||
|
||||
if (isDOMException) {
|
||||
error = new constructor(message, name);
|
||||
Object.defineProperties(error, {
|
||||
code: { value: e.code },
|
||||
columnNumber: { value: 0 }, // columnNumber is always 0 for DOMExceptions?
|
||||
filename: { value: fileName }, // note the lowercase `filename`
|
||||
lineNumber: { value: lineNumber },
|
||||
result: { value: e.result },
|
||||
stack: { value: serializeStack(parsedStack) }
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Constructing an error here retains all the stack information,
|
||||
// and we can add message, fileName and lineNumber via constructor, though
|
||||
// need to manually add columnNumber.
|
||||
error = new constructor(message, fileName, lineNumber);
|
||||
Object.defineProperty(error, "columnNumber", {
|
||||
value: columnNumber
|
||||
});
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user