Bug 1494796 - Convert ThreadClient into a Front; r=jdescottes

This is the first part of the threadClient refactor. It only moves the methods to the new
front. and does some basic fixes.

Differential Revision: https://phabricator.services.mozilla.com/D32692

--HG--
extra : moz-landing-system : lando
This commit is contained in:
yulia 2019-06-14 00:14:58 +00:00
Родитель 57a1e6ce79
Коммит 650d7f9c31
4 изменённых файлов: 115 добавлений и 233 удалений

Просмотреть файл

@ -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");
/**

Просмотреть файл

@ -20,7 +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, "ThreadClient", "devtools/shared/client/thread-client", true);
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
loader.lazyRequireGetter(this, "Front", "devtools/shared/protocol", true);
@ -230,7 +230,7 @@ DebuggerClient.prototype = {
return;
}
if (client.detach) {
client.detach(detachClients);
client.detach().then(detachClients);
return;
}
detachClients();
@ -248,22 +248,16 @@ DebuggerClient.prototype = {
* @param object options
* Configuration options.
*/
attachThread: function(threadActor, options = {}) {
attachThread: async 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];
});
const threadClient = new ThreadClient(this, threadActor);
const response = await threadClient.attach(options);
this.registerClient(threadClient);
return [response, threadClient];
},
/**

Просмотреть файл

@ -5,9 +5,9 @@
"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");
const { FrontClassWithSpec, registerFront } = require("devtools/shared/protocol");
const { threadSpec } = require("devtools/shared/specs/thread");
loader.lazyRequireGetter(
this,
@ -30,40 +30,42 @@ loader.lazyRequireGetter(
* @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;
}
class ThreadClient extends FrontClassWithSpec(threadSpec) {
constructor(client, actor) {
super(client);
this.events = ["newSource", "progress"];
this.client = client;
this._pauseGrips = {};
this._threadGrips = {};
this._state = "paused";
this.actorID = actor;
this.manage(this);
}
destroy() {
this.client.unregisterClient(this);
super.destroy();
}
ThreadClient.prototype = {
_state: "paused",
get state() {
return this._state;
},
}
get paused() {
return this._state === "paused";
},
_actor: null,
}
get actor() {
return this._actor;
},
return this.actorID;
}
get _transport() {
return this.client._transport;
},
_assertPaused: function(command) {
_assertPaused(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
@ -78,111 +80,94 @@ ThreadClient.prototype = {
* 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");
async _doResume(resumeLimit, rewind) {
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") {
// 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") {
// 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];
if (e.state) {
this._state = ThreadStateTypes[e.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),
}),
delete this._previousState;
}
/**
* Resume a paused thread.
*/
resume: function() {
resume() {
return this._doResume(null, false);
},
}
/**
* Resume then pause without stepping.
*
*/
resumeThenPause: function() {
resumeThenPause() {
return this._doResume({ type: "break" }, false);
},
}
/**
* Rewind a thread until a breakpoint is hit.
*/
rewind: function() {
return this._doResume(null, true);
},
rewind() {
this._doResume(null, true);
}
/**
* Step over a function call.
*/
stepOver: function() {
stepOver() {
return this._doResume({ type: "next" }, false);
},
}
/**
* Step into a function call.
*/
stepIn: function() {
stepIn() {
return this._doResume({ type: "step" }, false);
},
}
/**
* Step out of a function call.
*/
stepOut: function() {
stepOut() {
return this._doResume({ type: "finish" }, false);
},
}
/**
* Rewind step over a function call.
*/
reverseStepOver: function() {
reverseStepOver() {
return this._doResume({ type: "next" }, true);
},
}
/**
* Immediately interrupt a running thread.
*/
interrupt: function() {
interrupt() {
return this._doInterrupt(null);
},
}
/**
* Pause execution right before the next JavaScript bytecode is executed.
*/
breakOnNext: function() {
breakOnNext() {
return this._doInterrupt("onNext");
},
}
/**
* Warp through time to an execution point in the past or future.
@ -190,7 +175,7 @@ ThreadClient.prototype = {
* @param object aTarget
* Description of the warp destination.
*/
timeWarp: function(target) {
timeWarp(target) {
const warp = () => {
this._doResume({ type: "warp", target }, true);
};
@ -198,59 +183,30 @@ ThreadClient.prototype = {
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),
}),
_doInterrupt(when) {
return super.interrupt(when);
}
/**
* 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),
}),
async detach() {
const response = await super.detach();
this.client.unregisterClient(this);
return response;
}
/**
* Request the loaded sources for the current thread.
*/
getSources: DebuggerClient.requester({
type: "sources",
}),
getSources() {
return super.sources();
}
/**
* Request frames from the callstack for the current thread.
@ -262,31 +218,18 @@ ThreadClient.prototype = {
* 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),
}),
getFrames(start, count) {
return super.frames(start, count);
}
/**
* Request the frame environment.
*
* @param frameId string
*/
getEnvironment: function(frameId) {
return this.request({ to: frameId, type: "getEnvironment" });
},
getEnvironment(frameId) {
return this.client.request({ to: frameId, type: "getEnvironment" });
}
/**
* Return a ObjectClient object for the given object grip.
@ -294,7 +237,7 @@ ThreadClient.prototype = {
* @param grip object
* A pause-lifetime object grip returned by the protocol.
*/
pauseGrip: function(grip) {
pauseGrip(grip) {
if (grip.actor in this._pauseGrips) {
return this._pauseGrips[grip.actor];
}
@ -302,7 +245,7 @@ ThreadClient.prototype = {
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.
@ -310,34 +253,33 @@ ThreadClient.prototype = {
* @param gripCacheName
* The property name of the grip cache we want to clear.
*/
_clearObjectClients: function(gripCacheName) {
_clearObjectClients(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() {
_clearPauseGrips() {
this._clearObjectClients("_pauseGrips");
},
}
/**
* Invalidate thread-lifetime grip clients and clear the list of current grip
* clients.
*/
_clearThreadGrips: function() {
_clearThreadGrips() {
this._clearObjectClients("_threadGrips");
},
}
/**
* Handle thread state change by doing necessary cleanup and notifying all
* registered listeners.
* Handle thread state change by doing necessary cleanup
*/
_onThreadState: function(packet) {
_onThreadState(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
@ -346,84 +288,24 @@ ThreadClient.prototype = {
this._clearPauseGrips();
packet.type === ThreadStateTypes.detached && this._clearThreadGrips();
this.client._eventsEnabled && this.emit(packet.type, packet);
},
}
getLastPausePacket: function() {
getLastPausePacket() {
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) {
source(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;
exports.ThreadClient = ThreadClient;
registerFront(ThreadClient);

Просмотреть файл

@ -19,11 +19,17 @@ const { ArrayBufferFront } = require("devtools/shared/fronts/array-buffer");
class SourceFront extends FrontClassWithSpec(sourceSpec) {
constructor(client, form) {
super(client);
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);
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;
}
get actor() {