2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
31 function defineCommonExtensionSymbols(apiPrivate)
33 if (!apiPrivate.audits)
34 apiPrivate.audits = {};
35 apiPrivate.audits.Severity = {
41 if (!apiPrivate.console)
42 apiPrivate.console = {};
43 apiPrivate.console.Severity = {
50 if (!apiPrivate.panels)
51 apiPrivate.panels = {};
52 apiPrivate.panels.SearchAction = {
53 CancelSearch: "cancelSearch",
54 PerformSearch: "performSearch",
55 NextSearchResult: "nextSearchResult",
56 PreviousSearchResult: "previousSearchResult"
60 AuditStarted: "audit-started-",
61 ButtonClicked: "button-clicked-",
62 ConsoleMessageAdded: "console-message-added",
63 PanelObjectSelected: "panel-objectSelected-",
64 NetworkRequestFinished: "network-request-finished",
65 OpenResource: "open-resource",
66 PanelSearch: "panel-search-",
67 ResourceAdded: "resource-added",
68 ResourceContentCommitted: "resource-content-committed",
69 ViewShown: "view-shown-",
70 ViewHidden: "view-hidden-"
73 apiPrivate.Commands = {
74 AddAuditCategory: "addAuditCategory",
75 AddAuditResult: "addAuditResult",
76 AddConsoleMessage: "addConsoleMessage",
77 AddRequestHeaders: "addRequestHeaders",
78 ApplyStyleSheet: "applyStyleSheet",
79 CreatePanel: "createPanel",
80 CreateSidebarPane: "createSidebarPane",
81 CreateStatusBarButton: "createStatusBarButton",
82 EvaluateOnInspectedPage: "evaluateOnInspectedPage",
83 ForwardKeyboardEvent: "_forwardKeyboardEvent",
84 GetConsoleMessages: "getConsoleMessages",
86 GetPageResources: "getPageResources",
87 GetRequestContent: "getRequestContent",
88 GetResourceContent: "getResourceContent",
89 InspectedURLChanged: "inspectedURLChanged",
90 OpenResource: "openResource",
92 Subscribe: "subscribe",
93 SetOpenResourceHandler: "setOpenResourceHandler",
94 SetResourceContent: "setResourceContent",
95 SetSidebarContent: "setSidebarContent",
96 SetSidebarHeight: "setSidebarHeight",
97 SetSidebarPage: "setSidebarPage",
98 ShowPanel: "showPanel",
99 StopAuditCategoryRun: "stopAuditCategoryRun",
100 Unsubscribe: "unsubscribe",
101 UpdateAuditProgress: "updateAuditProgress",
102 UpdateButton: "updateButton"
107 * @param {number} injectedScriptId
109 * @suppressGlobalPropertiesCheck
111 function injectedExtensionAPI(injectedScriptId)
116 defineCommonExtensionSymbols(apiPrivate);
118 var commands = apiPrivate.Commands;
119 var events = apiPrivate.Events;
120 var userAction = false;
122 // Here and below, all constructors are private to API implementation.
123 // For a public type Foo, if internal fields are present, these are on
124 // a private FooImpl type, an instance of FooImpl is used in a closure
125 // by Foo consutrctor to re-bind publicly exported members to an instance
131 function EventSinkImpl(type, customDispatch)
134 this._listeners = [];
135 this._customDispatch = customDispatch;
138 EventSinkImpl.prototype = {
139 addListener: function(callback)
141 if (typeof callback !== "function")
142 throw "addListener: callback is not a function";
143 if (this._listeners.length === 0)
144 extensionServer.sendRequest({ command: commands.Subscribe, type: this._type });
145 this._listeners.push(callback);
146 extensionServer.registerHandler("notify-" + this._type, this._dispatch.bind(this));
149 removeListener: function(callback)
151 var listeners = this._listeners;
153 for (var i = 0; i < listeners.length; ++i) {
154 if (listeners[i] === callback) {
155 listeners.splice(i, 1);
159 if (this._listeners.length === 0)
160 extensionServer.sendRequest({ command: commands.Unsubscribe, type: this._type });
164 * @param {...} vararg
166 _fire: function(vararg)
168 var listeners = this._listeners.slice();
169 for (var i = 0; i < listeners.length; ++i)
170 listeners[i].apply(null, arguments);
173 _dispatch: function(request)
175 if (this._customDispatch)
176 this._customDispatch.call(this, request);
178 this._fire.apply(this, request.arguments);
185 function InspectorExtensionAPI()
187 this.audits = new Audits();
188 this.inspectedWindow = new InspectedWindow();
189 this.panels = new Panels();
190 this.network = new Network();
191 defineDeprecatedProperty(this, "webInspector", "resources", "network");
192 this.console = new ConsoleAPI();
198 function ConsoleAPI()
200 this.onMessageAdded = new EventSink(events.ConsoleMessageAdded);
203 ConsoleAPI.prototype = {
204 getMessages: function(callback)
206 extensionServer.sendRequest({ command: commands.GetConsoleMessages }, callback);
209 addMessage: function(severity, text, url, line)
211 extensionServer.sendRequest({ command: commands.AddConsoleMessage, severity: severity, text: text, url: url, line: line });
216 return apiPrivate.console.Severity;
226 * @this {EventSinkImpl}
228 function dispatchRequestEvent(message)
230 var request = message.arguments[1];
231 request.__proto__ = new Request(message.arguments[0]);
234 this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
235 defineDeprecatedProperty(this, "network", "onFinished", "onRequestFinished");
236 this.onNavigated = new EventSink(events.InspectedURLChanged);
239 Network.prototype = {
240 getHAR: function(callback)
242 function callbackWrapper(result)
244 var entries = (result && result.entries) || [];
245 for (var i = 0; i < entries.length; ++i) {
246 entries[i].__proto__ = new Request(entries[i]._requestId);
247 delete entries[i]._requestId;
251 extensionServer.sendRequest({ command: commands.GetHAR }, callback && callbackWrapper);
254 addRequestHeaders: function(headers)
256 extensionServer.sendRequest({ command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname });
263 function RequestImpl(id)
268 RequestImpl.prototype = {
269 getContent: function(callback)
271 function callbackWrapper(response)
273 callback(response.content, response.encoding);
275 extensionServer.sendRequest({ command: commands.GetRequestContent, id: this._id }, callback && callbackWrapper);
285 elements: new ElementsPanel(),
286 sources: new SourcesPanel(),
289 function panelGetter(name)
293 for (var panel in panels)
294 this.__defineGetter__(panel, panelGetter.bind(null, panel));
295 this.applyStyleSheet = function(styleSheet) { extensionServer.sendRequest({ command: commands.ApplyStyleSheet, styleSheet: styleSheet }); };
299 create: function(title, icon, page, callback)
301 var id = "extension-panel-" + extensionServer.nextObjectId();
303 command: commands.CreatePanel,
309 extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
312 setOpenResourceHandler: function(callback)
314 var hadHandler = extensionServer.hasHandler(events.OpenResource);
316 function callbackWrapper(message)
318 // Allow the panel to show itself when handling the event.
321 callback.call(null, new Resource(message.resource), message.lineNumber);
328 extensionServer.unregisterHandler(events.OpenResource);
330 extensionServer.registerHandler(events.OpenResource, callbackWrapper);
332 // Only send command if we either removed an existing handler or added handler and had none before.
333 if (hadHandler === !callback)
334 extensionServer.sendRequest({ command: commands.SetOpenResourceHandler, "handlerPresent": !!callback });
337 openResource: function(url, lineNumber, callback)
339 extensionServer.sendRequest({ command: commands.OpenResource, "url": url, "lineNumber": lineNumber }, callback);
344 return apiPrivate.panels.SearchAction;
351 function ExtensionViewImpl(id)
356 * @this {EventSinkImpl}
358 function dispatchShowEvent(message)
360 var frameIndex = message.arguments[0];
361 if (typeof frameIndex === "number")
362 this._fire(window.parent.frames[frameIndex]);
368 this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
369 this.onHidden = new EventSink(events.ViewHidden + id);
375 * @extends {ExtensionViewImpl}
376 * @param {string} hostPanelName
378 function PanelWithSidebarImpl(hostPanelName)
380 ExtensionViewImpl.call(this, null);
381 this._hostPanelName = hostPanelName;
382 this.onSelectionChanged = new EventSink(events.PanelObjectSelected + hostPanelName);
385 PanelWithSidebarImpl.prototype = {
386 createSidebarPane: function(title, callback)
388 var id = "extension-sidebar-" + extensionServer.nextObjectId();
390 command: commands.CreateSidebarPane,
391 panel: this._hostPanelName,
395 function callbackWrapper()
397 callback(new ExtensionSidebarPane(id));
399 extensionServer.sendRequest(request, callback && callbackWrapper);
402 __proto__: ExtensionViewImpl.prototype
405 function declareInterfaceClass(implConstructor)
409 var impl = { __proto__: implConstructor.prototype };
410 implConstructor.apply(impl, arguments);
411 populateInterfaceClass(this, impl);
415 function defineDeprecatedProperty(object, className, oldName, newName)
417 var warningGiven = false;
421 console.warn(className + "." + oldName + " is deprecated. Use " + className + "." + newName + " instead");
424 return object[newName];
426 object.__defineGetter__(oldName, getter);
429 function extractCallbackArgument(args)
431 var lastArgument = args[args.length - 1];
432 return typeof lastArgument === "function" ? lastArgument : undefined;
435 var AuditCategory = declareInterfaceClass(AuditCategoryImpl);
436 var AuditResult = declareInterfaceClass(AuditResultImpl);
437 var Button = declareInterfaceClass(ButtonImpl);
438 var EventSink = declareInterfaceClass(EventSinkImpl);
439 var ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
440 var ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
441 var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
442 var Request = declareInterfaceClass(RequestImpl);
443 var Resource = declareInterfaceClass(ResourceImpl);
447 * @extends {PanelWithSidebar}
449 function ElementsPanel()
451 PanelWithSidebar.call(this, "elements");
454 ElementsPanel.prototype = {
455 __proto__: PanelWithSidebar.prototype
460 * @extends {PanelWithSidebar}
462 function SourcesPanel()
464 PanelWithSidebar.call(this, "sources");
467 SourcesPanel.prototype = {
468 __proto__: PanelWithSidebar.prototype
473 * @extends {ExtensionViewImpl}
475 function ExtensionPanelImpl(id)
477 ExtensionViewImpl.call(this, id);
478 this.onSearch = new EventSink(events.PanelSearch + id);
481 ExtensionPanelImpl.prototype = {
485 createStatusBarButton: function(iconPath, tooltipText, disabled)
487 var id = "button-" + extensionServer.nextObjectId();
489 command: commands.CreateStatusBarButton,
493 tooltip: tooltipText,
496 extensionServer.sendRequest(request);
497 return new Button(id);
506 command: commands.ShowPanel,
509 extensionServer.sendRequest(request);
512 __proto__: ExtensionViewImpl.prototype
517 * @extends {ExtensionViewImpl}
519 function ExtensionSidebarPaneImpl(id)
521 ExtensionViewImpl.call(this, id);
524 ExtensionSidebarPaneImpl.prototype = {
525 setHeight: function(height)
527 extensionServer.sendRequest({ command: commands.SetSidebarHeight, id: this._id, height: height });
530 setExpression: function(expression, rootTitle, evaluateOptions)
533 command: commands.SetSidebarContent,
535 expression: expression,
536 rootTitle: rootTitle,
537 evaluateOnPage: true,
539 if (typeof evaluateOptions === "object")
540 request.evaluateOptions = evaluateOptions;
541 extensionServer.sendRequest(request, extractCallbackArgument(arguments));
544 setObject: function(jsonObject, rootTitle, callback)
546 extensionServer.sendRequest({ command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle }, callback);
549 setPage: function(page)
551 extensionServer.sendRequest({ command: commands.SetSidebarPage, id: this._id, page: page });
554 __proto__: ExtensionViewImpl.prototype
560 function ButtonImpl(id)
563 this.onClicked = new EventSink(events.ButtonClicked + id);
566 ButtonImpl.prototype = {
567 update: function(iconPath, tooltipText, disabled)
570 command: commands.UpdateButton,
573 tooltip: tooltipText,
576 extensionServer.sendRequest(request);
589 * @return {!AuditCategory}
591 addCategory: function(displayName, resultCount)
593 var id = "extension-audit-category-" + extensionServer.nextObjectId();
594 if (typeof resultCount !== "undefined")
595 console.warn("Passing resultCount to audits.addCategory() is deprecated. Use AuditResult.updateProgress() instead.");
596 extensionServer.sendRequest({ command: commands.AddAuditCategory, id: id, displayName: displayName, resultCount: resultCount });
597 return new AuditCategory(id);
604 function AuditCategoryImpl(id)
607 * @this {EventSinkImpl}
609 function dispatchAuditEvent(request)
611 var auditResult = new AuditResult(request.arguments[0]);
613 this._fire(auditResult);
615 console.error("Uncaught exception in extension audit event handler: " + e);
620 this.onAuditStarted = new EventSink(events.AuditStarted + id, dispatchAuditEvent);
626 function AuditResultImpl(id)
630 this.createURL = this._nodeFactory.bind(this, "url");
631 this.createSnippet = this._nodeFactory.bind(this, "snippet");
632 this.createText = this._nodeFactory.bind(this, "text");
633 this.createObject = this._nodeFactory.bind(this, "object");
634 this.createNode = this._nodeFactory.bind(this, "node");
637 AuditResultImpl.prototype = {
638 addResult: function(displayName, description, severity, details)
640 // shorthand for specifying details directly in addResult().
641 if (details && !(details instanceof AuditResultNode))
642 details = new AuditResultNode(details instanceof Array ? details : [details]);
645 command: commands.AddAuditResult,
647 displayName: displayName,
648 description: description,
652 extensionServer.sendRequest(request);
658 createResult: function()
660 return new AuditResultNode(Array.prototype.slice.call(arguments));
663 updateProgress: function(worked, totalWork)
665 extensionServer.sendRequest({ command: commands.UpdateAuditProgress, resultId: this._id, progress: worked / totalWork });
670 extensionServer.sendRequest({ command: commands.StopAuditCategoryRun, resultId: this._id });
674 * @type {!Object.<string, string>}
678 return apiPrivate.audits.Severity;
682 * @return {!{type: string, arguments: !Array.<string|number>}}
684 createResourceLink: function(url, lineNumber)
687 type: "resourceLink",
688 arguments: [url, lineNumber && lineNumber - 1]
693 * @return {!{type: string, arguments: !Array.<string|number>}}
695 _nodeFactory: function(type)
699 arguments: Array.prototype.slice.call(arguments, 1)
707 function AuditResultNode(contents)
709 this.contents = contents;
711 this.expanded = false;
714 AuditResultNode.prototype = {
720 var node = new AuditResultNode(Array.prototype.slice.call(arguments));
721 this.children.push(node);
729 function InspectedWindow()
732 * @this {EventSinkImpl}
734 function dispatchResourceEvent(message)
736 this._fire(new Resource(message.arguments[0]));
740 * @this {EventSinkImpl}
742 function dispatchResourceContentEvent(message)
744 this._fire(new Resource(message.arguments[0]), message.arguments[1]);
747 this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
748 this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
751 InspectedWindow.prototype = {
752 reload: function(optionsOrUserAgent)
755 if (typeof optionsOrUserAgent === "object")
756 options = optionsOrUserAgent;
757 else if (typeof optionsOrUserAgent === "string") {
758 options = { userAgent: optionsOrUserAgent };
759 console.warn("Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. " +
760 "Use inspectedWindow.reload({ userAgent: value}) instead.");
762 extensionServer.sendRequest({ command: commands.Reload, options: options });
768 eval: function(expression, evaluateOptions)
770 var callback = extractCallbackArgument(arguments);
771 function callbackWrapper(result)
773 if (result.isError || result.isException)
774 callback(undefined, result);
776 callback(result.value);
779 command: commands.EvaluateOnInspectedPage,
780 expression: expression
782 if (typeof evaluateOptions === "object")
783 request.evaluateOptions = evaluateOptions;
784 extensionServer.sendRequest(request, callback && callbackWrapper);
788 getResources: function(callback)
790 function wrapResource(resourceData)
792 return new Resource(resourceData);
794 function callbackWrapper(resources)
796 callback(resources.map(wrapResource));
798 extensionServer.sendRequest({ command: commands.GetPageResources }, callback && callbackWrapper);
805 function ResourceImpl(resourceData)
807 this._url = resourceData.url
808 this._type = resourceData.type;
811 ResourceImpl.prototype = {
822 getContent: function(callback)
824 function callbackWrapper(response)
826 callback(response.content, response.encoding);
829 extensionServer.sendRequest({ command: commands.GetResourceContent, url: this._url }, callback && callbackWrapper);
832 setContent: function(content, commit, callback)
834 extensionServer.sendRequest({ command: commands.SetResourceContent, url: this._url, content: content, commit: commit }, callback);
838 var keyboardEventRequestQueue = [];
839 var forwardTimer = null;
841 function forwardKeyboardEvent(event)
843 const Esc = "U+001B";
844 // We only care about global hotkeys, not about random text
845 if (!event.ctrlKey && !event.altKey && !event.metaKey && !/^F\d+$/.test(event.keyIdentifier) && event.keyIdentifier !== Esc)
847 var requestPayload = {
848 eventType: event.type,
849 ctrlKey: event.ctrlKey,
850 altKey: event.altKey,
851 metaKey: event.metaKey,
852 keyIdentifier: event.keyIdentifier,
853 location: event.location,
854 keyCode: event.keyCode
856 keyboardEventRequestQueue.push(requestPayload);
858 forwardTimer = setTimeout(forwardEventQueue, 0);
861 function forwardEventQueue()
865 command: commands.ForwardKeyboardEvent,
866 entries: keyboardEventRequestQueue
868 extensionServer.sendRequest(request);
869 keyboardEventRequestQueue = [];
872 document.addEventListener("keydown", forwardKeyboardEvent, false);
873 document.addEventListener("keypress", forwardKeyboardEvent, false);
878 function ExtensionServerClient()
880 this._callbacks = {};
882 this._lastRequestId = 0;
883 this._lastObjectId = 0;
885 this.registerHandler("callback", this._onCallback.bind(this));
887 var channel = new MessageChannel();
888 this._port = channel.port1;
889 this._port.addEventListener("message", this._onMessage.bind(this), false);
892 window.parent.postMessage("registerExtension", [ channel.port2 ], "*");
895 ExtensionServerClient.prototype = {
897 * @param {!Object} message
898 * @param {function()=} callback
900 sendRequest: function(message, callback)
902 if (typeof callback === "function")
903 message.requestId = this._registerCallback(callback);
904 this._port.postMessage(message);
910 hasHandler: function(command)
912 return !!this._handlers[command];
915 registerHandler: function(command, handler)
917 this._handlers[command] = handler;
920 unregisterHandler: function(command)
922 delete this._handlers[command];
928 nextObjectId: function()
930 return injectedScriptId + "_" + ++this._lastObjectId;
933 _registerCallback: function(callback)
935 var id = ++this._lastRequestId;
936 this._callbacks[id] = callback;
940 _onCallback: function(request)
942 if (request.requestId in this._callbacks) {
943 var callback = this._callbacks[request.requestId];
944 delete this._callbacks[request.requestId];
945 callback(request.result);
949 _onMessage: function(event)
951 var request = event.data;
952 var handler = this._handlers[request.command];
954 handler.call(this, request);
958 function populateInterfaceClass(interfaze, implementation)
960 for (var member in implementation) {
961 if (member.charAt(0) === "_")
963 var descriptor = null;
964 // Traverse prototype chain until we find the owner.
965 for (var owner = implementation; owner && !descriptor; owner = owner.__proto__)
966 descriptor = Object.getOwnPropertyDescriptor(owner, member);
969 if (typeof descriptor.value === "function")
970 interfaze[member] = descriptor.value.bind(implementation);
971 else if (typeof descriptor.get === "function")
972 interfaze.__defineGetter__(member, descriptor.get.bind(implementation));
974 Object.defineProperty(interfaze, member, descriptor);
978 // extensionServer is a closure variable defined by the glue below -- make sure we fail if it's not there.
979 if (!extensionServer)
980 extensionServer = new ExtensionServerClient();
982 return new InspectorExtensionAPI();
986 * @suppress {checkVars, checkTypes}
988 function platformExtensionAPI(coreAPI)
994 chrome = window.chrome || {};
995 // Override chrome.devtools as a workaround for a error-throwing getter being exposed
996 // in extension pages loaded into a non-extension process (only happens for remote client
998 var devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, "devtools");
999 if (!devtools_descriptor || devtools_descriptor.get)
1000 Object.defineProperty(chrome, "devtools", { value: {}, enumerable: true });
1001 // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.inspectedWindow.
1002 chrome.devtools.inspectedWindow = {};
1003 chrome.devtools.inspectedWindow.__defineGetter__("tabId", getTabId);
1004 chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow;
1005 chrome.devtools.network = coreAPI.network;
1006 chrome.devtools.panels = coreAPI.panels;
1008 // default to expose experimental APIs for now.
1009 if (extensionInfo.exposeExperimentalAPIs !== false) {
1010 chrome.experimental = chrome.experimental || {};
1011 chrome.experimental.devtools = chrome.experimental.devtools || {};
1013 var properties = Object.getOwnPropertyNames(coreAPI);
1014 for (var i = 0; i < properties.length; ++i) {
1015 var descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties[i]);
1016 Object.defineProperty(chrome.experimental.devtools, properties[i], descriptor);
1018 chrome.experimental.devtools.inspectedWindow = chrome.devtools.inspectedWindow;
1020 if (extensionInfo.exposeWebInspectorNamespace)
1021 window.webInspector = coreAPI;
1025 * @param {!ExtensionDescriptor} extensionInfo
1028 function buildPlatformExtensionAPI(extensionInfo)
1030 return "var extensionInfo = " + JSON.stringify(extensionInfo) + ";" +
1031 "var tabId = " + WebInspector._inspectedTabId + ";" +
1032 platformExtensionAPI.toString();
1036 * @param {!ExtensionDescriptor} extensionInfo
1039 function buildExtensionAPIInjectedScript(extensionInfo)
1041 return "(function(injectedScriptId){ " +
1042 "var extensionServer;" +
1043 defineCommonExtensionSymbols.toString() + ";" +
1044 injectedExtensionAPI.toString() + ";" +
1045 buildPlatformExtensionAPI(extensionInfo) + ";" +
1046 "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" +