2 * Copyright (C) 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //# sourceURL=__WebInspectorInjectedScript__
32 * @param {InjectedScriptHost} InjectedScriptHost
33 * @param {GlobalObject} inspectedGlobalObject
34 * @param {number} injectedScriptId
36 (function (InjectedScriptHost
, inspectedGlobalObject
, injectedScriptId
) {
38 // Protect against Object overwritten by the user code.
39 var Object
= {}.constructor;
44 var InjectedScript = function()
46 this._lastBoundObjectId
= 1;
47 this._idToWrappedObject
= {};
48 this._idToObjectGroupName
= {};
49 this._objectGroups
= {};
54 * @type {Object.<string, boolean>}
57 InjectedScript
.primitiveTypes
= {
64 InjectedScript
.prototype = {
69 isPrimitiveValue: function(object
)
71 // FIXME(33716): typeof document.all is always 'undefined'.
72 return InjectedScript
.primitiveTypes
[typeof object
] && !this._isHTMLAllCollection(object
);
77 * @param {string} groupName
78 * @param {boolean} canAccessInspectedGlobalObject
79 * @param {boolean} generatePreview
80 * @return {!RuntimeAgent.RemoteObject}
82 wrapObject: function(object
, groupName
, canAccessInspectedGlobalObject
, generatePreview
)
84 if (canAccessInspectedGlobalObject
)
85 return this._wrapObject(object
, groupName
, false, generatePreview
);
86 return this._fallbackWrapper(object
);
91 * @return {!RuntimeAgent.RemoteObject}
93 _fallbackWrapper: function(object
)
96 result
.type
= typeof object
;
97 if (this.isPrimitiveValue(object
))
98 result
.value
= object
;
100 result
.description
= this._toString(object
);
101 return /** @type {!RuntimeAgent.RemoteObject} */ (result
);
105 * @param {boolean} canAccessInspectedGlobalObject
106 * @param {Object} table
107 * @param {Array.<string>|string|boolean} columns
108 * @return {!RuntimeAgent.RemoteObject}
110 wrapTable: function(canAccessInspectedGlobalObject
, table
, columns
)
112 if (!canAccessInspectedGlobalObject
)
113 return this._fallbackWrapper(table
);
114 var columnNames
= null;
115 if (typeof columns
=== "string")
117 if (InjectedScriptHost
.type(columns
) == "array") {
119 for (var i
= 0; i
< columns
.length
; ++i
)
120 columnNames
.push(String(columns
[i
]));
122 return this._wrapObject(table
, "console", false, true, columnNames
);
128 inspectObject: function(object
)
130 if (this._commandLineAPIImpl
)
131 this._commandLineAPIImpl
.inspect(object
);
135 * This method cannot throw.
137 * @param {string=} objectGroupName
138 * @param {boolean=} forceValueType
139 * @param {boolean=} generatePreview
140 * @param {?Array.<string>=} columnNames
141 * @return {!RuntimeAgent.RemoteObject}
142 * @suppress {checkTypes}
144 _wrapObject: function(object
, objectGroupName
, forceValueType
, generatePreview
, columnNames
)
147 return new InjectedScript
.RemoteObject(object
, objectGroupName
, forceValueType
, generatePreview
, columnNames
);
150 var description
= injectedScript
._describe(e
);
152 var description
= "<failed to convert exception to string>";
154 return new InjectedScript
.RemoteObject(description
);
160 * @param {string=} objectGroupName
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
;
181 * @param {string} objectId
184 _parseObjectId: function(objectId
)
186 return InjectedScriptHost
.evaluate("(" + objectId
+ ")");
190 * @param {string} objectGroupName
192 releaseObjectGroup: function(objectGroupName
)
194 var group
= this._objectGroups
[objectGroupName
];
197 for (var i
= 0; i
< group
.length
; i
++)
198 this._releaseObject(group
[i
]);
199 delete this._objectGroups
[objectGroupName
];
203 * @param {string} methodName
204 * @param {string} args
207 dispatch: function(methodName
, args
)
209 var argsArray
= InjectedScriptHost
.evaluate("(" + args
+ ")");
210 var result
= this[methodName
].apply(this, argsArray
);
211 if (typeof result
=== "undefined") {
212 if (inspectedGlobalObject
.console
)
213 inspectedGlobalObject
.console
.error("Web Inspector error: InjectedScript.%s returns undefined", methodName
);
219 getProperties: function(objectId
, ownProperties
, ownAndGetterProperties
)
221 var parsedObjectId
= this._parseObjectId(objectId
);
222 var object
= this._objectForId(parsedObjectId
);
223 var objectGroupName
= this._idToObjectGroupName
[parsedObjectId
.id
];
225 if (!this._isDefined(object
))
228 var descriptors
= this._propertyDescriptors(object
, ownProperties
, ownAndGetterProperties
);
230 // Go over properties, wrap object values.
231 for (var i
= 0; i
< descriptors
.length
; ++i
) {
232 var descriptor
= descriptors
[i
];
233 if ("get" in descriptor
)
234 descriptor
.get = this._wrapObject(descriptor
.get, objectGroupName
);
235 if ("set" in descriptor
)
236 descriptor
.set = this._wrapObject(descriptor
.set, objectGroupName
);
237 if ("value" in descriptor
)
238 descriptor
.value
= this._wrapObject(descriptor
.value
, objectGroupName
);
239 if (!("configurable" in descriptor
))
240 descriptor
.configurable
= false;
241 if (!("enumerable" in descriptor
))
242 descriptor
.enumerable
= false;
249 * @param {string} objectId
250 * @return {Array.<Object>|boolean}
252 getInternalProperties: function(objectId
, ownProperties
)
254 var parsedObjectId
= this._parseObjectId(objectId
);
255 var object
= this._objectForId(parsedObjectId
);
256 var objectGroupName
= this._idToObjectGroupName
[parsedObjectId
.id
];
257 if (!this._isDefined(object
))
259 var descriptors
= [];
260 var internalProperties
= InjectedScriptHost
.getInternalProperties(object
);
261 if (internalProperties
) {
262 for (var i
= 0; i
< internalProperties
.length
; i
++) {
263 var property
= internalProperties
[i
];
266 value: this._wrapObject(property
.value
, objectGroupName
)
268 descriptors
.push(descriptor
);
275 * @param {string} functionId
276 * @return {!DebuggerAgent.FunctionDetails|string}
278 getFunctionDetails: function(functionId
)
280 var parsedFunctionId
= this._parseObjectId(functionId
);
281 var func
= this._objectForId(parsedFunctionId
);
282 if (typeof func
!== "function")
283 return "Cannot resolve function by id.";
284 var details
= InjectedScriptHost
.functionDetails(func
);
286 return "Cannot resolve function details.";
287 if ("rawScopes" in details
) {
288 var objectGroupName
= this._idToObjectGroupName
[parsedFunctionId
.id
];
289 var rawScopes
= details
.rawScopes
;
291 delete details
.rawScopes
;
292 for (var i
= 0; i
< rawScopes
.length
; i
++)
293 scopes
.push(InjectedScript
.CallFrameProxy
._createScopeJson(rawScopes
[i
].type
, rawScopes
[i
].object
, objectGroupName
));
294 details
.scopeChain
= scopes
;
300 * @param {string} objectId
302 releaseObject: function(objectId
)
304 var parsedObjectId
= this._parseObjectId(objectId
);
305 this._releaseObject(parsedObjectId
.id
);
311 _releaseObject: function(id
)
313 delete this._idToWrappedObject
[id
];
314 delete this._idToObjectGroupName
[id
];
318 * @param {string} expression
319 * @param {string} objectGroup
320 * @param {boolean} injectCommandLineAPI
321 * @param {boolean} returnByValue
322 * @param {boolean} generatePreview
325 evaluate: function(expression
, objectGroup
, injectCommandLineAPI
, returnByValue
, generatePreview
)
327 return this._evaluateAndWrap(InjectedScriptHost
.evaluate
, InjectedScriptHost
, expression
, objectGroup
, false, injectCommandLineAPI
, returnByValue
, generatePreview
);
331 * @param {string} objectId
332 * @param {string} expression
333 * @param {boolean} returnByValue
334 * @return {Object|string}
336 callFunctionOn: function(objectId
, expression
, args
, returnByValue
)
338 var parsedObjectId
= this._parseObjectId(objectId
);
339 var object
= this._objectForId(parsedObjectId
);
340 if (!this._isDefined(object
))
341 return "Could not find object with given id";
344 var resolvedArgs
= [];
345 args
= InjectedScriptHost
.evaluate(args
);
346 for (var i
= 0; i
< args
.length
; ++i
) {
347 var resolvedCallArgument
;
349 resolvedCallArgument
= this._resolveCallArgument(args
[i
]);
353 resolvedArgs
.push(resolvedCallArgument
)
358 var objectGroup
= this._idToObjectGroupName
[parsedObjectId
.id
];
359 var func
= InjectedScriptHost
.evaluate("(" + expression
+ ")");
360 if (typeof func
!== "function")
361 return "Given expression does not evaluate to a function";
363 return { wasThrown: false,
364 result: this._wrapObject(func
.apply(object
, resolvedArgs
), objectGroup
, returnByValue
) };
366 return this._createThrownValue(e
, objectGroup
);
371 * Resolves a value from CallArgument description.
372 * @param {RuntimeAgent.CallArgument} callArgumentJson
373 * @return {*} resolved value
374 * @throws {string} error message
376 _resolveCallArgument: function(callArgumentJson
) {
377 var objectId
= callArgumentJson
.objectId
;
379 var parsedArgId
= this._parseObjectId(objectId
);
380 if (!parsedArgId
|| parsedArgId
["injectedScriptId"] !== injectedScriptId
)
381 throw "Arguments should belong to the same JavaScript world as the target object.";
383 var resolvedArg
= this._objectForId(parsedArgId
);
384 if (!this._isDefined(resolvedArg
))
385 throw "Could not find object with given id";
388 } else if ("value" in callArgumentJson
)
389 return callArgumentJson
.value
;
395 * @param {Function} evalFunction
396 * @param {Object} object
397 * @param {string} objectGroup
398 * @param {boolean} isEvalOnCallFrame
399 * @param {boolean} injectCommandLineAPI
400 * @param {boolean} returnByValue
401 * @param {boolean} generatePreview
404 _evaluateAndWrap: function(evalFunction
, object
, expression
, objectGroup
, isEvalOnCallFrame
, injectCommandLineAPI
, returnByValue
, generatePreview
)
407 return { wasThrown: false,
408 result: this._wrapObject(this._evaluateOn(evalFunction
, object
, objectGroup
, expression
, isEvalOnCallFrame
, injectCommandLineAPI
), objectGroup
, returnByValue
, generatePreview
) };
410 return this._createThrownValue(e
, objectGroup
);
416 * @param {string} objectGroup
419 _createThrownValue: function(value
, objectGroup
)
421 var remoteObject
= this._wrapObject(value
, objectGroup
);
423 remoteObject
.description
= this._toString(value
);
425 return { wasThrown: true,
426 result: remoteObject
};
430 * @param {Function} evalFunction
431 * @param {Object} object
432 * @param {string} objectGroup
433 * @param {string} expression
434 * @param {boolean} isEvalOnCallFrame
435 * @param {boolean} injectCommandLineAPI
438 _evaluateOn: function(evalFunction
, object
, objectGroup
, expression
, isEvalOnCallFrame
, injectCommandLineAPI
)
440 var commandLineAPI
= null;
441 if (injectCommandLineAPI
) {
442 if (this.CommandLineAPI
)
443 commandLineAPI
= new this.CommandLineAPI(this._commandLineAPIImpl
, isEvalOnCallFrame
? object : null);
445 commandLineAPI
= new BasicCommandLineAPI
;
448 if (isEvalOnCallFrame
) {
449 // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with
450 // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we
451 // create that provides the command line APIs.
453 var parameters
= [InjectedScriptHost
.evaluate
, expression
];
454 var expressionFunctionBody
= "" +
455 "var global = Function('return this')() || (1, eval)('this');" +
456 "var __originalEval = global.eval; global.eval = __eval;" +
457 "try { return eval(__currentExpression); }" +
458 "finally { global.eval = __originalEval; }";
460 if (commandLineAPI
) {
461 // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object)
462 // we instead create a closure where we evaluate the expression. The command line APIs are passed as
463 // parameters to the closure so they are in scope but not injected. This allows the code evaluated in
464 // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing
465 // expressions with 'use strict';.
467 var parameterNames
= Object
.getOwnPropertyNames(commandLineAPI
);
468 for (var i
= 0; i
< parameterNames
.length
; ++i
)
469 parameters
.push(commandLineAPI
[parameterNames
[i
]]);
471 var expressionFunctionString
= "(function(__eval, __currentExpression, " + parameterNames
.join(", ") + ") { " + expressionFunctionBody
+ " })";
473 // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead
474 // of leaking out into the calling scope.
475 var expressionFunctionString
= "(function(__eval, __currentExpression) { " + expressionFunctionBody
+ " })";
478 // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind.
479 var boundExpressionFunctionString
= "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString
+ ", this)";
480 var expressionFunction
= evalFunction
.call(object
, boundExpressionFunctionString
);
481 var result
= expressionFunction
.apply(null, parameters
);
483 if (objectGroup
=== "console")
484 this._lastResult
= result
;
489 // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak
490 // into the global scope. This allow them to stick around between evaluations.
493 if (commandLineAPI
) {
494 if (inspectedGlobalObject
.console
)
495 inspectedGlobalObject
.console
.__commandLineAPI
= commandLineAPI
;
497 inspectedGlobalObject
.__commandLineAPI
= commandLineAPI
;
498 expression
= "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + expression
+ "\n}";
501 var result
= evalFunction
.call(inspectedGlobalObject
, expression
);
503 if (objectGroup
=== "console")
504 this._lastResult
= result
;
508 if (commandLineAPI
) {
509 if (inspectedGlobalObject
.console
)
510 delete inspectedGlobalObject
.console
.__commandLineAPI
;
512 delete inspectedGlobalObject
.__commandLineAPI
;
518 * @param {Object} callFrame
519 * @return {Array.<InjectedScript.CallFrameProxy>|boolean}
521 wrapCallFrames: function(callFrame
)
529 result
.push(new InjectedScript
.CallFrameProxy(depth
++, callFrame
));
530 callFrame
= callFrame
.caller
;
536 * @param {Object} topCallFrame
537 * @param {string} callFrameId
538 * @param {string} expression
539 * @param {string} objectGroup
540 * @param {boolean} injectCommandLineAPI
541 * @param {boolean} returnByValue
542 * @param {boolean} generatePreview
545 evaluateOnCallFrame: function(topCallFrame
, callFrameId
, expression
, objectGroup
, injectCommandLineAPI
, returnByValue
, generatePreview
)
547 var callFrame
= this._callFrameForId(topCallFrame
, callFrameId
);
549 return "Could not find call frame with given id";
550 return this._evaluateAndWrap(callFrame
.evaluate
, callFrame
, expression
, objectGroup
, true, injectCommandLineAPI
, returnByValue
, generatePreview
);
554 * @param {Object} topCallFrame
555 * @param {string} callFrameId
558 _callFrameForId: function(topCallFrame
, callFrameId
)
560 var parsedCallFrameId
= InjectedScriptHost
.evaluate("(" + callFrameId
+ ")");
561 var ordinal
= parsedCallFrameId
["ordinal"];
562 var callFrame
= topCallFrame
;
563 while (--ordinal
>= 0 && callFrame
)
564 callFrame
= callFrame
.caller
;
569 * @param {Object} objectId
572 _objectForId: function(objectId
)
574 return this._idToWrappedObject
[objectId
.id
];
578 * @param {string} objectId
581 findObjectById: function(objectId
)
583 var parsedObjectId
= this._parseObjectId(objectId
);
584 return this._objectForId(parsedObjectId
);
588 * @param {string} name
591 module: function(name
)
593 return this._modules
[name
];
597 * @param {string} name
598 * @param {string} source
601 injectModule: function(name
, source
, host
)
603 delete this._modules
[name
];
604 var moduleFunction
= InjectedScriptHost
.evaluate("(" + source
+ ")");
605 if (typeof moduleFunction
!== "function") {
606 if (inspectedGlobalObject
.console
)
607 inspectedGlobalObject
.console
.error("Web Inspector error: A function was expected for module %s evaluation", name
);
610 var module
= moduleFunction
.call(inspectedGlobalObject
, InjectedScriptHost
, inspectedGlobalObject
, injectedScriptId
, this, host
);
611 this._modules
[name
] = module
;
615 _propertyDescriptors: function(object
, ownProperties
, ownAndGetterProperties
)
618 // - ownProperties - only own properties and __proto__
619 // - ownAndGetterProperties - own properties, __proto__, and getters in the prototype chain
620 // - neither - get all properties in the prototype chain, exclude __proto__
622 var descriptors
= [];
623 var nameProcessed
= {};
624 nameProcessed
["__proto__"] = null;
626 function createFakeValueDescriptor(name
, descriptor
, isOwnProperty
)
629 return {name: name
, value: object
[name
], writable: descriptor
.writable
|| false, configurable: descriptor
.configurable
|| false, enumerable: descriptor
.enumerable
|| false};
631 var errorDescriptor
= {name: name
, value: e
, wasThrown: true};
633 errorDescriptor
.isOwn
= true;
634 return errorDescriptor
;
638 function processDescriptor(descriptor
, isOwnProperty
, possibleNativeBindingGetter
)
640 // Own properties only.
643 descriptors
.push(descriptor
);
647 // Own and getter properties.
648 if (ownAndGetterProperties
) {
650 // Own property, include the descriptor as is.
651 descriptors
.push(descriptor
);
652 } else if (descriptor
.hasOwnProperty("get") && descriptor
.get) {
653 // Getter property in the prototype chain. Create a fake value descriptor.
654 descriptors
.push(createFakeValueDescriptor(descriptor
.name
, descriptor
, isOwnProperty
));
655 } else if (possibleNativeBindingGetter
) {
656 // Possible getter property in the prototype chain.
657 descriptors
.push(descriptor
);
663 descriptors
.push(descriptor
);
666 function processPropertyNames(o
, names
, isOwnProperty
)
668 for (var i
= 0; i
< names
.length
; ++i
) {
670 if (nameProcessed
[name
] || name
=== "__proto__")
673 nameProcessed
[name
] = true;
675 var descriptor
= Object
.getOwnPropertyDescriptor(o
, name
);
677 // FIXME: Bad descriptor. Can we get here?
678 // Fall back to very restrictive settings.
679 var fakeDescriptor
= createFakeValueDescriptor(name
, {writable: false, configurable: false, enumerable: false}, isOwnProperty
);
680 processDescriptor(fakeDescriptor
, isOwnProperty
);
684 if (descriptor
.hasOwnProperty("get") && descriptor
.hasOwnProperty("set") && !descriptor
.get && !descriptor
.set) {
685 // FIXME: <https://webkit.org/b/140575> Web Inspector: Native Bindings Descriptors are Incomplete
686 // Developers may create such a descriptors, so we should be resilient:
687 // var x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
688 var fakeDescriptor
= createFakeValueDescriptor(name
, descriptor
, isOwnProperty
);
689 processDescriptor(fakeDescriptor
, isOwnProperty
, true);
693 descriptor
.name
= name
;
695 descriptor
.isOwn
= true;
696 processDescriptor(descriptor
, isOwnProperty
);
700 // Iterate prototype chain.
701 for (var o
= object
; this._isDefined(o
); o
= o
.__proto__
) {
702 var isOwnProperty
= o
=== object
;
703 processPropertyNames(o
, Object
.getOwnPropertyNames(o
), isOwnProperty
);
708 // Include __proto__ at the end.
710 if (object
.__proto__
)
711 descriptors
.push({name: "__proto__", value: object
.__proto__
, writable: true, configurable: true, enumerable: false, isOwn: true});
721 _isDefined: function(object
)
723 return !!object
|| this._isHTMLAllCollection(object
);
730 _isHTMLAllCollection: function(object
)
732 // document.all is reported as undefined, but we still want to process it.
733 return (typeof object
=== "undefined") && InjectedScriptHost
.isHTMLAllCollection(object
);
737 * @param {Object=} obj
740 _subtype: function(obj
)
745 if (this.isPrimitiveValue(obj
))
748 if (this._isHTMLAllCollection(obj
))
751 var preciseType
= InjectedScriptHost
.type(obj
);
755 // FireBug's array detection.
757 if (typeof obj
.splice
=== "function" && isFinite(obj
.length
))
759 if (Object
.prototype.toString
.call(obj
) === "[object Arguments]" && isFinite(obj
.length
)) // arguments.
764 // If owning frame has navigated to somewhere else window properties will be undefined.
772 _describe: function(obj
)
774 if (this.isPrimitiveValue(obj
))
777 obj
= /** @type {Object} */ (obj
);
779 // Type is object, get subtype.
780 var subtype
= this._subtype(obj
);
782 if (subtype
=== "regexp")
783 return this._toString(obj
);
785 if (subtype
=== "date")
786 return this._toString(obj
);
788 if (subtype
=== "node") {
789 var description
= obj
.nodeName
.toLowerCase();
790 switch (obj
.nodeType
) {
791 case 1 /* Node.ELEMENT_NODE */:
792 description
+= obj
.id
? "#" + obj
.id : "";
793 var className
= obj
.className
;
794 description
+= className
? "." + className : "";
796 case 10 /*Node.DOCUMENT_TYPE_NODE */:
797 description
= "<!DOCTYPE " + description
+ ">";
803 var className
= InjectedScriptHost
.internalConstructorName(obj
);
804 if (subtype
=== "array") {
805 if (typeof obj
.length
=== "number")
806 className
+= "[" + obj
.length
+ "]";
810 // NodeList in JSC is a function, check for array prior to this.
811 if (typeof obj
=== "function")
812 return this._toString(obj
);
814 if (className
=== "Object") {
815 // In Chromium DOM wrapper prototypes will have Object as their constructor name,
816 // get the real DOM wrapper name from the constructor property.
817 var constructorName
= obj
.constructor && obj
.constructor.name
;
819 return constructorName
;
828 _toString: function(obj
)
830 // We don't use String(obj) because inspectedGlobalObject.String is undefined if owning frame navigated to another page.
836 * @type {InjectedScript}
839 var injectedScript
= new InjectedScript();
844 * @param {string=} objectGroupName
845 * @param {boolean=} forceValueType
846 * @param {boolean=} generatePreview
847 * @param {?Array.<string>=} columnNames
849 InjectedScript
.RemoteObject = function(object
, objectGroupName
, forceValueType
, generatePreview
, columnNames
)
851 this.type
= typeof object
;
852 if (injectedScript
.isPrimitiveValue(object
) || object
=== null || forceValueType
) {
853 // We don't send undefined values over JSON.
854 if (typeof object
!== "undefined")
857 // Null object is object with 'null' subtype'
859 this.subtype
= "null";
861 // Provide user-friendly number values.
862 if (typeof object
=== "number")
863 this.description
= object
+ "";
867 object
= /** @type {Object} */ (object
);
869 this.objectId
= injectedScript
._bind(object
, objectGroupName
);
870 var subtype
= injectedScript
._subtype(object
);
872 this.subtype
= subtype
;
873 this.className
= InjectedScriptHost
.internalConstructorName(object
);
874 this.description
= injectedScript
._describe(object
);
876 if (generatePreview
&& (this.type
=== "object" || injectedScript
._isHTMLAllCollection(object
)))
877 this.preview
= this._generatePreview(object
, undefined, columnNames
);
880 InjectedScript
.RemoteObject
.prototype = {
882 * @param {Object} object
883 * @param {Array.<string>=} firstLevelKeys
884 * @param {?Array.<string>=} secondLevelKeys
885 * @return {Object} preview
887 _generatePreview: function(object
, firstLevelKeys
, secondLevelKeys
)
890 preview
.lossless
= true;
891 preview
.overflow
= false;
892 preview
.properties
= [];
894 var isTableRowsRequest
= secondLevelKeys
=== null || secondLevelKeys
;
895 var firstLevelKeysCount
= firstLevelKeys
? firstLevelKeys
.length : 0;
897 var propertiesThreshold
= {
898 properties: isTableRowsRequest
? 1000 : Math
.max(5, firstLevelKeysCount
),
899 indexes: isTableRowsRequest
? 1000 : Math
.max(100, firstLevelKeysCount
)
901 for (var o
= object
; injectedScript
._isDefined(o
); o
= o
.__proto__
)
902 this._generateProtoPreview(o
, preview
, propertiesThreshold
, firstLevelKeys
, secondLevelKeys
);
907 * @param {Object} object
908 * @param {Object} preview
909 * @param {Object} propertiesThreshold
910 * @param {Array.<string>=} firstLevelKeys
911 * @param {Array.<string>=} secondLevelKeys
913 _generateProtoPreview: function(object
, preview
, propertiesThreshold
, firstLevelKeys
, secondLevelKeys
)
915 var propertyNames
= firstLevelKeys
? firstLevelKeys : Object
.keys(/** @type {!Object} */(object
));
917 for (var i
= 0; i
< propertyNames
.length
; ++i
) {
918 if (!propertiesThreshold
.properties
|| !propertiesThreshold
.indexes
) {
919 preview
.overflow
= true;
920 preview
.lossless
= false;
923 var name
= propertyNames
[i
];
924 if (this.subtype
=== "array" && name
=== "length")
927 var descriptor
= Object
.getOwnPropertyDescriptor(/** @type {!Object} */(object
), name
);
928 if (!("value" in descriptor
) || !descriptor
.enumerable
) {
929 preview
.lossless
= false;
933 var value
= descriptor
.value
;
934 if (value
=== null) {
935 this._appendPropertyPreview(preview
, { name: name
, type: "object", value: "null" }, propertiesThreshold
);
939 const maxLength
= 100;
940 var type
= typeof value
;
942 if (InjectedScript
.primitiveTypes
[type
]) {
943 if (type
=== "string") {
944 if (value
.length
> maxLength
) {
945 value
= this._abbreviateString(value
, maxLength
, true);
946 preview
.lossless
= false;
948 value
= value
.replace(/\n/g, "\u21B5");
950 this._appendPropertyPreview(preview
, { name: name
, type: type
, value: value
+ "" }, propertiesThreshold
);
954 if (secondLevelKeys
=== null || secondLevelKeys
) {
955 var subPreview
= this._generatePreview(value
, secondLevelKeys
|| undefined);
956 var property
= { name: name
, type: type
, valuePreview: subPreview
};
957 this._appendPropertyPreview(preview
, property
, propertiesThreshold
);
958 if (!subPreview
.lossless
)
959 preview
.lossless
= false;
960 if (subPreview
.overflow
)
961 preview
.overflow
= true;
965 preview
.lossless
= false;
967 var subtype
= injectedScript
._subtype(value
);
968 var description
= "";
969 if (type
!== "function")
970 description
= this._abbreviateString(/** @type {string} */ (injectedScript
._describe(value
)), maxLength
, subtype
=== "regexp");
972 var property
= { name: name
, type: type
, value: description
};
974 property
.subtype
= subtype
;
975 this._appendPropertyPreview(preview
, property
, propertiesThreshold
);
982 * @param {Object} preview
983 * @param {Object} property
984 * @param {Object} propertiesThreshold
986 _appendPropertyPreview: function(preview
, property
, propertiesThreshold
)
988 if (isNaN(property
.name
))
989 propertiesThreshold
.properties
--;
991 propertiesThreshold
.indexes
--;
992 preview
.properties
.push(property
);
996 * @param {string} string
997 * @param {number} maxLength
998 * @param {boolean=} middle
1001 _abbreviateString: function(string
, maxLength
, middle
)
1003 if (string
.length
<= maxLength
)
1006 var leftHalf
= maxLength
>> 1;
1007 var rightHalf
= maxLength
- leftHalf
- 1;
1008 return string
.substr(0, leftHalf
) + "\u2026" + string
.substr(string
.length
- rightHalf
, rightHalf
);
1010 return string
.substr(0, maxLength
) + "\u2026";
1015 * @param {number} ordinal
1016 * @param {Object} callFrame
1018 InjectedScript
.CallFrameProxy = function(ordinal
, callFrame
)
1020 this.callFrameId
= "{\"ordinal\":" + ordinal
+ ",\"injectedScriptId\":" + injectedScriptId
+ "}";
1021 this.functionName
= (callFrame
.type
=== "function" ? callFrame
.functionName : "");
1022 this.location
= { scriptId: String(callFrame
.sourceID
), lineNumber: callFrame
.line
, columnNumber: callFrame
.column
};
1023 this.scopeChain
= this._wrapScopeChain(callFrame
);
1024 this.this = injectedScript
._wrapObject(callFrame
.thisObject
, "backtrace");
1027 InjectedScript
.CallFrameProxy
.prototype = {
1029 * @param {Object} callFrame
1030 * @return {!Array.<DebuggerAgent.Scope>}
1032 _wrapScopeChain: function(callFrame
)
1034 var scopeChain
= callFrame
.scopeChain
;
1035 var scopeChainProxy
= [];
1036 for (var i
= 0; i
< scopeChain
.length
; i
++) {
1037 var scope
= InjectedScript
.CallFrameProxy
._createScopeJson(callFrame
.scopeType(i
), scopeChain
[i
], "backtrace");
1038 scopeChainProxy
.push(scope
);
1040 return scopeChainProxy
;
1045 * @param {number} scopeTypeCode
1046 * @param {*} scopeObject
1047 * @param {string} groupId
1048 * @return {!DebuggerAgent.Scope}
1050 InjectedScript
.CallFrameProxy
._createScopeJson = function(scopeTypeCode
, scopeObject
, groupId
) {
1051 const GLOBAL_SCOPE
= 0;
1052 const LOCAL_SCOPE
= 1;
1053 const WITH_SCOPE
= 2;
1054 const CLOSURE_SCOPE
= 3;
1055 const CATCH_SCOPE
= 4;
1057 /** @type {!Object.<number, string>} */
1058 var scopeTypeNames
= {};
1059 scopeTypeNames
[GLOBAL_SCOPE
] = "global";
1060 scopeTypeNames
[LOCAL_SCOPE
] = "local";
1061 scopeTypeNames
[WITH_SCOPE
] = "with";
1062 scopeTypeNames
[CLOSURE_SCOPE
] = "closure";
1063 scopeTypeNames
[CATCH_SCOPE
] = "catch";
1066 object: injectedScript
._wrapObject(scopeObject
, groupId
),
1067 type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames
[scopeTypeCode
])
1071 function BasicCommandLineAPI()
1073 this.$_
= injectedScript
._lastResult
;
1076 return injectedScript
;