2 * Copyright (C) 2007, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 //# sourceURL=__WebInspectorInjectedScript__
32 (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) {
34 // Protect against Object overwritten by the user code.
35 var Object = {}.constructor;
37 function toString(obj)
42 function toStringDescription(obj)
44 if (obj === 0 && 1 / obj < 0)
50 function isUInt32(obj)
52 if (typeof obj === "number")
53 return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
54 return "" + (obj >>> 0) === obj;
57 function isSymbol(obj)
59 return typeof obj === "symbol";
62 var InjectedScript = function()
64 this._lastBoundObjectId = 1;
65 this._idToWrappedObject = {};
66 this._idToObjectGroupName = {};
67 this._objectGroups = {};
69 this._nextSavedResultIndex = 1;
70 this._savedResults = [];
73 InjectedScript.primitiveTypes = {
80 InjectedScript.CollectionMode = {
81 OwnProperties: 1 << 0, // own properties.
82 NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain.
83 AllProperties: 1 << 2, // all properties in the prototype chain.
86 InjectedScript.prototype = {
87 isPrimitiveValue: function(object)
89 // FIXME(33716): typeof document.all is always 'undefined'.
90 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
93 wrapObject: function(object, groupName, canAccessInspectedGlobalObject, generatePreview)
95 if (canAccessInspectedGlobalObject)
96 return this._wrapObject(object, groupName, false, generatePreview);
97 return this._fallbackWrapper(object);
100 setExceptionValue: function(value)
102 this._exceptionValue = value;
105 clearExceptionValue: function()
107 delete this._exceptionValue;
110 _fallbackWrapper: function(object)
113 result.type = typeof object;
114 if (this.isPrimitiveValue(object))
115 result.value = object;
117 result.description = toString(object);
121 wrapTable: function(canAccessInspectedGlobalObject, table, columns)
123 if (!canAccessInspectedGlobalObject)
124 return this._fallbackWrapper(table);
126 // FIXME: Currently columns are ignored. Instead, the frontend filters all
127 // properties based on the provided column names and in the provided order.
128 // Should we filter here too?
130 var columnNames = null;
131 if (typeof columns === "string")
134 if (InjectedScriptHost.subtype(columns) === "array") {
136 for (var i = 0; i < columns.length; ++i)
137 columnNames.push(toString(columns[i]));
140 return this._wrapObject(table, "console", false, true, columnNames);
143 inspectObject: function(object)
145 if (this._commandLineAPIImpl)
146 this._commandLineAPIImpl.inspect(object);
149 _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
152 return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
155 var description = injectedScript._describe(e);
157 var description = "<failed to convert exception to string>";
159 return new InjectedScript.RemoteObject(description);
163 _bind: function(object, objectGroupName)
165 var id = this._lastBoundObjectId++;
166 this._idToWrappedObject[id] = object;
167 var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
168 if (objectGroupName) {
169 var group = this._objectGroups[objectGroupName];
172 this._objectGroups[objectGroupName] = group;
175 this._idToObjectGroupName[id] = objectGroupName;
180 _parseObjectId: function(objectId)
182 return InjectedScriptHost.evaluate("(" + objectId + ")");
185 releaseObjectGroup: function(objectGroupName)
187 if (objectGroupName === "console") {
188 delete this._lastResult;
189 this._nextSavedResultIndex = 1;
190 this._savedResults = [];
193 var group = this._objectGroups[objectGroupName];
197 for (var i = 0; i < group.length; i++)
198 this._releaseObject(group[i]);
200 delete this._objectGroups[objectGroupName];
203 dispatch: function(methodName, args)
205 var argsArray = InjectedScriptHost.evaluate("(" + args + ")");
206 var result = this[methodName].apply(this, argsArray);
207 if (typeof result === "undefined") {
208 if (inspectedGlobalObject.console)
209 inspectedGlobalObject.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
215 _getProperties: function(objectId, collectionMode, generatePreview)
217 var parsedObjectId = this._parseObjectId(objectId);
218 var object = this._objectForId(parsedObjectId);
219 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
221 if (!this._isDefined(object))
224 if (isSymbol(object))
227 var descriptors = this._propertyDescriptors(object, collectionMode);
229 // Go over properties, wrap object values.
230 for (var i = 0; i < descriptors.length; ++i) {
231 var descriptor = descriptors[i];
232 if ("get" in descriptor)
233 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
234 if ("set" in descriptor)
235 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
236 if ("value" in descriptor)
237 descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
238 if (!("configurable" in descriptor))
239 descriptor.configurable = false;
240 if (!("enumerable" in descriptor))
241 descriptor.enumerable = false;
247 getProperties: function(objectId, ownProperties, generatePreview)
249 var collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties;
250 return this._getProperties(objectId, collectionMode, generatePreview);
253 getDisplayableProperties: function(objectId, generatePreview)
255 var collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties;
256 return this._getProperties(objectId, collectionMode, generatePreview);
259 getInternalProperties: function(objectId, generatePreview)
261 var parsedObjectId = this._parseObjectId(objectId);
262 var object = this._objectForId(parsedObjectId);
263 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
265 if (!this._isDefined(object))
268 if (isSymbol(object))
271 var descriptors = this._internalPropertyDescriptors(object);
275 // Go over properties, wrap object values.
276 for (var i = 0; i < descriptors.length; ++i) {
277 var descriptor = descriptors[i];
278 if ("value" in descriptor)
279 descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
285 getCollectionEntries: function(objectId, objectGroupName, startIndex, numberToFetch)
287 var parsedObjectId = this._parseObjectId(objectId);
288 var object = this._objectForId(parsedObjectId);
289 var objectGroupName = objectGroupName || this._idToObjectGroupName[parsedObjectId.id];
291 if (!this._isDefined(object))
294 if (typeof object !== "object")
297 var entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);
298 return entries.map(function(entry) {
299 entry.value = injectedScript._wrapObject(entry.value, objectGroupName, false, true);
301 entry.key = injectedScript._wrapObject(entry.key, objectGroupName, false, true);
306 saveResult: function(callArgumentJSON)
308 this._savedResultIndex = 0;
311 var callArgument = InjectedScriptHost.evaluate("(" + callArgumentJSON + ")");
312 var value = this._resolveCallArgument(callArgument);
313 this._saveResult(value);
316 return this._savedResultIndex;
319 getFunctionDetails: function(functionId)
321 var parsedFunctionId = this._parseObjectId(functionId);
322 var func = this._objectForId(parsedFunctionId);
323 if (typeof func !== "function")
324 return "Cannot resolve function by id.";
325 var details = InjectedScriptHost.functionDetails(func);
327 return "Cannot resolve function details.";
328 if ("rawScopes" in details) {
329 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
330 var rawScopes = details.rawScopes;
332 delete details.rawScopes;
333 for (var i = 0; i < rawScopes.length; i++)
334 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName));
335 details.scopeChain = scopes;
340 releaseObject: function(objectId)
342 var parsedObjectId = this._parseObjectId(objectId);
343 this._releaseObject(parsedObjectId.id);
346 _releaseObject: function(id)
348 delete this._idToWrappedObject[id];
349 delete this._idToObjectGroupName[id];
352 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
354 return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
357 callFunctionOn: function(objectId, expression, args, returnByValue, generatePreview)
359 var parsedObjectId = this._parseObjectId(objectId);
360 var object = this._objectForId(parsedObjectId);
361 if (!this._isDefined(object))
362 return "Could not find object with given id";
365 var resolvedArgs = [];
366 var callArgs = InjectedScriptHost.evaluate(args);
367 for (var i = 0; i < callArgs.length; ++i) {
369 resolvedArgs[i] = this._resolveCallArgument(callArgs[i]);
377 var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
378 var func = InjectedScriptHost.evaluate("(" + expression + ")");
379 if (typeof func !== "function")
380 return "Given expression does not evaluate to a function";
384 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue, generatePreview)
387 return this._createThrownValue(e, objectGroup);
391 _resolveCallArgument: function(callArgumentJSON)
393 if ("value" in callArgumentJSON)
394 return callArgumentJSON.value;
396 var objectId = callArgumentJSON.objectId;
398 var parsedArgId = this._parseObjectId(objectId);
399 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
400 throw "Arguments should belong to the same JavaScript world as the target object.";
402 var resolvedArg = this._objectForId(parsedArgId);
403 if (!this._isDefined(resolvedArg))
404 throw "Could not find object with given id";
412 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
415 this._savedResultIndex = 0;
419 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult), objectGroup, returnByValue, generatePreview)
422 if (saveResult && this._savedResultIndex)
423 returnObject.savedResultIndex = this._savedResultIndex;
427 return this._createThrownValue(e, objectGroup);
431 _createThrownValue: function(value, objectGroup)
433 var remoteObject = this._wrapObject(value, objectGroup);
435 remoteObject.description = toStringDescription(value);
443 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI, saveResult)
445 var commandLineAPI = null;
446 if (injectCommandLineAPI) {
447 if (this.CommandLineAPI)
448 commandLineAPI = new this.CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
450 commandLineAPI = new BasicCommandLineAPI;
453 if (isEvalOnCallFrame) {
454 // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with
455 // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we
456 // create that provides the command line APIs.
458 var parameters = [InjectedScriptHost.evaluate, expression];
459 var expressionFunctionBody = "" +
460 "var global = Function('return this')() || (1, eval)('this');" +
461 "var __originalEval = global.eval; global.eval = __eval;" +
462 "try { return eval(__currentExpression); }" +
463 "finally { global.eval = __originalEval; }";
465 if (commandLineAPI) {
466 // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object)
467 // we instead create a closure where we evaluate the expression. The command line APIs are passed as
468 // parameters to the closure so they are in scope but not injected. This allows the code evaluated in
469 // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing
470 // expressions with 'use strict';.
472 var parameterNames = Object.getOwnPropertyNames(commandLineAPI);
473 for (var i = 0; i < parameterNames.length; ++i)
474 parameters.push(commandLineAPI[parameterNames[i]]);
476 var expressionFunctionString = "(function(__eval, __currentExpression, " + parameterNames.join(", ") + ") { " + expressionFunctionBody + " })";
478 // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead
479 // of leaking out into the calling scope.
480 var expressionFunctionString = "(function(__eval, __currentExpression) { " + expressionFunctionBody + " })";
483 // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind.
484 var boundExpressionFunctionString = "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString + ", this)";
485 var expressionFunction = evalFunction.call(object, boundExpressionFunctionString);
486 var result = expressionFunction.apply(null, parameters);
488 if (objectGroup === "console" && saveResult)
489 this._saveResult(result);
494 // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak
495 // into the global scope. This allow them to stick around between evaluations.
498 if (commandLineAPI) {
499 if (inspectedGlobalObject.console)
500 inspectedGlobalObject.console.__commandLineAPI = commandLineAPI;
502 inspectedGlobalObject.__commandLineAPI = commandLineAPI;
503 expression = "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + expression + "\n}";
506 var result = evalFunction.call(inspectedGlobalObject, expression);
508 if (objectGroup === "console" && saveResult)
509 this._saveResult(result);
513 if (commandLineAPI) {
514 if (inspectedGlobalObject.console)
515 delete inspectedGlobalObject.console.__commandLineAPI;
517 delete inspectedGlobalObject.__commandLineAPI;
522 wrapCallFrames: function(callFrame)
530 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
531 callFrame = callFrame.caller;
536 evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult)
538 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
540 return "Could not find call frame with given id";
541 return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview, saveResult);
544 _callFrameForId: function(topCallFrame, callFrameId)
546 var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
547 var ordinal = parsedCallFrameId["ordinal"];
548 var callFrame = topCallFrame;
549 while (--ordinal >= 0 && callFrame)
550 callFrame = callFrame.caller;
554 _objectForId: function(objectId)
556 return this._idToWrappedObject[objectId.id];
559 findObjectById: function(objectId)
561 var parsedObjectId = this._parseObjectId(objectId);
562 return this._objectForId(parsedObjectId);
565 module: function(name)
567 return this._modules[name];
570 injectModule: function(name, source, host)
572 delete this._modules[name];
574 var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
575 if (typeof moduleFunction !== "function") {
576 if (inspectedGlobalObject.console)
577 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
581 var module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, host);
582 this._modules[name] = module;
586 _internalPropertyDescriptors: function(object, completeDescriptor)
588 var internalProperties = InjectedScriptHost.getInternalProperties(object);
589 if (!internalProperties)
592 var descriptors = [];
593 for (var i = 0; i < internalProperties.length; i++) {
594 var property = internalProperties[i];
595 var descriptor = {name: property.name, value: property.value};
596 if (completeDescriptor) {
597 descriptor.writable = false;
598 descriptor.configurable = false;
599 descriptor.enumerable = false;
600 descriptor.isOwn = true;
602 descriptors.push(descriptor);
607 _propertyDescriptors: function(object, collectionMode)
609 var descriptors = [];
610 var nameProcessed = {};
611 nameProcessed["__proto__"] = null;
613 function createFakeValueDescriptor(name, descriptor, isOwnProperty, possibleNativeBindingGetter)
616 var descriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
617 if (possibleNativeBindingGetter)
618 descriptor.nativeGetter = true;
621 var errorDescriptor = {name, value: e, wasThrown: true};
623 errorDescriptor.isOwn = true;
624 return errorDescriptor;
628 function processDescriptor(descriptor, isOwnProperty, possibleNativeBindingGetter)
631 if (collectionMode & InjectedScript.CollectionMode.AllProperties) {
632 descriptors.push(descriptor);
637 if (collectionMode & InjectedScript.CollectionMode.OwnProperties && isOwnProperty) {
638 descriptors.push(descriptor);
642 // Native Getter properties.
643 if (collectionMode & InjectedScript.CollectionMode.NativeGetterProperties) {
644 // FIXME: <https://webkit.org/b/140575> Web Inspector: Native Bindings Descriptors are Incomplete
645 // if (descriptor.hasOwnProperty("get") && descriptor.get && isNativeFunction(descriptor.get)) { ... }
647 if (possibleNativeBindingGetter) {
648 // Possible getter property in the prototype chain.
649 descriptors.push(descriptor);
655 function processPropertyNames(o, names, isOwnProperty)
657 for (var i = 0; i < names.length; ++i) {
659 if (nameProcessed[name] || name === "__proto__")
662 nameProcessed[name] = true;
664 var descriptor = Object.getOwnPropertyDescriptor(o, name);
666 // FIXME: Bad descriptor. Can we get here?
667 // Fall back to very restrictive settings.
668 var fakeDescriptor = createFakeValueDescriptor(name, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
669 processDescriptor(fakeDescriptor, isOwnProperty);
673 if (descriptor.hasOwnProperty("get") && descriptor.hasOwnProperty("set") && !descriptor.get && !descriptor.set) {
674 // FIXME: <https://webkit.org/b/140575> Web Inspector: Native Bindings Descriptors are Incomplete
675 // Developers may create such a descriptors, so we should be resilient:
676 // var x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
677 var fakeDescriptor = createFakeValueDescriptor(name, descriptor, isOwnProperty, true);
678 processDescriptor(fakeDescriptor, isOwnProperty, true);
682 descriptor.name = name;
684 descriptor.isOwn = true;
685 processDescriptor(descriptor, isOwnProperty);
689 // Iterate prototype chain.
690 for (var o = object; this._isDefined(o); o = o.__proto__) {
691 var isOwnProperty = o === object;
692 processPropertyNames(o, Object.getOwnPropertyNames(o), isOwnProperty);
693 if (collectionMode === InjectedScript.CollectionMode.OwnProperties)
697 // Always include __proto__ at the end.
699 if (object.__proto__)
700 descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
706 _isDefined: function(object)
708 return !!object || this._isHTMLAllCollection(object);
711 _isHTMLAllCollection: function(object)
713 // document.all is reported as undefined, but we still want to process it.
714 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
717 _subtype: function(obj)
722 if (this.isPrimitiveValue(obj) || isSymbol(obj))
725 if (this._isHTMLAllCollection(obj))
728 var preciseType = InjectedScriptHost.subtype(obj);
732 // FireBug's array detection.
734 if (typeof obj.splice === "function" && isFinite(obj.length))
736 if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments.
744 _describe: function(obj)
746 if (this.isPrimitiveValue(obj))
750 return toString(obj);
752 var subtype = this._subtype(obj);
754 if (subtype === "regexp")
755 return toString(obj);
757 if (subtype === "date")
758 return toString(obj);
760 if (subtype === "error")
761 return toString(obj);
763 if (subtype === "node") {
764 var description = obj.nodeName.toLowerCase();
765 switch (obj.nodeType) {
766 case 1 /* Node.ELEMENT_NODE */:
767 description += obj.id ? "#" + obj.id : "";
768 var className = obj.className;
769 description += (className && typeof className === "string") ? "." + className.trim().replace(/\s+/g, ".") : "";
771 case 10 /*Node.DOCUMENT_TYPE_NODE */:
772 description = "<!DOCTYPE " + description + ">";
778 var className = InjectedScriptHost.internalConstructorName(obj);
779 if (subtype === "array")
782 if (subtype === "class")
785 // NodeList in JSC is a function, check for array prior to this.
786 if (typeof obj === "function")
787 return toString(obj);
789 // If Object, try for a better name from the constructor.
790 if (className === "Object") {
791 var constructorName = obj.constructor && obj.constructor.name;
793 return constructorName;
799 _getSetEntries: function(object, skip, numberToFetch)
803 for (var value of object) {
809 entries.push({value});
811 if (numberToFetch && entries.length === numberToFetch)
818 _getMapEntries: function(object, skip, numberToFetch)
822 for (var [key, value] of object) {
828 entries.push({key, value});
830 if (numberToFetch && entries.length === numberToFetch)
837 _getWeakMapEntries: function(object, numberToFetch)
839 return InjectedScriptHost.weakMapEntries(object, numberToFetch);
842 _getIteratorEntries: function(object, numberToFetch)
844 return InjectedScriptHost.iteratorEntries(object, numberToFetch);
847 _entries: function(object, subtype, startIndex, numberToFetch)
849 if (subtype === "set")
850 return this._getSetEntries(object, startIndex, numberToFetch);
851 if (subtype === "map")
852 return this._getMapEntries(object, startIndex, numberToFetch);
853 if (subtype === "weakmap")
854 return this._getWeakMapEntries(object, numberToFetch);
855 if (subtype === "iterator")
856 return this._getIteratorEntries(object, numberToFetch);
858 throw "unexpected type";
861 _saveResult: function(result)
863 this._lastResult = result;
865 if (result === undefined || result === null)
868 var existingIndex = this._savedResults.indexOf(result);
869 if (existingIndex !== -1) {
870 this._savedResultIndex = existingIndex;
874 this._savedResultIndex = this._nextSavedResultIndex;
875 this._savedResults[this._nextSavedResultIndex++] = result;
877 // $n is limited from $1-$99. $0 is special.
878 if (this._nextSavedResultIndex >= 100)
879 this._nextSavedResultIndex = 1;
882 _savedResult: function(index)
884 return this._savedResults[index];
888 var injectedScript = new InjectedScript;
891 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
893 this.type = typeof object;
895 if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object))
896 this.type = "object";
898 if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
899 // We don't send undefined values over JSON.
900 if (this.type !== "undefined")
903 // Null object is object with 'null' subtype.
905 this.subtype = "null";
907 // Provide user-friendly number values.
908 if (this.type === "number")
909 this.description = toStringDescription(object);
913 this.objectId = injectedScript._bind(object, objectGroupName);
915 var subtype = injectedScript._subtype(object);
917 this.subtype = subtype;
919 this.className = InjectedScriptHost.internalConstructorName(object);
920 this.description = injectedScript._describe(object);
922 if (subtype === "array")
923 this.size = typeof object.length === "number" ? object.length : 0;
924 else if (subtype === "set" || subtype === "map")
925 this.size = object.size;
926 else if (subtype === "weakmap")
927 this.size = InjectedScriptHost.weakMapSize(object);
928 else if (subtype === "class")
929 this.classPrototype = injectedScript._wrapObject(object.prototype, objectGroupName);
931 if (generatePreview && this.type === "object")
932 this.preview = this._generatePreview(object, undefined, columnNames);
935 InjectedScript.RemoteObject.prototype = {
936 _emptyPreview: function()
940 description: this.description || toString(this.value),
945 preview.subtype = this.subtype;
946 if (this.subtype !== "null") {
947 preview.overflow = false;
948 preview.properties = [];
953 preview.size = this.size;
958 _createObjectPreviewForValue: function(value)
960 var remoteObject = new InjectedScript.RemoteObject(value, undefined, false, true, undefined);
961 if (remoteObject.objectId)
962 injectedScript.releaseObject(remoteObject.objectId);
963 if (remoteObject.classPrototype && remoteObject.classPrototype.objectId)
964 injectedScript.releaseObject(remoteObject.classPrototype.objectId);
966 return remoteObject.preview || remoteObject._emptyPreview();
969 _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
971 var preview = this._emptyPreview();
973 // Primitives just have a value.
974 if (this.type !== "object")
977 var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
978 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
980 var propertiesThreshold = {
981 properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
982 indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount)
986 // Maps, Sets, and Iterators have entries.
987 if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "iterator")
988 this._appendEntryPreviews(object, preview);
990 preview.properties = [];
992 // Internal Properties.
993 var internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true);
994 if (internalPropertyDescriptors) {
995 this._appendPropertyPreviews(preview, internalPropertyDescriptors, true, propertiesThreshold, firstLevelKeys, secondLevelKeys);
996 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1000 if (preview.entries)
1004 var descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties);
1005 this._appendPropertyPreviews(preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
1006 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1009 preview.lossless = false;
1015 _appendPropertyPreviews: function(preview, descriptors, internal, propertiesThreshold, firstLevelKeys, secondLevelKeys)
1017 for (var descriptor of descriptors) {
1019 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1022 // Error in descriptor.
1023 if (descriptor.wasThrown) {
1024 preview.lossless = false;
1028 // Do not show "__proto__" in preview.
1029 var name = descriptor.name;
1030 if (name === "__proto__")
1033 // Do not show "length" on array like objects in preview.
1034 if (this.subtype === "array" && name === "length")
1037 // Do not show non-enumerable non-own properties. Special case to allow array indexes that may be on the prototype.
1038 if (!descriptor.enumerable && !descriptor.isOwn && !(this.subtype === "array" && isUInt32(name)))
1041 // If we have a filter, only show properties in the filter.
1042 if (firstLevelKeys && firstLevelKeys.indexOf(name) === -1)
1046 if (!("value" in descriptor)) {
1047 preview.lossless = false;
1048 this._appendPropertyPreview(preview, internal, {name, type: "accessor"}, propertiesThreshold);
1053 var value = descriptor.value;
1054 if (value === null) {
1055 this._appendPropertyPreview(preview, internal, {name, type: "object", subtype: "null", value: "null"}, propertiesThreshold);
1059 // Ignore non-enumerable functions.
1060 var type = typeof value;
1061 if (!descriptor.enumerable && type === "function")
1064 // Fix type of document.all.
1065 if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
1069 const maxLength = 100;
1070 if (InjectedScript.primitiveTypes[type]) {
1071 if (type === "string" && value.length > maxLength) {
1072 value = this._abbreviateString(value, maxLength, true);
1073 preview.lossless = false;
1075 this._appendPropertyPreview(preview, internal, {name, type, value: toStringDescription(value)}, propertiesThreshold);
1080 if (isSymbol(value)) {
1081 var symbolString = toString(value);
1082 if (symbolString.length > maxLength) {
1083 symbolString = this._abbreviateString(symbolString, maxLength, true);
1084 preview.lossless = false;
1086 this._appendPropertyPreview(preview, internal, {name, type, value: symbolString}, propertiesThreshold);
1091 var property = {name, type};
1092 var subtype = injectedScript._subtype(value);
1094 property.subtype = subtype;
1097 if (secondLevelKeys === null || secondLevelKeys) {
1098 var subPreview = this._generatePreview(value, secondLevelKeys || undefined, undefined);
1099 property.valuePreview = subPreview;
1100 if (!subPreview.lossless)
1101 preview.lossless = false;
1102 if (subPreview.overflow)
1103 preview.overflow = true;
1104 } else if (this._isPreviewableObject(value)) {
1105 var subPreview = this._createObjectPreviewForValue(value);
1106 property.valuePreview = subPreview;
1107 if (!subPreview.lossless)
1108 preview.lossless = false;
1109 if (subPreview.overflow)
1110 preview.overflow = true;
1112 var description = "";
1113 if (type !== "function" || subtype === "class")
1114 description = this._abbreviateString(injectedScript._describe(value), maxLength, subtype === "regexp");
1115 property.value = description;
1116 preview.lossless = false;
1119 this._appendPropertyPreview(preview, internal, property, propertiesThreshold);
1123 _appendPropertyPreview: function(preview, internal, property, propertiesThreshold)
1125 if (toString(property.name >>> 0) === property.name)
1126 propertiesThreshold.indexes--;
1128 propertiesThreshold.properties--;
1130 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
1131 preview.overflow = true;
1132 preview.lossless = false;
1137 property.internal = true;
1139 preview.properties.push(property);
1142 _appendEntryPreviews: function(object, preview)
1144 // Fetch 6, but only return 5, so we can tell if we overflowed.
1145 var entries = injectedScript._entries(object, this.subtype, 0, 6);
1149 if (entries.length > 5) {
1151 preview.overflow = true;
1152 preview.lossless = false;
1155 preview.entries = entries.map(function(entry) {
1156 entry.value = this._createObjectPreviewForValue(entry.value);
1158 entry.key = this._createObjectPreviewForValue(entry.key);
1163 _isPreviewableObject: function(object)
1165 return this._isPreviewableObjectInternal(object, new Set, 1);
1168 _isPreviewableObjectInternal: function(object, knownObjects, depth)
1175 if (injectedScript.isPrimitiveValue(object) || isSymbol(object))
1179 if (object === null)
1183 if (knownObjects.has(object))
1187 knownObjects.add(object);
1189 // Arrays are simple if they have 5 or less simple objects.
1190 var subtype = injectedScript._subtype(object);
1191 if (subtype === "array") {
1192 var length = object.length;
1195 for (var i = 0; i < length; ++i) {
1196 if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth))
1202 // Not a basic object.
1203 if (object.__proto__ && object.__proto__.__proto__)
1206 // Objects are simple if they have 3 or less simple properties.
1207 var ownPropertyNames = Object.getOwnPropertyNames(object);
1208 if (ownPropertyNames.length > 3)
1210 for (var propertyName of ownPropertyNames) {
1211 if (!this._isPreviewableObjectInternal(object[propertyName], knownObjects, depth))
1218 _abbreviateString: function(string, maxLength, middle)
1220 if (string.length <= maxLength)
1224 var leftHalf = maxLength >> 1;
1225 var rightHalf = maxLength - leftHalf - 1;
1226 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1229 return string.substr(0, maxLength) + "\u2026";
1233 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
1235 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
1236 this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
1237 this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column};
1238 this.scopeChain = this._wrapScopeChain(callFrame);
1239 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
1242 InjectedScript.CallFrameProxy.prototype = {
1243 _wrapScopeChain: function(callFrame)
1245 var scopeChain = callFrame.scopeChain;
1246 var scopeChainProxy = [];
1247 for (var i = 0; i < scopeChain.length; i++)
1248 scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
1249 return scopeChainProxy;
1253 InjectedScript.CallFrameProxy._scopeTypeNames = {
1254 0: "global", // GLOBAL_SCOPE
1255 1: "local", // LOCAL_SCOPE
1256 2: "with", // WITH_SCOPE
1257 3: "closure", // CLOSURE_SCOPE
1258 4: "catch", // CATCH_SCOPE
1259 5: "functionName", // FUNCTION_NAME_SCOPE
1262 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId)
1265 object: injectedScript._wrapObject(scopeObject, groupId),
1266 type: InjectedScript.CallFrameProxy._scopeTypeNames[scopeTypeCode]
1271 function slice(array, index)
1274 for (var i = index || 0; i < array.length; ++i)
1275 result.push(array[i]);
1279 function bind(func, thisObject, var_args)
1281 var args = slice(arguments, 2);
1282 return function(var_args) {
1283 return func.apply(thisObject, args.concat(slice(arguments)));
1287 function BasicCommandLineAPI()
1289 this.$_ = injectedScript._lastResult;
1290 this.$exception = injectedScript._exceptionValue;
1293 for (var i = 1; i <= injectedScript._savedResults.length; ++i) {
1294 var member = "$" + i;
1295 if (member in inspectedGlobalObject)
1297 this.__defineGetter__("$" + i, bind(injectedScript._savedResult, injectedScript, i));
1301 return injectedScript;