2010-08-12 Pavel Feldman <pfeldman@chromium.org>
[WebKit-https.git] / WebCore / inspector / front-end / InjectedScript.js
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow, injectedScriptId, jsEngine) {
30
31 var InjectedScript = {};
32
33 InjectedScript.lastBoundObjectId = 1;
34 InjectedScript.idToWrappedObject = {};
35 InjectedScript.objectGroups = {};
36
37 InjectedScript.wrapObjectForConsole = function(object, canAccessInspectedWindow)
38 {
39     if (canAccessInspectedWindow)
40         return InjectedScript.wrapObject(object, "console");
41     var result = {};
42     result.type = typeof object;
43     result.description = InjectedScript._toString(object);
44     return result;
45 }
46
47 InjectedScript.wrapObject = function(object, objectGroupName)
48 {
49     try {
50         var objectId;
51         if (typeof object === "object" || typeof object === "function" || InjectedScript._isHTMLAllCollection(object)) {
52             var id = InjectedScript.lastBoundObjectId++;
53             objectId = id;
54             InjectedScript.idToWrappedObject[id] = object;
55
56             var group = InjectedScript.objectGroups[objectGroupName];
57             if (!group) {
58                 group = [];
59                 InjectedScript.objectGroups[objectGroupName] = group;
60             }
61             group.push(id);
62             objectId = new InjectedScript.RemoteObjectId("jsobject", id);
63         }
64         return InjectedScript.RemoteObject.fromObject(object, objectId);
65     } catch (e) {
66         return InjectedScript.RemoteObject.fromObject("[ Exception: " + e.toString() + " ]");
67     }
68 };
69
70 InjectedScript.unwrapObject = function(objectId) {
71     return InjectedScript.idToWrappedObject[objectId];
72 };
73
74 InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) {
75     var group = InjectedScript.objectGroups[objectGroupName];
76     if (!group)
77         return;
78     for (var i = 0; i < group.length; i++)
79         delete InjectedScript.idToWrappedObject[group[i]];
80     delete InjectedScript.objectGroups[objectGroupName];
81 };
82
83 InjectedScript.dispatch = function(methodName, args, callId)
84 {
85     var argsArray = eval("(" + args + ")");
86     if (callId)
87         argsArray.splice(0, 0, callId);  // Methods that run asynchronously have a call back id parameter.
88     var result = InjectedScript[methodName].apply(InjectedScript, argsArray);
89     if (typeof result === "undefined") {
90         inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
91         result = null;
92     }
93     return result;
94 }
95
96 InjectedScript.getPrototypes = function(nodeId)
97 {
98     var node = InjectedScript._nodeForId(nodeId);
99     if (!node)
100         return false;
101
102     var result = [];
103     for (var prototype = node; prototype; prototype = prototype.__proto__) {
104         var title = InjectedScript._describe(prototype, true);
105         if (title.match(/Prototype$/)) {
106             title = title.replace(/Prototype$/, "");
107         }
108         result.push(title);
109     }
110     return result;
111 }
112
113 InjectedScript.getProperties = function(objectId, ignoreHasOwnProperty, abbreviate)
114 {
115     var object = InjectedScript._objectForId(objectId);
116     if (!InjectedScript._isDefined(object))
117         return false;
118     var properties = [];
119     
120     var propertyNames = ignoreHasOwnProperty ? InjectedScript._getPropertyNames(object) : Object.getOwnPropertyNames(object);
121     if (!ignoreHasOwnProperty && object.__proto__)
122         propertyNames.push("__proto__");
123
124     // Go over properties, prepare results.
125     for (var i = 0; i < propertyNames.length; ++i) {
126         var propertyName = propertyNames[i];
127
128         var property = {};
129         property.name = propertyName + "";
130         var isGetter = object["__lookupGetter__"] && object.__lookupGetter__(propertyName);
131         if (!isGetter) {
132             try {
133                 var childObject = object[propertyName];
134                 var path = objectId.path ? objectId.path.slice() : [];
135                 path.push(propertyName);
136                 var childObjectId = new InjectedScript.RemoteObjectId(objectId.type, objectId.value, path);
137                 var childObjectProxy = new InjectedScript.RemoteObject.fromObject(childObject, childObjectId, abbreviate);
138                 property.value = childObjectProxy;
139             } catch(e) {
140                 property.value = new InjectedScript.RemoteObject.fromException(e);
141             }
142         } else {
143             // FIXME: this should show something like "getter" (bug 16734).
144             property.value = new InjectedScript.RemoteObject.fromObject("\u2014"); // em dash
145             property.isGetter = true;
146         }
147         properties.push(property);
148     }
149     return properties;
150 }
151
152 InjectedScript.setPropertyValue = function(objectId, propertyName, expression)
153 {
154     var object = InjectedScript._objectForId(objectId);
155     if (!InjectedScript._isDefined(object))
156         return false;
157
158     var expressionLength = expression.length;
159     if (!expressionLength) {
160         delete object[propertyName];
161         return !(propertyName in object);
162     }
163
164     try {
165         // Surround the expression in parenthesis so the result of the eval is the result
166         // of the whole expression not the last potential sub-expression.
167
168         // There is a regression introduced here: eval is now happening against global object,
169         // not call frame while on a breakpoint.
170         // TODO: bring evaluation against call frame back.
171         var result = inspectedWindow.eval("(" + expression + ")");
172         // Store the result in the property.
173         object[propertyName] = result;
174         return true;
175     } catch(e) {
176         try {
177             var result = inspectedWindow.eval("\"" + expression.replace(/"/g, "\\\"") + "\"");
178             object[propertyName] = result;
179             return true;
180         } catch(e) {
181             return false;
182         }
183     }
184 }
185
186 InjectedScript._populatePropertyNames = function(object, resultSet)
187 {
188     for (var o = object; o; o = o.__proto__) {
189         try {
190             var names = Object.getOwnPropertyNames(o);
191             for (var i = 0; i < names.length; ++i)
192                 resultSet[names[i]] = true;
193         } catch (e) {
194         }
195     }
196 }
197
198 InjectedScript._getPropertyNames = function(object, resultSet)
199 {
200     var propertyNameSet = {};
201     InjectedScript._populatePropertyNames(object, propertyNameSet);
202     return Object.keys(propertyNameSet);
203 }
204
205 InjectedScript.getCompletions = function(expression, includeInspectorCommandLineAPI, callFrameId)
206 {
207     var props = {};
208     try {
209         var expressionResult;
210         // Evaluate on call frame if call frame id is available.
211         if (typeof callFrameId === "number") {
212             var callFrame = InjectedScript._callFrameForId(callFrameId);
213             if (!callFrame)
214                 return props;
215             if (expression)
216                 expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression, true);
217             else {
218                 // Evaluate into properties in scope of the selected call frame.
219                 var scopeChain = callFrame.scopeChain;
220                 for (var i = 0; i < scopeChain.length; ++i)
221                     InjectedScript._populatePropertyNames(scopeChain[i], props);
222             }
223         } else {
224             if (!expression)
225                 expression = "this";
226             expressionResult = InjectedScript._evaluateOn(inspectedWindow.eval, inspectedWindow, expression, false);
227         }
228         if (typeof expressionResult === "object")
229             InjectedScript._populatePropertyNames(expressionResult, props);
230
231         if (includeInspectorCommandLineAPI) {
232             for (var prop in InjectedScript._commandLineAPI)
233                 props[prop] = true;
234         }
235     } catch(e) {
236     }
237     return props;
238 }
239
240 InjectedScript.evaluateAndStringify = function(expression)
241 {
242     var result = {};
243     try {
244         var value = InjectedScript._evaluateOn(inspectedWindow.eval, inspectedWindow, expression, false);
245         result.value = JSON.stringify(value);
246     } catch (e) {
247         result.value = e.toString();
248         result.isException = true;
249     }
250     return result;
251 }
252
253 InjectedScript.evaluate = function(expression, objectGroup)
254 {
255     return InjectedScript._evaluateAndWrap(inspectedWindow.eval, inspectedWindow, expression, objectGroup);
256 }
257
258 InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, objectGroup, dontUseCommandLineAPI)
259 {
260     try {
261         return InjectedScript.wrapObject(InjectedScript._evaluateOn(evalFunction, object, expression, dontUseCommandLineAPI), objectGroup);
262     } catch (e) {
263         return InjectedScript.RemoteObject.fromException(e);
264     }
265 }
266
267 InjectedScript._evaluateOn = function(evalFunction, object, expression, dontUseCommandLineAPI)
268 {
269     if (!dontUseCommandLineAPI) {
270         // Only install command line api object for the time of evaluation.
271
272         // Surround the expression in with statements to inject our command line API so that
273         // the window object properties still take more precedent than our API functions.
274         inspectedWindow.console._commandLineAPI = InjectedScript._commandLineAPI;
275
276         expression = "with (window.console._commandLineAPI) { with (window) {\n" + expression + "\n} }";
277     }
278
279     var value = evalFunction.call(object, expression);
280
281     if (!dontUseCommandLineAPI)
282         delete inspectedWindow.console._commandLineAPI;
283
284     // When evaluating on call frame error is not thrown, but returned as a value.
285     if (InjectedScript._type(value) === "error")
286         throw value.toString();
287
288     return value;
289 }
290
291 InjectedScript.getNodeId = function(node)
292 {
293     return InjectedScriptHost.pushNodePathToFrontend(node, false, false);
294 }
295
296 InjectedScript.openInInspectedWindow = function(url)
297 {
298     // Don't call window.open on wrapper - popup blocker mutes it.
299     // URIs should have no double quotes.
300     inspectedWindow.eval("window.open(\"" + url + "\")");
301     return true;
302 }
303
304 InjectedScript.callFrames = function()
305 {
306     var callFrame = InjectedScriptHost.currentCallFrame();
307     if (!callFrame)
308         return false;
309
310     var result = [];
311     var depth = 0;
312     do {
313         result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
314         callFrame = callFrame.caller;
315     } while (callFrame);
316     return result;
317 }
318
319 InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup)
320 {
321     var callFrame = InjectedScript._callFrameForId(callFrameId);
322     if (!callFrame)
323         return false;
324     return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup, true);
325 }
326
327 InjectedScript._callFrameForId = function(id)
328 {
329     var callFrame = InjectedScriptHost.currentCallFrame();
330     while (--id >= 0 && callFrame)
331         callFrame = callFrame.caller;
332     return callFrame;
333 }
334
335 InjectedScript._nodeForId = function(nodeId)
336 {
337     if (!nodeId)
338         return null;
339     return InjectedScriptHost.nodeForId(nodeId);
340 }
341
342 InjectedScript._objectForId = function(objectId)
343 {
344     // There are three types of object ids used:
345     // - numbers point to DOM Node via the InspectorDOMAgent mapping
346     // - strings point to console objects cached in InspectorController for lazy evaluation upon them
347     // - objects contain complex ids and are currently used for scoped objects
348     var object;
349     if (objectId.type === "node")
350         object = InjectedScript._nodeForId(objectId.value);
351     else if (objectId.type === "jsobject")
352         object = InjectedScript.unwrapObject(objectId.value);
353     else if (objectId.type === "scopeObject") {
354         var callFrame = InjectedScript._callFrameForId(objectId.value.callFrame);
355         if (objectId.thisObject)
356             object = callFrame.thisObject;
357         else
358             object = callFrame.scopeChain[objectId.value.chainIndex];
359     } else
360         return objectId;
361
362     var path = objectId.path;
363
364     // Follow the property path.
365     for (var i = 0; InjectedScript._isDefined(object) && path && i < path.length; ++i)
366         object = object[path[i]];
367
368     return object;
369 }
370
371 InjectedScript.pushNodeToFrontend = function(objectId)
372 {
373     var object = InjectedScript._objectForId(objectId);
374     if (!object || InjectedScript._type(object) !== "node")
375         return false;
376     return InjectedScriptHost.pushNodePathToFrontend(object, false, false);
377 }
378
379 // FIXME: RemoteObjectId and RemoteObject structs must match the WebInspector.* ones. Should reuse same file instead.
380 InjectedScript.RemoteObjectId = function(type, value, path)
381 {
382     this.injectedScriptId = injectedScriptId;
383     this.type = type;
384     this.value = value;
385     this.path = path || [];
386 }
387
388 InjectedScript.RemoteObject = function(objectId, type, description, hasChildren)
389 {
390     this.objectId = objectId;
391     this.type = type;
392     this.description = description;
393     this.hasChildren = hasChildren;
394 }
395
396 InjectedScript.RemoteObject.fromException = function(e)
397 {
398     return new InjectedScript.RemoteObject(null, "error", e.toString());
399 }
400
401 InjectedScript.RemoteObject.fromObject = function(object, objectId, abbreviate)
402 {
403     var type = InjectedScript._type(object);
404     var rawType = typeof object;
405     var hasChildren = (rawType === "object" && object !== null && (Object.getOwnPropertyNames(object).length || !!object.__proto__)) || rawType === "function";
406     var description = "";
407     try {
408         var description = InjectedScript._describe(object, abbreviate);
409         return new InjectedScript.RemoteObject(objectId, type, description, hasChildren);
410     } catch (e) {
411         return InjectedScript.RemoteObject.fromException(e);
412     }
413 }
414
415 InjectedScript.evaluateOnSelf = function(funcBody, args)
416 {
417     var func = window.eval("(" + funcBody + ")");
418     return func.apply(this, args || []);
419 }
420
421 InjectedScript.CallFrameProxy = function(id, callFrame)
422 {
423     this.id = id;
424     this.type = callFrame.type;
425     this.functionName = (this.type === "function" ? callFrame.functionName : "");
426     this.sourceID = callFrame.sourceID;
427     this.line = callFrame.line;
428     this.scopeChain = this._wrapScopeChain(callFrame);
429     this.injectedScriptId = injectedScriptId;
430 }
431
432 InjectedScript.CallFrameProxy.prototype = {
433     _wrapScopeChain: function(callFrame)
434     {
435         const GLOBAL_SCOPE = 0;
436         const LOCAL_SCOPE = 1;
437         const WITH_SCOPE = 2;
438         const CLOSURE_SCOPE = 3;
439         const CATCH_SCOPE = 4;
440     
441         var scopeChain = callFrame.scopeChain;
442         var scopeChainProxy = [];
443         var foundLocalScope = false;
444         for (var i = 0; i < scopeChain.length; i++) {
445             var scopeType = callFrame.scopeType(i);
446             var scopeObject = scopeChain[i];
447             var scopeObjectId = new InjectedScript.RemoteObjectId("scopeObject", { callFrame: this.id, chainIndex: i });
448             var scopeObjectProxy = InjectedScript.RemoteObject.fromObject(scopeObject, scopeObjectId, true);
449
450             switch(scopeType) {
451                 case LOCAL_SCOPE: {
452                     foundLocalScope = true;
453                     scopeObjectProxy.isLocal = true;
454                     var thisObjectId = new InjectedScript.RemoteObjectId("scopeObject", { callFrame: this.id, thisObject: true });
455                     scopeObjectProxy.thisObject = InjectedScript.RemoteObject.fromObject(callFrame.thisObject, thisObjectId, true);
456                     break;
457                 }
458                 case CLOSURE_SCOPE: {
459                     scopeObjectProxy.isClosure = true;
460                     break;
461                 }
462                 case WITH_SCOPE:
463                 case CATCH_SCOPE: {
464                     if (foundLocalScope && scopeObject instanceof inspectedWindow.Element)
465                         scopeObjectProxy.isElement = true;
466                     else if (foundLocalScope && scopeObject instanceof inspectedWindow.Document)
467                         scopeObjectProxy.isDocument = true;
468                     else
469                         scopeObjectProxy.isWithBlock = true;
470                     break;
471                 }
472             }
473             scopeChainProxy.push(scopeObjectProxy);
474         }
475         return scopeChainProxy;
476     }
477 }
478
479 InjectedScript._isDefined = function(object)
480 {
481     return object || InjectedScript._isHTMLAllCollection(object);
482 }
483
484 InjectedScript._isHTMLAllCollection = function(object)
485 {
486     // document.all is reported as undefined, but we still want to process it.
487     return (typeof object === "undefined") && inspectedWindow.HTMLAllCollection && object instanceof inspectedWindow.HTMLAllCollection;
488 }
489
490 InjectedScript._type = function(obj)
491 {
492     if (obj === null)
493         return "null";
494
495     var type = typeof obj;
496     if (type !== "object" && type !== "function") {
497         // FIXME(33716): typeof document.all is always 'undefined'.
498         if (InjectedScript._isHTMLAllCollection(obj))
499             return "array";
500         return type;
501     }
502
503     // If owning frame has navigated to somewhere else window properties will be undefined.
504     // In this case just return result of the typeof.
505     if (!inspectedWindow.document)
506         return type;
507
508     if (obj instanceof inspectedWindow.Node)
509         return (obj.nodeType === undefined ? type : "node");
510     if (obj instanceof inspectedWindow.String)
511         return "string";
512     if (obj instanceof inspectedWindow.Array)
513         return "array";
514     if (obj instanceof inspectedWindow.Boolean)
515         return "boolean";
516     if (obj instanceof inspectedWindow.Number)
517         return "number";
518     if (obj instanceof inspectedWindow.Date)
519         return "date";
520     if (obj instanceof inspectedWindow.RegExp)
521         return "regexp";
522     if (obj instanceof inspectedWindow.NodeList)
523         return "array";
524     if (obj instanceof inspectedWindow.HTMLCollection)
525         return "array";
526     if (typeof inspectedWindow.jQuery === "function" && obj instanceof inspectedWindow.jQuery)
527         return "array";
528     if (obj instanceof inspectedWindow.Error)
529         return "error";
530     return type;
531 }
532
533 InjectedScript._describe = function(obj, abbreviated)
534 {
535     var type = InjectedScript._type(obj);
536
537     switch (type) {
538     case "object":
539     case "node":
540     case "array":
541         var className = InjectedScript._className(obj);
542         if (typeof obj.length === "number")
543             className += "[" + obj.length + "]";
544         return className;
545     case "string":
546         if (!abbreviated)
547             return obj;
548         if (obj.length > 100)
549             return "\"" + obj.substring(0, 100) + "\u2026\"";
550         return "\"" + obj + "\"";
551     case "function":
552         var objectText = InjectedScript._toString(obj);
553         if (abbreviated)
554             objectText = /.*/.exec(objectText)[0].replace(/ +$/g, "");
555         return objectText;
556     default:
557         return InjectedScript._toString(obj);
558     }
559 }
560
561 InjectedScript._toString = function(obj)
562 {
563     // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page.
564     return "" + obj;
565 }
566
567 InjectedScript._className = function(obj)
568 {
569     // We can't use the same code for fetching class names of the dom bindings prototype chain.
570     // Both of the methods below result in "Object" names on the foreign engine bindings.
571     // I gave up and am using a check below to distinguish between the egine bingings.
572
573     if (jsEngine == "JSC") {
574         var str = inspectedWindow.Object ? inspectedWindow.Object.prototype.toString.call(obj) : InjectedScript._toString(obj);
575         return str.replace(/^\[object (.*)\]$/i, "$1");
576     } else {
577         // V8
578         return obj.constructor && obj.constructor.name || "Object";
579     }
580 }
581
582 InjectedScript._logEvent = function(event)
583 {
584     console.log(event.type, event);
585 }
586
587 InjectedScript._normalizeEventTypes = function(types)
588 {
589     if (typeof types === "undefined")
590         types = [ "mouse", "key", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll" ];
591     else if (typeof types === "string")
592         types = [ types ];
593
594     var result = [];
595     for (var i = 0; i < types.length; i++) {
596         if (types[i] === "mouse")
597             result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout");
598         else if (types[i] === "key")
599             result.splice(0, 0, "keydown", "keyup", "keypress");
600         else
601             result.push(types[i]);
602     }
603     return result;
604 }
605
606 InjectedScript._inspectedNode = function(num)
607 {
608     var nodeId = InjectedScriptHost.inspectedNode(num);
609     return InjectedScript._nodeForId(nodeId);
610 }
611
612 function CommandLineAPI()
613 {
614 }
615
616 CommandLineAPI.prototype = {
617     // Only add API functions here, private stuff should go to
618     // InjectedScript so that it is not suggested by the completion.
619     $: function()
620     {
621         return document.getElementById.apply(document, arguments)
622     },
623
624     $$: function()
625     {
626         return document.querySelectorAll.apply(document, arguments)
627     },
628
629     $x: function(xpath, context)
630     {
631         var nodes = [];
632         try {
633             var doc = context || document;
634             var results = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
635             var node;
636             while (node = results.iterateNext())
637                 nodes.push(node);
638         } catch (e) {
639         }
640         return nodes;
641     },
642
643     dir: function()
644     {
645         return console.dir.apply(console, arguments)
646     },
647
648     dirxml: function()
649     {
650         return console.dirxml.apply(console, arguments)
651     },
652
653     keys: function(object)
654     {
655         return Object.keys(object);
656     },
657
658     values: function(object)
659     {
660         var result = [];
661         for (var key in object)
662             result.push(object[key]);
663         return result;
664     },
665
666     profile: function()
667     {
668         return console.profile.apply(console, arguments)
669     },
670
671     profileEnd: function()
672     {
673         return console.profileEnd.apply(console, arguments)
674     },
675
676     monitorEvents: function(object, types)
677     {
678         if (!object || !object.addEventListener || !object.removeEventListener)
679             return;
680         types = InjectedScript._normalizeEventTypes(types);
681         for (var i = 0; i < types.length; ++i) {
682             object.removeEventListener(types[i], InjectedScript._logEvent, false);
683             object.addEventListener(types[i], InjectedScript._logEvent, false);
684         }
685     },
686
687     unmonitorEvents: function(object, types)
688     {
689         if (!object || !object.addEventListener || !object.removeEventListener)
690             return;
691         types = InjectedScript._normalizeEventTypes(types);
692         for (var i = 0; i < types.length; ++i)
693             object.removeEventListener(types[i], InjectedScript._logEvent, false);
694     },
695
696     inspect: function(object)
697     {
698         if (arguments.length === 0)
699             return;
700
701         inspectedWindow.console.log(object);
702         if (InjectedScript._type(object) === "node")
703             InjectedScriptHost.pushNodePathToFrontend(object, false, true);
704         else {
705             switch (InjectedScript._describe(object)) {
706                 case "Database":
707                     InjectedScriptHost.selectDatabase(object);
708                     break;
709                 case "Storage":
710                     InjectedScriptHost.selectDOMStorage(object);
711                     break;
712             }
713         }
714     },
715
716     copy: function(object)
717     {
718         if (InjectedScript._type(object) === "node") {
719             var nodeId = InjectedScriptHost.pushNodePathToFrontend(object, false, false);
720             InjectedScriptHost.copyNode(nodeId);
721         } else
722             InjectedScriptHost.copyText(object);
723     },
724
725     clear: function()
726     {
727         InjectedScriptHost.clearConsoleMessages();
728     },
729
730     get $0()
731     {
732         return InjectedScript._inspectedNode(0);
733     },
734
735     get $1()
736     {
737         return InjectedScript._inspectedNode(1);
738     },
739
740     get $2()
741     {
742         return InjectedScript._inspectedNode(2);
743     },
744
745     get $3()
746     {
747         return InjectedScript._inspectedNode(3);
748     },
749
750     get $4()
751     {
752         return InjectedScript._inspectedNode(4);
753     }
754 }
755
756 InjectedScript._commandLineAPI = new CommandLineAPI();
757
758 return InjectedScript;
759 });