diff options
Diffstat (limited to 'Source/WebCore/inspector/front-end/ExtensionServer.js')
-rw-r--r-- | Source/WebCore/inspector/front-end/ExtensionServer.js | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/Source/WebCore/inspector/front-end/ExtensionServer.js b/Source/WebCore/inspector/front-end/ExtensionServer.js new file mode 100644 index 0000000..373c855 --- /dev/null +++ b/Source/WebCore/inspector/front-end/ExtensionServer.js @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +WebInspector.ExtensionServer = function() +{ + this._clientObjects = {}; + this._handlers = {}; + this._subscribers = {}; + this._extraHeaders = {}; + this._status = new WebInspector.ExtensionStatus(); + + this._registerHandler("addRequestHeaders", this._onAddRequestHeaders.bind(this)); + this._registerHandler("addAuditCategory", this._onAddAuditCategory.bind(this)); + this._registerHandler("addAuditResult", this._onAddAuditResult.bind(this)); + this._registerHandler("createPanel", this._onCreatePanel.bind(this)); + this._registerHandler("createSidebarPane", this._onCreateSidebar.bind(this)); + this._registerHandler("createWatchExpressionSidebarPane", this._onCreateWatchExpressionSidebarPane.bind(this)); + this._registerHandler("evaluateOnInspectedPage", this._onEvaluateOnInspectedPage.bind(this)); + this._registerHandler("getHAR", this._onGetHAR.bind(this)); + this._registerHandler("getResourceContent", this._onGetResourceContent.bind(this)); + this._registerHandler("log", this._onLog.bind(this)); + this._registerHandler("reload", this._onReload.bind(this)); + this._registerHandler("setSidebarHeight", this._onSetSidebarHeight.bind(this)); + this._registerHandler("setWatchSidebarContent", this._onSetWatchSidebarContent.bind(this)); + this._registerHandler("stopAuditCategoryRun", this._onStopAuditCategoryRun.bind(this)); + this._registerHandler("subscribe", this._onSubscribe.bind(this)); + this._registerHandler("unsubscribe", this._onUnsubscribe.bind(this)); + + + window.addEventListener("message", this._onWindowMessage.bind(this), false); +} + +WebInspector.ExtensionServer.prototype = { + notifyPanelShown: function(panelName) + { + this._postNotification("panel-shown-" + panelName); + }, + + notifyObjectSelected: function(panelId, objectId) + { + this._postNotification("panel-objectSelected-" + panelId, objectId); + }, + + notifyResourceFinished: function(resource) + { + this._postNotification("resource-finished", resource.identifier, (new WebInspector.HAREntry(resource)).build()); + }, + + notifySearchAction: function(panelId, action, searchString) + { + this._postNotification("panel-search-" + panelId, action, searchString); + }, + + notifyPageLoaded: function(milliseconds) + { + this._postNotification("inspectedPageLoaded", milliseconds); + }, + + notifyPageDOMContentLoaded: function(milliseconds) + { + this._postNotification("inspectedPageDOMContentLoaded", milliseconds); + }, + + notifyInspectedURLChanged: function() + { + this._postNotification("inspectedURLChanged"); + }, + + notifyInspectorReset: function() + { + this._postNotification("reset"); + }, + + notifyExtensionWatchSidebarUpdated: function(id) + { + this._postNotification("watch-sidebar-updated-" + id); + }, + + startAuditRun: function(category, auditRun) + { + this._clientObjects[auditRun.id] = auditRun; + this._postNotification("audit-started-" + category.id, auditRun.id); + }, + + stopAuditRun: function(auditRun) + { + delete this._clientObjects[auditRun.id]; + }, + + _postNotification: function(type, details) + { + var subscribers = this._subscribers[type]; + if (!subscribers) + return; + var message = { + command: "notify-" + type, + arguments: Array.prototype.slice.call(arguments, 1) + }; + for (var i = 0; i < subscribers.length; ++i) + subscribers[i].postMessage(message); + }, + + _onSubscribe: function(message, port) + { + var subscribers = this._subscribers[message.type]; + if (subscribers) + subscribers.push(port); + else + this._subscribers[message.type] = [ port ]; + }, + + _onUnsubscribe: function(message, port) + { + var subscribers = this._subscribers[message.type]; + if (!subscribers) + return; + subscribers.remove(port); + if (!subscribers.length) + delete this._subscribers[message.type]; + }, + + _onAddRequestHeaders: function(message) + { + var id = message.extensionId; + if (typeof id !== "string") + return this._status.E_BADARGTYPE("extensionId", typeof id, "string"); + var extensionHeaders = this._extraHeaders[id]; + if (!extensionHeaders) { + extensionHeaders = {}; + this._extraHeaders[id] = extensionHeaders; + } + for (name in message.headers) + extensionHeaders[name] = message.headers[name]; + var allHeaders = {}; + for (extension in this._extraHeaders) { + var headers = this._extraHeaders[extension]; + for (name in headers) { + if (typeof headers[name] === "string") + allHeaders[name] = headers[name]; + } + } + InspectorBackend.setExtraHeaders(allHeaders); + }, + + _onCreatePanel: function(message, port) + { + var id = message.id; + // The ids are generated on the client API side and must be unique, so the check below + // shouldn't be hit unless someone is bypassing the API. + if (id in this._clientObjects || id in WebInspector.panels) + return this._status.E_EXISTS(id); + var panel = new WebInspector.ExtensionPanel(id, message.title, message.icon); + this._clientObjects[id] = panel; + + var toolbarElement = document.getElementById("toolbar"); + var lastToolbarItem = WebInspector.panelOrder[WebInspector.panelOrder.length - 1].toolbarItem; + WebInspector.addPanelToolbarIcon(toolbarElement, panel, lastToolbarItem); + WebInspector.panels[id] = panel; + var iframe = this._createClientIframe(panel.element, message.url); + iframe.style.height = "100%"; + return this._status.OK(); + }, + + _onCreateSidebar: function(message) + { + var sidebar = this._createSidebar(message, WebInspector.SidebarPane); + if (sidebar.isError) + return sidebar; + this._createClientIframe(sidebar.bodyElement, message.url); + return this._status.OK(); + }, + + _onCreateWatchExpressionSidebarPane: function(message) + { + var sidebar = this._createSidebar(message, WebInspector.ExtensionWatchSidebarPane); + return sidebar.isError ? sidebar : this._status.OK(); + }, + + _createSidebar: function(message, constructor) + { + var panel = WebInspector.panels[message.panel]; + if (!panel) + return this._status.E_NOTFOUND(message.panel); + if (!panel.sidebarElement || !panel.sidebarPanes) + return this._status.E_NOTSUPPORTED(); + var id = message.id; + var sidebar = new constructor(message.title, message.id); + this._clientObjects[id] = sidebar; + panel.sidebarPanes[id] = sidebar; + panel.sidebarElement.appendChild(sidebar.element); + + return sidebar; + }, + + _createClientIframe: function(parent, url, requestId, port) + { + var iframe = document.createElement("iframe"); + iframe.src = url; + iframe.style.width = "100%"; + parent.appendChild(iframe); + return iframe; + }, + + _onSetSidebarHeight: function(message) + { + var sidebar = this._clientObjects[message.id]; + if (!sidebar) + return this._status.E_NOTFOUND(message.id); + sidebar.bodyElement.firstChild.style.height = message.height; + }, + + _onSetWatchSidebarContent: function(message) + { + var sidebar = this._clientObjects[message.id]; + if (!sidebar) + return this._status.E_NOTFOUND(message.id); + if (message.evaluateOnPage) + sidebar.setExpression(message.expression, message.rootTitle); + else + sidebar.setObject(message.expression, message.rootTitle); + }, + + _onLog: function(message) + { + WebInspector.log(message.message); + }, + + _onReload: function() + { + InspectorBackend.reloadPage(); + return this._status.OK(); + }, + + _onEvaluateOnInspectedPage: function(message, port) + { + function callback(resultPayload) + { + var resultObject = WebInspector.RemoteObject.fromPayload(resultPayload); + var result = {}; + if (resultObject.isError()) + result.isException = true; + result.value = resultObject.description; + this._dispatchCallback(message.requestId, port, result); + } + var evalExpression = "JSON.stringify(eval('" + + "with (window.console._commandLineAPI) with (window) {' + unescape('" + escape(message.expression) + + "') + '}'));"; + InjectedScriptAccess.getDefault().evaluate(evalExpression, callback.bind(this)); + }, + + _onRevealAndSelect: function(message) + { + if (message.panelId === "resources" && type === "resource") + return this._onRevealAndSelectResource(message); + else + return this._status.E_NOTSUPPORTED(message.panelId, message.type); + }, + + _onRevealAndSelectResource: function(message) + { + var id = message.id; + var resource = null; + + resource = WebInspector.networkResources[id] || WebInspector.resourceForURL(id); + if (!resource) + return this._status.E_NOTFOUND(typeof id + ": " + id); + + WebInspector.panels.resources.showResource(resource, message.line); + WebInspector.showPanel("resources"); + }, + + _dispatchCallback: function(requestId, port, result) + { + port.postMessage({ command: "callback", requestId: requestId, result: result }); + }, + + _onGetHAR: function(request) + { + var harLog = new WebInspector.HARLog(); + harLog.includeResourceIds = true; + return harLog.build(); + }, + + _onGetResourceContent: function(message, port) + { + function onContentAvailable(content, encoded) + { + var response = { + encoding: encoded ? "base64" : "", + content: content + }; + this._dispatchCallback(message.requestId, port, response); + } + var resource = WebInspector.networkResources[message.id]; + if (!resource) + return this._status.E_NOTFOUND(message.id); + resource.requestContent(onContentAvailable.bind(this)); + }, + + _onAddAuditCategory: function(request) + { + var category = new WebInspector.ExtensionAuditCategory(request.id, request.displayName, request.resultCount); + if (WebInspector.panels.audits.getCategory(category.id)) + return this._status.E_EXISTS(category.id); + this._clientObjects[request.id] = category; + WebInspector.panels.audits.addCategory(category); + }, + + _onAddAuditResult: function(request) + { + var auditResult = this._clientObjects[request.resultId]; + if (!auditResult) + return this._status.E_NOTFOUND(request.resultId); + try { + auditResult.addResult(request.displayName, request.description, request.severity, request.details); + } catch (e) { + return e; + } + return this._status.OK(); + }, + + _onStopAuditCategoryRun: function(request) + { + var auditRun = this._clientObjects[request.resultId]; + if (!auditRun) + return this._status.E_NOTFOUND(request.resultId); + auditRun.cancel(); + }, + + initExtensions: function() + { + InspectorExtensionRegistry.getExtensionsAsync(); + }, + + _addExtensions: function(extensions) + { + // See ExtensionAPI.js and ExtensionCommon.js for details. + InspectorFrontendHost.setExtensionAPI(this._buildExtensionAPIInjectedScript()); + for (var i = 0; i < extensions.length; ++i) { + var extension = extensions[i]; + try { + if (!extension.startPage) + return; + var iframe = document.createElement("iframe"); + iframe.src = extension.startPage; + iframe.style.display = "none"; + document.body.appendChild(iframe); + } catch (e) { + console.error("Failed to initialize extension " + extension.startPage + ":" + e); + } + } + }, + + _buildExtensionAPIInjectedScript: function() + { + var resourceTypes = {}; + var resourceTypeProperties = Object.getOwnPropertyNames(WebInspector.Resource.Type); + for (var i = 0; i < resourceTypeProperties.length; ++i) { + var propName = resourceTypeProperties[i]; + var propValue = WebInspector.Resource.Type[propName]; + if (typeof propValue === "number") + resourceTypes[propName] = WebInspector.Resource.Type.toString(propValue); + } + var platformAPI = WebInspector.buildPlatformExtensionAPI ? WebInspector.buildPlatformExtensionAPI() : ""; + return "(function(){ " + + "var apiPrivate = {};" + + "(" + WebInspector.commonExtensionSymbols.toString() + ")(apiPrivate);" + + "(" + WebInspector.injectedExtensionAPI.toString() + ").apply(this, arguments);" + + "webInspector.resources.Types = " + JSON.stringify(resourceTypes) + ";" + + platformAPI + + "})"; + }, + + _onWindowMessage: function(event) + { + if (event.data !== "registerExtension") + return; + var port = event.ports[0]; + port.addEventListener("message", this._onmessage.bind(this), false); + port.start(); + }, + + _onmessage: function(event) + { + var request = event.data; + var result; + + if (request.command in this._handlers) + result = this._handlers[request.command](request, event.target); + else + result = this._status.E_NOTSUPPORTED(request.command); + + if (result && request.requestId) + this._dispatchCallback(request.requestId, event.target, result); + }, + + _registerHandler: function(command, callback) + { + this._handlers[command] = callback; + } +} + +WebInspector.ExtensionServer._statuses = +{ + OK: "", + E_EXISTS: "Object already exists: %s", + E_BADARG: "Invalid argument %s: %s", + E_BADARGTYPE: "Invalid type for argument %s: got %s, expected %s", + E_NOTFOUND: "Object not found: %s", + E_NOTSUPPORTED: "Object does not support requested operation: %s", +} + +WebInspector.ExtensionStatus = function() +{ + function makeStatus(code) + { + var description = WebInspector.ExtensionServer._statuses[code] || code; + var details = Array.prototype.slice.call(arguments, 1); + var status = { code: code, description: description, details: details }; + if (code !== "OK") { + status.isError = true; + console.log("Extension server error: " + String.vsprintf(description, details)); + } + return status; + } + for (status in WebInspector.ExtensionServer._statuses) + this[status] = makeStatus.bind(null, status); +} + +WebInspector.addExtensions = function(extensions) +{ + WebInspector.extensionServer._addExtensions(extensions); +} + +WebInspector.extensionServer = new WebInspector.ExtensionServer(); |