author | Brian Grinstead <bgrinstead@mozilla.com> |
Mon, 26 Oct 2015 09:13:11 -0700 | |
changeset 269584 | 0e616440737c12028953b5095ee7222d7e4d79a4 |
parent 269583 | 03eec3bcf2933e13f3f147b72b3e722c36a42e52 |
child 269585 | b39a7e4289d8de57151b826dec3902130f9d0634 |
push id | 29586 |
push user | cbook@mozilla.com |
push date | Tue, 27 Oct 2015 09:52:52 +0000 |
treeherder | mozilla-central@e36bf6850a93 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ejpbruel |
bugs | 1215117 |
milestone | 44.0a1 |
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
|
--- a/devtools/client/debugger/test/mochitest/browser.ini +++ b/devtools/client/debugger/test/mochitest/browser.ini @@ -569,16 +569,18 @@ skip-if = e10s && debug [browser_dbg_variables-view-reexpand-03.js] skip-if = e10s && debug [browser_dbg_variables-view-webidl.js] skip-if = e10s && debug [browser_dbg_watch-expressions-01.js] skip-if = e10s && debug [browser_dbg_watch-expressions-02.js] skip-if = e10s && debug +[browser_dbg_worker-console.js] +skip-if = e10s && debug [browser_dbg_worker-window.js] skip-if = e10s && debug [browser_dbg_WorkerActor.attach.js] skip-if = e10s && debug [browser_dbg_WorkerActor.attachThread.js] skip-if = e10s && debug [browser_dbg_split-console-keypress.js] skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-next-console.js @@ -28,17 +28,17 @@ function test() { .then(() => closeDebuggerAndFinish(gPanel)); }); let testConsole = Task.async(function*() { info("Starting testConsole"); let oncePaused = gTarget.once("thread-paused"); EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); - let jsterm = yield getSplitConsole(); + let jsterm = yield getSplitConsole(gDevTools.getToolbox(gPanel.target)); let executed = jsterm.execute("1+1"); yield oncePaused; let updatedFrame = yield waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES); let variables = gDebugger.DebuggerView.Variables; is(variables._store.length, 3, "Correct number of scopes available"); is(variables.getScopeAtIndex(0).name, "With scope [Object]", @@ -49,25 +49,9 @@ function test() { "Paused with correct scope (2)"); let onceResumed = gTarget.once("thread-resumed"); EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); yield onceResumed; yield executed; }); - - function getSplitConsole() { - return new Promise(resolve => { - let toolbox = gDevTools.getToolbox(gPanel.target); - toolbox.once("webconsole-ready", () => { - ok(toolbox.splitConsole, "Split console is shown."); - let jsterm = toolbox.getPanel("webconsole").hud.jsterm; - resolve(jsterm); - }); - EventUtils.synthesizeKey("VK_ESCAPE", {}, gDebugger); - }); - } } - -registerCleanupFunction(() => { - Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); -});
--- a/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_split-console-keypress.js @@ -89,24 +89,9 @@ function test() { gPanel.target.on("thread-resumed", () => { is(gThreadClient.paused, false, "Should not be paused after resume"); // Final test: did we preserve console inputNode focus during resume? is(consoleLostFocus, false, "Resume - console should keep focus"); closeDebuggerAndFinish(gPanel); }); }); - - function getSplitConsole(toolbox, theDebugger) { - return new Promise(resolve => { - toolbox.once("webconsole-ready", () => { - let jsterm = toolbox.getPanel("webconsole").hud.jsterm; - resolve(jsterm); - }); - EventUtils.synthesizeKey("VK_ESCAPE", {}, theDebugger); - }); - } } - -registerCleanupFunction(() => { - // We don't want the open split console to confuse other tests.. - Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); -});
new file mode 100644 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-console.js @@ -0,0 +1,145 @@ +// Check to make sure that a worker can be attached to a toolbox +// and that the console works. + +var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html"; +var WORKER_URL = "code_WorkerActor.attachThread-worker.js"; + +function* initWorkerDebugger(TAB_URL, WORKER_URL) { + if (!DebuggerServer.initialized) { + DebuggerServer.init(); + DebuggerServer.addBrowserActors(); + } + + let client = new DebuggerClient(DebuggerServer.connectPipe()); + yield connect(client); + + let tab = yield addTab(TAB_URL); + let { tabs } = yield listTabs(client); + let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL)); + + yield createWorkerInTab(tab, WORKER_URL); + + let { workers } = yield listWorkers(tabClient); + let [, workerClient] = yield attachWorker(tabClient, + findWorker(workers, WORKER_URL)); + + let toolbox = yield gDevTools.showToolbox(TargetFactory.forWorker(workerClient), + "jsdebugger", + Toolbox.HostType.WINDOW); + + let debuggerPanel = toolbox.getCurrentPanel(); + let gDebugger = debuggerPanel.panelWin; + + return {client,tab,tabClient,workerClient,toolbox,gDebugger}; +} + +add_task(function* testNormalExecution() { + let {client,tab,tabClient,workerClient,toolbox,gDebugger} = + yield initWorkerDebugger(TAB_URL, WORKER_URL); + + let jsterm = yield getSplitConsole(toolbox); + let executed = yield jsterm.execute("this.location.toString()"); + ok(executed.textContent.includes(WORKER_URL), + "Evaluating the global's location works"); + + yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient)); + terminateWorkerInTab(tab, WORKER_URL); + yield waitForWorkerClose(workerClient); + yield close(client); + yield removeTab(tab); +}); + +add_task(function* testWhilePaused() { + let {client,tab,tabClient,workerClient,toolbox,gDebugger} = + yield initWorkerDebugger(TAB_URL, WORKER_URL); + + let gTarget = gDebugger.gTarget; + let gResumeButton = gDebugger.document.getElementById("resume"); + let gResumeKey = gDebugger.document.getElementById("resumeKey"); + + // Execute some basic math to make sure evaluations are working. + let jsterm = yield getSplitConsole(toolbox); + let executed = yield jsterm.execute("10000+1"); + ok(executed.textContent.includes("10001"), "Text for message appeared correct"); + + // Pause the worker by waiting for next execution and then sending a message to + // it from the main thread. + let oncePaused = gTarget.once("thread-paused"); + EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); + once(gDebugger.gClient, "willInterrupt").then(() => { + info("Posting message to worker, then waiting for a pause"); + postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + }); + yield oncePaused; + + let command1 = jsterm.execute("10000+2"); + let command2 = jsterm.execute("10000+3"); + let command3 = jsterm.execute("foobar"); // throw an error + + info ("Trying to get the result of command1"); + executed = yield command1; + ok(executed.textContent.includes("10002"), + "command1 executed successfully"); + + 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"), + "command3 executed successfully"); + + let onceResumed = gTarget.once("thread-resumed"); + EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); + yield onceResumed; + + yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient)); + terminateWorkerInTab(tab, WORKER_URL); + yield waitForWorkerClose(workerClient); + yield close(client); + yield removeTab(tab); +}); + +// Test to see if creating the pause from the console works. +add_task(function* testPausedByConsole() { + let {client,tab,tabClient,workerClient,toolbox,gDebugger} = + yield initWorkerDebugger(TAB_URL, WORKER_URL); + + let gTarget = gDebugger.gTarget; + let gResumeButton = gDebugger.document.getElementById("resume"); + let gResumeKey = gDebugger.document.getElementById("resumeKey"); + + let jsterm = yield getSplitConsole(toolbox); + let executed = yield jsterm.execute("10000+1"); + ok(executed.textContent.includes("10001"), + "Text for message appeared correct"); + + let oncePaused = gTarget.once("thread-paused"); + EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); + let pausedExecution = jsterm.execute("10000+2"); + + info("Executed a command with 'break on next' active, waiting for pause"); + yield oncePaused; + + executed = yield jsterm.execute("10000+3"); + ok(executed.textContent.includes("10003"), + "Text for message appeared correct"); + + info("Waiting for a resume"); + let onceResumed = gTarget.once("thread-resumed"); + EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger); + yield onceResumed; + + executed = yield pausedExecution; + ok(executed.textContent.includes("10002"), + "Text for message appeared correct"); + + yield gDevTools.closeToolbox(TargetFactory.forWorker(workerClient)); + terminateWorkerInTab(tab, WORKER_URL); + yield waitForWorkerClose(workerClient); + yield close(client); + yield removeTab(tab); +});
--- a/devtools/client/debugger/test/mochitest/head.js +++ b/devtools/client/debugger/test/mochitest/head.js @@ -1185,8 +1185,33 @@ function afterDispatch(store, type) { predicate: action => ( action.type === type && action.status ? action.status === "done" : true ), run: resolve }); }); } + +// Return a promise with a reference to jsterm, opening the split +// console if necessary. This cleans up the split console pref so +// it won't pollute other tests. +function getSplitConsole(toolbox, win) { + registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); + }); + + if (!win) { + win = toolbox.doc.defaultView; + } + + if (!toolbox.splitConsole) { + EventUtils.synthesizeKey("VK_ESCAPE", {}, win); + } + + return new Promise(resolve => { + toolbox.getPanelWhenReady("webconsole").then(() => { + ok(toolbox.splitConsole, "Split console is shown."); + let jsterm = toolbox.getPanel("webconsole").hud.jsterm; + resolve(jsterm); + }); + }); +}
--- a/devtools/client/framework/target.js +++ b/devtools/client/framework/target.js @@ -727,18 +727,24 @@ WorkerTarget.prototype = { get isTabActor() { return true; }, get url() { return this._workerClient.url; }, + get isWorkerTarget() { + return true; + }, + get form() { - return {}; + return { + consoleActor: this._workerClient.consoleActor + }; }, get activeTab() { return this._workerClient; }, get client() { return this._workerClient.client;
--- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -5028,23 +5028,27 @@ WebConsoleConnectionProxy.prototype = { this._connectTimer.cancel(); this._connectTimer = null; }, () => { this._connectTimer = null; }); let client = this.client = this.target.client; - client.addListener("logMessage", this._onLogMessage); - client.addListener("pageError", this._onPageError); - client.addListener("consoleAPICall", this._onConsoleAPICall); - client.addListener("fileActivity", this._onFileActivity); - client.addListener("reflowActivity", this._onReflowActivity); - client.addListener("serverLogCall", this._onServerLogCall); - client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited); + if (this.target.isWorkerTarget) { + // XXXworkers: Not Console API yet inside of workers (Bug 1209353). + } else { + client.addListener("logMessage", this._onLogMessage); + client.addListener("pageError", this._onPageError); + client.addListener("consoleAPICall", this._onConsoleAPICall); + client.addListener("fileActivity", this._onFileActivity); + client.addListener("reflowActivity", this._onReflowActivity); + client.addListener("serverLogCall", this._onServerLogCall); + client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited); + } this.target.on("will-navigate", this._onTabNavigated); this.target.on("navigate", this._onTabNavigated); this._consoleActor = this.target.form.consoleActor; if (this.target.isTabActor) { let tab = this.target.form; this.owner.onLocationChange(tab.url, tab.title); }
--- a/devtools/server/actors/webconsole.js +++ b/devtools/server/actors/webconsole.js @@ -1,55 +1,42 @@ /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ /* vim: set ts=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/. */ "use strict"; +const Services = require("Services"); const { Cc, Ci, Cu } = require("chrome"); const { DebuggerServer, ActorPool } = require("devtools/server/main"); const { EnvironmentActor, ThreadActor } = require("devtools/server/actors/script"); const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object"); const DevToolsUtils = require("devtools/shared/DevToolsUtils"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => { - return require("devtools/shared/webconsole/network-monitor") - .NetworkMonitor; -}); -XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => { - return require("devtools/shared/webconsole/network-monitor") - .NetworkMonitorChild; -}); -XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => { - return require("devtools/shared/webconsole/network-monitor") - .ConsoleProgressListener; -}); -XPCOMUtils.defineLazyGetter(this, "events", () => { - return require("sdk/event/core"); -}); -XPCOMUtils.defineLazyGetter(this, "ServerLoggingListener", () => { - return require("devtools/shared/webconsole/server-logger") - .ServerLoggingListener; -}); +loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true); +loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true); +loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true); +loader.lazyRequireGetter(this, "events", "sdk/event/core"); +loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true); for (let name of ["WebConsoleUtils", "ConsoleServiceListener", "ConsoleAPIListener", "addWebConsoleCommands", "JSPropertyProvider", "ConsoleReflowListener", "CONSOLE_WORKER_IDS"]) { Object.defineProperty(this, name, { get: function(prop) { if (prop == "WebConsoleUtils") { prop = "Utils"; } - return require("devtools/shared/webconsole/utils")[prop]; + if (isWorker) { + return require("devtools/shared/webconsole/worker-utils")[prop]; + } else { + return require("devtools/shared/webconsole/utils")[prop]; + } }.bind(null, name), configurable: true, enumerable: true }); } /** * The WebConsoleActor implements capabilities needed for the Web Console @@ -551,16 +538,21 @@ WebConsoleActor.prototype = * * @param object aRequest * The JSON request object received from the Web Console client. * @return object * The response object which holds the startedListeners array. */ onStartListeners: function WCA_onStartListeners(aRequest) { + // XXXworkers: Not handling the Console API yet for workers (Bug 1209353). + if (isWorker) { + aRequest.listeners = []; + } + let startedListeners = []; let window = !this.parentActor.isRootActor ? this.window : null; let appId = null; let messageManager = null; if (this._parentIsContentActor) { appId = this.parentActor.docShell.appId; messageManager = this.parentActor.messageManager; @@ -844,18 +836,22 @@ 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); - errorMessage = error && (typeof error === "object") - ? error.unsafeDereference().toString() + // 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() : "" + error; } } // If a value is encountered that the debugger server doesn't support yet, // the console should remain functional. let resultGrip; try { @@ -894,18 +890,18 @@ WebConsoleActor.prototype = // This is the case of the paused debugger if (frameActorId) { let frameActor = this.conn.getActor(frameActorId); if (frameActor) { let frame = frameActor.frame; environment = frame.environment; } else { - Cu.reportError("Web Console Actor: the frame actor was not found: " + - frameActorId); + DevToolsUtils.reportException("onAutocomplete", + Error("The frame actor was not found: " + frameActorId)); } } // This is the general case (non-paused debugger) else { dbgObject = this.dbg.makeGlobalObjectReference(this.evalWindow); } let result = JSPropertyProvider(dbgObject, environment, aRequest.text, @@ -1038,19 +1034,23 @@ WebConsoleActor.prototype = // helpers like cd(), where we users sometimes want to pass a cross-origin // window. To circumvent this restriction, we use exportFunction along // with a special option designed for this purpose. See bug 1051224. obj[name] = Cu.exportFunction(obj[name], evalWindow, { allowCrossOriginArguments: true }); } for (let name in helpers.sandbox) { let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name); - maybeExport(desc, 'get'); - maybeExport(desc, 'set'); - maybeExport(desc, 'value'); + + // Workers don't have access to Cu so won't be able to exportFunction. + if (!isWorker) { + maybeExport(desc, 'get'); + maybeExport(desc, 'set'); + maybeExport(desc, 'value'); + } if (desc.value) { // Make sure the helpers can be used during eval. desc.value = aDebuggerGlobal.makeDebuggeeValue(desc.value); } Object.defineProperty(helpers.sandbox, name, desc); } return helpers; }, @@ -1133,18 +1133,18 @@ WebConsoleActor.prototype = // 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 { - Cu.reportError("Web Console Actor: the frame actor was not found: " + - aOptions.frameActor); + 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
--- a/devtools/server/actors/worker.js +++ b/devtools/server/actors/worker.js @@ -37,16 +37,17 @@ function WorkerActor(dbg) { } WorkerActor.prototype = { actorPrefix: "worker", form: function () { return { actor: this.actorID, + consoleActor: this._consoleActor, url: this._dbg.url, type: this._dbg.type }; }, onAttach: function () { if (this._dbg.isClosed) { return { error: "closed" }; @@ -82,23 +83,25 @@ WorkerActor.prototype = { return { type: "connected", threadActor: this._threadActor }; } return DebuggerServer.connectToWorker( this.conn, this._dbg, this.actorID, request.options - ).then(({ threadActor, transport }) => { + ).then(({ threadActor, transport, consoleActor }) => { this._threadActor = threadActor; this._transport = transport; + this._consoleActor = consoleActor; return { type: "connected", - threadActor: this._threadActor + threadActor: this._threadActor, + consoleActor: this._consoleActor }; }, (error) => { return { error: error.toString() }; }); }, onClose: function () { if (this._isAttached) {
--- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -828,39 +828,32 @@ var DebuggerServer = { aDbg.postMessage(JSON.stringify({ type: "connect", id: aId, options: aOptions })); // Steps 3-5 are performed on the worker thread (see worker.js). - // Step 6: Wait for a response from the worker debugger. + // Step 6: Wait for a connection response from the worker debugger. let listener = { onClose: () => { aDbg.removeListener(listener); reject("closed"); }, onMessage: (message) => { let packet = JSON.parse(message); - if (packet.type !== "message" || packet.id !== aId) { + if (packet.type !== "connected" || packet.id !== aId) { return; } - message = packet.message; - if (message.error) { - reject(error); - } - - if (message.type !== "paused") { - return; - } - + // The initial connection packet has been received, don't + // need to listen any longer aDbg.removeListener(listener); // Step 7: Create a transport for the connection to the worker. let transport = new WorkerDebuggerTransport(aDbg, aId); transport.ready(); transport.hooks = { onClosed: () => { if (!aDbg.isClosed) { @@ -882,17 +875,18 @@ var DebuggerServer = { }; // Ensure that any packets received from the client on the main thread // to actors on the worker thread are forwarded to the server on the // worker thread. aConnection.setForwarding(aId, transport); resolve({ - threadActor: message.from, + threadActor: packet.threadActor, + consoleActor: packet.consoleActor, transport: transport }); } }; aDbg.addListener(listener); }); },
--- a/devtools/server/worker.js +++ b/devtools/server/worker.js @@ -19,16 +19,17 @@ this.rpc = function (method, ...params) return deferred.promise; }; loadSubScript("resource://devtools/shared/worker/loader.js"); var Promise = worker.require("promise"); var { ActorPool } = worker.require("devtools/server/actors/common"); var { ThreadActor } = worker.require("devtools/server/actors/script"); +var { WebConsoleActor } = worker.require("devtools/server/actors/webconsole"); var { TabSources } = worker.require("devtools/server/actors/utils/TabSources"); var makeDebugger = worker.require("devtools/server/actors/utils/make-debugger"); var { DebuggerServer } = worker.require("devtools/server/main"); DebuggerServer.init(); DebuggerServer.createRootActor = function () { throw new Error("Should never get here!"); }; @@ -49,44 +50,51 @@ this.addEventListener("message", functi }; // Step 4: Create a thread actor for the connection to the parent. let pool = new ActorPool(connection); connection.addActorPool(pool); let sources = null; - let actor = new ThreadActor({ + let parent = { makeDebugger: makeDebugger.bind(null, { findDebuggees: () => { return [this.global]; }, shouldAddNewGlobalAsDebuggee: () => { return true; }, }), get sources() { if (sources === null) { - sources = new TabSources(actor); + sources = new TabSources(threadActor); } return sources; - } - }, global); + }, - pool.addActor(actor); + window: global + }; + + let threadActor = new ThreadActor(parent, global); + pool.addActor(threadActor); - // Step 5: Attach to the thread actor. - // - // This will cause a packet to be sent over the connection to the parent. - // Because this connection uses WorkerDebuggerTransport internally, this - // packet will be sent using WorkerDebuggerGlobalScope.postMessage, causing - // an onMessage event to be fired on the WorkerDebugger in the main thread. - actor.onAttach({}); + let consoleActor = new WebConsoleActor(connection, parent); + pool.addActor(consoleActor); + + // Step 5: Send a response packet to the parent to notify + // it that a connection has been established. + postMessage(JSON.stringify({ + type: "connected", + id: packet.id, + threadActor: threadActor.actorID, + consoleActor: consoleActor.actorID, + })); break; case "disconnect": connections[packet.id].connection.close(); break; case "rpc": let deferred = rpcDeferreds[packet.id];
--- a/devtools/shared/client/main.js +++ b/devtools/shared/client/main.js @@ -1386,30 +1386,46 @@ WorkerClient.prototype = { telemetry: "WORKERDETACH" }), attachThread: function(aOptions = {}, aOnResponse = noop) { if (this.thread) { DevToolsUtils.executeSoon(() => aOnResponse({ type: "connected", threadActor: this.thread._actor, + consoleActor: this.consoleActor, }, this.thread)); return; } + // The connect call on server doesn't attach the thread as of version 44. this.request({ to: this._actor, type: "connect", options: aOptions, - }, (aResponse) => { - if (!aResponse.error) { - this.thread = new ThreadClient(this, aResponse.threadActor); + }, (connectReponse) => { + if (connectReponse.error) { + aOnResponse(connectReponse, null); + return; + } + + this.request({ + to: connectReponse.threadActor, + type: "attach" + }, (attachResponse) => { + if (attachResponse.error) { + aOnResponse(attachResponse, null); + } + + this.thread = new ThreadClient(this, connectReponse.threadActor); + this.consoleActor = connectReponse.consoleActor; this.client.registerClient(this.thread); - } - aOnResponse(aResponse, this.thread); + + aOnResponse(connectReponse, this.thread); + }); }); }, _onClose: function () { this.removeListener("close", this._onClose); this.client.unregisterClient(this); this._isClosed = true;
--- a/devtools/shared/webconsole/client.js +++ b/devtools/shared/webconsole/client.js @@ -291,16 +291,23 @@ WebConsoleClient.prototype = { } }); }, /** * Handler for the actors's unsolicited evaluationResult packet. */ onEvaluationResult: function(aNotification, aPacket) { + // The client on the main thread can receive notification packets from + // multiple webconsole actors: the one on the main thread and the ones + // on worker threads. So make sure we should be handling this request. + if (aPacket.from !== this._actor) { + return; + } + // Find the associated callback based on this ID, and fire it. // In a sync evaluation, this would have already been called in // direct response to the client.request function. let onResponse = this.pendingEvaluationResults.get(aPacket.resultID); if (onResponse) { onResponse(aPacket); this.pendingEvaluationResults.delete(aPacket.resultID); } else {
--- a/devtools/shared/webconsole/moz.build +++ b/devtools/shared/webconsole/moz.build @@ -10,9 +10,10 @@ if CONFIG['OS_TARGET'] != 'Android': DevToolsModules( 'client.js', 'network-helper.js', 'network-monitor.js', 'server-logger-monitor.js', 'server-logger.js', 'utils.js', + 'worker-utils.js', )
new file mode 100644 --- /dev/null +++ b/devtools/shared/webconsole/worker-utils.js @@ -0,0 +1,13 @@ +// XXXworkers This file is loaded on the server side for worker debugging. +// Since the server is running in the worker thread, it doesn't +// have access to Services / Components. This functionality +// is stubbed out to prevent errors, and will need to implemented +// for Bug 1209353. + +exports.Utils = { l10n: function() {} }; +exports.ConsoleServiceListener = function() {}; +exports.ConsoleAPIListener = function() {}; +exports.addWebConsoleCommands = function() {}; +exports.JSPropertyProvider = function() {}; +exports.ConsoleReflowListener = function() {}; +exports.CONSOLE_WORKER_IDS = [];
--- a/devtools/shared/worker/loader.js +++ b/devtools/shared/worker/loader.js @@ -437,18 +437,22 @@ var { xpcInspector }; } else { // Worker thread let requestors = []; let scope = this; let xpcInspector = { + get eventLoopNestLevel() { + return requestors.length; + }, + get lastNestRequestor() { - return requestors.length === 0 ? null : requestors[0]; + return requestors.length === 0 ? null : requestors[requestors.length - 1]; }, enterNestedEventLoop: function (requestor) { requestors.push(requestor); scope.enterEventLoop(); return requestors.length; },