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. For example, you can only reload an chrome.app.AppWindow from a | |
10 * background page. Likewise, some chrome API's must be initiated by user | |
11 * interaction, which can only be called from the 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 * {requestType:{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 request with the requestType and enforces | |
21 * that only one handler can be registered per requestType 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 * var remoteMethods = new base.remoteMethods(); | |
27 * remoteMethods.register('my.service.name', foo); | |
28 * | |
29 * In the AppWindow document: | |
30 * base.remoteMethods.invoke('my.service.name', [arg1, arg2]).then( | |
Jamie
2015/01/23 23:33:26
Would it be a lot of work to allow args to be pass
| |
31 * function(result) { | |
32 * console.log('The result is ' + result); | |
33 * }); | |
34 * | |
35 * This will invoke foo() with the args [arg1, arg2]. | |
36 * The return value of foo() will be passed back to the caller in the | |
37 * form of a promise. | |
38 */ | |
39 | |
40 /** @suppress {duplicate} */ | |
41 var base = base || {}; | |
42 | |
43 (function() { | |
44 | |
45 'use strict'; | |
46 | |
47 /** | |
48 * @constructor | |
49 * @implements {base.Disposable} | |
50 */ | |
51 base.RemoteMethods = function() { | |
Jamie
2015/01/23 23:33:26
I suggest base.Ipc as a name instead.
Jamie
2015/01/23 23:33:26
With a suitable chrome.runtime.sendMessage mock, I
| |
52 /** | |
53 * @type {!Object.<Function>} | |
54 * @private | |
55 */ | |
56 this.handlers_ = {}; | |
57 this.onMessageHandler_ = this.onMessage_.bind(this); | |
58 chrome.runtime.onMessage.addListener(this.onMessageHandler_); | |
59 }; | |
60 | |
61 | |
62 /** | |
63 * @constructor | |
64 * @param {string} requestType | |
65 * @param {?Array} params | |
66 * @struct | |
67 */ | |
68 base.RemoteMethods.Request_ = function(requestType, params) { | |
Jamie
2015/01/23 23:33:26
If this is a private type, does it need to be expo
| |
69 this.requestType = requestType; | |
70 this.params = params; | |
71 }; | |
72 | |
73 base.RemoteMethods.prototype.dispose = function() { | |
74 chrome.runtime.onMessage.removeListener(this.onMessageHandler_); | |
75 }; | |
76 | |
77 /** | |
78 * @param {string} requestType | |
Jamie
2015/01/23 23:33:26
Since we're talking about methods, I think methodN
| |
79 * @param {Function} handler | |
Jamie
2015/01/23 23:33:26
s/Function/function(*):void/
| |
80 * @return {boolean} Whether the handler is successfully registered. | |
81 */ | |
82 base.RemoteMethods.prototype.register = function(requestType, handler) { | |
83 if (requestType in this.handlers_) { | |
84 console.error('service :' + requestType + ' is already registered.'); | |
85 return false; | |
86 } | |
87 this.handlers_[requestType] = handler; | |
88 return true; | |
89 }; | |
90 | |
91 /** | |
92 * @param {string} requestType | |
93 */ | |
94 base.RemoteMethods.prototype.unregister = function(requestType) { | |
95 delete this.handlers_[requestType]; | |
96 }; | |
97 | |
98 /** | |
99 * @param {base.RemoteMethods.Request_} message | |
100 * @param {chrome.runtime.MessageSender} sender | |
101 * @param {function(*): void} sendResponse | |
102 */ | |
103 base.RemoteMethods.prototype.onMessage_ = function(message, sender, | |
104 sendResponse) { | |
105 if (sender.id !== chrome.runtime.id) { | |
106 // We only handle incoming requests from our extension. | |
107 return; | |
108 } | |
109 | |
110 var requestType = message.requestType; | |
111 if (typeof requestType !== 'string') { | |
112 return; | |
113 } | |
114 | |
115 var remoteMethod = | |
116 /** @type {function(*):void} */ (this.handlers_[requestType]); | |
117 if (!remoteMethod) { | |
118 sendResponse({error : 'Unsupported request:= ' + requestType}); | |
Jamie
2015/01/23 23:33:26
Nit: s/:=/:/
| |
119 return; | |
120 } | |
121 | |
122 try { | |
123 sendResponse(remoteMethod.apply(null, message.params)); | |
124 } catch (/** @type {Error} */ e) { | |
125 sendResponse({error: e.message}); | |
126 } | |
127 }; | |
128 | |
129 /** | |
130 * Invokes a method on a remote page | |
Jamie
2015/01/23 23:33:26
Blank (comment) line after method description.
| |
131 * @param {string} requestType | |
132 * @param {Array} params | |
Jamie
2015/01/23 23:33:26
@return?
| |
133 */ | |
134 base.RemoteMethods.invoke = function(requestType, params) { | |
135 var sendMessage = base.Promise.as( | |
136 chrome.runtime.sendMessage, | |
137 [null, new base.RemoteMethods.Request_(requestType, params)]); | |
Jamie
2015/01/23 23:33:26
I don't think you need the leading null, since the
| |
138 | |
139 return sendMessage.then( | |
140 /** @param {{error: Error}} response */ | |
141 function(response) { | |
142 if (response.error) { | |
143 Promise.reject(response.error); | |
144 } else { | |
145 Promise.resolve(response); | |
Jamie
2015/01/23 23:33:26
Don't these reject/resolves need to be returned?
| |
146 } | |
147 }); | |
148 }; | |
149 | |
150 })(); | |
Jamie
2015/01/23 23:33:26
Although we have too many globals in our code, I t
| |
OLD | NEW |