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 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//# sourceURL=__WebInspectorCommandLineAPIModuleSource__
30
31/**
32 * @param {InjectedScriptHost} InjectedScriptHost
33 * @param {Window} inspectedWindow
34 * @param {number} injectedScriptId
35 * @param {InjectedScript} injectedScript
36 * @param {CommandLineAPIHost} CommandLineAPIHost
37 */
38(function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript, CommandLineAPIHost) {
39
40/**
41 * @param {Arguments} array
42 * @param {number=} index
43 * @return {Array.<*>}
44 */
45function slice(array, index)
46{
47    var result = [];
48    for (var i = index || 0; i < array.length; ++i)
49        result.push(array[i]);
50    return result;
51}
52
53/**
54 * Please use this bind, not the one from Function.prototype
55 * @param {function(...)} func
56 * @param {Object} thisObject
57 * @param {...number} var_args
58 */
59function bind(func, thisObject, var_args)
60{
61    var args = slice(arguments, 2);
62
63    /**
64     * @param {...number} var_args
65     */
66    function bound(var_args)
67    {
68        return func.apply(thisObject, args.concat(slice(arguments)));
69    }
70    bound.toString = function() {
71        return "bound: " + func;
72    };
73    return bound;
74}
75
76/**
77 * @constructor
78 * @param {CommandLineAPIImpl} commandLineAPIImpl
79 * @param {Object} callFrame
80 */
81function CommandLineAPI(commandLineAPIImpl, callFrame)
82{
83    /**
84     * @param {string} member
85     * @return {boolean}
86     */
87    function inScopeVariables(member)
88    {
89        if (!callFrame)
90            return false;
91
92        var scopeChain = callFrame.scopeChain;
93        for (var i = 0; i < scopeChain.length; ++i) {
94            if (member in scopeChain[i])
95                return true;
96        }
97        return false;
98    }
99
100    /**
101     * @param {string} name The name of the method for which a toString method should be generated.
102     * @return {function():string}
103     */
104    function customToStringMethod(name)
105    {
106        return function () { return "function " + name + "() { [Command Line API] }"; };
107    }
108
109    for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
110        var member = CommandLineAPI.members_[i];
111        if (member in inspectedWindow || inScopeVariables(member))
112            continue;
113
114        this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
115        this[member].toString = customToStringMethod(member);
116    }
117
118    for (var i = 0; i < 5; ++i) {
119        var member = "$" + i;
120        if (member in inspectedWindow || inScopeVariables(member))
121            continue;
122
123        this.__defineGetter__("$" + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
124    }
125
126    this.$_ = injectedScript._lastResult;
127}
128
129/**
130 * @type {Array.<string>}
131 * @const
132 */
133CommandLineAPI.members_ = [
134    "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
135    "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners"
136];
137
138/**
139 * @constructor
140 */
141function CommandLineAPIImpl()
142{
143}
144
145CommandLineAPIImpl.prototype = {
146    /**
147     * @param {string} selector
148     * @param {Node=} start
149     */
150    $: function (selector, start)
151    {
152        if (this._canQuerySelectorOnNode(start))
153            return start.querySelector(selector);
154
155        var result = inspectedWindow.document.querySelector(selector);
156        if (result)
157            return result;
158        if (selector && selector[0] !== "#") {
159            result = inspectedWindow.document.getElementById(selector);
160            if (result) {
161                inspectedWindow.console.warn("The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $(\"#%s\")", selector );
162                return null;
163            }
164        }
165        return result;
166    },
167
168    /**
169     * @param {string} selector
170     * @param {Node=} start
171     */
172    $$: function (selector, start)
173    {
174        if (this._canQuerySelectorOnNode(start))
175            return start.querySelectorAll(selector);
176        return inspectedWindow.document.querySelectorAll(selector);
177    },
178
179    /**
180     * @param {Node=} node
181     * @return {boolean}
182     */
183    _canQuerySelectorOnNode: function(node)
184    {
185        return !!node && InjectedScriptHost.type(node) === "node" && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
186    },
187
188    /**
189     * @param {string} xpath
190     * @param {Node=} context
191     */
192    $x: function(xpath, context)
193    {
194        var doc = (context && context.ownerDocument) || inspectedWindow.document;
195        var result = doc.evaluate(xpath, context || doc, null, XPathResult.ANY_TYPE, null);
196        switch (result.resultType) {
197        case XPathResult.NUMBER_TYPE:
198            return result.numberValue;
199        case XPathResult.STRING_TYPE:
200            return result.stringValue;
201        case XPathResult.BOOLEAN_TYPE:
202            return result.booleanValue;
203        default:
204            var nodes = [];
205            var node;
206            while (node = result.iterateNext())
207                nodes.push(node);
208            return nodes;
209        }
210    },
211
212    dir: function()
213    {
214        return inspectedWindow.console.dir.apply(inspectedWindow.console, arguments)
215    },
216
217    dirxml: function()
218    {
219        return inspectedWindow.console.dirxml.apply(inspectedWindow.console, arguments)
220    },
221
222    keys: function(object)
223    {
224        return Object.keys(object);
225    },
226
227    values: function(object)
228    {
229        var result = [];
230        for (var key in object)
231            result.push(object[key]);
232        return result;
233    },
234
235    profile: function()
236    {
237        return inspectedWindow.console.profile.apply(inspectedWindow.console, arguments)
238    },
239
240    profileEnd: function()
241    {
242        return inspectedWindow.console.profileEnd.apply(inspectedWindow.console, arguments)
243    },
244
245    /**
246     * @param {Object} object
247     * @param {Array.<string>|string=} types
248     */
249    monitorEvents: function(object, types)
250    {
251        if (!object || !object.addEventListener || !object.removeEventListener)
252            return;
253        types = this._normalizeEventTypes(types);
254        for (var i = 0; i < types.length; ++i) {
255            object.removeEventListener(types[i], this._logEvent, false);
256            object.addEventListener(types[i], this._logEvent, false);
257        }
258    },
259
260    /**
261     * @param {Object} object
262     * @param {Array.<string>|string=} types
263     */
264    unmonitorEvents: function(object, types)
265    {
266        if (!object || !object.addEventListener || !object.removeEventListener)
267            return;
268        types = this._normalizeEventTypes(types);
269        for (var i = 0; i < types.length; ++i)
270            object.removeEventListener(types[i], this._logEvent, false);
271    },
272
273    /**
274     * @param {*} object
275     * @return {*}
276     */
277    inspect: function(object)
278    {
279        return this._inspect(object);
280    },
281
282    copy: function(object)
283    {
284        if (injectedScript._subtype(object) === "node")
285            object = object.outerHTML;
286        CommandLineAPIHost.copyText(object);
287    },
288
289    clear: function()
290    {
291        CommandLineAPIHost.clearConsoleMessages();
292    },
293
294    /**
295     * @param {Node} node
296     */
297    getEventListeners: function(node)
298    {
299        return CommandLineAPIHost.getEventListeners(node);
300    },
301
302    /**
303     * @param {number} num
304     */
305    _inspectedObject: function(num)
306    {
307        return CommandLineAPIHost.inspectedObject(num);
308    },
309
310    /**
311     * @param {Array.<string>|string=} types
312     * @return {Array.<string>}
313     */
314    _normalizeEventTypes: function(types)
315    {
316        if (typeof types === "undefined")
317            types = [ "mouse", "key", "touch", "control", "load", "unload", "abort", "error", "select", "change", "submit", "reset", "focus", "blur", "resize", "scroll", "search", "devicemotion", "deviceorientation" ];
318        else if (typeof types === "string")
319            types = [ types ];
320
321        var result = [];
322        for (var i = 0; i < types.length; i++) {
323            if (types[i] === "mouse")
324                result.splice(0, 0, "mousedown", "mouseup", "click", "dblclick", "mousemove", "mouseover", "mouseout", "mousewheel");
325            else if (types[i] === "key")
326                result.splice(0, 0, "keydown", "keyup", "keypress", "textInput");
327            else if (types[i] === "touch")
328                result.splice(0, 0, "touchstart", "touchmove", "touchend", "touchcancel");
329            else if (types[i] === "control")
330                result.splice(0, 0, "resize", "scroll", "zoom", "focus", "blur", "select", "change", "submit", "reset");
331            else
332                result.push(types[i]);
333        }
334        return result;
335    },
336
337    /**
338     * @param {Event} event
339     */
340    _logEvent: function(event)
341    {
342        inspectedWindow.console.log(event.type, event);
343    },
344
345    /**
346     * @param {*} object
347     * @return {*}
348     */
349    _inspect: function(object)
350    {
351        if (arguments.length === 0)
352            return;
353
354        var objectId = injectedScript._wrapObject(object, "");
355        var hints = {};
356
357        switch (injectedScript._describe(object)) {
358        case "Database":
359            var databaseId = CommandLineAPIHost.databaseId(object)
360            if (databaseId)
361                hints.databaseId = databaseId;
362            break;
363        case "Storage":
364            var storageId = CommandLineAPIHost.storageId(object)
365            if (storageId)
366                hints.domStorageId = InjectedScriptHost.evaluate("(" + storageId + ")");
367            break;
368        }
369
370        CommandLineAPIHost.inspect(objectId, hints);
371        return object;
372    }
373}
374
375injectedScript.CommandLineAPI = CommandLineAPI;
376injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
377
378// This Module doesn't expose an object, it just adds an extension that InjectedScript uses.
379// However, we return an empty object, so that InjectedScript knows this module has been loaded.
380return {};
381
382})
383