| Index: src/inspector/injected-script-source.js
|
| diff --git a/src/inspector/injected-script-source.js b/src/inspector/injected-script-source.js
|
| index b52277a8ebec017a6deb3d3a590a8d66ae47f2ab..cc85c0b7f03ff378dc613cab59abced3a3badc31 100644
|
| --- a/src/inspector/injected-script-source.js
|
| +++ b/src/inspector/injected-script-source.js
|
| @@ -335,12 +335,10 @@ InjectedScript.prototype = {
|
| object = object.object;
|
| }
|
|
|
| - var descriptors = [];
|
| - var iter = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly, undefined);
|
| // Go over properties, wrap object values.
|
| - for (var descriptor of iter) {
|
| - if (subtype === "internal#scopeList" && descriptor.name === "length")
|
| - continue;
|
| + var descriptors = this._propertyDescriptors(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly);
|
| + for (var i = 0; i < descriptors.length; ++i) {
|
| + var descriptor = descriptors[i];
|
| if ("get" in descriptor)
|
| descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
|
| if ("set" in descriptor)
|
| @@ -353,9 +351,18 @@ InjectedScript.prototype = {
|
| descriptor.enumerable = false;
|
| if ("symbol" in descriptor)
|
| descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName);
|
| - push(descriptors, descriptor);
|
| }
|
| return descriptors;
|
| +
|
| + /**
|
| + * @param {!Array<!Object>} descriptors
|
| + * @param {!Object} descriptor
|
| + * @return {boolean}
|
| + */
|
| + function addPropertyIfNeeded(descriptors, descriptor) {
|
| + push(descriptors, descriptor);
|
| + return true;
|
| + }
|
| },
|
|
|
| /**
|
| @@ -375,62 +382,72 @@ InjectedScript.prototype = {
|
|
|
| /**
|
| * @param {!Object} object
|
| + * @param {!function(!Array<!Object>, !Object)} addPropertyIfNeeded
|
| * @param {boolean=} ownProperties
|
| * @param {boolean=} accessorPropertiesOnly
|
| - * @param {?Array.<string>=} propertyNamesOnly
|
| + * @param {?Array<string>=} propertyNamesOnly
|
| + * @return {!Array<!Object>}
|
| */
|
| - _propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly)
|
| + _propertyDescriptors: function(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly, propertyNamesOnly)
|
| {
|
| + var descriptors = [];
|
| + descriptors.__proto__ = null;
|
| var propertyProcessed = { __proto__: null };
|
| + var subtype = InjectedScriptHost.subtype(object);
|
|
|
| /**
|
| - * @param {?Object} o
|
| - * @param {!Iterable<string|symbol|number>|!Array<string|number|symbol>} properties
|
| + * @param {!Object} o
|
| + * @param {!Array<string|number|symbol>=} properties
|
| + * @param {number=} objectLength
|
| + * @return {boolean}
|
| */
|
| - function* process(o, properties)
|
| + function process(o, properties, objectLength)
|
| {
|
| - for (var property of properties) {
|
| + // When properties is not provided, iterate over the object's indices.
|
| + var length = properties ? properties.length : objectLength;
|
| + for (var i = 0; i < length; ++i) {
|
| + var property = properties ? properties[i] : ("" + i);
|
| + if (propertyProcessed[property])
|
| + continue;
|
| + propertyProcessed[property] = true;
|
| var name;
|
| if (isSymbol(property))
|
| name = /** @type {string} */ (injectedScript._describe(property));
|
| else
|
| name = typeof property === "number" ? ("" + property) : /** @type {string} */(property);
|
|
|
| - if (propertyProcessed[property])
|
| + if (subtype === "internal#scopeList" && name === "length")
|
| continue;
|
|
|
| + var descriptor;
|
| try {
|
| - propertyProcessed[property] = true;
|
| - var descriptor = nullifyObjectProto(Object.getOwnPropertyDescriptor(o, property));
|
| - if (descriptor) {
|
| - if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor))
|
| - continue;
|
| - if ("get" in descriptor && "set" in descriptor && name != "__proto__" && InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) {
|
| - descriptor.value = object[property];
|
| - descriptor.isOwn = true;
|
| - delete descriptor.get;
|
| - delete descriptor.set;
|
| - }
|
| - } else {
|
| - // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
|
| - if (accessorPropertiesOnly)
|
| - continue;
|
| - try {
|
| - descriptor = { name: name, value: o[property], writable: false, configurable: false, enumerable: false, __proto__: null };
|
| - if (o === object)
|
| - descriptor.isOwn = true;
|
| - yield descriptor;
|
| - } catch (e) {
|
| - // Silent catch.
|
| - }
|
| + descriptor = Object.getOwnPropertyDescriptor(o, property);
|
| + var isAccessorProperty = descriptor && ("get" in descriptor || "set" in descriptor);
|
| + if (accessorPropertiesOnly && !isAccessorProperty)
|
| continue;
|
| + if (descriptor && "get" in descriptor && "set" in descriptor && name !== "__proto__" &&
|
| + InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) &&
|
| + !doesAttributeHaveObservableSideEffectOnGet(object, name)) {
|
| + descriptor.value = object[property];
|
| + descriptor.isOwn = true;
|
| + delete descriptor.get;
|
| + delete descriptor.set;
|
| }
|
| } catch (e) {
|
| if (accessorPropertiesOnly)
|
| continue;
|
| - var descriptor = { __proto__: null };
|
| - descriptor.value = e;
|
| - descriptor.wasThrown = true;
|
| + descriptor = { value: e, wasThrown: true };
|
| + }
|
| +
|
| + // Not all bindings provide proper descriptors. Fall back to the non-configurable, non-enumerable,
|
| + // non-writable property.
|
| + if (!descriptor) {
|
| + try {
|
| + descriptor = { value: o[property], writable: false };
|
| + } catch (e) {
|
| + // Silent catch.
|
| + continue;
|
| + }
|
| }
|
|
|
| descriptor.name = name;
|
| @@ -438,66 +455,72 @@ InjectedScript.prototype = {
|
| descriptor.isOwn = true;
|
| if (isSymbol(property))
|
| descriptor.symbol = property;
|
| - yield descriptor;
|
| + descriptor = nullifyObjectProto(descriptor);
|
| + if (!addPropertyIfNeeded(descriptors, descriptor))
|
| + return false;
|
| }
|
| + return true;
|
| }
|
|
|
| if (propertyNamesOnly) {
|
| for (var i = 0; i < propertyNamesOnly.length; ++i) {
|
| var name = propertyNamesOnly[i];
|
| - for (var o = object; this._isDefined(o); o = this._objectPrototype(o)) {
|
| + for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) {
|
| + o = /** @type {!Object} */ (o);
|
| if (InjectedScriptHost.objectHasOwnProperty(o, name)) {
|
| - for (var descriptor of process(o, [name]))
|
| - yield descriptor;
|
| + if (!process(o, [name]))
|
| + return descriptors;
|
| break;
|
| }
|
| if (ownProperties)
|
| break;
|
| }
|
| }
|
| - return;
|
| - }
|
| -
|
| - /**
|
| - * @param {number} length
|
| - */
|
| - function* arrayIndexNames(length)
|
| - {
|
| - for (var i = 0; i < length; ++i)
|
| - yield "" + i;
|
| + return descriptors;
|
| }
|
|
|
| var skipGetOwnPropertyNames;
|
| try {
|
| - skipGetOwnPropertyNames = InjectedScriptHost.subtype(object) === "typedarray" && object.length > 500000;
|
| + skipGetOwnPropertyNames = subtype === "typedarray" && object.length > 500000;
|
| } catch (e) {
|
| }
|
|
|
| - for (var o = object; this._isDefined(o); o = this._objectPrototype(o)) {
|
| + for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) {
|
| + o = /** @type {!Object} */ (o);
|
| if (InjectedScriptHost.subtype(o) === "proxy")
|
| continue;
|
| - if (skipGetOwnPropertyNames && o === object) {
|
| - // Avoid OOM crashes from getting all own property names of a large TypedArray.
|
| - for (var descriptor of process(o, arrayIndexNames(o.length)))
|
| - yield descriptor;
|
| - } else {
|
| - // First call Object.keys() to enforce ordering of the property descriptors.
|
| - for (var descriptor of process(o, Object.keys(/** @type {!Object} */ (o))))
|
| - yield descriptor;
|
| - for (var descriptor of process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o))))
|
| - yield descriptor;
|
| - }
|
| - if (Object.getOwnPropertySymbols) {
|
| - for (var descriptor of process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (o))))
|
| - yield descriptor;
|
| +
|
| + try {
|
| + if (skipGetOwnPropertyNames && o === object) {
|
| + if (!process(o, undefined, o.length))
|
| + return descriptors;
|
| + } else {
|
| + // First call Object.keys() to enforce ordering of the property descriptors.
|
| + if (!process(o, Object.keys(o)))
|
| + return descriptors;
|
| + if (!process(o, Object.getOwnPropertyNames(o)))
|
| + return descriptors;
|
| + }
|
| + if (Object.getOwnPropertySymbols) {
|
| + if (!process(o, Object.getOwnPropertySymbols(o)))
|
| + return descriptors;
|
| + }
|
| +
|
| + if (ownProperties) {
|
| + var proto = this._objectPrototype(o);
|
| + if (proto && !accessorPropertiesOnly) {
|
| + var descriptor = { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null };
|
| + if (!addPropertyIfNeeded(descriptors, descriptor))
|
| + return descriptors;
|
| + }
|
| + }
|
| + } catch (e) {
|
| }
|
| - if (ownProperties) {
|
| - var proto = this._objectPrototype(o);
|
| - if (proto && !accessorPropertiesOnly)
|
| - yield { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null };
|
| +
|
| + if (ownProperties)
|
| break;
|
| - }
|
| }
|
| + return descriptors;
|
| },
|
|
|
| /**
|
| @@ -863,19 +886,15 @@ InjectedScript.RemoteObject.prototype = {
|
| {
|
| var preview = this._createEmptyPreview();
|
| var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
|
| -
|
| var propertiesThreshold = {
|
| properties: isTable ? 1000 : max(5, firstLevelKeysCount),
|
| indexes: isTable ? 1000 : max(100, firstLevelKeysCount),
|
| __proto__: null
|
| };
|
| + var subtype = this.subtype;
|
|
|
| try {
|
| - var descriptors = injectedScript._propertyDescriptors(object, undefined, undefined, firstLevelKeys);
|
| -
|
| - this._appendPropertyDescriptors(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable);
|
| - if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
|
| - return preview;
|
| + var descriptors = injectedScript._propertyDescriptors(object, addPropertyIfNeeded, false /* ownProperties */, undefined /* accessorPropertiesOnly */, firstLevelKeys);
|
|
|
| // Add internal properties to preview.
|
| var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || [];
|
| @@ -886,64 +905,80 @@ InjectedScript.RemoteObject.prototype = {
|
| entries = /** @type {!Array<*>} */(rawInternalProperties[i + 1]);
|
| continue;
|
| }
|
| - push(internalProperties, {
|
| + var internalPropertyDescriptor = {
|
| name: rawInternalProperties[i],
|
| value: rawInternalProperties[i + 1],
|
| isOwn: true,
|
| enumerable: true,
|
| __proto__: null
|
| - });
|
| + };
|
| + if (!addPropertyIfNeeded(descriptors, internalPropertyDescriptor))
|
| + break;
|
| }
|
| - this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable);
|
| + this._appendPropertyPreviewDescriptors(preview, descriptors, secondLevelKeys, isTable);
|
|
|
| - if (this.subtype === "map" || this.subtype === "set" || this.subtype === "iterator")
|
| + if (subtype === "map" || subtype === "set" || subtype === "iterator")
|
| this._appendEntriesPreview(entries, preview, skipEntriesPreview);
|
|
|
| } catch (e) {}
|
|
|
| return preview;
|
| - },
|
| -
|
| - /**
|
| - * @param {!RuntimeAgent.ObjectPreview} preview
|
| - * @param {!Array.<*>|!Iterable.<*>} descriptors
|
| - * @param {!Object} propertiesThreshold
|
| - * @param {?Array.<string>=} secondLevelKeys
|
| - * @param {boolean=} isTable
|
| - */
|
| - _appendPropertyDescriptors: function(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable)
|
| - {
|
| - for (var descriptor of descriptors) {
|
| - if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
|
| - break;
|
| - if (!descriptor || descriptor.wasThrown)
|
| - continue;
|
|
|
| - var name = descriptor.name;
|
| + /**
|
| + * @param {!Array<!Object>} descriptors
|
| + * @param {!Object} descriptor
|
| + * @return {boolean}
|
| + */
|
| + function addPropertyIfNeeded(descriptors, descriptor) {
|
| + if (descriptor.wasThrown)
|
| + return true;
|
|
|
| // Ignore __proto__ property.
|
| - if (name === "__proto__")
|
| - continue;
|
| + if (descriptor.name === "__proto__")
|
| + return true;
|
|
|
| // Ignore length property of array.
|
| - if ((this.subtype === "array" || this.subtype === "typedarray") && name === "length")
|
| - continue;
|
| + if ((subtype === "array" || subtype === "typedarray") && descriptor.name === "length")
|
| + return true;
|
|
|
| // Ignore size property of map, set.
|
| - if ((this.subtype === "map" || this.subtype === "set") && name === "size")
|
| - continue;
|
| + if ((subtype === "map" || subtype === "set") && descriptor.name === "size")
|
| + return true;
|
|
|
| // Never preview prototype properties.
|
| if (!descriptor.isOwn)
|
| - continue;
|
| + return true;
|
|
|
| // Ignore computed properties unless they have getters.
|
| - if (!("value" in descriptor)) {
|
| - if (descriptor.get)
|
| - this._appendPropertyPreview(preview, { name: name, type: "accessor", __proto__: null }, propertiesThreshold);
|
| - continue;
|
| + if (!("value" in descriptor) && !descriptor.get)
|
| + return true;
|
| +
|
| + if (toString(descriptor.name >>> 0) === descriptor.name)
|
| + propertiesThreshold.indexes--;
|
| + else
|
| + propertiesThreshold.properties--;
|
| +
|
| + var canContinue = propertiesThreshold.indexes >= 0 && propertiesThreshold.properties >= 0;
|
| + if (!canContinue) {
|
| + preview.overflow = true;
|
| + return false;
|
| }
|
| + push(descriptors, descriptor);
|
| + return true;
|
| + }
|
| + },
|
|
|
| + /**
|
| + * @param {!RuntimeAgent.ObjectPreview} preview
|
| + * @param {!Array.<*>|!Iterable.<*>} descriptors
|
| + * @param {?Array.<string>=} secondLevelKeys
|
| + * @param {boolean=} isTable
|
| + */
|
| + _appendPropertyPreviewDescriptors: function(preview, descriptors, secondLevelKeys, isTable)
|
| + {
|
| + for (var i = 0; i < descriptors.length; ++i) {
|
| + var descriptor = descriptors[i];
|
| + var name = descriptor.name;
|
| var value = descriptor.value;
|
| var type = typeof value;
|
|
|
| @@ -951,9 +986,15 @@ InjectedScript.RemoteObject.prototype = {
|
| if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
|
| type = "object";
|
|
|
| + // Ignore computed properties unless they have getters.
|
| + if (descriptor.get && !("value" in descriptor)) {
|
| + push(preview.properties, { name: name, type: "accessor", __proto__: null });
|
| + continue;
|
| + }
|
| +
|
| // Render own properties.
|
| if (value === null) {
|
| - this._appendPropertyPreview(preview, { name: name, type: "object", subtype: "null", value: "null", __proto__: null }, propertiesThreshold);
|
| + push(preview.properties, { name: name, type: "object", subtype: "null", value: "null", __proto__: null });
|
| continue;
|
| }
|
|
|
| @@ -961,7 +1002,7 @@ InjectedScript.RemoteObject.prototype = {
|
| if (InjectedScript.primitiveTypes[type]) {
|
| if (type === "string" && value.length > maxLength)
|
| value = this._abbreviateString(value, maxLength, true);
|
| - this._appendPropertyPreview(preview, { name: name, type: type, value: toStringDescription(value), __proto__: null }, propertiesThreshold);
|
| + push(preview.properties, { name: name, type: type, value: toStringDescription(value), __proto__: null });
|
| continue;
|
| }
|
|
|
| @@ -981,24 +1022,6 @@ InjectedScript.RemoteObject.prototype = {
|
| description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
|
| property.value = description;
|
| }
|
| - this._appendPropertyPreview(preview, property, propertiesThreshold);
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * @param {!RuntimeAgent.ObjectPreview} preview
|
| - * @param {!Object} property
|
| - * @param {!Object} propertiesThreshold
|
| - */
|
| - _appendPropertyPreview: function(preview, property, propertiesThreshold)
|
| - {
|
| - if (toString(property.name >>> 0) === property.name)
|
| - propertiesThreshold.indexes--;
|
| - else
|
| - propertiesThreshold.properties--;
|
| - if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
|
| - preview.overflow = true;
|
| - } else {
|
| push(preview.properties, property);
|
| }
|
| },
|
|
|