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 arg1, arg2 and ... . | |
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 | |
Jamie
2015/01/27 22:52:24
Maybe declare this @private?
kelvinp
2015/01/28 01:31:51
Done. JSCompile doesn't throw any error if you try
| |
48 */ | |
49 base.IPC = function() { | |
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.' | |
70 }; | |
71 | |
72 /** | |
73 * @constructor | |
74 * @param {string} methodName | |
75 * @param {?Array} params | |
76 * @struct | |
77 */ | |
78 base.IPC.Request_ = function(methodName, params) { | |
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 | |
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.'); | |
Jamie
2015/01/27 22:52:24
Nit: No colon needed.
kelvinp
2015/01/28 01:31:51
Done.
| |
94 return false; | |
95 } | |
96 this.handlers_[methodName] = handler; | |
97 return true; | |
98 }; | |
99 | |
100 /** | |
101 * @param {string} methodName | |
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) { | |
Jamie
2015/01/27 22:52:25
Indentation.
kelvinp
2015/01/28 01:31:51
Done.
| |
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}); | |
Jamie
2015/01/27 22:52:25
Nit: No space before colon, here and below.
kelvinp
2015/01/28 01:31:51
Done.
| |
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 | |
140 * | |
141 * @param {string} methodName | |
142 * @param {...} var_args | |
143 * @return A Promise that would resolve to the return value of the handler or | |
144 * reject 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)]); | |
151 | |
152 return sendMessage.then( | |
153 /** @param {?{error: Error}} response */ | |
154 function(response) { | |
155 if (response && response.error) { | |
156 return Promise.reject(response.error); | |
157 } else { | |
158 return Promise.resolve(response); | |
159 } | |
160 }); | |
161 }; | |
162 | |
163 | |
164 /** @type {base.IPC} */ | |
165 var instance_ = null; | |
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 |