Bug 1224073 - Instead of unsafeDereference to get the string of an error in a worker, evaluate it in Debugger;r=ejpbruel
MozReview-Commit-ID: 3juau0t3mus
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-02.js
@@ -38,18 +38,17 @@ add_task(function* testWhilePaused() {
info("Trying to get the result of command2");
executed = yield command2;
ok(executed.textContent.includes("10003"),
"command2 executed successfully");
info("Trying to get the result of command3");
executed = yield command3;
- // XXXworkers This is failing until Bug 1215120 is resolved.
- todo(executed.textContent.includes("ReferenceError: foobar is not defined"),
+ ok(executed.textContent.includes("ReferenceError: foobar is not defined"),
"command3 executed successfully");
let onceResumed = gTarget.once("thread-resumed");
EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
yield onceResumed;
terminateWorkerInTab(tab, WORKER_URL);
yield waitForWorkerClose(workerClient);
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -893,26 +893,32 @@ WebConsoleActor.prototype =
if (evalResult) {
if ("return" in evalResult) {
result = evalResult.return;
} else if ("yield" in evalResult) {
result = evalResult.yield;
} else if ("throw" in evalResult) {
let error = evalResult.throw;
errorGrip = this.createValueGrip(error);
- // XXXworkers: Calling unsafeDereference() returns an object with no
- // toString method in workers. See Bug 1215120.
- let unsafeDereference = error && (typeof error === "object") &&
- error.unsafeDereference();
- errorMessage = unsafeDereference && unsafeDereference.toString
- ? unsafeDereference.toString()
- : String(error);
+ errorMessage = String(error);
- // It is possible that we won't have permission to unwrap an
- // object and retrieve its errorMessageName.
+ try {
+ errorMessage = error.unsafeDereference().toString();
+ } catch(e) {
+ // We aren't able to access unsafeDereference inside worker thread, so
+ // use the Debugger to call toString on the object. See Bug 1224073.
+ let { frame, dbg } = this.getEvalDebuggerInfo(aRequest.frameActor);
+ let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
+ let evalErrorString = dbgWindow.executeInGlobalWithBindings("_self.toString()",
+ {_self: error});
+ if (evalErrorString.return) {
+ errorMessage = evalErrorString.return;
+ }
+ }
+
try {
errorDocURL = ErrorDocs.GetURL(error);
} catch (ex) {}
}
}
// If a value is encountered that the debugger server doesn't support yet,
// the console should remain functional.
@@ -1133,16 +1139,52 @@ WebConsoleActor.prototype =
desc.value = aDebuggerGlobal.makeDebuggeeValue(desc.value);
}
Object.defineProperty(helpers.sandbox, name, desc);
}
return helpers;
},
/**
+ * Get debugger / frame information needed to evaluate code with an optional
+ * frame.
+ *
+ * @param string frameActorID
+ * The actor for a frame (null if execution should happen in global)
+ * @return object
+ * An object that holds the following properties:
+ * - dbg: the debugger where code should be evaluated.
+ * - frame: the frame where code should be evaluated (can be null)
+ */
+ getEvalDebuggerInfo(frameActorID = null) {
+ // Find the Debugger.Frame of the given FrameActor.
+ let frame = null, frameActor = null;
+ if (frameActorID) {
+ frameActor = this.conn.getActor(frameActorID);
+ if (frameActor) {
+ frame = frameActor.frame;
+ }
+ else {
+ DevToolsUtils.reportException("evalWithDebugger",
+ Error("The frame actor was not found: " + frameActorID));
+ }
+ }
+
+ // If we've been given a frame actor in whose scope we should evaluate the
+ // expression, be sure to use that frame's Debugger (that is, the JavaScript
+ // debugger's Debugger) for the whole operation, not the console's Debugger.
+ // (One Debugger will treat a different Debugger's Debugger.Object instances
+ // as ordinary objects, not as references to be followed, so mixing
+ // debuggers causes strange behaviors.)
+ let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
+
+ return { frame, dbg };
+ },
+
+ /**
* Evaluates a string using the debugger API.
*
* To allow the variables view to update properties from the Web Console we
* provide the "bindObjectActor" mechanism: the Web Console tells the
* ObjectActor ID for which it desires to evaluate an expression. The
* Debugger.Object pointed at by the actor ID is bound such that it is
* available during expression evaluation (executeInGlobalWithBindings()).
*
@@ -1207,36 +1249,17 @@ WebConsoleActor.prototype =
aString = "help()";
}
// Add easter egg for console.mihai().
if (trimmedString == "console.mihai()" || trimmedString == "console.mihai();") {
aString = "\"http://incompleteness.me/blog/2015/02/09/console-dot-mihai/\"";
}
- // Find the Debugger.Frame of the given FrameActor.
- let frame = null, frameActor = null;
- if (aOptions.frameActor) {
- frameActor = this.conn.getActor(aOptions.frameActor);
- if (frameActor) {
- frame = frameActor.frame;
- }
- else {
- DevToolsUtils.reportException("evalWithDebugger",
- Error("The frame actor was not found: " + aOptions.frameActor));
- }
- }
-
- // If we've been given a frame actor in whose scope we should evaluate the
- // expression, be sure to use that frame's Debugger (that is, the JavaScript
- // debugger's Debugger) for the whole operation, not the console's Debugger.
- // (One Debugger will treat a different Debugger's Debugger.Object instances
- // as ordinary objects, not as references to be followed, so mixing
- // debuggers causes strange behaviors.)
- let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
+ let { frame, dbg } = this.getEvalDebuggerInfo(aOptions.frameActor);
let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
// If we have an object to bind to |_self|, create a Debugger.Object
// referring to that object, belonging to dbg.
let bindSelf = null;
if (aOptions.bindObjectActor || aOptions.selectedObjectActor) {
let objActor = this.getActorByID(aOptions.bindObjectActor ||
aOptions.selectedObjectActor);