Web Inspector: Provide $event in the console when paused on an event listener
[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     setEventValue(value)
357     {
358         this._eventValue = value;
359     }
360
361     clearEventValue()
362     {
363         delete this._eventValue;
364     }
365
366     setExceptionValue(value)
367     {
368         this._exceptionValue = value;
369     }
370
371     clearExceptionValue()
372     {
373         delete this._exceptionValue;
374     }
375
376     findObjectById(objectId)
377     {
378         let parsedObjectId = this._parseObjectId(objectId);
379         return this._objectForId(parsedObjectId);
380     }
381
382     inspectObject(object)
383     {
384         if (this._commandLineAPIImpl)
385             this._commandLineAPIImpl.inspect(object);
386     }
387
388     releaseObject(objectId)
389     {
390         let parsedObjectId = this._parseObjectId(objectId);
391         this._releaseObject(parsedObjectId.id);
392     }
393
394     releaseObjectGroup(objectGroupName)
395     {
396         if (objectGroupName === "console") {
397             delete this._lastResult;
398             this._nextSavedResultIndex = 1;
399             this._savedResults = [];
400         }
401
402         let group = this._objectGroups[objectGroupName];
403         if (!group)
404             return;
405
406         for (let i = 0; i < group.length; i++)
407             this._releaseObject(group[i]);
408
409         delete this._objectGroups[objectGroupName];
410     }
411
412     // InjectedScriptModule C++ API
413
414     module(name)
415     {
416         return this._modules[name];
417     }
418
419     injectModule(name, source, host)
420     {
421         delete this._modules[name];
422
423         let moduleFunction = InjectedScriptHost.evaluate("(" + source + ")");
424         if (typeof moduleFunction !== "function") {
425             if (inspectedGlobalObject.console)
426                 inspectedGlobalObject.console.error("Web Inspector error: A function was expected for module %s evaluation", name);
427             return null;
428         }
429
430         let module = moduleFunction.call(inspectedGlobalObject, InjectedScriptHost, inspectedGlobalObject, injectedScriptId, this, RemoteObject, host);
431         this._modules[name] = module;
432         return module;
433     }
434
435     // InjectedScriptModule JavaScript API
436
437     isPrimitiveValue(value)
438     {
439         return isPrimitiveValue(value);
440     }
441
442     // Private
443
444     _parseObjectId(objectId)
445     {
446         return InjectedScriptHost.evaluate("(" + objectId + ")");
447     }
448
449     _objectForId(objectId)
450     {
451         return this._idToWrappedObject[objectId.id];
452     }
453
454     _bind(object, objectGroupName)
455     {
456         let id = this._lastBoundObjectId++;
457         let objectId = `{"injectedScriptId":${injectedScriptId},"id":${id}}`;
458
459         this._idToWrappedObject[id] = object;
460
461         if (objectGroupName) {
462             let group = this._objectGroups[objectGroupName];
463             if (!group) {
464                 group = [];
465                 this._objectGroups[objectGroupName] = group;
466             }
467             group.push(id);
468             this._idToObjectGroupName[id] = objectGroupName;
469         }
470
471         return objectId;
472     }
473
474     _releaseObject(id)
475     {
476         delete this._idToWrappedObject[id];
477         delete this._idToObjectGroupName[id];
478     }
479
480     _fallbackWrapper(object)
481     {
482         let result = {};
483         result.type = typeof object;
484         if (isPrimitiveValue(object))
485             result.value = object;
486         else
487             result.description = toString(object);
488         return result;
489     }
490
491     _resolveCallArgument(callArgumentJSON)
492     {
493         if ("value" in callArgumentJSON)
494             return callArgumentJSON.value;
495
496         let objectId = callArgumentJSON.objectId;
497         if (objectId) {
498             let parsedArgId = this._parseObjectId(objectId);
499             if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
500                 throw "Arguments should belong to the same JavaScript world as the target object.";
501
502             let resolvedArg = this._objectForId(parsedArgId);
503             if (!isDefined(resolvedArg))
504                 throw "Could not find object with given id";
505
506             return resolvedArg;
507         }
508
509         return undefined;
510     }
511
512     _createThrownValue(value, objectGroup)
513     {
514         let remoteObject = RemoteObject.create(value, objectGroup);
515         try {
516             remoteObject.description = toStringDescription(value);
517         } catch (e) {}
518         return {
519             wasThrown: true,
520             result: remoteObject
521         };
522     }
523
524     _evaluateAndWrap(evalFunction, object, expression, objectGroup, isEvalOnCallFrame, includeCommandLineAPI, returnByValue, generatePreview, saveResult)
525     {
526         return this._wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, () => {
527             return this._evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI);
528         });
529     }
530
531     _wrapAndSaveCall(objectGroup, returnByValue, generatePreview, saveResult, func)
532     {
533         return this._wrapCall(objectGroup, returnByValue, generatePreview, saveResult, () => {
534             let result = func();
535             if (saveResult)
536                 this._saveResult(result);
537             return result;
538         });
539     }
540
541     _wrapCall(objectGroup, returnByValue, generatePreview, saveResult, func)
542     {
543         try {
544             this._savedResultIndex = 0;
545
546             let returnObject = {
547                 wasThrown: false,
548                 result: RemoteObject.create(func(), objectGroup, returnByValue, generatePreview)
549             };
550
551             if (saveResult && this._savedResultIndex)
552                 returnObject.savedResultIndex = this._savedResultIndex;
553
554             return returnObject;
555         } catch (e) {
556             return this._createThrownValue(e, objectGroup);
557         }
558     }
559
560     _evaluateOn(evalFunction, object, expression, isEvalOnCallFrame, includeCommandLineAPI)
561     {
562         let commandLineAPI = null;
563         if (includeCommandLineAPI) {
564             if (this.CommandLineAPI)
565                 commandLineAPI = new this.CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
566             else
567                 commandLineAPI = new BasicCommandLineAPI(isEvalOnCallFrame ? object : null);
568         }
569
570         return evalFunction.call(object, expression, commandLineAPI);
571     }
572
573     _callFrameForId(topCallFrame, callFrameId)
574     {
575         let parsedCallFrameId = InjectedScriptHost.evaluate("(" + callFrameId + ")");
576         let ordinal = parsedCallFrameId["ordinal"];
577         let callFrame = topCallFrame;
578         while (--ordinal >= 0 && callFrame)
579             callFrame = callFrame.caller;
580         return callFrame;
581     }
582
583     _getProperties(objectId, collectionMode, generatePreview, nativeGettersAsValues)
584     {
585         let parsedObjectId = this._parseObjectId(objectId);
586         let object = this._objectForId(parsedObjectId);
587         let objectGroupName = this._idToObjectGroupName[parsedObjectId.id];
588
589         if (!isDefined(object))
590             return false;
591
592         if (isSymbol(object))
593             return false;
594
595         let descriptors = this._propertyDescriptors(object, collectionMode, nativeGettersAsValues);
596
597         for (let i = 0; i < descriptors.length; ++i) {
598             let descriptor = descriptors[i];
599             if ("get" in descriptor)
600                 descriptor.get = RemoteObject.create(descriptor.get, objectGroupName);
601             if ("set" in descriptor)
602                 descriptor.set = RemoteObject.create(descriptor.set, objectGroupName);
603             if ("value" in descriptor)
604                 descriptor.value = RemoteObject.create(descriptor.value, objectGroupName, false, generatePreview);
605             if (!("configurable" in descriptor))
606                 descriptor.configurable = false;
607             if (!("enumerable" in descriptor))
608                 descriptor.enumerable = false;
609             if ("symbol" in descriptor)
610                 descriptor.symbol = RemoteObject.create(descriptor.symbol, objectGroupName);
611         }
612
613         return descriptors;
614     }
615
616     _internalPropertyDescriptors(object, completeDescriptor)
617     {
618         let internalProperties = InjectedScriptHost.getInternalProperties(object);
619         if (!internalProperties)
620             return null;
621
622         let descriptors = [];
623         for (let i = 0; i < internalProperties.length; i++) {
624             let property = internalProperties[i];
625             let descriptor = {name: property.name, value: property.value};
626             if (completeDescriptor) {
627                 descriptor.writable = false;
628                 descriptor.configurable = false;
629                 descriptor.enumerable = false;
630                 descriptor.isOwn = true;
631             }
632             descriptors.push(descriptor);
633         }
634         return descriptors;
635     }
636
637     _propertyDescriptors(object, collectionMode, nativeGettersAsValues)
638     {
639         if (InjectedScriptHost.subtype(object) === "proxy")
640             return [];
641
642         let descriptors = [];
643         let nameProcessed = new Set;
644
645         function createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, possibleNativeBindingGetter)
646         {
647             try {
648                 let fakeDescriptor = {name, value: object[name], writable: descriptor.writable || false, configurable: descriptor.configurable || false, enumerable: descriptor.enumerable || false};
649                 if (possibleNativeBindingGetter)
650                     fakeDescriptor.nativeGetter = true;
651                 if (isOwnProperty)
652                     fakeDescriptor.isOwn = true;
653                 if (symbol)
654                     fakeDescriptor.symbol = symbol;
655                 // Silence any possible unhandledrejection exceptions created from accessing a native accessor with a wrong this object.
656                 if (fakeDescriptor.value instanceof Promise)
657                     fakeDescriptor.value.catch(function(){});
658                 return fakeDescriptor;
659             } catch (e) {
660                 let errorDescriptor = {name, value: e, wasThrown: true};
661                 if (isOwnProperty)
662                     errorDescriptor.isOwn = true;
663                 if (symbol)
664                     errorDescriptor.symbol = symbol;
665                 return errorDescriptor;
666             }
667         }
668
669         function processDescriptor(descriptor, isOwnProperty, possibleNativeBindingGetter)
670         {
671             // All properties.
672             if (collectionMode & InjectedScript.CollectionMode.AllProperties) {
673                 descriptors.push(descriptor);
674                 return;
675             }
676
677             // Own properties.
678             if (collectionMode & InjectedScript.CollectionMode.OwnProperties && isOwnProperty) {
679                 descriptors.push(descriptor);
680                 return;
681             }
682
683             // Native Getter properties.
684             if (collectionMode & InjectedScript.CollectionMode.NativeGetterProperties) {
685                 if (possibleNativeBindingGetter) {
686                     descriptors.push(descriptor);
687                     return;
688                 }
689             }
690         }
691
692         function processProperties(o, properties, isOwnProperty)
693         {
694             for (let i = 0; i < properties.length; ++i) {
695                 let property = properties[i];
696                 if (nameProcessed.has(property) || property === "__proto__")
697                     continue;
698
699                 nameProcessed.add(property);
700
701                 let name = toString(property);
702                 let symbol = isSymbol(property) ? property : null;
703
704                 let descriptor = Object.getOwnPropertyDescriptor(o, property);
705                 if (!descriptor) {
706                     // FIXME: Bad descriptor. Can we get here?
707                     // Fall back to very restrictive settings.
708                     let fakeDescriptor = createFakeValueDescriptor(name, symbol, {writable: false, configurable: false, enumerable: false}, isOwnProperty);
709                     processDescriptor(fakeDescriptor, isOwnProperty);
710                     continue;
711                 }
712
713                 if (nativeGettersAsValues) {
714                     if (String(descriptor.get).endsWith("[native code]\n}") || (!descriptor.get && descriptor.hasOwnProperty("get") && !descriptor.set && descriptor.hasOwnProperty("set"))) {
715                         // Developers may create such a descriptor, so we should be resilient:
716                         // let x = {}; Object.defineProperty(x, "p", {get:undefined}); Object.getOwnPropertyDescriptor(x, "p")
717                         let fakeDescriptor = createFakeValueDescriptor(name, symbol, descriptor, isOwnProperty, true);
718                         processDescriptor(fakeDescriptor, isOwnProperty, true);
719                         continue;
720                     }
721                 }
722
723                 descriptor.name = name;
724                 if (isOwnProperty)
725                     descriptor.isOwn = true;
726                 if (symbol)
727                     descriptor.symbol = symbol;
728                 processDescriptor(descriptor, isOwnProperty);
729             }
730         }
731
732         function arrayIndexPropertyNames(o, length)
733         {
734             let array = [];
735             for (let i = 0; i < length; ++i) {
736                 if (i in o)
737                     array.push("" + i);
738             }
739             return array;
740         }
741
742         // FIXME: <https://webkit.org/b/143589> Web Inspector: Better handling for large collections in Object Trees
743         // For array types with a large length we attempt to skip getOwnPropertyNames and instead just sublist of indexes.
744         let isArrayLike = false;
745         try {
746             isArrayLike = RemoteObject.subtype(object) === "array" && isFinite(object.length) && object.length > 0;
747         } catch(e) {}
748
749         for (let o = object; isDefined(o); o = Object.getPrototypeOf(o)) {
750             let isOwnProperty = o === object;
751
752             if (isArrayLike && isOwnProperty)
753                 processProperties(o, arrayIndexPropertyNames(o, Math.min(object.length, 100)), isOwnProperty);
754             else {
755                 processProperties(o, Object.getOwnPropertyNames(o), isOwnProperty);
756                 if (Object.getOwnPropertySymbols)
757                     processProperties(o, Object.getOwnPropertySymbols(o), isOwnProperty);
758             }
759
760             if (collectionMode === InjectedScript.CollectionMode.OwnProperties)
761                 break;
762         }
763
764         // Always include __proto__ at the end.
765         try {
766             if (object.__proto__)
767                 descriptors.push({name: "__proto__", value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true});
768         } catch (e) {}
769
770         return descriptors;
771     }
772
773     _getSetEntries(object, skip, numberToFetch)
774     {
775         let entries = [];
776
777         // FIXME: This is observable if the page overrides Set.prototype[Symbol.iterator].
778         for (let value of object) {
779             if (skip > 0) {
780                 skip--;
781                 continue;
782             }
783
784             entries.push({value});
785
786             if (numberToFetch && entries.length === numberToFetch)
787                 break;
788         }
789
790         return entries;
791     }
792
793     _getMapEntries(object, skip, numberToFetch)
794     {
795         let entries = [];
796
797         // FIXME: This is observable if the page overrides Map.prototype[Symbol.iterator].
798         for (let [key, value] of object) {
799             if (skip > 0) {
800                 skip--;
801                 continue;
802             }
803
804             entries.push({key, value});
805
806             if (numberToFetch && entries.length === numberToFetch)
807                 break;
808         }
809
810         return entries;
811     }
812
813     _getWeakMapEntries(object, numberToFetch)
814     {
815         return InjectedScriptHost.weakMapEntries(object, numberToFetch);
816     }
817
818     _getWeakSetEntries(object, numberToFetch)
819     {
820         return InjectedScriptHost.weakSetEntries(object, numberToFetch);
821     }
822
823     _getIteratorEntries(object, numberToFetch)
824     {
825         return InjectedScriptHost.iteratorEntries(object, numberToFetch);
826     }
827
828     _entries(object, subtype, startIndex, numberToFetch)
829     {
830         if (subtype === "set")
831             return this._getSetEntries(object, startIndex, numberToFetch);
832         if (subtype === "map")
833             return this._getMapEntries(object, startIndex, numberToFetch);
834         if (subtype === "weakmap")
835             return this._getWeakMapEntries(object, numberToFetch);
836         if (subtype === "weakset")
837             return this._getWeakSetEntries(object, numberToFetch);
838         if (subtype === "iterator")
839             return this._getIteratorEntries(object, numberToFetch);
840
841         throw "unexpected type";
842     }
843
844     _saveResult(result)
845     {
846         this._lastResult = result;
847
848         if (result === undefined || result === null)
849             return;
850
851         let existingIndex = this._savedResults.indexOf(result);
852         if (existingIndex !== -1) {
853             this._savedResultIndex = existingIndex;
854             return;
855         }
856
857         this._savedResultIndex = this._nextSavedResultIndex;
858         this._savedResults[this._nextSavedResultIndex++] = result;
859
860         // $n is limited from $1-$99. $0 is special.
861         if (this._nextSavedResultIndex >= 100)
862             this._nextSavedResultIndex = 1;
863     }
864
865     _savedResult(index)
866     {
867         return this._savedResults[index];
868     }
869 }
870
871 InjectedScript.CollectionMode = {
872     OwnProperties: 1 << 0,          // own properties.
873     NativeGetterProperties: 1 << 1, // native getter properties in the prototype chain.
874     AllProperties: 1 << 2,          // all properties in the prototype chain.
875 };
876
877 var injectedScript = new InjectedScript;
878
879 // -------
880
881 let RemoteObject = class RemoteObject
882 {
883     constructor(object, objectGroupName, forceValueType, generatePreview, columnNames)
884     {
885         this.type = typeof object;
886
887         if (this.type === "undefined" && InjectedScriptHost.isHTMLAllCollection(object))
888             this.type = "object";
889
890         if (isPrimitiveValue(object) || object === null || forceValueType) {
891             // We don't send undefined values over JSON.
892             if (this.type !== "undefined")
893                 this.value = object;
894
895             // Null object is object with 'null' subtype.
896             if (object === null)
897                 this.subtype = "null";
898
899             // Provide user-friendly number values.
900             if (this.type === "number")
901                 this.description = toStringDescription(object);
902             return;
903         }
904
905         this.objectId = injectedScript._bind(object, objectGroupName);
906
907         let subtype = RemoteObject.subtype(object);
908         if (subtype)
909             this.subtype = subtype;
910
911         this.className = InjectedScriptHost.internalConstructorName(object);
912         this.description = RemoteObject.describe(object);
913
914         if (subtype === "array")
915             this.size = typeof object.length === "number" ? object.length : 0;
916         else if (subtype === "set" || subtype === "map")
917             this.size = object.size;
918         else if (subtype === "weakmap")
919             this.size = InjectedScriptHost.weakMapSize(object);
920         else if (subtype === "weakset")
921             this.size = InjectedScriptHost.weakSetSize(object);
922         else if (subtype === "class") {
923             this.classPrototype = RemoteObject.create(object.prototype, objectGroupName);
924             this.className = object.name;
925         }
926
927         if (generatePreview && this.type === "object") {
928             if (subtype === "proxy") {
929                 this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object));
930                 this.preview.lossless = false;
931             } else
932                 this.preview = this._generatePreview(object, undefined, columnNames);
933         }
934     }
935
936     // Static
937
938     static create(object, objectGroupName, forceValueType, generatePreview, columnNames)
939     {
940         try {
941             return new RemoteObject(object, objectGroupName, forceValueType, generatePreview, columnNames);
942         } catch (e) {
943             let description;
944             try {
945                 description = RemoteObject.describe(e);
946             } catch (ex) {
947                 alert(ex.message);
948                 description = "<failed to convert exception to string>";
949             }
950             return new RemoteObject(description);
951         }
952     }
953
954     static createObjectPreviewForValue(value, generatePreview, columnNames)
955     {
956         let remoteObject = new RemoteObject(value, undefined, false, generatePreview, columnNames);
957         if (remoteObject.objectId)
958             injectedScript.releaseObject(remoteObject.objectId);
959         if (remoteObject.classPrototype && remoteObject.classPrototype.objectId)
960             injectedScript.releaseObject(remoteObject.classPrototype.objectId);
961         return remoteObject.preview || remoteObject._emptyPreview();
962     }
963
964     static subtype(value)
965     {
966         if (value === null)
967             return "null";
968
969         if (isPrimitiveValue(value) || isSymbol(value))
970             return null;
971
972         if (InjectedScriptHost.isHTMLAllCollection(value))
973             return "array";
974
975         let preciseType = InjectedScriptHost.subtype(value);
976         if (preciseType)
977             return preciseType;
978
979         // FireBug's array detection.
980         try {
981             if (typeof value.splice === "function" && isFinite(value.length))
982                 return "array";
983         } catch (e) {}
984
985         return null;
986     }
987
988     static describe(value)
989     {
990         if (isPrimitiveValue(value))
991             return null;
992
993         if (isSymbol(value))
994             return toString(value);
995
996         let subtype = RemoteObject.subtype(value);
997
998         if (subtype === "regexp")
999             return toString(value);
1000
1001         if (subtype === "date")
1002             return toString(value);
1003
1004         if (subtype === "error")
1005             return toString(value);
1006
1007         if (subtype === "proxy")
1008             return "Proxy";
1009
1010         if (subtype === "node")
1011             return RemoteObject.nodePreview(value);
1012
1013         let className = InjectedScriptHost.internalConstructorName(value);
1014         if (subtype === "array")
1015             return className;
1016
1017         if (subtype === "iterator" && Symbol.toStringTag in value)
1018             return value[Symbol.toStringTag];
1019
1020         // NodeList in JSC is a function, check for array prior to this.
1021         if (typeof value === "function")
1022             return value.toString();
1023
1024         // If Object, try for a better name from the constructor.
1025         if (className === "Object") {
1026             let constructorName = value.constructor && value.constructor.name;
1027             if (constructorName)
1028                 return constructorName;
1029         }
1030
1031         return className;
1032     }
1033
1034     static nodePreview(node)
1035     {
1036         let isXMLDocument = node.ownerDocument && !!node.ownerDocument.xmlVersion;
1037         let nodeName = isXMLDocument ? node.nodeName : node.nodeName.toLowerCase();
1038
1039         switch (node.nodeType) {
1040         case 1: // Node.ELEMENT_NODE
1041             if (node.id)
1042                 return "<" + nodeName + " id=\"" + node.id + "\">";
1043             if (node.classList.length)
1044                 return "<" + nodeName + " class=\"" + node.classList.toString().replace(/\s+/, " ") + "\">";
1045             if (nodeName === "input" && node.type)
1046                 return "<" + nodeName + " type=\"" + node.type + "\">";
1047             return "<" + nodeName + ">";
1048
1049         case 3: // Node.TEXT_NODE
1050             return nodeName + " \"" + node.nodeValue + "\"";
1051
1052         case 8: // Node.COMMENT_NODE
1053             return "<!--" + node.nodeValue + "-->";
1054
1055         case 10: // Node.DOCUMENT_TYPE_NODE
1056             return "<!DOCTYPE " + nodeName + ">";
1057
1058         default:
1059             return nodeName;
1060         }
1061     }
1062
1063     // Private
1064
1065     _initialPreview()
1066     {
1067         let preview = {
1068             type: this.type,
1069             description: this.description || toString(this.value),
1070             lossless: true,
1071         };
1072
1073         if (this.subtype) {
1074             preview.subtype = this.subtype;
1075             if (this.subtype !== "null") {
1076                 preview.overflow = false;
1077                 preview.properties = [];
1078             }
1079         }
1080
1081         if ("size" in this)
1082             preview.size = this.size;
1083
1084         return preview;
1085     }
1086
1087     _emptyPreview()
1088     {
1089         let preview = this._initialPreview();
1090
1091         if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator") {
1092             if (this.size) {
1093                 preview.entries = [];
1094                 preview.lossless = false;
1095                 preview.overflow = true;
1096             }
1097         }
1098
1099         return preview;
1100     }
1101
1102     _generatePreview(object, firstLevelKeys, secondLevelKeys)
1103     {
1104         let preview = this._initialPreview();
1105         let isTableRowsRequest = secondLevelKeys === null || secondLevelKeys;
1106         let firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
1107
1108         let propertiesThreshold = {
1109             properties: isTableRowsRequest ? 1000 : Math.max(5, firstLevelKeysCount),
1110             indexes: isTableRowsRequest ? 1000 : Math.max(10, firstLevelKeysCount)
1111         };
1112
1113         try {
1114             // Maps, Sets, and Iterators have entries.
1115             if (this.subtype === "map" || this.subtype === "set" || this.subtype === "weakmap" || this.subtype === "weakset" || this.subtype === "iterator")
1116                 this._appendEntryPreviews(object, preview);
1117
1118             preview.properties = [];
1119
1120             // Internal Properties.
1121             let internalPropertyDescriptors = injectedScript._internalPropertyDescriptors(object, true);
1122             if (internalPropertyDescriptors) {
1123                 this._appendPropertyPreviews(object, preview, internalPropertyDescriptors, true, propertiesThreshold, firstLevelKeys, secondLevelKeys);
1124                 if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1125                     return preview;
1126             }
1127
1128             if (preview.entries)
1129                 return preview;
1130
1131             // Properties.
1132             let nativeGettersAsValues = true;
1133             let descriptors = injectedScript._propertyDescriptors(object, InjectedScript.CollectionMode.AllProperties, nativeGettersAsValues);
1134             this._appendPropertyPreviews(object, preview, descriptors, false, propertiesThreshold, firstLevelKeys, secondLevelKeys);
1135             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1136                 return preview;
1137         } catch (e) {
1138             preview.lossless = false;
1139         }
1140
1141         return preview;
1142     }
1143
1144     _appendPropertyPreviews(object, preview, descriptors, internal, propertiesThreshold, firstLevelKeys, secondLevelKeys)
1145     {
1146         for (let i = 0; i < descriptors.length; ++i) {
1147             let descriptor = descriptors[i];
1148
1149             // Seen enough.
1150             if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
1151                 break;
1152
1153             // Error in descriptor.
1154             if (descriptor.wasThrown) {
1155                 preview.lossless = false;
1156                 continue;
1157             }
1158
1159             // Do not show "__proto__" in preview.
1160             let name = descriptor.name;
1161             if (name === "__proto__") {
1162                 // Non basic __proto__ objects may have interesting, non-enumerable, methods to show.
1163                 if (descriptor.value && descriptor.value.constructor
1164                     && descriptor.value.constructor !== Object
1165                     && descriptor.value.constructor !== Array
1166                     && descriptor.value.constructor !== RegExp)
1167                     preview.lossless = false;
1168                 continue;
1169             }
1170
1171             // For arrays, only allow indexes.
1172             if (this.subtype === "array" && !isUInt32(name))
1173                 continue;
1174
1175             // Do not show non-enumerable non-own properties.
1176             // Special case to allow array indexes that may be on the prototype.
1177             // Special case to allow native getters on non-RegExp objects.
1178             if (!descriptor.enumerable && !descriptor.isOwn && !(this.subtype === "array" || (this.subtype !== "regexp" && descriptor.nativeGetter)))
1179                 continue;
1180
1181             // If we have a filter, only show properties in the filter.
1182             // FIXME: Currently these filters do nothing on the backend.
1183             if (firstLevelKeys && !firstLevelKeys.includes(name))
1184                 continue;
1185
1186             // Getter/setter.
1187             if (!("value" in descriptor)) {
1188                 preview.lossless = false;
1189                 this._appendPropertyPreview(preview, internal, {name, type: "accessor"}, propertiesThreshold);
1190                 continue;
1191             }
1192
1193             // Null value.
1194             let value = descriptor.value;
1195             if (value === null) {
1196                 this._appendPropertyPreview(preview, internal, {name, type: "object", subtype: "null", value: "null"}, propertiesThreshold);
1197                 continue;
1198             }
1199
1200             // Ignore non-enumerable functions.
1201             let type = typeof value;
1202             if (!descriptor.enumerable && type === "function")
1203                 continue;
1204
1205             // Fix type of document.all.
1206             if (InjectedScriptHost.isHTMLAllCollection(value))
1207                 type = "object";
1208
1209             // Primitive.
1210             const maxLength = 100;
1211             if (isPrimitiveValue(value)) {
1212                 if (type === "string" && value.length > maxLength) {
1213                     value = this._abbreviateString(value, maxLength, true);
1214                     preview.lossless = false;
1215                 }
1216                 this._appendPropertyPreview(preview, internal, {name, type, value: toStringDescription(value)}, propertiesThreshold);
1217                 continue;
1218             }
1219
1220             // Symbol.
1221             if (isSymbol(value)) {
1222                 let symbolString = toString(value);
1223                 if (symbolString.length > maxLength) {
1224                     symbolString = this._abbreviateString(symbolString, maxLength, true);
1225                     preview.lossless = false;
1226                 }
1227                 this._appendPropertyPreview(preview, internal, {name, type, value: symbolString}, propertiesThreshold);
1228                 continue;
1229             }
1230
1231             // Object.
1232             let property = {name, type};
1233             let subtype = RemoteObject.subtype(value);
1234             if (subtype)
1235                 property.subtype = subtype;
1236
1237             // Second level.
1238             if ((secondLevelKeys === null || secondLevelKeys) || this._isPreviewableObject(value, object)) {
1239                 // FIXME: If we want secondLevelKeys filter to continue we would need some refactoring.
1240                 let subPreview = RemoteObject.createObjectPreviewForValue(value, value !== object, secondLevelKeys);
1241                 property.valuePreview = subPreview;
1242                 if (!subPreview.lossless)
1243                     preview.lossless = false;
1244                 if (subPreview.overflow)
1245                     preview.overflow = true;
1246             } else {
1247                 let description = "";
1248                 if (type !== "function" || subtype === "class") {
1249                     let fullDescription;
1250                     if (subtype === "class")
1251                         fullDescription = "class " + value.name;
1252                     else if (subtype === "node")
1253                         fullDescription = RemoteObject.nodePreview(value);
1254                     else
1255                         fullDescription = RemoteObject.describe(value);
1256                     description = this._abbreviateString(fullDescription, maxLength, subtype === "regexp");
1257                 }
1258                 property.value = description;
1259                 preview.lossless = false;
1260             }
1261
1262             this._appendPropertyPreview(preview, internal, property, propertiesThreshold);
1263         }
1264     }
1265
1266     _appendPropertyPreview(preview, internal, property, propertiesThreshold)
1267     {
1268         if (toString(property.name >>> 0) === property.name)
1269             propertiesThreshold.indexes--;
1270         else
1271             propertiesThreshold.properties--;
1272
1273         if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
1274             preview.overflow = true;
1275             preview.lossless = false;
1276             return;
1277         }
1278
1279         if (internal)
1280             property.internal = true;
1281
1282         preview.properties.push(property);
1283     }
1284
1285     _appendEntryPreviews(object, preview)
1286     {
1287         // Fetch 6, but only return 5, so we can tell if we overflowed.
1288         let entries = injectedScript._entries(object, this.subtype, 0, 6);
1289         if (!entries)
1290             return;
1291
1292         if (entries.length > 5) {
1293             entries.pop();
1294             preview.overflow = true;
1295             preview.lossless = false;
1296         }
1297
1298         function updateMainPreview(subPreview) {
1299             if (!subPreview.lossless)
1300                 preview.lossless = false;
1301         }
1302
1303         preview.entries = entries.map(function(entry) {
1304             entry.value = RemoteObject.createObjectPreviewForValue(entry.value, entry.value !== object);
1305             updateMainPreview(entry.value);
1306             if ("key" in entry) {
1307                 entry.key = RemoteObject.createObjectPreviewForValue(entry.key, entry.key !== object);
1308                 updateMainPreview(entry.key);
1309             }
1310             return entry;
1311         });
1312     }
1313
1314     _isPreviewableObject(value, object)
1315     {
1316         let set = new Set;
1317         set.add(object);
1318
1319         return this._isPreviewableObjectInternal(value, set, 1);
1320     }
1321
1322     _isPreviewableObjectInternal(object, knownObjects, depth)
1323     {
1324         // Deep object.
1325         if (depth > 3)
1326             return false;
1327
1328         // Primitive.
1329         if (isPrimitiveValue(object) || isSymbol(object))
1330             return true;
1331
1332         // Null.
1333         if (object === null)
1334             return true;
1335
1336         // Cyclic objects.
1337         if (knownObjects.has(object))
1338             return false;
1339
1340         ++depth;
1341         knownObjects.add(object);
1342
1343         // Arrays are simple if they have 5 or less simple objects.
1344         let subtype = RemoteObject.subtype(object);
1345         if (subtype === "array") {
1346             let length = object.length;
1347             if (length > 5)
1348                 return false;
1349             for (let i = 0; i < length; ++i) {
1350                 if (!this._isPreviewableObjectInternal(object[i], knownObjects, depth))
1351                     return false;
1352             }
1353             return true;
1354         }
1355
1356         // Not a basic object.
1357         if (object.__proto__ && object.__proto__.__proto__)
1358             return false;
1359
1360         // Objects are simple if they have 3 or less simple value properties.
1361         let ownPropertyNames = Object.getOwnPropertyNames(object);
1362         if (ownPropertyNames.length > 3)
1363             return false;
1364         for (let i = 0; i < ownPropertyNames.length; ++i) {
1365             let propertyName = ownPropertyNames[i];
1366             let descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
1367             if (descriptor && !("value" in descriptor))
1368                 return false;
1369             if (!this._isPreviewableObjectInternal(object[propertyName], knownObjects, depth))
1370                 return false;
1371         }
1372
1373         return true;
1374     }
1375
1376     _abbreviateString(string, maxLength, middle)
1377     {
1378         if (string.length <= maxLength)
1379             return string;
1380
1381         if (middle) {
1382             let leftHalf = maxLength >> 1;
1383             let rightHalf = maxLength - leftHalf - 1;
1384             return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
1385         }
1386
1387         return string.substr(0, maxLength) + "\u2026";
1388     }
1389 }
1390
1391 // -------
1392
1393 InjectedScript.CallFrameProxy = function(ordinal, callFrame)
1394 {
1395     this.callFrameId = `{"ordinal":${ordinal},"injectedScriptId":${injectedScriptId}}`;
1396     this.functionName = callFrame.functionName;
1397     this.location = {scriptId: String(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column};
1398     this.scopeChain = this._wrapScopeChain(callFrame);
1399     this.this = RemoteObject.create(callFrame.thisObject, "backtrace");
1400     this.isTailDeleted = callFrame.isTailDeleted;
1401 }
1402
1403 InjectedScript.CallFrameProxy.prototype = {
1404     _wrapScopeChain(callFrame)
1405     {
1406         let scopeChain = callFrame.scopeChain;
1407         let scopeDescriptions = callFrame.scopeDescriptions();
1408
1409         let scopeChainProxy = [];
1410         for (let i = 0; i < scopeChain.length; i++)
1411             scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(scopeChain[i], scopeDescriptions[i], "backtrace");
1412         return scopeChainProxy;
1413     }
1414 }
1415
1416 InjectedScript.CallFrameProxy._scopeTypeNames = {
1417     0: "global", // GLOBAL_SCOPE
1418     1: "with", // WITH_SCOPE
1419     2: "closure", // CLOSURE_SCOPE
1420     3: "catch", // CATCH_SCOPE
1421     4: "functionName", // FUNCTION_NAME_SCOPE
1422     5: "globalLexicalEnvironment", // GLOBAL_LEXICAL_ENVIRONMENT_SCOPE
1423     6: "nestedLexical", // NESTED_LEXICAL_SCOPE
1424 };
1425
1426 InjectedScript.CallFrameProxy._createScopeJson = function(object, {name, type, location}, groupId)
1427 {
1428     let scope = {
1429         object: RemoteObject.create(object, groupId),
1430         type: InjectedScript.CallFrameProxy._scopeTypeNames[type],
1431     };
1432
1433     if (name)
1434         scope.name = name;
1435
1436     if (location)
1437         scope.location = location;
1438
1439     if (isEmptyObject(object))
1440         scope.empty = true;
1441
1442     return scope;
1443 }
1444
1445 // -------
1446
1447 function bind(func, thisObject, ...outerArgs)
1448 {
1449     return function(...innerArgs) {
1450         return func.apply(thisObject, outerArgs.concat(innerArgs));
1451     };
1452 }
1453
1454 function BasicCommandLineAPI(callFrame)
1455 {
1456     this.$_ = injectedScript._lastResult;
1457     this.$exception = injectedScript._exceptionValue;
1458
1459     if ("_eventValue" in injectedScript)
1460         this.$event = injectedScript._eventValue;
1461     else if ("$event" in this)
1462         delete this.$event;
1463
1464     // $1-$99
1465     for (let i = 1; i <= injectedScript._savedResults.length; ++i)
1466         this.__defineGetter__("$" + i, bind(injectedScript._savedResult, injectedScript, i));
1467
1468     // Command Line API methods.
1469     for (let i = 0; i < BasicCommandLineAPI.methods.length; ++i) {
1470         let method = BasicCommandLineAPI.methods[i];
1471         this[method.name] = method;
1472     }
1473 }
1474
1475 BasicCommandLineAPI.methods = [
1476     function dir() { return inspectedGlobalObject.console.dir(...arguments); },
1477     function clear() { return inspectedGlobalObject.console.clear(...arguments); },
1478     function table() { return inspectedGlobalObject.console.table(...arguments); },
1479     function profile() { return inspectedGlobalObject.console.profile(...arguments); },
1480     function profileEnd() { return inspectedGlobalObject.console.profileEnd(...arguments); },
1481
1482     function keys(object) { return Object.keys(object); },
1483     function values(object) {
1484         let result = [];
1485         for (let key in object)
1486             result.push(object[key]);
1487         return result;
1488     },
1489
1490     function queryObjects() {
1491         return InjectedScriptHost.queryObjects(...arguments);
1492     },
1493 ];
1494
1495 for (let i = 0; i < BasicCommandLineAPI.methods.length; ++i) {
1496     let method = BasicCommandLineAPI.methods[i];
1497     method.toString = function() { return "function " + method.name + "() { [Command Line API] }"; };
1498 }
1499
1500 return injectedScript;
1501 })