fc932764279b197c57bf26bb149d01718682a257
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / inspector / InjectedScriptSource.js
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2013 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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 Computer, 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.
17  *
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.
28  */
29
30 /**
31  * @param {!InjectedScriptHostClass} InjectedScriptHost
32  * @param {!Window} inspectedWindow
33  * @param {number} injectedScriptId
34  */
35 (function (InjectedScriptHost, inspectedWindow, injectedScriptId) {
36
37 /**
38  * Protect against Object overwritten by the user code.
39  * @suppress {duplicate}
40  */
41 var Object = /** @type {function(new:Object, *=)} */ ({}.constructor);
42
43 /**
44  * @param {!Array.<T>} array
45  * @param {...} var_args
46  * @template T
47  */
48 function push(array, var_args)
49 {
50     for (var i = 1; i < arguments.length; ++i)
51         array[array.length] = arguments[i];
52 }
53
54 /**
55  * @param {!Arguments.<T>} array
56  * @param {number=} index
57  * @return {!Array.<T>}
58  * @template T
59  */
60 function slice(array, index)
61 {
62     var result = [];
63     for (var i = index || 0, j = 0; i < array.length; ++i, ++j)
64         result[j] = array[i];
65     return result;
66 }
67
68 /**
69  * @param {!Array.<T>} array1
70  * @param {!Array.<T>} array2
71  * @return {!Array.<T>}
72  * @template T
73  */
74 function concat(array1, array2)
75 {
76     var result = [];
77     for (var i = 0; i < array1.length; ++i)
78         push(result, array1[i]);
79     for (var i = 0; i < array2.length; ++i)
80         push(result, array2[i]);
81     return result;
82 }
83
84 /**
85  * @param {*} obj
86  * @return {string}
87  * @suppress {uselessCode}
88  */
89 function toString(obj)
90 {
91     // We don't use String(obj) because String could be overridden.
92     // Also the ("" + obj) expression may throw.
93     try {
94         return "" + obj;
95     } catch (e) {
96         var name = InjectedScriptHost.internalConstructorName(obj) || InjectedScriptHost.subtype(obj) || (typeof obj);
97         return "#<" + name + ">";
98     }
99 }
100
101 /**
102  * @param {*} obj
103  * @return {string}
104  */
105 function toStringDescription(obj)
106 {
107     if (typeof obj === "number" && obj === 0 && 1 / obj < 0)
108         return "-0"; // Negative zero.
109     return toString(obj);
110 }
111
112 /**
113  * Please use this bind, not the one from Function.prototype
114  * @param {function(...)} func
115  * @param {?Object} thisObject
116  * @param {...} var_args
117  * @return {function(...)}
118  */
119 function bind(func, thisObject, var_args)
120 {
121     var args = slice(arguments, 2);
122
123     /**
124      * @param {...} var_args
125      */
126     function bound(var_args)
127     {
128         return InjectedScriptHost.callFunction(func, thisObject, concat(args, slice(arguments)));
129     }
130     bound.toString = function()
131     {
132         return "bound: " + toString(func);
133     };
134     return bound;
135 }
136
137 /**
138  * @param {T} obj
139  * @return {T}
140  * @template T
141  */
142 function nullifyObjectProto(obj)
143 {
144     if (obj && typeof obj === "object")
145         obj.__proto__ = null;
146     return obj;
147 }
148
149 /**
150  * @param {*} obj
151  * @return {boolean}
152  */
153 function isUInt32(obj)
154 {
155     return typeof obj === "number" && obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
156 }
157
158 /**
159  * FireBug's array detection.
160  * @param {*} obj
161  * @return {boolean}
162  */
163 function isArrayLike(obj)
164 {
165     if (typeof obj !== "object")
166         return false;
167     try {
168         if (typeof obj.splice === "function")
169             return isUInt32(obj.length);
170     } catch (e) {
171     }
172     return false;
173 }
174
175 /**
176  * @param {number} a
177  * @param {number} b
178  * @return {number}
179  */
180 function max(a, b)
181 {
182     return a > b ? a : b;
183 }
184
185 /**
186  * FIXME: Remove once ES6 is supported natively by JS compiler.
187  * @param {*} obj
188  * @return {boolean}
189  */
190 function isSymbol(obj)
191 {
192     var type = typeof obj;
193     return (type === "symbol");
194 }
195
196 /**
197  * @constructor
198  */
199 var InjectedScript = function()
200 {
201     /** @type {number} */
202     this._lastBoundObjectId = 1;
203     /** @type {!Object.<number, (!Object|symbol)>} */
204     this._idToWrappedObject = { __proto__: null };
205     /** @type {!Object.<number, string>} */
206     this._idToObjectGroupName = { __proto__: null };
207     /** @type {!Object.<string, !Array.<number>>} */
208     this._objectGroups = { __proto__: null };
209     /** @type {!Object.<string, !Object>} */
210     this._modules = { __proto__: null };
211 }
212
213 /**
214  * @type {!Object.<string, boolean>}
215  * @const
216  */
217 InjectedScript.primitiveTypes = {
218     "undefined": true,
219     "boolean": true,
220     "number": true,
221     "string": true,
222     __proto__: null
223 }
224
225 InjectedScript.prototype = {
226     /**
227      * @param {*} object
228      * @return {boolean}
229      */
230     isPrimitiveValue: function(object)
231     {
232         // FIXME(33716): typeof document.all is always 'undefined'.
233         return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
234     },
235
236     /**
237      * @param {*} object
238      * @param {string} groupName
239      * @param {boolean} canAccessInspectedWindow
240      * @param {boolean} generatePreview
241      * @return {!RuntimeAgent.RemoteObject}
242      */
243     wrapObject: function(object, groupName, canAccessInspectedWindow, generatePreview)
244     {
245         if (canAccessInspectedWindow)
246             return this._wrapObject(object, groupName, false, generatePreview);
247         return this._fallbackWrapper(object);
248     },
249
250     /**
251      * @param {*} object
252      * @return {!RuntimeAgent.RemoteObject}
253      */
254     _fallbackWrapper: function(object)
255     {
256         var result = { __proto__: null };
257         result.type = typeof object;
258         if (this.isPrimitiveValue(object))
259             result.value = object;
260         else
261             result.description = toString(object);
262         return /** @type {!RuntimeAgent.RemoteObject} */ (result);
263     },
264
265     /**
266      * @param {boolean} canAccessInspectedWindow
267      * @param {!Object} table
268      * @param {!Array.<string>|string|boolean} columns
269      * @return {!RuntimeAgent.RemoteObject}
270      */
271     wrapTable: function(canAccessInspectedWindow, table, columns)
272     {
273         if (!canAccessInspectedWindow)
274             return this._fallbackWrapper(table);
275         var columnNames = null;
276         if (typeof columns === "string")
277             columns = [columns];
278         if (InjectedScriptHost.subtype(columns) === "array") {
279             columnNames = [];
280             for (var i = 0; i < columns.length; ++i)
281                 columnNames[i] = toString(columns[i]);
282         }
283         return this._wrapObject(table, "console", false, true, columnNames, true);
284     },
285
286     /**
287      * @param {*} object
288      */
289     inspectNode: function(object)
290     {
291         this._inspect(object);
292     },
293
294     /**
295      * @param {*} object
296      * @return {*}
297      */
298     _inspect: function(object)
299     {
300         if (arguments.length === 0)
301             return;
302
303         var objectId = this._wrapObject(object, "");
304         var hints = { __proto__: null };
305
306         InjectedScriptHost.inspect(objectId, hints);
307         return object;
308     },
309
310     /**
311      * This method cannot throw.
312      * @param {*} object
313      * @param {string=} objectGroupName
314      * @param {boolean=} forceValueType
315      * @param {boolean=} generatePreview
316      * @param {?Array.<string>=} columnNames
317      * @param {boolean=} isTable
318      * @return {!RuntimeAgent.RemoteObject}
319      * @suppress {checkTypes}
320      */
321     _wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable)
322     {
323         try {
324             return new InjectedScript.RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable);
325         } catch (e) {
326             try {
327                 var description = injectedScript._describe(e);
328             } catch (ex) {
329                 var description = "<failed to convert exception to string>";
330             }
331             return new InjectedScript.RemoteObject(description);
332         }
333     },
334
335     /**
336      * @param {!Object|symbol} object
337      * @param {string=} objectGroupName
338      * @return {string}
339      */
340     _bind: function(object, objectGroupName)
341     {
342         var id = this._lastBoundObjectId++;
343         this._idToWrappedObject[id] = object;
344         var objectId = "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
345         if (objectGroupName) {
346             var group = this._objectGroups[objectGroupName];
347             if (!group) {
348                 group = [];
349                 this._objectGroups[objectGroupName] = group;
350             }
351             push(group, id);
352             this._idToObjectGroupName[id] = objectGroupName;
353         }
354         return objectId;
355     },
356
357     /**
358      * @param {string} objectId
359      * @return {!Object}
360      */
361     _parseObjectId: function(objectId)
362     {
363         return nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval("(" + objectId + ")")));
364     },
365
366     /**
367      * @param {string} objectGroupName
368      */
369     releaseObjectGroup: function(objectGroupName)
370     {
371         if (objectGroupName === "console")
372             delete this._lastResult;
373         var group = this._objectGroups[objectGroupName];
374         if (!group)
375             return;
376         for (var i = 0; i < group.length; i++)
377             this._releaseObject(group[i]);
378         delete this._objectGroups[objectGroupName];
379     },
380
381     /**
382      * @param {string} objectId
383      */
384     setLastEvaluationResult: function(objectId)
385     {
386         var parsedObjectId = this._parseObjectId(objectId);
387         var object = this._objectForId(parsedObjectId);
388         this._lastResult = object;
389     },
390
391     /**
392      * @param {string} methodName
393      * @param {string} args
394      * @return {*}
395      */
396     dispatch: function(methodName, args)
397     {
398         var argsArray = /** @type {!Array.<*>} */ (InjectedScriptHost.eval("(" + args + ")"));
399         var result = InjectedScriptHost.callFunction(this[methodName], this, argsArray);
400         if (typeof result === "undefined") {
401             inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
402             result = null;
403         }
404         return result;
405     },
406
407     /**
408      * @param {string} objectId
409      * @param {boolean} ownProperties
410      * @param {boolean} accessorPropertiesOnly
411      * @return {!Array.<!RuntimeAgent.PropertyDescriptor>|boolean}
412      */
413     getProperties: function(objectId, ownProperties, accessorPropertiesOnly)
414     {
415         var parsedObjectId = this._parseObjectId(objectId);
416         var object = this._objectForId(parsedObjectId);
417         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
418
419         if (!this._isDefined(object) || isSymbol(object))
420             return false;
421         object = /** @type {!Object} */ (object);
422         var descriptors = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly);
423
424         // Go over properties, wrap object values.
425         for (var i = 0; i < descriptors.length; ++i) {
426             var descriptor = descriptors[i];
427             if ("get" in descriptor)
428                 descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
429             if ("set" in descriptor)
430                 descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
431             if ("value" in descriptor)
432                 descriptor.value = this._wrapObject(descriptor.value, objectGroupName);
433             if (!("configurable" in descriptor))
434                 descriptor.configurable = false;
435             if (!("enumerable" in descriptor))
436                 descriptor.enumerable = false;
437             if ("symbol" in descriptor)
438                 descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName);
439         }
440         return descriptors;
441     },
442
443     /**
444      * @param {string} objectId
445      * @return {!Array.<!Object>|boolean}
446      */
447     getInternalProperties: function(objectId)
448     {
449         var parsedObjectId = this._parseObjectId(objectId);
450         var object = this._objectForId(parsedObjectId);
451         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
452         if (!this._isDefined(object) || isSymbol(object))
453             return false;
454         object = /** @type {!Object} */ (object);
455         var descriptors = [];
456         var internalProperties = InjectedScriptHost.getInternalProperties(object);
457         if (internalProperties) {
458             for (var i = 0; i < internalProperties.length; i++) {
459                 var property = internalProperties[i];
460                 var descriptor = {
461                     name: property.name,
462                     value: this._wrapObject(property.value, objectGroupName),
463                     __proto__: null
464                 };
465                 push(descriptors, descriptor);
466             }
467         }
468         return descriptors;
469     },
470
471     /**
472      * @param {string} functionId
473      * @return {!DebuggerAgent.FunctionDetails|string}
474      */
475     getFunctionDetails: function(functionId)
476     {
477         var parsedFunctionId = this._parseObjectId(functionId);
478         var func = this._objectForId(parsedFunctionId);
479         if (typeof func !== "function")
480             return "Cannot resolve function by id.";
481         var details = nullifyObjectProto(/** @type {!DebuggerAgent.FunctionDetails} */ (InjectedScriptHost.functionDetails(func)));
482         if ("rawScopes" in details) {
483             var objectGroupName = this._idToObjectGroupName[parsedFunctionId.id];
484             var rawScopes = details["rawScopes"];
485             delete details["rawScopes"];
486             var scopes = [];
487             for (var i = 0; i < rawScopes.length; ++i)
488                 scopes[i] = InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName);
489             details.scopeChain = scopes;
490         }
491         return details;
492     },
493
494     /**
495      * @param {string} objectId
496      * @return {!Array.<!Object>|string}
497      */
498     getCollectionEntries: function(objectId)
499     {
500         var parsedObjectId = this._parseObjectId(objectId);
501         var object = this._objectForId(parsedObjectId);
502         if (!object || typeof object !== "object")
503             return "Could not find object with given id";
504         var entries = InjectedScriptHost.collectionEntries(object);
505         if (!entries)
506             return "Object with given id is not a collection";
507         var objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
508         for (var i = 0; i < entries.length; ++i) {
509             var entry = nullifyObjectProto(entries[i]);
510             if ("key" in entry)
511                 entry.key = this._wrapObject(entry.key, objectGroupName);
512             entry.value = this._wrapObject(entry.value, objectGroupName);
513             entries[i] = entry;
514         }
515         return entries;
516     },
517
518     /**
519      * @param {string} objectId
520      */
521     releaseObject: function(objectId)
522     {
523         var parsedObjectId = this._parseObjectId(objectId);
524         this._releaseObject(parsedObjectId.id);
525     },
526
527     /**
528      * @param {number} id
529      */
530     _releaseObject: function(id)
531     {
532         delete this._idToWrappedObject[id];
533         delete this._idToObjectGroupName[id];
534     },
535
536     /**
537      * @param {!Object} object
538      * @param {boolean=} ownProperties
539      * @param {boolean=} accessorPropertiesOnly
540      * @return {!Array.<!Object>}
541      */
542     _propertyDescriptors: function(object, ownProperties, accessorPropertiesOnly)
543     {
544         var descriptors = [];
545         var propertyProcessed = { __proto__: null };
546
547         /**
548          * @param {?Object} o
549          * @param {!Array.<string|symbol>} properties
550          */
551         function process(o, properties)
552         {
553             for (var i = 0; i < properties.length; ++i) {
554                 var property = properties[i];
555                 if (propertyProcessed[property])
556                     continue;
557
558                 var name = property;
559                 if (isSymbol(property))
560                     name = injectedScript._describe(property);
561
562                 try {
563                     propertyProcessed[property] = true;
564                     var descriptor = nullifyObjectProto(InjectedScriptHost.suppressWarningsAndCallFunction(Object.getOwnPropertyDescriptor, Object, [o, property]));
565                     if (descriptor) {
566                         if (accessorPropertiesOnly && !("get" in descriptor || "set" in descriptor))
567                             continue;
568                     } else {
569                         // Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
570                         if (accessorPropertiesOnly)
571                             continue;
572                         try {
573                             descriptor = { name: name, value: o[property], writable: false, configurable: false, enumerable: false, __proto__: null };
574                             if (o === object)
575                                 descriptor.isOwn = true;
576                             push(descriptors, descriptor);
577                         } catch (e) {
578                             // Silent catch.
579                         }
580                         continue;
581                     }
582                 } catch (e) {
583                     if (accessorPropertiesOnly)
584                         continue;
585                     var descriptor = { __proto__: null };
586                     descriptor.value = e;
587                     descriptor.wasThrown = true;
588                 }
589
590                 descriptor.name = name;
591                 if (o === object)
592                     descriptor.isOwn = true;
593                 if (isSymbol(property))
594                     descriptor.symbol = property;
595                 push(descriptors, descriptor);
596             }
597         }
598
599         for (var o = object; this._isDefined(o); o = o.__proto__) {
600             // First call Object.keys() to enforce ordering of the property descriptors.
601             process(o, Object.keys(/** @type {!Object} */ (o)));
602             process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o)));
603             if (Object.getOwnPropertySymbols)
604                 process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (o)));
605
606             if (ownProperties) {
607                 if (object.__proto__ && !accessorPropertiesOnly)
608                     push(descriptors, { name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null });
609                 break;
610             }
611         }
612
613         return descriptors;
614     },
615
616     /**
617      * @param {string} expression
618      * @param {string} objectGroup
619      * @param {boolean} injectCommandLineAPI
620      * @param {boolean} returnByValue
621      * @param {boolean} generatePreview
622      * @return {*}
623      */
624     evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
625     {
626         return this._evaluateAndWrap(null, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview);
627     },
628
629     /**
630      * @param {string} objectId
631      * @param {string} expression
632      * @param {string} args
633      * @param {boolean} returnByValue
634      * @return {!Object|string}
635      */
636     callFunctionOn: function(objectId, expression, args, returnByValue)
637     {
638         var parsedObjectId = this._parseObjectId(objectId);
639         var object = this._objectForId(parsedObjectId);
640         if (!this._isDefined(object))
641             return "Could not find object with given id";
642
643         if (args) {
644             var resolvedArgs = [];
645             var callArgs = /** @type {!Array.<!RuntimeAgent.CallArgument>} */ (InjectedScriptHost.eval(args));
646             for (var i = 0; i < callArgs.length; ++i) {
647                 try {
648                     resolvedArgs[i] = this._resolveCallArgument(callArgs[i]);
649                 } catch (e) {
650                     return toString(e);
651                 }
652             }
653         }
654
655         try {
656             var objectGroup = this._idToObjectGroupName[parsedObjectId.id];
657             var func = InjectedScriptHost.eval("(" + expression + ")");
658             if (typeof func !== "function")
659                 return "Given expression does not evaluate to a function";
660
661             return { wasThrown: false,
662                      result: this._wrapObject(InjectedScriptHost.callFunction(func, object, resolvedArgs), objectGroup, returnByValue),
663                      __proto__: null };
664         } catch (e) {
665             return this._createThrownValue(e, objectGroup, false);
666         }
667     },
668
669     /**
670      * Resolves a value from CallArgument description.
671      * @param {!RuntimeAgent.CallArgument} callArgumentJson
672      * @return {*} resolved value
673      * @throws {string} error message
674      */
675     _resolveCallArgument: function(callArgumentJson)
676     {
677         callArgumentJson = nullifyObjectProto(callArgumentJson);
678         var objectId = callArgumentJson.objectId;
679         if (objectId) {
680             var parsedArgId = this._parseObjectId(objectId);
681             if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
682                 throw "Arguments should belong to the same JavaScript world as the target object.";
683
684             var resolvedArg = this._objectForId(parsedArgId);
685             if (!this._isDefined(resolvedArg))
686                 throw "Could not find object with given id";
687
688             return resolvedArg;
689         } else if ("value" in callArgumentJson) {
690             var value = callArgumentJson.value;
691             if (callArgumentJson.type === "number" && typeof value !== "number")
692                 value = Number(value);
693             return value;
694         }
695         return undefined;
696     },
697
698     /**
699      * @param {?JavaScriptCallFrame} callFrame
700      * @param {string} expression
701      * @param {string} objectGroup
702      * @param {boolean} injectCommandLineAPI
703      * @param {boolean} returnByValue
704      * @param {boolean} generatePreview
705      * @param {!Array.<!Object>=} scopeChain
706      * @return {!Object}
707      */
708     _evaluateAndWrap: function(callFrame, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, scopeChain)
709     {
710         var wrappedResult = this._evaluateOn(callFrame, objectGroup, expression, injectCommandLineAPI, scopeChain);
711         if (!wrappedResult.exceptionDetails) {
712             return { wasThrown: false,
713                      result: this._wrapObject(wrappedResult.result, objectGroup, returnByValue, generatePreview),
714                      __proto__: null };
715         }
716         return this._createThrownValue(wrappedResult.result, objectGroup, generatePreview, wrappedResult.exceptionDetails);
717     },
718
719     /**
720      * @param {*} value
721      * @param {string} objectGroup
722      * @param {boolean} generatePreview
723      * @param {!DebuggerAgent.ExceptionDetails=} exceptionDetails
724      * @return {!Object}
725      */
726     _createThrownValue: function(value, objectGroup, generatePreview, exceptionDetails)
727     {
728         var remoteObject = this._wrapObject(value, objectGroup, false, generatePreview && InjectedScriptHost.subtype(value) !== "error");
729         if (!remoteObject.description){
730             try {
731                 remoteObject.description = toStringDescription(value);
732             } catch (e) {}
733         }
734         return { wasThrown: true, result: remoteObject, exceptionDetails: exceptionDetails, __proto__: null };
735     },
736
737     /**
738      * @param {?JavaScriptCallFrame} callFrame
739      * @param {string} objectGroup
740      * @param {string} expression
741      * @param {boolean} injectCommandLineAPI
742      * @param {!Array.<!Object>=} scopeChain
743      * @return {*}
744      */
745     _evaluateOn: function(callFrame, objectGroup, expression, injectCommandLineAPI, scopeChain)
746     {
747         // Only install command line api object for the time of evaluation.
748         // Surround the expression in with statements to inject our command line API so that
749         // the window object properties still take more precedent than our API functions.
750
751         var scopeExtensionForEval = (callFrame && injectCommandLineAPI) ? new CommandLineAPI(this._commandLineAPIImpl, callFrame) : undefined;
752
753         injectCommandLineAPI = !scopeExtensionForEval && !callFrame && injectCommandLineAPI && !("__commandLineAPI" in inspectedWindow);
754         var injectScopeChain = scopeChain && scopeChain.length && !("__scopeChainForEval" in inspectedWindow);
755
756         try {
757             var prefix = "";
758             var suffix = "";
759             if (injectCommandLineAPI) {
760                 InjectedScriptHost.setNonEnumProperty(inspectedWindow, "__commandLineAPI", new CommandLineAPI(this._commandLineAPIImpl, callFrame));
761                 prefix = "with (typeof __commandLineAPI !== 'undefined' ? __commandLineAPI : { __proto__: null }) {";
762                 suffix = "}";
763             }
764             if (injectScopeChain) {
765                 InjectedScriptHost.setNonEnumProperty(inspectedWindow, "__scopeChainForEval", scopeChain);
766                 for (var i = 0; i < scopeChain.length; ++i) {
767                     prefix = "with (typeof __scopeChainForEval !== 'undefined' ? __scopeChainForEval[" + i + "] : { __proto__: null }) {" + (suffix ? " " : "") + prefix;
768                     if (suffix)
769                         suffix += " }";
770                     else
771                         suffix = "}";
772                 }
773             }
774
775             if (prefix)
776                 expression = prefix + "\n" + expression + "\n" + suffix;
777             var wrappedResult = callFrame ? callFrame.evaluateWithExceptionDetails(expression, scopeExtensionForEval) : InjectedScriptHost.evaluateWithExceptionDetails(expression);
778             if (objectGroup === "console" && !wrappedResult.exceptionDetails)
779                 this._lastResult = wrappedResult.result;
780             return wrappedResult;
781         } finally {
782             if (injectCommandLineAPI)
783                 delete inspectedWindow["__commandLineAPI"];
784             if (injectScopeChain)
785                 delete inspectedWindow["__scopeChainForEval"];
786         }
787     },
788
789     /**
790      * @param {?Object} callFrame
791      * @param {number} asyncOrdinal
792      * @return {!Array.<!InjectedScript.CallFrameProxy>|boolean}
793      */
794     wrapCallFrames: function(callFrame, asyncOrdinal)
795     {
796         if (!callFrame)
797             return false;
798
799         var result = [];
800         var depth = 0;
801         do {
802             result[depth] = new InjectedScript.CallFrameProxy(depth, callFrame, asyncOrdinal);
803             callFrame = callFrame.caller;
804             ++depth;
805         } while (callFrame);
806         return result;
807     },
808
809     /**
810      * @param {!JavaScriptCallFrame} topCallFrame
811      * @param {!Array.<!JavaScriptCallFrame>} asyncCallStacks
812      * @param {string} callFrameId
813      * @param {string} expression
814      * @param {string} objectGroup
815      * @param {boolean} injectCommandLineAPI
816      * @param {boolean} returnByValue
817      * @param {boolean} generatePreview
818      * @return {*}
819      */
820     evaluateOnCallFrame: function(topCallFrame, asyncCallStacks, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
821     {
822         var parsedCallFrameId = nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval("(" + callFrameId + ")")));
823         var callFrame = this._callFrameForParsedId(topCallFrame, parsedCallFrameId, asyncCallStacks);
824         if (!callFrame)
825             return "Could not find call frame with given id";
826         if (parsedCallFrameId["asyncOrdinal"])
827             return this._evaluateAndWrap(null, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, callFrame.scopeChain);
828         return this._evaluateAndWrap(callFrame, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview);
829     },
830
831     /**
832      * @param {!JavaScriptCallFrame} topCallFrame
833      * @param {string} callFrameId
834      * @return {*}
835      */
836     restartFrame: function(topCallFrame, callFrameId)
837     {
838         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
839         if (!callFrame)
840             return "Could not find call frame with given id";
841         var result = callFrame.restart();
842         if (result === false)
843             result = "Restart frame is not supported";
844         return result;
845     },
846
847     /**
848      * @param {!JavaScriptCallFrame} topCallFrame
849      * @param {string} callFrameId
850      * @return {*} a stepIn position array ready for protocol JSON or a string error
851      */
852     getStepInPositions: function(topCallFrame, callFrameId)
853     {
854         var callFrame = this._callFrameForId(topCallFrame, callFrameId);
855         if (!callFrame)
856             return "Could not find call frame with given id";
857         var stepInPositionsUnpacked = JSON.parse(callFrame.stepInPositions);
858         if (typeof stepInPositionsUnpacked !== "object")
859             return "Step in positions not available";
860         return stepInPositionsUnpacked;
861     },
862
863     /**
864      * Either callFrameId or functionObjectId must be specified.
865      * @param {!JavaScriptCallFrame} topCallFrame
866      * @param {string|boolean} callFrameId or false
867      * @param {string|boolean} functionObjectId or false
868      * @param {number} scopeNumber
869      * @param {string} variableName
870      * @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string
871      * @return {string|undefined} undefined if success or an error message
872      */
873     setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
874     {
875         try {
876             var newValueJson = /** @type {!RuntimeAgent.CallArgument} */ (InjectedScriptHost.eval("(" + newValueJsonString + ")"));
877             var resolvedValue = this._resolveCallArgument(newValueJson);
878             if (typeof callFrameId === "string") {
879                 var callFrame = this._callFrameForId(topCallFrame, callFrameId);
880                 if (!callFrame)
881                     return "Could not find call frame with given id";
882                 callFrame.setVariableValue(scopeNumber, variableName, resolvedValue)
883             } else {
884                 var parsedFunctionId = this._parseObjectId(/** @type {string} */ (functionObjectId));
885                 var func = this._objectForId(parsedFunctionId);
886                 if (typeof func !== "function")
887                     return "Could not resolve function by id";
888                 InjectedScriptHost.setFunctionVariableValue(func, scopeNumber, variableName, resolvedValue);
889             }
890         } catch (e) {
891             return toString(e);
892         }
893         return undefined;
894     },
895
896     /**
897      * @param {!JavaScriptCallFrame} topCallFrame
898      * @param {string} callFrameId
899      * @return {?JavaScriptCallFrame}
900      */
901     _callFrameForId: function(topCallFrame, callFrameId)
902     {
903         var parsedCallFrameId = nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval("(" + callFrameId + ")")));
904         return this._callFrameForParsedId(topCallFrame, parsedCallFrameId, []);
905     },
906
907     /**
908      * @param {!JavaScriptCallFrame} topCallFrame
909      * @param {!Object} parsedCallFrameId
910      * @param {!Array.<!JavaScriptCallFrame>} asyncCallStacks
911      * @return {?JavaScriptCallFrame}
912      */
913     _callFrameForParsedId: function(topCallFrame, parsedCallFrameId, asyncCallStacks)
914     {
915         var asyncOrdinal = parsedCallFrameId["asyncOrdinal"]; // 1-based index
916         if (asyncOrdinal)
917             topCallFrame = asyncCallStacks[asyncOrdinal - 1];
918         var ordinal = parsedCallFrameId["ordinal"];
919         var callFrame = topCallFrame;
920         while (--ordinal >= 0 && callFrame)
921             callFrame = callFrame.caller;
922         return callFrame;
923     },
924
925     /**
926      * @param {!Object} objectId
927      * @return {!Object|symbol}
928      */
929     _objectForId: function(objectId)
930     {
931         return this._idToWrappedObject[objectId.id];
932     },
933
934     /**
935      * @param {string} objectId
936      * @return {!Object|symbol}
937      */
938     findObjectById: function(objectId)
939     {
940         var parsedObjectId = this._parseObjectId(objectId);
941         return this._objectForId(parsedObjectId);
942     },
943
944     /**
945      * @param {string} objectId
946      * @return {?Node}
947      */
948     nodeForObjectId: function(objectId)
949     {
950         var object = this.findObjectById(objectId);
951         if (!object || this._subtype(object) !== "node")
952             return null;
953         return /** @type {!Node} */ (object);
954     },
955
956     /**
957      * @param {string} name
958      * @return {!Object}
959      */
960     module: function(name)
961     {
962         return this._modules[name];
963     },
964
965     /**
966      * @param {string} name
967      * @param {string} source
968      * @return {?Object}
969      */
970     injectModule: function(name, source)
971     {
972         delete this._modules[name];
973         var moduleFunction = InjectedScriptHost.eval("(" + source + ")");
974         if (typeof moduleFunction !== "function") {
975             inspectedWindow.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
976             return null;
977         }
978         var module = /** @type {!Object} */ (InjectedScriptHost.callFunction(moduleFunction, inspectedWindow, [InjectedScriptHost, inspectedWindow, injectedScriptId, this]));
979         this._modules[name] = module;
980         return module;
981     },
982
983     /**
984      * @param {*} object
985      * @return {boolean}
986      */
987     _isDefined: function(object)
988     {
989         return !!object || this._isHTMLAllCollection(object);
990     },
991
992     /**
993      * @param {*} object
994      * @return {boolean}
995      */
996     _isHTMLAllCollection: function(object)
997     {
998         // document.all is reported as undefined, but we still want to process it.
999         return (typeof object === "undefined") && InjectedScriptHost.isHTMLAllCollection(object);
1000     },
1001
1002     /**
1003      * @param {*} obj
1004      * @return {?string}
1005      */
1006     _subtype: function(obj)
1007     {
1008         if (obj === null)
1009             return "null";
1010
1011         if (this.isPrimitiveValue(obj))
1012             return null;
1013
1014         var subtype = InjectedScriptHost.subtype(obj);
1015         // FIXME: Consider exposing "error" subtype via protocol.
1016         if (subtype && subtype !== "error")
1017             return subtype;
1018
1019         if (isArrayLike(obj))
1020             return "array";
1021
1022         // If owning frame has navigated to somewhere else window properties will be undefined.
1023         return null;
1024     },
1025
1026     /**
1027      * @param {*} obj
1028      * @return {?string}
1029      */
1030     _describe: function(obj)
1031     {
1032         if (this.isPrimitiveValue(obj))
1033             return null;
1034
1035         var subtype = this._subtype(obj);
1036
1037         if (subtype === "regexp")
1038             return toString(obj);
1039
1040         if (subtype === "date")
1041             return toString(obj);
1042
1043         if (subtype === "node") {
1044             var description = obj.nodeName.toLowerCase();
1045             switch (obj.nodeType) {
1046             case 1 /* Node.ELEMENT_NODE */:
1047                 description += obj.id ? "#" + obj.id : "";
1048                 var className = obj.className;
1049                 description += (className && typeof className === "string") ? "." + className.trim().replace(/\s+/g, ".") : "";
1050                 break;
1051             case 10 /*Node.DOCUMENT_TYPE_NODE */:
1052                 description = "<!DOCTYPE " + description + ">";
1053                 break;
1054             }
1055             return description;
1056         }
1057
1058         var className = InjectedScriptHost.internalConstructorName(obj);
1059         if (subtype === "array") {
1060             if (typeof obj.length === "number")
1061                 className += "[" + obj.length + "]";
1062             return className;
1063         }
1064
1065         // NodeList in JSC is a function, check for array prior to this.
1066         if (typeof obj === "function")
1067             return toString(obj);
1068
1069         if (isSymbol(obj)) {
1070             try {
1071                 return /** @type {string} */ (InjectedScriptHost.callFunction(Symbol.prototype.toString, obj)) || "Symbol";
1072             } catch (e) {
1073                 return "Symbol";
1074             }
1075         }
1076
1077         if (InjectedScriptHost.subtype(obj) === "error") {
1078             try {
1079                 var message = obj.message;
1080                 if (message)
1081                     return className + ": " + message;
1082             } catch(e) {
1083             }
1084         }
1085
1086         return className;
1087     }
1088 }
1089
1090 /**
1091  * @type {!InjectedScript}
1092  * @const
1093  */
1094 var injectedScript = new InjectedScript();
1095
1096 /**
1097  * @constructor
1098  * @param {*} object
1099  * @param {string=} objectGroupName
1100  * @param {boolean=} forceValueType
1101  * @param {boolean=} generatePreview
1102  * @param {?Array.<string>=} columnNames
1103  * @param {boolean=} isTable
1104  * @param {boolean=} skipEntriesPreview
1105  */
1106 InjectedScript.RemoteObject = function(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable, skipEntriesPreview)
1107 {
1108     this.type = typeof object;
1109     if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object))
1110         this.type = "object";
1111
1112     if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
1113         // We don't send undefined values over JSON.
1114         if (this.type !== "undefined")
1115             this.value = object;
1116
1117         // Null object is object with 'null' subtype.
1118         if (object === null)
1119             this.subtype = "null";
1120
1121         // Provide user-friendly number values.
1122         if (this.type === "number") {
1123             this.description = toStringDescription(object);
1124             // Override "value" property for values that can not be JSON-stringified.
1125             switch (this.description) {
1126             case "NaN":
1127             case "Infinity":
1128             case "-Infinity":
1129             case "-0":
1130                 this.value = this.description;
1131                 break;
1132             }
1133         }
1134
1135         return;
1136     }
1137
1138     object = /** @type {!Object} */ (object);
1139
1140     this.objectId = injectedScript._bind(object, objectGroupName);
1141     var subtype = injectedScript._subtype(object);
1142     if (subtype)
1143         this.subtype = subtype;
1144     var className = InjectedScriptHost.internalConstructorName(object);
1145     if (className)
1146         this.className = className;
1147     this.description = injectedScript._describe(object);
1148
1149     if (generatePreview && this.type === "object")
1150         this.preview = this._generatePreview(object, undefined, columnNames, isTable, skipEntriesPreview);
1151 }
1152
1153 InjectedScript.RemoteObject.prototype = {
1154     /**
1155      * @return {!RuntimeAgent.ObjectPreview} preview
1156      */
1157     _createEmptyPreview: function()
1158     {
1159         var preview = {
1160             type: /** @type {!RuntimeAgent.ObjectPreviewType.<string>} */ (this.type),
1161             description: this.description || toStringDescription(this.value),
1162             lossless: true,
1163             overflow: false,
1164             properties: [],
1165             __proto__: null
1166         };
1167         if (this.subtype)
1168             preview.subtype = /** @type {!RuntimeAgent.ObjectPreviewSubtype.<string>} */ (this.subtype);
1169         return preview;
1170     },
1171
1172     /**
1173      * @param {!Object} object
1174      * @param {?Array.<string>=} firstLevelKeys
1175      * @param {?Array.<string>=} secondLevelKeys
1176      * @param {boolean=} isTable
1177      * @param {boolean=} skipEntriesPreview
1178      * @return {!RuntimeAgent.ObjectPreview} preview
1179      */
1180     _generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable, skipEntriesPreview)
1181     {
1182         var preview = this._createEmptyPreview();
1183         var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
1184
1185         var propertiesThreshold = {
1186             properties: isTable ? 1000 : max(5, firstLevelKeysCount),
1187             indexes: isTable ? 1000 : max(100, firstLevelKeysCount),
1188             __proto__: null
1189         };
1190
1191         try {
1192             var descriptors = injectedScript._propertyDescriptors(object);
1193
1194             if (firstLevelKeys) {
1195                 var nameToDescriptors = { __proto__: null };
1196                 for (var i = 0; i < descriptors.length; ++i) {
1197                     var descriptor = descriptors[i];
1198                     nameToDescriptors["#" + descriptor.name] = descriptor;
1199                 }
1200                 descriptors = [];
1201                 for (var i = 0; i < firstLevelKeys.length; ++i)
1202                     descriptors[i] = nameToDescriptors["#" + firstLevelKeys[i]];
1203             }
1204
1205             this._appendPropertyDescriptors(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable);
1206             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1207                 return preview;
1208
1209             // Add internal properties to preview.
1210             var internalProperties = InjectedScriptHost.getInternalProperties(object) || [];
1211             for (var i = 0; i < internalProperties.length; ++i) {
1212                 internalProperties[i] = nullifyObjectProto(internalProperties[i]);
1213                 internalProperties[i].enumerable = true;
1214             }
1215             this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable);
1216
1217             if (this.subtype === "map" || this.subtype === "set")
1218                 this._appendEntriesPreview(object, preview, skipEntriesPreview);
1219
1220         } catch (e) {
1221             preview.lossless = false;
1222         }
1223
1224         return preview;
1225     },
1226
1227     /**
1228      * @param {!RuntimeAgent.ObjectPreview} preview
1229      * @param {!Array.<!Object>} descriptors
1230      * @param {!Object} propertiesThreshold
1231      * @param {?Array.<string>=} secondLevelKeys
1232      * @param {boolean=} isTable
1233      */
1234     _appendPropertyDescriptors: function(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable)
1235     {
1236         for (var i = 0; i < descriptors.length; ++i) {
1237             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1238                 break;
1239
1240             var descriptor = descriptors[i];
1241             if (!descriptor)
1242                 continue;
1243             if (descriptor.wasThrown) {
1244                 preview.lossless = false;
1245                 continue;
1246             }
1247             if (!descriptor.enumerable && !descriptor.isOwn)
1248                 continue;
1249
1250             var name = descriptor.name;
1251             if (name === "__proto__")
1252                 continue;
1253             if (this.subtype === "array" && name === "length")
1254                 continue;
1255
1256             if (!("value" in descriptor)) {
1257                 preview.lossless = false;
1258                 this._appendPropertyPreview(preview, { name: name, type: "accessor", __proto__: null }, propertiesThreshold);
1259                 continue;
1260             }
1261
1262             var value = descriptor.value;
1263             if (value === null) {
1264                 this._appendPropertyPreview(preview, { name: name, type: "object", subtype: "null", value: "null", __proto__: null }, propertiesThreshold);
1265                 continue;
1266             }
1267
1268             var type = typeof value;
1269             if (!descriptor.enumerable && type === "function")
1270                 continue;
1271             if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
1272                 type = "object";
1273
1274             var maxLength = 100;
1275             if (InjectedScript.primitiveTypes[type]) {
1276                 if (type === "string" && value.length > maxLength) {
1277                     value = this._abbreviateString(value, maxLength, true);
1278                     preview.lossless = false;
1279                 }
1280                 this._appendPropertyPreview(preview, { name: name, type: type, value: toStringDescription(value), __proto__: null }, propertiesThreshold);
1281                 continue;
1282             }
1283
1284             var property = { name: name, type: type, __proto__: null };
1285             var subtype = injectedScript._subtype(value);
1286             if (subtype)
1287                 property.subtype = subtype;
1288
1289             if (secondLevelKeys === null || secondLevelKeys) {
1290                 var subPreview = this._generatePreview(value, secondLevelKeys || undefined, undefined, isTable);
1291                 property.valuePreview = subPreview;
1292                 if (!subPreview.lossless)
1293                     preview.lossless = false;
1294                 if (subPreview.overflow)
1295                     preview.overflow = true;
1296             } else {
1297                 var description = "";
1298                 if (type !== "function")
1299                     description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
1300                 property.value = description;
1301                 preview.lossless = false;
1302             }
1303             this._appendPropertyPreview(preview, property, propertiesThreshold);
1304         }
1305     },
1306
1307     /**
1308      * @param {!RuntimeAgent.ObjectPreview} preview
1309      * @param {!Object} property
1310      * @param {!Object} propertiesThreshold
1311      */
1312     _appendPropertyPreview: function(preview, property, propertiesThreshold)
1313     {
1314         if (toString(property.name >>> 0) === property.name)
1315             propertiesThreshold.indexes--;
1316         else
1317             propertiesThreshold.properties--;
1318         if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
1319             preview.overflow = true;
1320             preview.lossless = false;
1321         } else {
1322             push(preview.properties, property);
1323         }
1324     },
1325
1326     /**
1327      * @param {!Object} object
1328      * @param {!RuntimeAgent.ObjectPreview} preview
1329      * @param {boolean=} skipEntriesPreview
1330      */
1331     _appendEntriesPreview: function(object, preview, skipEntriesPreview)
1332     {
1333         var entries = InjectedScriptHost.collectionEntries(object);
1334         if (!entries)
1335             return;
1336         if (skipEntriesPreview) {
1337             if (entries.length) {
1338                 preview.overflow = true;
1339                 preview.lossless = false;
1340             }
1341             return;
1342         }
1343         preview.entries = [];
1344         var entriesThreshold = 5;
1345         for (var i = 0; i < entries.length; ++i) {
1346             if (preview.entries.length >= entriesThreshold) {
1347                 preview.overflow = true;
1348                 preview.lossless = false;
1349                 break;
1350             }
1351             var entry = nullifyObjectProto(entries[i]);
1352             var previewEntry = {
1353                 value: generateValuePreview(entry.value),
1354                 __proto__: null
1355             };
1356             if ("key" in entry)
1357                 previewEntry.key = generateValuePreview(entry.key);
1358             push(preview.entries, previewEntry);
1359         }
1360
1361         /**
1362          * @param {*} value
1363          * @return {!RuntimeAgent.ObjectPreview}
1364          */
1365         function generateValuePreview(value)
1366         {
1367             var remoteObject = new InjectedScript.RemoteObject(value, undefined, undefined, true, undefined, undefined, true);
1368             var valuePreview = remoteObject.preview || remoteObject._createEmptyPreview();
1369             if (remoteObject.objectId)
1370                 injectedScript.releaseObject(remoteObject.objectId);
1371             if (!valuePreview.lossless)
1372                 preview.lossless = false;
1373             return valuePreview;
1374         }
1375     },
1376
1377     /**
1378      * @param {string} string
1379      * @param {number} maxLength
1380      * @param {boolean=} middle
1381      * @return {string}
1382      */
1383     _abbreviateString: function(string, maxLength, middle)
1384     {
1385         if (string.length <= maxLength)
1386             return string;
1387         if (middle) {
1388             var leftHalf = maxLength >> 1;
1389             var rightHalf = maxLength - leftHalf - 1;
1390             return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1391         }
1392         return string.substr(0, maxLength) + "\u2026";
1393     },
1394
1395     __proto__: null
1396 }
1397 /**
1398  * @constructor
1399  * @param {number} ordinal
1400  * @param {!JavaScriptCallFrame} callFrame
1401  * @param {number} asyncOrdinal
1402  */
1403 InjectedScript.CallFrameProxy = function(ordinal, callFrame, asyncOrdinal)
1404 {
1405     this.callFrameId = "{\"ordinal\":" + ordinal + ",\"injectedScriptId\":" + injectedScriptId + (asyncOrdinal ? ",\"asyncOrdinal\":" + asyncOrdinal : "") + "}";
1406     this.functionName = (callFrame.type === "function" ? callFrame.functionName : "");
1407     this.location = { scriptId: toString(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column, __proto__: null };
1408     this.scopeChain = this._wrapScopeChain(callFrame);
1409     this.this = injectedScript._wrapObject(callFrame.thisObject, "backtrace");
1410     if (callFrame.isAtReturn)
1411         this.returnValue = injectedScript._wrapObject(callFrame.returnValue, "backtrace");
1412 }
1413
1414 InjectedScript.CallFrameProxy.prototype = {
1415     /**
1416      * @param {!JavaScriptCallFrame} callFrame
1417      * @return {!Array.<!DebuggerAgent.Scope>}
1418      */
1419     _wrapScopeChain: function(callFrame)
1420     {
1421         var scopeChain = callFrame.scopeChain;
1422         var scopeChainProxy = [];
1423         for (var i = 0; i < scopeChain.length; ++i)
1424             scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], "backtrace");
1425         return scopeChainProxy;
1426     },
1427
1428     __proto__: null
1429 }
1430
1431 /**
1432  * @param {number} scopeTypeCode
1433  * @param {*} scopeObject
1434  * @param {string} groupId
1435  * @return {!DebuggerAgent.Scope}
1436  */
1437 InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId)
1438 {
1439     const GLOBAL_SCOPE = 0;
1440     const LOCAL_SCOPE = 1;
1441     const WITH_SCOPE = 2;
1442     const CLOSURE_SCOPE = 3;
1443     const CATCH_SCOPE = 4;
1444
1445     /** @type {!Object.<number, string>} */
1446     var scopeTypeNames = { __proto__: null };
1447     scopeTypeNames[GLOBAL_SCOPE] = "global";
1448     scopeTypeNames[LOCAL_SCOPE] = "local";
1449     scopeTypeNames[WITH_SCOPE] = "with";
1450     scopeTypeNames[CLOSURE_SCOPE] = "closure";
1451     scopeTypeNames[CATCH_SCOPE] = "catch";
1452
1453     return {
1454         object: injectedScript._wrapObject(scopeObject, groupId),
1455         type: /** @type {!DebuggerAgent.ScopeType} */ (scopeTypeNames[scopeTypeCode]),
1456         __proto__: null
1457     };
1458 }
1459
1460 /**
1461  * @constructor
1462  * @param {!CommandLineAPIImpl} commandLineAPIImpl
1463  * @param {?JavaScriptCallFrame} callFrame
1464  */
1465 function CommandLineAPI(commandLineAPIImpl, callFrame)
1466 {
1467     /**
1468      * @param {string} member
1469      * @return {boolean}
1470      */
1471     function inScopeVariables(member)
1472     {
1473         if (!callFrame)
1474             return (member in inspectedWindow);
1475
1476         var scopeChain = callFrame.scopeChain;
1477         for (var i = 0; i < scopeChain.length; ++i) {
1478             if (member in scopeChain[i])
1479                 return true;
1480         }
1481         return false;
1482     }
1483
1484     /**
1485      * @param {string} name The name of the method for which a toString method should be generated.
1486      * @return {function():string}
1487      */
1488     function customToStringMethod(name)
1489     {
1490         return function()
1491         {
1492             var funcArgsSyntax = "";
1493             try {
1494                 var funcSyntax = "" + commandLineAPIImpl[name];
1495                 funcSyntax = funcSyntax.replace(/\n/g, " ");
1496                 funcSyntax = funcSyntax.replace(/^function[^\(]*\(([^\)]*)\).*$/, "$1");
1497                 funcSyntax = funcSyntax.replace(/\s*,\s*/g, ", ");
1498                 funcSyntax = funcSyntax.replace(/\bopt_(\w+)\b/g, "[$1]");
1499                 funcArgsSyntax = funcSyntax.trim();
1500             } catch (e) {
1501             }
1502             return "function " + name + "(" + funcArgsSyntax + ") { [Command Line API] }";
1503         };
1504     }
1505
1506     for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
1507         var member = CommandLineAPI.members_[i];
1508         if (inScopeVariables(member))
1509             continue;
1510
1511         this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
1512         this[member].toString = customToStringMethod(member);
1513     }
1514
1515     for (var i = 0; i < 5; ++i) {
1516         var member = "$" + i;
1517         if (inScopeVariables(member))
1518             continue;
1519
1520         this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
1521     }
1522
1523     this.$_ = injectedScript._lastResult;
1524
1525     this.__proto__ = null;
1526 }
1527
1528 // NOTE: Please keep the list of API methods below snchronized to that in WebInspector.RuntimeModel!
1529 // NOTE: Argument names of these methods will be printed in the console, so use pretty names!
1530 /**
1531  * @type {!Array.<string>}
1532  * @const
1533  */
1534 CommandLineAPI.members_ = [
1535     "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
1536     "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners",
1537     "debug", "undebug", "monitor", "unmonitor", "table"
1538 ];
1539
1540 /**
1541  * @constructor
1542  */
1543 function CommandLineAPIImpl()
1544 {
1545 }
1546
1547 CommandLineAPIImpl.prototype = {
1548     /**
1549      * @param {string} selector
1550      * @param {!Node=} opt_startNode
1551      * @return {*}
1552      */
1553     $: function (selector, opt_startNode)
1554     {
1555         if (this._canQuerySelectorOnNode(opt_startNode))
1556             return opt_startNode.querySelector(selector);
1557
1558         return inspectedWindow.document.querySelector(selector);
1559     },
1560
1561     /**
1562      * @param {string} selector
1563      * @param {!Node=} opt_startNode
1564      * @return {*}
1565      */
1566     $$: function (selector, opt_startNode)
1567     {
1568         if (this._canQuerySelectorOnNode(opt_startNode))
1569             return opt_startNode.querySelectorAll(selector);
1570         return inspectedWindow.document.querySelectorAll(selector);
1571     },
1572
1573     /**
1574      * @param {!Node=} node
1575      * @return {boolean}
1576      */
1577     _canQuerySelectorOnNode: function(node)
1578     {
1579         return !!node && InjectedScriptHost.subtype(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
1580     },
1581
1582     /**
1583      * @param {string} xpath
1584      * @param {!Node=} opt_startNode
1585      * @return {*}
1586      */
1587     $x: function(xpath, opt_startNode)
1588     {
1589         var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedWindow.document;
1590         var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null);
1591         switch (result.resultType) {
1592         case XPathResult.NUMBER_TYPE:
1593             return result.numberValue;
1594         case XPathResult.STRING_TYPE:
1595             return result.stringValue;
1596         case XPathResult.BOOLEAN_TYPE:
1597             return result.booleanValue;
1598         default:
1599             var nodes = [];
1600             var node;
1601             while (node = result.iterateNext())
1602                 push(nodes, node);
1603             return nodes;
1604         }
1605     },
1606
1607     /**
1608      * @return {*}
1609      */
1610     dir: function(var_args)
1611     {
1612         return InjectedScriptHost.callFunction(inspectedWindow.console.dir, inspectedWindow.console, slice(arguments));
1613     },
1614
1615     /**
1616      * @return {*}
1617      */
1618     dirxml: function(var_args)
1619     {
1620         return InjectedScriptHost.callFunction(inspectedWindow.console.dirxml, inspectedWindow.console, slice(arguments));
1621     },
1622
1623     /**
1624      * @return {!Array.<string>}
1625      */
1626     keys: function(object)
1627     {
1628         return Object.keys(object);
1629     },
1630
1631     /**
1632      * @return {!Array.<*>}
1633      */
1634     values: function(object)
1635     {
1636         var result = [];
1637         for (var key in object)
1638             push(result, object[key]);
1639         return result;
1640     },
1641
1642     /**
1643      * @return {*}
1644      */
1645     profile: function(opt_title)
1646     {
1647         return InjectedScriptHost.callFunction(inspectedWindow.console.profile, inspectedWindow.console, slice(arguments));
1648     },
1649
1650     /**
1651      * @return {*}
1652      */
1653     profileEnd: function(opt_title)
1654     {
1655         return InjectedScriptHost.callFunction(inspectedWindow.console.profileEnd, inspectedWindow.console, slice(arguments));
1656     },
1657
1658     /**
1659      * @param {!Object} object
1660      * @param {!Array.<string>|string=} opt_types
1661      */
1662     monitorEvents: function(object, opt_types)
1663     {
1664         if (!object || !object.addEventListener || !object.removeEventListener)
1665             return;
1666         var types = this._normalizeEventTypes(opt_types);
1667         for (var i = 0; i < types.length; ++i) {
1668             object.removeEventListener(types[i], this._logEvent, false);
1669             object.addEventListener(types[i], this._logEvent, false);
1670         }
1671     },
1672
1673     /**
1674      * @param {!Object} object
1675      * @param {!Array.<string>|string=} opt_types
1676      */
1677     unmonitorEvents: function(object, opt_types)
1678     {
1679         if (!object || !object.addEventListener || !object.removeEventListener)
1680             return;
1681         var types = this._normalizeEventTypes(opt_types);
1682         for (var i = 0; i < types.length; ++i)
1683             object.removeEventListener(types[i], this._logEvent, false);
1684     },
1685
1686     /**
1687      * @param {*} object
1688      * @return {*}
1689      */
1690     inspect: function(object)
1691     {
1692         return injectedScript._inspect(object);
1693     },
1694
1695     copy: function(object)
1696     {
1697         var string;
1698         if (injectedScript._subtype(object) === "node") {
1699             string = object.outerHTML;
1700         } else if (injectedScript.isPrimitiveValue(object)) {
1701             string = toString(object);
1702         } else {
1703             try {
1704                 string = JSON.stringify(object, null, "  ");
1705             } catch (e) {
1706                 string = toString(object);
1707             }
1708         }
1709
1710         var hints = { copyToClipboard: true, __proto__: null };
1711         var remoteObject = injectedScript._wrapObject(string, "")
1712         InjectedScriptHost.inspect(remoteObject, hints);
1713     },
1714
1715     clear: function()
1716     {
1717         InjectedScriptHost.clearConsoleMessages();
1718     },
1719
1720     /**
1721      * @param {!Node} node
1722      * @return {!Array.<!{type: string, listener: function(), useCapture: boolean, remove: function()}>|undefined}
1723      */
1724     getEventListeners: function(node)
1725     {
1726         var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node));
1727         if (!result)
1728             return result;
1729         /** @this {{type: string, listener: function(), useCapture: boolean}} */
1730         var removeFunc = function()
1731         {
1732             node.removeEventListener(this.type, this.listener, this.useCapture);
1733         }
1734         for (var type in result) {
1735             var listeners = result[type];
1736             for (var i = 0, listener; listener = listeners[i]; ++i) {
1737                 listener["type"] = type;
1738                 listener["remove"] = removeFunc;
1739             }
1740         }
1741         return result;
1742     },
1743
1744     debug: function(fn)
1745     {
1746         InjectedScriptHost.debugFunction(fn);
1747     },
1748
1749     undebug: function(fn)
1750     {
1751         InjectedScriptHost.undebugFunction(fn);
1752     },
1753
1754     monitor: function(fn)
1755     {
1756         InjectedScriptHost.monitorFunction(fn);
1757     },
1758
1759     unmonitor: function(fn)
1760     {
1761         InjectedScriptHost.unmonitorFunction(fn);
1762     },
1763
1764     table: function(data, opt_columns)
1765     {
1766         InjectedScriptHost.callFunction(inspectedWindow.console.table, inspectedWindow.console, slice(arguments));
1767     },
1768
1769     /**
1770      * @param {number} num
1771      */
1772     _inspectedObject: function(num)
1773     {
1774         return InjectedScriptHost.inspectedObject(num);
1775     },
1776
1777     /**
1778      * @param {!Array.<string>|string=} types
1779      * @return {!Array.<string>}
1780      */
1781     _normalizeEventTypes: function(types)
1782     {
1783         if (typeof types === "undefined")
1784             types = ["mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation"];
1785         else if (typeof types === "string")
1786             types = [types];
1787
1788         var result = [];
1789         for (var i = 0; i < types.length; ++i) {
1790             if (types[i] === "mouse")
1791                 push(result, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
1792             else if (types[i] === "key")
1793                 push(result, "keydown", "keyup", "keypress", "textInput");
1794             else if (types[i] === "touch")
1795                 push(result, "touchstart", "touchmove", "touchend", "touchcancel");
1796             else if (types[i] === "control")
1797                 push(result, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
1798             else
1799                 push(result, types[i]);
1800         }
1801         return result;
1802     },
1803
1804     /**
1805      * @param {!Event} event
1806      */
1807     _logEvent: function(event)
1808     {
1809         inspectedWindow.console.log(event.type, event);
1810     }
1811 }
1812
1813 injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
1814 return injectedScript;
1815 })