1/*
2 * Copyright (C) 2009 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @interface */
32function InspectorFrontendHostAPI()
33{
34}
35
36/** @typedef {{type:string, id:(number|undefined),
37               label:(string|undefined), enabled:(boolean|undefined), checked:(boolean|undefined),
38               subItems:(!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>|undefined)}} */
39InspectorFrontendHostAPI.ContextMenuDescriptor;
40
41InspectorFrontendHostAPI.Events = {
42    AppendedToURL: "appendedToURL",
43    CanceledSaveURL: "canceledSaveURL",
44    ContextMenuCleared: "contextMenuCleared",
45    ContextMenuItemSelected: "contextMenuItemSelected",
46    DeviceCountUpdated: "deviceCountUpdated",
47    DevicesUpdated: "devicesUpdated",
48    DispatchMessage: "dispatchMessage",
49    EnterInspectElementMode: "enterInspectElementMode",
50    FileSystemsLoaded: "fileSystemsLoaded",
51    FileSystemRemoved: "fileSystemRemoved",
52    FileSystemAdded: "fileSystemAdded",
53    IndexingTotalWorkCalculated: "indexingTotalWorkCalculated",
54    IndexingWorked: "indexingWorked",
55    IndexingDone: "indexingDone",
56    KeyEventUnhandled: "keyEventUnhandled",
57    RevealSourceLine: "revealSourceLine",
58    SavedURL: "savedURL",
59    SearchCompleted: "searchCompleted",
60    SetToolbarColors: "setToolbarColors",
61    SetUseSoftMenu: "setUseSoftMenu",
62    ShowConsole: "showConsole"
63}
64
65InspectorFrontendHostAPI.EventDescriptors = [
66    [InspectorFrontendHostAPI.Events.AppendedToURL, ["url"]],
67    [InspectorFrontendHostAPI.Events.CanceledSaveURL, ["url"]],
68    [InspectorFrontendHostAPI.Events.ContextMenuCleared, []],
69    [InspectorFrontendHostAPI.Events.ContextMenuItemSelected, ["id"]],
70    [InspectorFrontendHostAPI.Events.DeviceCountUpdated, ["count"]],
71    [InspectorFrontendHostAPI.Events.DevicesUpdated, ["devices"]],
72    [InspectorFrontendHostAPI.Events.DispatchMessage, ["messageObject"]],
73    [InspectorFrontendHostAPI.Events.EnterInspectElementMode, [], true],
74    [InspectorFrontendHostAPI.Events.FileSystemsLoaded, ["fileSystems"]],
75    [InspectorFrontendHostAPI.Events.FileSystemRemoved, ["fileSystemPath"]],
76    [InspectorFrontendHostAPI.Events.FileSystemAdded, ["errorMessage", "fileSystem"]],
77    [InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, ["requestId", "fileSystemPath", "totalWork"]],
78    [InspectorFrontendHostAPI.Events.IndexingWorked, ["requestId", "fileSystemPath", "worked"]],
79    [InspectorFrontendHostAPI.Events.IndexingDone, ["requestId", "fileSystemPath"]],
80    [InspectorFrontendHostAPI.Events.KeyEventUnhandled, ["event"], true],
81    [InspectorFrontendHostAPI.Events.RevealSourceLine, ["url", "lineNumber", "columnNumber"], true],
82    [InspectorFrontendHostAPI.Events.SavedURL, ["url"]],
83    [InspectorFrontendHostAPI.Events.SearchCompleted, ["requestId", "fileSystemPath", "files"]],
84    [InspectorFrontendHostAPI.Events.SetToolbarColors, ["backgroundColor", "color"]],
85    [InspectorFrontendHostAPI.Events.SetUseSoftMenu, ["useSoftMenu"]],
86    [InspectorFrontendHostAPI.Events.ShowConsole, [], true]
87];
88
89InspectorFrontendHostAPI.prototype = {
90    addFileSystem: function() { },
91
92    /**
93     * @param {string} url
94     * @param {string} content
95     */
96    append: function(url, content) { },
97
98    /**
99     * @param {number} requestId
100     * @param {string} fileSystemPath
101     */
102    indexPath: function(requestId, fileSystemPath) { },
103
104    /**
105     * @return {string}
106     */
107    getSelectionBackgroundColor: function() { },
108
109    /**
110     * @return {string}
111     */
112    getSelectionForegroundColor: function() { },
113
114    /**
115     * Requests inspected page to be placed atop of the inspector frontend with specified bounds.
116     * @param {{x: number, y: number, width: number, height: number}} bounds
117     */
118    setInspectedPageBounds: function(bounds) { },
119
120    /**
121     * Requests inspected page to be placed atop of the inspector frontend
122     * with passed insets from the frontend sides, respecting minimum size passed.
123     * @param {{top: number, left: number, right: number, bottom: number}} insets
124     * @param {{width: number, height: number}} minSize
125     */
126    setContentsResizingStrategy: function(insets, minSize) { },
127
128    /**
129     * @param {string} shortcuts
130     */
131    setWhitelistedShortcuts: function(shortcuts) { },
132
133    inspectElementCompleted: function() { },
134
135    /**
136     * @param {number} x
137     * @param {number} y
138     */
139    moveWindowBy: function(x, y) { },
140
141    /**
142     * @param {string} url
143     */
144    openInNewTab: function(url) { },
145
146    /**
147     * @param {string} fileSystemPath
148     */
149    removeFileSystem: function(fileSystemPath) { },
150
151    requestFileSystems: function() { },
152
153    /**
154     * @param {string} url
155     * @param {string} content
156     * @param {boolean} forceSaveAs
157     */
158    save: function(url, content, forceSaveAs) { },
159
160    /**
161     * @param {number} requestId
162     * @param {string} fileSystemPath
163     * @param {string} query
164     */
165    searchInPath: function(requestId, fileSystemPath, query) { },
166
167    /**
168     * @param {number} requestId
169     */
170    stopIndexing: function(requestId) { },
171
172    bringToFront: function() { },
173
174    /**
175     * @param {string} browserId
176     * @param {string} url
177     */
178    openUrlOnRemoteDeviceAndInspect: function(browserId, url) { },
179
180    closeWindow: function() { },
181
182    copyText: function(text) { },
183
184    /**
185     * @param {string} url
186     */
187    inspectedURLChanged: function(url) { },
188
189    /**
190     * @param {string} fileSystemId
191     * @param {string} registeredName
192     * @return {?DOMFileSystem}
193     */
194    isolatedFileSystem: function(fileSystemId, registeredName) { },
195
196    /**
197     * @param {!FileSystem} fileSystem
198     */
199    upgradeDraggedFileSystemPermissions: function(fileSystem) { },
200
201    /**
202     * @return {string}
203     */
204    platform: function() { },
205
206    /**
207     * @return {string}
208     */
209    port: function() { },
210
211    /**
212     * @param {number} actionCode
213     */
214    recordActionTaken: function(actionCode) { },
215
216    /**
217     * @param {number} panelCode
218     */
219    recordPanelShown: function(panelCode) { },
220
221    /**
222     * @param {string} message
223     */
224    sendMessageToBackend: function(message) { },
225
226    /**
227     * @param {string} message
228     */
229    sendMessageToEmbedder: function(message) { },
230
231    /**
232     * @param {boolean} enabled
233     */
234    setDeviceCountUpdatesEnabled: function(enabled) { },
235
236    /**
237     * @param {boolean} enabled
238     */
239    setDevicesUpdatesEnabled: function(enabled) { },
240
241    /**
242     * @param {string} origin
243     * @param {string} script
244     */
245    setInjectedScriptForOrigin: function(origin, script) { },
246
247    /**
248     * @param {boolean} isDocked
249     * @param {!function()} callback
250     */
251    setIsDocked: function(isDocked, callback) { },
252
253    /**
254     * @param {number} zoom
255     */
256    setZoomFactor: function(zoom) { },
257
258    /**
259     * @return {number}
260     */
261    zoomFactor: function() { },
262
263    zoomIn: function() { },
264
265    zoomOut: function() { },
266
267    resetZoom: function() { },
268
269    /**
270     * @param {number} x
271     * @param {number} y
272     * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items
273     */
274    showContextMenuAtPoint: function(x, y, items) { },
275
276    /**
277     * @return {boolean}
278     */
279    isUnderTest: function() { },
280
281    /**
282     * @return {boolean}
283     */
284    isHostedMode: function() { }
285}
286
287/**
288 * @constructor
289 * @implements {InspectorFrontendHostAPI}
290 */
291WebInspector.InspectorFrontendHostStub = function()
292{
293}
294
295WebInspector.InspectorFrontendHostStub.prototype = {
296    /**
297     * @return {string}
298     */
299    getSelectionBackgroundColor: function()
300    {
301        return "#6e86ff";
302    },
303
304    /**
305     * @return {string}
306     */
307    getSelectionForegroundColor: function()
308    {
309        return "#ffffff";
310    },
311
312    /**
313     * @return {string}
314     */
315    platform: function()
316    {
317        var match = navigator.userAgent.match(/Windows NT/);
318        if (match)
319            return "windows";
320        match = navigator.userAgent.match(/Mac OS X/);
321        if (match)
322            return "mac";
323        return "linux";
324    },
325
326    /**
327     * @return {string}
328     */
329    port: function()
330    {
331        return "unknown";
332    },
333
334    bringToFront: function()
335    {
336        this._windowVisible = true;
337    },
338
339    closeWindow: function()
340    {
341        this._windowVisible = false;
342    },
343
344    /**
345     * @param {boolean} isDocked
346     * @param {!function()} callback
347     */
348    setIsDocked: function(isDocked, callback)
349    {
350    },
351
352    /**
353     * Requests inspected page to be placed atop of the inspector frontend with specified bounds.
354     * @param {{x: number, y: number, width: number, height: number}} bounds
355     */
356    setInspectedPageBounds: function(bounds)
357    {
358    },
359
360    /**
361     * Requests inspected page to be placed atop of the inspector frontend
362     * with passed insets from the frontend sides, respecting minimum size passed.
363     * @param {{top: number, left: number, right: number, bottom: number}} insets
364     * @param {{width: number, height: number}} minSize
365     */
366    setContentsResizingStrategy: function(insets, minSize)
367    {
368    },
369
370    inspectElementCompleted: function()
371    {
372    },
373
374    /**
375     * @param {number} x
376     * @param {number} y
377     */
378    moveWindowBy: function(x, y)
379    {
380    },
381
382    /**
383     * @param {string} origin
384     * @param {string} script
385     */
386    setInjectedScriptForOrigin: function(origin, script)
387    {
388    },
389
390    /**
391     * @param {string} url
392     */
393    inspectedURLChanged: function(url)
394    {
395        document.title = WebInspector.UIString("Developer Tools - %s", url);
396    },
397
398    /**
399     * @param {string} text
400     */
401    copyText: function(text)
402    {
403        WebInspector.console.error("Clipboard is not enabled in hosted mode. Please inspect using chrome://inspect");
404    },
405
406    /**
407     * @param {string} url
408     */
409    openInNewTab: function(url)
410    {
411        window.open(url, "_blank");
412    },
413
414    /**
415     * @param {string} url
416     * @param {string} content
417     * @param {boolean} forceSaveAs
418     */
419    save: function(url, content, forceSaveAs)
420    {
421        WebInspector.console.error("Saving files is not enabled in hosted mode. Please inspect using chrome://inspect");
422        this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.CanceledSaveURL, url);
423    },
424
425    /**
426     * @param {string} url
427     * @param {string} content
428     */
429    append: function(url, content)
430    {
431        WebInspector.console.error("Saving files is not enabled in hosted mode. Please inspect using chrome://inspect");
432    },
433
434    /**
435     * @param {string} message
436     */
437    sendMessageToBackend: function(message)
438    {
439    },
440
441    /**
442     * @param {string} message
443     */
444    sendMessageToEmbedder: function(message)
445    {
446    },
447
448    /**
449     * @param {number} actionCode
450     */
451    recordActionTaken: function(actionCode)
452    {
453    },
454
455    /**
456     * @param {number} panelCode
457     */
458    recordPanelShown: function(panelCode)
459    {
460    },
461
462    requestFileSystems: function()
463    {
464    },
465
466    addFileSystem: function()
467    {
468    },
469
470    /**
471     * @param {string} fileSystemPath
472     */
473    removeFileSystem: function(fileSystemPath)
474    {
475    },
476
477    /**
478     * @param {string} fileSystemId
479     * @param {string} registeredName
480     * @return {?DOMFileSystem}
481     */
482    isolatedFileSystem: function(fileSystemId, registeredName)
483    {
484        return null;
485    },
486
487    /**
488     * @param {!FileSystem} fileSystem
489     */
490    upgradeDraggedFileSystemPermissions: function(fileSystem)
491    {
492    },
493
494    /**
495     * @param {number} requestId
496     * @param {string} fileSystemPath
497     */
498    indexPath: function(requestId, fileSystemPath)
499    {
500    },
501
502    /**
503     * @param {number} requestId
504     */
505    stopIndexing: function(requestId)
506    {
507    },
508
509    /**
510     * @param {number} requestId
511     * @param {string} fileSystemPath
512     * @param {string} query
513     */
514    searchInPath: function(requestId, fileSystemPath, query)
515    {
516    },
517
518    /**
519     * @param {number} zoom
520     */
521    setZoomFactor: function(zoom)
522    {
523    },
524
525    /**
526     * @return {number}
527     */
528    zoomFactor: function()
529    {
530        return 1;
531    },
532
533    zoomIn: function()
534    {
535    },
536
537    zoomOut: function()
538    {
539    },
540
541    resetZoom: function()
542    {
543    },
544
545    setWhitelistedShortcuts: function(shortcuts)
546    {
547    },
548
549    /**
550     * @return {boolean}
551     */
552    isUnderTest: function()
553    {
554        return false;
555    },
556
557    /**
558     * @param {string} browserId
559     * @param {string} url
560     */
561    openUrlOnRemoteDeviceAndInspect: function(browserId, url)
562    {
563    },
564
565    /**
566     * @param {boolean} enabled
567     */
568    setDeviceCountUpdatesEnabled: function(enabled)
569    {
570    },
571
572    /**
573     * @param {boolean} enabled
574     */
575    setDevicesUpdatesEnabled: function(enabled)
576    {
577    },
578
579    /**
580     * @param {number} x
581     * @param {number} y
582     * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items
583     */
584    showContextMenuAtPoint: function(x, y, items)
585    {
586        throw "Soft context menu should be used";
587    },
588
589    /**
590     * @return {boolean}
591     */
592    isHostedMode: function()
593    {
594        return true;
595    }
596};
597
598/**
599 * @type {!InspectorFrontendHostAPI}
600 */
601var InspectorFrontendHost = window.InspectorFrontendHost || null;
602
603(function() {
604    if (!InspectorFrontendHost) {
605        // Instantiate stub for web-hosted mode if necessary.
606        InspectorFrontendHost = new WebInspector.InspectorFrontendHostStub();
607    } else {
608        // Otherwise add stubs for missing methods that are declared in the interface.
609        var proto = WebInspector.InspectorFrontendHostStub.prototype;
610        for (var name in proto) {
611            var value = proto[name];
612            if (typeof value !== "function" || InspectorFrontendHost[name])
613                continue;
614
615            InspectorFrontendHost[name] = stub.bind(null, name);
616        }
617    }
618
619    /**
620     * @param {string} name
621     */
622    function stub(name)
623    {
624        console.error("Incompatible embedder: method InspectorFrontendHost." + name + " is missing. Using stub instead.");
625        var args = Array.prototype.slice.call(arguments, 1);
626        return proto[name].apply(InspectorFrontendHost, args);
627    }
628
629    // Attach the events object.
630    InspectorFrontendHost.events = new WebInspector.Object();
631})();
632
633/**
634 * @constructor
635 */
636function InspectorFrontendAPIImpl()
637{
638    this._isLoaded = false;
639    this._pendingCommands = [];
640    this._debugFrontend = !!Runtime.queryParam("debugFrontend");
641
642    var descriptors = InspectorFrontendHostAPI.EventDescriptors;
643    for (var i = 0; i < descriptors.length; ++i)
644        this[descriptors[i][0]] = this._dispatch.bind(this, descriptors[i][0], descriptors[i][1], descriptors[i][2]);
645}
646
647InspectorFrontendAPIImpl.prototype = {
648    loadCompleted: function()
649    {
650        this._isLoaded = true;
651        for (var i = 0; i < this._pendingCommands.length; ++i)
652            this._pendingCommands[i]();
653        this._pendingCommands = [];
654        if (window.opener)
655            window.opener.postMessage(["loadCompleted"], "*");
656    },
657
658    /**
659     * @param {string} name
660     * @param {!Array.<string>} signature
661     * @param {boolean} runOnceLoaded
662     */
663    _dispatch: function(name, signature, runOnceLoaded)
664    {
665        var params = Array.prototype.slice.call(arguments, 3);
666
667        if (this._debugFrontend)
668            setImmediate(innerDispatch.bind(this));
669        else
670            innerDispatch.call(this);
671
672        /**
673         * @this {!InspectorFrontendAPIImpl}
674         */
675        function innerDispatch()
676        {
677            if (runOnceLoaded)
678                this._runOnceLoaded(dispatchAfterLoad);
679            else
680                dispatchAfterLoad();
681
682            function dispatchAfterLoad()
683            {
684                // Single argument methods get dispatched with the param.
685                if (signature.length < 2) {
686                    InspectorFrontendHost.events.dispatchEventToListeners(name, params[0]);
687                    return;
688                }
689                var data = {};
690                for (var i = 0; i < signature.length; ++i)
691                    data[signature[i]] = params[i];
692                InspectorFrontendHost.events.dispatchEventToListeners(name, data);
693            }
694        }
695    },
696
697    /**
698     * @param {function()} command
699     */
700    _runOnceLoaded: function(command)
701    {
702        if (this._isLoaded) {
703            command();
704            return;
705        }
706        this._pendingCommands.push(command);
707    },
708
709    /**
710     * @param {number} id
711     * @param {?string} error
712     */
713    embedderMessageAck: function(id, error)
714    {
715        InspectorFrontendHost["embedderMessageAck"](id, error);
716    }
717}
718
719var InspectorFrontendAPI = new InspectorFrontendAPIImpl();
720