From 7d018750a18cd43155d0e15f2c32936c68d837f7 Mon Sep 17 00:00:00 2001 From: Narcis Beleuzu Date: Fri, 14 Jun 2019 07:20:42 +0300 Subject: [PATCH] Backed out 13 changesets (bug 1494796) for dt failures on browser_dbg-navigation.js . CLOSED TREE Backed out changeset 5db908b26d50 (bug 1494796) Backed out changeset c48f00f0df72 (bug 1494796) Backed out changeset 591453b88e8b (bug 1494796) Backed out changeset a14e820311bc (bug 1494796) Backed out changeset 0e214d450b35 (bug 1494796) Backed out changeset 1a4ab8b35a85 (bug 1494796) Backed out changeset fe1559f5f1d4 (bug 1494796) Backed out changeset 35d967de4223 (bug 1494796) Backed out changeset 1d21a55cae15 (bug 1494796) Backed out changeset 33eec873a43e (bug 1494796) Backed out changeset 25e69c21dc2e (bug 1494796) Backed out changeset b900d41c8ae8 (bug 1494796) Backed out changeset c2a034e34fa6 (bug 1494796) --- .../src/actions/breakpoints/modify.js | 92 ++-- .../debugger/src/actions/pause/resumed.js | 5 +- .../src/actions/pause/tests/pause.spec.js | 8 +- .../src/actions/types/BreakpointAction.js | 8 +- .../debugger/src/client/firefox/events.js | 5 +- .../debugger/src/client/firefox/types.js | 8 +- .../debugger/src/reducers/breakpoints.js | 10 +- .../src/reducers/pending-breakpoints.js | 5 - .../test/mochitest/browser_dbg-navigation.js | 1 - .../mochitest/browser_dbg-old-breakpoint.js | 4 +- .../test/mochitest/browser_dbg-reload.js | 2 - .../mochitest/browser_dbg-worker-scopes.js | 2 - .../client/debugger/test/mochitest/helpers.js | 2 - .../test/browser_toolbox_hosts_size.js | 8 - .../test/browser_dbg_worker-console-02.js | 3 +- devtools/server/actors/root.js | 2 - .../server/actors/targets/browsing-context.js | 5 +- devtools/server/actors/targets/worker.js | 1 - devtools/server/actors/thread.js | 162 +++---- devtools/server/tests/unit/head_dbg.js | 5 +- devtools/server/tests/unit/test_attach.js | 4 +- .../server/tests/unit/test_logpoint-03.js | 11 +- devtools/server/tests/unit/test_nesting-01.js | 3 +- devtools/server/tests/unit/test_nesting-02.js | 3 +- devtools/server/tests/unit/test_nesting-03.js | 18 +- .../server/tests/unit/test_registerClient.js | 94 ++++ .../server/tests/unit/test_stepping-06.js | 1 + devtools/server/tests/unit/testactors.js | 2 - devtools/server/tests/unit/xpcshell.ini | 1 + devtools/shared/client/debugger-client.js | 159 ++++--- .../shared/client/deprecated-thread-client.js | 436 ------------------ devtools/shared/client/moz.build | 1 - devtools/shared/client/thread-client.js | 350 ++++++++------ devtools/shared/fronts/source.js | 16 +- .../shared/fronts/targets/browsing-context.js | 1 - .../shared/fronts/targets/content-process.js | 1 - .../shared/fronts/targets/target-mixin.js | 33 +- devtools/shared/fronts/targets/worker.js | 2 - devtools/shared/specs/index.js | 3 +- devtools/shared/specs/thread.js | 14 +- package-lock.json | 5 - package.json | 4 +- 42 files changed, 608 insertions(+), 892 deletions(-) create mode 100644 devtools/server/tests/unit/test_registerClient.js delete mode 100644 devtools/shared/client/deprecated-thread-client.js diff --git a/devtools/client/debugger/src/actions/breakpoints/modify.js b/devtools/client/debugger/src/actions/breakpoints/modify.js index 32c92d40b096..cbdb56d9def8 100644 --- a/devtools/client/debugger/src/actions/breakpoints/modify.js +++ b/devtools/client/debugger/src/actions/breakpoints/modify.js @@ -23,7 +23,6 @@ import { import { setBreakpointPositions } from "./breakpointPositions"; -import { PROMISE } from "../utils/middleware/promise"; import { recordEvent } from "../../utils/telemetry"; import { comparePosition } from "../../utils/location"; import { getTextAtPosition } from "../../utils/source"; @@ -61,21 +60,24 @@ import type { // breakpoint will be added to the reducer, to restore the above invariant. // See syncBreakpoint.js for more. -function clientSetBreakpoint(client, state, breakpoint: Breakpoint) { - const breakpointLocation = makeBreakpointLocation( - state, - breakpoint.generatedLocation - ); - return client.setBreakpoint(breakpointLocation, breakpoint.options); +function clientSetBreakpoint(breakpoint: Breakpoint) { + return ({ getState, client }: ThunkArgs) => { + const breakpointLocation = makeBreakpointLocation( + getState(), + breakpoint.generatedLocation + ); + return client.setBreakpoint(breakpointLocation, breakpoint.options); + }; } -function clientRemoveBreakpoint( - client, - state, - generatedLocation: SourceLocation -) { - const breakpointLocation = makeBreakpointLocation(state, generatedLocation); - return client.removeBreakpoint(breakpointLocation); +function clientRemoveBreakpoint(generatedLocation: SourceLocation) { + return ({ getState, client }: ThunkArgs) => { + const breakpointLocation = makeBreakpointLocation( + getState(), + generatedLocation + ); + return client.removeBreakpoint(breakpointLocation); + }; } export function enableBreakpoint(cx: Context, initialBreakpoint: Breakpoint) { @@ -85,12 +87,13 @@ export function enableBreakpoint(cx: Context, initialBreakpoint: Breakpoint) { return; } - return dispatch({ + dispatch({ type: "SET_BREAKPOINT", cx, breakpoint: { ...breakpoint, disabled: false }, - [PROMISE]: clientSetBreakpoint(client, getState(), breakpoint), }); + + return dispatch(clientSetBreakpoint(breakpoint)); }; } @@ -158,16 +161,15 @@ export function addBreakpoint( return; } - return dispatch({ - type: "SET_BREAKPOINT", - cx, - breakpoint, + dispatch({ type: "SET_BREAKPOINT", cx, breakpoint }); + + if (disabled) { // If we just clobbered an enabled breakpoint with a disabled one, we need // to remove any installed breakpoint in the server. - [PROMISE]: disabled - ? clientRemoveBreakpoint(client, getState(), generatedLocation) - : clientSetBreakpoint(client, getState(), breakpoint), - }); + return dispatch(clientRemoveBreakpoint(generatedLocation)); + } + + return dispatch(clientSetBreakpoint(breakpoint)); }; } @@ -186,19 +188,18 @@ export function removeBreakpoint(cx: Context, initialBreakpoint: Breakpoint) { return; } - return dispatch({ + dispatch({ type: "REMOVE_BREAKPOINT", cx, location: breakpoint.location, - // If the breakpoint is disabled then it is not installed in the server. - [PROMISE]: breakpoint.disabled - ? Promise.resolve() - : clientRemoveBreakpoint( - client, - getState(), - breakpoint.generatedLocation - ), }); + + // If the breakpoint is disabled then it is not installed in the server. + if (breakpoint.disabled) { + return; + } + + return dispatch(clientRemoveBreakpoint(breakpoint.generatedLocation)); }; } @@ -214,12 +215,6 @@ export function removeBreakpointAtGeneratedLocation( target: SourceLocation ) { return ({ dispatch, getState, client }: ThunkArgs) => { - // remove breakpoint from the server - const onBreakpointRemoved = clientRemoveBreakpoint( - client, - getState(), - target - ); // Remove any breakpoints matching the generated location. const breakpoints = getBreakpointsList(getState()); for (const { location, generatedLocation } of breakpoints) { @@ -231,7 +226,6 @@ export function removeBreakpointAtGeneratedLocation( type: "REMOVE_BREAKPOINT", cx, location, - [PROMISE]: onBreakpointRemoved, }); } } @@ -250,7 +244,9 @@ export function removeBreakpointAtGeneratedLocation( }); } } - return onBreakpointRemoved; + + // Remove the breakpoint from the client itself. + return dispatch(clientRemoveBreakpoint(target)); }; } @@ -267,16 +263,13 @@ export function disableBreakpoint(cx: Context, initialBreakpoint: Breakpoint) { return; } - return dispatch({ + dispatch({ type: "SET_BREAKPOINT", cx, breakpoint: { ...breakpoint, disabled: true }, - [PROMISE]: clientRemoveBreakpoint( - client, - getState(), - breakpoint.generatedLocation - ), }); + + return dispatch(clientRemoveBreakpoint(breakpoint.generatedLocation)); }; } @@ -305,11 +298,12 @@ export function setBreakpointOptions( // Note: setting a breakpoint's options implicitly enables it. breakpoint = { ...breakpoint, disabled: false, options }; - return dispatch({ + dispatch({ type: "SET_BREAKPOINT", cx, breakpoint, - [PROMISE]: clientSetBreakpoint(client, getState(), breakpoint), }); + + return dispatch(clientSetBreakpoint(breakpoint)); }; } diff --git a/devtools/client/debugger/src/actions/pause/resumed.js b/devtools/client/debugger/src/actions/pause/resumed.js index 12b2492f5bdd..93cf5389faed 100644 --- a/devtools/client/debugger/src/actions/pause/resumed.js +++ b/devtools/client/debugger/src/actions/pause/resumed.js @@ -9,7 +9,7 @@ import { evaluateExpressions } from "../expressions"; import { inDebuggerEval } from "../../utils/pause"; import type { ThunkArgs } from "../types"; -import type { ActorId } from "../../types"; +import type { ResumedPacket } from "../../client/firefox/types"; /** * Debugger has just resumed @@ -17,8 +17,9 @@ import type { ActorId } from "../../types"; * @memberof actions/pause * @static */ -export function resumed(thread: ActorId) { +export function resumed(packet: ResumedPacket) { return async ({ dispatch, client, getState }: ThunkArgs) => { + const thread = packet.from; const why = getPauseReason(getState(), thread); const wasPausedInEval = inDebuggerEval(why); const wasStepping = isStepping(getState(), thread); diff --git a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js index 19e5ff73e5d2..1925081efd1c 100644 --- a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js +++ b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js @@ -72,7 +72,6 @@ const mockThreadClient = { }, getBreakpointPositions: async () => ({}), getBreakableLines: async () => [], - actorID: "threadActorID", }; const mockFrameId = "1"; @@ -100,6 +99,9 @@ function createPauseInfo( }; } +function resumedPacket() { + return { from: "FakeThread", type: "resumed" }; +} describe("pause", () => { describe("stepping", () => { it("should set and clear the command", async () => { @@ -381,7 +383,7 @@ describe("pause", () => { const cx = selectors.getThreadContext(getState()); dispatch(actions.stepIn(cx)); - await dispatch(actions.resumed(mockThreadClient.actorID)); + await dispatch(actions.resumed(resumedPacket())); expect(client.evaluateExpressions.mock.calls).toHaveLength(1); }); @@ -399,7 +401,7 @@ describe("pause", () => { client.evaluateExpressions.mockReturnValue(Promise.resolve(["YAY"])); await dispatch(actions.paused(mockPauseInfo)); - await dispatch(actions.resumed(mockThreadClient.actorID)); + await dispatch(actions.resumed(resumedPacket())); const expression = selectors.getExpression(getState(), "foo"); expect(expression && expression.value).toEqual("YAY"); }); diff --git a/devtools/client/debugger/src/actions/types/BreakpointAction.js b/devtools/client/debugger/src/actions/types/BreakpointAction.js index 8736f1760703..4bfd68a2215e 100644 --- a/devtools/client/debugger/src/actions/types/BreakpointAction.js +++ b/devtools/client/debugger/src/actions/types/BreakpointAction.js @@ -41,16 +41,16 @@ export type BreakpointAction = +index: number, +breakpoint: XHRBreakpoint, |}> - | PromiseAction<{| + | {| +type: "SET_BREAKPOINT", +cx: Context, +breakpoint: Breakpoint, - |}> - | PromiseAction<{| + |} + | {| +type: "REMOVE_BREAKPOINT", +cx: Context, +location: SourceLocation, - |}> + |} | {| +type: "REMOVE_PENDING_BREAKPOINT", +cx: Context, diff --git a/devtools/client/debugger/src/client/firefox/events.js b/devtools/client/debugger/src/client/firefox/events.js index ea36050861a3..11f7eccfc790 100644 --- a/devtools/client/debugger/src/client/firefox/events.js +++ b/devtools/client/debugger/src/client/firefox/events.js @@ -6,6 +6,7 @@ import type { SourcePacket, + ResumedPacket, PausedPacket, ThreadClient, Actions, @@ -73,7 +74,7 @@ async function paused(threadClient: ThreadClient, packet: PausedPacket) { } } -function resumed(threadClient: ThreadClient) { +function resumed(threadClient: ThreadClient, packet: ResumedPacket) { // NOTE: the client suppresses resumed events while interrupted // to prevent unintentional behavior. // see [client docs](../README.md#interrupted) for more information. @@ -82,7 +83,7 @@ function resumed(threadClient: ThreadClient) { return; } - actions.resumed(threadClient.actorID); + actions.resumed(packet); } function newSource(threadClient: ThreadClient, { source }: SourcePacket) { diff --git a/devtools/client/debugger/src/client/firefox/types.js b/devtools/client/debugger/src/client/firefox/types.js index 8e70075c2d5a..36673616f1bd 100644 --- a/devtools/client/debugger/src/client/firefox/types.js +++ b/devtools/client/debugger/src/client/firefox/types.js @@ -138,6 +138,11 @@ export type PausedPacket = { }, }; +export type ResumedPacket = { + from: ActorId, + type: string, +}; + /** * Response from the `getFrames` function call * @memberof firefox @@ -182,7 +187,7 @@ export type TabPayload = { */ export type Actions = { paused: Pause => void, - resumed: ActorId => void, + resumed: ResumedPacket => void, newQueuedSources: (QueuedSourceData[]) => void, fetchEventListeners: () => void, updateWorkers: () => void, @@ -364,7 +369,6 @@ export type ThreadClient = { getLastPausePacket: () => ?PausedPacket, _parent: TabClient, actor: ActorId, - actorID: ActorId, request: (payload: Object) => Promise<*>, url: string, setActiveEventBreakpoints: (string[]) => void, diff --git a/devtools/client/debugger/src/reducers/breakpoints.js b/devtools/client/debugger/src/reducers/breakpoints.js index 6003627f8c3e..8a13ccb80a61 100644 --- a/devtools/client/debugger/src/reducers/breakpoints.js +++ b/devtools/client/debugger/src/reducers/breakpoints.js @@ -50,17 +50,11 @@ function update( ): BreakpointsState { switch (action.type) { case "SET_BREAKPOINT": { - if (action.status === "start") { - return setBreakpoint(state, action); - } - return state; + return setBreakpoint(state, action); } case "REMOVE_BREAKPOINT": { - if (action.status === "start") { - return removeBreakpoint(state, action); - } - return state; + return removeBreakpoint(state, action); } case "NAVIGATE": { diff --git a/devtools/client/debugger/src/reducers/pending-breakpoints.js b/devtools/client/debugger/src/reducers/pending-breakpoints.js index 849dc5d3ae0b..628fad03cf24 100644 --- a/devtools/client/debugger/src/reducers/pending-breakpoints.js +++ b/devtools/client/debugger/src/reducers/pending-breakpoints.js @@ -29,11 +29,6 @@ function update(state: PendingBreakpointsState = {}, action: Action) { return setBreakpoint(state, action); case "REMOVE_BREAKPOINT": - if (action.status === "start") { - return removeBreakpoint(state, action); - } - return state; - case "REMOVE_PENDING_BREAKPOINT": return removeBreakpoint(state, action); } diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js index b8439669b897..51713b1dda1a 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js @@ -53,6 +53,5 @@ add_task(async function() { await reload(dbg, "long.js"); await waitForSelectedSource(dbg, "long.js"); - await waitForRequestsToSettle(dbg); ok(getSelectedSource().url.includes("long.js"), "Selected source is long.js"); }); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js b/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js index 4254aaa578ac..29fd7ee8839c 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js @@ -38,7 +38,6 @@ add_task(async function() { const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + "doc-scripts.html", "jsdebugger"); const dbg = createDebuggerContext(toolbox); - const onBreakpoint = waitForDispatch(dbg, "SET_BREAKPOINT", 2); // Pending breakpoints are installed asynchronously, keep invoking the entry // function until the debugger pauses. @@ -46,11 +45,12 @@ add_task(async function() { invokeInTab("main"); return isPaused(dbg); }); - await onBreakpoint; ok(true, "paused at unmapped breakpoint"); await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 2); ok(true, "unmapped breakpoints shown in UI"); + await waitForRequestsToSettle(dbg); + await toolbox.destroy(); }); // Test that if we show a breakpoint with an old generated location, it is diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-reload.js b/devtools/client/debugger/test/mochitest/browser_dbg-reload.js index 70b667416cbd..dd2828b04cc9 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-reload.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-reload.js @@ -25,7 +25,6 @@ add_task(async function() { await selectSource(dbg, "sjs_code_reload"); await addBreakpoint(dbg, "sjs_code_reload", 2); - await waitForRequestsToSettle(dbg); await reload(dbg, "sjs_code_reload.sjs"); await waitForSelectedSource(dbg, "sjs_code_reload.sjs"); @@ -39,5 +38,4 @@ add_task(async function() { is(breakpointList.length, 1); is(breakpoint.location.line, 6); - await waitForRequestsToSettle(dbg); }); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js b/devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js index aeac756fba43..b2e225e4f8b5 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js @@ -43,9 +43,7 @@ add_task(async function() { await dbg.toolbox._target.waitForRequestsToSettle(); invokeInTab("startWorker"); await waitForPaused(dbg, "scopes-worker.js"); - const onRemoved = waitForDispatch(dbg, "REMOVE_BREAKPOINT"); await removeBreakpoint(dbg, workerSource.id, 11); - await onRemoved; // We should be paused at the first line of simple-worker.js assertPausedAtSourceAndLine(dbg, workerSource.id, 11); diff --git a/devtools/client/debugger/test/mochitest/helpers.js b/devtools/client/debugger/test/mochitest/helpers.js index ee443f93fb7e..edc32d2cb024 100644 --- a/devtools/client/debugger/test/mochitest/helpers.js +++ b/devtools/client/debugger/test/mochitest/helpers.js @@ -799,13 +799,11 @@ async function addBreakpoint(dbg, source, line, column, options) { source = findSource(dbg, source); const sourceId = source.id; const bpCount = dbg.selectors.getBreakpointCount(); - const onBreakpoint = waitForDispatch(dbg, "SET_BREAKPOINT"); await dbg.actions.addBreakpoint( getContext(dbg), { sourceId, line, column }, options ); - await onBreakpoint; is( dbg.selectors.getBreakpointCount(), bpCount + 1, diff --git a/devtools/client/framework/test/browser_toolbox_hosts_size.js b/devtools/client/framework/test/browser_toolbox_hosts_size.js index d9f7442853db..18b630d6efc5 100644 --- a/devtools/client/framework/test/browser_toolbox_hosts_size.js +++ b/devtools/client/framework/test/browser_toolbox_hosts_size.js @@ -33,15 +33,7 @@ add_task(async function() { iframe.style.minWidth = "1px"; // Disable the min width set in css is(iframe.clientWidth, panelWidth - 25, "The iframe fits within the available space"); - // on shutdown, the sidebar width will be set to the clientWidth of the iframe - const expectedWidth = iframe.clientWidth; await cleanup(toolbox); - // Wait until the toolbox-host-manager was destroyed and updated the preferences - // to avoid side effects in the next test. - await waitUntil(() => { - const savedWidth = Services.prefs.getIntPref("devtools.toolbox.sidebar.width"); - return savedWidth === expectedWidth; - }); }); add_task(async function() { diff --git a/devtools/client/shared/test/browser_dbg_worker-console-02.js b/devtools/client/shared/test/browser_dbg_worker-console-02.js index be4aca861f49..d10a0b7b6414 100644 --- a/devtools/client/shared/test/browser_dbg_worker-console-02.js +++ b/devtools/client/shared/test/browser_dbg_worker-console-02.js @@ -20,7 +20,6 @@ var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js"; add_task(async function testWhilePaused() { const dbg = await initWorkerDebugger(TAB_URL, WORKER_URL); const {client, tab, workerTargetFront, toolbox} = dbg; - const workerThreadClient = await workerTargetFront.getFront("context"); // Execute some basic math to make sure evaluations are working. const jsterm = await getSplitConsole(toolbox); @@ -28,7 +27,7 @@ add_task(async function testWhilePaused() { ok(executed.textContent.includes("10001"), "Text for message appeared correct"); await clickElement(dbg, "pause"); - workerThreadClient.once("willInterrupt").then(() => { + once(dbg.client, "willInterrupt").then(() => { info("Posting message to worker, then waiting for a pause"); postMessageToWorkerInTab(tab, WORKER_URL, "ping"); }); diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js index 99bb26f391a5..fea1e9a7cf8b 100644 --- a/devtools/server/actors/root.js +++ b/devtools/server/actors/root.js @@ -173,8 +173,6 @@ RootActor.prototype = { // Supports native log points and modifying the condition/log of an existing // breakpoints. Fx66+ nativeLogpoints: true, - // support older browsers for Fx69+ - hasThreadFront: true, }, /** diff --git a/devtools/server/actors/targets/browsing-context.js b/devtools/server/actors/targets/browsing-context.js index 5e71b1320a5e..605f44a63b3a 100644 --- a/devtools/server/actors/targets/browsing-context.js +++ b/devtools/server/actors/targets/browsing-context.js @@ -335,7 +335,7 @@ const browsingContextTargetPrototype = { * Getter for the browsing context's current DOM window. */ get window() { - return this.docShell && this.docShell.domWindow; + return this.docShell.domWindow; }, get outerWindowID() { @@ -1346,7 +1346,8 @@ const browsingContextTargetPrototype = { // will-navigate const threadActor = this.threadActor; if (threadActor.state == "paused") { - threadActor.unsafeSynchronize(Promise.resolve(threadActor.doResume())); + this.conn.send( + threadActor.unsafeSynchronize(Promise.resolve(threadActor.onResume()))); threadActor.dbg.enabled = false; } threadActor.disableAllBreakpoints(); diff --git a/devtools/server/actors/targets/worker.js b/devtools/server/actors/targets/worker.js index 7c2c1109dfef..8b583d258dd4 100644 --- a/devtools/server/actors/targets/worker.js +++ b/devtools/server/actors/targets/worker.js @@ -38,7 +38,6 @@ const WorkerTargetActor = protocol.ActorClassWithSpec(workerTargetSpec, { const form = { actor: this.actorID, consoleActor: this._consoleActor, - contextActor: this._threadActor, id: this._dbg.id, url: this._dbg.url, type: this._dbg.type, diff --git a/devtools/server/actors/thread.js b/devtools/server/actors/thread.js index d9431976139c..b9b252b3885b 100644 --- a/devtools/server/actors/thread.js +++ b/devtools/server/actors/thread.js @@ -217,7 +217,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { destroy: function() { dumpn("in ThreadActor.prototype.destroy"); if (this._state == "paused") { - this.doResume(); + this.onResume(); } this._xhrBreakpoints = []; @@ -252,25 +252,20 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { }, // Request handlers - onAttach: function({options}) { + onAttach: function(request) { if (this.state === "exited") { - return { - error: "exited", - message: "threadActor has exited", - }; + return { type: "exited" }; } if (this.state !== "detached") { - return { - error: "wrongState", - message: "Current state is " + this.state, - }; + return { error: "wrongState", + message: "Current state is " + this.state }; } this._state = "attached"; this._debuggerSourcesSeen = new WeakSet(); - Object.assign(this._options, options || {}); + Object.assign(this._options, request.options || {}); this.sources.setOptions(this._options); this.sources.on("newSource", this.onNewSourceEvent); @@ -282,8 +277,10 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { thread: this, }); - if (options.breakpoints) { - this._setBreakpointsOnAttach(options.breakpoints); + if (request.options.breakpoints) { + for (const { location, options } of Object.values(request.options.breakpoints)) { + this.setBreakpoint(location, options); + } } this.dbg.addDebuggees(); @@ -304,37 +301,24 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { // Put ourselves in the paused state. const packet = this._paused(); if (!packet) { - return { - error: "notAttached", - message: "cannot attach, could not create pause packet", - }; + return { error: "notAttached" }; } packet.why = { type: "attached" }; // Send the response to the attach request now (rather than - // returning it), because we're going to start a nested event - // loop here. - this.conn.send({from: this.actorID}); - this.conn.sendActorEvent(this.actorID, "paused", packet); + // returning it), because we're going to start a nested event loop + // here. + this.conn.send(packet); // Start a nested event loop. this._pushThreadPause(); // We already sent a response to this request, don't send one - // now + // now. return null; } catch (e) { reportError(e); - return { - error: "notAttached", - message: e.toString(), - }; - } - }, - - _setBreakpointsOnAttach(breakpoints) { - for (const { location, options } of Object.values(breakpoints)) { - this.setBreakpoint(location, options); + return { error: "notAttached", message: e.toString() }; } }, @@ -481,8 +465,9 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { this._debuggerSourcesSeen = null; dumpn("ThreadActor.prototype.onDetach: returning 'detached' packet"); - this.conn.sendActorEvent(this.actorID, "detached"); - return {}; + return { + type: "detached", + }; }, onReconfigure: function(request) { @@ -553,7 +538,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { const pkt = onPacket(packet); this._priorPause = pkt; - this.conn.sendActorEvent(this.actorID, "paused", pkt); + this.conn.send(pkt); } catch (error) { reportError(error); this.conn.send({ @@ -847,14 +832,14 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { * Handle attaching the various stepping hooks we need to attach when we * receive a resume request with a resumeLimit property. * - * @param Object { rewind, resumeLimit } - * The values received over the RDP. + * @param Object request + * The request packet received over the RDP. * @returns A promise that resolves to true once the hooks are attached, or is * rejected with an error packet. */ - _handleResumeLimit: async function({rewind, resumeLimit}) { - let steppingType = resumeLimit.type; - const rewinding = rewind; + _handleResumeLimit: async function(request) { + let steppingType = request.resumeLimit.type; + const rewinding = request.rewind; if (!["break", "step", "next", "finish", "warp"].includes(steppingType)) { return Promise.reject({ error: "badParameterType", @@ -945,7 +930,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { /** * Handle a protocol request to resume execution of the debuggee. */ - onResume: async function({resumeLimit, rewind}) { + onResume: function(request) { if (this._state !== "paused") { return { error: "wrongState", @@ -968,67 +953,53 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { }; } - if (rewind && !this.dbg.replaying) { + const rewinding = request && request.rewind; + if (rewinding && !this.dbg.replaying) { return { error: "cantRewind", message: "Can't rewind a debuggee that is not replaying.", }; } - try { - if (resumeLimit) { - await this._handleResumeLimit({resumeLimit, rewind}); - } else { - this._clearSteppingHooks(); - } + let resumeLimitHandled; + if (request && request.resumeLimit) { + resumeLimitHandled = this._handleResumeLimit(request); + } else { + this._clearSteppingHooks(); + resumeLimitHandled = Promise.resolve(true); + } + + return resumeLimitHandled.then(() => { + this.maybePauseOnExceptions(); // When replaying execution in a separate process we need to explicitly // notify that process when to resume execution. if (this.dbg.replaying) { - if (resumeLimit && resumeLimit.type == "warp") { - this.dbg.replayTimeWarp(resumeLimit.target); - } else if (rewind) { + if (request && request.resumeLimit && request.resumeLimit.type == "warp") { + this.dbg.replayTimeWarp(request.resumeLimit.target); + } else if (rewinding) { this.dbg.replayResumeBackward(); } else { this.dbg.replayResumeForward(); } } - this.doResume(); - return {}; - } catch (error) { + const packet = this._resumed(); + this._popThreadPause(); + // Tell anyone who cares of the resume (as of now, that's the xpcshell harness and + // devtools-startup.js when handling the --wait-for-jsdebugger flag) + if (Services.obs) { + Services.obs.notifyObservers(this, "devtools-thread-resumed"); + } + return packet; + }, error => { return error instanceof Error - ? { - error: "unknownError", + ? { error: "unknownError", message: DevToolsUtils.safeErrorString(error) } // It is a known error, and the promise was rejected with an error // packet. : error; - } - }, - - /** - * Only resume and notify necessary observers. This should be used in cases - * when we do not want to notify the front end of a resume, for example when - * we are shutting down. - */ - doResume() { - this.maybePauseOnExceptions(); - this._state = "running"; - - // Drop the actors in the pause actor pool. - this.conn.removeActorPool(this._pausePool); - - this._pausePool = null; - this._pauseActor = null; - this._popThreadPause(); - // Tell anyone who cares of the resume (as of now, that's the xpcshell harness and - // devtools-startup.js when handling the --wait-for-jsdebugger flag) - this.conn.sendActorEvent(this.actorID, "resumed"); - - if (Services.obs) { - Services.obs.notifyObservers(this, "devtools-thread-resumed"); - } + }); }, /** @@ -1165,30 +1136,30 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { /** * Handle a protocol request to pause the debuggee. */ - onInterrupt: function({when}) { + onInterrupt: function(request) { if (this.state == "exited") { return { type: "exited" }; } else if (this.state == "paused") { // TODO: return the actual reason for the existing pause. - this.conn.sendActorEvent(this.actorID, "paused", { why: { type: "alreadyPaused" }}); - return {}; + return { type: "paused", why: { type: "alreadyPaused" } }; } else if (this.state != "running") { return { error: "wrongState", message: "Received interrupt request in " + this.state + " state." }; } + try { // If execution should pause just before the next JavaScript bytecode is // executed, just set an onEnterFrame handler. - if (when == "onNext" && !this.dbg.replaying) { + if (request.when == "onNext" && !this.dbg.replaying) { const onEnterFrame = (frame) => { - this._pauseAndRespond(frame, { type: "interrupted", onNext: true }); + return this._pauseAndRespond(frame, { type: "interrupted", onNext: true }); }; this.dbg.onEnterFrame = onEnterFrame; - this.conn.sendActorEvent(this.actorID, "willInterrupt"); - return {}; + return { type: "willInterrupt" }; } + if (this.dbg.replaying) { this.dbg.replayPause(); } @@ -1207,8 +1178,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { // Send the response to the interrupt request now (rather than // returning it), because we're going to start a nested event loop // here. - this.conn.send({from: this.actorID, type: "interrupt"}); - this.conn.sendActorEvent(this.actorID, "paused", packet); + this.conn.send(packet); // Start a nested event loop. this._pushThreadPause(); @@ -1294,6 +1264,18 @@ const ThreadActor = ActorClassWithSpec(threadSpec, { return packet; }, + _resumed: function() { + this._state = "running"; + + // Drop the actors in the pause actor pool. + this.conn.removeActorPool(this._pausePool); + + this._pausePool = null; + this._pauseActor = null; + + return { from: this.actorID, type: "resumed" }; + }, + /** * Expire frame actors for frames that have been popped. * diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js index aec8305d2139..53f1fb0fa013 100644 --- a/devtools/server/tests/unit/head_dbg.js +++ b/devtools/server/tests/unit/head_dbg.js @@ -392,12 +392,9 @@ async function attachTestTab(client, title) { // thread. async function attachTestThread(client, title, callback = () => {}) { const targetFront = await attachTestTab(client, title); - const threadClient = await targetFront.getFront("context"); - const onPaused = threadClient.once("paused"); - await targetFront.attachThread({ + const [response, threadClient] = await targetFront.attachThread({ autoBlackBox: true, }); - const response = await onPaused; Assert.equal(threadClient.state, "paused", "Thread client is paused"); Assert.ok("why" in response); Assert.equal(response.why.type, "attached"); diff --git a/devtools/server/tests/unit/test_attach.js b/devtools/server/tests/unit/test_attach.js index 9193d773945d..24c5d89b9961 100644 --- a/devtools/server/tests/unit/test_attach.js +++ b/devtools/server/tests/unit/test_attach.js @@ -3,7 +3,7 @@ "use strict"; -const { ThreadClient } = require("devtools/shared/client/thread-client"); +const ThreadClient = require("devtools/shared/client/thread-client"); const { BrowsingContextTargetFront } = require("devtools/shared/fronts/targets/browsing-context"); /** @@ -13,7 +13,7 @@ const { BrowsingContextTargetFront } = require("devtools/shared/fronts/targets/b add_task(threadClientTest(({ threadClient, debuggee, client, targetFront }) => { ok(true, "Thread actor was able to attach"); ok(threadClient instanceof ThreadClient, "Thread client is valid"); - Assert.equal(threadClient.state, "attached", "Thread client is resumed"); + Assert.equal(threadClient.state, "attached", "Thread client is paused"); Assert.equal(String(debuggee), "[object Sandbox]", "Debuggee client is valid"); ok(client instanceof DebuggerClient, "Client is valid"); ok(targetFront instanceof BrowsingContextTargetFront, "TargetFront is valid"); diff --git a/devtools/server/tests/unit/test_logpoint-03.js b/devtools/server/tests/unit/test_logpoint-03.js index f43b9c829e45..92215bb8c61e 100644 --- a/devtools/server/tests/unit/test_logpoint-03.js +++ b/devtools/server/tests/unit/test_logpoint-03.js @@ -44,16 +44,13 @@ function test_simple_breakpoint() { ); // Set a logpoint which should throw an error message. - await gThreadClient.setBreakpoint({ + gThreadClient.setBreakpoint({ sourceUrl: source.url, line: 3, }, { logValue: "c" }); // Execute the rest of the code. - await gThreadClient.resume(); - Assert.equal(lastMessage.level, "logPointError"); - Assert.equal(lastMessage.arguments[0], "[Logpoint threw]: c is not defined"); - finishClient(gClient); + gThreadClient.resume(); }); /* eslint-disable */ @@ -65,4 +62,8 @@ function test_simple_breakpoint() { "test.js", 1); /* eslint-enable */ + + Assert.equal(lastMessage.level, "logPointError"); + Assert.equal(lastMessage.arguments[0], "[Logpoint threw]: c is not defined"); + finishClient(gClient); } diff --git a/devtools/server/tests/unit/test_nesting-01.js b/devtools/server/tests/unit/test_nesting-01.js index 91542acfae9a..3cb53d05d14c 100644 --- a/devtools/server/tests/unit/test_nesting-01.js +++ b/devtools/server/tests/unit/test_nesting-01.js @@ -19,9 +19,8 @@ function run_test() { gClient, "test-nesting", function(response, targetFront, threadClient) { // Reach over the protocol connection and get a reference to the thread actor. - // TODO: rewrite the test so we don't do this.. gThreadActor = - gClient._transport._serverConnection.getActor(threadClient.actorID); + threadClient._transport._serverConnection.getActor(threadClient._actor); test_nesting(); }); diff --git a/devtools/server/tests/unit/test_nesting-02.js b/devtools/server/tests/unit/test_nesting-02.js index ca2deb5c7743..e5c7d01730e7 100644 --- a/devtools/server/tests/unit/test_nesting-02.js +++ b/devtools/server/tests/unit/test_nesting-02.js @@ -20,9 +20,8 @@ function run_test() { function(response, targetFront, threadClient) { // Reach over the protocol connection and get a reference to the thread // actor. - // TODO: rewrite tests so we don't do this kind of reaching anymore.. gThreadActor = - gClient._transport._serverConnection.getActor(threadClient.actorID); + threadClient._transport._serverConnection.getActor(threadClient._actor); test_nesting(); }); diff --git a/devtools/server/tests/unit/test_nesting-03.js b/devtools/server/tests/unit/test_nesting-03.js index 68ecbeaa8452..488db49d467d 100644 --- a/devtools/server/tests/unit/test_nesting-03.js +++ b/devtools/server/tests/unit/test_nesting-03.js @@ -37,19 +37,21 @@ function start_second_connection() { } async function test_nesting() { - let result; try { - result = await gThreadClient1.resume(); + await gThreadClient1.resume(); } catch (e) { - Assert.ok(e.includes("wrongOrder"), "rejects with the wrong order"); + Assert.equal(e.error, "wrongOrder"); + } + try { + await gThreadClient2.resume(); + } catch (e) { + Assert.ok(!e.error); + Assert.equal(e.from, gThreadClient2.actor); } - Assert.ok(!result, "no response"); - - result = await gThreadClient2.resume(); - Assert.ok(true, "resumed as expected"); gThreadClient1.resume().then(response => { - Assert.ok(true, "resumed as expected"); + Assert.ok(!response.error); + Assert.equal(response.from, gThreadClient1.actor); gClient1.close(() => finishClient(gClient2)); }); diff --git a/devtools/server/tests/unit/test_registerClient.js b/devtools/server/tests/unit/test_registerClient.js new file mode 100644 index 000000000000..9f1dfd4cfe10 --- /dev/null +++ b/devtools/server/tests/unit/test_registerClient.js @@ -0,0 +1,94 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the DebuggerClient.registerClient API + +var EventEmitter = require("devtools/shared/event-emitter"); + +var gClient; +var gTestClient; + +function TestActor(conn) { + this.conn = conn; +} +TestActor.prototype = { + actorPrefix: "test", + + start: function() { + this.conn.sendActorEvent(this.actorID, "foo", { + hello: "world", + }); + return {}; + }, +}; +TestActor.prototype.requestTypes = { + "start": TestActor.prototype.start, +}; + +function TestClient(client, form) { + this.client = client; + this.actor = form.test; + this.events = ["foo"]; + EventEmitter.decorate(this); + client.registerClient(this); + + this.detached = false; +} +TestClient.prototype = { + start: function() { + this.client.request({ + to: this.actor, + type: "start", + }); + }, + + detach: function(onDone) { + this.detached = true; + onDone(); + }, +}; + +function run_test() { + ActorRegistry.addGlobalActor({ + constructorName: "TestActor", + constructorFun: TestActor, + }, "test"); + + DebuggerServer.init(); + DebuggerServer.registerAllActors(); + + add_test(init); + add_test(test_client_events); + add_test(close_client); + run_next_test(); +} + +function init() { + gClient = new DebuggerClient(DebuggerServer.connectPipe()); + gClient.connect() + .then(() => gClient.mainRoot.rootForm) + .then(response => { + gTestClient = new TestClient(gClient, response); + run_next_test(); + }); +} + +function test_client_events() { + // Test DebuggerClient.registerClient and DebuggerServerConnection.sendActorEvent + gTestClient.on("foo", function(data) { + Assert.equal(data.hello, "world"); + run_next_test(); + }); + gTestClient.start(); +} + +function close_client() { + gClient.close().then(() => { + // Check that client.detach method is call on client destruction + Assert.ok(gTestClient.detached); + run_next_test(); + }); +} + diff --git a/devtools/server/tests/unit/test_stepping-06.js b/devtools/server/tests/unit/test_stepping-06.js index 4723867d1c60..f41d9852a112 100644 --- a/devtools/server/tests/unit/test_stepping-06.js +++ b/devtools/server/tests/unit/test_stepping-06.js @@ -129,6 +129,7 @@ async function testThrow(dbg) { {return: {type: "undefined"}}, `completion type` ); + await resume(threadClient); } function run_test() { diff --git a/devtools/server/tests/unit/testactors.js b/devtools/server/tests/unit/testactors.js index de6446f8eaa6..a8f6916d8636 100644 --- a/devtools/server/tests/unit/testactors.js +++ b/devtools/server/tests/unit/testactors.js @@ -96,8 +96,6 @@ function TestTargetActor(connection, global) { this.conn.addActor(this.threadActor); this._attached = false; this._extraActors = {}; - // This is a hack in order to enable threadActor to be accessed from getFront - this._extraActors.contextActor = this.threadActor; this.makeDebugger = makeDebugger.bind(null, { findDebuggees: () => [this._global], shouldAddNewGlobalAsDebuggee: g => { diff --git a/devtools/server/tests/unit/xpcshell.ini b/devtools/server/tests/unit/xpcshell.ini index 24c49878f683..ec4d2d0de9b6 100644 --- a/devtools/server/tests/unit/xpcshell.ini +++ b/devtools/server/tests/unit/xpcshell.ini @@ -217,6 +217,7 @@ skip-if = true # tests for breakpoint actors are obsolete bug 1524374 reason = bug 937197 [test_layout-reflows-observer.js] [test_protocolSpec.js] +[test_registerClient.js] [test_client_request.js] [test_symbols-01.js] [test_symbols-02.js] diff --git a/devtools/shared/client/debugger-client.js b/devtools/shared/client/debugger-client.js index cfa9b6d273f8..2a8911f2b245 100644 --- a/devtools/shared/client/debugger-client.js +++ b/devtools/shared/client/debugger-client.js @@ -20,6 +20,7 @@ loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socke loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter"); loader.lazyRequireGetter(this, "RootFront", "devtools/shared/fronts/root", true); +loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client"); loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client"); loader.lazyRequireGetter(this, "Front", "devtools/shared/protocol", true); @@ -32,6 +33,10 @@ function DebuggerClient(transport) { this._transport = transport; this._transport.hooks = this; + // Map actor ID to client instance for each actor type. + // To be removed once all clients are refactored to protocol.js + this._clients = new Map(); + this._pendingRequests = new Map(); this._activeRequests = new Map(); this._eventsEnabled = true; @@ -212,11 +217,55 @@ DebuggerClient.prototype = { this.once("closed", deferred.resolve); - cleanup(); + // Call each client's `detach` method by calling + // lastly registered ones first to give a chance + // to detach child clients first. + const clients = [...this._clients.values()]; + this._clients.clear(); + const detachClients = () => { + const client = clients.pop(); + if (!client) { + // All clients detached. + cleanup(); + return; + } + if (client.detach) { + client.detach(detachClients); + return; + } + detachClients(); + }; + detachClients(); return deferred.promise; }, + /** + * Attach to a global-scoped thread actor for chrome debugging. + * + * @param string threadActor + * The actor ID for the thread to attach. + * @param object options + * Configuration options. + */ + attachThread: function(threadActor, options = {}) { + if (this._clients.has(threadActor)) { + const client = this._clients.get(threadActor); + return promise.resolve([{}, client]); + } + + const packet = { + to: threadActor, + type: "attach", + options, + }; + return this.request(packet).then(response => { + const threadClient = new ThreadClient(this, threadActor); + this.registerClient(threadClient); + return [response, threadClient]; + }); + }, + /** * Release an object actor. * @@ -546,13 +595,6 @@ DebuggerClient.prototype = { return; } - // support older browsers for Fx69+ for using the old thread client - if (!this.traits.hasThreadFront && - packet.from.includes("context")) { - this.sendToDeprecatedThreadClient(packet); - return; - } - // If we have a registered Front for this actor, let it handle the packet // and skip all the rest of this unpleasantness. const front = this.getActor(packet.from); @@ -561,12 +603,24 @@ DebuggerClient.prototype = { return; } + if (this._clients.has(packet.from) && packet.type) { + const client = this._clients.get(packet.from); + const type = packet.type; + if (client.events.includes(type)) { + client.emit(type, packet); + // we ignore the rest, as the client is expected to handle this packet. + return; + } + } + let activeRequest; // See if we have a handler function waiting for a reply from this // actor. (Don't count unsolicited notifications or pauses as // replies.) if (this._activeRequests.has(packet.from) && - !(packet.type in UnsolicitedNotifications)) { + !(packet.type in UnsolicitedNotifications) && + !(packet.type == ThreadStateTypes.paused && + packet.why.type in UnsolicitedPauses)) { activeRequest = this._activeRequests.get(packet.from); this._activeRequests.delete(packet.from); } @@ -576,6 +630,13 @@ DebuggerClient.prototype = { // in the local transport case. this._attemptNextRequest(packet.from); + // Packets that indicate thread state changes get special treatment. + if (packet.type in ThreadStateTypes && + this._clients.has(packet.from) && + typeof this._clients.get(packet.from)._onThreadState == "function") { + this._clients.get(packet.from)._onThreadState(packet); + } + // Only try to notify listeners on events, not responses to requests // that lack a packet type. if (packet.type) { @@ -593,54 +654,6 @@ DebuggerClient.prototype = { } }, - // support older browsers for Fx69+ - // The code duplication here is intentional until we drop support for - // these versions. Once that happens this code can be deleted. - sendToDeprecatedThreadClient(packet) { - const deprecatedThreadClient = this.getActor(packet.from); - if (deprecatedThreadClient && packet.type) { - const type = packet.type; - if (deprecatedThreadClient.events.includes(type)) { - deprecatedThreadClient.emit(type, packet); - // we ignore the rest, as the client is expected to handle this packet. - return; - } - } - - let activeRequest; - // See if we have a handler function waiting for a reply from this - // actor. (Don't count unsolicited notifications or pauses as - // replies.) - if (this._activeRequests.has(packet.from) && - !(packet.type == ThreadStateTypes.paused && - packet.why.type in UnsolicitedPauses)) { - activeRequest = this._activeRequests.get(packet.from); - this._activeRequests.delete(packet.from); - } - - // If there is a subsequent request for the same actor, hand it off to the - // transport. Delivery of packets on the other end is always async, even - // in the local transport case. - this._attemptNextRequest(packet.from); - - // Packets that indicate thread state changes get special treatment. - if (packet.type in ThreadStateTypes && - deprecatedThreadClient && - typeof deprecatedThreadClient._onThreadState == "function") { - deprecatedThreadClient._onThreadState(packet); - } - - // Only try to notify listeners on events, not responses to requests - // that lack a packet type. - if (packet.type) { - this.emit(packet.type, packet); - } - - if (activeRequest) { - activeRequest.emit("json-reply", packet); - } - }, - /** * Called by the DebuggerTransport to dispatch incoming bulk packets as * appropriate. @@ -848,6 +861,38 @@ DebuggerClient.prototype = { }); }, + registerClient: function(client) { + const actorID = client.actor; + if (!actorID) { + throw new Error("DebuggerServer.registerClient expects " + + "a client instance with an `actor` attribute."); + } + if (!Array.isArray(client.events)) { + throw new Error("DebuggerServer.registerClient expects " + + "a client instance with an `events` attribute " + + "that is an array."); + } + if (client.events.length > 0 && typeof (client.emit) != "function") { + throw new Error("DebuggerServer.registerClient expects " + + "a client instance with non-empty `events` array to" + + "have an `emit` function."); + } + if (this._clients.has(actorID)) { + throw new Error("DebuggerServer.registerClient already registered " + + "a client for this actor."); + } + this._clients.set(actorID, client); + }, + + unregisterClient: function(client) { + const actorID = client.actor; + if (!actorID) { + throw new Error("DebuggerServer.unregisterClient expects " + + "a Client instance with a `actor` attribute."); + } + this._clients.delete(actorID); + }, + /** * Actor lifetime management, echos the server's actor pools. */ diff --git a/devtools/shared/client/deprecated-thread-client.js b/devtools/shared/client/deprecated-thread-client.js deleted file mode 100644 index 2bb54a7c0f06..000000000000 --- a/devtools/shared/client/deprecated-thread-client.js +++ /dev/null @@ -1,436 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* 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 {arg, DebuggerClient} = require("devtools/shared/client/debugger-client"); -const EventEmitter = require("devtools/shared/event-emitter"); -const {ThreadStateTypes} = require("devtools/shared/client/constants"); - -loader.lazyRequireGetter( - this, - "ObjectClient", - "devtools/shared/client/object-client" -); -loader.lazyRequireGetter( - this, - "SourceFront", - "devtools/shared/fronts/source", - true -); - -/** - * Creates a thread client for the remote debugging protocol server. This client - * is a front to the thread actor created in the server side, hiding the - * protocol details in a traditional JavaScript API. - * - * @param client DebuggerClient - * @param actor string - * The actor ID for this thread. - */ -function ThreadClient(client, actor) { - this.client = client; - this._actor = actor; - this._pauseGrips = {}; - this._threadGrips = {}; - this.request = this.client.request; -} - -ThreadClient.prototype = { - _state: "paused", - get state() { - return this._state; - }, - get paused() { - return this._state === "paused"; - }, - - _actor: null, - - get actor() { - return this._actor; - }, - - get _transport() { - return this.client._transport; - }, - - _assertPaused: function(command) { - if (!this.paused) { - throw Error( - command + " command sent while not paused. Currently " + this._state - ); - } - }, - - /** - * Resume a paused thread. If the optional limit parameter is present, then - * the thread will also pause when that limit is reached. - * - * @param [optional] object limit - * An object with a type property set to the appropriate limit (next, - * step, or finish) per the remote debugging protocol specification. - * Use null to specify no limit. - * @param bool aRewind - * Whether execution should rewind until the limit is reached, rather - * than proceeding forwards. This parameter has no effect if the - * server does not support rewinding. - */ - _doResume: DebuggerClient.requester({ - type: "resume", - resumeLimit: arg(0), - rewind: arg(1), - }, { - before: function(packet) { - this._assertPaused("resume"); - - // Put the client in a tentative "resuming" state so we can prevent - // further requests that should only be sent in the paused state. - this._previousState = this._state; - this._state = "resuming"; - - return packet; - }, - after: function(response) { - if (response.error && this._state == "resuming") { - // There was an error resuming, update the state to the new one - // reported by the server, if given (only on wrongState), otherwise - // reset back to the previous state. - if (response.state) { - this._state = ThreadStateTypes[response.state]; - } else { - this._state = this._previousState; - } - } - delete this._previousState; - return response; - }, - }), - - /** - * Reconfigure the thread actor. - * - * @param object options - * A dictionary object of the new options to use in the thread actor. - */ - reconfigure: DebuggerClient.requester({ - type: "reconfigure", - options: arg(0), - }), - - /** - * Resume a paused thread. - */ - resume: function() { - return this._doResume(null, false); - }, - - /** - * Resume then pause without stepping. - * - */ - resumeThenPause: function() { - return this._doResume({ type: "break" }, false); - }, - - /** - * Rewind a thread until a breakpoint is hit. - */ - rewind: function() { - return this._doResume(null, true); - }, - - /** - * Step over a function call. - */ - stepOver: function() { - return this._doResume({ type: "next" }, false); - }, - - /** - * Step into a function call. - */ - stepIn: function() { - return this._doResume({ type: "step" }, false); - }, - - /** - * Step out of a function call. - */ - stepOut: function() { - return this._doResume({ type: "finish" }, false); - }, - - /** - * Rewind step over a function call. - */ - reverseStepOver: function() { - return this._doResume({ type: "next" }, true); - }, - - /** - * Immediately interrupt a running thread. - */ - interrupt: function() { - return this._doInterrupt(null); - }, - - /** - * Pause execution right before the next JavaScript bytecode is executed. - */ - breakOnNext: function() { - return this._doInterrupt("onNext"); - }, - - /** - * Warp through time to an execution point in the past or future. - * - * @param object aTarget - * Description of the warp destination. - */ - timeWarp: function(target) { - const warp = () => { - this._doResume({ type: "warp", target }, true); - }; - if (this.paused) { - return warp(); - } - return this.interrupt().then(warp); - }, - - /** - * Interrupt a running thread. - */ - _doInterrupt: DebuggerClient.requester({ - type: "interrupt", - when: arg(0), - }), - - /** - * Enable or disable pausing when an exception is thrown. - * - * @param boolean pauseOnExceptions - * Enables pausing if true, disables otherwise. - * @param boolean ignoreCaughtExceptions - * Whether to ignore caught exceptions - */ - pauseOnExceptions: DebuggerClient.requester({ - type: "pauseOnExceptions", - pauseOnExceptions: arg(0), - ignoreCaughtExceptions: arg(1), - }), - - /** - * Detach from the thread actor. - */ - detach: DebuggerClient.requester({ - type: "detach", - }), - - destroy: function() { - return this.detach(); - }, - - /** - * attach to the thread actor. - */ - attach: DebuggerClient.requester({ - type: "attach", - options: arg(0), - }), - - /** - * Promote multiple pause-lifetime object actors to thread-lifetime ones. - * - * @param array actors - * An array with actor IDs to promote. - */ - threadGrips: DebuggerClient.requester({ - type: "threadGrips", - actors: arg(0), - }), - - /** - * Request the loaded sources for the current thread. - */ - getSources: DebuggerClient.requester({ - type: "sources", - }), - - /** - * Request frames from the callstack for the current thread. - * - * @param start integer - * The number of the youngest stack frame to return (the youngest - * frame is 0). - * @param count integer - * The maximum number of frames to return, or null to return all - * frames. - */ - getFrames: DebuggerClient.requester({ - type: "frames", - start: arg(0), - count: arg(1), - }), - - /** - * Toggle pausing via breakpoints in the server. - * - * @param skip boolean - * Whether the server should skip pausing via breakpoints - */ - skipBreakpoints: DebuggerClient.requester({ - type: "skipBreakpoints", - skip: arg(0), - }), - - /** - * Request the frame environment. - * - * @param frameId string - */ - getEnvironment: function(frameId) { - return this.request({ to: frameId, type: "getEnvironment" }); - }, - - /** - * Return a ObjectClient object for the given object grip. - * - * @param grip object - * A pause-lifetime object grip returned by the protocol. - */ - pauseGrip: function(grip) { - if (grip.actor in this._pauseGrips) { - return this._pauseGrips[grip.actor]; - } - - const client = new ObjectClient(this.client, grip); - this._pauseGrips[grip.actor] = client; - return client; - }, - - /** - * Clear and invalidate all the grip clients from the given cache. - * - * @param gripCacheName - * The property name of the grip cache we want to clear. - */ - _clearObjectClients: function(gripCacheName) { - for (const id in this[gripCacheName]) { - this[gripCacheName][id].valid = false; - } - this[gripCacheName] = {}; - }, - - /** - * Invalidate pause-lifetime grip clients and clear the list of current grip - * clients. - */ - _clearPauseGrips: function() { - this._clearObjectClients("_pauseGrips"); - }, - - /** - * Invalidate thread-lifetime grip clients and clear the list of current grip - * clients. - */ - _clearThreadGrips: function() { - this._clearObjectClients("_threadGrips"); - }, - - /** - * Handle thread state change by doing necessary cleanup and notifying all - * registered listeners. - */ - _onThreadState: function(packet) { - this._state = ThreadStateTypes[packet.type]; - // The debugger UI may not be initialized yet so we want to keep - // the packet around so it knows what to pause state to display - // when it's initialized - this._lastPausePacket = packet.type === "resumed" ? null : packet; - this._clearPauseGrips(); - packet.type === ThreadStateTypes.detached && this._clearThreadGrips(); - this.client._eventsEnabled && this.emit(packet.type, packet); - }, - - getLastPausePacket: function() { - return this._lastPausePacket; - }, - - setBreakpoint: DebuggerClient.requester({ - type: "setBreakpoint", - location: arg(0), - options: arg(1), - }), - - removeBreakpoint: DebuggerClient.requester({ - type: "removeBreakpoint", - location: arg(0), - }), - - /** - * Requests to set XHR breakpoint - * @param string path - * pause when url contains `path` - * @param string method - * pause when method of request is `method` - */ - setXHRBreakpoint: DebuggerClient.requester({ - type: "setXHRBreakpoint", - path: arg(0), - method: arg(1), - }), - - /** - * Request to remove XHR breakpoint - * @param string path - * @param string method - */ - removeXHRBreakpoint: DebuggerClient.requester({ - type: "removeXHRBreakpoint", - path: arg(0), - method: arg(1), - }), - - /** - * Request to get the set of available event breakpoints. - */ - getAvailableEventBreakpoints: DebuggerClient.requester({ - type: "getAvailableEventBreakpoints", - }), - - /** - * Request to get the IDs of the active event breakpoints. - */ - getActiveEventBreakpoints: DebuggerClient.requester({ - type: "getActiveEventBreakpoints", - }), - - /** - * Request to set the IDs of the active event breakpoints. - */ - setActiveEventBreakpoints: DebuggerClient.requester({ - type: "setActiveEventBreakpoints", - ids: arg(0), - }), - - /** - * Return an instance of SourceFront for the given source actor form. - */ - source: function(form) { - if (form.actor in this._threadGrips) { - return this._threadGrips[form.actor]; - } - - this._threadGrips[form.actor] = new SourceFront(this.client, form); - return this._threadGrips[form.actor]; - }, - - events: ["newSource", "progress"], -}; - -EventEmitter.decorate(ThreadClient.prototype); - -module.exports = ThreadClient; diff --git a/devtools/shared/client/moz.build b/devtools/shared/client/moz.build index 5d7f86aa6453..49769aa01497 100644 --- a/devtools/shared/client/moz.build +++ b/devtools/shared/client/moz.build @@ -8,7 +8,6 @@ DevToolsModules( 'connection-manager.js', 'constants.js', 'debugger-client.js', - 'deprecated-thread-client.js', 'environment-client.js', 'event-source.js', 'long-string-client.js', diff --git a/devtools/shared/client/thread-client.js b/devtools/shared/client/thread-client.js index 5c8aca6a75b1..2badc708515e 100644 --- a/devtools/shared/client/thread-client.js +++ b/devtools/shared/client/thread-client.js @@ -5,9 +5,9 @@ "use strict"; -const { ThreadStateTypes } = require("devtools/shared/client/constants"); -const { FrontClassWithSpec, registerFront } = require("devtools/shared/protocol"); -const { threadSpec } = require("devtools/shared/specs/thread"); +const {arg, DebuggerClient} = require("devtools/shared/client/debugger-client"); +const EventEmitter = require("devtools/shared/event-emitter"); +const {ThreadStateTypes} = require("devtools/shared/client/constants"); loader.lazyRequireGetter( this, @@ -30,42 +30,40 @@ loader.lazyRequireGetter( * @param actor string * The actor ID for this thread. */ -class ThreadClient extends FrontClassWithSpec(threadSpec) { - constructor(client) { - super(client); - this.client = client; - this._pauseGrips = {}; - this._threadGrips = {}; - this._state = "paused"; - this._beforePaused = this._beforePaused.bind(this); - this._beforeResumed = this._beforeResumed.bind(this); - this._beforeDetached = this._beforeDetached.bind(this); - this.before("paused", this._beforePaused); - this.before("resumed", this._beforeResumed); - this.before("detached", this._beforeDetached); - // Attribute name from which to retrieve the actorID out of the target actor's form - this.formAttributeName = "contextActor"; - } +function ThreadClient(client, actor) { + this.client = client; + this._actor = actor; + this._pauseGrips = {}; + this._threadGrips = {}; + this.request = this.client.request; +} +ThreadClient.prototype = { + _state: "paused", get state() { return this._state; - } - + }, get paused() { return this._state === "paused"; - } + }, + + _actor: null, get actor() { - return this.actorID; - } + return this._actor; + }, - _assertPaused(command) { + get _transport() { + return this.client._transport; + }, + + _assertPaused: function(command) { if (!this.paused) { throw Error( command + " command sent while not paused. Currently " + this._state ); } - } + }, /** * Resume a paused thread. If the optional limit parameter is present, then @@ -80,94 +78,111 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { * than proceeding forwards. This parameter has no effect if the * server does not support rewinding. */ - async _doResume(resumeLimit, rewind) { - this._assertPaused("resume"); + _doResume: DebuggerClient.requester({ + type: "resume", + resumeLimit: arg(0), + rewind: arg(1), + }, { + before: function(packet) { + this._assertPaused("resume"); - // Put the client in a tentative "resuming" state so we can prevent - // further requests that should only be sent in the paused state. - this._previousState = this._state; - this._state = "resuming"; - try { - await super.resume(resumeLimit, rewind); - } catch (e) { - if (this._state == "resuming") { + // Put the client in a tentative "resuming" state so we can prevent + // further requests that should only be sent in the paused state. + this._previousState = this._state; + this._state = "resuming"; + + return packet; + }, + after: function(response) { + if (response.error && this._state == "resuming") { // There was an error resuming, update the state to the new one // reported by the server, if given (only on wrongState), otherwise // reset back to the previous state. - if (e.state) { - this._state = ThreadStateTypes[e.state]; + if (response.state) { + this._state = ThreadStateTypes[response.state]; } else { this._state = this._previousState; } } - } + delete this._previousState; + return response; + }, + }), - delete this._previousState; - } + /** + * Reconfigure the thread actor. + * + * @param object options + * A dictionary object of the new options to use in the thread actor. + */ + reconfigure: DebuggerClient.requester({ + type: "reconfigure", + options: arg(0), + }), /** * Resume a paused thread. */ - resume() { + resume: function() { return this._doResume(null, false); - } + }, /** * Resume then pause without stepping. * */ - resumeThenPause() { + resumeThenPause: function() { return this._doResume({ type: "break" }, false); - } + }, /** * Rewind a thread until a breakpoint is hit. */ - rewind() { - this._doResume(null, true); - } + rewind: function() { + return this._doResume(null, true); + }, /** * Step over a function call. */ - stepOver() { + stepOver: function() { return this._doResume({ type: "next" }, false); - } + }, /** * Step into a function call. */ - stepIn() { + stepIn: function() { return this._doResume({ type: "step" }, false); - } + }, /** * Step out of a function call. */ - stepOut() { + stepOut: function() { return this._doResume({ type: "finish" }, false); - } + }, /** * Rewind step over a function call. */ - reverseStepOver() { + reverseStepOver: function() { return this._doResume({ type: "next" }, true); - } + }, /** * Immediately interrupt a running thread. */ - interrupt() { + interrupt: function() { return this._doInterrupt(null); - } + }, /** * Pause execution right before the next JavaScript bytecode is executed. */ - breakOnNext() { + breakOnNext: function() { return this._doInterrupt("onNext"); - } + }, /** * Warp through time to an execution point in the past or future. @@ -175,7 +190,7 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { * @param object aTarget * Description of the warp destination. */ - timeWarp(target) { + timeWarp: function(target) { const warp = () => { this._doResume({ type: "warp", target }, true); }; @@ -183,21 +198,59 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { return warp(); } return this.interrupt().then(warp); - } + }, /** * Interrupt a running thread. */ - _doInterrupt(when) { - return super.interrupt(when); - } + _doInterrupt: DebuggerClient.requester({ + type: "interrupt", + when: arg(0), + }), + + /** + * Enable or disable pausing when an exception is thrown. + * + * @param boolean pauseOnExceptions + * Enables pausing if true, disables otherwise. + * @param boolean ignoreCaughtExceptions + * Whether to ignore caught exceptions + */ + pauseOnExceptions: DebuggerClient.requester({ + type: "pauseOnExceptions", + pauseOnExceptions: arg(0), + ignoreCaughtExceptions: arg(1), + }), + + /** + * Detach from the thread actor. + */ + detach: DebuggerClient.requester({ + type: "detach", + }, { + after: function(response) { + this.client.unregisterClient(this); + return response; + }, + }), + + /** + * Promote multiple pause-lifetime object actors to thread-lifetime ones. + * + * @param array actors + * An array with actor IDs to promote. + */ + threadGrips: DebuggerClient.requester({ + type: "threadGrips", + actors: arg(0), + }), /** * Request the loaded sources for the current thread. */ - getSources() { - return super.sources(); - } + getSources: DebuggerClient.requester({ + type: "sources", + }), /** * Request frames from the callstack for the current thread. @@ -209,42 +262,31 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { * The maximum number of frames to return, or null to return all * frames. */ - getFrames(start, count) { - return super.frames(start, count); - } - /** - * attach to the thread actor. - */ - async attach(options) { - let response; - try { - const onPaused = this.once("paused"); - response = await super.attach(options); - await onPaused; - } catch (e) { - throw new Error(e); - } - return response; - } + getFrames: DebuggerClient.requester({ + type: "frames", + start: arg(0), + count: arg(1), + }), /** - * Detach from the thread actor. + * Toggle pausing via breakpoints in the server. + * + * @param skip boolean + * Whether the server should skip pausing via breakpoints */ - async detach() { - const onDetached = this.once("detached"); - await super.detach(); - await onDetached; - await this.destroy(); - } + skipBreakpoints: DebuggerClient.requester({ + type: "skipBreakpoints", + skip: arg(0), + }), /** * Request the frame environment. * * @param frameId string */ - getEnvironment(frameId) { - return this.client.request({ to: frameId, type: "getEnvironment" }); - } + getEnvironment: function(frameId) { + return this.request({ to: frameId, type: "getEnvironment" }); + }, /** * Return a ObjectClient object for the given object grip. @@ -252,7 +294,7 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { * @param grip object * A pause-lifetime object grip returned by the protocol. */ - pauseGrip(grip) { + pauseGrip: function(grip) { if (grip.actor in this._pauseGrips) { return this._pauseGrips[grip.actor]; } @@ -260,7 +302,7 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { const client = new ObjectClient(this.client, grip); this._pauseGrips[grip.actor] = client; return client; - } + }, /** * Clear and invalidate all the grip clients from the given cache. @@ -268,72 +310,120 @@ class ThreadClient extends FrontClassWithSpec(threadSpec) { * @param gripCacheName * The property name of the grip cache we want to clear. */ - _clearObjectClients(gripCacheName) { + _clearObjectClients: function(gripCacheName) { for (const id in this[gripCacheName]) { this[gripCacheName][id].valid = false; } this[gripCacheName] = {}; - } + }, /** * Invalidate pause-lifetime grip clients and clear the list of current grip * clients. */ - _clearPauseGrips() { + _clearPauseGrips: function() { this._clearObjectClients("_pauseGrips"); - } + }, /** * Invalidate thread-lifetime grip clients and clear the list of current grip * clients. */ - _clearThreadGrips() { + _clearThreadGrips: function() { this._clearObjectClients("_threadGrips"); - } - - _beforePaused(packet) { - this._state = "paused"; - this._onThreadState(packet); - } - - _beforeResumed() { - this._state = "attached"; - this._onThreadState(null); - } - - _beforeDetached(packet) { - this._state = "detached"; - this._onThreadState(packet); - this._clearThreadGrips(); - } + }, /** - * Handle thread state change by doing necessary cleanup + * Handle thread state change by doing necessary cleanup and notifying all + * registered listeners. */ - _onThreadState(packet) { + _onThreadState: function(packet) { + this._state = ThreadStateTypes[packet.type]; // The debugger UI may not be initialized yet so we want to keep // the packet around so it knows what to pause state to display // when it's initialized - this._lastPausePacket = packet; + this._lastPausePacket = packet.type === "resumed" ? null : packet; this._clearPauseGrips(); - } + packet.type === ThreadStateTypes.detached && this._clearThreadGrips(); + this.client._eventsEnabled && this.emit(packet.type, packet); + }, - getLastPausePacket() { + getLastPausePacket: function() { return this._lastPausePacket; - } + }, + + setBreakpoint: DebuggerClient.requester({ + type: "setBreakpoint", + location: arg(0), + options: arg(1), + }), + + removeBreakpoint: DebuggerClient.requester({ + type: "removeBreakpoint", + location: arg(0), + }), + + /** + * Requests to set XHR breakpoint + * @param string path + * pause when url contains `path` + * @param string method + * pause when method of request is `method` + */ + setXHRBreakpoint: DebuggerClient.requester({ + type: "setXHRBreakpoint", + path: arg(0), + method: arg(1), + }), + + /** + * Request to remove XHR breakpoint + * @param string path + * @param string method + */ + removeXHRBreakpoint: DebuggerClient.requester({ + type: "removeXHRBreakpoint", + path: arg(0), + method: arg(1), + }), + + /** + * Request to get the set of available event breakpoints. + */ + getAvailableEventBreakpoints: DebuggerClient.requester({ + type: "getAvailableEventBreakpoints", + }), + + /** + * Request to get the IDs of the active event breakpoints. + */ + getActiveEventBreakpoints: DebuggerClient.requester({ + type: "getActiveEventBreakpoints", + }), + + /** + * Request to set the IDs of the active event breakpoints. + */ + setActiveEventBreakpoints: DebuggerClient.requester({ + type: "setActiveEventBreakpoints", + ids: arg(0), + }), /** * Return an instance of SourceFront for the given source actor form. */ - source(form) { + source: function(form) { if (form.actor in this._threadGrips) { return this._threadGrips[form.actor]; } this._threadGrips[form.actor] = new SourceFront(this.client, form); return this._threadGrips[form.actor]; - } -} + }, -exports.ThreadClient = ThreadClient; -registerFront(ThreadClient); + events: ["newSource", "progress"], +}; + +EventEmitter.decorate(ThreadClient.prototype); + +module.exports = ThreadClient; diff --git a/devtools/shared/fronts/source.js b/devtools/shared/fronts/source.js index 37977181509a..664eab873801 100644 --- a/devtools/shared/fronts/source.js +++ b/devtools/shared/fronts/source.js @@ -19,17 +19,11 @@ const { ArrayBufferFront } = require("devtools/shared/fronts/array-buffer"); class SourceFront extends FrontClassWithSpec(sourceSpec) { constructor(client, form) { super(client); - if (form) { - this._url = form.url; - // this is here for the time being, until the source front is managed - // via protocol.js marshalling - this.actorID = form.actor; - this.manage(this); - } - } - - form(json) { - this._url = json.url; + this._url = form.url; + // this is here for the time being, until the source front is managed + // via protocol.js marshalling + this.actorID = form.actor; + this.manage(this); } get actor() { diff --git a/devtools/shared/fronts/targets/browsing-context.js b/devtools/shared/fronts/targets/browsing-context.js index 7399539e7616..631488dc0fc9 100644 --- a/devtools/shared/fronts/targets/browsing-context.js +++ b/devtools/shared/fronts/targets/browsing-context.js @@ -98,7 +98,6 @@ class BrowsingContextTargetFront extends const response = await super.attach(); this._threadActor = response.threadActor; - this.targetForm.contextActor = this._threadActor; this.configureOptions.javascriptEnabled = response.javascriptEnabled; this.traits = response.traits || {}; diff --git a/devtools/shared/fronts/targets/content-process.js b/devtools/shared/fronts/targets/content-process.js index 7c2d58ace121..d0abe34063a1 100644 --- a/devtools/shared/fronts/targets/content-process.js +++ b/devtools/shared/fronts/targets/content-process.js @@ -21,7 +21,6 @@ class ContentProcessTargetFront extends // Save the full form for Target class usage. // Do not use `form` name to avoid colliding with protocol.js's `form` method this.targetForm = json; - this.targetForm.contextActor = json.chromeDebugger; this._threadActor = json.chromeDebugger; } diff --git a/devtools/shared/fronts/targets/target-mixin.js b/devtools/shared/fronts/targets/target-mixin.js index 384146db795b..d3daa50b6766 100644 --- a/devtools/shared/fronts/targets/target-mixin.js +++ b/devtools/shared/fronts/targets/target-mixin.js @@ -10,7 +10,6 @@ // used by a subclass, specific to local tabs. loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true); -loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/deprecated-thread-client"); loader.lazyRequireGetter(this, "getFront", "devtools/shared/protocol", true); /** @@ -405,21 +404,15 @@ function TargetMixin(parentClass) { "TargetMixin sub class should set _threadActor before calling " + "attachThread" ); } - if (this.getTrait("hasThreadFront")) { - this.threadClient = await this.getFront("context"); - } else { - // Backwards compat for Firefox 68 - // mimics behavior of a front - this.threadClient = new ThreadClient(this._client, this._threadActor); - this.fronts.set("context", this.threadClient); - this.threadClient.actorID = this._threadActor; - this.manage(this.threadClient); - } - const result = await this.threadClient.attach(options); + const [response, threadClient] = await this._client.attachThread( + this._threadActor, + options + ); + this.threadClient = threadClient; this.threadClient.on("newSource", this._onNewSource); - return [result, this.threadClient]; + return [response, threadClient]; } // Listener for "newSource" event fired by the thread actor @@ -461,9 +454,7 @@ function TargetMixin(parentClass) { */ _teardownRemoteListeners() { // Remove listeners set in _setupRemoteListeners - if (this.client) { - this.client.off("closed", this.destroy); - } + this.client.off("closed", this.destroy); this.off("tabDetached", this.destroy); // Remove listeners set in attachThread @@ -545,8 +536,6 @@ function TargetMixin(parentClass) { this._teardownRemoteListeners(); - this.threadClient = null; - if (this.isLocalTab) { // We started with a local tab and created the client ourselves, so we // should close it. @@ -566,6 +555,14 @@ function TargetMixin(parentClass) { } } + if (this.threadClient) { + try { + await this.threadClient.detach(); + } catch (e) { + console.warn(`Error while detaching the thread front: ${e.message}`); + } + } + // Do that very last in order to let a chance to dispatch `detach` requests. super.destroy(); diff --git a/devtools/shared/fronts/targets/worker.js b/devtools/shared/fronts/targets/worker.js index 6c21f41739fb..79efd8339558 100644 --- a/devtools/shared/fronts/targets/worker.js +++ b/devtools/shared/fronts/targets/worker.js @@ -51,9 +51,7 @@ class WorkerTargetFront extends // that will be later used by Target. const connectResponse = await this.connect({}); // Set the console actor ID on the form to expose it to Target.attachConsole - // Set the ThreadActor on the target form so it is accessible by getFront this.targetForm.consoleActor = connectResponse.consoleActor; - this.targetForm.contextActor = connectResponse.threadActor; this._threadActor = connectResponse.threadActor; return this.attachConsole(); diff --git a/devtools/shared/specs/index.js b/devtools/shared/specs/index.js index 90ebb3f08637..df2e510d5544 100644 --- a/devtools/shared/specs/index.js +++ b/devtools/shared/specs/index.js @@ -252,10 +252,11 @@ const Types = exports.__TypesForTests = [ spec: "devtools/shared/specs/targets/worker", front: "devtools/shared/fronts/targets/worker", }, + /* Thread has an old fashion client and no front */ { types: ["context"], spec: "devtools/shared/specs/thread", - front: "devtools/shared/client/thread-client", + front: null, }, { types: ["console"], diff --git a/devtools/shared/specs/thread.js b/devtools/shared/specs/thread.js index 15e362eed0d5..fa0e29b38a88 100644 --- a/devtools/shared/specs/thread.js +++ b/devtools/shared/specs/thread.js @@ -18,18 +18,8 @@ const threadSpec = generateActorSpec({ typeName: "context", events: { - paused: { - actor: Option(0, "nullable:string"), - frame: Option(0, "nullable:json"), - why: Option(0, "nullable:json"), - poppedFrames: Option(0, "nullable:json"), - error: Option(0, "nullable:json"), - }, - resumed: {}, - detached: {}, - willInterrupt: {}, newSource: { - source: Option(0, "json"), + source: Option(0, "source"), }, progress: { recording: Option(0, "json"), @@ -45,7 +35,6 @@ const threadSpec = generateActorSpec({ response: RetVal("nullable:json"), }, detach: { - request: {}, response: {}, }, reconfigure: { @@ -72,6 +61,7 @@ const threadSpec = generateActorSpec({ request: { when: Arg(0, "json"), }, + response: RetVal("array:json"), }, sources: { response: RetVal("array:json"), diff --git a/package-lock.json b/package-lock.json index b5c4d54906ca..45831321a8e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1865,11 +1865,6 @@ "requires": { "mkdirp": "^0.5.1" } - }, - "yarn": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.16.0.tgz", - "integrity": "sha512-cfemyGlnWKA1zopUUgebTPf8C4WkPIZ+TJmklwcEAJ4u6oWPtJeAzrsamaGGh/+b1XWe8W51yzAImC4AWbWR1g==" } } } diff --git a/package.json b/package.json index cab46b9aa7b9..a930785c22dd 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,7 @@ "description": "This package file is for node modules used in mozilla-central", "repository": {}, "license": "MPL-2.0", - "dependencies": { - "yarn": "^1.16.0" - }, + "dependencies": {}, "devDependencies": { "babel-eslint": "10.0.1", "eslint": "5.16.0",