| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| /** |
| * @typedef {{object: ?SDK.RemoteObject, wasThrown: (boolean|undefined)}} |
| */ |
| SDK.CallFunctionResult; |
| |
| SDK.RemoteObject = class { |
| /** |
| * This may not be an interface due to "instanceof SDK.RemoteObject" checks in the code. |
| */ |
| |
| /** |
| * @param {*} value |
| * @return {!SDK.RemoteObject} |
| */ |
| static fromLocalObject(value) { |
| return new SDK.LocalJSONObject(value); |
| } |
| |
| /** |
| * @param {!SDK.RemoteObject} remoteObject |
| * @return {string} |
| */ |
| static type(remoteObject) { |
| if (remoteObject === null) |
| return 'null'; |
| |
| const type = typeof remoteObject; |
| if (type !== 'object' && type !== 'function') |
| return type; |
| |
| return remoteObject.type; |
| } |
| |
| /** |
| * @param {string} description |
| * @return {string} |
| */ |
| static arrayNameFromDescription(description) { |
| return description.replace(SDK.RemoteObject._descriptionLengthParenRegex, '') |
| .replace(SDK.RemoteObject._descriptionLengthSquareRegex, ''); |
| } |
| |
| /** |
| * @param {!SDK.RemoteObject|!Protocol.Runtime.RemoteObject|!Protocol.Runtime.ObjectPreview} object |
| * @return {number} |
| */ |
| static arrayLength(object) { |
| if (object.subtype !== 'array' && object.subtype !== 'typedarray') |
| return 0; |
| // Array lengths in V8-generated descriptions switched from square brackets to parentheses. |
| // Both formats are checked in case the front end is dealing with an old version of V8. |
| const parenMatches = object.description.match(SDK.RemoteObject._descriptionLengthParenRegex); |
| const squareMatches = object.description.match(SDK.RemoteObject._descriptionLengthSquareRegex); |
| return parenMatches ? parseInt(parenMatches[1], 10) : (squareMatches ? parseInt(squareMatches[1], 10) : 0); |
| } |
| |
| /** |
| * @param {*} object |
| * @return {?string} |
| */ |
| static unserializableDescription(object) { |
| const type = typeof object; |
| if (type === 'number') { |
| const description = String(object); |
| if (object === 0 && 1 / object < 0) |
| return SDK.RemoteObject.UnserializableNumber.Negative0; |
| if (description === SDK.RemoteObject.UnserializableNumber.NaN || |
| description === SDK.RemoteObject.UnserializableNumber.Infinity || |
| description === SDK.RemoteObject.UnserializableNumber.NegativeInfinity) |
| return description; |
| } |
| if (type === 'bigint') |
| return object + 'n'; |
| return null; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.RemoteObject|!SDK.RemoteObject|number|string|boolean|undefined|null|bigint} object |
| * @return {!Protocol.Runtime.CallArgument} |
| */ |
| static toCallArgument(object) { |
| const type = typeof object; |
| if (type === 'undefined') |
| return {}; |
| const unserializableDescription = SDK.RemoteObject.unserializableDescription(object); |
| if (type === 'number') { |
| if (unserializableDescription !== null) |
| return {unserializableValue: unserializableDescription}; |
| return {value: object}; |
| } |
| if (type === 'bigint') |
| return {unserializableValue: /** @type {!Protocol.Runtime.UnserializableValue} */ (unserializableDescription)}; |
| if (type === 'string' || type === 'boolean') |
| return {value: object}; |
| |
| if (!object) |
| return {value: null}; |
| |
| // The unserializableValue is a function on SDK.RemoteObject's and a simple property on |
| // Protocol.Runtime.RemoteObject's. |
| if (object instanceof SDK.RemoteObject) { |
| const unserializableValue = object.unserializableValue(); |
| if (unserializableValue !== undefined) |
| return {unserializableValue: unserializableValue}; |
| } else if (object.unserializableValue !== undefined) { |
| return {unserializableValue: object.unserializableValue}; |
| } |
| |
| if (typeof object.objectId !== 'undefined') |
| return {objectId: object.objectId}; |
| |
| return {value: object.value}; |
| } |
| |
| /** |
| * @param {!SDK.RemoteObject} object |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| static loadFromObjectPerProto(object, generatePreview, callback) { |
| // Combines 2 asynch calls. Doesn't rely on call-back orders (some calls may be loop-back). |
| let savedOwnProperties; |
| let savedAccessorProperties; |
| let savedInternalProperties; |
| let resultCounter = 2; |
| |
| function processCallback() { |
| if (--resultCounter) |
| return; |
| if (savedOwnProperties && savedAccessorProperties) { |
| const propertiesMap = new Map(); |
| const propertySymbols = []; |
| for (let i = 0; i < savedAccessorProperties.length; i++) { |
| const property = savedAccessorProperties[i]; |
| if (property.symbol) |
| propertySymbols.push(property); |
| else |
| propertiesMap.set(property.name, property); |
| } |
| for (let i = 0; i < savedOwnProperties.length; i++) { |
| const property = savedOwnProperties[i]; |
| if (property.isAccessorProperty()) |
| continue; |
| if (property.symbol) |
| propertySymbols.push(property); |
| else |
| propertiesMap.set(property.name, property); |
| } |
| return callback( |
| propertiesMap.valuesArray().concat(propertySymbols), |
| savedInternalProperties ? savedInternalProperties : null); |
| } else { |
| callback(null, null); |
| } |
| } |
| |
| /** |
| * @param {?Array.<!SDK.RemoteObjectProperty>} properties |
| * @param {?Array.<!SDK.RemoteObjectProperty>} internalProperties |
| */ |
| function allAccessorPropertiesCallback(properties, internalProperties) { |
| savedAccessorProperties = properties; |
| processCallback(); |
| } |
| |
| /** |
| * @param {?Array.<!SDK.RemoteObjectProperty>} properties |
| * @param {?Array.<!SDK.RemoteObjectProperty>} internalProperties |
| */ |
| function ownPropertiesCallback(properties, internalProperties) { |
| savedOwnProperties = properties; |
| savedInternalProperties = internalProperties; |
| processCallback(); |
| } |
| |
| object.getAllProperties(true /* accessorPropertiesOnly */, generatePreview, allAccessorPropertiesCallback); |
| object.getOwnProperties(generatePreview, ownPropertiesCallback); |
| } |
| |
| /** |
| * @return {?Protocol.Runtime.CustomPreview} |
| */ |
| customPreview() { |
| return null; |
| } |
| |
| /** @return {!Protocol.Runtime.RemoteObjectId|undefined} */ |
| get objectId() { |
| return 'Not implemented'; |
| } |
| |
| /** @return {string} */ |
| get type() { |
| throw 'Not implemented'; |
| } |
| |
| /** @return {string|undefined} */ |
| get subtype() { |
| throw 'Not implemented'; |
| } |
| |
| /** @return {*} */ |
| get value() { |
| throw 'Not implemented'; |
| } |
| |
| /** @return {string|undefined} */ |
| unserializableValue() { |
| throw 'Not implemented'; |
| } |
| |
| /** @return {string|undefined} */ |
| get description() { |
| throw 'Not implemented'; |
| } |
| |
| /** @return {boolean} */ |
| get hasChildren() { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @return {!Protocol.Runtime.ObjectPreview|undefined} |
| */ |
| get preview() { |
| return undefined; |
| } |
| |
| /** |
| * @return {?string} |
| */ |
| get className() { |
| return null; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| arrayLength() { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getOwnProperties(generatePreview, callback) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {boolean} generatePreview |
| * @return {!Promise<!{properties: ?Array.<!SDK.RemoteObjectProperty>, internalProperties: ?Array.<!SDK.RemoteObjectProperty>}>} |
| */ |
| getOwnPropertiesPromise(generatePreview) { |
| return new Promise(promiseConstructor.bind(this)); |
| |
| /** |
| * @param {function(!{properties: ?Array.<!SDK.RemoteObjectProperty>, internalProperties: ?Array.<!SDK.RemoteObjectProperty>})} success |
| * @this {SDK.RemoteObject} |
| */ |
| function promiseConstructor(success) { |
| this.getOwnProperties(!!generatePreview, getOwnPropertiesCallback.bind(null, success)); |
| } |
| |
| /** |
| * @param {function(!{properties: ?Array.<!SDK.RemoteObjectProperty>, internalProperties: ?Array.<!SDK.RemoteObjectProperty>})} callback |
| * @param {?Array.<!SDK.RemoteObjectProperty>} properties |
| * @param {?Array.<!SDK.RemoteObjectProperty>} internalProperties |
| */ |
| function getOwnPropertiesCallback(callback, properties, internalProperties) { |
| callback({properties: properties, internalProperties: internalProperties}); |
| } |
| } |
| |
| /** |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @param {function(?Array<!SDK.RemoteObjectProperty>, ?Array<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getAllProperties(accessorPropertiesOnly, generatePreview, callback) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @return {!Promise<!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties: ?Array<!SDK.RemoteObjectProperty>}>} |
| */ |
| getAllPropertiesPromise(accessorPropertiesOnly, generatePreview) { |
| return new Promise(promiseConstructor.bind(this)); |
| |
| /** |
| * @param {function(!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties: ?Array.<!SDK.RemoteObjectProperty>})} success |
| * @this {SDK.RemoteObject} |
| */ |
| function promiseConstructor(success) { |
| this.getAllProperties(accessorPropertiesOnly, generatePreview, getAllPropertiesCallback.bind(null, success)); |
| } |
| |
| /** |
| * @param {function(!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties: ?Array<!SDK.RemoteObjectProperty>})} callback |
| * @param {?Array<!SDK.RemoteObjectProperty>} properties |
| * @param {?Array<!SDK.RemoteObjectProperty>} internalProperties |
| */ |
| function getAllPropertiesCallback(callback, properties, internalProperties) { |
| callback({properties: properties, internalProperties: internalProperties}); |
| } |
| } |
| |
| /** |
| * @param {!Array.<string>} propertyPath |
| * @param {function(?SDK.RemoteObject, boolean=)} callback |
| */ |
| getProperty(propertyPath, callback) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.CallArgument} name |
| * @return {!Promise<string|undefined>} |
| */ |
| async deleteProperty(name) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {string|!Protocol.Runtime.CallArgument} name |
| * @param {string} value |
| * @return {!Promise<string|undefined>} |
| */ |
| async setPropertyValue(name, value) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @param {function(?SDK.RemoteObject, boolean=)=} callback |
| */ |
| callFunction(functionDeclaration, args, callback) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @return {!Promise<!SDK.CallFunctionResult>} |
| */ |
| callFunctionPromise(functionDeclaration, args) { |
| return new Promise(promiseConstructor.bind(this)); |
| |
| /** |
| * @param {function(!SDK.CallFunctionResult)} success |
| * @this {SDK.RemoteObject} |
| */ |
| function promiseConstructor(success) { |
| this.callFunction(functionDeclaration, args, callFunctionCallback.bind(null, success)); |
| } |
| |
| /** |
| * @param {function(!SDK.CallFunctionResult)} callback |
| * @param {?SDK.RemoteObject} object |
| * @param {boolean=} wasThrown |
| */ |
| function callFunctionCallback(callback, object, wasThrown) { |
| callback({object: object, wasThrown: wasThrown}); |
| } |
| } |
| |
| /** |
| * @template T |
| * @param {function(this:Object, ...):T} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args |
| * @param {function(T)} callback |
| */ |
| callFunctionJSON(functionDeclaration, args, callback) { |
| throw 'Not implemented'; |
| } |
| |
| /** |
| * @param {function(this:Object, ...):T} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args |
| * @return {!Promise<T>} |
| * @template T |
| */ |
| callFunctionJSONPromise(functionDeclaration, args) { |
| return new Promise(success => this.callFunctionJSON(functionDeclaration, args, success)); |
| } |
| |
| release() { |
| } |
| |
| /** |
| * @return {!SDK.DebuggerModel} |
| */ |
| debuggerModel() { |
| throw new Error('DebuggerModel-less object'); |
| } |
| |
| /** |
| * @return {!SDK.RuntimeModel} |
| */ |
| runtimeModel() { |
| throw new Error('RuntimeModel-less object'); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isNode() { |
| return false; |
| } |
| }; |
| |
| |
| SDK.RemoteObjectImpl = class extends SDK.RemoteObject { |
| /** |
| * @param {!SDK.RuntimeModel} runtimeModel |
| * @param {string|undefined} objectId |
| * @param {string} type |
| * @param {string|undefined} subtype |
| * @param {*} value |
| * @param {!Protocol.Runtime.UnserializableValue=} unserializableValue |
| * @param {string=} description |
| * @param {!Protocol.Runtime.ObjectPreview=} preview |
| * @param {!Protocol.Runtime.CustomPreview=} customPreview |
| * @param {string=} className |
| */ |
| constructor( |
| runtimeModel, objectId, type, subtype, value, unserializableValue, description, preview, customPreview, |
| className) { |
| super(); |
| |
| this._runtimeModel = runtimeModel; |
| this._runtimeAgent = runtimeModel.target().runtimeAgent(); |
| |
| this._type = type; |
| this._subtype = subtype; |
| if (objectId) { |
| // handle |
| this._objectId = objectId; |
| this._description = description; |
| this._hasChildren = (type !== 'symbol'); |
| this._preview = preview; |
| } else { |
| this._description = description; |
| if (!this.description && unserializableValue) |
| this._description = unserializableValue; |
| if (!this._description && (typeof value !== 'object' || value === null)) |
| this._description = value + ''; |
| this._hasChildren = false; |
| if (typeof unserializableValue === 'string') { |
| this._unserializableValue = unserializableValue; |
| if (unserializableValue === SDK.RemoteObject.UnserializableNumber.Infinity || |
| unserializableValue === SDK.RemoteObject.UnserializableNumber.NegativeInfinity || |
| unserializableValue === SDK.RemoteObject.UnserializableNumber.Negative0 || |
| unserializableValue === SDK.RemoteObject.UnserializableNumber.NaN) |
| this._value = Number(unserializableValue); |
| else if (type === 'bigint' && unserializableValue.endsWith('n')) |
| this._value = BigInt(unserializableValue.substring(0, unserializableValue.length - 1)); |
| else |
| this._value = unserializableValue; |
| |
| } else { |
| this._value = value; |
| } |
| } |
| this._customPreview = customPreview || null; |
| this._className = typeof className === 'string' ? className : null; |
| } |
| |
| /** |
| * @override |
| * @return {?Protocol.Runtime.CustomPreview} |
| */ |
| customPreview() { |
| return this._customPreview; |
| } |
| |
| /** |
| * @override |
| * @return {!Protocol.Runtime.RemoteObjectId|undefined} |
| */ |
| get objectId() { |
| return this._objectId; |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| get type() { |
| return this._type; |
| } |
| |
| /** |
| * @override |
| * @return {string|undefined} |
| */ |
| get subtype() { |
| return this._subtype; |
| } |
| |
| /** |
| * @override |
| * @return {*} |
| */ |
| get value() { |
| return this._value; |
| } |
| |
| /** |
| * @override |
| * @return {string|undefined} |
| */ |
| unserializableValue() { |
| return this._unserializableValue; |
| } |
| |
| /** |
| * @override |
| * @return {string|undefined} |
| */ |
| get description() { |
| return this._description; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| get hasChildren() { |
| return this._hasChildren; |
| } |
| |
| /** |
| * @override |
| * @return {!Protocol.Runtime.ObjectPreview|undefined} |
| */ |
| get preview() { |
| return this._preview; |
| } |
| |
| /** |
| * @override |
| * @return {?string} |
| */ |
| get className() { |
| return this._className; |
| } |
| |
| /** |
| * @override |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getOwnProperties(generatePreview, callback) { |
| this.doGetProperties(true, false, generatePreview, callback); |
| } |
| |
| /** |
| * @override |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getAllProperties(accessorPropertiesOnly, generatePreview, callback) { |
| this.doGetProperties(false, accessorPropertiesOnly, generatePreview, callback); |
| } |
| |
| /** |
| * @override |
| * @param {!Array.<string>} propertyPath |
| * @param {function(?SDK.RemoteObject, boolean=)} callback |
| */ |
| getProperty(propertyPath, callback) { |
| /** |
| * @param {string} arrayStr |
| * @suppressReceiverCheck |
| * @this {Object} |
| */ |
| function remoteFunction(arrayStr) { |
| let result = this; |
| const properties = JSON.parse(arrayStr); |
| for (let i = 0, n = properties.length; i < n; ++i) |
| result = result[properties[i]]; |
| return result; |
| } |
| |
| const args = [{value: JSON.stringify(propertyPath)}]; |
| this.callFunction(remoteFunction, args, callback); |
| } |
| |
| /** |
| * @param {boolean} ownProperties |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @param {function(?Array<!SDK.RemoteObjectProperty>, ?Array<!SDK.RemoteObjectProperty>)} callback |
| */ |
| doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview, callback) { |
| if (!this._objectId) { |
| callback(null, null); |
| return; |
| } |
| |
| this._runtimeAgent |
| .invoke_getProperties({objectId: this._objectId, ownProperties, accessorPropertiesOnly, generatePreview}) |
| .then(remoteObjectBinder.bind(this)); |
| |
| /** |
| * @param {!Protocol.RuntimeAgent.GetPropertiesResponse} response |
| * @this {SDK.RemoteObjectImpl} |
| */ |
| function remoteObjectBinder(response) { |
| if (response[Protocol.Error]) { |
| callback(null, null); |
| return; |
| } |
| if (response.exceptionDetails) { |
| this._runtimeModel.exceptionThrown(Date.now(), response.exceptionDetails); |
| callback(null, null); |
| return; |
| } |
| const properties = response.result; |
| const internalProperties = response.internalProperties; |
| const result = []; |
| for (let i = 0; properties && i < properties.length; ++i) { |
| const property = properties[i]; |
| const propertyValue = property.value ? this._runtimeModel.createRemoteObject(property.value) : null; |
| const propertySymbol = property.symbol ? this._runtimeModel.createRemoteObject(property.symbol) : null; |
| const remoteProperty = new SDK.RemoteObjectProperty( |
| property.name, propertyValue, !!property.enumerable, !!property.writable, !!property.isOwn, |
| !!property.wasThrown, propertySymbol); |
| |
| if (typeof property.value === 'undefined') { |
| if (property.get && property.get.type !== 'undefined') |
| remoteProperty.getter = this._runtimeModel.createRemoteObject(property.get); |
| if (property.set && property.set.type !== 'undefined') |
| remoteProperty.setter = this._runtimeModel.createRemoteObject(property.set); |
| } |
| |
| result.push(remoteProperty); |
| } |
| let internalPropertiesResult = null; |
| if (internalProperties) { |
| internalPropertiesResult = []; |
| for (let i = 0; i < internalProperties.length; i++) { |
| const property = internalProperties[i]; |
| if (!property.value) |
| continue; |
| const propertyValue = this._runtimeModel.createRemoteObject(property.value); |
| internalPropertiesResult.push(new SDK.RemoteObjectProperty( |
| property.name, propertyValue, true, false, undefined, undefined, undefined, true)); |
| } |
| } |
| callback(result, internalPropertiesResult); |
| } |
| } |
| |
| /** |
| * @override |
| * @param {string|!Protocol.Runtime.CallArgument} name |
| * @param {string} value |
| * @return {!Promise<string|undefined>} |
| */ |
| async setPropertyValue(name, value) { |
| if (!this._objectId) |
| return `Can't set a property of non-object.`; |
| |
| const response = await this._runtimeAgent.invoke_evaluate({expression: value, silent: true}); |
| if (response[Protocol.Error] || response.exceptionDetails) { |
| return response[Protocol.Error] || |
| (response.result.type !== 'string' ? response.result.description : |
| /** @type {string} */ (response.result.value)); |
| } |
| |
| if (typeof name === 'string') |
| name = SDK.RemoteObject.toCallArgument(name); |
| |
| const resultPromise = this.doSetObjectPropertyValue(response.result, name); |
| |
| if (response.result.objectId) |
| this._runtimeAgent.releaseObject(response.result.objectId); |
| |
| return resultPromise; |
| } |
| |
| /** |
| * @param {!Protocol.Runtime.RemoteObject} result |
| * @param {!Protocol.Runtime.CallArgument} name |
| * @return {!Promise<string|undefined>} |
| */ |
| async doSetObjectPropertyValue(result, name) { |
| // This assignment may be for a regular (data) property, and for an accessor property (with getter/setter). |
| // Note the sensitive matter about accessor property: the property may be physically defined in some proto object, |
| // but logically it is bound to the object in question. JavaScript passes this object to getters/setters, not the object |
| // where property was defined; so do we. |
| const setPropertyValueFunction = 'function(a, b) { this[a] = b; }'; |
| |
| const argv = [name, SDK.RemoteObject.toCallArgument(result)]; |
| const response = await this._runtimeAgent.invoke_callFunctionOn( |
| {objectId: this._objectId, functionDeclaration: setPropertyValueFunction, arguments: argv, silent: true}); |
| const error = response[Protocol.Error]; |
| return error || response.exceptionDetails ? error || response.result.description : undefined; |
| } |
| |
| /** |
| * @override |
| * @param {!Protocol.Runtime.CallArgument} name |
| * @return {!Promise<string|undefined>} |
| */ |
| async deleteProperty(name) { |
| if (!this._objectId) |
| return `Can't delete a property of non-object.`; |
| |
| const deletePropertyFunction = 'function(a) { delete this[a]; return !(a in this); }'; |
| const response = await this._runtimeAgent.invoke_callFunctionOn( |
| {objectId: this._objectId, functionDeclaration: deletePropertyFunction, arguments: [name], silent: true}); |
| |
| if (response[Protocol.Error] || response.exceptionDetails) |
| return response[Protocol.Error] || response.result.description; |
| |
| if (!response.result.value) |
| return 'Failed to delete property.'; |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array.<!Protocol.Runtime.CallArgument>=} args |
| * @param {function(?SDK.RemoteObject, boolean=)=} callback |
| */ |
| callFunction(functionDeclaration, args, callback) { |
| this._runtimeAgent |
| .invoke_callFunctionOn({ |
| objectId: this._objectId, |
| functionDeclaration: functionDeclaration.toString(), |
| arguments: args, |
| silent: true |
| }) |
| .then(response => { |
| if (!callback) |
| return; |
| if (response[Protocol.Error]) |
| callback(null, false); |
| else |
| callback(this._runtimeModel.createRemoteObject(response.result), !!response.exceptionDetails); |
| }); |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object)} functionDeclaration |
| * @param {!Array.<!Protocol.Runtime.CallArgument>|undefined} args |
| * @param {function(*)} callback |
| */ |
| callFunctionJSON(functionDeclaration, args, callback) { |
| this._runtimeAgent |
| .invoke_callFunctionOn({ |
| objectId: this._objectId, |
| functionDeclaration: functionDeclaration.toString(), |
| arguments: args, |
| silent: true, |
| returnByValue: true |
| }) |
| .then( |
| response => callback(response[Protocol.Error] || response.exceptionDetails ? null : response.result.value)); |
| } |
| |
| /** |
| * @override |
| */ |
| release() { |
| if (!this._objectId) |
| return; |
| this._runtimeAgent.releaseObject(this._objectId); |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| arrayLength() { |
| return SDK.RemoteObject.arrayLength(this); |
| } |
| |
| /** |
| * @override |
| * @return {!SDK.DebuggerModel} |
| */ |
| debuggerModel() { |
| return this._runtimeModel.debuggerModel(); |
| } |
| |
| /** |
| * @override |
| * @return {!SDK.RuntimeModel} |
| */ |
| runtimeModel() { |
| return this._runtimeModel; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| isNode() { |
| return !!this._objectId && this.type === 'object' && this.subtype === 'node'; |
| } |
| }; |
| |
| |
| SDK.ScopeRemoteObject = class extends SDK.RemoteObjectImpl { |
| /** |
| * @param {!SDK.RuntimeModel} runtimeModel |
| * @param {string|undefined} objectId |
| * @param {!SDK.ScopeRef} scopeRef |
| * @param {string} type |
| * @param {string|undefined} subtype |
| * @param {*} value |
| * @param {!Protocol.Runtime.UnserializableValue=} unserializableValue |
| * @param {string=} description |
| * @param {!Protocol.Runtime.ObjectPreview=} preview |
| */ |
| constructor(runtimeModel, objectId, scopeRef, type, subtype, value, unserializableValue, description, preview) { |
| super(runtimeModel, objectId, type, subtype, value, unserializableValue, description, preview); |
| this._scopeRef = scopeRef; |
| this._savedScopeProperties = undefined; |
| } |
| |
| /** |
| * @override |
| * @param {boolean} ownProperties |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview, callback) { |
| if (accessorPropertiesOnly) { |
| callback([], []); |
| return; |
| } |
| |
| if (this._savedScopeProperties) { |
| // No need to reload scope variables, as the remote object never |
| // changes its properties. If variable is updated, the properties |
| // array is patched locally. |
| callback(this._savedScopeProperties.slice(), []); |
| return; |
| } |
| |
| /** |
| * @param {?Array.<!SDK.RemoteObjectProperty>} properties |
| * @param {?Array.<!SDK.RemoteObjectProperty>} internalProperties |
| * @this {SDK.ScopeRemoteObject} |
| */ |
| function wrappedCallback(properties, internalProperties) { |
| if (this._scopeRef && Array.isArray(properties)) { |
| this._savedScopeProperties = properties.slice(); |
| if (!this._scopeRef.callFrameId) { |
| for (const property of this._savedScopeProperties) |
| property.writable = false; |
| } |
| } |
| callback(properties, internalProperties); |
| } |
| |
| // Scope objects always fetch preview. |
| generatePreview = true; |
| |
| super.doGetProperties(ownProperties, accessorPropertiesOnly, generatePreview, wrappedCallback.bind(this)); |
| } |
| |
| /** |
| * @override |
| * @param {!Protocol.Runtime.RemoteObject} result |
| * @param {!Protocol.Runtime.CallArgument} argumentName |
| * @return {!Promise<string|undefined>} |
| */ |
| async doSetObjectPropertyValue(result, argumentName) { |
| const name = /** @type {string} */ (argumentName.value); |
| const error = await this.debuggerModel().setVariableValue( |
| this._scopeRef.number, name, SDK.RemoteObject.toCallArgument(result), this._scopeRef.callFrameId); |
| if (error) |
| return error; |
| if (this._savedScopeProperties) { |
| for (const property of this._savedScopeProperties) { |
| if (property.name === name) |
| property.value = this._runtimeModel.createRemoteObject(result); |
| } |
| } |
| } |
| }; |
| |
| SDK.ScopeRef = class { |
| /** |
| * @param {number} number |
| * @param {string=} callFrameId |
| */ |
| constructor(number, callFrameId) { |
| this.number = number; |
| this.callFrameId = callFrameId; |
| } |
| }; |
| |
| /** |
| * @unrestricted |
| */ |
| SDK.RemoteObjectProperty = class { |
| /** |
| * @param {string} name |
| * @param {?SDK.RemoteObject} value |
| * @param {boolean=} enumerable |
| * @param {boolean=} writable |
| * @param {boolean=} isOwn |
| * @param {boolean=} wasThrown |
| * @param {?SDK.RemoteObject=} symbol |
| * @param {boolean=} synthetic |
| * @param {function(string):!Promise<?SDK.RemoteObject>=} syntheticSetter |
| */ |
| constructor(name, value, enumerable, writable, isOwn, wasThrown, symbol, synthetic, syntheticSetter) { |
| this.name = name; |
| if (value !== null) |
| this.value = value; |
| this.enumerable = typeof enumerable !== 'undefined' ? enumerable : true; |
| const isNonSyntheticOrSyntheticWritable = !synthetic || !!syntheticSetter; |
| this.writable = typeof writable !== 'undefined' ? writable : isNonSyntheticOrSyntheticWritable; |
| this.isOwn = !!isOwn; |
| this.wasThrown = !!wasThrown; |
| if (symbol) |
| this.symbol = symbol; |
| this.synthetic = !!synthetic; |
| if (syntheticSetter) |
| this.syntheticSetter = syntheticSetter; |
| } |
| |
| /** |
| * @param {string} expression |
| * @return {!Promise<boolean>} |
| */ |
| async setSyntheticValue(expression) { |
| if (!this.syntheticSetter) |
| return false; |
| const result = await this.syntheticSetter(expression); |
| if (result) |
| this.value = result; |
| return !!result; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isAccessorProperty() { |
| return !!(this.getter || this.setter); |
| } |
| }; |
| |
| // Below is a wrapper around a local object that implements the RemoteObject interface, |
| // which can be used by the UI code (primarily ObjectPropertiesSection). |
| // Note that only JSON-compliant objects are currently supported, as there's no provision |
| // for traversing prototypes, extracting class names via constructor, handling properties |
| // or functions. |
| |
| SDK.LocalJSONObject = class extends SDK.RemoteObject { |
| /** |
| * @param {*} value |
| */ |
| constructor(value) { |
| super(); |
| this._value = value; |
| /** @type {string} */ |
| this._cachedDescription; |
| /** @type {!Array<!SDK.RemoteObjectProperty>} */ |
| this._cachedChildren; |
| } |
| |
| /** |
| * @override |
| * @return {!Protocol.Runtime.RemoteObjectId|undefined} |
| * */ |
| get objectId() { |
| return undefined; |
| } |
| |
| /** |
| * @override |
| * @return {*} |
| */ |
| get value() { |
| return this._value; |
| } |
| |
| /** |
| * @override |
| * @return {string|undefined} |
| */ |
| unserializableValue() { |
| const unserializableDescription = SDK.RemoteObject.unserializableDescription(this._value); |
| return unserializableDescription || undefined; |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| get description() { |
| if (this._cachedDescription) |
| return this._cachedDescription; |
| |
| /** |
| * @param {!SDK.RemoteObjectProperty} property |
| * @return {string} |
| * @this {SDK.LocalJSONObject} |
| */ |
| function formatArrayItem(property) { |
| return this._formatValue(property.value); |
| } |
| |
| /** |
| * @param {!SDK.RemoteObjectProperty} property |
| * @return {string} |
| * @this {SDK.LocalJSONObject} |
| */ |
| function formatObjectItem(property) { |
| let name = property.name; |
| if (/^\s|\s$|^$|\n/.test(name)) |
| name = '"' + name.replace(/\n/g, '\u21B5') + '"'; |
| return name + ': ' + this._formatValue(property.value); |
| } |
| |
| if (this.type === 'object') { |
| switch (this.subtype) { |
| case 'array': |
| this._cachedDescription = this._concatenate('[', ']', formatArrayItem.bind(this)); |
| break; |
| case 'date': |
| this._cachedDescription = '' + this._value; |
| break; |
| case 'null': |
| this._cachedDescription = 'null'; |
| break; |
| default: |
| this._cachedDescription = this._concatenate('{', '}', formatObjectItem.bind(this)); |
| } |
| } else { |
| this._cachedDescription = String(this._value); |
| } |
| |
| return this._cachedDescription; |
| } |
| |
| /** |
| * @param {?SDK.RemoteObject} value |
| * @return {string} |
| */ |
| _formatValue(value) { |
| if (!value) |
| return 'undefined'; |
| const description = value.description || ''; |
| if (value.type === 'string') |
| return '"' + description.replace(/\n/g, '\u21B5') + '"'; |
| return description; |
| } |
| |
| /** |
| * @param {string} prefix |
| * @param {string} suffix |
| * @param {function(!SDK.RemoteObjectProperty)} formatProperty |
| * @return {string} |
| */ |
| _concatenate(prefix, suffix, formatProperty) { |
| const previewChars = 100; |
| |
| let buffer = prefix; |
| const children = this._children(); |
| for (let i = 0; i < children.length; ++i) { |
| const itemDescription = formatProperty(children[i]); |
| if (buffer.length + itemDescription.length > previewChars) { |
| buffer += ',\u2026'; |
| break; |
| } |
| if (i) |
| buffer += ', '; |
| buffer += itemDescription; |
| } |
| buffer += suffix; |
| return buffer; |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| get type() { |
| return typeof this._value; |
| } |
| |
| /** |
| * @override |
| * @return {string|undefined} |
| */ |
| get subtype() { |
| if (this._value === null) |
| return 'null'; |
| |
| if (Array.isArray(this._value)) |
| return 'array'; |
| |
| if (this._value instanceof Date) |
| return 'date'; |
| |
| return undefined; |
| } |
| |
| /** |
| * @override |
| * @return {boolean} |
| */ |
| get hasChildren() { |
| if ((typeof this._value !== 'object') || (this._value === null)) |
| return false; |
| return !!Object.keys(/** @type {!Object} */ (this._value)).length; |
| } |
| |
| /** |
| * @override |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getOwnProperties(generatePreview, callback) { |
| callback(this._children(), null); |
| } |
| |
| /** |
| * @override |
| * @param {boolean} accessorPropertiesOnly |
| * @param {boolean} generatePreview |
| * @param {function(?Array.<!SDK.RemoteObjectProperty>, ?Array.<!SDK.RemoteObjectProperty>)} callback |
| */ |
| getAllProperties(accessorPropertiesOnly, generatePreview, callback) { |
| if (accessorPropertiesOnly) |
| callback([], null); |
| else |
| callback(this._children(), null); |
| } |
| |
| /** |
| * @return {!Array.<!SDK.RemoteObjectProperty>} |
| */ |
| _children() { |
| if (!this.hasChildren) |
| return []; |
| const value = /** @type {!Object} */ (this._value); |
| |
| /** |
| * @param {string} propName |
| * @return {!SDK.RemoteObjectProperty} |
| */ |
| function buildProperty(propName) { |
| let propValue = value[propName]; |
| if (!(propValue instanceof SDK.RemoteObject)) |
| propValue = SDK.RemoteObject.fromLocalObject(propValue); |
| return new SDK.RemoteObjectProperty(propName, propValue); |
| } |
| if (!this._cachedChildren) |
| this._cachedChildren = Object.keys(value).map(buildProperty); |
| return this._cachedChildren; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| isError() { |
| return false; |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| arrayLength() { |
| return Array.isArray(this._value) ? this._value.length : 0; |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object, ...)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>=} args |
| * @param {function(?SDK.RemoteObject, boolean=)=} callback |
| */ |
| callFunction(functionDeclaration, args, callback) { |
| const target = /** @type {?Object} */ (this._value); |
| const rawArgs = args ? args.map(arg => arg.value) : []; |
| |
| let result; |
| let wasThrown = false; |
| try { |
| result = functionDeclaration.apply(target, rawArgs); |
| } catch (e) { |
| wasThrown = true; |
| } |
| |
| if (!callback) |
| return; |
| callback(SDK.RemoteObject.fromLocalObject(result), wasThrown); |
| } |
| |
| /** |
| * @override |
| * @param {function(this:Object)} functionDeclaration |
| * @param {!Array<!Protocol.Runtime.CallArgument>|undefined} args |
| * @param {function(*)} callback |
| */ |
| callFunctionJSON(functionDeclaration, args, callback) { |
| const target = /** @type {?Object} */ (this._value); |
| const rawArgs = args ? args.map(arg => arg.value) : []; |
| |
| let result; |
| try { |
| result = functionDeclaration.apply(target, rawArgs); |
| } catch (e) { |
| result = null; |
| } |
| |
| callback(result); |
| } |
| }; |
| |
| SDK.RemoteArray = class { |
| /** |
| * @param {!SDK.RemoteObject} object |
| */ |
| constructor(object) { |
| this._object = object; |
| } |
| |
| /** |
| * @param {?SDK.RemoteObject} object |
| * @return {!SDK.RemoteArray} |
| */ |
| static objectAsArray(object) { |
| if (!object || object.type !== 'object' || (object.subtype !== 'array' && object.subtype !== 'typedarray')) |
| throw new Error('Object is empty or not an array'); |
| return new SDK.RemoteArray(object); |
| } |
| |
| /** |
| * @param {!Array<!SDK.RemoteObject>} objects |
| * @return {!Promise<!SDK.RemoteArray>} |
| */ |
| static createFromRemoteObjects(objects) { |
| if (!objects.length) |
| throw new Error('Input array is empty'); |
| const objectArguments = []; |
| for (let i = 0; i < objects.length; ++i) |
| objectArguments.push(SDK.RemoteObject.toCallArgument(objects[i])); |
| return objects[0].callFunctionPromise(createArray, objectArguments).then(returnRemoteArray); |
| |
| /** |
| * @return {!Array<*>} |
| */ |
| function createArray() { |
| if (arguments.length > 1) |
| return new Array(arguments); |
| return [arguments[0]]; |
| } |
| |
| /** |
| * @param {!SDK.CallFunctionResult} result |
| * @return {!SDK.RemoteArray} |
| */ |
| function returnRemoteArray(result) { |
| if (result.wasThrown || !result.object) |
| throw new Error('Call function throws exceptions or returns empty value'); |
| return SDK.RemoteArray.objectAsArray(result.object); |
| } |
| } |
| |
| /** |
| * @param {number} index |
| * @return {!Promise<!SDK.RemoteObject>} |
| */ |
| at(index) { |
| if (index < 0 || index > this._object.arrayLength()) |
| throw new Error('Out of range'); |
| return this._object.callFunctionPromise(at, [SDK.RemoteObject.toCallArgument(index)]) |
| .then(assertCallFunctionResult); |
| |
| /** |
| * @suppressReceiverCheck |
| * @param {number} index |
| * @return {*} |
| * @this {!Object} |
| */ |
| function at(index) { |
| return this[index]; |
| } |
| |
| /** |
| * @param {!SDK.CallFunctionResult} result |
| * @return {!SDK.RemoteObject} |
| */ |
| function assertCallFunctionResult(result) { |
| if (result.wasThrown || !result.object) |
| throw new Error('Exception in callFunction or result value is empty'); |
| return result.object; |
| } |
| } |
| |
| /** |
| * @return {number} |
| */ |
| length() { |
| return this._object.arrayLength(); |
| } |
| |
| /** |
| * @param {function(!SDK.RemoteObject):!Promise<T>} func |
| * @return {!Promise<!Array<T>>} |
| * @template T |
| */ |
| map(func) { |
| const promises = []; |
| for (let i = 0; i < this.length(); ++i) |
| promises.push(this.at(i).then(func)); |
| return Promise.all(promises); |
| } |
| |
| /** |
| * @return {!SDK.RemoteObject} |
| */ |
| object() { |
| return this._object; |
| } |
| }; |
| |
| |
| SDK.RemoteFunction = class { |
| /** |
| * @param {!SDK.RemoteObject} object |
| */ |
| constructor(object) { |
| this._object = object; |
| } |
| |
| /** |
| * @param {?SDK.RemoteObject} object |
| * @return {!SDK.RemoteFunction} |
| */ |
| static objectAsFunction(object) { |
| if (!object || object.type !== 'function') |
| throw new Error('Object is empty or not a function'); |
| return new SDK.RemoteFunction(object); |
| } |
| |
| /** |
| * @return {!Promise<!SDK.RemoteObject>} |
| */ |
| targetFunction() { |
| return this._object.getOwnPropertiesPromise(false /* generatePreview */).then(targetFunction.bind(this)); |
| |
| /** |
| * @param {!{properties: ?Array<!SDK.RemoteObjectProperty>, internalProperties: ?Array<!SDK.RemoteObjectProperty>}} ownProperties |
| * @return {!SDK.RemoteObject} |
| * @this {SDK.RemoteFunction} |
| */ |
| function targetFunction(ownProperties) { |
| if (!ownProperties.internalProperties) |
| return this._object; |
| const internalProperties = ownProperties.internalProperties; |
| for (const property of internalProperties) { |
| if (property.name === '[[TargetFunction]]') |
| return property.value; |
| } |
| return this._object; |
| } |
| } |
| |
| /** |
| * @return {!Promise<?SDK.DebuggerModel.FunctionDetails>} |
| */ |
| targetFunctionDetails() { |
| return this.targetFunction().then(functionDetails.bind(this)); |
| |
| /** |
| * @param {!SDK.RemoteObject} targetFunction |
| * @return {!Promise<?SDK.DebuggerModel.FunctionDetails>} |
| * @this {SDK.RemoteFunction} |
| */ |
| function functionDetails(targetFunction) { |
| const boundReleaseFunctionDetails = |
| releaseTargetFunction.bind(null, this._object !== targetFunction ? targetFunction : null); |
| return targetFunction.debuggerModel().functionDetailsPromise(targetFunction).then(boundReleaseFunctionDetails); |
| } |
| |
| /** |
| * @param {?SDK.RemoteObject} targetFunction |
| * @param {?SDK.DebuggerModel.FunctionDetails} functionDetails |
| * @return {?SDK.DebuggerModel.FunctionDetails} |
| */ |
| function releaseTargetFunction(targetFunction, functionDetails) { |
| if (targetFunction) |
| targetFunction.release(); |
| return functionDetails; |
| } |
| } |
| |
| /** |
| * @return {!SDK.RemoteObject} |
| */ |
| object() { |
| return this._object; |
| } |
| }; |
| |
| /** |
| * @const |
| * @type {!RegExp} |
| */ |
| SDK.RemoteObject._descriptionLengthParenRegex = /\(([0-9]+)\)/; |
| |
| /** |
| * @const |
| * @type {!RegExp} |
| */ |
| SDK.RemoteObject._descriptionLengthSquareRegex = /\[([0-9]+)\]/; |
| |
| /** |
| * @const |
| * @enum {!Protocol.Runtime.UnserializableValue} |
| */ |
| SDK.RemoteObject.UnserializableNumber = { |
| Negative0: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-0'), |
| NaN: /** @type {!Protocol.Runtime.UnserializableValue} */ ('NaN'), |
| Infinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('Infinity'), |
| NegativeInfinity: /** @type {!Protocol.Runtime.UnserializableValue} */ ('-Infinity') |
| }; |