]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/InjectedScriptSource.js
JavaScriptCore-7600.1.4.13.1.tar.gz
[apple/javascriptcore.git] / inspector / InjectedScriptSource.js
1 /*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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.
16 *
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.
27 */
28
29 //# sourceURL=__WebInspectorInjectedScript__
30
31 /**
32 * @param {InjectedScriptHost} InjectedScriptHost
33 * @param {GlobalObject} inspectedGlobalObject
34 * @param {number} injectedScriptId
35 */
36 (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) {
37
38 // Protect against Object overwritten by the user code.
39 var Object = {}.constructor;
40
41 /**
42 * @constructor
43 */
44 var InjectedScript = function()
45 {
46 this._lastBoundObjectId = 1;
47 this._idToWrappedObject = {};
48 this._idToObjectGroupName = {};
49 this._objectGroups = {};
50 this._modules = {};
51 }
52
53 /**
54 * @type {Object.<string, boolean>}
55 * @const
56 */
57 InjectedScript.primitiveTypes = {
58 undefined: true,
59 boolean: true,
60 number: true,
61 string: true
62 }
63
64 InjectedScript.prototype = {
65 /**
66 * @param {*} object
67 * @return {boolean}
68 */
69 isPrimitiveValue: function(object)
70 {
71 // FIXME(33716): typeof document.all is always 'undefined'.
72 return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
73 },
74
75 /**
76 * @param {*} object
77 * @param {string} groupName
78 * @param {boolean} canAccessInspectedGlobalObject
79 * @param {boolean} generatePreview
80 * @return {!RuntimeAgent.RemoteObject}
81 */
82 wrapObject: function(object, groupName, canAccessInspectedGlobalObject, generatePreview)
83 {
84 if (canAccessInspectedGlobalObject)
85 return this._wrapObject(object, groupName, false, generatePreview);
86 return this._fallbackWrapper(object);
87 },
88
89 /**
90 * @param {*} object
91 * @return {!RuntimeAgent.RemoteObject}
92 */
93 _fallbackWrapper: function(object)
94 {
95 var result = {};
96 result.type = typeof object;
97 if (this.isPrimitiveValue(object))
98 result.value = object;
99 else
100 result.description = this._toString(object);
101 return /** @type {!RuntimeAgent.RemoteObject} */ (result);
102 },
103
104 /**
105 * @param {boolean} canAccessInspectedGlobalObject
106 * @param {Object} table
107 * @param {Array.<string>|string|boolean} columns
108 * @return {!RuntimeAgent.RemoteObject}
109 */
110 wrapTable: function(canAccessInspectedGlobalObject, table, columns)
111 {
112 if (!canAccessInspectedGlobalObject)
113 return this._fallbackWrapper(table);
114 var columnNames = null;
115 if (typeof columns === "string")
116 columns = [columns];
117 if (InjectedScriptHost.type(columns) == "array") {
118 columnNames = [];
119 for (var i = 0; i < columns.length; ++i)
120 columnNames.push(String(columns[i]));
121 }
122 return this._wrapObject(table, "console", false, true, columnNames);
123 },
124
125 /**
126 * @param {*} object
127 */
128 inspectObject: function(object)
129 {
130 if (this._commandLineAPIImpl)
131 this._commandLineAPIImpl.inspect(object);
132 },
133
134 /**
135 * This method cannot throw.
136 * @param {*} object
137 * @param {string=} objectGroupName
138 * @param {boolean=} forceValueType
139 * @param {boolean=} generatePreview
140 * @param {?Array.<string>=} columnNames
141 * @return {!RuntimeAgent.RemoteObject}
142 * @suppress {checkTypes}
143 */
144 _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames)
145 {
146 try {
147 return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
148 } catch (e) {
149 try {
150 var description = injectedScript._describe(e);
151 } catch (ex) {
152 var description = "<failed to convert exception to string>";
153 }
154 return new InjectedScript.RemoteObject(description);
155 }
156 },
157
158 /**
159 * @param {*} object
160 * @param {string=} objectGroupName
161 * @return {string}
162 */
163 _bind: function(object, objectGroupName)
164 {
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];
170 if (!group) {
171 group = [];
172 this._objectGroups[objectGroupName] = group;
173 }
174 group.push(id);
175 this._idToObjectGroupName[id] = objectGroupName;
176 }
177 return objectId;
178 },
179
180 /**
181 * @param {string} objectId
182 * @return {Object}
183 */
184 _parseObjectId: function(objectId)
185 {
186 return InjectedScriptHost.evaluate("(" + objectId + ")");
187 },
188
189 /**
190 * @param {string} objectGroupName
191 */
192 releaseObjectGroup: function(objectGroupName)
193 {
194 var group = this._objectGroups[objectGroupName];
195 if (!group)
196 return;
197 for (var i = 0; i < group.length; i++)
198 this._releaseObject(group[i]);
199 delete this._objectGroups[objectGroupName];
200 },
201
202 /**
203 * @param {string} methodName
204 * @param {string} args
205 * @return {*}
206 */
207 dispatch: function(methodName, args)
208 {
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);
214 result = null;
215 }
216 return result;
217 },
218
219 /**
220 * @param {string} objectId
221 * @param {boolean} ownProperties
222 * @return {Array.<RuntimeAgent.PropertyDescriptor>|boolean}
223 */
224 getProperties: function(objectId, ownProperties)
225 {
226 var parsedObjectId = this._parseObjectId(objectId);
227 var object = this._objectForId(parsedObjectId);
228 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
229
230 if (!this._isDefined(object))
231 return false;
232 var descriptors = this._propertyDescriptors(object, ownProperties);
233
234 // Go over properties, wrap object values.
235 for (var i = 0; i < descriptors.length; ++i) {
236 var descriptor = descriptors[i];
237 if ("get" in descriptor)
238 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
239 if ("set" in descriptor)
240 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
241 if ("value" in descriptor)
242 descriptor.value = this._wrapObject(descriptor.value, objectGroupName);
243 if (!("configurable" in descriptor))
244 descriptor.configurable = false;
245 if (!("enumerable" in descriptor))
246 descriptor.enumerable = false;
247 }
248 return descriptors;
249 },
250
251 /**
252 * @param {string} objectId
253 * @return {Array.<Object>|boolean}
254 */
255 getInternalProperties: function(objectId, ownProperties)
256 {
257 var parsedObjectId = this._parseObjectId(objectId);
258 var object = this._objectForId(parsedObjectId);
259 var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
260 if (!this._isDefined(object))
261 return false;
262 var descriptors = [];
263 var internalProperties = InjectedScriptHost.getInternalProperties(object);
264 if (internalProperties) {
265 for (var i = 0; i < internalProperties.length; i++) {
266 var property = internalProperties[i];
267 var descriptor = {
268 name: property.name,
269 value: this._wrapObject(property.value, objectGroupName)
270 };
271 descriptors.push(descriptor);
272 }
273 }
274 return descriptors;
275 },
276
277 /**
278 * @param {string} functionId
279 * @return {!DebuggerAgent.FunctionDetails|string}
280 */
281 getFunctionDetails: function(functionId)
282 {
283 var parsedFunctionId = this._parseObjectId(functionId);
284 var func = this._objectForId(parsedFunctionId);
285 if (typeof func !== "function")
286 return "Cannot resolve function by id.";
287 var details = InjectedScriptHost.functionDetails(func);
288 if (!details)
289 return "Cannot resolve function details.";
290 if ("rawScopes" in details) {
291 var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
292 var rawScopes = details.rawScopes;
293 var scopes = [];
294 delete details.rawScopes;
295 for (var i = 0; i < rawScopes.length; i++)
296 scopes.push(InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName));
297 details.scopeChain = scopes;
298 }
299 return details;
300 },
301
302 /**
303 * @param {string} objectId
304 */
305 releaseObject: function(objectId)
306 {
307 var parsedObjectId = this._parseObjectId(objectId);
308 this._releaseObject(parsedObjectId.id);
309 },
310
311 /**
312 * @param {string} id
313 */
314 _releaseObject: function(id)
315 {
316 delete this._idToWrappedObject[id];
317 delete this._idToObjectGroupName[id];
318 },
319
320 /**
321 * @param {Object} object
322 * @param {boolean} ownProperties
323 * @return {Array.<Object>}
324 */
325 _propertyDescriptors: function(object, ownProperties)
326 {
327 var descriptors = [];
328 var nameProcessed = {};
329 nameProcessed["__proto__"] = null;
330 for (var o = object; this._isDefined(o); o = o.__proto__) {
331 var names = Object.getOwnPropertyNames(/** @type {!Object} */ (o));
332 for (var i = 0; i < names.length; ++i) {
333 var name = names[i];
334 if (nameProcessed[name])
335 continue;
336
337 try {
338 nameProcessed[name] = true;
339 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */ (object), name);
340 if (!descriptor) {
341 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
342 try {
343 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false};
344 if (o === object)
345 descriptor.isOwn = true;
346 descriptors.push(descriptor);
347 } catch (e) {
348 // Silent catch.
349 }
350 continue;
351 }
352 if (descriptor.hasOwnProperty("get") && descriptor.hasOwnProperty("set") && !descriptor.get && !descriptor.set) {
353 // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
354 try {
355 descriptor = { name: name, value: object[name], writable: false, configurable: false, enumerable: false};
356 if (o === object)
357 descriptor.isOwn = true;
358 descriptors.push(descriptor);
359 } catch (e) {
360 // Silent catch.
361 }
362 continue;
363 }
364 } catch (e) {
365 var descriptor = {};
366 descriptor.value = e;
367 descriptor.wasThrown = true;
368 }
369
370 descriptor.name = name;
371 if (o === object)
372 descriptor.isOwn = true;
373 descriptors.push(descriptor);
374 }
375 if (ownProperties) {
376 if (object.__proto__)
377 descriptors.push({ name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
378 break;
379 }
380 }
381 return descriptors;
382 },
383
384 /**
385 * @param {string} expression
386 * @param {string} objectGroup
387 * @param {boolean} injectCommandLineAPI
388 * @param {boolean} returnByValue
389 * @param {boolean} generatePreview
390 * @return {*}
391 */
392 evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
393 {
394 return this._evaluateAndWrap(InjectedScriptHost.evaluate, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview);
395 },
396
397 /**
398 * @param {string} objectId
399 * @param {string} expression
400 * @param {boolean} returnByValue
401 * @return {Object|string}
402 */
403 callFunctionOn: function(objectId, expression, args, returnByValue)
404 {
405 var parsedObjectId = this._parseObjectId(objectId);
406 var object = this._objectForId(parsedObjectId);
407 if (!this._isDefined(object))
408 return "Could not find object with given id";
409
410 if (args) {
411 var resolvedArgs = [];
412 args = InjectedScriptHost.evaluate(args);
413 for (var i = 0; i < args.length; ++i) {
414 var resolvedCallArgument;
415 try {
416 resolvedCallArgument = this._resolveCallArgument(args[i]);
417 } catch (e) {
418 return String(e);
419 }
420 resolvedArgs.push(resolvedCallArgument)
421 }
422 }
423
424 try {
425 var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
426 var func = InjectedScriptHost.evaluate("(" + expression + ")");
427 if (typeof func !== "function")
428 return "Given expression does not evaluate to a function";
429
430 return { wasThrown: false,
431 result: this._wrapObject(func.apply(object, resolvedArgs), objectGroup, returnByValue) };
432 } catch (e) {
433 return this._createThrownValue(e, objectGroup);
434 }
435 },
436
437 /**
438 * Resolves a value from CallArgument description.
439 * @param {RuntimeAgent.CallArgument} callArgumentJson
440 * @return {*} resolved value
441 * @throws {string} error message
442 */
443 _resolveCallArgument: function(callArgumentJson) {
444 var objectId = callArgumentJson.objectId;
445 if (objectId) {
446 var parsedArgId = this._parseObjectId(objectId);
447 if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
448 throw "Arguments should belong to the same JavaScript world as the target object.";
449
450 var resolvedArg = this._objectForId(parsedArgId);
451 if (!this._isDefined(resolvedArg))
452 throw "Could not find object with given id";
453
454 return resolvedArg;
455 } else if ("value" in callArgumentJson)
456 return callArgumentJson.value;
457 else
458 return undefined;
459 },
460
461 /**
462 * @param {Function} evalFunction
463 * @param {Object} object
464 * @param {string} objectGroup
465 * @param {boolean} isEvalOnCallFrame
466 * @param {boolean} injectCommandLineAPI
467 * @param {boolean} returnByValue
468 * @param {boolean} generatePreview
469 * @return {*}
470 */
471 _evaluateAndWrap: function(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, injectCommandLineAPI, returnByValue, generatePreview)
472 {
473 try {
474 return { wasThrown: false,
475 result: this._wrapObject(this._evaluateOn(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI), objectGroup, returnByValue, generatePreview) };
476 } catch (e) {
477 return this._createThrownValue(e, objectGroup);
478 }
479 },
480
481 /**
482 * @param {*} value
483 * @param {string} objectGroup
484 * @return {Object}
485 */
486 _createThrownValue: function(value, objectGroup)
487 {
488 var remoteObject = this._wrapObject(value, objectGroup);
489 try {
490 remoteObject.description = this._toString(value);
491 } catch (e) {}
492 return { wasThrown: true,
493 result: remoteObject };
494 },
495
496 /**
497 * @param {Function} evalFunction
498 * @param {Object} object
499 * @param {string} objectGroup
500 * @param {string} expression
501 * @param {boolean} isEvalOnCallFrame
502 * @param {boolean} injectCommandLineAPI
503 * @return {*}
504 */
505 _evaluateOn: function(evalFunction, object, objectGroup, expression, isEvalOnCallFrame, injectCommandLineAPI)
506 {
507 var commandLineAPI = null;
508 if (injectCommandLineAPI) {
509 if (this.CommandLineAPI)
510 commandLineAPI = new this.CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
511 else
512 commandLineAPI = new BasicCommandLineAPI;
513 }
514
515 if (isEvalOnCallFrame) {
516 // We can only use this approach if the evaluate function is the true 'eval'. That allows us to use it with
517 // the 'eval' identifier when calling it. Using 'eval' grants access to the local scope of the closure we
518 // create that provides the command line APIs.
519
520 var parameters = [InjectedScriptHost.evaluate, expression];
521 var expressionFunctionBody = "" +
522 "var global = Function('return this')() || (1, eval)('this');" +
523 "var __originalEval = global.eval; global.eval = __eval;" +
524 "try { return eval(__currentExpression); }" +
525 "finally { global.eval = __originalEval; }";
526
527 if (commandLineAPI) {
528 // To avoid using a 'with' statement (which fails in strict mode and requires injecting the API object)
529 // we instead create a closure where we evaluate the expression. The command line APIs are passed as
530 // parameters to the closure so they are in scope but not injected. This allows the code evaluated in
531 // the console to stay in strict mode (if is was already set), or to get strict mode by prefixing
532 // expressions with 'use strict';.
533
534 var parameterNames = Object.getOwnPropertyNames(commandLineAPI);
535 for (var i = 0; i < parameterNames.length; ++i)
536 parameters.push(commandLineAPI[parameterNames[i]]);
537
538 var expressionFunctionString = "(function(__eval, __currentExpression, " + parameterNames.join(", ") + ") { " + expressionFunctionBody + " })";
539 } else {
540 // Use a closure in this case too to keep the same behavior of 'var' being captured by the closure instead
541 // of leaking out into the calling scope.
542 var expressionFunctionString = "(function(__eval, __currentExpression) { " + expressionFunctionBody + " })";
543 }
544
545 // Bind 'this' to the function expression using another closure instead of Function.prototype.bind. This ensures things will work if the page replaces bind.
546 var boundExpressionFunctionString = "(function(__function, __thisObject) { return function() { return __function.apply(__thisObject, arguments) }; })(" + expressionFunctionString + ", this)";
547 var expressionFunction = evalFunction.call(object, boundExpressionFunctionString);
548 var result = expressionFunction.apply(null, parameters);
549
550 if (objectGroup === "console")
551 this._lastResult = result;
552
553 return result;
554 }
555
556 // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak
557 // into the global scope. This allow them to stick around between evaluations.
558
559 try {
560 if (commandLineAPI) {
561 if (inspectedGlobalObject.console)
562 inspectedGlobalObject.console.__commandLineAPI = commandLineAPI;
563 else
564 inspectedGlobalObject.__commandLineAPI = commandLineAPI;
565 expression = "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + expression + "\n}";
566 }
567
568 var result = evalFunction.call(inspectedGlobalObject, expression);
569
570 if (objectGroup === "console")
571 this._lastResult = result;
572
573 return result;
574 } finally {
575 if (commandLineAPI) {
576 if (inspectedGlobalObject.console)
577 delete inspectedGlobalObject.console.__commandLineAPI;
578 else
579 delete inspectedGlobalObject.__commandLineAPI;
580 }
581 }
582 },
583
584 /**
585 * @param {Object} callFrame
586 * @return {Array.<InjectedScript.CallFrameProxy>|boolean}
587 */
588 wrapCallFrames: function(callFrame)
589 {
590 if (!callFrame)
591 return false;
592
593 var result = [];
594 var depth = 0;
595 do {
596 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
597 callFrame = callFrame.caller;
598 } while (callFrame);
599 return result;
600 },
601
602 /**
603 * @param {Object} topCallFrame
604 * @param {string} callFrameId
605 * @param {string} expression
606 * @param {string} objectGroup
607 * @param {boolean} injectCommandLineAPI
608 * @param {boolean} returnByValue
609 * @param {boolean} generatePreview
610 * @return {*}
611 */
612 evaluateOnCallFrame: function(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
613 {
614 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
615 if (!callFrame)
616 return "Could not find call frame with given id";
617 return this._evaluateAndWrap(callFrame.evaluate, callFrame, expression, objectGroup, true, injectCommandLineAPI, returnByValue, generatePreview);
618 },
619
620 /**
621 * @param {Object} topCallFrame
622 * @param {string} callFrameId
623 * @return {Object}
624 */
625 _callFrameForId: function(topCallFrame, callFrameId)
626 {
627 var parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
628 var ordinal = parsedCallFrameId["ordinal"];
629 var callFrame = topCallFrame;
630 while (--ordinal >= 0 && callFrame)
631 callFrame = callFrame.caller;
632 return callFrame;
633 },
634
635 /**
636 * @param {Object} objectId
637 * @return {Object}
638 */
639 _objectForId: function(objectId)
640 {
641 return this._idToWrappedObject[objectId.id];
642 },
643
644 /**
645 * @param {string} objectId
646 * @return {Object}
647 */
648 findObjectById: function(objectId)
649 {
650 var parsedObjectId = this._parseObjectId(objectId);
651 return this._objectForId(parsedObjectId);
652 },
653
654 /**
655 * @param {string} name
656 * @return {Object}
657 */
658 module: function(name)
659 {
660 return this._modules[name];
661 },
662
663 /**
664 * @param {string} name
665 * @param {string} source
666 * @return {Object}
667 */
668 injectModule: function(name, source, host)
669 {
670 delete this._modules[name];
671 var moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
672 if (typeof moduleFunction !== "function") {
673 if (inspectedGlobalObject.console)
674 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
675 return null;
676 }
677 var module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, host);
678 this._modules[name] = module;
679 return module;
680 },
681
682 /**
683 * @param {*} object
684 * @return {boolean}
685 */
686 _isDefined: function(object)
687 {
688 return !!object || this._isHTMLAllCollection(object);
689 },
690
691 /**
692 * @param {*} object
693 * @return {boolean}
694 */
695 _isHTMLAllCollection: function(object)
696 {
697 // document.all is reported as undefined, but we still want to process it.
698 return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
699 },
700
701 /**
702 * @param {Object=} obj
703 * @return {string?}
704 */
705 _subtype: function(obj)
706 {
707 if (obj === null)
708 return "null";
709
710 if (this.isPrimitiveValue(obj))
711 return null;
712
713 if (this._isHTMLAllCollection(obj))
714 return "array";
715
716 var preciseType = InjectedScriptHost.type(obj);
717 if (preciseType)
718 return preciseType;
719
720 // FireBug's array detection.
721 try {
722 if (typeof obj.splice === "function" && isFinite(obj.length))
723 return "array";
724 if (Object.prototype.toString.call(obj) === "[object Arguments]" && isFinite(obj.length)) // arguments.
725 return "array";
726 } catch (e) {
727 }
728
729 // If owning frame has navigated to somewhere else window properties will be undefined.
730 return null;
731 },
732
733 /**
734 * @param {*} obj
735 * @return {string?}
736 */
737 _describe: function(obj)
738 {
739 if (this.isPrimitiveValue(obj))
740 return null;
741
742 obj = /** @type {Object} */ (obj);
743
744 // Type is object, get subtype.
745 var subtype = this._subtype(obj);
746
747 if (subtype === "regexp")
748 return this._toString(obj);
749
750 if (subtype === "date")
751 return this._toString(obj);
752
753 if (subtype === "node") {
754 var description = obj.nodeName.toLowerCase();
755 switch (obj.nodeType) {
756 case 1 /* Node.ELEMENT_NODE */:
757 description += obj.id ? "#" + obj.id : "";
758 var className = obj.className;
759 description += className ? "." + className : "";
760 break;
761 case 10 /*Node.DOCUMENT_TYPE_NODE */:
762 description = "<!DOCTYPE " + description + ">";
763 break;
764 }
765 return description;
766 }
767
768 var className = InjectedScriptHost.internalConstructorName(obj);
769 if (subtype === "array") {
770 if (typeof obj.length === "number")
771 className += "[" + obj.length + "]";
772 return className;
773 }
774
775 // NodeList in JSC is a function, check for array prior to this.
776 if (typeof obj === "function")
777 return this._toString(obj);
778
779 if (className === "Object") {
780 // In Chromium DOM wrapper prototypes will have Object as their constructor name,
781 // get the real DOM wrapper name from the constructor property.
782 var constructorName = obj.constructor && obj.constructor.name;
783 if (constructorName)
784 return constructorName;
785 }
786 return className;
787 },
788
789 /**
790 * @param {*} obj
791 * @return {string}
792 */
793 _toString: function(obj)
794 {
795 // We don't use String(obj) because inspectedGlobalObject.String is undefined if owning frame navigated to another page.
796 return "" + obj;
797 }
798 }
799
800 /**
801 * @type {InjectedScript}
802 * @const
803 */
804 var injectedScript = new InjectedScript();
805
806 /**
807 * @constructor
808 * @param {*} object
809 * @param {string=} objectGroupName
810 * @param {boolean=} forceValueType
811 * @param {boolean=} generatePreview
812 * @param {?Array.<string>=} columnNames
813 */
814 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames)
815 {
816 this.type = typeof object;
817 if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
818 // We don't send undefined values over JSON.
819 if (typeof object !== "undefined")
820 this.value = object;
821
822 // Null object is object with 'null' subtype'
823 if (object === null)
824 this.subtype = "null";
825
826 // Provide user-friendly number values.
827 if (typeof object === "number")
828 this.description = object + "";
829 return;
830 }
831
832 object = /** @type {Object} */ (object);
833
834 this.objectId = injectedScript._bind(object, objectGroupName);
835 var subtype = injectedScript._subtype(object);
836 if (subtype)
837 this.subtype = subtype;
838 this.className = InjectedScriptHost.internalConstructorName(object);
839 this.description = injectedScript._describe(object);
840
841 if (generatePreview && (this.type === "object" || injectedScript._isHTMLAllCollection(object)))
842 this.preview = this._generatePreview(object, undefined, columnNames);
843 }
844
845 InjectedScript.RemoteObject.prototype = {
846 /**
847 * @param {Object} object
848 * @param {Array.<string>=} firstLevelKeys
849 * @param {?Array.<string>=} secondLevelKeys
850 * @return {Object} preview
851 */
852 _generatePreview: function(object, firstLevelKeys, secondLevelKeys)
853 {
854 var preview = {};
855 preview.lossless = true;
856 preview.overflow = false;
857 preview.properties = [];
858
859 var isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
860 var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
861
862 var propertiesThreshold = {
863 properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
864 indexes: isTableRowsRequest ? 1000 : Math.max(100, firstLevelKeysCount)
865 };
866 for (var o = object; injectedScript._isDefined(o); o = o.__proto__)
867 this._generateProtoPreview(o, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys);
868 return preview;
869 },
870
871 /**
872 * @param {Object} object
873 * @param {Object} preview
874 * @param {Object} propertiesThreshold
875 * @param {Array.<string>=} firstLevelKeys
876 * @param {Array.<string>=} secondLevelKeys
877 */
878 _generateProtoPreview: function(object, preview, propertiesThreshold, firstLevelKeys, secondLevelKeys)
879 {
880 var propertyNames = firstLevelKeys ? firstLevelKeys : Object.keys(/** @type {!Object} */(object));
881 try {
882 for (var i = 0; i < propertyNames.length; ++i) {
883 if (!propertiesThreshold.properties || !propertiesThreshold.indexes) {
884 preview.overflow = true;
885 preview.lossless = false;
886 break;
887 }
888 var name = propertyNames[i];
889 if (this.subtype === "array" && name === "length")
890 continue;
891
892 var descriptor = Object.getOwnPropertyDescriptor(/** @type {!Object} */(object), name);
893 if (!("value" in descriptor) || !descriptor.enumerable) {
894 preview.lossless = false;
895 continue;
896 }
897
898 var value = descriptor.value;
899 if (value === null) {
900 this._appendPropertyPreview(preview, { name: name, type: "object", value: "null" }, propertiesThreshold);
901 continue;
902 }
903
904 const maxLength = 100;
905 var type = typeof value;
906
907 if (InjectedScript.primitiveTypes[type]) {
908 if (type === "string") {
909 if (value.length > maxLength) {
910 value = this._abbreviateString(value, maxLength, true);
911 preview.lossless = false;
912 }
913 value = value.replace(/\n/g, "\u21B5");
914 }
915 this._appendPropertyPreview(preview, { name: name, type: type, value: value + "" }, propertiesThreshold);
916 continue;
917 }
918
919 if (secondLevelKeys === null || secondLevelKeys) {
920 var subPreview = this._generatePreview(value, secondLevelKeys || undefined);
921 var property = { name: name, type: type, valuePreview: subPreview };
922 this._appendPropertyPreview(preview, property, propertiesThreshold);
923 if (!subPreview.lossless)
924 preview.lossless = false;
925 if (subPreview.overflow)
926 preview.overflow = true;
927 continue;
928 }
929
930 preview.lossless = false;
931
932 var subtype = injectedScript._subtype(value);
933 var description = "";
934 if (type !== "function")
935 description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
936
937 var property = { name: name, type: type, value: description };
938 if (subtype)
939 property.subtype = subtype;
940 this._appendPropertyPreview(preview, property, propertiesThreshold);
941 }
942 } catch (e) {
943 }
944 },
945
946 /**
947 * @param {Object} preview
948 * @param {Object} property
949 * @param {Object} propertiesThreshold
950 */
951 _appendPropertyPreview: function(preview, property, propertiesThreshold)
952 {
953 if (isNaN(property.name))
954 propertiesThreshold.properties--;
955 else
956 propertiesThreshold.indexes--;
957 preview.properties.push(property);
958 },
959
960 /**
961 * @param {string} string
962 * @param {number} maxLength
963 * @param {boolean=} middle
964 * @returns
965 */
966 _abbreviateString: function(string, maxLength, middle)
967 {
968 if (string.length <= maxLength)
969 return string;
970 if (middle) {
971 var leftHalf = maxLength >> 1;
972 var rightHalf = maxLength - leftHalf - 1;
973 return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
974 }
975 return string.substr(0, maxLength) + "\u2026";
976 }
977 }
978 /**
979 * @constructor
980 * @param {number} ordinal
981 * @param {Object} callFrame
982 */
983 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
984 {
985 this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + "}";
986 this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
987 this.location = { scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column };
988 this.scopeChain = this._wrapScopeChain(callFrame);
989 this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
990 }
991
992 InjectedScript.CallFrameProxy.prototype = {
993 /**
994 * @param {Object} callFrame
995 * @return {!Array.<DebuggerAgent.Scope>}
996 */
997 _wrapScopeChain: function(callFrame)
998 {
999 var scopeChain = callFrame.scopeChain;
1000 var scopeChainProxy = [];
1001 for (var i = 0; i < scopeChain.length; i++) {
1002 var scope = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
1003 scopeChainProxy.push(scope);
1004 }
1005 return scopeChainProxy;
1006 }
1007 }
1008
1009 /**
1010 * @param {number} scopeTypeCode
1011 * @param {*} scopeObject
1012 * @param {string} groupId
1013 * @return {!DebuggerAgent.Scope}
1014 */
1015 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId) {
1016 const GLOBAL_SCOPE = 0;
1017 const LOCAL_SCOPE = 1;
1018 const WITH_SCOPE = 2;
1019 const CLOSURE_SCOPE = 3;
1020 const CATCH_SCOPE = 4;
1021
1022 /** @type {!Object.<number, string>} */
1023 var scopeTypeNames = {};
1024 scopeTypeNames[GLOBAL_SCOPE] = "global";
1025 scopeTypeNames[LOCAL_SCOPE] = "local";
1026 scopeTypeNames[WITH_SCOPE] = "with";
1027 scopeTypeNames[CLOSURE_SCOPE] = "closure";
1028 scopeTypeNames[CATCH_SCOPE] = "catch";
1029
1030 return {
1031 object: injectedScript._wrapObject(scopeObject, groupId),
1032 type: /** @type {DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode])
1033 };
1034 }
1035
1036 function BasicCommandLineAPI()
1037 {
1038 this.$_ = injectedScript._lastResult;
1039 }
1040
1041 return injectedScript;
1042 })