/* * Copyright (C) 2007, 2014-2015 Apple Inc. All rights reserved. * Copyright (C) 2013 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: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. */ //# sourceURL=__InjectedScript_InjectedScriptSource.js (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) { // FIXME: Web Inspector: Parse InjectedScriptSource as a built-in to get guaranteed non-user-overridden built-ins var Object = {}.constructor; function toString(obj) { return String(obj); } function toStringDescription(obj) { if (obj === 0 && 1 / obj < 0) return "-0"; if (isBigInt(obj)) return toString(obj) + "n"; return toString(obj); } function isUInt32(obj) { if (typeof obj === "number") return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0); return "" + (obj >>> 0) === obj; } function isSymbol(value) { return typeof value === "symbol"; } function isBigInt(value) { return typeof value === "bigint"; } function isEmptyObject(object) { for (let key in object) return false; return true; } function isDefined(value) { return !!value || InjectedScriptHost.isHTMLAllCollection(value); } function isPrimitiveValue(value) { switch (typeof value) { case "boolean": case "number": case "string": return true; case "undefined": return !InjectedScriptHost.isHTMLAllCollection(value); default: return false; } } // ------- let InjectedScript = class InjectedScript { constructor() { this._lastBoundObjectId = 1; this._idToWrappedObject = {}; this._idToObjectGroupName = {}; this._objectGroups = {}; this._modules = {}; this._nextSavedResultIndex = 1; this._savedResults = []; } // InjectedScript C++ API execute(functionString, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult, args) { return this._wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, () => { const isEvalOnCallFrame = false; return this._evaluateOn(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, functionString, isEvalOnCallFrame, includeCommandLineAPI).apply(undefined, args); }); } evaluate(expression, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult) { const isEvalOnCallFrame = false; return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult); } awaitPromise(promiseObjectId, returnByValue, generatePreview, saveResult, callback) { let parsedPromiseObjectId = this._parseObjectId(promiseObjectId); let promiseObject = this._objectForId(parsedPromiseObjectId); let promiseObjectGroupName = this._idToObjectGroupName[parsedPromiseObjectId.id]; if (!isDefined(promiseObject)) { callback("Could not find object with given id"); return; } if (!(promiseObject instanceof Promise)) { callback("Object with given id is not a Promise"); return; } let resolve = (value) => { let returnObject = { wasThrown: false, result: RemoteObject.create(value, promiseObjectGroupName, returnByValue, generatePreview), }; if (saveResult) { this._savedResultIndex = 0; this._saveResult(returnObject.result); if (this._savedResultIndex) returnObject.savedResultIndex = this._savedResultIndex; } callback(returnObject); }; let reject = (reason) => { callback(this._createThrownValue(reason, promiseObjectGroupName)); }; promiseObject.then(resolve, reject); } evaluateOnCallFrame(topCallFrame, callFrameId, expression, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult) { let callFrame = this._callFrameForId(topCallFrame, callFrameId); if (!callFrame) return "Could not find call frame with given id"; const isEvalOnCallFrame = true; return this._evaluateAndWrap(callFrame.evaluateWithScopeExtension, callFrame, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult); } callFunctionOn(objectId, expression, args, returnByValue, generatePreview) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); let objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; if (!isDefined(object)) return "Could not find object with given id"; let resolvedArgs = []; if (args) { let callArgs = InjectedScriptHost.evaluate(args); for (let i = 0; i < callArgs.length; ++i) { try { resolvedArgs[i] = this._resolveCallArgument(callArgs[i]); } catch (e) { return String(e); } } } try { let func = InjectedScriptHost.evaluate("(" + expression + ")"); if (typeof func !== "function") return "Given expression does not evaluate to a function"; return { wasThrown: false, result: RemoteObject.create(func.apply(object, resolvedArgs), objectGroupName, returnByValue, generatePreview) }; } catch (e) { return this._createThrownValue(e, objectGroupName); } } getFunctionDetails(objectId) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); if (typeof object !== "function") return "Cannot resolve function by id."; return this.functionDetails(object); } functionDetails(func) { let details = InjectedScriptHost.functionDetails(func); if (!details) return "Cannot resolve function details."; return details; } getPreview(objectId) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); return RemoteObject.createObjectPreviewForValue(object, true); } getProperties(objectId, ownProperties, fetchStart, fetchCount, generatePreview) { let collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties; return this._getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview}); } getDisplayableProperties(objectId, fetchStart, fetchCount, generatePreview) { let collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties; return this._getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview, nativeGettersAsValues: true}); } getInternalProperties(objectId, generatePreview) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); let objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; if (!isDefined(object)) return false; if (isSymbol(object)) return false; let descriptors = this._internalPropertyDescriptors(object); if (!descriptors) return []; for (let i = 0; i < descriptors.length; ++i) { let descriptor = descriptors[i]; if ("value" in descriptor) descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview); } return descriptors; } getCollectionEntries(objectId, objectGroupName, fetchStart, fetchCount) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); objectGroupName = objectGroupName || this._idToObjectGroupName[parsedObjectId.id]; if (!isDefined(object)) return; if (typeof object !== "object") return; let entries = this._entries(object, InjectedScriptHost.subtype(object), fetchStart, fetchCount); return entries.map(function(entry) { entry.value = RemoteObject.create(entry.value, objectGroupName, false, true); if ("key" in entry) entry.key = RemoteObject.create(entry.key, objectGroupName, false, true); return entry; }); } saveResult(callArgumentJSON) { this._savedResultIndex = 0; try { let callArgument = InjectedScriptHost.evaluate("(" + callArgumentJSON + ")"); let value = this._resolveCallArgument(callArgument); this._saveResult(value); } catch { } return this._savedResultIndex; } wrapCallFrames(callFrame) { if (!callFrame) return false; let result = []; let depth = 0; do { result.push(new InjectedScript.CallFrameProxy(depth++, callFrame)); callFrame = callFrame.caller; } while (callFrame); return result; } wrapObject(object, groupName, canAccessInspectedGlobalObject, generatePreview) { if (!canAccessInspectedGlobalObject) return this._fallbackWrapper(object); return RemoteObject.create(object, groupName, false, generatePreview); } wrapJSONString(jsonString, groupName, generatePreview) { try { return this.wrapObject(JSON.parse(jsonString), groupName, true, generatePreview); } catch { return null; } } wrapTable(canAccessInspectedGlobalObject, table, columns) { if (!canAccessInspectedGlobalObject) return this._fallbackWrapper(table); // FIXME: Currently columns are ignored. Instead, the frontend filters all // properties based on the provided column names and in the provided order. // We could filter here to avoid sending very large preview objects. let columnNames = null; if (typeof columns === "string") columns = [columns]; if (InjectedScriptHost.subtype(columns) === "array") { columnNames = []; for (let i = 0; i < columns.length; ++i) columnNames.push(toString(columns[i])); } return RemoteObject.create(table, "console", false, true, columnNames); } previewValue(value) { return RemoteObject.createObjectPreviewForValue(value, true); } setEventValue(value) { this._eventValue = value; } clearEventValue() { delete this._eventValue; } setExceptionValue(value) { this._exceptionValue = value; } clearExceptionValue() { delete this._exceptionValue; } findObjectById(objectId) { let parsedObjectId = this._parseObjectId(objectId); return this._objectForId(parsedObjectId); } releaseObject(objectId) { let parsedObjectId = this._parseObjectId(objectId); this._releaseObject(parsedObjectId.id); } releaseObjectGroup(objectGroupName) { if (objectGroupName === "console") { delete this._lastResult; this._nextSavedResultIndex = 1; this._savedResults = []; } let group = this._objectGroups[objectGroupName]; if (!group) return; for (let i = 0; i < group.length; i++) this._releaseObject(group[i]); delete this._objectGroups[objectGroupName]; } // CommandLineAPI inspectObject(object) { if (this._inspectObject) this._inspectObject(object); } // InjectedScriptModule C++ API hasInjectedModule(name) { return this._modules[name]; } injectModule(name, source, host) { this._modules[name] = false; let moduleFunction = InjectedScriptHost.evaluate("(" + source + ")"); if (typeof moduleFunction !== "function") throw "Error: Web Inspector: a function was expected for injectModule"; moduleFunction(InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, {RemoteObject, CommandLineAPI}, host); this._modules[name] = true; } // InjectedScriptModule JavaScript API isPrimitiveValue(value) { return isPrimitiveValue(value); } // Private _parseObjectId(objectId) { return InjectedScriptHost.evaluate("(" + objectId + ")"); } _objectForId(objectId) { return this._idToWrappedObject[objectId.id]; } _bind(object, objectGroupName) { let id = this._lastBoundObjectId++; let objectId = `{"injectedScriptId":${injectedScriptId},"id":${id}}`; this._idToWrappedObject[id] = object; if (objectGroupName) { let group = this._objectGroups[objectGroupName]; if (!group) { group = []; this._objectGroups[objectGroupName] = group; } group.push(id); this._idToObjectGroupName[id] = objectGroupName; } return objectId; } _releaseObject(id) { delete this._idToWrappedObject[id]; delete this._idToObjectGroupName[id]; } _fallbackWrapper(object) { let result = {}; result.type = typeof object; if (isPrimitiveValue(object)) result.value = object; else result.description = toStringDescription(object); return result; } _resolveCallArgument(callArgumentJSON) { if ("value" in callArgumentJSON) return callArgumentJSON.value; let objectId = callArgumentJSON.objectId; if (objectId) { let parsedArgId = this._parseObjectId(objectId); if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId) throw "Arguments should belong to the same JavaScript world as the target object."; let resolvedArg = this._objectForId(parsedArgId); if (!isDefined(resolvedArg)) throw "Could not find object with given id"; return resolvedArg; } return undefined; } _createThrownValue(value, objectGroup) { let remoteObject = RemoteObject.create(value, objectGroup); try { remoteObject.description = toStringDescription(value); } catch { } return { wasThrown: true, result: remoteObject }; } _evaluateAndWrap(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult) { return this._wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, () => { return this._evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI); }); } _wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, func) { return this._wrapCall(objectGroup, returnByValue, generatePreview, saveResult, () => { let result = func(); if (saveResult) this._saveResult(result); return result; }); } _wrapCall(objectGroup, returnByValue, generatePreview, saveResult, func) { try { this._savedResultIndex = 0; let returnObject = { wasThrown: false, result: RemoteObject.create(func(), objectGroup, returnByValue, generatePreview) }; if (saveResult && this._savedResultIndex) returnObject.savedResultIndex = this._savedResultIndex; return returnObject; } catch (e) { return this._createThrownValue(e, objectGroup); } } _evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI) { let commandLineAPI = null; if (includeCommandLineAPI) commandLineAPI = new CommandLineAPI(isEvalOnCallFrame ? object : null); return evalFunction.call(object, expression, commandLineAPI); } _callFrameForId(topCallFrame, callFrameId) { let parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")"); let ordinal = parsedCallFrameId["ordinal"]; let callFrame = topCallFrame; while (--ordinal >= 0 && callFrame) callFrame = callFrame.caller; return callFrame; } _getProperties(objectId, collectionMode, {fetchStart, fetchCount, generatePreview, nativeGettersAsValues}) { let parsedObjectId = this._parseObjectId(objectId); let object = this._objectForId(parsedObjectId); let objectGroupName = this._idToObjectGroupName[parsedObjectId.id]; if (!isDefined(object)) return false; if (isSymbol(object)) return false; let start = fetchStart || 0; if (start < 0) start = 0; let count = fetchCount || 0; if (count < 0) count = 0; // Always include __proto__ at the end, but only for the first fetch. let includeProto = !start; let descriptors = []; this._forEachPropertyDescriptor(object, collectionMode, (descriptor) => { if (start > 0) { --start; return InjectedScript.PropertyFetchAction.Continue; } if ("get" in descriptor) descriptor.get = RemoteObject.create(descriptor.get, objectGroupName); if ("set" in descriptor) descriptor.set = RemoteObject.create(descriptor.set, objectGroupName); if ("value" in descriptor) descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview); if ("symbol" in descriptor) descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName); descriptors.push(descriptor); if (includeProto && count && descriptors.length >= count && descriptor.name !== "__proto__") return InjectedScript.PropertyFetchAction.Stop; return (count && descriptors.length >= count) ? InjectedScript.PropertyFetchAction.Stop : InjectedScript.PropertyFetchAction.Continue; }, {nativeGettersAsValues, includeProto}); return descriptors; } _internalPropertyDescriptors(object, completeDescriptor) { let internalProperties = InjectedScriptHost.getInternalProperties(object); if (!internalProperties) return null; let descriptors = []; for (let i = 0; i < internalProperties.length; i++) { let property = internalProperties[i]; let descriptor = {name: property.name, value: property.value}; if (completeDescriptor) descriptor.isOwn = true; descriptors.push(descriptor); } return descriptors; } _forEachPropertyDescriptor(object, collectionMode, callback, {nativeGettersAsValues, includeProto}) { if (InjectedScriptHost.subtype(object) === "proxy") return; let nameProcessed = new Set; // Handled below when `includeProto`. nameProcessed.add("__proto__"); function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter) { try { let fakeDescriptor = {name, value: object[name]}; if (descriptor) { if (descriptor.writable) fakeDescriptor.writable = true; if (descriptor.configurable) fakeDescriptor.configurable = true; if (descriptor.enumerable) fakeDescriptor.enumerable = true; } if (possibleNativeBindingGetter) fakeDescriptor.nativeGetter = true; if (isOwnProperty) fakeDescriptor.isOwn = true; if (symbol) fakeDescriptor.symbol = symbol; // Silence any possible unhandledrejection exceptions created from accessing a native accessor with a wrong this object. if (fakeDescriptor.value instanceof Promise && InjectedScriptHost.isPromiseRejectedWithNativeGetterTypeError(fakeDescriptor.value)) fakeDescriptor.value.catch(function(){}); return fakeDescriptor; } catch (e) { let errorDescriptor = {name, value: e, wasThrown: true}; if (isOwnProperty) errorDescriptor.isOwn = true; if (symbol) errorDescriptor.symbol = symbol; return errorDescriptor; } } function processDescriptor(descriptor, isOwnProperty, possibleNativeBindingGetter) { // All properties. if (collectionMode & InjectedScript.CollectionMode.AllProperties) return callback(descriptor); // Own properties. if (collectionMode & InjectedScript.CollectionMode.OwnProperties && isOwnProperty) return callback(descriptor); // Native Getter properties. if (collectionMode & InjectedScript.CollectionMode.NativeGetterProperties) { if (possibleNativeBindingGetter) return callback(descriptor); } } function processProperty(o, propertyName, isOwnProperty) { if (nameProcessed.has(propertyName)) return InjectedScript.PropertyFetchAction.Continue; nameProcessed.add(propertyName); let name = toString(propertyName); let symbol = isSymbol(propertyName) ? propertyName : null; let descriptor = Object.getOwnPropertyDescriptor(o, propertyName); if (!descriptor) { // FIXME: Bad descriptor. Can we get here? // Fall back to very restrictive settings. let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty); return processDescriptor(fakeDescriptor, isOwnProperty); } if (nativeGettersAsValues) { if (String(descriptor.get).endsWith("[native code]\n}") || (!descriptor.get && descriptor.hasOwnProperty("get") && !descriptor.set && descriptor.hasOwnProperty("set"))) { // Developers may create such a descriptor, so we should be resilient: // let x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p") let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, true); return processDescriptor(fakeDescriptor, isOwnProperty, true); } } descriptor.name = name; if (isOwnProperty) descriptor.isOwn = true; if (symbol) descriptor.symbol = symbol; return processDescriptor(descriptor, isOwnProperty); } let isArrayLike = false; try { isArrayLike = RemoteObject.subtype(object) === "array" && isFinite(object.length) && object.length > 0; } catch { } for (let o = object; isDefined(o); o = Object.getPrototypeOf(o)) { let isOwnProperty = o === object; let shouldBreak = false; // FIXME: Web Inspector: show autocomplete entries for non-index properties on arrays if (isArrayLike && isOwnProperty) { for (let i = 0; i < o.length; ++i) { if (!(i in o)) continue; let result = processProperty(o, toString(i), isOwnProperty); shouldBreak = result === InjectedScript.PropertyFetchAction.Stop; if (shouldBreak) break; } } else { let propertyNames = Object.getOwnPropertyNames(o); for (let i = 0; i < propertyNames.length; ++i) { let result = processProperty(o, propertyNames[i], isOwnProperty); shouldBreak = result === InjectedScript.PropertyFetchAction.Stop; if (shouldBreak) break; } } if (shouldBreak) break; if (Object.getOwnPropertySymbols) { let propertySymbols = Object.getOwnPropertySymbols(o); for (let i = 0; i < propertySymbols.length; ++i) { let result = processProperty(o, propertySymbols[i], isOwnProperty); shouldBreak = result === InjectedScript.PropertyFetchAction.Stop; if (shouldBreak) break; } } if (shouldBreak) break; if (collectionMode === InjectedScript.CollectionMode.OwnProperties) break; } if (includeProto) { try { if (object.__proto__) callback({name: "__proto__", value: object.__proto__, writable: true, configurable: true, isOwn: true}); } catch { } } } _getSetEntries(object, fetchStart, fetchCount) { let entries = []; // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator]. for (let value of object) { if (fetchStart > 0) { fetchStart--; continue; } entries.push({value}); if (fetchCount && entries.length === fetchCount) break; } return entries; } _getMapEntries(object, fetchStart, fetchCount) { let entries = []; // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator]. for (let [key, value] of object) { if (fetchStart > 0) { fetchStart--; continue; } entries.push({key, value}); if (fetchCount && entries.length === fetchCount) break; } return entries; } _getWeakMapEntries(object, fetchCount) { return InjectedScriptHost.weakMapEntries(object, fetchCount); } _getWeakSetEntries(object, fetchCount) { return InjectedScriptHost.weakSetEntries(object, fetchCount); } _getIteratorEntries(object, fetchCount) { return InjectedScriptHost.iteratorEntries(object, fetchCount); } _entries(object, subtype, fetchStart, fetchCount) { if (subtype === "set") return this._getSetEntries(object, fetchStart, fetchCount); if (subtype === "map") return this._getMapEntries(object, fetchStart, fetchCount); if (subtype === "weakmap") return this._getWeakMapEntries(object, fetchCount); if (subtype === "weakset") return this._getWeakSetEntries(object, fetchCount); if (subtype === "iterator") return this._getIteratorEntries(object, fetchCount); throw "unexpected type"; } _saveResult(result) { this._lastResult = result; if (result === undefined || result === null) return; let existingIndex = this._savedResults.indexOf(result); if (existingIndex !== -1) { this._savedResultIndex = existingIndex; return; } this._savedResultIndex = this._nextSavedResultIndex; this._savedResults[this._nextSavedResultIndex++] = result; // $n is limited from $1-$99. $0 is special. if (this._nextSavedResultIndex >= 100) this._nextSavedResultIndex = 1; } }; InjectedScript.CollectionMode = { OwnProperties: 1 << 0, // own properties. NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain. AllProperties: 1 << 2, // all properties in the prototype chain. }; InjectedScript.PropertyFetchAction = { Continue: Symbol("continue"), Stop: Symbol("stop"), } var injectedScript = new InjectedScript; // ------- let RemoteObject = class RemoteObject { constructor(object, objectGroupName, forceValueType, generatePreview, columnNames) { this.type = typeof object; if (this.type === "undefined" && InjectedScriptHost.isHTMLAllCollection(object)) this.type = "object"; if (isPrimitiveValue(object) || isBigInt(object) || object === null || forceValueType) { // We don't send undefined values over JSON. // BigInt values are not JSON serializable. if (this.type !== "undefined" && this.type !== "bigint") this.value = object; // Null object is object with 'null' subtype. if (object === null) this.subtype = "null"; // Provide user-friendly number values. if (this.type === "number" || this.type === "bigint") this.description = toStringDescription(object); return; } this.objectId = injectedScript._bind(object, objectGroupName); let subtype = RemoteObject.subtype(object); if (subtype) this.subtype = subtype; this.className = InjectedScriptHost.internalConstructorName(object); this.description = RemoteObject.describe(object); if (subtype === "array") this.size = typeof object.length === "number" ? object.length : 0; else if (subtype === "set" || subtype === "map") this.size = object.size; else if (subtype === "weakmap") this.size = InjectedScriptHost.weakMapSize(object); else if (subtype === "weakset") this.size = InjectedScriptHost.weakSetSize(object); else if (subtype === "class") { this.classPrototype = RemoteObject.create(object.prototype, objectGroupName); this.className = object.name; } if (generatePreview && this.type === "object") { if (subtype === "proxy") { this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object)); this.preview.lossless = false; } else this.preview = this._generatePreview(object, undefined, columnNames); } } // Static static create(object, objectGroupName, forceValueType, generatePreview, columnNames) { try { return new RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames); } catch (e) { let description; try { description = RemoteObject.describe(e); } catch (ex) { alert(ex.message); description = ""; } return new RemoteObject(description); } } static createObjectPreviewForValue(value, generatePreview, columnNames) { let remoteObject = new RemoteObject(value, undefined, false, generatePreview, columnNames); if (remoteObject.objectId) injectedScript.releaseObject(remoteObject.objectId); if (remoteObject.classPrototype && remoteObject.classPrototype.objectId) injectedScript.releaseObject(remoteObject.classPrototype.objectId); return remoteObject.preview || remoteObject._emptyPreview(); } static subtype(value) { if (value === null) return "null"; if (isPrimitiveValue(value) || isBigInt(value) || isSymbol(value)) return null; if (InjectedScriptHost.isHTMLAllCollection(value)) return "array"; let preciseType = InjectedScriptHost.subtype(value); if (preciseType) return preciseType; // FireBug's array detection. try { if (typeof value.splice === "function" && isFinite(value.length)) return "array"; } catch { } return null; } static describe(value) { if (isPrimitiveValue(value)) return null; if (isBigInt(value)) return null; if (isSymbol(value)) return toString(value); let subtype = RemoteObject.subtype(value); if (subtype === "regexp") return toString(value); if (subtype === "date") return toString(value); if (subtype === "error") return toString(value); if (subtype === "proxy") return "Proxy"; if (subtype === "node") return RemoteObject.nodePreview(value); let className = InjectedScriptHost.internalConstructorName(value); if (subtype === "array") return className; if (subtype === "iterator" && Symbol.toStringTag in value) return value[Symbol.toStringTag]; // NodeList in JSC is a function, check for array prior to this. if (typeof value === "function") return value.toString(); // If Object, try for a better name from the constructor. if (className === "Object") { let constructorName = value.constructor && value.constructor.name; if (constructorName) return constructorName; } return className; } static nodePreview(node) { let isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion; let nodeName = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase(); switch (node.nodeType) { case 1: // Node.ELEMENT_NODE if (node.id) return "<" + nodeName + " id=\"" + node.id + "\">"; if (node.classList.length) return "<" + nodeName + " class=\"" + node.classList.toString().replace(/\s+/, " ") + "\">"; if (nodeName === "input" && node.type) return "<" + nodeName + " type=\"" + node.type + "\">"; return "<" + nodeName + ">"; case 3: // Node.TEXT_NODE return nodeName + " \"" + node.nodeValue + "\""; case 8: // Node.COMMENT_NODE return ""; case 10: // Node.DOCUMENT_TYPE_NODE return ""; default: return nodeName; } } // Private _initialPreview() { let preview = { type: this.type, description: this.description || toString(this.value), lossless: true, }; if (this.subtype) { preview.subtype = this.subtype; if (this.subtype !== "null") { preview.overflow = false; preview.properties = []; } } if ("size" in this) preview.size = this.size; return preview; } _emptyPreview() { let preview = this._initialPreview(); if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator") { if (this.size) { preview.entries = []; preview.lossless = false; preview.overflow = true; } } return preview; } _generatePreview(object, firstLevelKeys, secondLevelKeys) { let preview = this._initialPreview(); let isTableRowsRequest = secondLevelKeys === null || secondLevelKeys; let firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0; let propertiesThreshold = { properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount), indexes: isTableRowsRequest ? 1000 : Math.max(10, firstLevelKeysCount) }; try { // Maps, Sets, and Iterators have entries. if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator") this._appendEntryPreviews(object, preview); preview.properties = []; // Internal Properties. let internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true); if (internalPropertyDescriptors) { for (let i = 0; i < internalPropertyDescriptors.length; ++i) { let result = this._appendPropertyPreview(object, preview, internalPropertyDescriptors[i], propertiesThreshold, firstLevelKeys, secondLevelKeys, {internal: true}); if (result === InjectedScript.PropertyFetchAction.Stop) return preview; } } if (preview.entries) return preview; // Properties. injectedScript._forEachPropertyDescriptor(object, InjectedScript.CollectionMode.AllProperties, (descriptor) => { return this._appendPropertyPreview(object, preview, descriptor, propertiesThreshold, firstLevelKeys, secondLevelKeys); }, {nativeGettersAsValues: true, includeProto: true}) } catch { preview.lossless = false; } return preview; } _appendPropertyPreview(object, preview, descriptor, propertiesThreshold, firstLevelKeys, secondLevelKeys, {internal} = {}) { // Error in descriptor. if (descriptor.wasThrown) { preview.lossless = false; return InjectedScript.PropertyFetchAction.Continue; } // Do not show "__proto__" in preview. let name = descriptor.name; if (name === "__proto__") { // Non basic __proto__ objects may have interesting, non-enumerable, methods to show. if (descriptor.value && descriptor.value.constructor && descriptor.value.constructor !== Object && descriptor.value.constructor !== Array && descriptor.value.constructor !== RegExp) preview.lossless = false; return InjectedScript.PropertyFetchAction.Continue; } // For arrays, only allow indexes. if (this.subtype === "array" && !isUInt32(name)) return InjectedScript.PropertyFetchAction.Continue; // Do not show non-enumerable non-own properties. // Special case to allow array indexes that may be on the prototype. // Special case to allow native getters on non-RegExp objects. if (!descriptor.enumerable && !descriptor.isOwn && !(this.subtype === "array" || (this.subtype !== "regexp" && descriptor.nativeGetter))) return InjectedScript.PropertyFetchAction.Continue; // If we have a filter, only show properties in the filter. // FIXME: Currently these filters do nothing on the backend. if (firstLevelKeys && !firstLevelKeys.includes(name)) return InjectedScript.PropertyFetchAction.Continue; function appendPreview(property) { if (toString(property.name >>> 0) === property.name) propertiesThreshold.indexes--; else propertiesThreshold.properties--; if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) { preview.overflow = true; preview.lossless = false; return InjectedScript.PropertyFetchAction.Stop; } if (internal) property.internal = true; preview.properties.push(property); return InjectedScript.PropertyFetchAction.Continue; } // Getter/setter. if (!("value" in descriptor)) { preview.lossless = false; return appendPreview({name, type: "accessor"}); } // Null value. let value = descriptor.value; if (value === null) return appendPreview({name, type: "object", subtype: "null", value: "null"}); // Ignore non-enumerable functions. let type = typeof value; if (!descriptor.enumerable && type === "function") return InjectedScript.PropertyFetchAction.Continue; // Fix type of document.all. if (InjectedScriptHost.isHTMLAllCollection(value)) type = "object"; // Primitive. const maxLength = 100; if (isPrimitiveValue(value) || isBigInt(value)) { if (type === "string" && value.length > maxLength) { value = this._abbreviateString(value, maxLength, true); preview.lossless = false; } return appendPreview({name, type, value: toStringDescription(value)}); } // Symbol. if (isSymbol(value)) { let symbolString = toString(value); if (symbolString.length > maxLength) { symbolString = this._abbreviateString(symbolString, maxLength, true); preview.lossless = false; } return appendPreview({name, type, value: symbolString}); } // Object. let property = {name, type}; let subtype = RemoteObject.subtype(value); if (subtype) property.subtype = subtype; // Second level. if ((secondLevelKeys === null || secondLevelKeys) || this._isPreviewableObject(value, object)) { // FIXME: If we want secondLevelKeys filter to continue we would need some refactoring. let subPreview = RemoteObject.createObjectPreviewForValue(value, value !== object, secondLevelKeys); property.valuePreview = subPreview; if (!subPreview.lossless) preview.lossless = false; if (subPreview.overflow) preview.overflow = true; } else { let description = ""; if (type !== "function" || subtype === "class") { let fullDescription; if (subtype === "class") fullDescription = "class " + value.name; else if (subtype === "node") fullDescription = RemoteObject.nodePreview(value); else fullDescription = RemoteObject.describe(value); description = this._abbreviateString(fullDescription, maxLength, subtype === "regexp"); } property.value = description; preview.lossless = false; } return appendPreview(property); } _appendEntryPreviews(object, preview) { // Fetch 6, but only return 5, so we can tell if we overflowed. let entries = injectedScript._entries(object, this.subtype, 0, 6); if (!entries) return; if (entries.length > 5) { entries.pop(); preview.overflow = true; preview.lossless = false; } function updateMainPreview(subPreview) { if (!subPreview.lossless) preview.lossless = false; } preview.entries = entries.map(function(entry) { entry.value = RemoteObject.createObjectPreviewForValue(entry.value, entry.value !== object); updateMainPreview(entry.value); if ("key" in entry) { entry.key = RemoteObject.createObjectPreviewForValue(entry.key, entry.key !== object); updateMainPreview(entry.key); } return entry; }); } _isPreviewableObject(value, object) { let set = new Set; set.add(object); return this._isPreviewableObjectInternal(value, set, 1); } _isPreviewableObjectInternal(object, knownObjects, depth) { // Deep object. if (depth > 3) return false; // Primitive. if (isPrimitiveValue(object) || isBigInt(object) || isSymbol(object)) return true; // Null. if (object === null) return true; // Cyclic objects. if (knownObjects.has(object)) return false; ++depth; knownObjects.add(object); // Arrays are simple if they have 5 or less simple objects. let subtype = RemoteObject.subtype(object); if (subtype === "array") { let length = object.length; if (length > 5) return false; for (let i = 0; i < length; ++i) { if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth)) return false; } return true; } // Not a basic object. if (object.__proto__ && object.__proto__.__proto__) return false; // Objects are simple if they have 3 or less simple value properties. let ownPropertyNames = Object.getOwnPropertyNames(object); if (ownPropertyNames.length > 3) return false; for (let i = 0; i < ownPropertyNames.length; ++i) { let propertyName = ownPropertyNames[i]; let descriptor = Object.getOwnPropertyDescriptor(object, propertyName); if (descriptor && !("value" in descriptor)) return false; if (!this._isPreviewableObjectInternal(object[propertyName], knownObjects, depth)) return false; } return true; } _abbreviateString(string, maxLength, middle) { if (string.length <= maxLength) return string; if (middle) { let leftHalf = maxLength >> 1; let rightHalf = maxLength - leftHalf - 1; return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf); } return string.substr(0, maxLength) + "\u2026"; } }; // ------- InjectedScript.CallFrameProxy = function(ordinal, callFrame) { this.callFrameId = `{"ordinal":${ordinal},"injectedScriptId":${injectedScriptId}}`; this.functionName = callFrame.functionName; this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column}; this.scopeChain = this._wrapScopeChain(callFrame); this.this = RemoteObject.create(callFrame.thisObject, "backtrace"); this.isTailDeleted = callFrame.isTailDeleted; }; InjectedScript.CallFrameProxy.prototype = { _wrapScopeChain(callFrame) { let scopeChain = callFrame.scopeChain; let scopeDescriptions = callFrame.scopeDescriptions(); let scopeChainProxy = []; for (let i = 0; i < scopeChain.length; i++) scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(scopeChain[i], scopeDescriptions[i], "backtrace"); return scopeChainProxy; } }; InjectedScript.CallFrameProxy._scopeTypeNames = { 0: "global", // GLOBAL_SCOPE 1: "with", // WITH_SCOPE 2: "closure", // CLOSURE_SCOPE 3: "catch", // CATCH_SCOPE 4: "functionName", // FUNCTION_NAME_SCOPE 5: "globalLexicalEnvironment", // GLOBAL_LEXICAL_ENVIRONMENT_SCOPE 6: "nestedLexical", // NESTED_LEXICAL_SCOPE }; InjectedScript.CallFrameProxy._createScopeJson = function(object, {name, type, location}, groupId) { let scope = { object: RemoteObject.create(object, groupId), type: InjectedScript.CallFrameProxy._scopeTypeNames[type], }; if (name) scope.name = name; if (location) scope.location = location; if (isEmptyObject(object)) scope.empty = true; return scope; } // ------- function CommandLineAPI(callFrame) { let savedResultAlias = InjectedScriptHost.savedResultAlias; let defineGetter = (key, value, wrap) => { if (wrap) { let originalValue = value; value = function() { return originalValue; }; } this.__defineGetter__("$" + key, value); if (savedResultAlias && savedResultAlias !== "$") this.__defineGetter__(savedResultAlias + key, value); }; if ("_lastResult" in injectedScript) defineGetter("_", injectedScript._lastResult, true); if ("_exceptionValue" in injectedScript) defineGetter("exception", injectedScript._exceptionValue, true); if ("_eventValue" in injectedScript) defineGetter("event", injectedScript._eventValue, true); // $1-$99 for (let i = 1; i < injectedScript._savedResults.length; ++i) defineGetter(i, injectedScript._savedResults[i], true); for (let name in CommandLineAPI.getters) defineGetter(name, CommandLineAPI.getters[name]); for (let name in CommandLineAPI.methods) this[name] = CommandLineAPI.methods[name]; } CommandLineAPI.getters = {}; CommandLineAPI.methods = {}; CommandLineAPI.methods["keys"] = function(object) { return Object.keys(object); }; CommandLineAPI.methods["values"] = function(object) { return Object.values(object); }; CommandLineAPI.methods["queryInstances"] = function() { return InjectedScriptHost.queryInstances(...arguments); }; CommandLineAPI.methods["queryObjects"] = function() { return InjectedScriptHost.queryInstances(...arguments); }; CommandLineAPI.methods["queryHolders"] = function() { return InjectedScriptHost.queryHolders(...arguments); }; CommandLineAPI.methods["inspect"] = function(object) { return injectedScript.inspectObject(object); }; CommandLineAPI.methods["assert"] = function() { return inspectedGlobalObject.console.assert(...arguments); }; CommandLineAPI.methods["clear"] = function() { return inspectedGlobalObject.console.clear(...arguments); }; CommandLineAPI.methods["count"] = function() { return inspectedGlobalObject.console.count(...arguments); }; CommandLineAPI.methods["countReset"] = function() { return inspectedGlobalObject.console.countReset(...arguments); }; CommandLineAPI.methods["debug"] = function() { return inspectedGlobalObject.console.debug(...arguments); }; CommandLineAPI.methods["dir"] = function() { return inspectedGlobalObject.console.dir(...arguments); }; CommandLineAPI.methods["dirxml"] = function() { return inspectedGlobalObject.console.dirxml(...arguments); }; CommandLineAPI.methods["error"] = function() { return inspectedGlobalObject.console.error(...arguments); }; CommandLineAPI.methods["group"] = function() { return inspectedGlobalObject.console.group(...arguments); }; CommandLineAPI.methods["groupCollapsed"] = function() { return inspectedGlobalObject.console.groupCollapsed(...arguments); }; CommandLineAPI.methods["groupEnd"] = function() { return inspectedGlobalObject.console.groupEnd(...arguments); }; CommandLineAPI.methods["info"] = function() { return inspectedGlobalObject.console.info(...arguments); }; CommandLineAPI.methods["log"] = function() { return inspectedGlobalObject.console.log(...arguments); }; CommandLineAPI.methods["profile"] = function() { return inspectedGlobalObject.console.profile(...arguments); }; CommandLineAPI.methods["profileEnd"] = function() { return inspectedGlobalObject.console.profileEnd(...arguments); }; CommandLineAPI.methods["record"] = function() { return inspectedGlobalObject.console.record(...arguments); }; CommandLineAPI.methods["recordEnd"] = function() { return inspectedGlobalObject.console.recordEnd(...arguments); }; CommandLineAPI.methods["screenshot"] = function() { return inspectedGlobalObject.console.screenshot(...arguments); }; CommandLineAPI.methods["table"] = function() { return inspectedGlobalObject.console.table(...arguments); }; CommandLineAPI.methods["takeHeapSnapshot"] = function() { return inspectedGlobalObject.console.takeHeapSnapshot(...arguments); }; CommandLineAPI.methods["time"] = function() { return inspectedGlobalObject.console.time(...arguments); }; CommandLineAPI.methods["timeEnd"] = function() { return inspectedGlobalObject.console.timeEnd(...arguments); }; CommandLineAPI.methods["timeLog"] = function() { return inspectedGlobalObject.console.timeLog(...arguments); }; CommandLineAPI.methods["timeStamp"] = function() { return inspectedGlobalObject.console.timeStamp(...arguments); }; CommandLineAPI.methods["trace"] = function() { return inspectedGlobalObject.console.trace(...arguments); }; CommandLineAPI.methods["warn"] = function() { return inspectedGlobalObject.console.warn(...arguments); }; for (let name in CommandLineAPI.methods) CommandLineAPI.methods[name].toString = function() { return "function " + name + "() { [Command Line API] }"; }; return injectedScript; })