1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const { ThreadStateTypes
} = require("resource://devtools/client/constants.js");
11 } = require("resource://devtools/shared/protocol.js");
13 const { threadSpec
} = require("resource://devtools/shared/specs/thread.js");
15 loader
.lazyRequireGetter(
18 "resource://devtools/client/fronts/object.js",
21 loader
.lazyRequireGetter(
24 "resource://devtools/client/fronts/frame.js"
26 loader
.lazyRequireGetter(
29 "resource://devtools/client/fronts/source.js",
34 * Creates a thread front for the remote debugging protocol server. This client
35 * is a front to the thread actor created in the server side, hiding the
36 * protocol details in a traditional JavaScript API.
38 * @param client DevToolsClient
40 * The actor ID for this thread.
42 class ThreadFront
extends FrontClassWithSpec(threadSpec
) {
43 constructor(client
, targetFront
, parentFront
) {
44 super(client
, targetFront
, parentFront
);
46 this._threadGrips
= {};
47 // Note that this isn't matching ThreadActor state field.
48 // ThreadFront is only using two values: paused or attached.
49 this._state
= "attached";
51 this._beforePaused
= this._beforePaused
.bind(this);
52 this._beforeResumed
= this._beforeResumed
.bind(this);
53 this.before("paused", this._beforePaused
);
54 this.before("resumed", this._beforeResumed
);
55 this.targetFront
.on("will-navigate", this._onWillNavigate
.bind(this));
56 // Attribute name from which to retrieve the actorID out of the target actor's form
57 this.formAttributeName
= "threadActor";
65 return this._state
=== "paused";
72 _assertPaused(command
) {
75 command
+ " command sent while not paused. Currently " + this._state
80 getFrames(start
, count
) {
81 return super.frames(start
, count
);
85 * Resume a paused thread. If the optional limit parameter is present, then
86 * the thread will also pause when that limit is reached.
88 * @param [optional] object limit
89 * An object with a type property set to the appropriate limit (next,
90 * step, or finish) per the remote debugging protocol specification.
91 * Use null to specify no limit.
93 async
_doResume(resumeLimit
, frameActorID
) {
94 this._assertPaused("resume");
96 // Put the client in a tentative "resuming" state so we can prevent
97 // further requests that should only be sent in the paused state.
98 this._previousState
= this._state
;
99 this._state
= "resuming";
101 await
super.resume(resumeLimit
, frameActorID
);
103 if (this._state
== "resuming") {
104 // There was an error resuming, update the state to the new one
105 // reported by the server, if given (only on wrongState), otherwise
106 // reset back to the previous state.
108 this._state
= ThreadStateTypes
[e
.state
];
110 this._state
= this._previousState
;
115 delete this._previousState
;
119 * Resume a paused thread.
122 return this._doResume(null);
126 * Resume then pause without stepping.
130 return this._doResume({ type
: "break" });
134 * Step over a function call.
136 stepOver(frameActorID
) {
137 return this._doResume({ type
: "next" }, frameActorID
);
141 * Step into a function call.
143 stepIn(frameActorID
) {
144 return this._doResume({ type
: "step" }, frameActorID
);
148 * Step out of a function call.
150 stepOut(frameActorID
) {
151 return this._doResume({ type
: "finish" }, frameActorID
);
155 * Restart selected frame.
157 restart(frameActorID
) {
158 return this._doResume({ type
: "restart" }, frameActorID
);
162 * Immediately interrupt a running thread.
165 return this._doInterrupt(null);
169 * Pause execution right before the next JavaScript bytecode is executed.
172 return this._doInterrupt("onNext");
176 * Interrupt a running thread.
179 return super.interrupt(when
);
183 * Request the loaded sources for the current thread.
188 sources
= await
super.sources();
190 // we may have closed the connection
191 console
.log(`getSources failed. Connection may have closed: ${e}`);
197 * This is only used by tests, which should be migrated to DevToolsClient.createObjectFront
200 return new ObjectFront(this.conn
, this.targetFront
, this, grip
);
204 * Clear and invalidate all the grip fronts from the given cache.
206 * @param gripCacheName
207 * The property name of the grip cache we want to clear.
209 _clearObjectFronts(gripCacheName
) {
210 for (const id
in this[gripCacheName
]) {
211 this[gripCacheName
][id
].valid
= false;
213 this[gripCacheName
] = {};
216 _beforePaused(packet
) {
217 this._state
= "paused";
218 this._onThreadState(packet
);
222 this._state
= "attached";
223 this._onThreadState(null);
224 this.unmanageChildren(FrameFront
);
228 this.unmanageChildren(SourceFront
);
232 * Handle thread state change by doing necessary cleanup
234 _onThreadState(packet
) {
235 // The debugger UI may not be initialized yet so we want to keep
236 // the packet around so it knows what to pause state to display
237 // when it's initialized
238 this._lastPausePacket
= packet
;
241 getLastPausePacket() {
242 return this._lastPausePacket
;
246 * Return an instance of SourceFront for the given source actor form.
249 if (form
.actor
in this._threadGrips
) {
250 return this._threadGrips
[form
.actor
];
253 const sourceFront
= new SourceFront(this.client
, form
);
254 this.manage(sourceFront
);
255 this._threadGrips
[form
.actor
] = sourceFront
;
260 exports
.ThreadFront
= ThreadFront
;
261 registerFront(ThreadFront
);