3 * Copyright (C) 2012 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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.
32 function defineCommonExtensionSymbols(apiPrivate)
34 if (!apiPrivate.audits)
35 apiPrivate.audits = {};
36 apiPrivate.audits.Severity = {
42 if (!apiPrivate.console)
43 apiPrivate.console = {};
44 apiPrivate.console.Severity = {
51 if (!apiPrivate.panels)
52 apiPrivate.panels = {};
53 apiPrivate.panels.SearchAction = {
54 CancelSearch: "cancelSearch",
55 PerformSearch: "performSearch",
56 NextSearchResult: "nextSearchResult",
57 PreviousSearchResult: "previousSearchResult"
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-"
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",
88 GetPageResources: "getPageResources",
89 GetRequestContent: "getRequestContent",
90 GetResourceContent: "getResourceContent",
91 InspectedURLChanged: "inspectedURLChanged",
92 OpenResource: "openResource",
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"
109 * @param {number} injectedScriptId
112 function injectedExtensionAPI(injectedScriptId)
117 defineCommonExtensionSymbols(apiPrivate);
119 var commands = apiPrivate.Commands;
120 var events = apiPrivate.Events;
121 var userAction = false;
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
132 function EventSinkImpl(type, customDispatch)
135 this._listeners = [];
136 this._customDispatch = customDispatch;
139 EventSinkImpl.prototype = {
140 addListener: function(callback)
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));
150 removeListener: function(callback)
152 var listeners = this._listeners;
154 for (var i = 0; i < listeners.length; ++i) {
155 if (listeners[i] === callback) {
156 listeners.splice(i, 1);
160 if (this._listeners.length === 0)
161 extensionServer.sendRequest({ command: commands.Unsubscribe, type: this._type });
165 * @param {...} vararg
167 _fire: function(vararg)
169 var listeners = this._listeners.slice();
170 for (var i = 0; i < listeners.length; ++i)
171 listeners[i].apply(null, arguments);
174 _dispatch: function(request)
176 if (this._customDispatch)
177 this._customDispatch.call(this, request);
179 this._fire.apply(this, request.arguments);
186 function InspectorExtensionAPI()
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();
200 function ConsoleAPI()
202 this.onMessageAdded = new EventSink(events.ConsoleMessageAdded);
205 ConsoleAPI.prototype = {
206 getMessages: function(callback)
208 extensionServer.sendRequest({ command: commands.GetConsoleMessages }, callback);
211 addMessage: function(severity, text, url, line)
213 extensionServer.sendRequest({ command: commands.AddConsoleMessage, severity: severity, text: text, url: url, line: line });
218 return apiPrivate.console.Severity;
228 * @this {EventSinkImpl}
230 function dispatchRequestEvent(message)
232 var request = message.arguments[1];
233 request.__proto__ = new Request(message.arguments[0]);
236 this.onRequestFinished = new EventSink(events.NetworkRequestFinished, dispatchRequestEvent);
237 defineDeprecatedProperty(this, "network", "onFinished", "onRequestFinished");
238 this.onNavigated = new EventSink(events.InspectedURLChanged);
241 Network.prototype = {
242 getHAR: function(callback)
244 function callbackWrapper(result)
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;
253 return extensionServer.sendRequest({ command: commands.GetHAR }, callback && callbackWrapper);
256 addRequestHeaders: function(headers)
258 return extensionServer.sendRequest({ command: commands.AddRequestHeaders, headers: headers, extensionId: window.location.hostname });
265 function RequestImpl(id)
270 RequestImpl.prototype = {
271 getContent: function(callback)
273 function callbackWrapper(response)
275 callback(response.content, response.encoding);
277 extensionServer.sendRequest({ command: commands.GetRequestContent, id: this._id }, callback && callbackWrapper);
287 elements: new ElementsPanel(),
288 sources: new SourcesPanel(),
291 function panelGetter(name)
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 }); };
301 create: function(title, icon, page, callback)
303 var id = "extension-panel-" + extensionServer.nextObjectId();
305 command: commands.CreatePanel,
311 extensionServer.sendRequest(request, callback && callback.bind(this, new ExtensionPanel(id)));
314 setOpenResourceHandler: function(callback)
316 var hadHandler = extensionServer.hasHandler(events.OpenResource);
318 function callbackWrapper(message)
320 // Allow the panel to show itself when handling the event.
323 callback.call(null, new Resource(message.resource), message.lineNumber);
330 extensionServer.unregisterHandler(events.OpenResource);
332 extensionServer.registerHandler(events.OpenResource, callbackWrapper);
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 });
339 openResource: function(url, lineNumber, callback)
341 extensionServer.sendRequest({ command: commands.OpenResource, "url": url, "lineNumber": lineNumber }, callback);
346 return apiPrivate.panels.SearchAction;
353 function ExtensionViewImpl(id)
358 * @this {EventSinkImpl}
360 function dispatchShowEvent(message)
362 var frameIndex = message.arguments[0];
363 if (typeof frameIndex === "number")
364 this._fire(window.parent.frames[frameIndex]);
368 this.onShown = new EventSink(events.ViewShown + id, dispatchShowEvent);
369 this.onHidden = new EventSink(events.ViewHidden + id);
374 * @extends {ExtensionViewImpl}
376 function PanelWithSidebarImpl(hostPanelName)
378 this._hostPanelName = hostPanelName;
379 this.onSelectionChanged = new EventSink(events.PanelObjectSelected + hostPanelName);
382 PanelWithSidebarImpl.prototype = {
383 createSidebarPane: function(title, callback)
385 var id = "extension-sidebar-" + extensionServer.nextObjectId();
387 command: commands.CreateSidebarPane,
388 panel: this._hostPanelName,
392 function callbackWrapper()
394 callback(new ExtensionSidebarPane(id));
396 extensionServer.sendRequest(request, callback && callbackWrapper);
399 __proto__: ExtensionViewImpl.prototype
402 function declareInterfaceClass(implConstructor)
406 var impl = { __proto__: implConstructor.prototype };
407 implConstructor.apply(impl, arguments);
408 populateInterfaceClass(this, impl);
412 function defineDeprecatedProperty(object, className, oldName, newName)
414 var warningGiven = false;
418 console.warn(className + "." + oldName + " is deprecated. Use " + className + "." + newName + " instead");
421 return object[newName];
423 object.__defineGetter__(oldName, getter);
426 function extractCallbackArgument(args)
428 var lastArgument = args[args.length - 1];
429 return typeof lastArgument === "function" ? lastArgument : undefined;
432 var AuditCategory = declareInterfaceClass(AuditCategoryImpl);
433 var AuditResult = declareInterfaceClass(AuditResultImpl);
434 var Button = declareInterfaceClass(ButtonImpl);
435 var EventSink = declareInterfaceClass(EventSinkImpl);
436 var ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
437 var ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl);
438 var PanelWithSidebar = declareInterfaceClass(PanelWithSidebarImpl);
439 var Request = declareInterfaceClass(RequestImpl);
440 var Resource = declareInterfaceClass(ResourceImpl);
441 var Timeline = declareInterfaceClass(TimelineImpl);
445 * @extends {PanelWithSidebar}
447 function ElementsPanel()
449 PanelWithSidebar.call(this, "elements");
452 ElementsPanel.prototype = {
453 __proto__: PanelWithSidebar.prototype
458 * @extends {PanelWithSidebar}
460 function SourcesPanel()
462 PanelWithSidebar.call(this, "sources");
465 SourcesPanel.prototype = {
466 __proto__: PanelWithSidebar.prototype
471 * @extends {ExtensionViewImpl}
473 function ExtensionPanelImpl(id)
475 ExtensionViewImpl.call(this, id);
476 this.onSearch = new EventSink(events.PanelSearch + id);
479 ExtensionPanelImpl.prototype = {
480 createStatusBarButton: function(iconPath, tooltipText, disabled)
482 var id = "button-" + extensionServer.nextObjectId();
484 command: commands.CreateStatusBarButton,
488 tooltip: tooltipText,
491 extensionServer.sendRequest(request);
492 return new Button(id);
501 command: commands.ShowPanel,
504 extensionServer.sendRequest(request);
507 __proto__: ExtensionViewImpl.prototype
512 * @extends {ExtensionViewImpl}
514 function ExtensionSidebarPaneImpl(id)
516 ExtensionViewImpl.call(this, id);
519 ExtensionSidebarPaneImpl.prototype = {
520 setHeight: function(height)
522 extensionServer.sendRequest({ command: commands.SetSidebarHeight, id: this._id, height: height });
525 setExpression: function(expression, rootTitle, evaluateOptions)
528 command: commands.SetSidebarContent,
530 expression: expression,
531 rootTitle: rootTitle,
532 evaluateOnPage: true,
534 if (typeof evaluateOptions === "object")
535 request.evaluateOptions = evaluateOptions;
536 extensionServer.sendRequest(request, extractCallbackArgument(arguments));
539 setObject: function(jsonObject, rootTitle, callback)
541 extensionServer.sendRequest({ command: commands.SetSidebarContent, id: this._id, expression: jsonObject, rootTitle: rootTitle }, callback);
544 setPage: function(page)
546 extensionServer.sendRequest({ command: commands.SetSidebarPage, id: this._id, page: page });
549 __proto__: ExtensionViewImpl.prototype
555 function ButtonImpl(id)
558 this.onClicked = new EventSink(events.ButtonClicked + id);
561 ButtonImpl.prototype = {
562 update: function(iconPath, tooltipText, disabled)
565 command: commands.UpdateButton,
568 tooltip: tooltipText,
571 extensionServer.sendRequest(request);
583 addCategory: function(displayName, resultCount)
585 var id = "extension-audit-category-" + extensionServer.nextObjectId();
586 if (typeof resultCount !== "undefined")
587 console.warn("Passing resultCount to audits.addCategory() is deprecated. Use AuditResult.updateProgress() instead.");
588 extensionServer.sendRequest({ command: commands.AddAuditCategory, id: id, displayName: displayName, resultCount: resultCount });
589 return new AuditCategory(id);
596 function AuditCategoryImpl(id)
599 * @this {EventSinkImpl}
601 function dispatchAuditEvent(request)
603 var auditResult = new AuditResult(request.arguments[0]);
605 this._fire(auditResult);
607 console.error("Uncaught exception in extension audit event handler: " + e);
612 this.onAuditStarted = new EventSink(events.AuditStarted + id, dispatchAuditEvent);
618 function AuditResultImpl(id)
622 this.createURL = this._nodeFactory.bind(null, "url");
623 this.createSnippet = this._nodeFactory.bind(null, "snippet");
624 this.createText = this._nodeFactory.bind(null, "text");
625 this.createObject = this._nodeFactory.bind(null, "object");
626 this.createNode = this._nodeFactory.bind(null, "node");
629 AuditResultImpl.prototype = {
630 addResult: function(displayName, description, severity, details)
632 // shorthand for specifying details directly in addResult().
633 if (details && !(details instanceof AuditResultNode))
634 details = new AuditResultNode(details instanceof Array ? details : [details]);
637 command: commands.AddAuditResult,
639 displayName: displayName,
640 description: description,
644 extensionServer.sendRequest(request);
647 createResult: function()
649 return new AuditResultNode(Array.prototype.slice.call(arguments));
652 updateProgress: function(worked, totalWork)
654 extensionServer.sendRequest({ command: commands.UpdateAuditProgress, resultId: this._id, progress: worked / totalWork });
659 extensionServer.sendRequest({ command: commands.StopAuditCategoryRun, resultId: this._id });
664 return apiPrivate.audits.Severity;
667 createResourceLink: function(url, lineNumber)
670 type: "resourceLink",
671 arguments: [url, lineNumber && lineNumber - 1]
675 _nodeFactory: function(type)
679 arguments: Array.prototype.slice.call(arguments, 1)
687 function AuditResultNode(contents)
689 this.contents = contents;
691 this.expanded = false;
694 AuditResultNode.prototype = {
697 var node = new AuditResultNode(Array.prototype.slice.call(arguments));
698 this.children.push(node);
706 function InspectedWindow()
709 * @this {EventSinkImpl}
711 function dispatchResourceEvent(message)
713 this._fire(new Resource(message.arguments[0]));
717 * @this {EventSinkImpl}
719 function dispatchResourceContentEvent(message)
721 this._fire(new Resource(message.arguments[0]), message.arguments[1]);
724 this.onResourceAdded = new EventSink(events.ResourceAdded, dispatchResourceEvent);
725 this.onResourceContentCommitted = new EventSink(events.ResourceContentCommitted, dispatchResourceContentEvent);
728 InspectedWindow.prototype = {
729 reload: function(optionsOrUserAgent)
732 if (typeof optionsOrUserAgent === "object")
733 options = optionsOrUserAgent;
734 else if (typeof optionsOrUserAgent === "string") {
735 options = { userAgent: optionsOrUserAgent };
736 console.warn("Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. " +
737 "Use inspectedWindow.reload({ userAgent: value}) instead.");
739 return extensionServer.sendRequest({ command: commands.Reload, options: options });
742 eval: function(expression, evaluateOptions)
744 var callback = extractCallbackArgument(arguments);
745 function callbackWrapper(result)
747 if (result.isError || result.isException)
748 callback(undefined, result);
750 callback(result.value);
753 command: commands.EvaluateOnInspectedPage,
754 expression: expression
756 if (typeof evaluateOptions === "object")
757 request.evaluateOptions = evaluateOptions;
758 return extensionServer.sendRequest(request, callback && callbackWrapper);
761 getResources: function(callback)
763 function wrapResource(resourceData)
765 return new Resource(resourceData);
767 function callbackWrapper(resources)
769 callback(resources.map(wrapResource));
771 return extensionServer.sendRequest({ command: commands.GetPageResources }, callback && callbackWrapper);
778 function ResourceImpl(resourceData)
780 this._url = resourceData.url
781 this._type = resourceData.type;
784 ResourceImpl.prototype = {
795 getContent: function(callback)
797 function callbackWrapper(response)
799 callback(response.content, response.encoding);
802 return extensionServer.sendRequest({ command: commands.GetResourceContent, url: this._url }, callback && callbackWrapper);
805 setContent: function(content, commit, callback)
807 return extensionServer.sendRequest({ command: commands.SetResourceContent, url: this._url, content: content, commit: commit }, callback);
814 function TimelineImpl()
816 this.onEventRecorded = new EventSink(events.TimelineEventRecorded);
819 function forwardKeyboardEvent(event)
821 const Esc = "U+001B";
822 // We only care about global hotkeys, not about random text
823 if (!event.ctrlKey && !event.altKey && !event.metaKey && !/^F\d+$/.test(event.keyIdentifier) && event.keyIdentifier !== Esc)
826 command: commands.ForwardKeyboardEvent,
827 eventType: event.type,
828 ctrlKey: event.ctrlKey,
829 altKey: event.altKey,
830 metaKey: event.metaKey,
831 keyIdentifier: event.keyIdentifier,
832 location: event.location
834 extensionServer.sendRequest(request);
837 document.addEventListener("keydown", forwardKeyboardEvent, false);
838 document.addEventListener("keypress", forwardKeyboardEvent, false);
843 function ExtensionServerClient()
845 this._callbacks = {};
847 this._lastRequestId = 0;
848 this._lastObjectId = 0;
850 this.registerHandler("callback", this._onCallback.bind(this));
852 var channel = new MessageChannel();
853 this._port = channel.port1;
854 this._port.addEventListener("message", this._onMessage.bind(this), false);
857 window.parent.postMessage("registerExtension", [ channel.port2 ], "*");
860 ExtensionServerClient.prototype = {
862 * @param {function()=} callback
864 sendRequest: function(message, callback)
866 if (typeof callback === "function")
867 message.requestId = this._registerCallback(callback);
868 return this._port.postMessage(message);
871 hasHandler: function(command)
873 return !!this._handlers[command];
876 registerHandler: function(command, handler)
878 this._handlers[command] = handler;
881 unregisterHandler: function(command)
883 delete this._handlers[command];
886 nextObjectId: function()
888 return injectedScriptId + "_" + ++this._lastObjectId;
891 _registerCallback: function(callback)
893 var id = ++this._lastRequestId;
894 this._callbacks[id] = callback;
898 _onCallback: function(request)
900 if (request.requestId in this._callbacks) {
901 var callback = this._callbacks[request.requestId];
902 delete this._callbacks[request.requestId];
903 callback(request.result);
907 _onMessage: function(event)
909 var request = event.data;
910 var handler = this._handlers[request.command];
912 handler.call(this, request);
916 function populateInterfaceClass(interface, implementation)
918 for (var member in implementation) {
919 if (member.charAt(0) === "_")
921 var descriptor = null;
922 // Traverse prototype chain until we find the owner.
923 for (var owner = implementation; owner && !descriptor; owner = owner.__proto__)
924 descriptor = Object.getOwnPropertyDescriptor(owner, member);
927 if (typeof descriptor.value === "function")
928 interface[member] = descriptor.value.bind(implementation);
929 else if (typeof descriptor.get === "function")
930 interface.__defineGetter__(member, descriptor.get.bind(implementation));
932 Object.defineProperty(interface, member, descriptor);
936 // extensionServer is a closure variable defined by the glue below -- make sure we fail if it's not there.
937 if (!extensionServer)
938 extensionServer = new ExtensionServerClient();
940 return new InspectorExtensionAPI();
944 * @param {!ExtensionDescriptor} extensionInfo
947 function buildExtensionAPIInjectedScript(extensionInfo)
949 return "(function(injectedScriptId){ " +
950 "var extensionServer;" +
951 defineCommonExtensionSymbols.toString() + ";" +
952 injectedExtensionAPI.toString() + ";" +
953 buildPlatformExtensionAPI(extensionInfo) + ";" +
954 "platformExtensionAPI(injectedExtensionAPI(injectedScriptId));" +
959 * Copyright (C) 2011 Google Inc. All rights reserved.
961 * Redistribution and use in source and binary forms, with or without
962 * modification, are permitted provided that the following conditions are
965 * * Redistributions of source code must retain the above copyright
966 * notice, this list of conditions and the following disclaimer.
967 * * Redistributions in binary form must reproduce the above
968 * copyright notice, this list of conditions and the following disclaimer
969 * in the documentation and/or other materials provided with the
971 * * Neither the name of Google Inc. nor the names of its
972 * contributors may be used to endorse or promote products derived from
973 * this software without specific prior written permission.
975 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
976 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
977 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
978 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
979 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
980 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
981 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
982 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
983 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
984 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
985 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 var extensionInfo = {};
1026 var extensionServer;
1027 platformExtensionAPI(injectedExtensionAPI("remote-" + window.parent.frames.length));