Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  */
     34 function InspectorBackendClass()
     35 {
     36     this._connection = null;
     37     this._agentPrototypes = {};
     38     this._dispatcherPrototypes = {};
     39     this._initialized = false;
     40     this._enums = {};
     41     this._initProtocolAgentsConstructor();
     42 }
     43 
     44 InspectorBackendClass.prototype = {
     45 
     46     _initProtocolAgentsConstructor: function()
     47     {
     48         window.Protocol = {};
     49 
     50         /**
     51          * @constructor
     52          * @param {!Object.<string, !Object>} agentsMap
     53          */
     54         window.Protocol.Agents = function(agentsMap) {
     55             this._agentsMap = agentsMap;
     56         };
     57     },
     58 
     59     /**
     60      * @param {string} domain
     61      */
     62     _addAgentGetterMethodToProtocolAgentsPrototype: function(domain)
     63     {
     64         var upperCaseLength = 0;
     65         while (upperCaseLength < domain.length && domain[upperCaseLength].toLowerCase() !== domain[upperCaseLength])
     66             ++upperCaseLength;
     67 
     68         var methodName = domain.substr(0, upperCaseLength).toLowerCase() + domain.slice(upperCaseLength) + "Agent";
     69 
     70         /**
     71          * @this {Protocol.Agents}
     72          */
     73         function agentGetter()
     74         {
     75             return this._agentsMap[domain];
     76         }
     77 
     78         window.Protocol.Agents.prototype[methodName] = agentGetter;
     79 
     80         /**
     81          * @this {Protocol.Agents}
     82          */
     83         function registerDispatcher(dispatcher)
     84         {
     85             this.registerDispatcher(domain, dispatcher)
     86         }
     87 
     88         window.Protocol.Agents.prototype["register" + domain + "Dispatcher"] = registerDispatcher;
     89     },
     90 
     91     /**
     92      * @return {!InspectorBackendClass.Connection}
     93      */
     94     connection: function()
     95     {
     96         if (!this._connection)
     97             throw "Main connection was not initialized";
     98         return this._connection;
     99     },
    100 
    101     /**
    102      * @param {!InspectorBackendClass.MainConnection} connection
    103      */
    104     setConnection: function(connection)
    105     {
    106         this._connection = connection;
    107 
    108         this._connection.registerAgentsOn(window);
    109         for (var type in this._enums) {
    110             var domainAndMethod = type.split(".");
    111             window[domainAndMethod[0] + "Agent"][domainAndMethod[1]] = this._enums[type];
    112         }
    113     },
    114 
    115     /**
    116      * @param {string} domain
    117      * @return {!InspectorBackendClass.AgentPrototype}
    118      */
    119     _agentPrototype: function(domain)
    120     {
    121         if (!this._agentPrototypes[domain]) {
    122             this._agentPrototypes[domain] = new InspectorBackendClass.AgentPrototype(domain);
    123             this._addAgentGetterMethodToProtocolAgentsPrototype(domain);
    124         }
    125 
    126         return this._agentPrototypes[domain];
    127     },
    128 
    129     /**
    130      * @param {string} domain
    131      * @return {!InspectorBackendClass.DispatcherPrototype}
    132      */
    133     _dispatcherPrototype: function(domain)
    134     {
    135         if (!this._dispatcherPrototypes[domain])
    136             this._dispatcherPrototypes[domain] = new InspectorBackendClass.DispatcherPrototype();
    137         return this._dispatcherPrototypes[domain];
    138     },
    139 
    140     /**
    141      * @param {string} method
    142      * @param {!Array.<!Object>} signature
    143      * @param {!Array.<string>} replyArgs
    144      * @param {boolean} hasErrorData
    145      */
    146     registerCommand: function(method, signature, replyArgs, hasErrorData)
    147     {
    148         var domainAndMethod = method.split(".");
    149         this._agentPrototype(domainAndMethod[0]).registerCommand(domainAndMethod[1], signature, replyArgs, hasErrorData);
    150         this._initialized = true;
    151     },
    152 
    153     /**
    154      * @param {string} type
    155      * @param {!Object} values
    156      */
    157     registerEnum: function(type, values)
    158     {
    159         this._enums[type] = values;
    160         this._initialized = true;
    161     },
    162 
    163     /**
    164      * @param {string} eventName
    165      * @param {!Object} params
    166      */
    167     registerEvent: function(eventName, params)
    168     {
    169         var domain = eventName.split(".")[0];
    170         this._dispatcherPrototype(domain).registerEvent(eventName, params);
    171         this._initialized = true;
    172     },
    173 
    174     /**
    175      * @param {string} domain
    176      * @param {!Object} dispatcher
    177      */
    178     registerDomainDispatcher: function(domain, dispatcher)
    179     {
    180         this._connection.registerDispatcher(domain, dispatcher);
    181     },
    182 
    183     /**
    184      * @param {string} jsonUrl
    185      */
    186     loadFromJSONIfNeeded: function(jsonUrl)
    187     {
    188         if (this._initialized)
    189             return;
    190 
    191         var xhr = new XMLHttpRequest();
    192         xhr.open("GET", jsonUrl, false);
    193         xhr.send(null);
    194 
    195         var schema = JSON.parse(xhr.responseText);
    196         var code = InspectorBackendClass._generateCommands(schema);
    197         eval(code);
    198     },
    199 
    200     /**
    201      * @param {function(T)} clientCallback
    202      * @param {string} errorPrefix
    203      * @param {function(new:T,S)=} constructor
    204      * @param {T=} defaultValue
    205      * @return {function(?string, S)}
    206      * @template T,S
    207      */
    208     wrapClientCallback: function(clientCallback, errorPrefix, constructor, defaultValue)
    209     {
    210         /**
    211          * @param {?string} error
    212          * @param {S} value
    213          * @template S
    214          */
    215         function callbackWrapper(error, value)
    216         {
    217             if (error) {
    218                 console.error(errorPrefix + error);
    219                 clientCallback(defaultValue);
    220                 return;
    221             }
    222             if (constructor)
    223                 clientCallback(new constructor(value));
    224             else
    225                 clientCallback(value);
    226         }
    227         return callbackWrapper;
    228     }
    229 }
    230 
    231 /**
    232  * @param {*} schema
    233  * @return {string}
    234  */
    235 InspectorBackendClass._generateCommands = function(schema) {
    236     var jsTypes = { integer: "number", array: "object" };
    237     var rawTypes = {};
    238     var result = [];
    239 
    240     var domains = schema["domains"] || [];
    241     for (var i = 0; i < domains.length; ++i) {
    242         var domain = domains[i];
    243         for (var j = 0; domain.types && j < domain.types.length; ++j) {
    244             var type = domain.types[j];
    245             rawTypes[domain.domain + "." + type.id] = jsTypes[type.type] || type.type;
    246         }
    247     }
    248 
    249     function toUpperCase(groupIndex, group0, group1)
    250     {
    251         return [group0, group1][groupIndex].toUpperCase();
    252     }
    253     function generateEnum(enumName, items)
    254     {
    255         var members = []
    256         for (var m = 0; m < items.length; ++m) {
    257             var value = items[m];
    258             var name = value.replace(/-(\w)/g, toUpperCase.bind(null, 1)).toTitleCase();
    259             name = name.replace(/HTML|XML|WML|API/ig, toUpperCase.bind(null, 0));
    260             members.push(name + ": \"" + value +"\"");
    261         }
    262         return "InspectorBackend.registerEnum(\"" + enumName + "\", {" + members.join(", ") + "});";
    263     }
    264 
    265     for (var i = 0; i < domains.length; ++i) {
    266         var domain = domains[i];
    267 
    268         var types = domain["types"] || [];
    269         for (var j = 0; j < types.length; ++j) {
    270             var type = types[j];
    271             if ((type["type"] === "string") && type["enum"])
    272                 result.push(generateEnum(domain.domain + "." + type.id, type["enum"]));
    273             else if (type["type"] === "object") {
    274                 var properties = type["properties"] || [];
    275                 for (var k = 0; k < properties.length; ++k) {
    276                     var property = properties[k];
    277                     if ((property["type"] === "string") && property["enum"])
    278                         result.push(generateEnum(domain.domain + "." + type.id + property["name"].toTitleCase(), property["enum"]));
    279                 }
    280             }
    281         }
    282 
    283         var commands = domain["commands"] || [];
    284         for (var j = 0; j < commands.length; ++j) {
    285             var command = commands[j];
    286             var parameters = command["parameters"];
    287             var paramsText = [];
    288             for (var k = 0; parameters && k < parameters.length; ++k) {
    289                 var parameter = parameters[k];
    290 
    291                 var type;
    292                 if (parameter.type)
    293                     type = jsTypes[parameter.type] || parameter.type;
    294                 else {
    295                     var ref = parameter["$ref"];
    296                     if (ref.indexOf(".") !== -1)
    297                         type = rawTypes[ref];
    298                     else
    299                         type = rawTypes[domain.domain + "." + ref];
    300                 }
    301 
    302                 var text = "{\"name\": \"" + parameter.name + "\", \"type\": \"" + type + "\", \"optional\": " + (parameter.optional ? "true" : "false") + "}";
    303                 paramsText.push(text);
    304             }
    305 
    306             var returnsText = [];
    307             var returns = command["returns"] || [];
    308             for (var k = 0; k < returns.length; ++k) {
    309                 var parameter = returns[k];
    310                 returnsText.push("\"" + parameter.name + "\"");
    311             }
    312             var hasErrorData = String(Boolean(command.error));
    313             result.push("InspectorBackend.registerCommand(\"" + domain.domain + "." + command.name + "\", [" + paramsText.join(", ") + "], [" + returnsText.join(", ") + "], " + hasErrorData + ");");
    314         }
    315 
    316         for (var j = 0; domain.events && j < domain.events.length; ++j) {
    317             var event = domain.events[j];
    318             var paramsText = [];
    319             for (var k = 0; event.parameters && k < event.parameters.length; ++k) {
    320                 var parameter = event.parameters[k];
    321                 paramsText.push("\"" + parameter.name + "\"");
    322             }
    323             result.push("InspectorBackend.registerEvent(\"" + domain.domain + "." + event.name + "\", [" + paramsText.join(", ") + "]);");
    324         }
    325 
    326         result.push("InspectorBackend.register" + domain.domain + "Dispatcher = InspectorBackend.registerDomainDispatcher.bind(InspectorBackend, \"" + domain.domain + "\");");
    327     }
    328     return result.join("\n");
    329 }
    330 
    331 /**
    332  *  @constructor
    333  *  @extends {WebInspector.Object}
    334  */
    335 InspectorBackendClass.Connection = function()
    336 {
    337     this._lastMessageId = 1;
    338     this._pendingResponsesCount = 0;
    339     this._agents = {};
    340     this._dispatchers = {};
    341     this._callbacks = {};
    342     this._initialize(InspectorBackend._agentPrototypes, InspectorBackend._dispatcherPrototypes);
    343 }
    344 
    345 InspectorBackendClass.Connection.Events = {
    346     Disconnected: "Disconnected",
    347 }
    348 
    349 InspectorBackendClass.Connection.prototype = {
    350 
    351     /**
    352      * @param {!Object.<string, !InspectorBackendClass.AgentPrototype>} agentPrototypes
    353      * @param {!Object.<string, !InspectorBackendClass.DispatcherPrototype>} dispatcherPrototypes
    354      */
    355     _initialize: function(agentPrototypes, dispatcherPrototypes)
    356     {
    357         for (var domain in agentPrototypes) {
    358             this._agents[domain] = Object.create(agentPrototypes[domain]);
    359             this._agents[domain].setConnection(this);
    360         }
    361 
    362         for (var domain in dispatcherPrototypes)
    363             this._dispatchers[domain] = Object.create(dispatcherPrototypes[domain])
    364 
    365     },
    366 
    367     /**
    368      * @param {!Object} object
    369      */
    370     registerAgentsOn: function(object)
    371     {
    372         for (var domain in this._agents)
    373             object[domain + "Agent"]  = this._agents[domain];
    374     },
    375 
    376     /**
    377      * @return {number}
    378      */
    379     nextMessageId: function()
    380     {
    381         return this._lastMessageId++;
    382     },
    383 
    384     /**
    385      * @param {string} domain
    386      * @return {!InspectorBackendClass.AgentPrototype}
    387      */
    388     agent: function(domain)
    389     {
    390         return this._agents[domain];
    391     },
    392 
    393     /**
    394      * @return {!Object.<string, !Object>}
    395      */
    396     agentsMap: function()
    397     {
    398         return this._agents;
    399     },
    400 
    401     /**
    402      * @param {string} domain
    403      * @param {string} method
    404      * @param {?Object} params
    405      * @param {?function(*)} callback
    406      * @private
    407      */
    408     _wrapCallbackAndSendMessageObject: function(domain, method, params, callback)
    409     {
    410         var messageObject = {};
    411 
    412         var messageId = this.nextMessageId();
    413         messageObject.id = messageId;
    414 
    415         messageObject.method = method;
    416         if (params)
    417             messageObject.params = params;
    418 
    419         var wrappedCallback = this._wrap(callback, domain, method);
    420 
    421         if (InspectorBackendClass.Options.dumpInspectorProtocolMessages)
    422             this._dumpProtocolMessage("frontend: " + JSON.stringify(messageObject));
    423 
    424         this.sendMessage(messageObject);
    425         ++this._pendingResponsesCount;
    426         this._callbacks[messageId] = wrappedCallback;
    427     },
    428 
    429     /**
    430      * @param {?function(*)} callback
    431      * @param {string} method
    432      * @param {string} domain
    433      * @return {!function(*)}
    434      */
    435     _wrap: function(callback, domain, method)
    436     {
    437         if (!callback)
    438             callback = function() {};
    439 
    440         callback.methodName = method;
    441         callback.domain = domain;
    442         if (InspectorBackendClass.Options.dumpInspectorTimeStats)
    443             callback.sendRequestTime = Date.now();
    444 
    445         return callback;
    446     },
    447 
    448     /**
    449      * @param {!Object} messageObject
    450      */
    451     sendMessage: function(messageObject)
    452     {
    453         throw "Not implemented";
    454     },
    455 
    456     /**
    457      * @param {!Object} messageObject
    458      */
    459     reportProtocolError: function(messageObject)
    460     {
    461         console.error("Protocol Error: the message with wrong id. Message =  " + JSON.stringify(messageObject));
    462     },
    463 
    464     /**
    465      * @param {!Object|string} message
    466      */
    467     dispatch: function(message)
    468     {
    469         if (InspectorBackendClass.Options.dumpInspectorProtocolMessages)
    470             this._dumpProtocolMessage("backend: " + ((typeof message === "string") ? message : JSON.stringify(message)));
    471 
    472         var messageObject = /** @type {!Object} */ ((typeof message === "string") ? JSON.parse(message) : message);
    473 
    474         if ("id" in messageObject) { // just a response for some request
    475 
    476             var callback = this._callbacks[messageObject.id];
    477             if (!callback) {
    478                 this.reportProtocolError(messageObject);
    479                 return;
    480             }
    481 
    482             var processingStartTime;
    483             if (InspectorBackendClass.Options.dumpInspectorTimeStats)
    484                 processingStartTime = Date.now();
    485 
    486             this.agent(callback.domain).dispatchResponse(messageObject.id, messageObject, callback.methodName, callback);
    487             --this._pendingResponsesCount;
    488             delete this._callbacks[messageObject.id];
    489 
    490             if (InspectorBackendClass.Options.dumpInspectorTimeStats)
    491                 console.log("time-stats: " + callback.methodName + " = " + (processingStartTime - callback.sendRequestTime) + " + " + (Date.now() - processingStartTime));
    492 
    493             if (this._scripts && !this._pendingResponsesCount)
    494                 this.runAfterPendingDispatches();
    495             return;
    496         } else {
    497             var method = messageObject.method.split(".");
    498             var domainName = method[0];
    499             if (!(domainName in this._dispatchers)) {
    500                 console.error("Protocol Error: the message " + messageObject.method + " is for non-existing domain '" + domainName + "'");
    501                 return;
    502             }
    503 
    504             this._dispatchers[domainName].dispatch(method[1], messageObject);
    505 
    506         }
    507 
    508     },
    509 
    510     /**
    511      * @param {string} domain
    512      * @param {!Object} dispatcher
    513      */
    514     registerDispatcher: function(domain, dispatcher)
    515     {
    516         if (!this._dispatchers[domain])
    517             return;
    518 
    519         this._dispatchers[domain].setDomainDispatcher(dispatcher);
    520     },
    521 
    522     /**
    523      * @param {string=} script
    524      */
    525     runAfterPendingDispatches: function(script)
    526     {
    527         if (!this._scripts)
    528             this._scripts = [];
    529 
    530         if (script)
    531             this._scripts.push(script);
    532 
    533         if (!this._pendingResponsesCount) {
    534             var scripts = this._scripts;
    535             this._scripts = []
    536             for (var id = 0; id < scripts.length; ++id)
    537                 scripts[id].call(this);
    538         }
    539     },
    540 
    541     /**
    542      * @param {string} reason
    543      */
    544     fireDisconnected: function(reason)
    545     {
    546         this.dispatchEventToListeners(InspectorBackendClass.Connection.Events.Disconnected, {reason: reason});
    547     },
    548 
    549     _dumpProtocolMessage: function(message)
    550     {
    551         console.log(message);
    552     },
    553 
    554     __proto__: WebInspector.Object.prototype
    555 
    556 }
    557 
    558 /**
    559  * @constructor
    560  * @extends {InspectorBackendClass.Connection}
    561  * @param {!function(!InspectorBackendClass.Connection)} onConnectionReady
    562  */
    563 InspectorBackendClass.MainConnection = function(onConnectionReady)
    564 {
    565     InspectorBackendClass.Connection.call(this);
    566     onConnectionReady(this);
    567 }
    568 
    569 InspectorBackendClass.MainConnection.prototype = {
    570 
    571     /**
    572      * @param {!Object} messageObject
    573      */
    574     sendMessage: function(messageObject)
    575     {
    576         var message = JSON.stringify(messageObject);
    577         InspectorFrontendHost.sendMessageToBackend(message);
    578     },
    579 
    580     __proto__: InspectorBackendClass.Connection.prototype
    581 }
    582 
    583 /**
    584  * @constructor
    585  * @extends {InspectorBackendClass.Connection}
    586  * @param {string} url
    587  * @param {!function(!InspectorBackendClass.Connection)} onConnectionReady
    588  */
    589 InspectorBackendClass.WebSocketConnection = function(url, onConnectionReady)
    590 {
    591     InspectorBackendClass.Connection.call(this);
    592     this._socket = new WebSocket(url);
    593     this._socket.onmessage = this._onMessage.bind(this);
    594     this._socket.onerror = this._onError.bind(this);
    595     this._socket.onopen = onConnectionReady.bind(null, this);
    596     this._socket.onclose = this.fireDisconnected.bind(this, "websocket_closed");
    597 }
    598 
    599 InspectorBackendClass.WebSocketConnection.prototype = {
    600 
    601     /**
    602      * @param {!MessageEvent} message
    603      */
    604     _onMessage: function(message)
    605     {
    606         var data = /** @type {string} */ (message.data)
    607         this.dispatch(data);
    608     },
    609 
    610     /**
    611      * @param {!Event} error
    612      */
    613     _onError: function(error)
    614     {
    615         console.error(error);
    616     },
    617 
    618     /**
    619      * @param {!Object} messageObject
    620      */
    621     sendMessage: function(messageObject)
    622     {
    623         var message = JSON.stringify(messageObject);
    624         this._socket.send(message);
    625     },
    626 
    627     __proto__: InspectorBackendClass.Connection.prototype
    628 }
    629 
    630 
    631 /**
    632  * @constructor
    633  * @extends {InspectorBackendClass.Connection}
    634  * @param {!function(!InspectorBackendClass.Connection)} onConnectionReady
    635  */
    636 InspectorBackendClass.StubConnection = function(onConnectionReady)
    637 {
    638     InspectorBackendClass.Connection.call(this);
    639     onConnectionReady(this);
    640 }
    641 
    642 InspectorBackendClass.StubConnection.prototype = {
    643 
    644     /**
    645      * @param {!Object} messageObject
    646      */
    647     sendMessage: function(messageObject)
    648     {
    649         var message = JSON.stringify(messageObject);
    650         setTimeout(this._echoResponse.bind(this, messageObject), 0);
    651     },
    652 
    653     /**
    654      * @param {!Object} messageObject
    655      */
    656     _echoResponse: function(messageObject)
    657     {
    658         this.dispatch(messageObject)
    659     },
    660 
    661     __proto__: InspectorBackendClass.Connection.prototype
    662 }
    663 
    664 /**
    665  * @constructor
    666  * @param {string} domain
    667  */
    668 InspectorBackendClass.AgentPrototype = function(domain)
    669 {
    670     this._replyArgs = {};
    671     this._hasErrorData = {};
    672     this._domain = domain;
    673 }
    674 
    675 InspectorBackendClass.AgentPrototype.prototype = {
    676 
    677     /**
    678      * @param {!InspectorBackendClass.Connection} connection
    679      */
    680     setConnection: function(connection)
    681     {
    682         this._connection = connection;
    683     },
    684 
    685     /**
    686      * @param {string} methodName
    687      * @param {!Array.<!Object>} signature
    688      * @param {!Array.<string>} replyArgs
    689      * @param {boolean} hasErrorData
    690      */
    691     registerCommand: function(methodName, signature, replyArgs, hasErrorData)
    692     {
    693         var domainAndMethod = this._domain + "." + methodName;
    694 
    695         /**
    696          * @this {InspectorBackendClass.AgentPrototype}
    697          */
    698         function sendMessage(vararg)
    699         {
    700             var params = [domainAndMethod, signature].concat(Array.prototype.slice.call(arguments));
    701             InspectorBackendClass.AgentPrototype.prototype._sendMessageToBackend.apply(this, params);
    702         }
    703 
    704         this[methodName] = sendMessage;
    705 
    706         /**
    707          * @this {InspectorBackendClass.AgentPrototype}
    708          */
    709         function invoke(vararg)
    710         {
    711             var params = [domainAndMethod].concat(Array.prototype.slice.call(arguments));
    712             InspectorBackendClass.AgentPrototype.prototype._invoke.apply(this, params);
    713         }
    714 
    715         this["invoke_" + methodName] = invoke;
    716 
    717         this._replyArgs[domainAndMethod] = replyArgs;
    718         if (hasErrorData)
    719             this._hasErrorData[domainAndMethod] = true;
    720 
    721     },
    722 
    723     /**
    724      * @param {string} method
    725      * @param {!Array.<!Object>} signature
    726      * @param {*} vararg
    727      * @private
    728      */
    729     _sendMessageToBackend: function(method, signature, vararg)
    730     {
    731         var args = Array.prototype.slice.call(arguments, 2);
    732         var callback = (args.length && typeof args[args.length - 1] === "function") ? args.pop() : null;
    733 
    734         var params = {};
    735         var hasParams = false;
    736         for (var i = 0; i < signature.length; ++i) {
    737             var param = signature[i];
    738             var paramName = param["name"];
    739             var typeName = param["type"];
    740             var optionalFlag = param["optional"];
    741 
    742             if (!args.length && !optionalFlag) {
    743                 console.error("Protocol Error: Invalid number of arguments for method '" + method + "' call. It must have the following arguments '" + JSON.stringify(signature) + "'.");
    744                 return;
    745             }
    746 
    747             var value = args.shift();
    748             if (optionalFlag && typeof value === "undefined") {
    749                 continue;
    750             }
    751 
    752             if (typeof value !== typeName) {
    753                 console.error("Protocol Error: Invalid type of argument '" + paramName + "' for method '" + method + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
    754                 return;
    755             }
    756 
    757             params[paramName] = value;
    758             hasParams = true;
    759         }
    760 
    761         if (args.length === 1 && !callback && (typeof args[0] !== "undefined")) {
    762             console.error("Protocol Error: Optional callback argument for method '" + method + "' call must be a function but its type is '" + typeof args[0] + "'.");
    763             return;
    764         }
    765 
    766         this._connection._wrapCallbackAndSendMessageObject(this._domain, method, hasParams ? params : null, callback);
    767     },
    768 
    769     /**
    770      * @param {string} method
    771      * @param {?Object} args
    772      * @param {?function(*)} callback
    773      */
    774     _invoke: function(method, args, callback)
    775     {
    776         this._connection._wrapCallbackAndSendMessageObject(this._domain, method, args, callback);
    777     },
    778 
    779     /**
    780      * @param {number} messageId
    781      * @param {!Object} messageObject
    782      * @param {string} methodName
    783      * @param {function(!Array.<*>)} callback
    784      */
    785     dispatchResponse: function(messageId, messageObject, methodName, callback)
    786     {
    787         if (messageObject.error && messageObject.error.code !== -32000)
    788             console.error("Request with id = " + messageObject.id + " failed. " + JSON.stringify(messageObject.error));
    789 
    790         var argumentsArray = [];
    791         argumentsArray[0] = messageObject.error ? messageObject.error.message: null;
    792 
    793         if (this._hasErrorData[methodName])
    794             argumentsArray[1] = messageObject.error ? messageObject.error.data : null;
    795 
    796         if (messageObject.result) {
    797             var paramNames = this._replyArgs[methodName] || [];
    798             for (var i = 0; i < paramNames.length; ++i)
    799                 argumentsArray.push(messageObject.result[paramNames[i]]);
    800         }
    801 
    802         callback.apply(null, argumentsArray);
    803     }
    804 }
    805 
    806 /**
    807  * @constructor
    808  */
    809 InspectorBackendClass.DispatcherPrototype = function()
    810 {
    811     this._eventArgs = {};
    812     this._dispatcher = null;
    813 }
    814 
    815 InspectorBackendClass.DispatcherPrototype.prototype = {
    816 
    817     /**
    818      * @param {string} eventName
    819      * @param {!Object} params
    820      */
    821     registerEvent: function(eventName, params)
    822     {
    823         this._eventArgs[eventName] = params
    824     },
    825 
    826     /**
    827      * @param {!Object} dispatcher
    828      */
    829     setDomainDispatcher: function(dispatcher)
    830     {
    831         this._dispatcher = dispatcher;
    832     },
    833 
    834     /**
    835      * @param {string} functionName
    836      * @param {!Object} messageObject
    837      */
    838     dispatch: function(functionName, messageObject)
    839     {
    840         if (!this._dispatcher)
    841             return;
    842 
    843         if (!(functionName in this._dispatcher)) {
    844             console.error("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'");
    845             return;
    846         }
    847 
    848         if (!this._eventArgs[messageObject.method]) {
    849             console.error("Protocol Error: Attempted to dispatch an unspecified method '" + messageObject.method + "'");
    850             return;
    851         }
    852 
    853         var params = [];
    854         if (messageObject.params) {
    855             var paramNames = this._eventArgs[messageObject.method];
    856             for (var i = 0; i < paramNames.length; ++i)
    857                 params.push(messageObject.params[paramNames[i]]);
    858         }
    859 
    860         var processingStartTime;
    861         if (InspectorBackendClass.Options.dumpInspectorTimeStats)
    862             processingStartTime = Date.now();
    863 
    864         this._dispatcher[functionName].apply(this._dispatcher, params);
    865 
    866         if (InspectorBackendClass.Options.dumpInspectorTimeStats)
    867             console.log("time-stats: " + messageObject.method + " = " + (Date.now() - processingStartTime));
    868     }
    869 
    870 }
    871 
    872 InspectorBackendClass.Options = {
    873     dumpInspectorTimeStats: false,
    874     dumpInspectorProtocolMessages: false
    875 }
    876 
    877 InspectorBackend = new InspectorBackendClass();
    878