2 * Copyright (C) 2007, 2014-2015 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;
242 if ("symbol" in descriptor
)
243 descriptor
.symbol
= this._wrapObject(descriptor
.symbol
, objectGroupName
);
249 getProperties: function(objectId
, ownProperties
, generatePreview
)
251 var collectionMode
= ownProperties
? InjectedScript
.CollectionMode
.OwnProperties : InjectedScript
.CollectionMode
.AllProperties
;
252 return this._getProperties(objectId
, collectionMode
, generatePreview
);
255 getDisplayableProperties: function(objectId
, generatePreview
)
257 var collectionMode
= InjectedScript
.CollectionMode
.OwnProperties
| InjectedScript
.CollectionMode
.NativeGetterProperties
;
258 return this._getProperties(objectId
, collectionMode
, generatePreview
);
261 getInternalProperties: function(objectId
, generatePreview
)
263 var parsedObjectId
= this._parseObjectId(objectId
);
264 var object
= this._objectForId(parsedObjectId
);
265 var objectGroupName
= this._idToObjectGroupName
[parsedObjectId
.id
];
267 if (!this._isDefined(object
))
270 if (isSymbol(object
))
273 var descriptors
= this._internalPropertyDescriptors(object
);
277 // Go over properties, wrap object values.
278 for (var i
= 0; i
< descriptors
.length
; ++i
) {
279 var descriptor
= descriptors
[i
];
280 if ("value" in descriptor
)
281 descriptor
.value
= this._wrapObject(descriptor
.value
, objectGroupName
, false, generatePreview
);
287 getCollectionEntries: function(objectId
, objectGroupName
, startIndex
, numberToFetch
)
289 var parsedObjectId
= this._parseObjectId(objectId
);
290 var object
= this._objectForId(parsedObjectId
);
291 var objectGroupName
= objectGroupName
|| this._idToObjectGroupName
[parsedObjectId
.id
];
293 if (!this._isDefined(object
))
296 if (typeof object
!== "object")
299 var entries
= this._entries(object
, InjectedScriptHost
.subtype(object
), startIndex
, numberToFetch
);
300 return entries
.map(function(entry
) {
301 entry
.value
= injectedScript
._wrapObject(entry
.value
, objectGroupName
, false, true);
303 entry
.key
= injectedScript
._wrapObject(entry
.key
, objectGroupName
, false, true);
308 saveResult: function(callArgumentJSON
)
310 this._savedResultIndex
= 0;
313 var callArgument
= InjectedScriptHost
.evaluate("(" + callArgumentJSON
+ ")");
314 var value
= this._resolveCallArgument(callArgument
);
315 this._saveResult(value
);
318 return this._savedResultIndex
;
321 getFunctionDetails: function(functionId
)
323 var parsedFunctionId
= this._parseObjectId(functionId
);
324 var func
= this._objectForId(parsedFunctionId
);
325 if (typeof func
!== "function")
326 return "Cannot resolve function by id.";
327 var details
= InjectedScriptHost
.functionDetails(func
);
329 return "Cannot resolve function details.";
330 if ("rawScopes" in details
) {
331 var objectGroupName
= this._idToObjectGroupName
[parsedFunctionId
.id
];
332 var rawScopes
= details
.rawScopes
;
334 delete details
.rawScopes
;
335 for (var i
= 0; i
< rawScopes
.length
; i
++)
336 scopes
.push(InjectedScript
.CallFrameProxy
._createScopeJson(rawScopes
[i
].type
, rawScopes
[i
].object
, objectGroupName
));
337 details
.scopeChain
= scopes
;
342 releaseObject: function(objectId
)
344 var parsedObjectId
= this._parseObjectId(objectId
);
345 this._releaseObject(parsedObjectId
.id
);
348 _releaseObject: function(id
)
350 delete this._idToWrappedObject
[id
];
351 delete this._idToObjectGroupName
[id
];
354 evaluate: function(expression
, objectGroup
, injectCommandLineAPI
, returnByValue
, generatePreview
, saveResult
)
356 return this._evaluateAndWrap(InjectedScriptHost
.evaluate
, InjectedScriptHost
, expression
, objectGroup
, false, injectCommandLineAPI
, returnByValue
, generatePreview
, saveResult
);
359 callFunctionOn: function(objectId
, expression
, args
, returnByValue
, generatePreview
)
361 var parsedObjectId
= this._parseObjectId(objectId
);
362 var object
= this._objectForId(parsedObjectId
);
363 if (!this._isDefined(object
))
364 return "Could not find object with given id";
367 var resolvedArgs
= [];
368 var callArgs
= InjectedScriptHost
.evaluate(args
);
369 for (var i
= 0; i
< callArgs
.length
; ++i
) {
371 resolvedArgs
[i
] = this._resolveCallArgument(callArgs
[i
]);
379 var objectGroup
= this._idToObjectGroupName
[parsedObjectId
.id
];
380 var func
= InjectedScriptHost
.evaluate("(" + expression
+ ")");
381 if (typeof func
!== "function")
382 return "Given expression does not evaluate to a function";
386 result: this._wrapObject(func
.apply(object
, resolvedArgs
), objectGroup
, returnByValue
, generatePreview
)
389 return this._createThrownValue(e
, objectGroup
);
393 _resolveCallArgument: function(callArgumentJSON
)
395 if ("value" in callArgumentJSON
)
396 return callArgumentJSON
.value
;
398 var objectId
= callArgumentJSON
.objectId
;
400 var parsedArgId
= this._parseObjectId(objectId
);
401 if (!parsedArgId
|| parsedArgId
["injectedScriptId"] !== injectedScriptId
)
402 throw "Arguments should belong to the same JavaScript world as the target object.";
404 var resolvedArg
= this._objectForId(parsedArgId
);
405 if (!this._isDefined(resolvedArg
))
406 throw "Could not find object with given id";
414 _evaluateAndWrap: function(evalFunction
, object
, expression
, objectGroup
, isEvalOnCallFrame
, injectCommandLineAPI
, returnByValue
, generatePreview
, saveResult
)
417 this._savedResultIndex
= 0;
421 result: this._wrapObject(this._evaluateOn(evalFunction
, object
, objectGroup
, expression
, isEvalOnCallFrame
, injectCommandLineAPI
, saveResult
), objectGroup
, returnByValue
, generatePreview
)
424 if (saveResult
&& this._savedResultIndex
)
425 returnObject
.savedResultIndex
= this._savedResultIndex
;
429 return this._createThrownValue(e
, objectGroup
);
433 _createThrownValue: function(value
, objectGroup
)
435 var remoteObject
= this._wrapObject(value
, objectGroup
);
437 remoteObject
.description
= toStringDescription(value
);
445 _evaluateOn: function(evalFunction
, object
, objectGroup
, expression
, isEvalOnCallFrame
, injectCommandLineAPI
, saveResult
)
447 var commandLineAPI
= null;
448 if (injectCommandLineAPI
) {
449 if (this.CommandLineAPI
)
450 commandLineAPI
= new this.CommandLineAPI(this._commandLineAPIImpl
, isEvalOnCallFrame
? object : null);
452 commandLineAPI
= new BasicCommandLineAPI
;
455 if (isEvalOnCallFrame
) {
456 // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with
457 // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we
458 // create that provides the command line APIs.
460 var parameters
= [InjectedScriptHost
.evaluate
, expression
];
461 var expressionFunctionBody
= "" +
462 "var global = Function('return this')() || (1, eval)('this');" +
463 "var __originalEval = global.eval; global.eval = __eval;" +
464 "try { return eval(__currentExpression); }" +
465 "finally { global.eval = __originalEval; }";
467 if (commandLineAPI
) {
468 // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object)
469 // we instead create a closure where we evaluate the expression. The command line APIs are passed as
470 // parameters to the closure so they are in scope but not injected. This allows the code evaluated in
471 // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing
472 // expressions with 'use strict';.
474 var parameterNames
= Object
.getOwnPropertyNames(commandLineAPI
);
475 for (var i
= 0; i
< parameterNames
.length
; ++i
)
476 parameters
.push(commandLineAPI
[parameterNames
[i
]]);
478 var expressionFunctionString
= "(function(__eval, __currentExpression, " + parameterNames
.join(", ") + ") { " + expressionFunctionBody
+ " })";
480 // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead
481 // of leaking out into the calling scope.
482 var expressionFunctionString
= "(function(__eval, __currentExpression) { " + expressionFunctionBody
+ " })";
485 // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind.
486 var boundExpressionFunctionString
= "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString
+ ", this)";
487 var expressionFunction
= evalFunction
.call(object
, boundExpressionFunctionString
);
488 var result
= expressionFunction
.apply(null, parameters
);
490 if (objectGroup
=== "console" && saveResult
)
491 this._saveResult(result
);
496 // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak
497 // into the global scope. This allow them to stick around between evaluations.
500 if (commandLineAPI
) {
501 if (inspectedGlobalObject
.console
)
502 inspectedGlobalObject
.console
.__commandLineAPI
= commandLineAPI
;
504 inspectedGlobalObject
.__commandLineAPI
= commandLineAPI
;
505 expression
= "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + expression
+ "\n}";
508 var result
= evalFunction
.call(inspectedGlobalObject
, expression
);
510 if (objectGroup
=== "console" && saveResult
)
511 this._saveResult(result
);
515 if (commandLineAPI
) {
516 if (inspectedGlobalObject
.console
)
517 delete inspectedGlobalObject
.console
.__commandLineAPI
;
519 delete inspectedGlobalObject
.__commandLineAPI
;
524 wrapCallFrames: function(callFrame
)
532 result
.push(new InjectedScript
.CallFrameProxy(depth
++, callFrame
));
533 callFrame
= callFrame
.caller
;
538 evaluateOnCallFrame: function(topCallFrame
, callFrameId
, expression
, objectGroup
, injectCommandLineAPI
, returnByValue
, generatePreview
, saveResult
)
540 var callFrame
= this._callFrameForId(topCallFrame
, callFrameId
);
542 return "Could not find call frame with given id";
543 return this._evaluateAndWrap(callFrame
.evaluate
, callFrame
, expression
, objectGroup
, true, injectCommandLineAPI
, returnByValue
, generatePreview
, saveResult
);
546 _callFrameForId: function(topCallFrame
, callFrameId
)
548 var parsedCallFrameId
= InjectedScriptHost
.evaluate("(" + callFrameId
+ ")");
549 var ordinal
= parsedCallFrameId
["ordinal"];
550 var callFrame
= topCallFrame
;
551 while (--ordinal
>= 0 && callFrame
)
552 callFrame
= callFrame
.caller
;
556 _objectForId: function(objectId
)
558 return this._idToWrappedObject
[objectId
.id
];
561 findObjectById: function(objectId
)
563 var parsedObjectId
= this._parseObjectId(objectId
);
564 return this._objectForId(parsedObjectId
);
567 module: function(name
)
569 return this._modules
[name
];
572 injectModule: function(name
, source
, host
)
574 delete this._modules
[name
];
576 var moduleFunction
= InjectedScriptHost
.evaluate("(" + source
+ ")");
577 if (typeof moduleFunction
!== "function") {
578 if (inspectedGlobalObject
.console
)
579 inspectedGlobalObject
.console
.error("Web Inspector error: A function was expected for module %s evaluation", name
);
583 var module
= moduleFunction
.call(inspectedGlobalObject
, InjectedScriptHost
, inspectedGlobalObject
, injectedScriptId
, this, host
);
584 this._modules
[name
] = module
;
588 _internalPropertyDescriptors: function(object
, completeDescriptor
)
590 var internalProperties
= InjectedScriptHost
.getInternalProperties(object
);
591 if (!internalProperties
)
594 var descriptors
= [];
595 for (var i
= 0; i
< internalProperties
.length
; i
++) {
596 var property
= internalProperties
[i
];
597 var descriptor
= {name: property
.name
, value: property
.value
};
598 if (completeDescriptor
) {
599 descriptor
.writable
= false;
600 descriptor
.configurable
= false;
601 descriptor
.enumerable
= false;
602 descriptor
.isOwn
= true;
604 descriptors
.push(descriptor
);
609 _propertyDescriptors: function(object
, collectionMode
)
611 var descriptors
= [];
612 var nameProcessed
= new Set
;
614 function createFakeValueDescriptor(name
, symbol
, descriptor
, isOwnProperty
, possibleNativeBindingGetter
)
617 var descriptor
= {name
, value: object
[name
], writable: descriptor
.writable
|| false, configurable: descriptor
.configurable
|| false, enumerable: descriptor
.enumerable
|| false};
618 if (possibleNativeBindingGetter
)
619 descriptor
.nativeGetter
= true;
621 descriptor
.isOwn
= true;
623 descriptor
.symbol
= symbol
;
626 var errorDescriptor
= {name
, value: e
, wasThrown: true};
628 errorDescriptor
.isOwn
= true;
630 errorDescriptor
.symbol
= symbol
;
631 return errorDescriptor
;
635 function processDescriptor(descriptor
, isOwnProperty
, possibleNativeBindingGetter
)
638 if (collectionMode
& InjectedScript
.CollectionMode
.AllProperties
) {
639 descriptors
.push(descriptor
);
644 if (collectionMode
& InjectedScript
.CollectionMode
.OwnProperties
&& isOwnProperty
) {
645 descriptors
.push(descriptor
);
649 // Native Getter properties.
650 if (collectionMode
& InjectedScript
.CollectionMode
.NativeGetterProperties
) {
651 // FIXME: <https://webkit.org/b/140575> Web Inspector: Native Bindings Descriptors are Incomplete
652 // if (descriptor.hasOwnProperty("get") && descriptor.get && isNativeFunction(descriptor.get)) { ... }
654 if (possibleNativeBindingGetter
) {
655 // Possible getter property in the prototype chain.
656 descriptors
.push(descriptor
);
662 function processProperties(o
, properties
, isOwnProperty
)
664 for (var i
= 0; i
< properties
.length
; ++i
) {
665 var property
= properties
[i
];
666 if (nameProcessed
.has(property
) || property
=== "__proto__")
669 nameProcessed
.add(property
);
671 var name
= toString(property
);
672 var symbol
= isSymbol(property
) ? property : null;
674 var descriptor
= Object
.getOwnPropertyDescriptor(o
, property
);
676 // FIXME: Bad descriptor. Can we get here?
677 // Fall back to very restrictive settings.
678 var fakeDescriptor
= createFakeValueDescriptor(name
, symbol
, {writable: false, configurable: false, enumerable: false}, isOwnProperty
);
679 processDescriptor(fakeDescriptor
, isOwnProperty
);
683 if (descriptor
.hasOwnProperty("get") && descriptor
.hasOwnProperty("set") && !descriptor
.get && !descriptor
.set) {
684 // FIXME: <https://webkit.org/b/140575> Web Inspector: Native Bindings Descriptors are Incomplete
685 // Developers may create such a descriptors, so we should be resilient:
686 // var x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
687 var fakeDescriptor
= createFakeValueDescriptor(name
, symbol
, descriptor
, isOwnProperty
, true);
688 processDescriptor(fakeDescriptor
, isOwnProperty
, true);
692 descriptor
.name
= name
;
694 descriptor
.isOwn
= true;
696 descriptor
.symbol
= symbol
;
697 processDescriptor(descriptor
, isOwnProperty
);
701 function arrayIndexPropertyNames(o
, length
)
703 var array
= new Array(length
);
704 for (var i
= 0; i
< length
; ++i
) {
711 // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
712 // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.
713 var isArrayTypeWithLargeLength
= false;
715 isArrayTypeWithLargeLength
= injectedScript
._subtype(object
) === "array" && isFinite(object
.length
) && object
.length
> 100;
718 for (var o
= object
; this._isDefined(o
); o
= o
.__proto__
) {
719 var isOwnProperty
= o
=== object
;
721 if (isArrayTypeWithLargeLength
&& isOwnProperty
)
722 processProperties(o
, arrayIndexPropertyNames(o
, 100), isOwnProperty
);
724 processProperties(o
, Object
.getOwnPropertyNames(o
), isOwnProperty
);
725 if (Object
.getOwnPropertySymbols
)
726 processProperties(o
, Object
.getOwnPropertySymbols(o
), isOwnProperty
);
729 if (collectionMode
=== InjectedScript
.CollectionMode
.OwnProperties
)
733 // Always include __proto__ at the end.
735 if (object
.__proto__
)
736 descriptors
.push({name: "__proto__", value: object
.__proto__
, writable: true, configurable: true, enumerable: false, isOwn: true});
742 _isDefined: function(object
)
744 return !!object
|| this._isHTMLAllCollection(object
);
747 _isHTMLAllCollection: function(object
)
749 // document.all is reported as undefined, but we still want to process it.
750 return (typeof object
=== "undefined") && InjectedScriptHost
.isHTMLAllCollection(object
);
753 _subtype: function(obj
)
758 if (this.isPrimitiveValue(obj
) || isSymbol(obj
))
761 if (this._isHTMLAllCollection(obj
))
764 var preciseType
= InjectedScriptHost
.subtype(obj
);
768 // FireBug's array detection.
770 if (typeof obj
.splice
=== "function" && isFinite(obj
.length
))
778 _nodeDescription: function(node
)
780 var isXMLDocument
= node
.ownerDocument
&& !!node
.ownerDocument
.xmlVersion
;
781 var description
= isXMLDocument
? node
.nodeName : node
.nodeName
.toLowerCase();
783 switch (node
.nodeType
) {
784 case 1: // Node.ELEMENT_NODE
786 description
+= "#" + node
.id
;
787 if (node
.hasAttribute("class")) {
788 // Using .getAttribute() is a workaround for SVG*Element.className returning SVGAnimatedString,
789 // which doesn't have any useful String methods. See <https://webkit.org/b/145363/>.
790 description
+= "." + node
.getAttribute("class").trim().replace(/\s+/g, ".");
799 _classPreview: function(classConstructorValue
)
801 return "class " + classConstructorValue
.name
;
804 _nodePreview: function(node
)
806 var isXMLDocument
= node
.ownerDocument
&& !!node
.ownerDocument
.xmlVersion
;
807 var nodeName
= isXMLDocument
? node
.nodeName : node
.nodeName
.toLowerCase();
809 switch (node
.nodeType
) {
810 case 1: // Node.ELEMENT_NODE
812 return "<" + nodeName
+ " id=\"" + node
.id
+ "\">";
814 return "<" + nodeName
+ " class=\"" + node
.className
+ "\">";
815 if (nodeName
=== "input" && node
.type
)
816 return "<" + nodeName
+ " type=\"" + node
.type
+ "\">";
817 return "<" + nodeName
+ ">";
819 case 3: // Node.TEXT_NODE
820 return nodeName
+ " \"" + node
.nodeValue
+ "\"";
822 case 8: // Node.COMMENT_NODE
823 return "<!--" + node
.nodeValue
+ "-->";
825 case 10: // Node.DOCUMENT_TYPE_NODE
826 return "<!DOCTYPE " + nodeName
+ ">";
833 _describe: function(obj
)
835 if (this.isPrimitiveValue(obj
))
839 return toString(obj
);
841 var subtype
= this._subtype(obj
);
843 if (subtype
=== "regexp")
844 return toString(obj
);
846 if (subtype
=== "date")
847 return toString(obj
);
849 if (subtype
=== "error")
850 return toString(obj
);
852 if (subtype
=== "node")
853 return this._nodeDescription(obj
);
855 var className
= InjectedScriptHost
.internalConstructorName(obj
);
856 if (subtype
=== "array")
859 // NodeList in JSC is a function, check for array prior to this.
860 if (typeof obj
=== "function")
861 return toString(obj
);
863 // If Object, try for a better name from the constructor.
864 if (className
=== "Object") {
865 var constructorName
= obj
.constructor && obj
.constructor.name
;
867 return constructorName
;
873 _getSetEntries: function(object
, skip
, numberToFetch
)
877 for (var value
of object
) {
883 entries
.push({value
});
885 if (numberToFetch
&& entries
.length
=== numberToFetch
)
892 _getMapEntries: function(object
, skip
, numberToFetch
)
896 for (var [key
, value
] of object
) {
902 entries
.push({key
, value
});
904 if (numberToFetch
&& entries
.length
=== numberToFetch
)
911 _getWeakMapEntries: function(object
, numberToFetch
)
913 return InjectedScriptHost
.weakMapEntries(object
, numberToFetch
);
916 _getWeakSetEntries: function(object
, numberToFetch
)
918 return InjectedScriptHost
.weakSetEntries(object
, numberToFetch
);
921 _getIteratorEntries: function(object
, numberToFetch
)
923 return InjectedScriptHost
.iteratorEntries(object
, numberToFetch
);
926 _entries: function(object
, subtype
, startIndex
, numberToFetch
)
928 if (subtype
=== "set")
929 return this._getSetEntries(object
, startIndex
, numberToFetch
);
930 if (subtype
=== "map")
931 return this._getMapEntries(object
, startIndex
, numberToFetch
);
932 if (subtype
=== "weakmap")
933 return this._getWeakMapEntries(object
, numberToFetch
);
934 if (subtype
=== "weakset")
935 return this._getWeakSetEntries(object
, numberToFetch
);
936 if (subtype
=== "iterator")
937 return this._getIteratorEntries(object
, numberToFetch
);
939 throw "unexpected type";
942 _saveResult: function(result
)
944 this._lastResult
= result
;
946 if (result
=== undefined || result
=== null)
949 var existingIndex
= this._savedResults
.indexOf(result
);
950 if (existingIndex
!== -1) {
951 this._savedResultIndex
= existingIndex
;
955 this._savedResultIndex
= this._nextSavedResultIndex
;
956 this._savedResults
[this._nextSavedResultIndex
++] = result
;
958 // $n is limited from $1-$99. $0 is special.
959 if (this._nextSavedResultIndex
>= 100)
960 this._nextSavedResultIndex
= 1;
963 _savedResult: function(index
)
965 return this._savedResults
[index
];
969 var injectedScript
= new InjectedScript
;
972 InjectedScript
.RemoteObject = function(object
, objectGroupName
, forceValueType
, generatePreview
, columnNames
)
974 this.type
= typeof object
;
976 if (this.type
=== "undefined" && injectedScript
._isHTMLAllCollection(object
))
977 this.type
= "object";
979 if (injectedScript
.isPrimitiveValue(object
) || object
=== null || forceValueType
) {
980 // We don't send undefined values over JSON.
981 if (this.type
!== "undefined")
984 // Null object is object with 'null' subtype.
986 this.subtype
= "null";
988 // Provide user-friendly number values.
989 if (this.type
=== "number")
990 this.description
= toStringDescription(object
);
994 this.objectId
= injectedScript
._bind(object
, objectGroupName
);
996 var subtype
= injectedScript
._subtype(object
);
998 this.subtype
= subtype
;
1000 this.className
= InjectedScriptHost
.internalConstructorName(object
);
1001 this.description
= injectedScript
._describe(object
);
1003 if (subtype
=== "array")
1004 this.size
= typeof object
.length
=== "number" ? object
.length : 0;
1005 else if (subtype
=== "set" || subtype
=== "map")
1006 this.size
= object
.size
;
1007 else if (subtype
=== "weakmap")
1008 this.size
= InjectedScriptHost
.weakMapSize(object
);
1009 else if (subtype
=== "weakset")
1010 this.size
= InjectedScriptHost
.weakSetSize(object
);
1011 else if (subtype
=== "class") {
1012 this.classPrototype
= injectedScript
._wrapObject(object
.prototype, objectGroupName
);
1013 this.className
= object
.name
;
1016 if (generatePreview
&& this.type
=== "object")
1017 this.preview
= this._generatePreview(object
, undefined, columnNames
);
1020 InjectedScript
.RemoteObject
.prototype = {
1021 _emptyPreview: function()
1025 description: this.description
|| toString(this.value
),
1030 preview
.subtype
= this.subtype
;
1031 if (this.subtype
!== "null") {
1032 preview
.overflow
= false;
1033 preview
.properties
= [];
1038 preview
.size
= this.size
;
1043 _createObjectPreviewForValue: function(value
)
1045 var remoteObject
= new InjectedScript
.RemoteObject(value
, undefined, false, true, undefined);
1046 if (remoteObject
.objectId
)
1047 injectedScript
.releaseObject(remoteObject
.objectId
);
1048 if (remoteObject
.classPrototype
&& remoteObject
.classPrototype
.objectId
)
1049 injectedScript
.releaseObject(remoteObject
.classPrototype
.objectId
);
1051 return remoteObject
.preview
|| remoteObject
._emptyPreview();
1054 _generatePreview: function(object
, firstLevelKeys
, secondLevelKeys
)
1056 var preview
= this._emptyPreview();
1058 // Primitives just have a value.
1059 if (this.type
!== "object")
1062 var isTableRowsRequest
= secondLevelKeys
=== null || secondLevelKeys
;
1063 var firstLevelKeysCount
= firstLevelKeys
? firstLevelKeys
.length : 0;
1065 var propertiesThreshold
= {
1066 properties: isTableRowsRequest
? 1000 : Math
.max(5, firstLevelKeysCount
),
1067 indexes: isTableRowsRequest
? 1000 : Math
.max(10, firstLevelKeysCount
)
1071 // Maps, Sets, and Iterators have entries.
1072 if (this.subtype
=== "map" || this.subtype
=== "set" || this.subtype
=== "weakmap" || this.subtype
=== "weakset" || this.subtype
=== "iterator")
1073 this._appendEntryPreviews(object
, preview
);
1075 preview
.properties
= [];
1077 // Internal Properties.
1078 var internalPropertyDescriptors
= injectedScript
._internalPropertyDescriptors(object
, true);
1079 if (internalPropertyDescriptors
) {
1080 this._appendPropertyPreviews(preview
, internalPropertyDescriptors
, true, propertiesThreshold
, firstLevelKeys
, secondLevelKeys
);
1081 if (propertiesThreshold
.indexes
< 0 || propertiesThreshold
.properties
< 0)
1085 if (preview
.entries
)
1089 var descriptors
= injectedScript
._propertyDescriptors(object
, InjectedScript
.CollectionMode
.AllProperties
);
1090 this._appendPropertyPreviews(preview
, descriptors
, false, propertiesThreshold
, firstLevelKeys
, secondLevelKeys
);
1091 if (propertiesThreshold
.indexes
< 0 || propertiesThreshold
.properties
< 0)
1094 preview
.lossless
= false;
1100 _appendPropertyPreviews: function(preview
, descriptors
, internal, propertiesThreshold
, firstLevelKeys
, secondLevelKeys
)
1102 for (var descriptor
of descriptors
) {
1104 if (propertiesThreshold
.indexes
< 0 || propertiesThreshold
.properties
< 0)
1107 // Error in descriptor.
1108 if (descriptor
.wasThrown
) {
1109 preview
.lossless
= false;
1113 // Do not show "__proto__" in preview.
1114 var name
= descriptor
.name
;
1115 if (name
=== "__proto__")
1118 // For arrays, only allow indexes.
1119 if (this.subtype
=== "array" && !isUInt32(name
))
1122 // Do not show non-enumerable non-own properties. Special case to allow array indexes that may be on the prototype.
1123 if (!descriptor
.enumerable
&& !descriptor
.isOwn
&& this.subtype
!== "array")
1126 // If we have a filter, only show properties in the filter.
1127 // FIXME: Currently these filters do nothing on the backend.
1128 if (firstLevelKeys
&& !firstLevelKeys
.includes(name
))
1132 if (!("value" in descriptor
)) {
1133 preview
.lossless
= false;
1134 this._appendPropertyPreview(preview
, internal, {name
, type: "accessor"}, propertiesThreshold
);
1139 var value
= descriptor
.value
;
1140 if (value
=== null) {
1141 this._appendPropertyPreview(preview
, internal, {name
, type: "object", subtype: "null", value: "null"}, propertiesThreshold
);
1145 // Ignore non-enumerable functions.
1146 var type
= typeof value
;
1147 if (!descriptor
.enumerable
&& type
=== "function")
1150 // Fix type of document.all.
1151 if (type
=== "undefined" && injectedScript
._isHTMLAllCollection(value
))
1155 const maxLength
= 100;
1156 if (InjectedScript
.primitiveTypes
[type
]) {
1157 if (type
=== "string" && value
.length
> maxLength
) {
1158 value
= this._abbreviateString(value
, maxLength
, true);
1159 preview
.lossless
= false;
1161 this._appendPropertyPreview(preview
, internal, {name
, type
, value: toStringDescription(value
)}, propertiesThreshold
);
1166 if (isSymbol(value
)) {
1167 var symbolString
= toString(value
);
1168 if (symbolString
.length
> maxLength
) {
1169 symbolString
= this._abbreviateString(symbolString
, maxLength
, true);
1170 preview
.lossless
= false;
1172 this._appendPropertyPreview(preview
, internal, {name
, type
, value: symbolString
}, propertiesThreshold
);
1177 var property
= {name
, type
};
1178 var subtype
= injectedScript
._subtype(value
);
1180 property
.subtype
= subtype
;
1183 if ((secondLevelKeys
=== null || secondLevelKeys
) || this._isPreviewableObject(value
)) {
1184 // FIXME: If we want secondLevelKeys filter to continue we would need some refactoring.
1185 var subPreview
= this._createObjectPreviewForValue(value
);
1186 property
.valuePreview
= subPreview
;
1187 if (!subPreview
.lossless
)
1188 preview
.lossless
= false;
1189 if (subPreview
.overflow
)
1190 preview
.overflow
= true;
1192 var description
= "";
1193 if (type
!== "function" || subtype
=== "class") {
1194 var fullDescription
;
1195 if (subtype
=== "class")
1196 fullDescription
= "class " + value
.name
;
1197 else if (subtype
=== "node")
1198 fullDescription
= injectedScript
._nodePreview(value
);
1200 fullDescription
= injectedScript
._describe(value
);
1201 description
= this._abbreviateString(fullDescription
, maxLength
, subtype
=== "regexp");
1203 property
.value
= description
;
1204 preview
.lossless
= false;
1207 this._appendPropertyPreview(preview
, internal, property
, propertiesThreshold
);
1211 _appendPropertyPreview: function(preview
, internal, property
, propertiesThreshold
)
1213 if (toString(property
.name
>>> 0) === property
.name
)
1214 propertiesThreshold
.indexes
--;
1216 propertiesThreshold
.properties
--;
1218 if (propertiesThreshold
.indexes
< 0 || propertiesThreshold
.properties
< 0) {
1219 preview
.overflow
= true;
1220 preview
.lossless
= false;
1225 property
.internal = true;
1227 preview
.properties
.push(property
);
1230 _appendEntryPreviews: function(object
, preview
)
1232 // Fetch 6, but only return 5, so we can tell if we overflowed.
1233 var entries
= injectedScript
._entries(object
, this.subtype
, 0, 6);
1237 if (entries
.length
> 5) {
1239 preview
.overflow
= true;
1240 preview
.lossless
= false;
1243 preview
.entries
= entries
.map(function(entry
) {
1244 entry
.value
= this._createObjectPreviewForValue(entry
.value
);
1246 entry
.key
= this._createObjectPreviewForValue(entry
.key
);
1251 _isPreviewableObject: function(object
)
1253 return this._isPreviewableObjectInternal(object
, new Set
, 1);
1256 _isPreviewableObjectInternal: function(object
, knownObjects
, depth
)
1263 if (injectedScript
.isPrimitiveValue(object
) || isSymbol(object
))
1267 if (object
=== null)
1271 if (knownObjects
.has(object
))
1275 knownObjects
.add(object
);
1277 // Arrays are simple if they have 5 or less simple objects.
1278 var subtype
= injectedScript
._subtype(object
);
1279 if (subtype
=== "array") {
1280 var length
= object
.length
;
1283 for (var i
= 0; i
< length
; ++i
) {
1284 if (!this._isPreviewableObjectInternal(object
[i
], knownObjects
, depth
))
1290 // Not a basic object.
1291 if (object
.__proto__
&& object
.__proto__
.__proto__
)
1294 // Objects are simple if they have 3 or less simple properties.
1295 var ownPropertyNames
= Object
.getOwnPropertyNames(object
);
1296 if (ownPropertyNames
.length
> 3)
1298 for (var propertyName
of ownPropertyNames
) {
1299 if (!this._isPreviewableObjectInternal(object
[propertyName
], knownObjects
, depth
))
1306 _abbreviateString: function(string
, maxLength
, middle
)
1308 if (string
.length
<= maxLength
)
1312 var leftHalf
= maxLength
>> 1;
1313 var rightHalf
= maxLength
- leftHalf
- 1;
1314 return string
.substr(0, leftHalf
) + "\u2026" + string
.substr(string
.length
- rightHalf
, rightHalf
);
1317 return string
.substr(0, maxLength
) + "\u2026";
1321 InjectedScript
.CallFrameProxy = function(ordinal
, callFrame
)
1323 this.callFrameId
= "{\"ordinal\":" + ordinal
+ ",\"injectedScriptId\":" + injectedScriptId
+ "}";
1324 this.functionName
= (callFrame
.type
=== "function" ? callFrame
.functionName : "");
1325 this.location
= {scriptId: String(callFrame
.sourceID
), lineNumber: callFrame
.line
, columnNumber: callFrame
.column
};
1326 this.scopeChain
= this._wrapScopeChain(callFrame
);
1327 this.this = injectedScript
._wrapObject(callFrame
.thisObject
, "backtrace");
1330 InjectedScript
.CallFrameProxy
.prototype = {
1331 _wrapScopeChain: function(callFrame
)
1333 var scopeChain
= callFrame
.scopeChain
;
1334 var scopeChainProxy
= [];
1335 for (var i
= 0; i
< scopeChain
.length
; i
++)
1336 scopeChainProxy
[i
] = InjectedScript
.CallFrameProxy
._createScopeJson(callFrame
.scopeType(i
), scopeChain
[i
], "backtrace");
1337 return scopeChainProxy
;
1341 InjectedScript
.CallFrameProxy
._scopeTypeNames
= {
1342 0: "global", // GLOBAL_SCOPE
1343 1: "local", // LOCAL_SCOPE
1344 2: "with", // WITH_SCOPE
1345 3: "closure", // CLOSURE_SCOPE
1346 4: "catch", // CATCH_SCOPE
1347 5: "functionName", // FUNCTION_NAME_SCOPE
1350 InjectedScript
.CallFrameProxy
._createScopeJson = function(scopeTypeCode
, scopeObject
, groupId
)
1353 object: injectedScript
._wrapObject(scopeObject
, groupId
),
1354 type: InjectedScript
.CallFrameProxy
._scopeTypeNames
[scopeTypeCode
]
1359 function slice(array
, index
)
1362 for (var i
= index
|| 0; i
< array
.length
; ++i
)
1363 result
.push(array
[i
]);
1367 function bind(func
, thisObject
, var_args
)
1369 var args
= slice(arguments
, 2);
1370 return function(var_args
) {
1371 return func
.apply(thisObject
, args
.concat(slice(arguments
)));
1375 function BasicCommandLineAPI()
1377 this.$_
= injectedScript
._lastResult
;
1378 this.$exception
= injectedScript
._exceptionValue
;
1381 for (var i
= 1; i
<= injectedScript
._savedResults
.length
; ++i
) {
1382 var member
= "$" + i
;
1383 if (member
in inspectedGlobalObject
)
1385 this.__defineGetter__("$" + i
, bind(injectedScript
._savedResult
, injectedScript
, i
));
1389 return injectedScript
;