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