813e9dc8f5d32fa119087f25f8251656c1e87ae8
[WebKit-https.git] / Source / JavaScriptCore / inspector / InjectedScriptSource.js
1 /*
2  * Copyright (C) 2007, 2014-2015 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 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 //# sourceURL=__InjectedScript_InjectedScriptSource.js
31
32 (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) {
33
34 // FIXME: <https://webkit.org/b/152294> Web Inspector: Parse InjectedScriptSource as a built-in to get guaranteed non-user-overridden built-ins
35
36 var Object = {}.constructor;
37
38 function toString(obj)
39 {
40     return String(obj);
41 }
42
43 function toStringDescription(obj)
44 {
45     if (obj === 0 && 1 / obj < 0)
46         return "-0";
47
48     return toString(obj);
49 }
50
51 function isUInt32(obj)
52 {
53     if (typeof obj === "number")
54         return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
55     return "" + (obj >>> 0) === obj;
56 }
57
58 function isSymbol(obj)
59 {
60     return typeof obj === "symbol";
61 }
62
63 function isEmptyObject(object)
64 {
65     for (let key in object)
66         return false;
67     return true;
68 }
69
70 function isDefined(value)
71 {
72     return !!value || InjectedScriptHost.isHTMLAllCollection(value);
73 }
74
75 function isPrimitiveValue(value)
76 {
77     switch (typeof value) {
78     case "boolean":
79     case "number":
80     case "string":
81         return true;
82     case "undefined":
83         return !InjectedScriptHost.isHTMLAllCollection(value);
84     default:
85         return false;
86     }
87 }
88
89 // -------
90
91 let InjectedScript = class InjectedScript
92 {
93     constructor()
94     {
95         this._lastBoundObjectId = 1;
96         this._idToWrappedObject = {};
97         this._idToObjectGroupName = {};
98         this._objectGroups = {};
99         this._modules = {};
100         this._nextSavedResultIndex = 1;
101         this._savedResults = [];
102     }
103
104     // InjectedScript C++ API
105
106     execute(functionString, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult, args)
107     {
108         return this._wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, () => {
109             const isEvalOnCallFrame = false;
110             return this._evaluateOn(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, functionString, isEvalOnCallFrame, includeCommandLineAPI).apply(undefined, args);
111         });
112     }
113
114     evaluate(expression, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult)
115     {
116         const isEvalOnCallFrame = false;
117         return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult);
118     }
119
120     awaitPromise(promiseObjectId, returnByValue, generatePreview, saveResult, callback)
121     {
122         let parsedPromiseObjectId = this._parseObjectId(promiseObjectId);
123         let promiseObject = this._objectForId(parsedPromiseObjectId);
124         let promiseObjectGroupName = this._idToObjectGroupName[parsedPromiseObjectId.id];
125
126         if (!isDefined(promiseObject)) {
127             callback("Could not find object with given id");
128             return;
129         }
130
131         if (!(promiseObject instanceof Promise)) {
132             callback("Object with given id is not a Promise");
133             return;
134         }
135
136         let resolve = (value) => {
137             let returnObject = {
138                 wasThrown: false,
139                 result: RemoteObject.create(value, promiseObjectGroupName, returnByValue, generatePreview),
140             };
141
142             if (saveResult) {
143                 this._savedResultIndex = 0;
144                 this._saveResult(returnObject.result);
145                 if (this._savedResultIndex)
146                     returnObject.savedResultIndex = this._savedResultIndex;
147             }
148
149             callback(returnObject);
150         };
151         let reject = (reason) => {
152             callback(this._createThrownValue(reason, promiseObjectGroupName));
153         };
154         promiseObject.then(resolve, reject);
155     }
156
157     evaluateOnCallFrame(topCallFrame, callFrameId, expression, objectGroup, includeCommandLineAPI, returnByValue, generatePreview, saveResult)
158     {
159         let callFrame = this._callFrameForId(topCallFrame, callFrameId);
160         if (!callFrame)
161             return "Could not find call frame with given id";
162         const isEvalOnCallFrame = true;
163         return this._evaluateAndWrap(callFrame.evaluateWithScopeExtension, callFrame, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult);
164     }
165
166     callFunctionOn(objectId, expression, args, returnByValue, generatePreview)
167     {
168         let parsedObjectId = this._parseObjectId(objectId);
169         let object = this._objectForId(parsedObjectId);
170         let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
171
172         if (!isDefined(object))
173             return "Could not find object with given id";
174
175         let resolvedArgs = [];
176         if (args) {
177             let callArgs = InjectedScriptHost.evaluate(args);
178             for (let i = 0; i < callArgs.length; ++i) {
179                 try {
180                     resolvedArgs[i] = this._resolveCallArgument(callArgs[i]);
181                 } catch (e) {
182                     return String(e);
183                 }
184             }
185         }
186
187         try {
188             let func = InjectedScriptHost.evaluate("(" + expression + ")");
189             if (typeof func !== "function")
190                 return "Given expression does not evaluate to a function";
191
192             return {
193                 wasThrown: false,
194                 result: RemoteObject.create(func.apply(object, resolvedArgs), objectGroupName, returnByValue, generatePreview)
195             };
196         } catch (e) {
197             return this._createThrownValue(e, objectGroupName);
198         }
199     }
200
201     getFunctionDetails(objectId)
202     {
203         let parsedObjectId = this._parseObjectId(objectId);
204         let object = this._objectForId(parsedObjectId);
205         if (typeof object !== "function")
206             return "Cannot resolve function by id.";
207         return this.functionDetails(object);
208     }
209
210     functionDetails(func)
211     {
212         let details = InjectedScriptHost.functionDetails(func);
213         if (!details)
214             return "Cannot resolve function details.";
215         return details;
216     }
217
218     getPreview(objectId)
219     {
220         let parsedObjectId = this._parseObjectId(objectId);
221         let object = this._objectForId(parsedObjectId);
222         return RemoteObject.createObjectPreviewForValue(object, true);
223     }
224
225     getProperties(objectId, ownProperties, generatePreview)
226     {
227         let nativeGettersAsValues = false;
228         let collectionMode = ownProperties ? InjectedScript.CollectionMode.OwnProperties : InjectedScript.CollectionMode.AllProperties;
229         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
230     }
231
232     getDisplayableProperties(objectId, generatePreview)
233     {
234         let nativeGettersAsValues = true;
235         let collectionMode = InjectedScript.CollectionMode.OwnProperties | InjectedScript.CollectionMode.NativeGetterProperties;
236         return this._getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues);
237     }
238
239     getInternalProperties(objectId, generatePreview)
240     {
241         let parsedObjectId = this._parseObjectId(objectId);
242         let object = this._objectForId(parsedObjectId);
243         let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
244
245         if (!isDefined(object))
246             return false;
247
248         if (isSymbol(object))
249             return false;
250
251         let descriptors = this._internalPropertyDescriptors(object);
252         if (!descriptors)
253             return [];
254
255         for (let i = 0; i < descriptors.length; ++i) {
256             let descriptor = descriptors[i];
257             if ("value" in descriptor)
258                 descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
259         }
260
261         return descriptors;
262     }
263
264     getCollectionEntries(objectId, objectGroupName, startIndex, numberToFetch)
265     {
266         let parsedObjectId = this._parseObjectId(objectId);
267         let object = this._objectForId(parsedObjectId);
268         objectGroupName = objectGroupName || this._idToObjectGroupName[parsedObjectId.id];
269
270         if (!isDefined(object))
271             return;
272
273         if (typeof object !== "object")
274             return;
275
276         let entries = this._entries(object, InjectedScriptHost.subtype(object), startIndex, numberToFetch);
277         return entries.map(function(entry) {
278             entry.value = RemoteObject.create(entry.value, objectGroupName, false, true);
279             if ("key" in entry)
280                 entry.key = RemoteObject.create(entry.key, objectGroupName, false, true);
281             return entry;
282         });
283     }
284
285     saveResult(callArgumentJSON)
286     {
287         this._savedResultIndex = 0;
288
289         try {
290             let callArgument = InjectedScriptHost.evaluate("(" + callArgumentJSON + ")");
291             let value = this._resolveCallArgument(callArgument);
292             this._saveResult(value);
293         } catch (e) {}
294
295         return this._savedResultIndex;
296     }
297
298     wrapCallFrames(callFrame)
299     {
300         if (!callFrame)
301             return false;
302
303         let result = [];
304         let depth = 0;
305         do {
306             result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
307             callFrame = callFrame.caller;
308         } while (callFrame);
309         return result;
310     }
311
312     wrapObject(object, groupName, canAccessInspectedGlobalObject, generatePreview)
313     {
314         if (!canAccessInspectedGlobalObject)
315             return this._fallbackWrapper(object);
316
317         return RemoteObject.create(object, groupName, false, generatePreview);
318     }
319
320     wrapJSONString(jsonString, groupName, generatePreview)
321     {
322         try {
323             return this.wrapObject(JSON.parse(jsonString), groupName, true, generatePreview);
324         } catch {
325             return null;
326         }
327     }
328
329     wrapTable(canAccessInspectedGlobalObject, table, columns)
330     {
331         if (!canAccessInspectedGlobalObject)
332             return this._fallbackWrapper(table);
333
334         // FIXME: Currently columns are ignored. Instead, the frontend filters all
335         // properties based on the provided column names and in the provided order.
336         // We could filter here to avoid sending very large preview objects.
337
338         let columnNames = null;
339         if (typeof columns === "string")
340             columns = [columns];
341
342         if (InjectedScriptHost.subtype(columns) === "array") {
343             columnNames = [];
344             for (let i = 0; i < columns.length; ++i)
345                 columnNames.push(toString(columns[i]));
346         }
347
348         return RemoteObject.create(table, "console", false, true, columnNames);
349     }
350
351     previewValue(value)
352     {
353         return RemoteObject.createObjectPreviewForValue(value, true);
354     }
355
356     setExceptionValue(value)
357     {
358         this._exceptionValue = value;
359     }
360
361     clearExceptionValue()
362     {
363         delete this._exceptionValue;
364     }
365
366     findObjectById(objectId)
367     {
368         let parsedObjectId = this._parseObjectId(objectId);
369         return this._objectForId(parsedObjectId);
370     }
371
372     inspectObject(object)
373     {
374         if (this._commandLineAPIImpl)
375             this._commandLineAPIImpl.inspect(object);
376     }
377
378     releaseObject(objectId)
379     {
380         let parsedObjectId = this._parseObjectId(objectId);
381         this._releaseObject(parsedObjectId.id);
382     }
383
384     releaseObjectGroup(objectGroupName)
385     {
386         if (objectGroupName === "console") {
387             delete this._lastResult;
388             this._nextSavedResultIndex = 1;
389             this._savedResults = [];
390         }
391
392         let group = this._objectGroups[objectGroupName];
393         if (!group)
394             return;
395
396         for (let i = 0; i < group.length; i++)
397             this._releaseObject(group[i]);
398
399         delete this._objectGroups[objectGroupName];
400     }
401
402     // InjectedScriptModule C++ API
403
404     module(name)
405     {
406         return this._modules[name];
407     }
408
409     injectModule(name, source, host)
410     {
411         delete this._modules[name];
412
413         let moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
414         if (typeof moduleFunction !== "function") {
415             if (inspectedGlobalObject.console)
416                 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
417             return null;
418         }
419
420         let module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, RemoteObject, host);
421         this._modules[name] = module;
422         return module;
423     }
424
425     // InjectedScriptModule JavaScript API
426
427     isPrimitiveValue(value)
428     {
429         return isPrimitiveValue(value);
430     }
431
432     // Private
433
434     _parseObjectId(objectId)
435     {
436         return InjectedScriptHost.evaluate("(" + objectId + ")");
437     }
438
439     _objectForId(objectId)
440     {
441         return this._idToWrappedObject[objectId.id];
442     }
443
444     _bind(object, objectGroupName)
445     {
446         let id = this._lastBoundObjectId++;
447         let objectId = `{"injectedScriptId":${injectedScriptId},"id":${id}}`;
448
449         this._idToWrappedObject[id] = object;
450
451         if (objectGroupName) {
452             let group = this._objectGroups[objectGroupName];
453             if (!group) {
454                 group = [];
455                 this._objectGroups[objectGroupName] = group;
456             }
457             group.push(id);
458             this._idToObjectGroupName[id] = objectGroupName;
459         }
460
461         return objectId;
462     }
463
464     _releaseObject(id)
465     {
466         delete this._idToWrappedObject[id];
467         delete this._idToObjectGroupName[id];
468     }
469
470     _fallbackWrapper(object)
471     {
472         let result = {};
473         result.type = typeof object;
474         if (isPrimitiveValue(object))
475             result.value = object;
476         else
477             result.description = toString(object);
478         return result;
479     }
480
481     _resolveCallArgument(callArgumentJSON)
482     {
483         if ("value" in callArgumentJSON)
484             return callArgumentJSON.value;
485
486         let objectId = callArgumentJSON.objectId;
487         if (objectId) {
488             let parsedArgId = this._parseObjectId(objectId);
489             if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
490                 throw "Arguments should belong to the same JavaScript world as the target object.";
491
492             let resolvedArg = this._objectForId(parsedArgId);
493             if (!isDefined(resolvedArg))
494                 throw "Could not find object with given id";
495
496             return resolvedArg;
497         }
498
499         return undefined;
500     }
501
502     _createThrownValue(value, objectGroup)
503     {
504         let remoteObject = RemoteObject.create(value, objectGroup);
505         try {
506             remoteObject.description = toStringDescription(value);
507         } catch (e) {}
508         return {
509             wasThrown: true,
510             result: remoteObject
511         };
512     }
513
514     _evaluateAndWrap(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult)
515     {
516         return this._wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, () => {
517             return this._evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI);
518         });
519     }
520
521     _wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, func)
522     {
523         return this._wrapCall(objectGroup, returnByValue, generatePreview, saveResult, () => {
524             let result = func();
525             if (saveResult)
526                 this._saveResult(result);
527             return result;
528         });
529     }
530
531     _wrapCall(objectGroup, returnByValue, generatePreview, saveResult, func)
532     {
533         try {
534             this._savedResultIndex = 0;
535
536             let returnObject = {
537                 wasThrown: false,
538                 result: RemoteObject.create(func(), objectGroup, returnByValue, generatePreview)
539             };
540
541             if (saveResult && this._savedResultIndex)
542                 returnObject.savedResultIndex = this._savedResultIndex;
543
544             return returnObject;
545         } catch (e) {
546             return this._createThrownValue(e, objectGroup);
547         }
548     }
549
550     _evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI)
551     {
552         let commandLineAPI = null;
553         if (includeCommandLineAPI) {
554             if (this.CommandLineAPI)
555                 commandLineAPI = new this.CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
556             else
557                 commandLineAPI = new BasicCommandLineAPI(isEvalOnCallFrame ? object : null);
558         }
559
560         return evalFunction.call(object, expression, commandLineAPI);
561     }
562
563     _callFrameForId(topCallFrame, callFrameId)
564     {
565         let parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
566         let ordinal = parsedCallFrameId["ordinal"];
567         let callFrame = topCallFrame;
568         while (--ordinal >= 0 && callFrame)
569             callFrame = callFrame.caller;
570         return callFrame;
571     }
572
573     _getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues)
574     {
575         let parsedObjectId = this._parseObjectId(objectId);
576         let object = this._objectForId(parsedObjectId);
577         let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
578
579         if (!isDefined(object))
580             return false;
581
582         if (isSymbol(object))
583             return false;
584
585         let descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);
586
587         for (let i = 0; i < descriptors.length; ++i) {
588             let descriptor = descriptors[i];
589             if ("get" in descriptor)
590                 descriptor.get = RemoteObject.create(descriptor.get, objectGroupName);
591             if ("set" in descriptor)
592                 descriptor.set = RemoteObject.create(descriptor.set, objectGroupName);
593             if ("value" in descriptor)
594                 descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
595             if (!("configurable" in descriptor))
596                 descriptor.configurable = false;
597             if (!("enumerable" in descriptor))
598                 descriptor.enumerable = false;
599             if ("symbol" in descriptor)
600                 descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName);
601         }
602
603         return descriptors;
604     }
605
606     _internalPropertyDescriptors(object, completeDescriptor)
607     {
608         let internalProperties = InjectedScriptHost.getInternalProperties(object);
609         if (!internalProperties)
610             return null;
611
612         let descriptors = [];
613         for (let i = 0; i < internalProperties.length; i++) {
614             let property = internalProperties[i];
615             let descriptor = {name: property.name, value: property.value};
616             if (completeDescriptor) {
617                 descriptor.writable = false;
618                 descriptor.configurable = false;
619                 descriptor.enumerable = false;
620                 descriptor.isOwn = true;
621             }
622             descriptors.push(descriptor);
623         }
624         return descriptors;
625     }
626
627     _propertyDescriptors(object, collectionMode, nativeGettersAsValues)
628     {
629         if (InjectedScriptHost.subtype(object) === "proxy")
630             return [];
631
632         let descriptors = [];
633         let nameProcessed = new Set;
634
635         function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter)
636         {
637             try {
638                 let fakeDescriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
639                 if (possibleNativeBindingGetter)
640                     fakeDescriptor.nativeGetter = true;
641                 if (isOwnProperty)
642                     fakeDescriptor.isOwn = true;
643                 if (symbol)
644                     fakeDescriptor.symbol = symbol;
645                 // Silence any possible unhandledrejection exceptions created from accessing a native accessor with a wrong this object.
646                 if (fakeDescriptor.value instanceof Promise)
647                     fakeDescriptor.value.catch(function(){});
648                 return fakeDescriptor;
649             } catch (e) {
650                 let errorDescriptor = {name, value: e, wasThrown: true};
651                 if (isOwnProperty)
652                     errorDescriptor.isOwn = true;
653                 if (symbol)
654                     errorDescriptor.symbol = symbol;
655                 return errorDescriptor;
656             }
657         }
658
659         function processDescriptor(descriptor, isOwnProperty, possibleNativeBindingGetter)
660         {
661             // All properties.
662             if (collectionMode & InjectedScript.CollectionMode.AllProperties) {
663                 descriptors.push(descriptor);
664                 return;
665             }
666
667             // Own properties.
668             if (collectionMode & InjectedScript.CollectionMode.OwnProperties && isOwnProperty) {
669                 descriptors.push(descriptor);
670                 return;
671             }
672
673             // Native Getter properties.
674             if (collectionMode & InjectedScript.CollectionMode.NativeGetterProperties) {
675                 if (possibleNativeBindingGetter) {
676                     descriptors.push(descriptor);
677                     return;
678                 }
679             }
680         }
681
682         function processProperties(o, properties, isOwnProperty)
683         {
684             for (let i = 0; i < properties.length; ++i) {
685                 let property = properties[i];
686                 if (nameProcessed.has(property) || property === "__proto__")
687                     continue;
688
689                 nameProcessed.add(property);
690
691                 let name = toString(property);
692                 let symbol = isSymbol(property) ? property : null;
693
694                 let descriptor = Object.getOwnPropertyDescriptor(o, property);
695                 if (!descriptor) {
696                     // FIXME: Bad descriptor. Can we get here?
697                     // Fall back to very restrictive settings.
698                     let fakeDescriptor = createFakeValueDescriptor(name, symbol, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
699                     processDescriptor(fakeDescriptor, isOwnProperty);
700                     continue;
701                 }
702
703                 if (nativeGettersAsValues) {
704                     if (String(descriptor.get).endsWith("[native code]\n}") || (!descriptor.get && descriptor.hasOwnProperty("get") && !descriptor.set && descriptor.hasOwnProperty("set"))) {
705                         // Developers may create such a descriptor, so we should be resilient:
706                         // let x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
707                         let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, true);
708                         processDescriptor(fakeDescriptor, isOwnProperty, true);
709                         continue;
710                     }
711                 }
712
713                 descriptor.name = name;
714                 if (isOwnProperty)
715                     descriptor.isOwn = true;
716                 if (symbol)
717                     descriptor.symbol = symbol;
718                 processDescriptor(descriptor, isOwnProperty);
719             }
720         }
721
722         function arrayIndexPropertyNames(o, length)
723         {
724             let array = [];
725             for (let i = 0; i < length; ++i) {
726                 if (i in o)
727                     array.push("" + i);
728             }
729             return array;
730         }
731
732         // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
733         // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.
734         let isArrayLike = false;
735         try {
736             isArrayLike = RemoteObject.subtype(object) === "array" && isFinite(object.length) && object.length > 0;
737         } catch(e) {}
738
739         for (let o = object; isDefined(o); o = Object.getPrototypeOf(o)) {
740             let isOwnProperty = o === object;
741
742             if (isArrayLike && isOwnProperty)
743                 processProperties(o, arrayIndexPropertyNames(o, Math.min(object.length, 100)), isOwnProperty);
744             else {
745                 processProperties(o, Object.getOwnPropertyNames(o), isOwnProperty);
746                 if (Object.getOwnPropertySymbols)
747                     processProperties(o, Object.getOwnPropertySymbols(o), isOwnProperty);
748             }
749
750             if (collectionMode === InjectedScript.CollectionMode.OwnProperties)
751                 break;
752         }
753
754         // Always include __proto__ at the end.
755         try {
756             if (object.__proto__)
757                 descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
758         } catch (e) {}
759
760         return descriptors;
761     }
762
763     _getSetEntries(object, skip, numberToFetch)
764     {
765         let entries = [];
766
767         // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator].
768         for (let value of object) {
769             if (skip > 0) {
770                 skip--;
771                 continue;
772             }
773
774             entries.push({value});
775
776             if (numberToFetch && entries.length === numberToFetch)
777                 break;
778         }
779
780         return entries;
781     }
782
783     _getMapEntries(object, skip, numberToFetch)
784     {
785         let entries = [];
786
787         // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator].
788         for (let [key, value] of object) {
789             if (skip > 0) {
790                 skip--;
791                 continue;
792             }
793
794             entries.push({key, value});
795
796             if (numberToFetch && entries.length === numberToFetch)
797                 break;
798         }
799
800         return entries;
801     }
802
803     _getWeakMapEntries(object, numberToFetch)
804     {
805         return InjectedScriptHost.weakMapEntries(object, numberToFetch);
806     }
807
808     _getWeakSetEntries(object, numberToFetch)
809     {
810         return InjectedScriptHost.weakSetEntries(object, numberToFetch);
811     }
812
813     _getIteratorEntries(object, numberToFetch)
814     {
815         return InjectedScriptHost.iteratorEntries(object, numberToFetch);
816     }
817
818     _entries(object, subtype, startIndex, numberToFetch)
819     {
820         if (subtype === "set")
821             return this._getSetEntries(object, startIndex, numberToFetch);
822         if (subtype === "map")
823             return this._getMapEntries(object, startIndex, numberToFetch);
824         if (subtype === "weakmap")
825             return this._getWeakMapEntries(object, numberToFetch);
826         if (subtype === "weakset")
827             return this._getWeakSetEntries(object, numberToFetch);
828         if (subtype === "iterator")
829             return this._getIteratorEntries(object, numberToFetch);
830
831         throw "unexpected type";
832     }
833
834     _saveResult(result)
835     {
836         this._lastResult = result;
837
838         if (result === undefined || result === null)
839             return;
840
841         let existingIndex = this._savedResults.indexOf(result);
842         if (existingIndex !== -1) {
843             this._savedResultIndex = existingIndex;
844             return;
845         }
846
847         this._savedResultIndex = this._nextSavedResultIndex;
848         this._savedResults[this._nextSavedResultIndex++] = result;
849
850         // $n is limited from $1-$99. $0 is special.
851         if (this._nextSavedResultIndex >= 100)
852             this._nextSavedResultIndex = 1;
853     }
854
855     _savedResult(index)
856     {
857         return this._savedResults[index];
858     }
859 }
860
861 InjectedScript.CollectionMode = {
862     OwnProperties: 1 << 0,          // own properties.
863     NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain.
864     AllProperties: 1 << 2,          // all properties in the prototype chain.
865 };
866
867 var injectedScript = new InjectedScript;
868
869 // -------
870
871 let RemoteObject = class RemoteObject
872 {
873     constructor(object, objectGroupName, forceValueType, generatePreview, columnNames)
874     {
875         this.type = typeof object;
876
877         if (this.type === "undefined" && InjectedScriptHost.isHTMLAllCollection(object))
878             this.type = "object";
879
880         if (isPrimitiveValue(object) || object === null || forceValueType) {
881             // We don't send undefined values over JSON.
882             if (this.type !== "undefined")
883                 this.value = object;
884
885             // Null object is object with 'null' subtype.
886             if (object === null)
887                 this.subtype = "null";
888
889             // Provide user-friendly number values.
890             if (this.type === "number")
891                 this.description = toStringDescription(object);
892             return;
893         }
894
895         this.objectId = injectedScript._bind(object, objectGroupName);
896
897         let subtype = RemoteObject.subtype(object);
898         if (subtype)
899             this.subtype = subtype;
900
901         this.className = InjectedScriptHost.internalConstructorName(object);
902         this.description = RemoteObject.describe(object);
903
904         if (subtype === "array")
905             this.size = typeof object.length === "number" ? object.length : 0;
906         else if (subtype === "set" || subtype === "map")
907             this.size = object.size;
908         else if (subtype === "weakmap")
909             this.size = InjectedScriptHost.weakMapSize(object);
910         else if (subtype === "weakset")
911             this.size = InjectedScriptHost.weakSetSize(object);
912         else if (subtype === "class") {
913             this.classPrototype = RemoteObject.create(object.prototype, objectGroupName);
914             this.className = object.name;
915         }
916
917         if (generatePreview && this.type === "object") {
918             if (subtype === "proxy") {
919                 this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object));
920                 this.preview.lossless = false;
921             } else
922                 this.preview = this._generatePreview(object, undefined, columnNames);
923         }
924     }
925
926     // Static
927
928     static create(object, objectGroupName, forceValueType, generatePreview, columnNames)
929     {
930         try {
931             return new RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
932         } catch (e) {
933             let description;
934             try {
935                 description = RemoteObject.describe(e);
936             } catch (ex) {
937                 alert(ex.message);
938                 description = "<failed to convert exception to string>";
939             }
940             return new RemoteObject(description);
941         }
942     }
943
944     static createObjectPreviewForValue(value, generatePreview, columnNames)
945     {
946         let remoteObject = new RemoteObject(value, undefined, false, generatePreview, columnNames);
947         if (remoteObject.objectId)
948             injectedScript.releaseObject(remoteObject.objectId);
949         if (remoteObject.classPrototype && remoteObject.classPrototype.objectId)
950             injectedScript.releaseObject(remoteObject.classPrototype.objectId);
951         return remoteObject.preview || remoteObject._emptyPreview();
952     }
953
954     static subtype(value)
955     {
956         if (value === null)
957             return "null";
958
959         if (isPrimitiveValue(value) || isSymbol(value))
960             return null;
961
962         if (InjectedScriptHost.isHTMLAllCollection(value))
963             return "array";
964
965         let preciseType = InjectedScriptHost.subtype(value);
966         if (preciseType)
967             return preciseType;
968
969         // FireBug's array detection.
970         try {
971             if (typeof value.splice === "function" && isFinite(value.length))
972                 return "array";
973         } catch (e) {}
974
975         return null;
976     }
977
978     static describe(value)
979     {
980         if (isPrimitiveValue(value))
981             return null;
982
983         if (isSymbol(value))
984             return toString(value);
985
986         let subtype = RemoteObject.subtype(value);
987
988         if (subtype === "regexp")
989             return toString(value);
990
991         if (subtype === "date")
992             return toString(value);
993
994         if (subtype === "error")
995             return toString(value);
996
997         if (subtype === "proxy")
998             return "Proxy";
999
1000         if (subtype === "node")
1001             return RemoteObject.nodePreview(value);
1002
1003         let className = InjectedScriptHost.internalConstructorName(value);
1004         if (subtype === "array")
1005             return className;
1006
1007         if (subtype === "iterator" && Symbol.toStringTag in value)
1008             return value[Symbol.toStringTag];
1009
1010         // NodeList in JSC is a function, check for array prior to this.
1011         if (typeof value === "function")
1012             return value.toString();
1013
1014         // If Object, try for a better name from the constructor.
1015         if (className === "Object") {
1016             let constructorName = value.constructor && value.constructor.name;
1017             if (constructorName)
1018                 return constructorName;
1019         }
1020
1021         return className;
1022     }
1023
1024     static nodePreview(node)
1025     {
1026         let isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion;
1027         let nodeName = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase();
1028
1029         switch (node.nodeType) {
1030         case 1: // Node.ELEMENT_NODE
1031             if (node.id)
1032                 return "<" + nodeName + " id=\"" + node.id + "\">";
1033             if (node.classList.length)
1034                 return "<" + nodeName + " class=\"" + node.classList.toString().replace(/\s+/, " ") + "\">";
1035             if (nodeName === "input" && node.type)
1036                 return "<" + nodeName + " type=\"" + node.type + "\">";
1037             return "<" + nodeName + ">";
1038
1039         case 3: // Node.TEXT_NODE
1040             return nodeName + " \"" + node.nodeValue + "\"";
1041
1042         case 8: // Node.COMMENT_NODE
1043             return "<!--" + node.nodeValue + "-->";
1044
1045         case 10: // Node.DOCUMENT_TYPE_NODE
1046             return "<!DOCTYPE " + nodeName + ">";
1047
1048         default:
1049             return nodeName;
1050         }
1051     }
1052
1053     // Private
1054
1055     _initialPreview()
1056     {
1057         let preview = {
1058             type: this.type,
1059             description: this.description || toString(this.value),
1060             lossless: true,
1061         };
1062
1063         if (this.subtype) {
1064             preview.subtype = this.subtype;
1065             if (this.subtype !== "null") {
1066                 preview.overflow = false;
1067                 preview.properties = [];
1068             }
1069         }
1070
1071         if ("size" in this)
1072             preview.size = this.size;
1073
1074         return preview;
1075     }
1076
1077     _emptyPreview()
1078     {
1079         let preview = this._initialPreview();
1080
1081         if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator") {
1082             if (this.size) {
1083                 preview.entries = [];
1084                 preview.lossless = false;
1085                 preview.overflow = true;
1086             }
1087         }
1088
1089         return preview;
1090     }
1091
1092     _generatePreview(object, firstLevelKeys, secondLevelKeys)
1093     {
1094         let preview = this._initialPreview();
1095         let isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
1096         let firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
1097
1098         let propertiesThreshold = {
1099             properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
1100             indexes: isTableRowsRequest ? 1000 : Math.max(10, firstLevelKeysCount)
1101         };
1102
1103         try {
1104             // Maps, Sets, and Iterators have entries.
1105             if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator")
1106                 this._appendEntryPreviews(object, preview);
1107
1108             preview.properties = [];
1109
1110             // Internal Properties.
1111             let internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true);
1112             if (internalPropertyDescriptors) {
1113                 this._appendPropertyPreviews(object, preview, internalPropertyDescriptors, true, propertiesThreshold, firstLevelKeys, secondLevelKeys);
1114                 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1115                     return preview;
1116             }
1117
1118             if (preview.entries)
1119                 return preview;
1120
1121             // Properties.
1122             let nativeGettersAsValues = true;
1123             let descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, nativeGettersAsValues);
1124             this._appendPropertyPreviews(object, preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
1125             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1126                 return preview;
1127         } catch (e) {
1128             preview.lossless = false;
1129         }
1130
1131         return preview;
1132     }
1133
1134     _appendPropertyPreviews(object, preview, descriptors, internal, propertiesThreshold, firstLevelKeys, secondLevelKeys)
1135     {
1136         for (let i = 0; i < descriptors.length; ++i) {
1137             let descriptor = descriptors[i];
1138
1139             // Seen enough.
1140             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1141                 break;
1142
1143             // Error in descriptor.
1144             if (descriptor.wasThrown) {
1145                 preview.lossless = false;
1146                 continue;
1147             }
1148
1149             // Do not show "__proto__" in preview.
1150             let name = descriptor.name;
1151             if (name === "__proto__") {
1152                 // Non basic __proto__ objects may have interesting, non-enumerable, methods to show.
1153                 if (descriptor.value && descriptor.value.constructor
1154                     && descriptor.value.constructor !== Object
1155                     && descriptor.value.constructor !== Array
1156                     && descriptor.value.constructor !== RegExp)
1157                     preview.lossless = false;
1158                 continue;
1159             }
1160
1161             // For arrays, only allow indexes.
1162             if (this.subtype === "array" && !isUInt32(name))
1163                 continue;
1164
1165             // Do not show non-enumerable non-own properties.
1166             // Special case to allow array indexes that may be on the prototype.
1167             // Special case to allow native getters on non-RegExp objects.
1168             if (!descriptor.enumerable && !descriptor.isOwn && !(this.subtype === "array" || (this.subtype !== "regexp" && descriptor.nativeGetter)))
1169                 continue;
1170
1171             // If we have a filter, only show properties in the filter.
1172             // FIXME: Currently these filters do nothing on the backend.
1173             if (firstLevelKeys && !firstLevelKeys.includes(name))
1174                 continue;
1175
1176             // Getter/setter.
1177             if (!("value" in descriptor)) {
1178                 preview.lossless = false;
1179                 this._appendPropertyPreview(preview, internal, {name, type: "accessor"}, propertiesThreshold);
1180                 continue;
1181             }
1182
1183             // Null value.
1184             let value = descriptor.value;
1185             if (value === null) {
1186                 this._appendPropertyPreview(preview, internal, {name, type: "object", subtype: "null", value: "null"}, propertiesThreshold);
1187                 continue;
1188             }
1189
1190             // Ignore non-enumerable functions.
1191             let type = typeof value;
1192             if (!descriptor.enumerable && type === "function")
1193                 continue;
1194
1195             // Fix type of document.all.
1196             if (InjectedScriptHost.isHTMLAllCollection(value))
1197                 type = "object";
1198
1199             // Primitive.
1200             const maxLength = 100;
1201             if (isPrimitiveValue(value)) {
1202                 if (type === "string" && value.length > maxLength) {
1203                     value = this._abbreviateString(value, maxLength, true);
1204                     preview.lossless = false;
1205                 }
1206                 this._appendPropertyPreview(preview, internal, {name, type, value: toStringDescription(value)}, propertiesThreshold);
1207                 continue;
1208             }
1209
1210             // Symbol.
1211             if (isSymbol(value)) {
1212                 let symbolString = toString(value);
1213                 if (symbolString.length > maxLength) {
1214                     symbolString = this._abbreviateString(symbolString, maxLength, true);
1215                     preview.lossless = false;
1216                 }
1217                 this._appendPropertyPreview(preview, internal, {name, type, value: symbolString}, propertiesThreshold);
1218                 continue;
1219             }
1220
1221             // Object.
1222             let property = {name, type};
1223             let subtype = RemoteObject.subtype(value);
1224             if (subtype)
1225                 property.subtype = subtype;
1226
1227             // Second level.
1228             if ((secondLevelKeys === null || secondLevelKeys) || this._isPreviewableObject(value, object)) {
1229                 // FIXME: If we want secondLevelKeys filter to continue we would need some refactoring.
1230                 let subPreview = RemoteObject.createObjectPreviewForValue(value, value !== object, secondLevelKeys);
1231                 property.valuePreview = subPreview;
1232                 if (!subPreview.lossless)
1233                     preview.lossless = false;
1234                 if (subPreview.overflow)
1235                     preview.overflow = true;
1236             } else {
1237                 let description = "";
1238                 if (type !== "function" || subtype === "class") {
1239                     let fullDescription;
1240                     if (subtype === "class")
1241                         fullDescription = "class " + value.name;
1242                     else if (subtype === "node")
1243                         fullDescription = RemoteObject.nodePreview(value);
1244                     else
1245                         fullDescription = RemoteObject.describe(value);
1246                     description = this._abbreviateString(fullDescription, maxLength, subtype === "regexp");
1247                 }
1248                 property.value = description;
1249                 preview.lossless = false;
1250             }
1251
1252             this._appendPropertyPreview(preview, internal, property, propertiesThreshold);
1253         }
1254     }
1255
1256     _appendPropertyPreview(preview, internal, property, propertiesThreshold)
1257     {
1258         if (toString(property.name >>> 0) === property.name)
1259             propertiesThreshold.indexes--;
1260         else
1261             propertiesThreshold.properties--;
1262
1263         if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
1264             preview.overflow = true;
1265             preview.lossless = false;
1266             return;
1267         }
1268
1269         if (internal)
1270             property.internal = true;
1271
1272         preview.properties.push(property);
1273     }
1274
1275     _appendEntryPreviews(object, preview)
1276     {
1277         // Fetch 6, but only return 5, so we can tell if we overflowed.
1278         let entries = injectedScript._entries(object, this.subtype, 0, 6);
1279         if (!entries)
1280             return;
1281
1282         if (entries.length > 5) {
1283             entries.pop();
1284             preview.overflow = true;
1285             preview.lossless = false;
1286         }
1287
1288         function updateMainPreview(subPreview) {
1289             if (!subPreview.lossless)
1290                 preview.lossless = false;
1291         }
1292
1293         preview.entries = entries.map(function(entry) {
1294             entry.value = RemoteObject.createObjectPreviewForValue(entry.value, entry.value !== object);
1295             updateMainPreview(entry.value);
1296             if ("key" in entry) {
1297                 entry.key = RemoteObject.createObjectPreviewForValue(entry.key, entry.key !== object);
1298                 updateMainPreview(entry.key);
1299             }
1300             return entry;
1301         });
1302     }
1303
1304     _isPreviewableObject(value, object)
1305     {
1306         let set = new Set;
1307         set.add(object);
1308
1309         return this._isPreviewableObjectInternal(value, set, 1);
1310     }
1311
1312     _isPreviewableObjectInternal(object, knownObjects, depth)
1313     {
1314         // Deep object.
1315         if (depth > 3)
1316             return false;
1317
1318         // Primitive.
1319         if (isPrimitiveValue(object) || isSymbol(object))
1320             return true;
1321
1322         // Null.
1323         if (object === null)
1324             return true;
1325
1326         // Cyclic objects.
1327         if (knownObjects.has(object))
1328             return false;
1329
1330         ++depth;
1331         knownObjects.add(object);
1332
1333         // Arrays are simple if they have 5 or less simple objects.
1334         let subtype = RemoteObject.subtype(object);
1335         if (subtype === "array") {
1336             let length = object.length;
1337             if (length > 5)
1338                 return false;
1339             for (let i = 0; i < length; ++i) {
1340                 if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth))
1341                     return false;
1342             }
1343             return true;
1344         }
1345
1346         // Not a basic object.
1347         if (object.__proto__ && object.__proto__.__proto__)
1348             return false;
1349
1350         // Objects are simple if they have 3 or less simple value properties.
1351         let ownPropertyNames = Object.getOwnPropertyNames(object);
1352         if (ownPropertyNames.length > 3)
1353             return false;
1354         for (let i = 0; i < ownPropertyNames.length; ++i) {
1355             let propertyName = ownPropertyNames[i];
1356             let descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
1357             if (descriptor && !("value" in descriptor))
1358                 return false;
1359             if (!this._isPreviewableObjectInternal(object[propertyName], knownObjects, depth))
1360                 return false;
1361         }
1362
1363         return true;
1364     }
1365
1366     _abbreviateString(string, maxLength, middle)
1367     {
1368         if (string.length <= maxLength)
1369             return string;
1370
1371         if (middle) {
1372             let leftHalf = maxLength >> 1;
1373             let rightHalf = maxLength - leftHalf - 1;
1374             return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1375         }
1376
1377         return string.substr(0, maxLength) + "\u2026";
1378     }
1379 }
1380
1381 // -------
1382
1383 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
1384 {
1385     this.callFrameId = `{"ordinal":${ordinal},"injectedScriptId":${injectedScriptId}}`;
1386     this.functionName = callFrame.functionName;
1387     this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column};
1388     this.scopeChain = this._wrapScopeChain(callFrame);
1389     this.this = RemoteObject.create(callFrame.thisObject, "backtrace");
1390     this.isTailDeleted = callFrame.isTailDeleted;
1391 }
1392
1393 InjectedScript.CallFrameProxy.prototype = {
1394     _wrapScopeChain(callFrame)
1395     {
1396         let scopeChain = callFrame.scopeChain;
1397         let scopeDescriptions = callFrame.scopeDescriptions();
1398
1399         let scopeChainProxy = [];
1400         for (let i = 0; i < scopeChain.length; i++)
1401             scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(scopeChain[i], scopeDescriptions[i], "backtrace");
1402         return scopeChainProxy;
1403     }
1404 }
1405
1406 InjectedScript.CallFrameProxy._scopeTypeNames = {
1407     0: "global", // GLOBAL_SCOPE
1408     1: "with", // WITH_SCOPE
1409     2: "closure", // CLOSURE_SCOPE
1410     3: "catch", // CATCH_SCOPE
1411     4: "functionName", // FUNCTION_NAME_SCOPE
1412     5: "globalLexicalEnvironment", // GLOBAL_LEXICAL_ENVIRONMENT_SCOPE
1413     6: "nestedLexical", // NESTED_LEXICAL_SCOPE
1414 };
1415
1416 InjectedScript.CallFrameProxy._createScopeJson = function(object, {name, type, location}, groupId)
1417 {
1418     let scope = {
1419         object: RemoteObject.create(object, groupId),
1420         type: InjectedScript.CallFrameProxy._scopeTypeNames[type],
1421     };
1422
1423     if (name)
1424         scope.name = name;
1425
1426     if (location)
1427         scope.location = location;
1428
1429     if (isEmptyObject(object))
1430         scope.empty = true;
1431
1432     return scope;
1433 }
1434
1435 // -------
1436
1437 function bind(func, thisObject, ...outerArgs)
1438 {
1439     return function(...innerArgs) {
1440         return func.apply(thisObject, outerArgs.concat(innerArgs));
1441     };
1442 }
1443
1444 function BasicCommandLineAPI(callFrame)
1445 {
1446     this.$_ = injectedScript._lastResult;
1447     this.$exception = injectedScript._exceptionValue;
1448
1449     // $1-$99
1450     for (let i = 1; i <= injectedScript._savedResults.length; ++i)
1451         this.__defineGetter__("$" + i, bind(injectedScript._savedResult, injectedScript, i));
1452
1453     // Command Line API methods.
1454     for (let i = 0; i < BasicCommandLineAPI.methods.length; ++i) {
1455         let method = BasicCommandLineAPI.methods[i];
1456         this[method.name] = method;
1457     }
1458 }
1459
1460 BasicCommandLineAPI.methods = [
1461     function dir() { return inspectedGlobalObject.console.dir(...arguments); },
1462     function clear() { return inspectedGlobalObject.console.clear(...arguments); },
1463     function table() { return inspectedGlobalObject.console.table(...arguments); },
1464     function profile() { return inspectedGlobalObject.console.profile(...arguments); },
1465     function profileEnd() { return inspectedGlobalObject.console.profileEnd(...arguments); },
1466
1467     function keys(object) { return Object.keys(object); },
1468     function values(object) {
1469         let result = [];
1470         for (let key in object)
1471             result.push(object[key]);
1472         return result;
1473     },
1474
1475     function queryObjects() {
1476         return InjectedScriptHost.queryObjects(...arguments);
1477     },
1478 ];
1479
1480 for (let i = 0; i < BasicCommandLineAPI.methods.length; ++i) {
1481     let method = BasicCommandLineAPI.methods[i];
1482     method.toString = function() { return "function " + method.name + "() { [Command Line API] }"; };
1483 }
1484
1485 return injectedScript;
1486 })