1/*
2 * Copyright (C) 2012 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
31function defineCommonExtensionSymbols(apiPrivate)
32{
33    if (!apiPrivate.audits)
34        apiPrivate.audits = {};
35    apiPrivate.audits.Severity = {
36        Info: "info",
37        Warning: "warning",
38        Severe: "severe"
39    };
40
41    if (!apiPrivate.console)
42        apiPrivate.console = {};
43    apiPrivate.console.Severity = {
44        Debug: "debug",
45        Log: "log",
46        Warning: "warning",
47        Error: "error"
48    };
49
50    if (!apiPrivate.panels)
51        apiPrivate.panels = {};
52    apiPrivate.panels.SearchAction = {
53        CancelSearch: "cancelSearch",
54        PerformSearch: "performSearch",
55        NextSearchResult: "nextSearchResult",
56        PreviousSearchResult: "previousSearchResult"
57    };
58
59    apiPrivate.Events = {
60        AuditStarted: "audit-started-",
61        ButtonClicked: "button-clicked-",
62        ConsoleMessageAdded: "console-message-added",
63        ElementsPanelObjectSelected: "panel-objectSelected-elements",
64        NetworkRequestFinished: "network-request-finished",
65        OpenResource: "open-resource",
66        PanelSearch: "panel-search-",
67        Reload: "Reload",
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        CreatePanel: "createPanel",
81        CreateSidebarPane: "createSidebarPane",
82        CreateStatusBarButton: "createStatusBarButton",
83        EvaluateOnInspectedPage: "evaluateOnInspectedPage",
84        GetConsoleMessages: "getConsoleMessages",
85        GetHAR: "getHAR",
86        GetPageResources: "getPageResources",
87        GetRequestContent: "getRequestContent",
88        GetResourceContent: "getResourceContent",
89        Subscribe: "subscribe",
90        SetOpenResourceHandler: "setOpenResourceHandler",
91        SetResourceContent: "setResourceContent",
92        SetSidebarContent: "setSidebarContent",
93        SetSidebarHeight: "setSidebarHeight",
94        SetSidebarPage: "setSidebarPage",
95        ShowPanel: "showPanel",
96        StopAuditCategoryRun: "stopAuditCategoryRun",
97        Unsubscribe: "unsubscribe",
98        UpdateAuditProgress: "updateAuditProgress",
99        UpdateButton: "updateButton",
100        InspectedURLChanged: "inspectedURLChanged"
101    };
102}
103
104function injectedExtensionAPI(injectedScriptId)
105{
106
107var apiPrivate = {};
108
109defineCommonExtensionSymbols(apiPrivate);
110
111var commands = apiPrivate.Commands;
112var events = apiPrivate.Events;
113var userAction = false;
114
115// Here and below, all constructors are private to API implementation.
116// For a public type Foo, if internal fields are present, these are on
117// a private FooImpl type, an instance of FooImpl is used in a closure
118// by Foo consutrctor to re-bind publicly exported members to an instance
119// of Foo.
120
121/**
122 * @constructor
123 */
124function EventSinkImpl(type, customDispatch)
125{
126    this._type = type;
127    this._listeners = [];
128    this._customDispatch = customDispatch;
129}
130
131EventSinkImpl.prototype = {
132    addListener: function(callback)
133    {
134        if (typeof callback !== "function")
135            throw "addListener: callback is not a function";
136        if (this._listeners.length === 0)
137            extensionServer.sendRequest({ command: commands.Subscribe, type: this._type });
138        this._listeners.push(callback);
139        extensionServer.registerHandler("notify-" + this._type, this._dispatch.bind(this));
140    },
141
142    removeListener: function(callback)
143    {
144        var listeners = this._listeners;
145
146        for (var i = 0; i < listeners.length; ++i) {
147            if (listeners[i] === callback) {
148                listeners.splice(i, 1);
149                break;
150            }
151        }
152        if (this._listeners.length === 0)
153            extensionServer.sendRequest({ command: commands.Unsubscribe, type: this._type });
154    },
155
156    _fire: function()
157    {
158        var listeners = this._listeners.slice();
159        for (var i = 0; i < listeners.length; ++i)
160            listeners[i].apply(null, arguments);
161    },
162
163    _dispatch: function(request)
164    {
165         if (this._customDispatch)
166             this._customDispatch.call(this, request);
167         else
168             this._fire.apply(this, request.arguments);
169    }
170}
171
172/**
173 * @constructor
174 */
175function InspectorExtensionAPI()
176{
177    this.audits = new Audits();
178    this.inspectedWindow = new InspectedWindow();
179    this.panels = new Panels();
180    this.network = new Network();
181    defineDeprecatedProperty(this, "webInspector", "resources", "network");
182    this.timeline = new Timeline();
183    this.console = new ConsoleAPI();
184}
185
186/**
187 * @constructor
188 */
189InspectorExtensionAPI.prototype = {
190    log: function(message)
191    {
192        extensionServer.sendRequest({ command: commands.Log, message: message });
193    }
194}
195
196/**
197 * @constructor
198 */
199function ConsoleAPI()
200{
201    this.onMessageAdded = new EventSink(events.ConsoleMessageAdded);
202}
203
204ConsoleAPI.prototype = {
205    getMessages: function(callback)
206    {
207        extensionServer.sendRequest({ command: commands.GetConsoleMessages }, callback);
208    },
209
210    addMessage: function(severity, text, url, line)
211    {
212        extensionServer.sendRequest({ command: commands.AddConsoleMessage, severity: severity, text: text, url: url, line: line });
213    },
214
215    get Severity()
216    {
217        return apiPrivate.console.Severity;
218    }
219}
220
221/**
222 * @constructor
223 */
224function Network()
225{
226    function dispatchRequestEvent(message)
227    {
228        var request = message.arguments[1];
229        request.__proto__ = new Request(message.arguments[0]);
230        this._fire(request);
231    }
232    this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
233    defineDeprecatedProperty(this, "network", "onFinished", "onRequestFinished");
234    this.onNavigated = new EventSink(events.InspectedURLChanged);
235}
236
237Network.prototype = {
238    getHAR: function(callback)
239    {
240        function callbackWrapper(result)
241        {
242            var entries = (result && result.entries) || [];
243            for (var i = 0; i < entries.length; ++i) {
244                entries[i].__proto__ = new Request(entries[i]._requestId);
245                delete entries[i]._requestId;
246            }
247            callback(result);
248        }
249        return extensionServer.sendRequest({ command: commands.GetHAR }, callback && callbackWrapper);
250    },
251
252    addRequestHeaders: function(headers)
253    {
254        return extensionServer.sendRequest({ command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname });
255    }
256}
257
258/**
259 * @constructor
260 */
261function RequestImpl(id)
262{
263    this._id = id;
264}
265
266RequestImpl.prototype = {
267    getContent: function(callback)
268    {
269        function callbackWrapper(response)
270        {
271            callback(response.content, response.encoding);
272        }
273        extensionServer.sendRequest({ command: commands.GetRequestContent, id: this._id }, callback && callbackWrapper);
274    }
275}
276
277/**
278 * @constructor
279 */
280function Panels()
281{
282    var panels = {
283        elements: new ElementsPanel()
284    };
285
286    function panelGetter(name)
287    {
288        return panels[name];
289    }
290    for (var panel in panels)
291        this.__defineGetter__(panel, panelGetter.bind(null, panel));
292}
293
294Panels.prototype = {
295    create: function(title, icon, page, callback)
296    {
297        var id = "extension-panel-" + extensionServer.nextObjectId();
298        var request = {
299            command: commands.CreatePanel,
300            id: id,
301            title: title,
302            icon: icon,
303            page: page
304        };
305        extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
306    },
307
308    setOpenResourceHandler: function(callback)
309    {
310        var hadHandler = extensionServer.hasHandler(events.OpenResource);
311
312        if (!callback)
313            extensionServer.unregisterHandler(events.OpenResource);
314        else {
315            function callbackWrapper(message)
316            {
317                // Allow the panel to show itself when handling the event.
318                userAction = true;
319                try {
320                    callback.call(null, new Resource(message.resource), message.lineNumber);
321                } finally {
322                    userAction = false;
323                }
324            }
325            extensionServer.registerHandler(events.OpenResource, callbackWrapper);
326        }
327        // Only send command if we either removed an existing handler or added handler and had none before.
328        if (hadHandler === !callback)
329            extensionServer.sendRequest({ command: commands.SetOpenResourceHandler, "handlerPresent": !!callback });
330    },
331
332    get SearchAction()
333    {
334        return apiPrivate.panels.SearchAction;
335    }
336}
337
338/**
339 * @constructor
340 */
341function ExtensionViewImpl(id)
342{
343    this._id = id;
344
345    function dispatchShowEvent(message)
346    {
347        var frameIndex = message.arguments[0];
348        this._fire(window.parent.frames[frameIndex]);
349    }
350    this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
351    this.onHidden = new EventSink(events.ViewHidden + id);
352}
353
354/**
355 * @constructor
356 */
357function PanelWithSidebarImpl(id)
358{
359    this._id = id;
360}
361
362PanelWithSidebarImpl.prototype = {
363    createSidebarPane: function(title, callback)
364    {
365        var id = "extension-sidebar-" + extensionServer.nextObjectId();
366        var request = {
367            command: commands.CreateSidebarPane,
368            panel: this._id,
369            id: id,
370            title: title
371        };
372        function callbackWrapper()
373        {
374            callback(new ExtensionSidebarPane(id));
375        }
376        extensionServer.sendRequest(request, callback && callbackWrapper);
377    },
378
379    __proto__: ExtensionViewImpl.prototype
380}
381
382/**
383 * @constructor
384 * @extends {PanelWithSidebar}
385 */
386function ElementsPanel()
387{
388    var id = "elements";
389    PanelWithSidebar.call(this, id);
390    this.onSelectionChanged = new EventSink(events.ElementsPanelObjectSelected);
391}
392
393/**
394 * @constructor
395 * @extends {ExtensionViewImpl}
396 */
397function ExtensionPanelImpl(id)
398{
399    ExtensionViewImpl.call(this, id);
400    this.onSearch = new EventSink(events.PanelSearch + id);
401}
402
403ExtensionPanelImpl.prototype = {
404    createStatusBarButton: function(iconPath, tooltipText, disabled)
405    {
406        var id = "button-" + extensionServer.nextObjectId();
407        var request = {
408            command: commands.CreateStatusBarButton,
409            panel: this._id,
410            id: id,
411            icon: iconPath,
412            tooltip: tooltipText,
413            disabled: !!disabled
414        };
415        extensionServer.sendRequest(request);
416        return new Button(id);
417    },
418
419    show: function()
420    {
421        if (!userAction)
422            return;
423
424        var request = {
425            command: commands.ShowPanel,
426            id: this._id
427        };
428        extensionServer.sendRequest(request);
429    },
430
431    __proto__: ExtensionViewImpl.prototype
432}
433
434/**
435 * @constructor
436 * @extends {ExtensionViewImpl}
437 */
438function ExtensionSidebarPaneImpl(id)
439{
440    ExtensionViewImpl.call(this, id);
441}
442
443ExtensionSidebarPaneImpl.prototype = {
444    setHeight: function(height)
445    {
446        extensionServer.sendRequest({ command: commands.SetSidebarHeight, id: this._id, height: height });
447    },
448
449    setExpression: function(expression, rootTitle, evaluateOptions)
450    {
451        var request = {
452            command: commands.SetSidebarContent,
453            id: this._id,
454            expression: expression,
455            rootTitle: rootTitle,
456            evaluateOnPage: true,
457        };
458        if (typeof evaluateOptions === "object")
459            request.evaluateOptions = evaluateOptions;
460        extensionServer.sendRequest(request, extractCallbackArgument(arguments));
461    },
462
463    setObject: function(jsonObject, rootTitle, callback)
464    {
465        extensionServer.sendRequest({ command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle }, callback);
466    },
467
468    setPage: function(page)
469    {
470        extensionServer.sendRequest({ command: commands.SetSidebarPage, id: this._id, page: page });
471    }
472}
473
474/**
475 * @constructor
476 */
477function ButtonImpl(id)
478{
479    this._id = id;
480    this.onClicked = new EventSink(events.ButtonClicked + id);
481}
482
483ButtonImpl.prototype = {
484    update: function(iconPath, tooltipText, disabled)
485    {
486        var request = {
487            command: commands.UpdateButton,
488            id: this._id,
489            icon: iconPath,
490            tooltip: tooltipText,
491            disabled: !!disabled
492        };
493        extensionServer.sendRequest(request);
494    }
495};
496
497/**
498 * @constructor
499 */
500function Audits()
501{
502}
503
504Audits.prototype = {
505    addCategory: function(displayName, resultCount)
506    {
507        var id = "extension-audit-category-" + extensionServer.nextObjectId();
508        if (typeof resultCount !== "undefined")
509            console.warn("Passing resultCount to audits.addCategory() is deprecated. Use AuditResult.updateProgress() instead.");
510        extensionServer.sendRequest({ command: commands.AddAuditCategory, id: id, displayName: displayName, resultCount: resultCount });
511        return new AuditCategory(id);
512    }
513}
514
515/**
516 * @constructor
517 */
518function AuditCategoryImpl(id)
519{
520    function dispatchAuditEvent(request)
521    {
522        var auditResult = new AuditResult(request.arguments[0]);
523        try {
524            this._fire(auditResult);
525        } catch (e) {
526            console.error("Uncaught exception in extension audit event handler: " + e);
527            auditResult.done();
528        }
529    }
530    this._id = id;
531    this.onAuditStarted = new EventSink(events.AuditStarted + id, dispatchAuditEvent);
532}
533
534/**
535 * @constructor
536 */
537function AuditResultImpl(id)
538{
539    this._id = id;
540
541    this.createURL = this._nodeFactory.bind(null, "url");
542    this.createSnippet = this._nodeFactory.bind(null, "snippet");
543    this.createText = this._nodeFactory.bind(null, "text");
544    this.createObject = this._nodeFactory.bind(null, "object");
545    this.createNode = this._nodeFactory.bind(null, "node");
546}
547
548AuditResultImpl.prototype = {
549    addResult: function(displayName, description, severity, details)
550    {
551        // shorthand for specifying details directly in addResult().
552        if (details && !(details instanceof AuditResultNode))
553            details = new AuditResultNode(details instanceof Array ? details : [details]);
554
555        var request = {
556            command: commands.AddAuditResult,
557            resultId: this._id,
558            displayName: displayName,
559            description: description,
560            severity: severity,
561            details: details
562        };
563        extensionServer.sendRequest(request);
564    },
565
566    createResult: function()
567    {
568        return new AuditResultNode(Array.prototype.slice.call(arguments));
569    },
570
571    updateProgress: function(worked, totalWork)
572    {
573        extensionServer.sendRequest({ command: commands.UpdateAuditProgress, resultId: this._id, progress: worked / totalWork });
574    },
575
576    done: function()
577    {
578        extensionServer.sendRequest({ command: commands.StopAuditCategoryRun, resultId: this._id });
579    },
580
581    get Severity()
582    {
583        return apiPrivate.audits.Severity;
584    },
585
586    createResourceLink: function(url, lineNumber)
587    {
588        return {
589            type: "resourceLink",
590            arguments: [url, lineNumber && lineNumber - 1]
591        };
592    },
593
594    _nodeFactory: function(type)
595    {
596        return {
597            type: type,
598            arguments: Array.prototype.slice.call(arguments, 1)
599        };
600    }
601}
602
603/**
604 * @constructor
605 */
606function AuditResultNode(contents)
607{
608    this.contents = contents;
609    this.children = [];
610    this.expanded = false;
611}
612
613AuditResultNode.prototype = {
614    addChild: function()
615    {
616        var node = new AuditResultNode(Array.prototype.slice.call(arguments));
617        this.children.push(node);
618        return node;
619    }
620};
621
622/**
623 * @constructor
624 */
625function InspectedWindow()
626{
627    function dispatchResourceEvent(message)
628    {
629        this._fire(new Resource(message.arguments[0]));
630    }
631    function dispatchResourceContentEvent(message)
632    {
633        this._fire(new Resource(message.arguments[0]), message.arguments[1]);
634    }
635    this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
636    this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
637}
638
639InspectedWindow.prototype = {
640    reload: function(optionsOrUserAgent)
641    {
642        var options = null;
643        if (typeof optionsOrUserAgent === "object")
644            options = optionsOrUserAgent;
645        else if (typeof optionsOrUserAgent === "string") {
646            options = { userAgent: optionsOrUserAgent };
647            console.warn("Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. " +
648                         "Use inspectedWindow.reload({ userAgent: value}) instead.");
649        }
650        return extensionServer.sendRequest({ command: commands.Reload, options: options });
651    },
652
653    eval: function(expression, evaluateOptions)
654    {
655        var callback = extractCallbackArgument(arguments);
656        function callbackWrapper(result)
657        {
658            if (result.isError || result.isException)
659                callback(undefined, result);
660            else
661                callback(result.value);
662        }
663        var request = {
664            command: commands.EvaluateOnInspectedPage,
665            expression: expression
666        };
667        if (typeof evaluateOptions === "object")
668            request.evaluateOptions = evaluateOptions;
669        return extensionServer.sendRequest(request, callback && callbackWrapper);
670    },
671
672    getResources: function(callback)
673    {
674        function wrapResource(resourceData)
675        {
676            return new Resource(resourceData);
677        }
678        function callbackWrapper(resources)
679        {
680            callback(resources.map(wrapResource));
681        }
682        return extensionServer.sendRequest({ command: commands.GetPageResources }, callback && callbackWrapper);
683    }
684}
685
686/**
687 * @constructor
688 */
689function ResourceImpl(resourceData)
690{
691    this._url = resourceData.url
692    this._type = resourceData.type;
693}
694
695ResourceImpl.prototype = {
696    get url()
697    {
698        return this._url;
699    },
700
701    get type()
702    {
703        return this._type;
704    },
705
706    getContent: function(callback)
707    {
708        function callbackWrapper(response)
709        {
710            callback(response.content, response.encoding);
711        }
712
713        return extensionServer.sendRequest({ command: commands.GetResourceContent, url: this._url }, callback && callbackWrapper);
714    },
715
716    setContent: function(content, commit, callback)
717    {
718        return extensionServer.sendRequest({ command: commands.SetResourceContent, url: this._url, content: content, commit: commit }, callback);
719    }
720}
721
722/**
723 * @constructor
724 */
725function TimelineImpl()
726{
727    this.onEventRecorded = new EventSink(events.TimelineEventRecorded);
728}
729
730/**
731 * @constructor
732 */
733function ExtensionServerClient()
734{
735    this._callbacks = {};
736    this._handlers = {};
737    this._lastRequestId = 0;
738    this._lastObjectId = 0;
739
740    this.registerHandler("callback", this._onCallback.bind(this));
741
742    var channel = new MessageChannel();
743    this._port = channel.port1;
744    this._port.addEventListener("message", this._onMessage.bind(this), false);
745    this._port.start();
746
747    window.parent.postMessage("registerExtension", [ channel.port2 ], "*");
748}
749
750ExtensionServerClient.prototype = {
751    /**
752     * @param {function()=} callback
753     */
754    sendRequest: function(message, callback)
755    {
756        if (typeof callback === "function")
757            message.requestId = this._registerCallback(callback);
758        return this._port.postMessage(message);
759    },
760
761    hasHandler: function(command)
762    {
763        return !!this._handlers[command];
764    },
765
766    registerHandler: function(command, handler)
767    {
768        this._handlers[command] = handler;
769    },
770
771    unregisterHandler: function(command)
772    {
773        delete this._handlers[command];
774    },
775
776    nextObjectId: function()
777    {
778        return injectedScriptId + "_" + ++this._lastObjectId;
779    },
780
781    _registerCallback: function(callback)
782    {
783        var id = ++this._lastRequestId;
784        this._callbacks[id] = callback;
785        return id;
786    },
787
788    _onCallback: function(request)
789    {
790        if (request.requestId in this._callbacks) {
791            var callback = this._callbacks[request.requestId];
792            delete this._callbacks[request.requestId];
793            callback(request.result);
794        }
795    },
796
797    _onMessage: function(event)
798    {
799        var request = event.data;
800        var handler = this._handlers[request.command];
801        if (handler)
802            handler.call(this, request);
803    }
804}
805
806function populateInterfaceClass(interface, implementation)
807{
808    for (var member in implementation) {
809        if (member.charAt(0) === "_")
810            continue;
811        var descriptor = null;
812        // Traverse prototype chain until we find the owner.
813        for (var owner = implementation; owner && !descriptor; owner = owner.__proto__)
814            descriptor = Object.getOwnPropertyDescriptor(owner, member);
815        if (!descriptor)
816            continue;
817        if (typeof descriptor.value === "function")
818            interface[member] = descriptor.value.bind(implementation);
819        else if (typeof descriptor.get === "function")
820            interface.__defineGetter__(member, descriptor.get.bind(implementation));
821        else
822            Object.defineProperty(interface, member, descriptor);
823    }
824}
825
826function declareInterfaceClass(implConstructor)
827{
828    return function()
829    {
830        var impl = { __proto__: implConstructor.prototype };
831        implConstructor.apply(impl, arguments);
832        populateInterfaceClass(this, impl);
833    }
834}
835
836function defineDeprecatedProperty(object, className, oldName, newName)
837{
838    var warningGiven = false;
839    function getter()
840    {
841        if (!warningGiven) {
842            console.warn(className + "." + oldName + " is deprecated. Use " + className + "." + newName + " instead");
843            warningGiven = true;
844        }
845        return object[newName];
846    }
847    object.__defineGetter__(oldName, getter);
848}
849
850function extractCallbackArgument(args)
851{
852    var lastArgument = args[args.length - 1];
853    return typeof lastArgument === "function" ? lastArgument : undefined;
854}
855
856var AuditCategory = declareInterfaceClass(AuditCategoryImpl);
857var AuditResult = declareInterfaceClass(AuditResultImpl);
858var Button = declareInterfaceClass(ButtonImpl);
859var EventSink = declareInterfaceClass(EventSinkImpl);
860var ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
861var ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
862var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
863var Request = declareInterfaceClass(RequestImpl);
864var Resource = declareInterfaceClass(ResourceImpl);
865var Timeline = declareInterfaceClass(TimelineImpl);
866
867// extensionServer is a closure variable defined by the glue below -- make sure we fail if it's not there.
868if (!extensionServer)
869    extensionServer = new ExtensionServerClient();
870
871return new InspectorExtensionAPI();
872}
873
874// Default implementation; platforms will override.
875function buildPlatformExtensionAPI(extensionInfo)
876{
877    function platformExtensionAPI(coreAPI)
878    {
879        window.webInspector = coreAPI;
880    }
881    return platformExtensionAPI.toString();
882}
883
884
885function buildExtensionAPIInjectedScript(extensionInfo)
886{
887    return "(function(injectedScriptId){ " +
888        "var extensionServer;" +
889        defineCommonExtensionSymbols.toString() + ";" +
890        injectedExtensionAPI.toString() + ";" +
891        buildPlatformExtensionAPI(extensionInfo) + ";" +
892        "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" +
893        "return {};" +
894        "})";
895}
896