зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1529917 - Remove VariablesViewController.jsm. r=miker.
It isn't used anywhere, so we can safely remove it. Differential Revision: https://phabricator.services.mozilla.com/D54733 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
efdf9bb557
Коммит
bb864342f1
|
@ -1,886 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
|
||||
var { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
|
||||
var {
|
||||
VariablesView,
|
||||
} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
|
||||
var Services = require("Services");
|
||||
var promise = require("promise");
|
||||
var defer = require("devtools/shared/defer");
|
||||
var { LocalizationHelper, ELLIPSIS } = require("devtools/shared/l10n");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "VARIABLES_SORTING_ENABLED", () =>
|
||||
Services.prefs.getBoolPref("devtools.debugger.ui.variables-sorting-enabled")
|
||||
);
|
||||
|
||||
const MAX_LONG_STRING_LENGTH = 200000;
|
||||
const MAX_PROPERTY_ITEMS = 2000;
|
||||
const DBG_STRINGS_URI = "devtools/client/locales/debugger.properties";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["VariablesViewController", "StackFrameUtils"];
|
||||
|
||||
/**
|
||||
* Localization convenience methods.
|
||||
*/
|
||||
var L10N = new LocalizationHelper(DBG_STRINGS_URI);
|
||||
|
||||
/**
|
||||
* Controller for a VariablesView that handles interfacing with the debugger
|
||||
* protocol. Is able to populate scopes and variables via the protocol as well
|
||||
* as manage actor lifespans.
|
||||
*
|
||||
* @param VariablesView aView
|
||||
* The view to attach to.
|
||||
* @param object aOptions [optional]
|
||||
* Options for configuring the controller. Supported options:
|
||||
* - getObjectFront: @see this._setClientGetters
|
||||
* - getLongStringFront: @see this._setClientGetters
|
||||
* - getEnvironmentFront: @see this._setClientGetters
|
||||
* - releaseActor: @see this._setClientGetters
|
||||
* - overrideValueEvalMacro: @see _setEvaluationMacros
|
||||
* - getterOrSetterEvalMacro: @see _setEvaluationMacros
|
||||
* - simpleValueEvalMacro: @see _setEvaluationMacros
|
||||
*/
|
||||
function VariablesViewController(aView, aOptions = {}) {
|
||||
this.addExpander = this.addExpander.bind(this);
|
||||
|
||||
this._setClientGetters(aOptions);
|
||||
this._setEvaluationMacros(aOptions);
|
||||
|
||||
this._actors = new Set();
|
||||
this.view = aView;
|
||||
this.view.controller = this;
|
||||
}
|
||||
this.VariablesViewController = VariablesViewController;
|
||||
|
||||
VariablesViewController.prototype = {
|
||||
/**
|
||||
* The default getter/setter evaluation macro.
|
||||
*/
|
||||
_getterOrSetterEvalMacro: VariablesView.getterOrSetterEvalMacro,
|
||||
|
||||
/**
|
||||
* The default override value evaluation macro.
|
||||
*/
|
||||
_overrideValueEvalMacro: VariablesView.overrideValueEvalMacro,
|
||||
|
||||
/**
|
||||
* The default simple value evaluation macro.
|
||||
*/
|
||||
_simpleValueEvalMacro: VariablesView.simpleValueEvalMacro,
|
||||
|
||||
/**
|
||||
* Set the functions used to retrieve debugger client grips.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Options for getting the client grips. Supported options:
|
||||
* - getObjectFront: callback for creating an object grip front
|
||||
* - getLongStringFront: callback for creating a long string front
|
||||
* - getEnvironmentFront: callback for creating an environment front
|
||||
* - releaseActor: callback for releasing an actor when it's no longer needed
|
||||
*/
|
||||
_setClientGetters: function(aOptions) {
|
||||
if (aOptions.getObjectFront) {
|
||||
this._getObjectFront = aOptions.getObjectFront;
|
||||
}
|
||||
if (aOptions.getLongStringFront) {
|
||||
this._getLongStringFront = aOptions.getLongStringFront;
|
||||
}
|
||||
if (aOptions.getEnvironmentFront) {
|
||||
this._getEnvironmentFront = aOptions.getEnvironmentFront;
|
||||
}
|
||||
if (aOptions.releaseActor) {
|
||||
this._releaseActor = aOptions.releaseActor;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the functions used when evaluating strings in the variables view.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Options for configuring the macros. Supported options:
|
||||
* - overrideValueEvalMacro: callback for creating an overriding eval macro
|
||||
* - getterOrSetterEvalMacro: callback for creating a getter/setter eval macro
|
||||
* - simpleValueEvalMacro: callback for creating a simple value eval macro
|
||||
*/
|
||||
_setEvaluationMacros: function(aOptions) {
|
||||
if (aOptions.overrideValueEvalMacro) {
|
||||
this._overrideValueEvalMacro = aOptions.overrideValueEvalMacro;
|
||||
}
|
||||
if (aOptions.getterOrSetterEvalMacro) {
|
||||
this._getterOrSetterEvalMacro = aOptions.getterOrSetterEvalMacro;
|
||||
}
|
||||
if (aOptions.simpleValueEvalMacro) {
|
||||
this._simpleValueEvalMacro = aOptions.simpleValueEvalMacro;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate a long string into a target using a grip.
|
||||
*
|
||||
* @param Variable aTarget
|
||||
* The target Variable/Property to put the retrieved string into.
|
||||
* @param LongStringActor aGrip
|
||||
* The long string grip that use to retrieve the full string.
|
||||
* @return Promise
|
||||
* The promise that will be resolved when the string is retrieved.
|
||||
*/
|
||||
_populateFromLongString: async function(aTarget, aGrip) {
|
||||
const from = aGrip.initial.length;
|
||||
const to = Math.min(aGrip.length, MAX_LONG_STRING_LENGTH);
|
||||
|
||||
const response = await this._getLongStringFront(aGrip).substring(from, to);
|
||||
// Stop tracking the actor because it's no longer needed.
|
||||
this.releaseActor(aGrip);
|
||||
|
||||
// Replace the preview with the full string and make it non-expandable.
|
||||
aTarget.onexpand = null;
|
||||
aTarget.setGrip(aGrip.initial + response);
|
||||
aTarget.hideArrow();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds pseudo items in case there is too many properties to display.
|
||||
* Each item can expand into property slices.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The property iterator grip.
|
||||
*/
|
||||
_populatePropertySlices: function(aTarget, aGrip) {
|
||||
if (aGrip.count < MAX_PROPERTY_ITEMS) {
|
||||
return this._populateFromPropertyIterator(aTarget, aGrip);
|
||||
}
|
||||
|
||||
// Divide the keys into quarters.
|
||||
const items = Math.ceil(aGrip.count / 4);
|
||||
const iterator = aGrip.propertyIterator;
|
||||
const promises = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const start = aGrip.start + i * items;
|
||||
const count = i != 3 ? items : aGrip.count - i * items;
|
||||
|
||||
// Create a new kind of grip, with additional fields to define the slice
|
||||
const sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: start,
|
||||
count: count,
|
||||
};
|
||||
|
||||
// Query the name of the first and last items for this slice
|
||||
const deferred = defer();
|
||||
iterator.names([start, start + count - 1], ({ names }) => {
|
||||
const label = "[" + names[0] + ELLIPSIS + names[1] + "]";
|
||||
const item = aTarget.addItem(label, {}, { internalItem: true });
|
||||
item.showArrow();
|
||||
this.addExpander(item, sliceGrip);
|
||||
deferred.resolve();
|
||||
});
|
||||
promises.push(deferred.promise);
|
||||
}
|
||||
|
||||
return promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a property slice for a Variable in the view using the already
|
||||
* property iterator
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The property iterator grip.
|
||||
*/
|
||||
_populateFromPropertyIterator: function(aTarget, aGrip) {
|
||||
if (aGrip.count >= MAX_PROPERTY_ITEMS) {
|
||||
// We already started to split, but there is still too many properties, split again.
|
||||
return this._populatePropertySlices(aTarget, aGrip);
|
||||
}
|
||||
// We started slicing properties, and the slice is now small enough to be displayed
|
||||
const deferred = defer();
|
||||
// eslint-disable-next-line mozilla/use-returnValue
|
||||
aGrip.propertyIterator.slice(
|
||||
aGrip.start,
|
||||
aGrip.count,
|
||||
({ ownProperties }) => {
|
||||
// Add all the variable properties.
|
||||
if (Object.keys(ownProperties).length > 0) {
|
||||
aTarget.addItems(ownProperties, {
|
||||
sorted: true,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander,
|
||||
});
|
||||
}
|
||||
deferred.resolve();
|
||||
}
|
||||
);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the properties for a Variable in the view using a new feature in FF40+
|
||||
* that allows iteration over properties in slices.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The grip to use to populate the target.
|
||||
* @param string aQuery [optional]
|
||||
* The query string used to fetch only a subset of properties
|
||||
*/
|
||||
_populateFromObjectWithIterator: function(aTarget, aGrip, aQuery) {
|
||||
// FF40+ starts exposing `ownPropertyLength` on ObjectActor's grip,
|
||||
// as well as `enumProperties` request.
|
||||
const deferred = defer();
|
||||
const objectFront = this._getObjectFront(aGrip);
|
||||
const isArray = aGrip.preview && aGrip.preview.kind === "ArrayLike";
|
||||
if (isArray) {
|
||||
// First enumerate array items, e.g. properties from `0` to `array.length`.
|
||||
const options = {
|
||||
ignoreNonIndexedProperties: true,
|
||||
query: aQuery,
|
||||
};
|
||||
objectFront.enumProperties(options).then(iterator => {
|
||||
const sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count,
|
||||
};
|
||||
this._populatePropertySlices(aTarget, sliceGrip).then(() => {
|
||||
// Then enumerate the rest of the properties, like length, buffer, etc.
|
||||
const options = {
|
||||
ignoreIndexedProperties: true,
|
||||
sort: true,
|
||||
query: aQuery,
|
||||
};
|
||||
objectFront.enumProperties(options).then(iterator => {
|
||||
const sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count,
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip));
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const options = { sort: true, query: aQuery };
|
||||
// For objects, we just enumerate all the properties sorted by name.
|
||||
objectFront.enumProperties(options).then(iterator => {
|
||||
const sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count,
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip));
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given prototype in the view.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aProtype
|
||||
* The prototype grip.
|
||||
*/
|
||||
_populateObjectPrototype: function(aTarget, aPrototype) {
|
||||
// Add the variable's __proto__.
|
||||
if (aPrototype && aPrototype.type != "null") {
|
||||
const proto = aTarget.addItem("__proto__", { value: aPrototype });
|
||||
this.addExpander(proto, aPrototype);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds properties to a Scope, Variable, or Property in the view. Triggered
|
||||
* when a scope is expanded or certain variables are hovered.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The grip to use to populate the target.
|
||||
*/
|
||||
_populateFromObject: function(aTarget, aGrip) {
|
||||
if (aGrip.class === "Proxy") {
|
||||
// Refuse to play the proxy's stupid game and just expose the target and handler.
|
||||
const deferred = defer();
|
||||
const objectFront = this._getObjectFront(aGrip);
|
||||
objectFront.getProxySlots().then(aResponse => {
|
||||
const target = aTarget.addItem(
|
||||
"<target>",
|
||||
{ value: aResponse.proxyTarget },
|
||||
{ internalItem: true }
|
||||
);
|
||||
this.addExpander(target, aResponse.proxyTarget);
|
||||
const handler = aTarget.addItem(
|
||||
"<handler>",
|
||||
{ value: aResponse.proxyHandler },
|
||||
{ internalItem: true }
|
||||
);
|
||||
this.addExpander(handler, aResponse.proxyHandler);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
if (aGrip.class === "Promise" && aGrip.promiseState) {
|
||||
const { state, value, reason } = aGrip.promiseState;
|
||||
aTarget.addItem("<state>", { value: state }, { internalItem: true });
|
||||
if (state === "fulfilled") {
|
||||
this.addExpander(
|
||||
aTarget.addItem("<value>", { value }, { internalItem: true }),
|
||||
value
|
||||
);
|
||||
} else if (state === "rejected") {
|
||||
this.addExpander(
|
||||
aTarget.addItem(
|
||||
"<reason>",
|
||||
{ value: reason },
|
||||
{ internalItem: true }
|
||||
),
|
||||
reason
|
||||
);
|
||||
}
|
||||
} else if (["Map", "WeakMap", "Set", "WeakSet"].includes(aGrip.class)) {
|
||||
const entriesList = aTarget.addItem(
|
||||
"<entries>",
|
||||
{},
|
||||
{ internalItem: true }
|
||||
);
|
||||
entriesList.showArrow();
|
||||
this.addExpander(entriesList, {
|
||||
type: "entries-list",
|
||||
obj: aGrip,
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch properties by slices if there is too many in order to prevent UI freeze.
|
||||
if (
|
||||
"ownPropertyLength" in aGrip &&
|
||||
aGrip.ownPropertyLength >= MAX_PROPERTY_ITEMS
|
||||
) {
|
||||
return this._populateFromObjectWithIterator(aTarget, aGrip).then(() => {
|
||||
const deferred = defer();
|
||||
const objectFront = this._getObjectFront(aGrip);
|
||||
objectFront.getPrototype().then(prototype => {
|
||||
this._populateObjectPrototype(aTarget, prototype);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
return this._populateProperties(aTarget, aGrip);
|
||||
},
|
||||
|
||||
_populateProperties: function(aTarget, aGrip, aOptions) {
|
||||
const deferred = defer();
|
||||
|
||||
const objectFront = this._getObjectFront(aGrip);
|
||||
objectFront.getPrototypeAndProperties().then(aResponse => {
|
||||
const ownProperties = aResponse.ownProperties || {};
|
||||
const prototype = aResponse.prototype || null;
|
||||
// 'safeGetterValues' is new and isn't necessary defined on old actors.
|
||||
const safeGetterValues = aResponse.safeGetterValues || {};
|
||||
const sortable = VariablesView.isSortable(aGrip.class);
|
||||
|
||||
// Merge the safe getter values into one object such that we can use it
|
||||
// in VariablesView.
|
||||
for (const name of Object.keys(safeGetterValues)) {
|
||||
if (name in ownProperties) {
|
||||
const { getterValue, getterPrototypeLevel } = safeGetterValues[name];
|
||||
ownProperties[name].getterValue = getterValue;
|
||||
ownProperties[name].getterPrototypeLevel = getterPrototypeLevel;
|
||||
} else {
|
||||
ownProperties[name] = safeGetterValues[name];
|
||||
}
|
||||
}
|
||||
|
||||
// Add all the variable properties.
|
||||
aTarget.addItems(ownProperties, {
|
||||
// Not all variables need to force sorted properties.
|
||||
sorted: sortable,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander,
|
||||
});
|
||||
|
||||
// Add the variable's __proto__.
|
||||
this._populateObjectPrototype(aTarget, prototype);
|
||||
|
||||
// If the object is a function we need to fetch its scope chain
|
||||
// to show them as closures for the respective function.
|
||||
if (aGrip.class == "Function") {
|
||||
objectFront.getScope().then(aResponse => {
|
||||
if (aResponse.error) {
|
||||
// This function is bound to a built-in object or it's not present
|
||||
// in the current scope chain. Not necessarily an actual error,
|
||||
// it just means that there's no closure for the function.
|
||||
console.warn(aResponse.error + ": " + aResponse.message);
|
||||
return void deferred.resolve();
|
||||
}
|
||||
this._populateWithClosure(aTarget, aResponse.scope).then(
|
||||
deferred.resolve
|
||||
);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the scope chain elements (closures) of a function variable.
|
||||
*
|
||||
* @param Variable aTarget
|
||||
* The variable where the properties will be placed into.
|
||||
* @param Scope aScope
|
||||
* The lexical environment form as specified in the protocol.
|
||||
*/
|
||||
_populateWithClosure: function(aTarget, aScope) {
|
||||
const objectScopes = [];
|
||||
let environment = aScope;
|
||||
const funcScope = aTarget.addItem("<Closure>");
|
||||
funcScope.target.setAttribute("scope", "");
|
||||
funcScope.showArrow();
|
||||
|
||||
do {
|
||||
// Create a scope to contain all the inspected variables.
|
||||
const label = StackFrameUtils.getScopeLabel(environment);
|
||||
|
||||
// Block scopes may have the same label, so make addItem allow duplicates.
|
||||
const closure = funcScope.addItem(label, undefined, { relaxed: true });
|
||||
closure.target.setAttribute("scope", "");
|
||||
closure.showArrow();
|
||||
|
||||
// Add nodes for every argument and every other variable in scope.
|
||||
if (environment.bindings) {
|
||||
this._populateWithEnvironmentBindings(closure, environment.bindings);
|
||||
} else {
|
||||
const deferred = defer();
|
||||
objectScopes.push(deferred.promise);
|
||||
this._getEnvironmentFront(environment)
|
||||
.getBindings()
|
||||
.then(response => {
|
||||
this._populateWithEnvironmentBindings(closure, response.bindings);
|
||||
deferred.resolve();
|
||||
});
|
||||
}
|
||||
} while ((environment = environment.parent));
|
||||
|
||||
return promise.all(objectScopes).then(() => {
|
||||
// Signal that scopes have been fetched.
|
||||
this.view.emit("fetched", "scopes", funcScope);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds nodes for every specified binding to the closure node.
|
||||
*
|
||||
* @param Variable aTarget
|
||||
* The variable where the bindings will be placed into.
|
||||
* @param object aBindings
|
||||
* The bindings form as specified in the protocol.
|
||||
*/
|
||||
_populateWithEnvironmentBindings: function(aTarget, aBindings) {
|
||||
// Add nodes for every argument in the scope.
|
||||
aTarget.addItems(
|
||||
aBindings.arguments.reduce((accumulator, arg) => {
|
||||
const name = Object.getOwnPropertyNames(arg)[0];
|
||||
const descriptor = arg[name];
|
||||
accumulator[name] = descriptor;
|
||||
return accumulator;
|
||||
}, {}),
|
||||
{
|
||||
// Arguments aren't sorted.
|
||||
sorted: false,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander,
|
||||
}
|
||||
);
|
||||
|
||||
// Add nodes for every other variable in the scope.
|
||||
aTarget.addItems(aBindings.variables, {
|
||||
// Not all variables need to force sorted properties.
|
||||
sorted: VARIABLES_SORTING_ENABLED,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander,
|
||||
});
|
||||
},
|
||||
|
||||
_populateFromEntries: function(target, grip) {
|
||||
const objGrip = grip.obj;
|
||||
const objectFront = this._getObjectFront(objGrip);
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
return new promise((resolve, reject) => {
|
||||
objectFront.enumEntries().then(response => {
|
||||
if (response.error) {
|
||||
// Older server might not support the enumEntries method
|
||||
console.warn(response.error + ": " + response.message);
|
||||
resolve();
|
||||
} else {
|
||||
const sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: response.iterator,
|
||||
start: 0,
|
||||
count: response.iterator.count,
|
||||
};
|
||||
|
||||
resolve(this._populatePropertySlices(target, sliceGrip));
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an 'onexpand' callback for a variable, lazily handling
|
||||
* the addition of new properties.
|
||||
*
|
||||
* @param Variable aTarget
|
||||
* The variable where the properties will be placed into.
|
||||
* @param any aSource
|
||||
* The source to use to populate the target.
|
||||
*/
|
||||
addExpander: function(aTarget, aSource) {
|
||||
// Attach evaluation macros as necessary.
|
||||
if (aTarget.getter || aTarget.setter) {
|
||||
aTarget.evaluationMacro = this._overrideValueEvalMacro;
|
||||
const getter = aTarget.get("get");
|
||||
if (getter) {
|
||||
getter.evaluationMacro = this._getterOrSetterEvalMacro;
|
||||
}
|
||||
const setter = aTarget.get("set");
|
||||
if (setter) {
|
||||
setter.evaluationMacro = this._getterOrSetterEvalMacro;
|
||||
}
|
||||
} else {
|
||||
aTarget.evaluationMacro = this._simpleValueEvalMacro;
|
||||
}
|
||||
|
||||
// If the source is primitive then an expander is not needed.
|
||||
if (VariablesView.isPrimitive({ value: aSource })) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the source is a long string then show the arrow.
|
||||
if (isActorGrip(aSource) && aSource.type == "longString") {
|
||||
aTarget.showArrow();
|
||||
}
|
||||
|
||||
// Make sure that properties are always available on expansion.
|
||||
aTarget.onexpand = () => this.populate(aTarget, aSource);
|
||||
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It's a good idea to be prepared in case of an expansion.
|
||||
if (aTarget.shouldPrefetch) {
|
||||
aTarget.addEventListener("mouseover", aTarget.onexpand);
|
||||
}
|
||||
|
||||
// Register all the actors that this controller now depends on.
|
||||
for (const grip of [aTarget.value, aTarget.getter, aTarget.setter]) {
|
||||
if (isActorGrip(grip)) {
|
||||
this._actors.add(grip.actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds properties to a Scope, Variable, or Property in the view. Triggered
|
||||
* when a scope is expanded or certain variables are hovered.
|
||||
*
|
||||
* This does not expand the target, it only populates it.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope to be expanded.
|
||||
* @param object aSource
|
||||
* The source to use to populate the target.
|
||||
* @return Promise
|
||||
* The promise that is resolved once the target has been expanded.
|
||||
*/
|
||||
populate: function(aTarget, aSource) {
|
||||
// Fetch the variables only once.
|
||||
if (aTarget._fetched) {
|
||||
return aTarget._fetched;
|
||||
}
|
||||
// Make sure the source grip is available.
|
||||
if (!aSource) {
|
||||
return promise.reject(
|
||||
new Error("No actor grip was given for the variable.")
|
||||
);
|
||||
}
|
||||
|
||||
const deferred = defer();
|
||||
aTarget._fetched = deferred.promise;
|
||||
|
||||
if (aSource.type === "property-iterator") {
|
||||
return this._populateFromPropertyIterator(aTarget, aSource);
|
||||
}
|
||||
|
||||
if (aSource.type === "entries-list") {
|
||||
return this._populateFromEntries(aTarget, aSource);
|
||||
}
|
||||
|
||||
if (aSource.type === "mapEntry" || aSource.type === "storageEntry") {
|
||||
aTarget.addItems(
|
||||
{
|
||||
key: { value: aSource.preview.key },
|
||||
value: { value: aSource.preview.value },
|
||||
},
|
||||
{
|
||||
callback: this.addExpander,
|
||||
}
|
||||
);
|
||||
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
// If the target is a Variable or Property then we're fetching properties.
|
||||
if (VariablesView.isVariable(aTarget)) {
|
||||
this._populateFromObject(aTarget, aSource).then(() => {
|
||||
// Signal that properties have been fetched.
|
||||
this.view.emit("fetched", "properties", aTarget);
|
||||
// Commit the hierarchy because new items were added.
|
||||
this.view.commitHierarchy();
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
switch (aSource.type) {
|
||||
case "longString":
|
||||
this._populateFromLongString(aTarget, aSource).then(() => {
|
||||
// Signal that a long string has been fetched.
|
||||
this.view.emit("fetched", "longString", aTarget);
|
||||
deferred.resolve();
|
||||
});
|
||||
break;
|
||||
case "with":
|
||||
case "object":
|
||||
this._populateFromObject(aTarget, aSource.object).then(() => {
|
||||
// Signal that variables have been fetched.
|
||||
this.view.emit("fetched", "variables", aTarget);
|
||||
// Commit the hierarchy because new items were added.
|
||||
this.view.commitHierarchy();
|
||||
deferred.resolve();
|
||||
});
|
||||
break;
|
||||
case "block":
|
||||
case "function":
|
||||
this._populateWithEnvironmentBindings(aTarget, aSource.bindings);
|
||||
// No need to signal that variables have been fetched, since
|
||||
// the scope arguments and variables are already attached to the
|
||||
// environment bindings, so pausing the active thread is unnecessary.
|
||||
// Commit the hierarchy because new items were added.
|
||||
this.view.commitHierarchy();
|
||||
deferred.resolve();
|
||||
break;
|
||||
default:
|
||||
const error = "Unknown Debugger.Environment type: " + aSource.type;
|
||||
console.error(error);
|
||||
deferred.reject(error);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates to the view if the targeted actor supports properties search
|
||||
*
|
||||
* @return boolean True, if the actor supports enumProperty request
|
||||
*/
|
||||
supportsSearch: function() {
|
||||
// FF40+ starts exposing ownPropertyLength on object actor's grip
|
||||
// as well as enumProperty which allows to query a subset of properties.
|
||||
return this.objectActor && "ownPropertyLength" in this.objectActor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Try to use the actor to perform an attribute search.
|
||||
*
|
||||
* @param Scope aScope
|
||||
* The Scope instance to populate with properties
|
||||
* @param string aToken
|
||||
* The query string
|
||||
*/
|
||||
performSearch: function(aScope, aToken) {
|
||||
this._populateFromObjectWithIterator(aScope, this.objectActor, aToken).then(
|
||||
() => {
|
||||
this.view.emit("fetched", "search", aScope);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release an actor from the controller.
|
||||
*
|
||||
* @param object aActor
|
||||
* The actor to release.
|
||||
*/
|
||||
releaseActor: function(aActor) {
|
||||
if (this._releaseActor) {
|
||||
this._releaseActor(aActor);
|
||||
}
|
||||
this._actors.delete(aActor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release all the actors referenced by the controller, optionally filtered.
|
||||
*
|
||||
* @param function aFilter [optional]
|
||||
* Callback to filter which actors are released.
|
||||
*/
|
||||
releaseActors: function(aFilter) {
|
||||
for (const actor of this._actors) {
|
||||
if (!aFilter || aFilter(actor)) {
|
||||
this.releaseActor(actor);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function for setting up a single Scope with a single Variable
|
||||
* contained within it.
|
||||
*
|
||||
* This function will empty the variables view.
|
||||
*
|
||||
* @param object options
|
||||
* Options for the contents of the view:
|
||||
* - objectActor: the grip of the new ObjectActor to show.
|
||||
* - rawObject: the raw object to show.
|
||||
* - label: the label for the inspected object.
|
||||
* @param object configuration
|
||||
* Additional options for the controller:
|
||||
* - overrideValueEvalMacro: @see _setEvaluationMacros
|
||||
* - getterOrSetterEvalMacro: @see _setEvaluationMacros
|
||||
* - simpleValueEvalMacro: @see _setEvaluationMacros
|
||||
* @return Object
|
||||
* - variable: the created Variable.
|
||||
* - expanded: the Promise that resolves when the variable expands.
|
||||
*/
|
||||
setSingleVariable: function(options, configuration = {}) {
|
||||
this._setEvaluationMacros(configuration);
|
||||
this.view.empty();
|
||||
|
||||
const scope = this.view.addScope(options.label);
|
||||
scope.expanded = true; // Expand the scope by default.
|
||||
scope.locked = true; // Prevent collapsing the scope.
|
||||
|
||||
const variable = scope.addItem(undefined, { enumerable: true });
|
||||
let populated;
|
||||
|
||||
if (options.objectActor) {
|
||||
// Save objectActor for properties filtering
|
||||
this.objectActor = options.objectActor;
|
||||
if (VariablesView.isPrimitive({ value: this.objectActor })) {
|
||||
populated = promise.resolve();
|
||||
} else {
|
||||
populated = this.populate(variable, options.objectActor);
|
||||
variable.expand();
|
||||
}
|
||||
} else if (options.rawObject) {
|
||||
variable.populate(options.rawObject, { expanded: true });
|
||||
populated = promise.resolve();
|
||||
}
|
||||
|
||||
return { variable: variable, expanded: populated };
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches a VariablesViewController to a VariablesView if it doesn't already
|
||||
* have one.
|
||||
*
|
||||
* @param VariablesView aView
|
||||
* The view to attach to.
|
||||
* @param object aOptions
|
||||
* The options to use in creating the controller.
|
||||
* @return VariablesViewController
|
||||
*/
|
||||
VariablesViewController.attach = function(aView, aOptions) {
|
||||
if (aView.controller) {
|
||||
return aView.controller;
|
||||
}
|
||||
return new VariablesViewController(aView, aOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility functions for handling stackframes.
|
||||
*/
|
||||
var StackFrameUtils = (this.StackFrameUtils = {
|
||||
/**
|
||||
* Create a textual representation for the specified stack frame
|
||||
* to display in the stackframes container.
|
||||
*
|
||||
* @param object aFrame
|
||||
* The stack frame to label.
|
||||
*/
|
||||
getFrameTitle: function(aFrame) {
|
||||
if (aFrame.type == "call") {
|
||||
const c = aFrame.callee;
|
||||
return c.name || c.userDisplayName || c.displayName || "(anonymous)";
|
||||
}
|
||||
return "(" + aFrame.type + ")";
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a scope label based on its environment.
|
||||
*
|
||||
* @param object aEnv
|
||||
* The scope's environment.
|
||||
* @return string
|
||||
* The scope's label.
|
||||
*/
|
||||
getScopeLabel: function(aEnv) {
|
||||
let name = "";
|
||||
|
||||
// Name the outermost scope Global.
|
||||
if (!aEnv.parent) {
|
||||
name = L10N.getStr("globalScopeLabel");
|
||||
} else {
|
||||
// Otherwise construct the scope name.
|
||||
name = aEnv.type.charAt(0).toUpperCase() + aEnv.type.slice(1);
|
||||
}
|
||||
|
||||
let label = L10N.getFormatStr("scopeLabel", name);
|
||||
switch (aEnv.type) {
|
||||
case "with":
|
||||
case "object":
|
||||
label += " [" + aEnv.object.class + "]";
|
||||
break;
|
||||
case "function":
|
||||
const f = aEnv.function;
|
||||
label +=
|
||||
" [" +
|
||||
(f.name || f.userDisplayName || f.displayName || "(anonymous)") +
|
||||
"]";
|
||||
break;
|
||||
}
|
||||
return label;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the given value is a grip with an actor.
|
||||
*
|
||||
* @param mixed grip
|
||||
* Value you want to check if it is a grip with an actor.
|
||||
* @return boolean
|
||||
* True if the given value is a grip with an actor.
|
||||
*/
|
||||
function isActorGrip(grip) {
|
||||
return grip && typeof grip == "object" && grip.actor;
|
||||
}
|
|
@ -23,7 +23,5 @@ DevToolsModules(
|
|||
'Spectrum.js',
|
||||
'TableWidget.js',
|
||||
'TreeWidget.js',
|
||||
'VariablesView.jsm',
|
||||
'VariablesViewController.jsm',
|
||||
'view-helpers.js',
|
||||
)
|
||||
|
|
|
@ -232,7 +232,6 @@
|
|||
"utils.js": ["encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer", "getLoginTelemetryScalar", "syncTestLogging"],
|
||||
"Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache"],
|
||||
"VariablesView.jsm": ["VariablesView", "escapeHTML"],
|
||||
"VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
|
||||
"version.jsm": ["VERSION"],
|
||||
"vtt.jsm": ["WebVTT"],
|
||||
"WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
|
||||
|
|
Загрузка…
Ссылка в новой задаче