Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / tools / test / reference_build / chrome_linux / resources / inspector / devtools_extension_api.js
1 (function() {
2     /*
3  * Copyright (C) 2012 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 are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 function defineCommonExtensionSymbols(apiPrivate)
33 {
34     if (!apiPrivate.audits)
35         apiPrivate.audits = {};
36     apiPrivate.audits.Severity = {
37         Info: "info",
38         Warning: "warning",
39         Severe: "severe"
40     };
41
42     if (!apiPrivate.console)
43         apiPrivate.console = {};
44     apiPrivate.console.Severity = {
45         Debug: "debug",
46         Log: "log",
47         Warning: "warning",
48         Error: "error"
49     };
50
51     if (!apiPrivate.panels)
52         apiPrivate.panels = {};
53     apiPrivate.panels.SearchAction = {
54         CancelSearch: "cancelSearch",
55         PerformSearch: "performSearch",
56         NextSearchResult: "nextSearchResult",
57         PreviousSearchResult: "previousSearchResult"
58     };
59
60     apiPrivate.Events = {
61         AuditStarted: "audit-started-",
62         ButtonClicked: "button-clicked-",
63         ConsoleMessageAdded: "console-message-added",
64         PanelObjectSelected: "panel-objectSelected-",
65         NetworkRequestFinished: "network-request-finished",
66         OpenResource: "open-resource",
67         PanelSearch: "panel-search-",
68         ResourceAdded: "resource-added",
69         ResourceContentCommitted: "resource-content-committed",
70         TimelineEventRecorded: "timeline-event-recorded",
71         ViewShown: "view-shown-",
72         ViewHidden: "view-hidden-"
73     };
74
75     apiPrivate.Commands = {
76         AddAuditCategory: "addAuditCategory",
77         AddAuditResult: "addAuditResult",
78         AddConsoleMessage: "addConsoleMessage",
79         AddRequestHeaders: "addRequestHeaders",
80         ApplyStyleSheet: "applyStyleSheet",
81         CreatePanel: "createPanel",
82         CreateSidebarPane: "createSidebarPane",
83         CreateStatusBarButton: "createStatusBarButton",
84         EvaluateOnInspectedPage: "evaluateOnInspectedPage",
85         ForwardKeyboardEvent: "_forwardKeyboardEvent",
86         GetConsoleMessages: "getConsoleMessages",
87         GetHAR: "getHAR",
88         GetPageResources: "getPageResources",
89         GetRequestContent: "getRequestContent",
90         GetResourceContent: "getResourceContent",
91         InspectedURLChanged: "inspectedURLChanged",
92         OpenResource: "openResource",
93         Reload: "Reload",
94         Subscribe: "subscribe",
95         SetOpenResourceHandler: "setOpenResourceHandler",
96         SetResourceContent: "setResourceContent",
97         SetSidebarContent: "setSidebarContent",
98         SetSidebarHeight: "setSidebarHeight",
99         SetSidebarPage: "setSidebarPage",
100         ShowPanel: "showPanel",
101         StopAuditCategoryRun: "stopAuditCategoryRun",
102         Unsubscribe: "unsubscribe",
103         UpdateAuditProgress: "updateAuditProgress",
104         UpdateButton: "updateButton"
105     };
106 }
107
108 /**
109  * @param {number} injectedScriptId
110  * @return {!Object}
111  */
112 function injectedExtensionAPI(injectedScriptId)
113 {
114
115 var apiPrivate = {};
116
117 defineCommonExtensionSymbols(apiPrivate);
118
119 var commands = apiPrivate.Commands;
120 var events = apiPrivate.Events;
121 var userAction = false;
122
123 // Here and below, all constructors are private to API implementation.
124 // For a public type Foo, if internal fields are present, these are on
125 // a private FooImpl type, an instance of FooImpl is used in a closure
126 // by Foo consutrctor to re-bind publicly exported members to an instance
127 // of Foo.
128
129 /**
130  * @constructor
131  */
132 function EventSinkImpl(type, customDispatch)
133 {
134     this._type = type;
135     this._listeners = [];
136     this._customDispatch = customDispatch;
137 }
138
139 EventSinkImpl.prototype = {
140     addListener: function(callback)
141     {
142         if (typeof callback !== "function")
143             throw "addListener: callback is not a function";
144         if (this._listeners.length === 0)
145             extensionServer.sendRequest({ command: commands.Subscribe, type: this._type });
146         this._listeners.push(callback);
147         extensionServer.registerHandler("notify-" + this._type, this._dispatch.bind(this));
148     },
149
150     removeListener: function(callback)
151     {
152         var listeners = this._listeners;
153
154         for (var i = 0; i < listeners.length; ++i) {
155             if (listeners[i] === callback) {
156                 listeners.splice(i, 1);
157                 break;
158             }
159         }
160         if (this._listeners.length === 0)
161             extensionServer.sendRequest({ command: commands.Unsubscribe, type: this._type });
162     },
163
164     /**
165      * @param {...} vararg
166      */
167     _fire: function(vararg)
168     {
169         var listeners = this._listeners.slice();
170         for (var i = 0; i < listeners.length; ++i)
171             listeners[i].apply(null, arguments);
172     },
173
174     _dispatch: function(request)
175     {
176          if (this._customDispatch)
177              this._customDispatch.call(this, request);
178          else
179              this._fire.apply(this, request.arguments);
180     }
181 }
182
183 /**
184  * @constructor
185  */
186 function InspectorExtensionAPI()
187 {
188     this.audits = new Audits();
189     this.inspectedWindow = new InspectedWindow();
190     this.panels = new Panels();
191     this.network = new Network();
192     defineDeprecatedProperty(this, "webInspector", "resources", "network");
193     this.timeline = new Timeline();
194     this.console = new ConsoleAPI();
195 }
196
197 /**
198  * @constructor
199  */
200 function ConsoleAPI()
201 {
202     this.onMessageAdded = new EventSink(events.ConsoleMessageAdded);
203 }
204
205 ConsoleAPI.prototype = {
206     getMessages: function(callback)
207     {
208         extensionServer.sendRequest({ command: commands.GetConsoleMessages }, callback);
209     },
210
211     addMessage: function(severity, text, url, line)
212     {
213         extensionServer.sendRequest({ command: commands.AddConsoleMessage, severity: severity, text: text, url: url, line: line });
214     },
215
216     get Severity()
217     {
218         return apiPrivate.console.Severity;
219     }
220 }
221
222 /**
223  * @constructor
224  */
225 function Network()
226 {
227     /**
228      * @this {EventSinkImpl}
229      */
230     function dispatchRequestEvent(message)
231     {
232         var request = message.arguments[1];
233         request.__proto__ = new Request(message.arguments[0]);
234         this._fire(request);
235     }
236     this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
237     defineDeprecatedProperty(this, "network", "onFinished", "onRequestFinished");
238     this.onNavigated = new EventSink(events.InspectedURLChanged);
239 }
240
241 Network.prototype = {
242     getHAR: function(callback)
243     {
244         function callbackWrapper(result)
245         {
246             var entries = (result && result.entries) || [];
247             for (var i = 0; i < entries.length; ++i) {
248                 entries[i].__proto__ = new Request(entries[i]._requestId);
249                 delete entries[i]._requestId;
250             }
251             callback(result);
252         }
253         extensionServer.sendRequest({ command: commands.GetHAR }, callback && callbackWrapper);
254     },
255
256     addRequestHeaders: function(headers)
257     {
258         extensionServer.sendRequest({ command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname });
259     }
260 }
261
262 /**
263  * @constructor
264  */
265 function RequestImpl(id)
266 {
267     this._id = id;
268 }
269
270 RequestImpl.prototype = {
271     getContent: function(callback)
272     {
273         function callbackWrapper(response)
274         {
275             callback(response.content, response.encoding);
276         }
277         extensionServer.sendRequest({ command: commands.GetRequestContent, id: this._id }, callback && callbackWrapper);
278     }
279 }
280
281 /**
282  * @constructor
283  */
284 function Panels()
285 {
286     var panels = {
287         elements: new ElementsPanel(),
288         sources: new SourcesPanel(),
289     };
290
291     function panelGetter(name)
292     {
293         return panels[name];
294     }
295     for (var panel in panels)
296         this.__defineGetter__(panel, panelGetter.bind(null, panel));
297     this.applyStyleSheet = function(styleSheet) { extensionServer.sendRequest({ command: commands.ApplyStyleSheet, styleSheet: styleSheet }); };
298 }
299
300 Panels.prototype = {
301     create: function(title, icon, page, callback)
302     {
303         var id = "extension-panel-" + extensionServer.nextObjectId();
304         var request = {
305             command: commands.CreatePanel,
306             id: id,
307             title: title,
308             icon: icon,
309             page: page
310         };
311         extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
312     },
313
314     setOpenResourceHandler: function(callback)
315     {
316         var hadHandler = extensionServer.hasHandler(events.OpenResource);
317
318         function callbackWrapper(message)
319         {
320             // Allow the panel to show itself when handling the event.
321             userAction = true;
322             try {
323                 callback.call(null, new Resource(message.resource), message.lineNumber);
324             } finally {
325                 userAction = false;
326             }
327         }
328
329         if (!callback)
330             extensionServer.unregisterHandler(events.OpenResource);
331         else
332             extensionServer.registerHandler(events.OpenResource, callbackWrapper);
333
334         // Only send command if we either removed an existing handler or added handler and had none before.
335         if (hadHandler === !callback)
336             extensionServer.sendRequest({ command: commands.SetOpenResourceHandler, "handlerPresent": !!callback });
337     },
338
339     openResource: function(url, lineNumber, callback)
340     {
341         extensionServer.sendRequest({ command: commands.OpenResource, "url": url, "lineNumber": lineNumber }, callback);
342     },
343
344     get SearchAction()
345     {
346         return apiPrivate.panels.SearchAction;
347     }
348 }
349
350 /**
351  * @constructor
352  */
353 function ExtensionViewImpl(id)
354 {
355     this._id = id;
356
357     /**
358      * @this {EventSinkImpl}
359      */
360     function dispatchShowEvent(message)
361     {
362         var frameIndex = message.arguments[0];
363         if (typeof frameIndex === "number")
364             this._fire(window.parent.frames[frameIndex]);
365         else
366             this._fire();
367     }
368
369     if (id) {
370         this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
371         this.onHidden = new EventSink(events.ViewHidden + id);
372     }
373 }
374
375 /**
376  * @constructor
377  * @extends {ExtensionViewImpl}
378  * @param {string} hostPanelName
379  */
380 function PanelWithSidebarImpl(hostPanelName)
381 {
382     ExtensionViewImpl.call(this, null);
383     this._hostPanelName = hostPanelName;
384     this.onSelectionChanged = new EventSink(events.PanelObjectSelected + hostPanelName);
385 }
386
387 PanelWithSidebarImpl.prototype = {
388     createSidebarPane: function(title, callback)
389     {
390         var id = "extension-sidebar-" + extensionServer.nextObjectId();
391         var request = {
392             command: commands.CreateSidebarPane,
393             panel: this._hostPanelName,
394             id: id,
395             title: title
396         };
397         function callbackWrapper()
398         {
399             callback(new ExtensionSidebarPane(id));
400         }
401         extensionServer.sendRequest(request, callback && callbackWrapper);
402     },
403
404     __proto__: ExtensionViewImpl.prototype
405 }
406
407 function declareInterfaceClass(implConstructor)
408 {
409     return function()
410     {
411         var impl = { __proto__: implConstructor.prototype };
412         implConstructor.apply(impl, arguments);
413         populateInterfaceClass(this, impl);
414     }
415 }
416
417 function defineDeprecatedProperty(object, className, oldName, newName)
418 {
419     var warningGiven = false;
420     function getter()
421     {
422         if (!warningGiven) {
423             console.warn(className + "." + oldName + " is deprecated. Use " + className + "." + newName + " instead");
424             warningGiven = true;
425         }
426         return object[newName];
427     }
428     object.__defineGetter__(oldName, getter);
429 }
430
431 function extractCallbackArgument(args)
432 {
433     var lastArgument = args[args.length - 1];
434     return typeof lastArgument === "function" ? lastArgument : undefined;
435 }
436
437 var AuditCategory = declareInterfaceClass(AuditCategoryImpl);
438 var AuditResult = declareInterfaceClass(AuditResultImpl);
439 var Button = declareInterfaceClass(ButtonImpl);
440 var EventSink = declareInterfaceClass(EventSinkImpl);
441 var ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
442 var ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
443 var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
444 var Request = declareInterfaceClass(RequestImpl);
445 var Resource = declareInterfaceClass(ResourceImpl);
446 var Timeline = declareInterfaceClass(TimelineImpl);
447
448 /**
449  * @constructor
450  * @extends {PanelWithSidebar}
451  */
452 function ElementsPanel()
453 {
454     PanelWithSidebar.call(this, "elements");
455 }
456
457 ElementsPanel.prototype = {
458     __proto__: PanelWithSidebar.prototype
459 }
460
461 /**
462  * @constructor
463  * @extends {PanelWithSidebar}
464  */
465 function SourcesPanel()
466 {
467     PanelWithSidebar.call(this, "sources");
468 }
469
470 SourcesPanel.prototype = {
471     __proto__: PanelWithSidebar.prototype
472 }
473
474 /**
475  * @constructor
476  * @extends {ExtensionViewImpl}
477  */
478 function ExtensionPanelImpl(id)
479 {
480     ExtensionViewImpl.call(this, id);
481     this.onSearch = new EventSink(events.PanelSearch + id);
482 }
483
484 ExtensionPanelImpl.prototype = {
485     /**
486      * @return {!Object}
487      */
488     createStatusBarButton: function(iconPath, tooltipText, disabled)
489     {
490         var id = "button-" + extensionServer.nextObjectId();
491         var request = {
492             command: commands.CreateStatusBarButton,
493             panel: this._id,
494             id: id,
495             icon: iconPath,
496             tooltip: tooltipText,
497             disabled: !!disabled
498         };
499         extensionServer.sendRequest(request);
500         return new Button(id);
501     },
502
503     show: function()
504     {
505         if (!userAction)
506             return;
507
508         var request = {
509             command: commands.ShowPanel,
510             id: this._id
511         };
512         extensionServer.sendRequest(request);
513     },
514
515     __proto__: ExtensionViewImpl.prototype
516 }
517
518 /**
519  * @constructor
520  * @extends {ExtensionViewImpl}
521  */
522 function ExtensionSidebarPaneImpl(id)
523 {
524     ExtensionViewImpl.call(this, id);
525 }
526
527 ExtensionSidebarPaneImpl.prototype = {
528     setHeight: function(height)
529     {
530         extensionServer.sendRequest({ command: commands.SetSidebarHeight, id: this._id, height: height });
531     },
532
533     setExpression: function(expression, rootTitle, evaluateOptions)
534     {
535         var request = {
536             command: commands.SetSidebarContent,
537             id: this._id,
538             expression: expression,
539             rootTitle: rootTitle,
540             evaluateOnPage: true,
541         };
542         if (typeof evaluateOptions === "object")
543             request.evaluateOptions = evaluateOptions;
544         extensionServer.sendRequest(request, extractCallbackArgument(arguments));
545     },
546
547     setObject: function(jsonObject, rootTitle, callback)
548     {
549         extensionServer.sendRequest({ command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle }, callback);
550     },
551
552     setPage: function(page)
553     {
554         extensionServer.sendRequest({ command: commands.SetSidebarPage, id: this._id, page: page });
555     },
556
557     __proto__: ExtensionViewImpl.prototype
558 }
559
560 /**
561  * @constructor
562  */
563 function ButtonImpl(id)
564 {
565     this._id = id;
566     this.onClicked = new EventSink(events.ButtonClicked + id);
567 }
568
569 ButtonImpl.prototype = {
570     update: function(iconPath, tooltipText, disabled)
571     {
572         var request = {
573             command: commands.UpdateButton,
574             id: this._id,
575             icon: iconPath,
576             tooltip: tooltipText,
577             disabled: !!disabled
578         };
579         extensionServer.sendRequest(request);
580     }
581 };
582
583 /**
584  * @constructor
585  */
586 function Audits()
587 {
588 }
589
590 Audits.prototype = {
591     /**
592      * @return {!AuditCategory}
593      */
594     addCategory: function(displayName, resultCount)
595     {
596         var id = "extension-audit-category-" + extensionServer.nextObjectId();
597         if (typeof resultCount !== "undefined")
598             console.warn("Passing resultCount to audits.addCategory() is deprecated. Use AuditResult.updateProgress() instead.");
599         extensionServer.sendRequest({ command: commands.AddAuditCategory, id: id, displayName: displayName, resultCount: resultCount });
600         return new AuditCategory(id);
601     }
602 }
603
604 /**
605  * @constructor
606  */
607 function AuditCategoryImpl(id)
608 {
609     /**
610      * @this {EventSinkImpl}
611      */
612     function dispatchAuditEvent(request)
613     {
614         var auditResult = new AuditResult(request.arguments[0]);
615         try {
616             this._fire(auditResult);
617         } catch (e) {
618             console.error("Uncaught exception in extension audit event handler: " + e);
619             auditResult.done();
620         }
621     }
622     this._id = id;
623     this.onAuditStarted = new EventSink(events.AuditStarted + id, dispatchAuditEvent);
624 }
625
626 /**
627  * @constructor
628  */
629 function AuditResultImpl(id)
630 {
631     this._id = id;
632
633     this.createURL = this._nodeFactory.bind(this, "url");
634     this.createSnippet = this._nodeFactory.bind(this, "snippet");
635     this.createText = this._nodeFactory.bind(this, "text");
636     this.createObject = this._nodeFactory.bind(this, "object");
637     this.createNode = this._nodeFactory.bind(this, "node");
638 }
639
640 AuditResultImpl.prototype = {
641     addResult: function(displayName, description, severity, details)
642     {
643         // shorthand for specifying details directly in addResult().
644         if (details && !(details instanceof AuditResultNode))
645             details = new AuditResultNode(details instanceof Array ? details : [details]);
646
647         var request = {
648             command: commands.AddAuditResult,
649             resultId: this._id,
650             displayName: displayName,
651             description: description,
652             severity: severity,
653             details: details
654         };
655         extensionServer.sendRequest(request);
656     },
657
658     /**
659      * @return {!Object}
660      */
661     createResult: function()
662     {
663         return new AuditResultNode(Array.prototype.slice.call(arguments));
664     },
665
666     updateProgress: function(worked, totalWork)
667     {
668         extensionServer.sendRequest({ command: commands.UpdateAuditProgress, resultId: this._id, progress: worked / totalWork });
669     },
670
671     done: function()
672     {
673         extensionServer.sendRequest({ command: commands.StopAuditCategoryRun, resultId: this._id });
674     },
675
676     /**
677      * @type {!Object.<string, string>}
678      */
679     get Severity()
680     {
681         return apiPrivate.audits.Severity;
682     },
683
684     /**
685      * @return {!{type: string, arguments: !Array.<string|number>}}
686      */
687     createResourceLink: function(url, lineNumber)
688     {
689         return {
690             type: "resourceLink",
691             arguments: [url, lineNumber && lineNumber - 1]
692         };
693     },
694
695     /**
696      * @return {!{type: string, arguments: !Array.<string|number>}}
697      */
698     _nodeFactory: function(type)
699     {
700         return {
701             type: type,
702             arguments: Array.prototype.slice.call(arguments, 1)
703         };
704     }
705 }
706
707 /**
708  * @constructor
709  */
710 function AuditResultNode(contents)
711 {
712     this.contents = contents;
713     this.children = [];
714     this.expanded = false;
715 }
716
717 AuditResultNode.prototype = {
718     /**
719      * @return {!Object}
720      */
721     addChild: function()
722     {
723         var node = new AuditResultNode(Array.prototype.slice.call(arguments));
724         this.children.push(node);
725         return node;
726     }
727 };
728
729 /**
730  * @constructor
731  */
732 function InspectedWindow()
733 {
734     /**
735      * @this {EventSinkImpl}
736      */
737     function dispatchResourceEvent(message)
738     {
739         this._fire(new Resource(message.arguments[0]));
740     }
741
742     /**
743      * @this {EventSinkImpl}
744      */
745     function dispatchResourceContentEvent(message)
746     {
747         this._fire(new Resource(message.arguments[0]), message.arguments[1]);
748     }
749
750     this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
751     this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
752 }
753
754 InspectedWindow.prototype = {
755     reload: function(optionsOrUserAgent)
756     {
757         var options = null;
758         if (typeof optionsOrUserAgent === "object")
759             options = optionsOrUserAgent;
760         else if (typeof optionsOrUserAgent === "string") {
761             options = { userAgent: optionsOrUserAgent };
762             console.warn("Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. " +
763                          "Use inspectedWindow.reload({ userAgent: value}) instead.");
764         }
765         extensionServer.sendRequest({ command: commands.Reload, options: options });
766     },
767
768     /**
769      * @return {?Object}
770      */
771     eval: function(expression, evaluateOptions)
772     {
773         var callback = extractCallbackArgument(arguments);
774         function callbackWrapper(result)
775         {
776             if (result.isError || result.isException)
777                 callback(undefined, result);
778             else
779                 callback(result.value);
780         }
781         var request = {
782             command: commands.EvaluateOnInspectedPage,
783             expression: expression
784         };
785         if (typeof evaluateOptions === "object")
786             request.evaluateOptions = evaluateOptions;
787         extensionServer.sendRequest(request, callback && callbackWrapper);
788         return null;
789     },
790
791     getResources: function(callback)
792     {
793         function wrapResource(resourceData)
794         {
795             return new Resource(resourceData);
796         }
797         function callbackWrapper(resources)
798         {
799             callback(resources.map(wrapResource));
800         }
801         extensionServer.sendRequest({ command: commands.GetPageResources }, callback && callbackWrapper);
802     }
803 }
804
805 /**
806  * @constructor
807  */
808 function ResourceImpl(resourceData)
809 {
810     this._url = resourceData.url
811     this._type = resourceData.type;
812 }
813
814 ResourceImpl.prototype = {
815     get url()
816     {
817         return this._url;
818     },
819
820     get type()
821     {
822         return this._type;
823     },
824
825     getContent: function(callback)
826     {
827         function callbackWrapper(response)
828         {
829             callback(response.content, response.encoding);
830         }
831
832         extensionServer.sendRequest({ command: commands.GetResourceContent, url: this._url }, callback && callbackWrapper);
833     },
834
835     setContent: function(content, commit, callback)
836     {
837         extensionServer.sendRequest({ command: commands.SetResourceContent, url: this._url, content: content, commit: commit }, callback);
838     }
839 }
840
841 /**
842  * @constructor
843  */
844 function TimelineImpl()
845 {
846     this.onEventRecorded = new EventSink(events.TimelineEventRecorded);
847 }
848
849 var keyboardEventRequestQueue = [];
850 var forwardTimer = null;
851
852 function forwardKeyboardEvent(event)
853 {
854     const Esc = "U+001B";
855     // We only care about global hotkeys, not about random text
856     if (!event.ctrlKey && !event.altKey && !event.metaKey && !/^F\d+$/.test(event.keyIdentifier) && event.keyIdentifier !== Esc)
857         return;
858     var requestPayload = {
859         eventType: event.type,
860         ctrlKey: event.ctrlKey,
861         altKey: event.altKey,
862         metaKey: event.metaKey,
863         keyIdentifier: event.keyIdentifier,
864         location: event.location,
865         keyCode: event.keyCode
866     };
867     keyboardEventRequestQueue.push(requestPayload);
868     if (!forwardTimer)
869         forwardTimer = setTimeout(forwardEventQueue, 0);
870 }
871
872 function forwardEventQueue()
873 {
874     forwardTimer = null;
875     var request = {
876         command: commands.ForwardKeyboardEvent,
877         entries: keyboardEventRequestQueue
878     };
879     extensionServer.sendRequest(request);
880     keyboardEventRequestQueue = [];
881 }
882
883 document.addEventListener("keydown", forwardKeyboardEvent, false);
884 document.addEventListener("keypress", forwardKeyboardEvent, false);
885
886 /**
887  * @constructor
888  */
889 function ExtensionServerClient()
890 {
891     this._callbacks = {};
892     this._handlers = {};
893     this._lastRequestId = 0;
894     this._lastObjectId = 0;
895
896     this.registerHandler("callback", this._onCallback.bind(this));
897
898     var channel = new MessageChannel();
899     this._port = channel.port1;
900     this._port.addEventListener("message", this._onMessage.bind(this), false);
901     this._port.start();
902
903     window.parent.postMessage("registerExtension", [ channel.port2 ], "*");
904 }
905
906 ExtensionServerClient.prototype = {
907     /**
908      * @param {!Object} message
909      * @param {function()=} callback
910      */
911     sendRequest: function(message, callback)
912     {
913         if (typeof callback === "function")
914             message.requestId = this._registerCallback(callback);
915         this._port.postMessage(message);
916     },
917
918     /**
919      * @return {boolean}
920      */
921     hasHandler: function(command)
922     {
923         return !!this._handlers[command];
924     },
925
926     registerHandler: function(command, handler)
927     {
928         this._handlers[command] = handler;
929     },
930
931     unregisterHandler: function(command)
932     {
933         delete this._handlers[command];
934     },
935
936     /**
937      * @return {string}
938      */
939     nextObjectId: function()
940     {
941         return injectedScriptId + "_" + ++this._lastObjectId;
942     },
943
944     _registerCallback: function(callback)
945     {
946         var id = ++this._lastRequestId;
947         this._callbacks[id] = callback;
948         return id;
949     },
950
951     _onCallback: function(request)
952     {
953         if (request.requestId in this._callbacks) {
954             var callback = this._callbacks[request.requestId];
955             delete this._callbacks[request.requestId];
956             callback(request.result);
957         }
958     },
959
960     _onMessage: function(event)
961     {
962         var request = event.data;
963         var handler = this._handlers[request.command];
964         if (handler)
965             handler.call(this, request);
966     }
967 }
968
969 function populateInterfaceClass(interfaze, implementation)
970 {
971     for (var member in implementation) {
972         if (member.charAt(0) === "_")
973             continue;
974         var descriptor = null;
975         // Traverse prototype chain until we find the owner.
976         for (var owner = implementation; owner && !descriptor; owner = owner.__proto__)
977             descriptor = Object.getOwnPropertyDescriptor(owner, member);
978         if (!descriptor)
979             continue;
980         if (typeof descriptor.value === "function")
981             interfaze[member] = descriptor.value.bind(implementation);
982         else if (typeof descriptor.get === "function")
983             interfaze.__defineGetter__(member, descriptor.get.bind(implementation));
984         else
985             Object.defineProperty(interfaze, member, descriptor);
986     }
987 }
988
989 // extensionServer is a closure variable defined by the glue below -- make sure we fail if it's not there.
990 if (!extensionServer)
991     extensionServer = new ExtensionServerClient();
992
993 return new InspectorExtensionAPI();
994 }
995
996 /**
997  * @suppress {checkVars, checkTypes}
998  */
999 function platformExtensionAPI(coreAPI)
1000 {
1001     function getTabId()
1002     {
1003         return tabId;
1004     }
1005     chrome = window.chrome || {};
1006     // Override chrome.devtools as a workaround for a error-throwing getter being exposed
1007     // in extension pages loaded into a non-extension process (only happens for remote client
1008     // extensions)
1009     var devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, "devtools");
1010     if (!devtools_descriptor || devtools_descriptor.get)
1011         Object.defineProperty(chrome, "devtools", { value: {}, enumerable: true });
1012     // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.inspectedWindow.
1013     chrome.devtools.inspectedWindow = {};
1014     chrome.devtools.inspectedWindow.__defineGetter__("tabId", getTabId);
1015     chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow;
1016     chrome.devtools.network = coreAPI.network;
1017     chrome.devtools.panels = coreAPI.panels;
1018
1019     // default to expose experimental APIs for now.
1020     if (extensionInfo.exposeExperimentalAPIs !== false) {
1021         chrome.experimental = chrome.experimental || {};
1022         chrome.experimental.devtools = chrome.experimental.devtools || {};
1023
1024         var properties = Object.getOwnPropertyNames(coreAPI);
1025         for (var i = 0; i < properties.length; ++i) {
1026             var descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties[i]);
1027             Object.defineProperty(chrome.experimental.devtools, properties[i], descriptor);
1028         }
1029         chrome.experimental.devtools.inspectedWindow = chrome.devtools.inspectedWindow;
1030     }
1031     if (extensionInfo.exposeWebInspectorNamespace)
1032         window.webInspector = coreAPI;
1033 }
1034
1035 /**
1036  * @param {!ExtensionDescriptor} extensionInfo
1037  * @return {string}
1038  */
1039 function buildPlatformExtensionAPI(extensionInfo)
1040 {
1041     return "var extensionInfo = " + JSON.stringify(extensionInfo) + ";" +
1042        "var tabId = " + WebInspector._inspectedTabId + ";" +
1043        platformExtensionAPI.toString();
1044 }
1045
1046 /**
1047  * @param {!ExtensionDescriptor} extensionInfo
1048  * @return {string}
1049  */
1050 function buildExtensionAPIInjectedScript(extensionInfo)
1051 {
1052     return "(function(injectedScriptId){ " +
1053         "var extensionServer;" +
1054         defineCommonExtensionSymbols.toString() + ";" +
1055         injectedExtensionAPI.toString() + ";" +
1056         buildPlatformExtensionAPI(extensionInfo) + ";" +
1057         "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" +
1058         "return {};" +
1059         "})";
1060 }
1061
1062         var tabId;
1063         var extensionInfo = {};
1064         var extensionServer;
1065         platformExtensionAPI(injectedExtensionAPI("remote-" + window.parent.frames.length));
1066     })();