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(); 935