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