mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 1023386 - Split and filter properties remotely for objects. r=past
This commit is contained in:
parent
208678a31d
commit
6259e01e06
@ -70,10 +70,7 @@ function initialChecks() {
|
||||
is(objectVar.expanded, false,
|
||||
"The 'largeObject' variable shouldn't be expanded.");
|
||||
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES, 2);
|
||||
arrayVar.expand();
|
||||
objectVar.expand();
|
||||
return finished;
|
||||
return promise.all([arrayVar.expand(),objectVar.expand()]);
|
||||
}
|
||||
|
||||
function verifyFirstLevel() {
|
||||
@ -96,55 +93,56 @@ function verifyFirstLevel() {
|
||||
"The 'largeObject' should contain all the created non-enumerable elements.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
0 + gEllipsis + 1999, "The first page in the 'largeArray' is named correctly.");
|
||||
"[0" + gEllipsis + "2499]", "The first page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"", "The first page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
2000 + gEllipsis + 3999, "The second page in the 'largeArray' is named correctly.");
|
||||
"[2500" + gEllipsis + "4999]", "The second page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
"", "The second page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
4000 + gEllipsis + 5999, "The third page in the 'largeArray' is named correctly.");
|
||||
"[5000" + gEllipsis + "7499]", "The third page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[2].getAttribute("value"),
|
||||
"", "The third page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
6000 + gEllipsis + 9999, "The fourth page in the 'largeArray' is named correctly.");
|
||||
"[7500" + gEllipsis + "9999]", "The fourth page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[3].getAttribute("value"),
|
||||
"", "The fourth page in the 'largeArray' should not have a corresponding value.");
|
||||
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
0 + gEllipsis + 1999, "The first page in the 'largeObject' is named correctly.");
|
||||
"[0" + gEllipsis + "2499]", "The first page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"", "The first page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
2000 + gEllipsis + 3999, "The second page in the 'largeObject' is named correctly.");
|
||||
"[2500" + gEllipsis + "4999]", "The second page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
"", "The second page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
4000 + gEllipsis + 5999, "The thrid page in the 'largeObject' is named correctly.");
|
||||
"[5000" + gEllipsis + "7499]", "The thrid page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[2].getAttribute("value"),
|
||||
"", "The thrid page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
6000 + gEllipsis + 9999, "The fourth page in the 'largeObject' is named correctly.");
|
||||
"[7500" + gEllipsis + "9999]", "The fourth page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[3].getAttribute("value"),
|
||||
"", "The fourth page in the 'largeObject' should not have a corresponding value.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[4].getAttribute("value"),
|
||||
"length", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[4].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[5].getAttribute("value"),
|
||||
"buffer", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[5].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[4].getAttribute("value"),
|
||||
"ArrayBuffer", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[6].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[5].getAttribute("value"),
|
||||
"byteLength", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[6].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[5].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[7].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[6].getAttribute("value"),
|
||||
"byteOffset", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[7].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[6].getAttribute("value"),
|
||||
"0", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[7].getAttribute("value"),
|
||||
"length", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[7].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[8].getAttribute("value"),
|
||||
"__proto__", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[8].getAttribute("value"),
|
||||
@ -160,10 +158,13 @@ function verifyNextLevels() {
|
||||
let localScope = gVariables.getScopeAtIndex(0);
|
||||
let objectVar = localScope.get("largeObject");
|
||||
|
||||
let lastPage1 = objectVar.get(6000 + gEllipsis + 9999);
|
||||
let lastPage1 = objectVar.get("[7500" + gEllipsis + "9999]");
|
||||
ok(lastPage1, "The last page in the first level was retrieved successfully.");
|
||||
lastPage1.expand();
|
||||
return lastPage1.expand()
|
||||
.then(verifyNextLevels2.bind(null, lastPage1));
|
||||
}
|
||||
|
||||
function verifyNextLevels2(lastPage1) {
|
||||
let pageEnums1 = lastPage1.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums1 = lastPage1.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums1.length, 0,
|
||||
@ -172,61 +173,44 @@ function verifyNextLevels() {
|
||||
"The last page in the first level should contain all the created non-enumerable elements.");
|
||||
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
6000 + gEllipsis + 6999, "The first page in this level named correctly (1).");
|
||||
"[7500" + gEllipsis + "8124]", "The first page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
7000 + gEllipsis + 7999, "The second page in this level named correctly (1).");
|
||||
"[8125" + gEllipsis + "8749]", "The second page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
8000 + gEllipsis + 8999, "The third page in this level named correctly (1).");
|
||||
"[8750" + gEllipsis + "9374]", "The third page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
9000 + gEllipsis + 9999, "The fourth page in this level named correctly (1).");
|
||||
"[9375" + gEllipsis + "9999]", "The fourth page in this level named correctly (1).");
|
||||
|
||||
let lastPage2 = lastPage1.get(9000 + gEllipsis + 9999);
|
||||
let lastPage2 = lastPage1.get("[9375" + gEllipsis + "9999]");
|
||||
ok(lastPage2, "The last page in the second level was retrieved successfully.");
|
||||
lastPage2.expand();
|
||||
return lastPage2.expand()
|
||||
.then(verifyNextLevels3.bind(null, lastPage2));
|
||||
}
|
||||
|
||||
function verifyNextLevels3(lastPage2) {
|
||||
let pageEnums2 = lastPage2.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums2 = lastPage2.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums2.length, 0,
|
||||
"The last page in the second level shouldn't contain any enumerable elements.");
|
||||
is(pageNonEnums2.length, 4,
|
||||
"The last page in the second level should contain all the created non-enumerable elements.");
|
||||
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9000 + gEllipsis + 9199, "The first page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9200 + gEllipsis + 9399, "The second page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
9400 + gEllipsis + 9599, "The third page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
9600 + gEllipsis + 9999, "The fourth page in this level named correctly (2).");
|
||||
|
||||
let lastPage3 = lastPage2.get(9600 + gEllipsis + 9999);
|
||||
ok(lastPage3, "The last page in the third level was retrieved successfully.");
|
||||
lastPage3.expand();
|
||||
|
||||
let pageEnums3 = lastPage3.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums3 = lastPage3.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums3.length, 400,
|
||||
is(pageEnums2.length, 625,
|
||||
"The last page in the third level should contain all the created enumerable elements.");
|
||||
is(pageNonEnums3.length, 0,
|
||||
is(pageNonEnums2.length, 0,
|
||||
"The last page in the third level shouldn't contain any non-enumerable elements.");
|
||||
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9600, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9601, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[398].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9375, "The properties in this level are named correctly (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9376, "The properties in this level are named correctly (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[623].getAttribute("value"),
|
||||
9998, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[399].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[624].getAttribute("value"),
|
||||
9999, "The properties in this level are named correctly (3).");
|
||||
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
399, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
398, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[398].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
624, "The properties in this level have the correct value (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
623, "The properties in this level have the correct value (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[623].getAttribute("value"),
|
||||
1, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[399].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[624].getAttribute("value"),
|
||||
0, "The properties in this level have the correct value (3).");
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
|
||||
const LAZY_EMPTY_DELAY = 150; // ms
|
||||
const LAZY_EXPAND_DELAY = 50; // ms
|
||||
const SCROLL_PAGE_SIZE_DEFAULT = 0;
|
||||
const APPEND_PAGE_SIZE_DEFAULT = 500;
|
||||
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
|
||||
const PAGE_SIZE_MAX_JUMPS = 30;
|
||||
const SEARCH_ACTION_MAX_DELAY = 300; // ms
|
||||
@ -246,12 +245,6 @@ VariablesView.prototype = {
|
||||
*/
|
||||
scrollPageSize: SCROLL_PAGE_SIZE_DEFAULT,
|
||||
|
||||
/**
|
||||
* The maximum number of elements allowed in a scope, variable or property
|
||||
* that allows pagination when appending children.
|
||||
*/
|
||||
appendPageSize: APPEND_PAGE_SIZE_DEFAULT,
|
||||
|
||||
/**
|
||||
* Function called each time a variable or property's value is changed via
|
||||
* user interaction. If null, then value changes are disabled.
|
||||
@ -556,6 +549,14 @@ VariablesView.prototype = {
|
||||
* The variable or property to search for.
|
||||
*/
|
||||
_doSearch: function(aToken) {
|
||||
if (this.controller.supportsSearch()) {
|
||||
this.empty();
|
||||
let scope = this.addScope(aToken);
|
||||
scope.expanded = true; // Expand the scope by default.
|
||||
scope.locked = true; // Prevent collapsing the scope.
|
||||
this.controller.performSearch(scope, aToken);
|
||||
return;
|
||||
}
|
||||
for (let scope of this._store) {
|
||||
switch (aToken) {
|
||||
case "":
|
||||
@ -1214,7 +1215,6 @@ function Scope(aView, aName, aFlags = {}) {
|
||||
// Inherit properties and flags from the parent view. You can override
|
||||
// each of these directly onto any scope, variable or property instance.
|
||||
this.scrollPageSize = aView.scrollPageSize;
|
||||
this.appendPageSize = aView.appendPageSize;
|
||||
this.eval = aView.eval;
|
||||
this.switch = aView.switch;
|
||||
this.delete = aView.delete;
|
||||
@ -1320,81 +1320,12 @@ Scope.prototype = {
|
||||
* Additional options for adding the properties. Supported options:
|
||||
* - sorted: true to sort all the properties before adding them
|
||||
* - callback: function invoked after each item is added
|
||||
* @param string aKeysType [optional]
|
||||
* Helper argument in the case of paginated items. Can be either
|
||||
* "just-strings" or "just-numbers". Humans shouldn't use this argument.
|
||||
*/
|
||||
addItems: function(aItems, aOptions = {}, aKeysType = "") {
|
||||
addItems: function(aItems, aOptions = {}) {
|
||||
let names = Object.keys(aItems);
|
||||
|
||||
// Building the view when inspecting an object with a very large number of
|
||||
// properties may take a long time. To avoid blocking the UI, group
|
||||
// the items into several lazily populated pseudo-items.
|
||||
let exceedsThreshold = names.length >= this.appendPageSize;
|
||||
let shouldPaginate = exceedsThreshold && aKeysType != "just-strings";
|
||||
if (shouldPaginate && this.allowPaginate) {
|
||||
// Group the items to append into two separate arrays, one containing
|
||||
// number-like keys, the other one containing string keys.
|
||||
if (aKeysType == "just-numbers") {
|
||||
var numberKeys = names;
|
||||
var stringKeys = [];
|
||||
} else {
|
||||
var numberKeys = [];
|
||||
var stringKeys = [];
|
||||
for (let name of names) {
|
||||
// Be very careful. Avoid Infinity, NaN and non Natural number keys.
|
||||
let coerced = +name;
|
||||
if (Number.isInteger(coerced) && coerced > -1) {
|
||||
numberKeys.push(name);
|
||||
} else {
|
||||
stringKeys.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This object contains a very large number of properties, but they're
|
||||
// almost all strings that can't be coerced to numbers. Don't paginate.
|
||||
if (numberKeys.length < this.appendPageSize) {
|
||||
this.addItems(aItems, aOptions, "just-strings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Slices a section of the { name: descriptor } data properties.
|
||||
let paginate = (aArray, aBegin = 0, aEnd = aArray.length) => {
|
||||
let store = {}
|
||||
for (let i = aBegin; i < aEnd; i++) {
|
||||
let name = aArray[i];
|
||||
store[name] = aItems[name];
|
||||
}
|
||||
return store;
|
||||
};
|
||||
|
||||
// Creates a pseudo-item that populates itself with the data properties
|
||||
// from the corresponding page range.
|
||||
let createRangeExpander = (aArray, aBegin, aEnd, aOptions, aKeyTypes) => {
|
||||
let rangeVar = this.addItem(aArray[aBegin] + Scope.ellipsis + aArray[aEnd - 1]);
|
||||
rangeVar.onexpand = () => {
|
||||
let pageItems = paginate(aArray, aBegin, aEnd);
|
||||
rangeVar.addItems(pageItems, aOptions, aKeyTypes);
|
||||
}
|
||||
rangeVar.showArrow();
|
||||
rangeVar.target.setAttribute("pseudo-item", "");
|
||||
};
|
||||
|
||||
// Divide the number keys into quarters.
|
||||
let page = +Math.round(numberKeys.length / 4).toPrecision(1);
|
||||
createRangeExpander(numberKeys, 0, page, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page, page * 2, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page * 2, page * 3, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page * 3, numberKeys.length, aOptions, "just-numbers");
|
||||
|
||||
// Append all the string keys together.
|
||||
this.addItems(paginate(stringKeys), aOptions, "just-strings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort all of the properties before adding them, if preferred.
|
||||
if (aOptions.sorted && aKeysType != "just-numbers") {
|
||||
if (aOptions.sorted) {
|
||||
names.sort(this._naturalSort);
|
||||
}
|
||||
|
||||
@ -1536,7 +1467,11 @@ Scope.prototype = {
|
||||
this._isExpanded = true;
|
||||
|
||||
if (this.onexpand) {
|
||||
this.onexpand(this);
|
||||
// We return onexpand as it sometimes returns a promise
|
||||
// (up to the user of VariableView to do it)
|
||||
// that can indicate when the view is done expanding
|
||||
// and attributes are available. (Mostly used for tests)
|
||||
return this.onexpand(this);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -32,8 +32,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
const MAX_LONG_STRING_LENGTH = 200000;
|
||||
const MAX_PROPERTY_ITEMS = 2000;
|
||||
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
|
||||
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["VariablesViewController", "StackFrameUtils"];
|
||||
|
||||
|
||||
@ -158,6 +161,166 @@ VariablesViewController.prototype = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param object aIterator
|
||||
* The property iterator client.
|
||||
*/
|
||||
_populatePropertySlices: function(aTarget, aGrip, aIterator) {
|
||||
if (aGrip.count < MAX_PROPERTY_ITEMS) {
|
||||
return this._populateFromPropertyIterator(aTarget, aGrip);
|
||||
}
|
||||
|
||||
// Divide the keys into quarters.
|
||||
let items = Math.ceil(aGrip.count / 4);
|
||||
|
||||
let promises = [];
|
||||
for(let i = 0; i < 4; i++) {
|
||||
let start = aGrip.start + i * items;
|
||||
let count = i != 3 ? items : aGrip.count - i * items;
|
||||
|
||||
// Create a new kind of grip, with additional fields to define the slice
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: aIterator,
|
||||
start: start,
|
||||
count: count
|
||||
};
|
||||
|
||||
// Query the name of the first and last items for this slice
|
||||
let deferred = promise.defer();
|
||||
aIterator.names([start, start + count - 1], ({ names }) => {
|
||||
let label = "[" + names[0] + ELLIPSIS + names[1] + "]";
|
||||
let item = aTarget.addItem(label);
|
||||
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, aGrip.propertyIterator);
|
||||
}
|
||||
// We started slicing properties, and the slice is now small enough to be displayed
|
||||
let deferred = promise.defer();
|
||||
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.
|
||||
let deferred = promise.defer();
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
let isArray = aGrip.preview && aGrip.preview.kind === "ArrayLike";
|
||||
if (isArray) {
|
||||
// First enumerate array items, e.g. properties from `0` to `array.length`.
|
||||
let options = {
|
||||
ignoreNonIndexedProperties: true,
|
||||
ignoreSafeGetters: true,
|
||||
query: aQuery
|
||||
};
|
||||
objectClient.enumProperties(options, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
this._populatePropertySlices(aTarget, sliceGrip, iterator)
|
||||
.then(() => {
|
||||
// Then enumerate the rest of the properties, like length, buffer, etc.
|
||||
let options = {
|
||||
ignoreIndexedProperties: true,
|
||||
sort: true,
|
||||
query: aQuery
|
||||
};
|
||||
objectClient.enumProperties(options, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip, iterator));
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// For objects, we just enumerate all the properties sorted by name.
|
||||
objectClient.enumProperties({ sort: true, query: aQuery }, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip, iterator));
|
||||
});
|
||||
|
||||
}
|
||||
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") {
|
||||
let 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.
|
||||
@ -168,7 +331,19 @@ VariablesViewController.prototype = {
|
||||
* The grip to use to populate the target.
|
||||
*/
|
||||
_populateFromObject: function(aTarget, 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(() => {
|
||||
let deferred = promise.defer();
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
objectClient.getPrototype(({ prototype }) => {
|
||||
this._populateObjectPrototype(aTarget, prototype);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
if (aGrip.class === "Promise" && aGrip.promiseState) {
|
||||
const { state, value, reason } = aGrip.promiseState;
|
||||
@ -179,10 +354,16 @@ VariablesViewController.prototype = {
|
||||
this.addExpander(aTarget.addItem("<reason>", { value: reason }), reason);
|
||||
}
|
||||
}
|
||||
return this._populateProperties(aTarget, aGrip);
|
||||
},
|
||||
|
||||
_populateProperties: function(aTarget, aGrip, aOptions) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
objectClient.getPrototypeAndProperties(aResponse => {
|
||||
let { ownProperties, prototype } = aResponse;
|
||||
let ownProperties = aResponse.ownProperties || {};
|
||||
let prototype = aResponse.prototype || null;
|
||||
// 'safeGetterValues' is new and isn't necessary defined on old actors.
|
||||
let safeGetterValues = aResponse.safeGetterValues || {};
|
||||
let sortable = VariablesView.isSortable(aGrip.class);
|
||||
@ -200,21 +381,15 @@ VariablesViewController.prototype = {
|
||||
}
|
||||
|
||||
// Add all the variable properties.
|
||||
if (ownProperties) {
|
||||
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__.
|
||||
if (prototype && prototype.type != "null") {
|
||||
let proto = aTarget.addItem("__proto__", { value: prototype });
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
this.addExpander(proto, prototype);
|
||||
}
|
||||
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.
|
||||
@ -389,6 +564,10 @@ VariablesViewController.prototype = {
|
||||
let deferred = promise.defer();
|
||||
aTarget._fetched = deferred.promise;
|
||||
|
||||
if (aSource.type === "property-iterator") {
|
||||
return this._populateFromPropertyIterator(aTarget, aSource);
|
||||
}
|
||||
|
||||
// If the target is a Variable or Property then we're fetching properties.
|
||||
if (VariablesView.isVariable(aTarget)) {
|
||||
this._populateFromObject(aTarget, aSource).then(() => {
|
||||
@ -438,6 +617,29 @@ VariablesViewController.prototype = {
|
||||
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);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release an actor from the controller.
|
||||
*
|
||||
@ -497,6 +699,8 @@ VariablesViewController.prototype = {
|
||||
let populated;
|
||||
|
||||
if (aOptions.objectActor) {
|
||||
// Save objectActor for properties filtering
|
||||
this.objectActor = aOptions.objectActor;
|
||||
populated = this.populate(variable, aOptions.objectActor);
|
||||
variable.expand();
|
||||
} else if (aOptions.rawObject) {
|
||||
|
@ -5345,6 +5345,20 @@
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'prototypeAndProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ENUMPROPERTIES_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'enumProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ENUMPROPERTIES_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'enumProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOTYPESANDPROPERTIES_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
|
@ -2327,6 +2327,39 @@ ObjectClient.prototype = {
|
||||
telemetry: "PROTOTYPEANDPROPERTIES"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request a PropertyIteratorClient instance to ease listing
|
||||
* properties for this object.
|
||||
*
|
||||
* @param options Object
|
||||
* A dictionary object with various boolean attributes:
|
||||
* - ignoreSafeGetters Boolean
|
||||
* If true, do not iterate over safe getters.
|
||||
* - ignoreIndexedProperties Boolean
|
||||
* If true, filters out Array items.
|
||||
* e.g. properties names between `0` and `object.length`.
|
||||
* - ignoreNonIndexedProperties Boolean
|
||||
* If true, filters out items that aren't array items
|
||||
* e.g. properties names that are not a number between `0`
|
||||
* and `object.length`.
|
||||
* - sort Boolean
|
||||
* If true, the iterator will sort the properties by name
|
||||
* before dispatching them.
|
||||
* @param aOnResponse function Called with the client instance.
|
||||
*/
|
||||
enumProperties: DebuggerClient.requester({
|
||||
type: "enumProperties",
|
||||
options: args(0)
|
||||
}, {
|
||||
after: function(aResponse) {
|
||||
if (aResponse.iterator) {
|
||||
return { iterator: new PropertyIteratorClient(this._client, aResponse.iterator) };
|
||||
}
|
||||
return aResponse;
|
||||
},
|
||||
telemetry: "ENUMPROPERTIES"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the property descriptor of the object's specified property.
|
||||
*
|
||||
@ -2380,6 +2413,74 @@ ObjectClient.prototype = {
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* A PropertyIteratorClient provides a way to access to property names and
|
||||
* values of an object efficiently, slice by slice.
|
||||
* Note that the properties can be sorted in the backend,
|
||||
* this is controled while creating the PropertyIteratorClient
|
||||
* from ObjectClient.enumProperties.
|
||||
*
|
||||
* @param aClient DebuggerClient
|
||||
* The debugger client parent.
|
||||
* @param aGrip Object
|
||||
* A PropertyIteratorActor grip returned by the protocol via
|
||||
* TabActor.enumProperties request.
|
||||
*/
|
||||
function PropertyIteratorClient(aClient, aGrip) {
|
||||
this._grip = aGrip;
|
||||
this._client = aClient;
|
||||
this.request = this._client.request;
|
||||
}
|
||||
|
||||
PropertyIteratorClient.prototype = {
|
||||
get actor() { return this._grip.actor; },
|
||||
|
||||
/**
|
||||
* Get the total number of properties available in the iterator.
|
||||
*/
|
||||
get count() { return this._grip.count; },
|
||||
|
||||
/**
|
||||
* Get one or more property names that correspond to the positions in the
|
||||
* indexes parameter.
|
||||
*
|
||||
* @param indexes Array
|
||||
* An array of property indexes.
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property names.
|
||||
*/
|
||||
names: DebuggerClient.requester({
|
||||
type: "names",
|
||||
indexes: args(0)
|
||||
}, {}),
|
||||
|
||||
/**
|
||||
* Get a set of following property value(s).
|
||||
*
|
||||
* @param start Number
|
||||
* The index of the first property to fetch.
|
||||
* @param count Number
|
||||
* The number of properties to fetch.
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property values.
|
||||
*/
|
||||
slice: DebuggerClient.requester({
|
||||
type: "slice",
|
||||
start: args(0),
|
||||
count: args(1)
|
||||
}, {}),
|
||||
|
||||
/**
|
||||
* Get all the property values.
|
||||
*
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property values.
|
||||
*/
|
||||
all: DebuggerClient.requester({
|
||||
type: "all"
|
||||
}, {}),
|
||||
};
|
||||
|
||||
/**
|
||||
* A LongStringClient provides a way to access "very long" strings from the
|
||||
* debugger server.
|
||||
|
@ -3247,6 +3247,163 @@ let stringifiers = {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates an actor to iterate over an object's property names and values.
|
||||
*
|
||||
* @param aObjectActor ObjectActor
|
||||
* The object actor.
|
||||
* @param aOptions Object
|
||||
* A dictionary object with various boolean attributes:
|
||||
* - ignoreSafeGetters Boolean
|
||||
* If true, do not iterate over safe getters.
|
||||
* - ignoreIndexedProperties Boolean
|
||||
* If true, filters out Array items.
|
||||
* e.g. properties names between `0` and `object.length`.
|
||||
* - ignoreNonIndexedProperties Boolean
|
||||
* If true, filters out items that aren't array items
|
||||
* e.g. properties names that are not a number between `0`
|
||||
* and `object.length`.
|
||||
* - sort Boolean
|
||||
* If true, the iterator will sort the properties by name
|
||||
* before dispatching them.
|
||||
* - query String
|
||||
* If non-empty, will filter the properties by names containing
|
||||
* this query string. The match is not case-sensitive.
|
||||
*/
|
||||
function PropertyIteratorActor(aObjectActor, aOptions)
|
||||
{
|
||||
this.objectActor = aObjectActor;
|
||||
|
||||
let ownProperties = Object.create(null);
|
||||
let names = [];
|
||||
try {
|
||||
names = this.objectActor.obj.getOwnPropertyNames();
|
||||
} catch (ex) {}
|
||||
|
||||
|
||||
let safeGetterValues = {};
|
||||
let safeGetterNames = [];
|
||||
if (!aOptions.ignoreSafeGetters) {
|
||||
// Merge the safe getter values into the existing properties list.
|
||||
safeGetterValues = this.objectActor._findSafeGetterValues(names);
|
||||
safeGetterNames = Object.keys(safeGetterValues);
|
||||
for (let name of safeGetterNames) {
|
||||
if (names.indexOf(name) === -1) {
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.ignoreIndexedProperties || aOptions.ignoreNonIndexedProperties) {
|
||||
let length = DevToolsUtils.getProperty(this.objectActor.obj, "length");
|
||||
if (typeof(length) !== "number") {
|
||||
// Pseudo arrays are flagged as ArrayLike if they have
|
||||
// subsequent indexed properties without having any length attribute.
|
||||
length = 0;
|
||||
for (let key of names) {
|
||||
if (isNaN(key) || key != length++) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.ignoreIndexedProperties) {
|
||||
names = names.filter(i => {
|
||||
// Use parseFloat in order to reject floats...
|
||||
// (parseInt converts floats to integer)
|
||||
// (Number(str) converts spaces to 0)
|
||||
i = parseFloat(i);
|
||||
return !Number.isInteger(i) || i < 0 || i >= length;
|
||||
});
|
||||
}
|
||||
|
||||
if (aOptions.ignoreNonIndexedProperties) {
|
||||
names = names.filter(i => {
|
||||
i = parseFloat(i);
|
||||
return Number.isInteger(i) && i >= 0 && i < length;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (aOptions.query) {
|
||||
let { query } = aOptions;
|
||||
query = query.toLowerCase();
|
||||
names = names.filter(name => {
|
||||
return name.toLowerCase().includes(query);
|
||||
});
|
||||
}
|
||||
|
||||
if (aOptions.sort) {
|
||||
names.sort();
|
||||
}
|
||||
|
||||
// Now build the descriptor list
|
||||
for (let name of names) {
|
||||
let desc = this.objectActor._propertyDescriptor(name);
|
||||
if (!desc) {
|
||||
desc = safeGetterValues[name];
|
||||
}
|
||||
else if (name in safeGetterValues) {
|
||||
// Merge the safe getter values into the existing properties list.
|
||||
let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
|
||||
desc.getterValue = getterValue;
|
||||
desc.getterPrototypeLevel = getterPrototypeLevel;
|
||||
}
|
||||
ownProperties[name] = desc;
|
||||
}
|
||||
|
||||
this.names = names;
|
||||
this.ownProperties = ownProperties;
|
||||
}
|
||||
|
||||
PropertyIteratorActor.prototype = {
|
||||
actorPrefix: "propertyIterator",
|
||||
|
||||
grip: function () {
|
||||
return {
|
||||
type: "propertyIterator",
|
||||
actor: this.actorID,
|
||||
count: this.names.length
|
||||
};
|
||||
},
|
||||
|
||||
names: function ({ indexes }) {
|
||||
let list = [];
|
||||
for (let idx of indexes) {
|
||||
list.push(this.names[idx]);
|
||||
}
|
||||
return {
|
||||
names: list
|
||||
};
|
||||
},
|
||||
|
||||
slice: function ({ start, count }) {
|
||||
let names = this.names.slice(start, start + count);
|
||||
let props = Object.create(null);
|
||||
for (let name of names) {
|
||||
props[name] = this.ownProperties[name];
|
||||
}
|
||||
return {
|
||||
ownProperties: props
|
||||
};
|
||||
},
|
||||
|
||||
all: function () {
|
||||
return {
|
||||
ownProperties: this.ownProperties
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
PropertyIteratorActor.prototype.requestTypes = {
|
||||
"names": PropertyIteratorActor.prototype.names,
|
||||
"slice": PropertyIteratorActor.prototype.slice,
|
||||
"all": PropertyIteratorActor.prototype.all,
|
||||
};
|
||||
|
||||
exports.PropertyIteratorActor = PropertyIteratorActor;
|
||||
|
||||
/**
|
||||
* Creates an actor for the specified object.
|
||||
*
|
||||
@ -3260,6 +3417,7 @@ function ObjectActor(aObj, aThreadActor)
|
||||
dbg_assert(!aObj.optimizedOut, "Should not create object actors for optimized out values!");
|
||||
this.obj = aObj;
|
||||
this.threadActor = aThreadActor;
|
||||
this.iterators = new Set();
|
||||
}
|
||||
|
||||
ObjectActor.prototype = {
|
||||
@ -3292,6 +3450,16 @@ ObjectActor.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// FF40+: Allow to know how many properties an object has
|
||||
// to lazily display them when there is a bunch.
|
||||
// Throws on some MouseEvent object in tests.
|
||||
try {
|
||||
// Bug 1163520: Assert on internal functions
|
||||
if (this.obj.class != "Function") {
|
||||
g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
let raw = this.obj.unsafeDereference();
|
||||
|
||||
// If Cu is not defined, we are running on a worker thread, where xrays
|
||||
@ -3328,6 +3496,8 @@ ObjectActor.prototype = {
|
||||
if (this.registeredPool.objectActors) {
|
||||
this.registeredPool.objectActors.delete(this.obj);
|
||||
}
|
||||
this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
|
||||
this.iterators.clear();
|
||||
this.registeredPool.removeActor(this);
|
||||
},
|
||||
|
||||
@ -3380,6 +3550,20 @@ ObjectActor.prototype = {
|
||||
ownPropertyNames: this.obj.getOwnPropertyNames() };
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an actor to iterate over an object property names and values.
|
||||
* See PropertyIteratorActor constructor for more info about options param.
|
||||
*
|
||||
* @param aRequest object
|
||||
* The protocol request object.
|
||||
*/
|
||||
onEnumProperties: function (aRequest) {
|
||||
let actor = new PropertyIteratorActor(this, aRequest.options);
|
||||
this.registeredPool.addActor(actor);
|
||||
this.iterators.add(actor);
|
||||
return { iterator: actor.grip() };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the prototype and own properties of
|
||||
* the object.
|
||||
@ -3406,15 +3590,15 @@ ObjectActor.prototype = {
|
||||
return { from: this.actorID,
|
||||
prototype: this.threadActor.createValueGrip(this.obj.proto),
|
||||
ownProperties: ownProperties,
|
||||
safeGetterValues: this._findSafeGetterValues(ownProperties) };
|
||||
safeGetterValues: this._findSafeGetterValues(names) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the safe getter values for the current Debugger.Object, |this.obj|.
|
||||
*
|
||||
* @private
|
||||
* @param object aOwnProperties
|
||||
* The object that holds the list of known ownProperties for
|
||||
* @param array aOwnProperties
|
||||
* The array that holds the list of known ownProperties names for
|
||||
* |this.obj|.
|
||||
* @param number [aLimit=0]
|
||||
* Optional limit of getter values to find.
|
||||
@ -3435,7 +3619,7 @@ ObjectActor.prototype = {
|
||||
// avoid providing safeGetterValues from prototypes if property |name|
|
||||
// is already defined as an own property.
|
||||
if (name in safeGetterValues ||
|
||||
(obj != this.obj && name in aOwnProperties)) {
|
||||
(obj != this.obj && aOwnProperties.indexOf(name) !== -1)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -3702,6 +3886,7 @@ ObjectActor.prototype.requestTypes = {
|
||||
"definitionSite": ObjectActor.prototype.onDefinitionSite,
|
||||
"parameterNames": ObjectActor.prototype.onParameterNames,
|
||||
"prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
|
||||
"enumProperties": ObjectActor.prototype.onEnumProperties,
|
||||
"prototype": ObjectActor.prototype.onPrototype,
|
||||
"property": ObjectActor.prototype.onProperty,
|
||||
"displayString": ObjectActor.prototype.onDisplayString,
|
||||
@ -4423,7 +4608,7 @@ DebuggerServer.ObjectActorPreviewers.Object = [
|
||||
|
||||
if (i < OBJECT_PREVIEW_MAX_ITEMS) {
|
||||
preview.safeGetterValues = aObjectActor.
|
||||
_findSafeGetterValues(preview.ownProperties,
|
||||
_findSafeGetterValues(Object.keys(preview.ownProperties),
|
||||
OBJECT_PREVIEW_MAX_ITEMS - i);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user