Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/InspectorBackend.js

Issue 2453673002: [DevTools] Scope common protocol infrastructure under Protocol namespace in a separate module. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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._agentPrototypes = {};
37 this._dispatcherPrototypes = {};
38 this._initialized = false;
39 }
40
41 InspectorBackendClass._DevToolsErrorCode = -32000;
42 InspectorBackendClass.DevToolsStubErrorCode = -32015;
43
44 /**
45 * @param {string} error
46 * @param {!Object} messageObject
47 */
48 InspectorBackendClass.reportProtocolError = function(error, messageObject)
49 {
50 console.error(error + ": " + JSON.stringify(messageObject));
51 };
52
53 InspectorBackendClass.prototype = {
54 /**
55 * @return {boolean}
56 */
57 isInitialized: function()
58 {
59 return this._initialized;
60 },
61
62 /**
63 * @param {string} domain
64 */
65 _addAgentGetterMethodToProtocolTargetPrototype: function(domain)
66 {
67 var upperCaseLength = 0;
68 while (upperCaseLength < domain.length && domain[upperCaseLength].toLowe rCase() !== domain[upperCaseLength])
69 ++upperCaseLength;
70
71 var methodName = domain.substr(0, upperCaseLength).toLowerCase() + domai n.slice(upperCaseLength) + "Agent";
72
73 /**
74 * @this {Protocol.Target}
75 */
76 function agentGetter()
77 {
78 return this._agents[domain];
79 }
80
81 Protocol.Target.prototype[methodName] = agentGetter;
82
83 /**
84 * @this {Protocol.Target}
85 */
86 function registerDispatcher(dispatcher)
87 {
88 this.registerDispatcher(domain, dispatcher);
89 }
90
91 Protocol.Target.prototype["register" + domain + "Dispatcher"] = register Dispatcher;
92 },
93
94 /**
95 * @param {string} domain
96 * @return {!InspectorBackendClass._AgentPrototype}
97 */
98 _agentPrototype: function(domain)
99 {
100 if (!this._agentPrototypes[domain]) {
101 this._agentPrototypes[domain] = new InspectorBackendClass._AgentProt otype(domain);
102 this._addAgentGetterMethodToProtocolTargetPrototype(domain);
103 }
104
105 return this._agentPrototypes[domain];
106 },
107
108 /**
109 * @param {string} domain
110 * @return {!InspectorBackendClass._DispatcherPrototype}
111 */
112 _dispatcherPrototype: function(domain)
113 {
114 if (!this._dispatcherPrototypes[domain])
115 this._dispatcherPrototypes[domain] = new InspectorBackendClass._Disp atcherPrototype();
116 return this._dispatcherPrototypes[domain];
117 },
118
119 /**
120 * @param {string} method
121 * @param {!Array.<!Object>} signature
122 * @param {!Array.<string>} replyArgs
123 * @param {boolean} hasErrorData
124 */
125 registerCommand: function(method, signature, replyArgs, hasErrorData)
126 {
127 var domainAndMethod = method.split(".");
128 this._agentPrototype(domainAndMethod[0]).registerCommand(domainAndMethod [1], signature, replyArgs, hasErrorData);
129 this._initialized = true;
130 },
131
132 /**
133 * @param {string} type
134 * @param {!Object} values
135 */
136 registerEnum: function(type, values)
137 {
138 var domainAndMethod = type.split(".");
139 var agentName = domainAndMethod[0] + "Agent";
140 if (!window[agentName])
141 window[agentName] = {};
142
143 window[agentName][domainAndMethod[1]] = values;
144 this._initialized = true;
145 },
146
147 /**
148 * @param {string} eventName
149 * @param {!Object} params
150 */
151 registerEvent: function(eventName, params)
152 {
153 var domain = eventName.split(".")[0];
154 this._dispatcherPrototype(domain).registerEvent(eventName, params);
155 this._initialized = true;
156 },
157
158 /**
159 * @param {function(T)} clientCallback
160 * @param {string} errorPrefix
161 * @param {function(new:T,S)=} constructor
162 * @param {T=} defaultValue
163 * @return {function(?string, S)}
164 * @template T,S
165 */
166 wrapClientCallback: function(clientCallback, errorPrefix, constructor, defau ltValue)
167 {
168 /**
169 * @param {?string} error
170 * @param {S} value
171 * @template S
172 */
173 function callbackWrapper(error, value)
174 {
175 if (error) {
176 console.error(errorPrefix + error);
177 clientCallback(defaultValue);
178 return;
179 }
180 if (constructor)
181 clientCallback(new constructor(value));
182 else
183 clientCallback(value);
184 }
185 return callbackWrapper;
186 }
187 };
188
189 var InspectorBackend = new InspectorBackendClass();
190
191 /**
192 * @interface
193 */
194 InspectorBackendClass.Connection = function()
195 {
196 };
197
198 InspectorBackendClass.Connection.prototype = {
199 /**
200 * @param {string} message
201 */
202 sendMessage: function(message) { },
203
204 /**
205 * @return {!Promise}
206 */
207 disconnect: function() { },
208 };
209
210 /**
211 * @typedef {!{
212 * onMessage: function((!Object|string)),
213 * onDisconnect: function(string)
214 * }}
215 */
216 InspectorBackendClass.Connection.Params;
217
218 /**
219 * @typedef {function(!InspectorBackendClass.Connection.Params):!InspectorBacken dClass.Connection}
220 */
221 InspectorBackendClass.Connection.Factory;
222
223 var Protocol = {};
224
225 /** @typedef {string} */
226 Protocol.Error;
227
228 /**
229 * @constructor
230 * @param {!InspectorBackendClass.Connection.Factory} connectionFactory
231 */
232 Protocol.Target = function(connectionFactory)
233 {
234 this._connection = connectionFactory({onMessage: this._onMessage.bind(this), onDisconnect: this._onDisconnect.bind(this)});
235 this._lastMessageId = 1;
236 this._pendingResponsesCount = 0;
237 this._agents = {};
238 this._dispatchers = {};
239 this._callbacks = {};
240 this._initialize(InspectorBackend._agentPrototypes, InspectorBackend._dispat cherPrototypes);
241 if (!InspectorBackendClass.deprecatedRunAfterPendingDispatches)
242 InspectorBackendClass.deprecatedRunAfterPendingDispatches = this._deprec atedRunAfterPendingDispatches.bind(this);
243 if (!InspectorBackendClass.sendRawMessageForTesting)
244 InspectorBackendClass.sendRawMessageForTesting = this._sendRawMessageFor Testing.bind(this);
245 };
246
247 Protocol.Target.prototype = {
248 /**
249 * @param {!Object.<string, !InspectorBackendClass._AgentPrototype>} agentPr ototypes
250 * @param {!Object.<string, !InspectorBackendClass._DispatcherPrototype>} di spatcherPrototypes
251 */
252 _initialize: function(agentPrototypes, dispatcherPrototypes)
253 {
254 for (var domain in agentPrototypes) {
255 this._agents[domain] = Object.create(agentPrototypes[domain]);
256 this._agents[domain].setTarget(this);
257 }
258
259 for (var domain in dispatcherPrototypes)
260 this._dispatchers[domain] = Object.create(dispatcherPrototypes[domai n]);
261 },
262
263 /**
264 * @return {number}
265 */
266 _nextMessageId: function()
267 {
268 return this._lastMessageId++;
269 },
270
271 /**
272 * @param {string} domain
273 * @return {!InspectorBackendClass._AgentPrototype}
274 */
275 _agent: function(domain)
276 {
277 return this._agents[domain];
278 },
279
280 /**
281 * @param {string} domain
282 * @param {string} method
283 * @param {?Object} params
284 * @param {?function(*)} callback
285 */
286 _wrapCallbackAndSendMessageObject: function(domain, method, params, callback )
287 {
288 if (!this._connection) {
289 if (callback)
290 this._dispatchConnectionErrorResponse(domain, method, callback);
291 return;
292 }
293
294 var messageObject = {};
295 var messageId = this._nextMessageId();
296 messageObject.id = messageId;
297 messageObject.method = method;
298 if (params)
299 messageObject.params = params;
300
301 var wrappedCallback = this._wrap(callback, domain, method);
302 var message = JSON.stringify(messageObject);
303
304 if (InspectorBackendClass.Options.dumpInspectorProtocolMessages)
305 this._dumpProtocolMessage("frontend: " + message);
306
307 this._connection.sendMessage(message);
308 ++this._pendingResponsesCount;
309 this._callbacks[messageId] = wrappedCallback;
310 },
311
312 /**
313 * @param {?function(*)} callback
314 * @param {string} method
315 * @param {string} domain
316 * @return {function(*)}
317 */
318 _wrap: function(callback, domain, method)
319 {
320 if (!callback)
321 callback = function() {};
322
323 callback.methodName = method;
324 callback.domain = domain;
325 if (InspectorBackendClass.Options.dumpInspectorTimeStats)
326 callback.sendRequestTime = Date.now();
327
328 return callback;
329 },
330
331 /**
332 * @param {string} method
333 * @param {?Object} params
334 * @param {?function(...*)} callback
335 */
336 _sendRawMessageForTesting: function(method, params, callback)
337 {
338 var domain = method.split(".")[0];
339 this._wrapCallbackAndSendMessageObject(domain, method, params, callback) ;
340 },
341
342 /**
343 * @param {!Object|string} message
344 */
345 _onMessage: function(message)
346 {
347 if (InspectorBackendClass.Options.dumpInspectorProtocolMessages)
348 this._dumpProtocolMessage("backend: " + ((typeof message === "string ") ? message : JSON.stringify(message)));
349
350 var messageObject = /** @type {!Object} */ ((typeof message === "string" ) ? JSON.parse(message) : message);
351
352 if ("id" in messageObject) { // just a response for some request
353 var callback = this._callbacks[messageObject.id];
354 if (!callback) {
355 InspectorBackendClass.reportProtocolError("Protocol Error: the m essage with wrong id", messageObject);
356 return;
357 }
358
359 var processingStartTime;
360 if (InspectorBackendClass.Options.dumpInspectorTimeStats)
361 processingStartTime = Date.now();
362
363 this._agent(callback.domain).dispatchResponse(messageObject, callbac k.methodName, callback);
364 --this._pendingResponsesCount;
365 delete this._callbacks[messageObject.id];
366
367 if (InspectorBackendClass.Options.dumpInspectorTimeStats)
368 console.log("time-stats: " + callback.methodName + " = " + (proc essingStartTime - callback.sendRequestTime) + " + " + (Date.now() - processingSt artTime));
369
370 if (this._scripts && !this._pendingResponsesCount)
371 this._deprecatedRunAfterPendingDispatches();
372 } else {
373 if (!("method" in messageObject)) {
374 InspectorBackendClass.reportProtocolError("Protocol Error: the m essage without method", messageObject);
375 return;
376 }
377
378 var method = messageObject.method.split(".");
379 var domainName = method[0];
380 if (!(domainName in this._dispatchers)) {
381 InspectorBackendClass.reportProtocolError("Protocol Error: the m essage " + messageObject.method + " is for non-existing domain '" + domainName + "'", messageObject);
382 return;
383 }
384
385 this._dispatchers[domainName].dispatch(method[1], messageObject);
386 }
387 },
388
389 /**
390 * @param {string} domain
391 * @param {!Object} dispatcher
392 */
393 registerDispatcher: function(domain, dispatcher)
394 {
395 if (!this._dispatchers[domain])
396 return;
397
398 this._dispatchers[domain].setDomainDispatcher(dispatcher);
399 },
400
401 /**
402 * @param {function()=} script
403 */
404 _deprecatedRunAfterPendingDispatches: function(script)
405 {
406 if (!this._scripts)
407 this._scripts = [];
408
409 if (script)
410 this._scripts.push(script);
411
412 // Execute all promises.
413 setTimeout(function() {
414 if (!this._pendingResponsesCount)
415 this._executeAfterPendingDispatches();
416 else
417 this._deprecatedRunAfterPendingDispatches();
418 }.bind(this), 0);
419 },
420
421 _executeAfterPendingDispatches: function()
422 {
423 if (!this._pendingResponsesCount) {
424 var scripts = this._scripts;
425 this._scripts = [];
426 for (var id = 0; id < scripts.length; ++id)
427 scripts[id].call(this);
428 }
429 },
430
431 /**
432 * @param {string} message
433 */
434 _dumpProtocolMessage: function(message)
435 {
436 console.log(message);
437 },
438
439 /**
440 * @param {string} reason
441 */
442 _onDisconnect: function(reason)
443 {
444 this._connection = null;
445 this._runPendingCallbacks();
446 this.dispose();
447 },
448
449 /**
450 * @protected
451 */
452 dispose: function()
453 {
454 },
455
456 /**
457 * @return {boolean}
458 */
459 isDisposed: function()
460 {
461 return !this._connection;
462 },
463
464 _runPendingCallbacks: function()
465 {
466 var keys = Object.keys(this._callbacks).map(function(num) { return parse Int(num, 10); });
467 for (var i = 0; i < keys.length; ++i) {
468 var callback = this._callbacks[keys[i]];
469 this._dispatchConnectionErrorResponse(callback.domain, callback.meth odName, callback);
470 }
471 this._callbacks = {};
472 },
473
474 /**
475 * @param {string} domain
476 * @param {string} methodName
477 * @param {function(*)} callback
478 */
479 _dispatchConnectionErrorResponse: function(domain, methodName, callback)
480 {
481 var error = { message: "Connection is closed, can't dispatch pending " + methodName, code: InspectorBackendClass._DevToolsErrorCode, data: null};
482 var messageObject = {error: error};
483 setTimeout(InspectorBackendClass._AgentPrototype.prototype.dispatchRespo nse.bind(this._agent(domain), messageObject, methodName, callback), 0);
484 },
485 };
486
487 /**
488 * @constructor
489 * @param {string} domain
490 */
491 InspectorBackendClass._AgentPrototype = function(domain)
492 {
493 this._replyArgs = {};
494 this._hasErrorData = {};
495 this._domain = domain;
496 };
497
498 InspectorBackendClass._AgentPrototype.prototype = {
499 /**
500 * @param {!Protocol.Target} target
501 */
502 setTarget: function(target)
503 {
504 this._target = target;
505 },
506
507 /**
508 * @param {string} methodName
509 * @param {!Array.<!Object>} signature
510 * @param {!Array.<string>} replyArgs
511 * @param {boolean} hasErrorData
512 */
513 registerCommand: function(methodName, signature, replyArgs, hasErrorData)
514 {
515 var domainAndMethod = this._domain + "." + methodName;
516
517 /**
518 * @param {...*} vararg
519 * @this {InspectorBackendClass._AgentPrototype}
520 * @return {!Promise.<*>}
521 */
522 function sendMessagePromise(vararg)
523 {
524 var params = Array.prototype.slice.call(arguments);
525 return InspectorBackendClass._AgentPrototype.prototype._sendMessageT oBackendPromise.call(this, domainAndMethod, signature, params);
526 }
527
528 this[methodName] = sendMessagePromise;
529
530 /**
531 * @param {...*} vararg
532 * @this {InspectorBackendClass._AgentPrototype}
533 */
534 function invoke(vararg)
535 {
536 var params = [domainAndMethod].concat(Array.prototype.slice.call(arg uments));
537 InspectorBackendClass._AgentPrototype.prototype._invoke.apply(this, params);
538 }
539
540 this["invoke_" + methodName] = invoke;
541
542 this._replyArgs[domainAndMethod] = replyArgs;
543 if (hasErrorData)
544 this._hasErrorData[domainAndMethod] = true;
545 },
546
547 /**
548 * @param {string} method
549 * @param {!Array.<!Object>} signature
550 * @param {!Array.<*>} args
551 * @param {boolean} allowExtraUndefinedArg
552 * @param {function(string)} errorCallback
553 * @return {?Object}
554 */
555 _prepareParameters: function(method, signature, args, allowExtraUndefinedArg , errorCallback)
556 {
557 var params = {};
558 var hasParams = false;
559 for (var i = 0; i < signature.length; ++i) {
560 var param = signature[i];
561 var paramName = param["name"];
562 var typeName = param["type"];
563 var optionalFlag = param["optional"];
564
565 if (!args.length && !optionalFlag) {
566 errorCallback("Protocol Error: Invalid number of arguments for m ethod '" + method + "' call. It must have the following arguments '" + JSON.stri ngify(signature) + "'.");
567 return null;
568 }
569
570 var value = args.shift();
571 if (optionalFlag && typeof value === "undefined")
572 continue;
573
574 if (typeof value !== typeName) {
575 errorCallback("Protocol Error: Invalid type of argument '" + par amName + "' for method '" + method + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
576 return null;
577 }
578
579 params[paramName] = value;
580 hasParams = true;
581 }
582
583 if (args.length === 1 && (!allowExtraUndefinedArg || (typeof args[0] !== "undefined"))) {
584 errorCallback("Protocol Error: Optional callback argument for method '" + method + "' call must be a function but its type is '" + typeof args[0] + "'.");
585 return null;
586 }
587
588 if (args.length > 1) {
589 errorCallback("Protocol Error: Extra " + args.length + " arguments i n a call to method '" + method + "'.");
590 return null;
591 }
592
593 return hasParams ? params : null;
594 },
595
596 /**
597 * @param {string} method
598 * @param {!Array.<!Object>} signature
599 * @param {!Array.<*>} args
600 * @return {!Promise.<*>}
601 */
602 _sendMessageToBackendPromise: function(method, signature, args)
603 {
604 var errorMessage;
605 /**
606 * @param {string} message
607 */
608 function onError(message)
609 {
610 console.error(message);
611 errorMessage = message;
612 }
613 var userCallback = (args.length && typeof args.peekLast() === "function" ) ? args.pop() : null;
614 var params = this._prepareParameters(method, signature, args, !userCallb ack, onError);
615 if (errorMessage)
616 return Promise.reject(new Error(errorMessage));
617 else
618 return new Promise(promiseAction.bind(this));
619
620 /**
621 * @param {function(?)} resolve
622 * @param {function(!Error)} reject
623 * @this {InspectorBackendClass._AgentPrototype}
624 */
625 function promiseAction(resolve, reject)
626 {
627 /**
628 * @param {...*} vararg
629 */
630 function callback(vararg)
631 {
632 var result = userCallback ? userCallback.apply(null, arguments) : undefined;
633 resolve(result);
634 }
635 this._target._wrapCallbackAndSendMessageObject(this._domain, method, params, callback);
636 }
637 },
638
639 /**
640 * @param {string} method
641 * @param {?Object} args
642 * @param {?function(*)} callback
643 */
644 _invoke: function(method, args, callback)
645 {
646 this._target._wrapCallbackAndSendMessageObject(this._domain, method, arg s, callback);
647 },
648
649 /**
650 * @param {!Object} messageObject
651 * @param {string} methodName
652 * @param {function(*)|function(?Protocol.Error, ?Object)} callback
653 */
654 dispatchResponse: function(messageObject, methodName, callback)
655 {
656 if (messageObject.error && messageObject.error.code !== InspectorBackend Class._DevToolsErrorCode && messageObject.error.code !== InspectorBackendClass.D evToolsStubErrorCode && !InspectorBackendClass.Options.suppressRequestErrors) {
657 var id = InspectorBackendClass.Options.dumpInspectorProtocolMessages ? " with id = " + messageObject.id : "";
658 console.error("Request " + methodName + id + " failed. " + JSON.stri ngify(messageObject.error));
659 }
660
661 var argumentsArray = [];
662 argumentsArray[0] = messageObject.error ? messageObject.error.message : null;
663
664 if (this._hasErrorData[methodName])
665 argumentsArray[1] = messageObject.error ? messageObject.error.data : null;
666
667 if (messageObject.result) {
668 var paramNames = this._replyArgs[methodName] || [];
669 for (var i = 0; i < paramNames.length; ++i)
670 argumentsArray.push(messageObject.result[paramNames[i]]);
671 }
672
673 callback.apply(null, argumentsArray);
674 },
675 };
676
677 /**
678 * @constructor
679 */
680 InspectorBackendClass._DispatcherPrototype = function()
681 {
682 this._eventArgs = {};
683 this._dispatcher = null;
684 };
685
686 InspectorBackendClass._DispatcherPrototype.prototype = {
687
688 /**
689 * @param {string} eventName
690 * @param {!Object} params
691 */
692 registerEvent: function(eventName, params)
693 {
694 this._eventArgs[eventName] = params;
695 },
696
697 /**
698 * @param {!Object} dispatcher
699 */
700 setDomainDispatcher: function(dispatcher)
701 {
702 this._dispatcher = dispatcher;
703 },
704
705 /**
706 * @param {string} functionName
707 * @param {!Object} messageObject
708 */
709 dispatch: function(functionName, messageObject)
710 {
711 if (!this._dispatcher)
712 return;
713
714 if (!(functionName in this._dispatcher)) {
715 InspectorBackendClass.reportProtocolError("Protocol Error: Attempted to dispatch an unimplemented method '" + messageObject.method + "'", messageObj ect);
716 return;
717 }
718
719 if (!this._eventArgs[messageObject.method]) {
720 InspectorBackendClass.reportProtocolError("Protocol Error: Attempted to dispatch an unspecified method '" + messageObject.method + "'", messageObjec t);
721 return;
722 }
723
724 var params = [];
725 if (messageObject.params) {
726 var paramNames = this._eventArgs[messageObject.method];
727 for (var i = 0; i < paramNames.length; ++i)
728 params.push(messageObject.params[paramNames[i]]);
729 }
730
731 var processingStartTime;
732 if (InspectorBackendClass.Options.dumpInspectorTimeStats)
733 processingStartTime = Date.now();
734
735 this._dispatcher[functionName].apply(this._dispatcher, params);
736
737 if (InspectorBackendClass.Options.dumpInspectorTimeStats)
738 console.log("time-stats: " + messageObject.method + " = " + (Date.no w() - processingStartTime));
739 }
740 };
741
742 InspectorBackendClass.Options = {
743 dumpInspectorTimeStats: false,
744 dumpInspectorProtocolMessages: false,
745 suppressRequestErrors: false
746 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698