зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1610416 - Expose SavedFrame frames via debugger server. r=jlast
Differential Revision: https://phabricator.services.mozilla.com/D61517 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
628207e269
Коммит
af0a5181d1
|
@ -4,6 +4,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { assert } = require("devtools/shared/DevToolsUtils");
|
||||
const { ActorPool } = require("devtools/server/actors/common");
|
||||
const { createValueGrip } = require("devtools/server/actors/object/utils");
|
||||
const { ActorClassWithSpec } = require("devtools/shared/protocol");
|
||||
|
@ -18,6 +20,49 @@ function formatDisplayName(frame) {
|
|||
return `(${frame.type})`;
|
||||
}
|
||||
|
||||
function isDeadSavedFrame(savedFrame) {
|
||||
return Cu && Cu.isDeadWrapper(savedFrame);
|
||||
}
|
||||
function isValidSavedFrame(threadActor, savedFrame) {
|
||||
return (
|
||||
!isDeadSavedFrame(savedFrame) &&
|
||||
// If the frame's source is unknown to the debugger, then we ignore it
|
||||
// since the frame likely does not belong to a realm that is marked
|
||||
// as a debuggee.
|
||||
// This check will also fail if the frame would have been known but was
|
||||
// GCed before the debugger was opened on the page.
|
||||
// TODO: Use SavedFrame's security principal to limit non-debuggee frames
|
||||
// and pass all unknown frames to the debugger as a URL with no sourceID.
|
||||
getSavedFrameSource(threadActor, savedFrame)
|
||||
);
|
||||
}
|
||||
function getSavedFrameSource(threadActor, savedFrame) {
|
||||
return threadActor.sources.getSourceActorByInternalSourceId(
|
||||
savedFrame.sourceId
|
||||
);
|
||||
}
|
||||
function getSavedFrameParent(threadActor, savedFrame) {
|
||||
if (isDeadSavedFrame(savedFrame)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
savedFrame = savedFrame.parent || savedFrame.asyncParent;
|
||||
|
||||
// If the saved frame is a dead wrapper, we don't have any way to keep
|
||||
// stepping through parent frames.
|
||||
if (!savedFrame || isDeadSavedFrame(savedFrame)) {
|
||||
savedFrame = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isValidSavedFrame(threadActor, savedFrame)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return savedFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* An actor for a specified stack frame.
|
||||
*/
|
||||
|
@ -25,7 +70,7 @@ const FrameActor = ActorClassWithSpec(frameSpec, {
|
|||
/**
|
||||
* Creates the Frame actor.
|
||||
*
|
||||
* @param frame Debugger.Frame
|
||||
* @param frame Debugger.Frame|SavedFrame
|
||||
* The debuggee frame.
|
||||
* @param threadActor ThreadActor
|
||||
* The parent thread actor for this frame.
|
||||
|
@ -81,13 +126,41 @@ const FrameActor = ActorClassWithSpec(frameSpec, {
|
|||
* Returns a frame form for use in a protocol message.
|
||||
*/
|
||||
form: function() {
|
||||
// SavedFrame actors have their own frame handling.
|
||||
if (!(this.frame instanceof Debugger.Frame)) {
|
||||
// The Frame actor shouldn't be used after evaluation is resumed, so
|
||||
// there shouldn't be an easy way for the saved frame to be referenced
|
||||
// once it has died.
|
||||
assert(!isDeadSavedFrame(this.frame));
|
||||
|
||||
const obj = {
|
||||
actor: this.actorID,
|
||||
// TODO: Bug 1610418 - Consider updating SavedFrame to have a type.
|
||||
type: "dead",
|
||||
asyncCause: this.frame.asyncCause,
|
||||
state: "dead",
|
||||
displayName: this.frame.functionDisplayName,
|
||||
arguments: [],
|
||||
where: {
|
||||
// The frame's source should always be known because
|
||||
// getSavedFrameParent will skip over frames with unknown sources.
|
||||
actor: getSavedFrameSource(this.threadActor, this.frame).actorID,
|
||||
line: this.frame.line,
|
||||
// SavedFrame objects have a 1-based column number, but this API and
|
||||
// Debugger API objects use a 0-based column value.
|
||||
column: this.frame.column - 1,
|
||||
},
|
||||
oldest: !getSavedFrameParent(this.threadActor, this.frame),
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
const threadActor = this.threadActor;
|
||||
const form = {
|
||||
actor: this.actorID,
|
||||
type: this.frame.type,
|
||||
asyncCause: this.frame.onStack ? null : "await",
|
||||
|
||||
// This should expand with "dead" when we support SavedFrames.
|
||||
state: this.frame.onStack ? "on-stack" : "suspended",
|
||||
};
|
||||
|
||||
|
@ -139,3 +212,5 @@ const FrameActor = ActorClassWithSpec(frameSpec, {
|
|||
|
||||
exports.FrameActor = FrameActor;
|
||||
exports.formatDisplayName = formatDisplayName;
|
||||
exports.getSavedFrameParent = getSavedFrameParent;
|
||||
exports.isValidSavedFrame = isValidSavedFrame;
|
||||
|
|
|
@ -54,6 +54,18 @@ loader.lazyRequireGetter(
|
|||
"devtools/server/actors/frame",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"getSavedFrameParent",
|
||||
"devtools/server/actors/frame",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"isValidSavedFrame",
|
||||
"devtools/server/actors/frame",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
|
@ -114,6 +126,7 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
this._activeEventPause = null;
|
||||
this._pauseOverlay = null;
|
||||
this._priorPause = null;
|
||||
this._frameActorMap = new WeakMap();
|
||||
|
||||
this._watchpointsMap = new WatchpointMap(this);
|
||||
|
||||
|
@ -1351,8 +1364,18 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
const currentFrame = frame;
|
||||
frame = null;
|
||||
|
||||
if (currentFrame.older) {
|
||||
if (!(currentFrame instanceof Debugger.Frame)) {
|
||||
frame = getSavedFrameParent(this, currentFrame);
|
||||
} else if (currentFrame.older) {
|
||||
frame = currentFrame.older;
|
||||
} else if (
|
||||
this._options.shouldIncludeSavedFrames &&
|
||||
currentFrame.olderSavedFrame
|
||||
) {
|
||||
frame = currentFrame.olderSavedFrame;
|
||||
if (frame && !isValidSavedFrame(this, frame)) {
|
||||
frame = null;
|
||||
}
|
||||
} else if (
|
||||
this._options.shouldIncludeAsyncLiveFrames &&
|
||||
currentFrame.asyncPromise
|
||||
|
@ -1395,9 +1418,14 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
// Return count frames, or all remaining frames if count is not defined.
|
||||
const frames = [];
|
||||
for (; frame && (!count || i < start + count); i++, walkToParentFrame()) {
|
||||
const sourceActor = this.sources.createSourceActor(frame.script.source);
|
||||
if (!sourceActor) {
|
||||
continue;
|
||||
// SavedFrame instances don't have direct Debugger.Source object. If
|
||||
// there is an active Debugger.Source that represents the SaveFrame's
|
||||
// source, it will have already been created in the server.
|
||||
if (frame instanceof Debugger.Frame) {
|
||||
const sourceActor = this.sources.createSourceActor(frame.script.source);
|
||||
if (!sourceActor) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const frameActor = this._createFrameActor(frame, i);
|
||||
frames.push(frameActor);
|
||||
|
@ -1685,15 +1713,14 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
|||
},
|
||||
|
||||
_createFrameActor: function(frame, depth) {
|
||||
if (frame.actor) {
|
||||
return frame.actor;
|
||||
let actor = this._frameActorMap.get(frame);
|
||||
if (!actor) {
|
||||
actor = new FrameActor(frame, this, depth);
|
||||
this._frameActors.push(actor);
|
||||
this._framesPool.addActor(actor);
|
||||
|
||||
this._frameActorMap.set(frame, actor);
|
||||
}
|
||||
|
||||
const actor = new FrameActor(frame, this, depth);
|
||||
this._frameActors.push(actor);
|
||||
this._framesPool.addActor(actor);
|
||||
frame.actor = actor;
|
||||
|
||||
return actor;
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче