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