| /* |
| * Copyright (C) 2011 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. |
| */ |
| |
| /** |
| * @constructor |
| */ |
| function InspectorBackendClass() |
| { |
| this._agentPrototypes = {}; |
| this._dispatcherPrototypes = {}; |
| this._initialized = false; |
| this._initProtocolAgentsConstructor(); |
| } |
| |
| InspectorBackendClass._DevToolsErrorCode = -32000; |
| |
| /** |
| * @param {string} error |
| * @param {!Object} messageObject |
| */ |
| InspectorBackendClass.reportProtocolError = function(error, messageObject) |
| { |
| console.error(error + ": " + JSON.stringify(messageObject)); |
| } |
| |
| InspectorBackendClass.prototype = { |
| |
| _initProtocolAgentsConstructor: function() |
| { |
| window.Protocol = {}; |
| |
| /** |
| * @constructor |
| * @param {!Object.<string, !Object>} agentsMap |
| */ |
| window.Protocol.Agents = function(agentsMap) { |
| this._agentsMap = agentsMap; |
| }; |
| }, |
| |
| /** |
| * @param {string} domain |
| */ |
| _addAgentGetterMethodToProtocolAgentsPrototype: function(domain) |
| { |
| var upperCaseLength = 0; |
| while (upperCaseLength < domain.length && domain[upperCaseLength].toLowerCase() !== domain[upperCaseLength]) |
| ++upperCaseLength; |
| |
| var methodName = domain.substr(0, upperCaseLength).toLowerCase() + domain.slice(upperCaseLength) + "Agent"; |
| |
| /** |
| * @this {Protocol.Agents} |
| */ |
| function agentGetter() |
| { |
| return this._agentsMap[domain]; |
| } |
| |
| window.Protocol.Agents.prototype[methodName] = agentGetter; |
| |
| /** |
| * @this {Protocol.Agents} |
| */ |
| function registerDispatcher(dispatcher) |
| { |
| this.registerDispatcher(domain, dispatcher) |
| } |
| |
| window.Protocol.Agents.prototype["register" + domain + "Dispatcher"] = registerDispatcher; |
| }, |
| |
| /** |
| * @param {string} domain |
| * @return {!InspectorBackendClass.AgentPrototype} |
| */ |
| _agentPrototype: function(domain) |
| { |
| if (!this._agentPrototypes[domain]) { |
| this._agentPrototypes[domain] = new InspectorBackendClass.AgentPrototype(domain); |
| this._addAgentGetterMethodToProtocolAgentsPrototype(domain); |
| } |
| |
| return this._agentPrototypes[domain]; |
| }, |
| |
| /** |
| * @param {string} domain |
| * @return {!InspectorBackendClass.DispatcherPrototype} |
| */ |
| _dispatcherPrototype: function(domain) |
| { |
| if (!this._dispatcherPrototypes[domain]) |
| this._dispatcherPrototypes[domain] = new InspectorBackendClass.DispatcherPrototype(); |
| return this._dispatcherPrototypes[domain]; |
| }, |
| |
| /** |
| * @param {string} method |
| * @param {!Array.<!Object>} signature |
| * @param {!Array.<string>} replyArgs |
| * @param {boolean} hasErrorData |
| */ |
| registerCommand: function(method, signature, replyArgs, hasErrorData) |
| { |
| var domainAndMethod = method.split("."); |
| this._agentPrototype(domainAndMethod[0]).registerCommand(domainAndMethod[1], signature, replyArgs, hasErrorData); |
| this._initialized = true; |
| }, |
| |
| /** |
| * @param {string} type |
| * @param {!Object} values |
| */ |
| registerEnum: function(type, values) |
| { |
| var domainAndMethod = type.split("."); |
| var agentName = domainAndMethod[0] + "Agent"; |
| if (!window[agentName]) |
| window[agentName] = {}; |
| |
| window[agentName][domainAndMethod[1]] = values; |
| this._initialized = true; |
| }, |
| |
| /** |
| * @param {string} eventName |
| * @param {!Object} params |
| */ |
| registerEvent: function(eventName, params) |
| { |
| var domain = eventName.split(".")[0]; |
| this._dispatcherPrototype(domain).registerEvent(eventName, params); |
| this._initialized = true; |
| }, |
| |
| /** |
| * @param {string} jsonUrl |
| */ |
| loadFromJSONIfNeeded: function(jsonUrl) |
| { |
| if (this._initialized) |
| return; |
| |
| var xhr = new XMLHttpRequest(); |
| xhr.open("GET", jsonUrl, false); |
| xhr.send(null); |
| |
| var schema = JSON.parse(xhr.responseText); |
| var code = InspectorBackendClass._generateCommands(schema); |
| eval(code); |
| }, |
| |
| /** |
| * @param {function(T)} clientCallback |
| * @param {string} errorPrefix |
| * @param {function(new:T,S)=} constructor |
| * @param {T=} defaultValue |
| * @return {function(?string, S)} |
| * @template T,S |
| */ |
| wrapClientCallback: function(clientCallback, errorPrefix, constructor, defaultValue) |
| { |
| /** |
| * @param {?string} error |
| * @param {S} value |
| * @template S |
| */ |
| function callbackWrapper(error, value) |
| { |
| if (error) { |
| console.error(errorPrefix + error); |
| clientCallback(defaultValue); |
| return; |
| } |
| if (constructor) |
| clientCallback(new constructor(value)); |
| else |
| clientCallback(value); |
| } |
| return callbackWrapper; |
| } |
| } |
| |
| /** |
| * @param {*} schema |
| * @return {string} |
| */ |
| InspectorBackendClass._generateCommands = function(schema) { |
| var jsTypes = { integer: "number", array: "object" }; |
| var rawTypes = {}; |
| var result = []; |
| |
| var domains = schema["domains"] || []; |
| for (var i = 0; i < domains.length; ++i) { |
| var domain = domains[i]; |
| for (var j = 0; domain.types && j < domain.types.length; ++j) { |
| var type = domain.types[j]; |
| rawTypes[domain.domain + "." + type.id] = jsTypes[type.type] || type.type; |
| } |
| } |
| |
| function toUpperCase(groupIndex, group0, group1) |
| { |
| return [group0, group1][groupIndex].toUpperCase(); |
| } |
| function generateEnum(enumName, items) |
| { |
| var members = []; |
| for (var m = 0; m < items.length; ++m) { |
| var value = items[m]; |
| var name = value.replace(/-(\w)/g, toUpperCase.bind(null, 1)).toTitleCase(); |
| name = name.replace(/HTML|XML|WML|API/ig, toUpperCase.bind(null, 0)); |
| members.push(name + ": \"" + value +"\""); |
| } |
| return "InspectorBackend.registerEnum(\"" + enumName + "\", {" + members.join(", ") + "});"; |
| } |
| |
| for (var i = 0; i < domains.length; ++i) { |
| var domain = domains[i]; |
| |
| var types = domain["types"] || []; |
| for (var j = 0; j < types.length; ++j) { |
| var type = types[j]; |
| if ((type["type"] === "string") && type["enum"]) |
| result.push(generateEnum(domain.domain + "." + type.id, type["enum"])); |
| else if (type["type"] === "object") { |
| var properties = type["properties"] || []; |
| for (var k = 0; k < properties.length; ++k) { |
| var property = properties[k]; |
| if ((property["type"] === "string") && property["enum"]) |
| result.push(generateEnum(domain.domain + "." + type.id + property["name"].toTitleCase(), property["enum"])); |
| } |
| } |
| } |
| |
| var commands = domain["commands"] || []; |
| for (var j = 0; j < commands.length; ++j) { |
| var command = commands[j]; |
| var parameters = command["parameters"]; |
| var paramsText = []; |
| for (var k = 0; parameters && k < parameters.length; ++k) { |
| var parameter = parameters[k]; |
| |
| var type; |
| if (parameter.type) |
| type = jsTypes[parameter.type] || parameter.type; |
| else { |
| var ref = parameter["$ref"]; |
| if (ref.indexOf(".") !== -1) |
| type = rawTypes[ref]; |
| else |
| type = rawTypes[domain.domain + "." + ref]; |
| } |
| |
| var text = "{\"name\": \"" + parameter.name + "\", \"type\": \"" + type + "\", \"optional\": " + (parameter.optional ? "true" : "false") + "}"; |
| paramsText.push(text); |
| } |
| |
| var returnsText = []; |
| var returns = command["returns"] || []; |
| for (var k = 0; k < returns.length; ++k) { |
| var parameter = returns[k]; |
| returnsText.push("\"" + parameter.name + "\""); |
| } |
| var hasErrorData = String(Boolean(command.error)); |
| result.push("InspectorBackend.registerCommand(\"" + domain.domain + "." + command.name + "\", [" + paramsText.join(", ") + "], [" + returnsText.join(", ") + "], " + hasErrorData + ");"); |
| } |
| |
| for (var j = 0; domain.events && j < domain.events.length; ++j) { |
| var event = domain.events[j]; |
| var paramsText = []; |
| for (var k = 0; event.parameters && k < event.parameters.length; ++k) { |
| var parameter = event.parameters[k]; |
| paramsText.push("\"" + parameter.name + "\""); |
| } |
| result.push("InspectorBackend.registerEvent(\"" + domain.domain + "." + event.name + "\", [" + paramsText.join(", ") + "]);"); |
| } |
| } |
| return result.join("\n"); |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| */ |
| InspectorBackendClass.Connection = function() |
| { |
| this._lastMessageId = 1; |
| this._pendingResponsesCount = 0; |
| this._agents = {}; |
| this._dispatchers = {}; |
| this._callbacks = {}; |
| this._initialize(InspectorBackend._agentPrototypes, InspectorBackend._dispatcherPrototypes); |
| this._isConnected = true; |
| } |
| |
| InspectorBackendClass.Connection.Events = { |
| Disconnected: "Disconnected", |
| } |
| |
| InspectorBackendClass.Connection.prototype = { |
| |
| /** |
| * @param {!Object.<string, !InspectorBackendClass.AgentPrototype>} agentPrototypes |
| * @param {!Object.<string, !InspectorBackendClass.DispatcherPrototype>} dispatcherPrototypes |
| */ |
| _initialize: function(agentPrototypes, dispatcherPrototypes) |
| { |
| for (var domain in agentPrototypes) { |
| this._agents[domain] = Object.create(agentPrototypes[domain]); |
| this._agents[domain].setConnection(this); |
| } |
| |
| for (var domain in dispatcherPrototypes) |
| this._dispatchers[domain] = Object.create(dispatcherPrototypes[domain]); |
| |
| }, |
| |
| /** |
| * @return {number} |
| */ |
| nextMessageId: function() |
| { |
| return this._lastMessageId++; |
| }, |
| |
| /** |
| * @param {string} domain |
| * @return {!InspectorBackendClass.AgentPrototype} |
| */ |
| agent: function(domain) |
| { |
| return this._agents[domain]; |
| }, |
| |
| /** |
| * @return {!Object.<string, !Object>} |
| */ |
| agentsMap: function() |
| { |
| return this._agents; |
| }, |
| |
| /** |
| * @param {string} domain |
| * @param {string} method |
| * @param {?Object} params |
| * @param {?function(*)} callback |
| */ |
| _wrapCallbackAndSendMessageObject: function(domain, method, params, callback) |
| { |
| if (!this._isConnected && callback) { |
| this._dispatchConnectionErrorResponse(domain, method, callback); |
| return; |
| } |
| |
| var messageObject = {}; |
| |
| var messageId = this.nextMessageId(); |
| messageObject.id = messageId; |
| |
| messageObject.method = method; |
| if (params) |
| messageObject.params = params; |
| |
| var wrappedCallback = this._wrap(callback, domain, method); |
| |
| if (InspectorBackendClass.Options.dumpInspectorProtocolMessages) |
| this._dumpProtocolMessage("frontend: " + JSON.stringify(messageObject)); |
| |
| this.sendMessage(messageObject); |
| ++this._pendingResponsesCount; |
| this._callbacks[messageId] = wrappedCallback; |
| }, |
| |
| /** |
| * @param {?function(*)} callback |
| * @param {string} method |
| * @param {string} domain |
| * @return {function(*)} |
| */ |
| _wrap: function(callback, domain, method) |
| { |
| if (!callback) |
| callback = function() {}; |
| |
| callback.methodName = method; |
| callback.domain = domain; |
| if (InspectorBackendClass.Options.dumpInspectorTimeStats) |
| callback.sendRequestTime = Date.now(); |
| |
| return callback; |
| }, |
| |
| /** |
| * @param {!Object} messageObject |
| */ |
| sendMessage: function(messageObject) |
| { |
| throw "Not implemented"; |
| }, |
| |
| /** |
| * @param {!Object|string} message |
| */ |
| dispatch: function(message) |
| { |
| if (InspectorBackendClass.Options.dumpInspectorProtocolMessages) |
| this._dumpProtocolMessage("backend: " + ((typeof message === "string") ? message : JSON.stringify(message))); |
| |
| var messageObject = /** @type {!Object} */ ((typeof message === "string") ? JSON.parse(message) : message); |
| |
| if ("id" in messageObject) { // just a response for some request |
| var callback = this._callbacks[messageObject.id]; |
| if (!callback) { |
| InspectorBackendClass.reportProtocolError("Protocol Error: the message with wrong id", messageObject); |
| return; |
| } |
| |
| var processingStartTime; |
| if (InspectorBackendClass.Options.dumpInspectorTimeStats) |
| processingStartTime = Date.now(); |
| |
| this.agent(callback.domain).dispatchResponse(messageObject, callback.methodName, callback); |
| --this._pendingResponsesCount; |
| delete this._callbacks[messageObject.id]; |
| |
| if (InspectorBackendClass.Options.dumpInspectorTimeStats) |
| console.log("time-stats: " + callback.methodName + " = " + (processingStartTime - callback.sendRequestTime) + " + " + (Date.now() - processingStartTime)); |
| |
| if (this._scripts && !this._pendingResponsesCount) |
| this.runAfterPendingDispatches(); |
| return; |
| } else { |
| if (messageObject.error) { |
| InspectorBackendClass.reportProtocolError("Generic message format error", messageObject); |
| return; |
| } |
| var method = messageObject.method.split("."); |
| var domainName = method[0]; |
| if (!(domainName in this._dispatchers)) { |
| InspectorBackendClass.reportProtocolError("Protocol Error: the message " + messageObject.method + " is for non-existing domain '" + domainName + "'", messageObject); |
| return; |
| } |
| |
| this._dispatchers[domainName].dispatch(method[1], messageObject); |
| } |
| |
| }, |
| |
| /** |
| * @param {string} domain |
| * @param {!Object} dispatcher |
| */ |
| registerDispatcher: function(domain, dispatcher) |
| { |
| if (!this._dispatchers[domain]) |
| return; |
| |
| this._dispatchers[domain].setDomainDispatcher(dispatcher); |
| }, |
| |
| /** |
| * @param {string=} script |
| */ |
| runAfterPendingDispatches: function(script) |
| { |
| if (!this._scripts) |
| this._scripts = []; |
| |
| if (script) |
| this._scripts.push(script); |
| |
| // Execute all promises. |
| setTimeout(function() { |
| if (!this._pendingResponsesCount) |
| this._executeAfterPendingDispatches(); |
| else |
| this.runAfterPendingDispatches(); |
| }.bind(this), 0); |
| }, |
| |
| _executeAfterPendingDispatches: function() |
| { |
| if (!this._pendingResponsesCount) { |
| var scripts = this._scripts; |
| this._scripts = []; |
| for (var id = 0; id < scripts.length; ++id) |
| scripts[id].call(this); |
| } |
| }, |
| |
| _dumpProtocolMessage: function(message) |
| { |
| console.log(message); |
| }, |
| |
| /** |
| * @protected |
| * @param {string} reason |
| */ |
| connectionClosed: function(reason) |
| { |
| this._isConnected = false; |
| this._runPendingCallbacks(); |
| this.dispatchEventToListeners(InspectorBackendClass.Connection.Events.Disconnected, {reason: reason}); |
| }, |
| |
| _runPendingCallbacks: function() |
| { |
| var keys = Object.keys(this._callbacks).map(function(num) { return parseInt(num, 10); }); |
| for (var i = 0; i < keys.length; ++i) { |
| var callback = this._callbacks[keys[i]]; |
| this._dispatchConnectionErrorResponse(callback.domain, callback.methodName, callback); |
| } |
| this._callbacks = {}; |
| }, |
| |
| /** |
| * @param {string} domain |
| * @param {string} methodName |
| * @param {function(*)} callback |
| */ |
| _dispatchConnectionErrorResponse: function(domain, methodName, callback) |
| { |
| var error = { message: "Connection is closed, can't dispatch pending " + methodName, code: InspectorBackendClass._DevToolsErrorCode, data: null}; |
| var messageObject = {error: error}; |
| setTimeout(InspectorBackendClass.AgentPrototype.prototype.dispatchResponse.bind(this.agent(domain), messageObject, methodName, callback), 0); |
| }, |
| |
| /** |
| * @return {boolean} |
| */ |
| isClosed: function() |
| { |
| return !this._isConnected; |
| }, |
| |
| /** |
| * @param {!Array.<string>} domains |
| */ |
| suppressErrorsForDomains: function(domains) |
| { |
| domains.forEach(function(domain) { this._agents[domain].suppressErrorLogging(); }, this); |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| |
| } |
| |
| /** |
| * @constructor |
| * @extends {InspectorBackendClass.Connection} |
| */ |
| InspectorBackendClass.MainConnection = function() |
| { |
| InspectorBackendClass.Connection.call(this); |
| InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this); |
| InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this); |
| } |
| |
| InspectorBackendClass.MainConnection.prototype = { |
| /** |
| * @override |
| * @param {!Object} messageObject |
| */ |
| sendMessage: function(messageObject) |
| { |
| var message = JSON.stringify(messageObject); |
| InspectorFrontendHost.sendMessageToBackend(message); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _dispatchMessage: function(event) |
| { |
| this.dispatch(/** @type {string} */ (event.data)); |
| }, |
| |
| /** |
| * @param {!WebInspector.Event} event |
| */ |
| _dispatchMessageChunk: function(event) |
| { |
| var messageChunk = /** @type {string} */ (event.data["messageChunk"]); |
| var messageSize = /** @type {number} */ (event.data["messageSize"]); |
| if (messageSize) { |
| this._messageBuffer = ""; |
| this._messageSize = messageSize; |
| } |
| this._messageBuffer += messageChunk; |
| if (this._messageBuffer.length === this._messageSize) { |
| this.dispatch(this._messageBuffer); |
| this._messageBuffer = ""; |
| this._messageSize = 0; |
| } |
| }, |
| |
| __proto__: InspectorBackendClass.Connection.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {InspectorBackendClass.Connection} |
| * @param {string} url |
| * @param {function(!InspectorBackendClass.Connection)} onConnectionReady |
| */ |
| InspectorBackendClass.WebSocketConnection = function(url, onConnectionReady) |
| { |
| InspectorBackendClass.Connection.call(this); |
| this._socket = new WebSocket(url); |
| this._socket.onmessage = this._onMessage.bind(this); |
| this._socket.onerror = this._onError.bind(this); |
| this._socket.onopen = onConnectionReady.bind(null, this); |
| this._socket.onclose = this.connectionClosed.bind(this, "websocket_closed"); |
| } |
| |
| /** |
| * @param {string} url |
| * @param {function(!InspectorBackendClass.Connection)} onConnectionReady |
| */ |
| InspectorBackendClass.WebSocketConnection.Create = function(url, onConnectionReady) |
| { |
| new InspectorBackendClass.WebSocketConnection(url, onConnectionReady); |
| } |
| |
| InspectorBackendClass.WebSocketConnection.prototype = { |
| |
| /** |
| * @param {!MessageEvent} message |
| */ |
| _onMessage: function(message) |
| { |
| var data = /** @type {string} */ (message.data); |
| this.dispatch(data); |
| }, |
| |
| /** |
| * @param {!Event} error |
| */ |
| _onError: function(error) |
| { |
| console.error(error); |
| }, |
| |
| /** |
| * @override |
| * @param {!Object} messageObject |
| */ |
| sendMessage: function(messageObject) |
| { |
| var message = JSON.stringify(messageObject); |
| this._socket.send(message); |
| }, |
| |
| __proto__: InspectorBackendClass.Connection.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {InspectorBackendClass.Connection} |
| */ |
| InspectorBackendClass.StubConnection = function() |
| { |
| InspectorBackendClass.Connection.call(this); |
| } |
| |
| InspectorBackendClass.StubConnection.prototype = { |
| /** |
| * @override |
| * @param {!Object} messageObject |
| */ |
| sendMessage: function(messageObject) |
| { |
| setTimeout(this._echoResponse.bind(this, messageObject), 0); |
| }, |
| |
| /** |
| * @param {!Object} messageObject |
| */ |
| _echoResponse: function(messageObject) |
| { |
| this.dispatch(messageObject); |
| }, |
| |
| __proto__: InspectorBackendClass.Connection.prototype |
| } |
| |
| /** |
| * @constructor |
| * @param {string} domain |
| */ |
| InspectorBackendClass.AgentPrototype = function(domain) |
| { |
| this._replyArgs = {}; |
| this._hasErrorData = {}; |
| this._domain = domain; |
| this._suppressErrorLogging = false; |
| this._promisified = domain in InspectorBackendClass.AgentPrototype.PromisifiedDomains; |
| } |
| |
| InspectorBackendClass.AgentPrototype.PromisifiedDomains = { |
| "Accessibility": true, |
| "CSS": true, |
| "Emulation": true, |
| "Profiler": true |
| } |
| |
| InspectorBackendClass.AgentPrototype.prototype = { |
| /** |
| * @param {!InspectorBackendClass.Connection} connection |
| */ |
| setConnection: function(connection) |
| { |
| this._connection = connection; |
| }, |
| |
| /** |
| * @param {string} methodName |
| * @param {!Array.<!Object>} signature |
| * @param {!Array.<string>} replyArgs |
| * @param {boolean} hasErrorData |
| */ |
| registerCommand: function(methodName, signature, replyArgs, hasErrorData) |
| { |
| var domainAndMethod = this._domain + "." + methodName; |
| |
| /** |
| * @param {...*} vararg |
| * @this {InspectorBackendClass.AgentPrototype} |
| */ |
| function sendMessage(vararg) |
| { |
| var params = Array.prototype.slice.call(arguments); |
| InspectorBackendClass.AgentPrototype.prototype._sendMessageToBackend.call(this, domainAndMethod, signature, params); |
| } |
| |
| /** |
| * @param {...*} vararg |
| * @this {InspectorBackendClass.AgentPrototype} |
| * @return {!Promise.<*>} |
| */ |
| function sendMessagePromise(vararg) |
| { |
| var params = Array.prototype.slice.call(arguments); |
| return InspectorBackendClass.AgentPrototype.prototype._sendMessageToBackendPromise.call(this, domainAndMethod, signature, replyArgs, params); |
| } |
| |
| this[methodName] = this._promisified ? sendMessagePromise : sendMessage; |
| |
| /** |
| * @param {...*} vararg |
| * @this {InspectorBackendClass.AgentPrototype} |
| */ |
| function invoke(vararg) |
| { |
| var params = [domainAndMethod].concat(Array.prototype.slice.call(arguments)); |
| InspectorBackendClass.AgentPrototype.prototype._invoke.apply(this, params); |
| } |
| |
| this["invoke_" + methodName] = invoke; |
| |
| this._replyArgs[domainAndMethod] = replyArgs; |
| if (hasErrorData) |
| this._hasErrorData[domainAndMethod] = true; |
| }, |
| |
| /** |
| * @param {string} method |
| * @param {!Array.<!Object>} signature |
| * @param {!Array.<*>} args |
| * @param {boolean} allowExtraUndefinedArg |
| * @param {function(string)} errorCallback |
| * @return {?Object} |
| */ |
| _prepareParameters: function(method, signature, args, allowExtraUndefinedArg, errorCallback) |
| { |
| var params = {}; |
| var hasParams = false; |
| for (var i = 0; i < signature.length; ++i) { |
| var param = signature[i]; |
| var paramName = param["name"]; |
| var typeName = param["type"]; |
| var optionalFlag = param["optional"]; |
| |
| if (!args.length && !optionalFlag) { |
| errorCallback("Protocol Error: Invalid number of arguments for method '" + method + "' call. It must have the following arguments '" + JSON.stringify(signature) + "'."); |
| return null; |
| } |
| |
| var value = args.shift(); |
| if (optionalFlag && typeof value === "undefined") |
| continue; |
| |
| if (typeof value !== typeName) { |
| errorCallback("Protocol Error: Invalid type of argument '" + paramName + "' for method '" + method + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'."); |
| return null; |
| } |
| |
| params[paramName] = value; |
| hasParams = true; |
| } |
| |
| if (args.length === 1 && (!allowExtraUndefinedArg || (typeof args[0] !== "undefined"))) { |
| errorCallback("Protocol Error: Optional callback argument for method '" + method + "' call must be a function but its type is '" + typeof args[0] + "'."); |
| return null; |
| } |
| |
| if (args.length > 1) { |
| errorCallback("Protocol Error: Extra " + args.length + " arguments in a call to method '" + method + "'."); |
| return null; |
| } |
| |
| return hasParams ? params : null |
| }, |
| |
| /** |
| * @param {string} method |
| * @param {!Array.<!Object>} signature |
| * @param {!Array.<*>} args |
| */ |
| _sendMessageToBackend: function(method, signature, args) |
| { |
| var errorMessage; |
| /** |
| * @param {string} message |
| */ |
| function onError(message) |
| { |
| console.error(message) |
| errorMessage = message; |
| } |
| var callback = (args.length && typeof args.peekLast() === "function") ? args.pop() : null; |
| var params = this._prepareParameters(method, signature, args, !callback, onError); |
| if (errorMessage) |
| return; |
| this._connection._wrapCallbackAndSendMessageObject(this._domain, method, params, callback); |
| }, |
| |
| /** |
| * @param {string} method |
| * @param {!Array.<!Object>} signature |
| * @param {!Array.<string>} replyArgs |
| * @param {!Array.<*>} args |
| * @return {!Promise.<*>} |
| */ |
| _sendMessageToBackendPromise: function(method, signature, replyArgs, args) |
| { |
| var errorMessage; |
| /** |
| * @param {string} message |
| */ |
| function onError(message) |
| { |
| console.error(message) |
| errorMessage = message; |
| } |
| var userCallback = (args.length && typeof args.peekLast() === "function") ? args.pop() : null; |
| var params = this._prepareParameters(method, signature, args, false, onError); |
| if (errorMessage) |
| return Promise.reject(new Error(errorMessage)); |
| else |
| return new Promise(promiseAction.bind(this)); |
| |
| /** |
| * @param {function(?)} resolve |
| * @param {function(!Error)} reject |
| * @this {InspectorBackendClass.AgentPrototype} |
| */ |
| function promiseAction(resolve, reject) |
| { |
| /** |
| * @param {...*} vararg |
| */ |
| function callback(vararg) |
| { |
| var result = userCallback ? userCallback.apply(null, arguments) : undefined; |
| resolve(result); |
| } |
| this._connection._wrapCallbackAndSendMessageObject(this._domain, method, params, callback); |
| } |
| }, |
| |
| /** |
| * @param {string} method |
| * @param {?Object} args |
| * @param {?function(*)} callback |
| */ |
| _invoke: function(method, args, callback) |
| { |
| this._connection._wrapCallbackAndSendMessageObject(this._domain, method, args, callback); |
| }, |
| |
| /** |
| * @param {!Object} messageObject |
| * @param {string} methodName |
| * @param {function(*)|function(?Protocol.Error, ?Object)} callback |
| */ |
| dispatchResponse: function(messageObject, methodName, callback) |
| { |
| if (messageObject.error && messageObject.error.code !== InspectorBackendClass._DevToolsErrorCode && !InspectorBackendClass.Options.suppressRequestErrors && !this._suppressErrorLogging) { |
| var id = InspectorFrontendHost.isUnderTest() ? "##" : messageObject.id; |
| console.error("Request with id = " + id + " failed. " + JSON.stringify(messageObject.error)); |
| } |
| |
| var argumentsArray = []; |
| argumentsArray[0] = messageObject.error ? messageObject.error.message: null; |
| |
| if (this._hasErrorData[methodName]) |
| argumentsArray[1] = messageObject.error ? messageObject.error.data : null; |
| |
| if (messageObject.result) { |
| var paramNames = this._replyArgs[methodName] || []; |
| for (var i = 0; i < paramNames.length; ++i) |
| argumentsArray.push(messageObject.result[paramNames[i]]); |
| } |
| |
| callback.apply(null, argumentsArray); |
| }, |
| |
| suppressErrorLogging: function() |
| { |
| this._suppressErrorLogging = true; |
| } |
| } |
| |
| /** |
| * @constructor |
| */ |
| InspectorBackendClass.DispatcherPrototype = function() |
| { |
| this._eventArgs = {}; |
| this._dispatcher = null; |
| } |
| |
| InspectorBackendClass.DispatcherPrototype.prototype = { |
| |
| /** |
| * @param {string} eventName |
| * @param {!Object} params |
| */ |
| registerEvent: function(eventName, params) |
| { |
| this._eventArgs[eventName] = params; |
| }, |
| |
| /** |
| * @param {!Object} dispatcher |
| */ |
| setDomainDispatcher: function(dispatcher) |
| { |
| this._dispatcher = dispatcher; |
| }, |
| |
| /** |
| * @param {string} functionName |
| * @param {!Object} messageObject |
| */ |
| dispatch: function(functionName, messageObject) |
| { |
| if (!this._dispatcher) |
| return; |
| |
| if (!(functionName in this._dispatcher)) { |
| InspectorBackendClass.reportProtocolError("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'", messageObject); |
| return; |
| } |
| |
| if (!this._eventArgs[messageObject.method]) { |
| InspectorBackendClass.reportProtocolError("Protocol Error: Attempted to dispatch an unspecified method '" + messageObject.method + "'", messageObject); |
| return; |
| } |
| |
| var params = []; |
| if (messageObject.params) { |
| var paramNames = this._eventArgs[messageObject.method]; |
| for (var i = 0; i < paramNames.length; ++i) |
| params.push(messageObject.params[paramNames[i]]); |
| } |
| |
| var processingStartTime; |
| if (InspectorBackendClass.Options.dumpInspectorTimeStats) |
| processingStartTime = Date.now(); |
| |
| this._dispatcher[functionName].apply(this._dispatcher, params); |
| |
| if (InspectorBackendClass.Options.dumpInspectorTimeStats) |
| console.log("time-stats: " + messageObject.method + " = " + (Date.now() - processingStartTime)); |
| } |
| } |
| |
| InspectorBackendClass.Options = { |
| dumpInspectorTimeStats: false, |
| dumpInspectorProtocolMessages: false, |
| suppressRequestErrors: false |
| } |
| |
| InspectorBackend = new InspectorBackendClass(); |