Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * | |
| 8 * In Chrome Apps, some platform APIs can only be called from the background | |
| 9 * page (e.g. reloading a chrome.app.AppWindow). Likewise, some chrome API's | |
| 10 * must be initiated by user interaction, which can only be called from the | |
| 11 * foreground. | |
| 12 * | |
| 13 * This class provides helper functions to invoke methods on different pages | |
| 14 * using chrome.runtime.sendMessage. Messages are passed in the following | |
| 15 * format: | |
| 16 * {methodName:{string}, params:{Array}} | |
| 17 * | |
| 18 * chrome.runtime.sendMessage allows multiple handlers to be registered on a | |
| 19 * document, but only one handler can send a response. | |
| 20 * This class uniquely identifies a method with the |methodName| and enforces | |
| 21 * that only one handler can be registered per |methodName| in the document. | |
| 22 * | |
| 23 * For example, to call method foo() in the background page from the foreground | |
| 24 * chrome.app.AppWindow, you can do the following. | |
| 25 * In the background page: | |
| 26 * base.IPC.getInstance().register('my.service.name', foo); | |
| 27 * | |
| 28 * In the AppWindow document: | |
| 29 * base.IPC.invoke('my.service.name', [arg1, arg2]).then( | |
| 30 * function(result) { | |
| 31 * console.log('The result is ' + result); | |
| 32 * }); | |
| 33 * | |
| 34 * This will invoke foo() with the args [arg1, arg2]. | |
|
kelvinp
2015/01/27 01:27:22
Jamie: Would it be a lot of work to allow args to
kelvinp
2015/01/27 01:35:00
Done.
| |
| 35 * The return value of foo() will be passed back to the caller in the | |
| 36 * form of a promise. | |
| 37 */ | |
| 38 | |
| 39 /** @suppress {duplicate} */ | |
| 40 var base = base || {}; | |
| 41 | |
| 42 (function() { | |
| 43 | |
| 44 'use strict'; | |
| 45 | |
| 46 /** | |
| 47 * @constructor | |
| 48 */ | |
| 49 base.IPC = function() { | |
|
kelvinp
2015/01/27 01:27:21
Jamie: I suggest base.Ipc as a name instead.
kelvinp
2015/01/27 01:34:59
I use base.IPC instead as IPC is an abbreviation.
Jamie
2015/01/27 22:52:24
I tend to treat them as if they weren't abbreviati
kelvinp
2015/01/28 01:31:51
Done.
| |
| 50 base.debug.assert(instance_ === null); | |
| 51 /** | |
| 52 * @type {!Object.<Function>} | |
| 53 * @private | |
| 54 */ | |
| 55 this.handlers_ = {}; | |
| 56 this.onMessageHandler_ = this.onMessage_.bind(this); | |
| 57 chrome.runtime.onMessage.addListener(this.onMessageHandler_); | |
| 58 }; | |
| 59 | |
| 60 /** @private */ | |
| 61 base.IPC.prototype.dispose_ = function() { | |
| 62 chrome.runtime.onMessage.removeListener(this.onMessageHandler_); | |
| 63 }; | |
| 64 | |
| 65 /** @enum {string} */ | |
| 66 base.IPC.Error = { | |
| 67 UNSUPPORTED_REQUEST_TYPE: 'Unsupported method name.', | |
| 68 INVALID_REQUEST_ORIGIN: | |
| 69 'base.IPC only accept incoming requests from the same extension.' | |
|
Jamie
2015/01/27 22:52:24
Are these string ever displayed in the UI? If so t
kelvinp
2015/01/28 01:31:51
The are only logged in the console.
| |
| 70 }; | |
| 71 | |
| 72 /** | |
| 73 * @constructor | |
| 74 * @param {string} methodName | |
| 75 * @param {?Array} params | |
| 76 * @struct | |
| 77 */ | |
| 78 base.IPC.Request_ = function(methodName, params) { | |
|
kelvinp
2015/01/27 01:27:21
Jamie: If this is a private type, does it need to
kelvinp
2015/01/27 01:34:59
JSCompile cannot recognize the type unless I put i
Jamie
2015/01/27 22:52:24
Okay, in that case please annotate it as @private
kelvinp
2015/01/28 01:31:51
JSCompile can understand classes that is attached
| |
| 79 this.methodName = methodName; | |
| 80 this.params = params; | |
| 81 }; | |
| 82 | |
| 83 | |
| 84 /** | |
| 85 * @param {string} methodName | |
| 86 * @param {Function} handler The handler can be invoked by calling | |
|
kelvinp
2015/01/27 01:34:59
Jamie: s/Function/function(*):void/
kelvinp
2015/01/27 01:34:59
I think function(*):void is a bit misleading as it
Jamie
2015/01/27 22:52:24
How about function(*) then? I don't think we use F
kelvinp
2015/01/28 01:31:51
Done. Function is the JavaScript type name for all
| |
| 87 * base.IPC.invoke(|methodName|, arg1, arg2, ...) | |
| 88 * Async handlers that return promises are currently not supported. | |
| 89 * @return {boolean} Whether the handler is successfully registered. | |
| 90 */ | |
| 91 base.IPC.prototype.register = function(methodName, handler) { | |
| 92 if (methodName in this.handlers_) { | |
| 93 console.error('service :' + methodName + ' is already registered.'); | |
| 94 return false; | |
| 95 } | |
| 96 this.handlers_[methodName] = handler; | |
| 97 return true; | |
| 98 }; | |
| 99 | |
| 100 /** | |
| 101 * @param {string} methodName | |
|
kelvinp
2015/01/27 01:27:22
Jamie: Since we're talking about methods, I think
kelvinp
2015/01/27 01:35:00
Done.
| |
| 102 */ | |
| 103 base.IPC.prototype.unregister = function(methodName) { | |
| 104 delete this.handlers_[methodName]; | |
| 105 }; | |
| 106 | |
| 107 /** | |
| 108 * @param {base.IPC.Request_} message | |
| 109 * @param {chrome.runtime.MessageSender} sender | |
| 110 * @param {function(*): void} sendResponse | |
| 111 */ | |
| 112 base.IPC.prototype.onMessage_ = function(message, sender, | |
| 113 sendResponse) { | |
| 114 var methodName = message.methodName; | |
| 115 if (typeof methodName !== 'string') { | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 if (sender.id !== chrome.runtime.id) { | |
| 120 sendResponse({error : base.IPC.Error.INVALID_REQUEST_ORIGIN}); | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 var remoteMethod = | |
| 125 /** @type {function(*):void} */ (this.handlers_[methodName]); | |
| 126 if (!remoteMethod) { | |
| 127 sendResponse({error : base.IPC.Error.UNSUPPORTED_REQUEST_TYPE}); | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 try { | |
| 132 sendResponse(remoteMethod.apply(null, message.params)); | |
| 133 } catch (/** @type {Error} */ e) { | |
| 134 sendResponse({error: e.message}); | |
| 135 } | |
| 136 }; | |
| 137 | |
| 138 /** | |
| 139 * Invokes a method on a remote page | |
|
kelvinp
2015/01/27 01:34:59
Done.
kelvinp
2015/01/27 01:34:59
Jamie: Blank (comment) line after method descripti
| |
| 140 * | |
| 141 * @param {string} methodName | |
| 142 * @param {...} var_args | |
| 143 * @return A Promise that would resolves to the return value of the handler or | |
|
kelvinp
2015/01/27 01:27:21
Jamie: @return?
kelvinp
2015/01/27 01:34:59
Done.
| |
| 144 * rejects if the handler throws an exception. | |
| 145 */ | |
| 146 base.IPC.invoke = function(methodName, var_args) { | |
| 147 var params = Array.prototype.slice.call(arguments, 1); | |
| 148 var sendMessage = base.Promise.as( | |
| 149 chrome.runtime.sendMessage, | |
| 150 [null, new base.IPC.Request_(methodName, params)]); | |
|
kelvinp
2015/01/27 01:27:21
Jamie: I don't think you need the leading null, si
kelvinp
2015/01/27 01:34:59
Good point. The leading null is there to reduce t
Jamie
2015/01/27 22:52:24
Fair enough, but I worry about the robustness of t
kelvinp
2015/01/28 01:31:51
Done.
| |
| 151 | |
| 152 return sendMessage.then( | |
| 153 /** @param {?{error: Error}} response */ | |
| 154 function(response) { | |
| 155 if (response && response.error) { | |
| 156 return Promise.reject(response.error); | |
|
kelvinp
2015/01/27 01:27:22
Jamie: Don't these reject/resolves need to be retu
kelvinp
2015/01/27 01:34:59
Good catch. Done.
| |
| 157 } else { | |
| 158 return Promise.resolve(response); | |
| 159 } | |
| 160 }); | |
| 161 }; | |
| 162 | |
| 163 | |
| 164 /** @type {base.IPC} */ | |
| 165 var instance_ = null; | |
|
kelvinp
2015/01/27 01:27:22
Jamie: Although we have too many globals in our co
kelvinp
2015/01/27 01:34:59
Done.
| |
| 166 | |
| 167 /** @return {base.IPC} */ | |
| 168 base.IPC.getInstance = function() { | |
| 169 if (!instance_) { | |
| 170 instance_ = new base.IPC(); | |
| 171 } | |
| 172 return instance_; | |
| 173 }; | |
| 174 | |
| 175 base.IPC.deleteInstance = function() { | |
| 176 if (instance_) { | |
| 177 instance_.dispose_(); | |
| 178 instance_ = null; | |
| 179 } | |
| 180 }; | |
| 181 | |
| 182 })(); | |
| OLD | NEW |