• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
34function InspectorBackendClass()
35{
36    this._connection = null;
37    this._agentPrototypes = {};
38    this._dispatcherPrototypes = {};
39    this._initialized = false;
40    this._enums = {};
41    this._initProtocolAgentsConstructor();
42}
43
44InspectorBackendClass.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 */
235InspectorBackendClass._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 */
335InspectorBackendClass.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
345InspectorBackendClass.Connection.Events = {
346    Disconnected: "Disconnected",
347}
348
349InspectorBackendClass.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 */
563InspectorBackendClass.MainConnection = function(onConnectionReady)
564{
565    InspectorBackendClass.Connection.call(this);
566    onConnectionReady(this);
567}
568
569InspectorBackendClass.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 */
589InspectorBackendClass.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
599InspectorBackendClass.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 */
636InspectorBackendClass.StubConnection = function(onConnectionReady)
637{
638    InspectorBackendClass.Connection.call(this);
639    onConnectionReady(this);
640}
641
642InspectorBackendClass.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 */
668InspectorBackendClass.AgentPrototype = function(domain)
669{
670    this._replyArgs = {};
671    this._hasErrorData = {};
672    this._domain = domain;
673}
674
675InspectorBackendClass.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 */
809InspectorBackendClass.DispatcherPrototype = function()
810{
811    this._eventArgs = {};
812    this._dispatcher = null;
813}
814
815InspectorBackendClass.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
872InspectorBackendClass.Options = {
873    dumpInspectorTimeStats: false,
874    dumpInspectorProtocolMessages: false
875}
876
877InspectorBackend = new InspectorBackendClass();
878