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.
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
13 * This class provides helper functions to invoke methods on different pages
14 * using chrome.runtime.sendMessage. Messages are passed in the following
16 * {methodName:{string}, params:{Array}}
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.
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);
28 * In the AppWindow document:
29 * base.Ipc.invoke('my.service.name', arg1, arg2, ...).then(
31 * console.log('The result is ' + result);
34 * This will invoke foo() with the arg1, arg2, ....
35 * The return value of foo() will be passed back to the caller in the
39 /** @suppress {duplicate} */
40 var base
= base
|| {};
50 base
.Ipc = function() {
51 base
.debug
.assert(instance_
=== null);
52 /** @private {!Object<Function>} */
54 this.onMessageHandler_
= this.onMessage_
.bind(this);
55 chrome
.runtime
.onMessage
.addListener(this.onMessageHandler_
);
59 base
.Ipc
.prototype.dispose_ = function() {
60 chrome
.runtime
.onMessage
.removeListener(this.onMessageHandler_
);
64 * The error strings are only used for debugging purposes and are not localized.
69 UNSUPPORTED_REQUEST_TYPE
: 'Unsupported method name.',
70 INVALID_REQUEST_ORIGIN
:
71 'base.Ipc only accept incoming requests from the same extension.'
76 * @param {string} methodName
77 * @param {?Array} params
81 base
.Ipc
.Request_ = function(methodName
, params
) {
82 this.methodName
= methodName
;
88 * @param {string} methodName
89 * @param {Function} handler The handler can be invoked by calling
90 * base.Ipc.invoke(|methodName|, arg1, arg2, ...)
91 * Async handlers that return promises are currently not supported.
92 * @return {boolean} Whether the handler is successfully registered.
94 base
.Ipc
.prototype.register = function(methodName
, handler
) {
95 if (methodName
in this.handlers_
) {
96 console
.error('service ' + methodName
+ ' is already registered.');
99 this.handlers_
[methodName
] = handler
;
104 * @param {string} methodName
106 base
.Ipc
.prototype.unregister = function(methodName
) {
107 delete this.handlers_
[methodName
];
111 * @param {base.Ipc.Request_} message
112 * @param {chrome.runtime.MessageSender} sender
113 * @param {function(*): void} sendResponse
115 base
.Ipc
.prototype.onMessage_ = function(message
, sender
, sendResponse
) {
116 var methodName
= message
.methodName
;
117 if (typeof methodName
!== 'string') {
121 if (sender
.id
!== chrome
.runtime
.id
) {
122 sendResponse({error
: base
.Ipc
.Error
.INVALID_REQUEST_ORIGIN
});
127 /** @type {function(*):void} */ (this.handlers_
[methodName
]);
129 sendResponse({error
: base
.Ipc
.Error
.UNSUPPORTED_REQUEST_TYPE
});
134 sendResponse(remoteMethod
.apply(null, message
.params
));
135 } catch (/** @type {Error} */ e
) {
136 sendResponse({error
: e
.message
});
141 * Invokes a method on a remote page
143 * @param {string} methodName
144 * @param {...} var_args
145 * @return {Promise} A Promise that would resolve to the return value of the
146 * handler or reject if the handler throws an exception.
148 base
.Ipc
.invoke = function(methodName
, var_args
) {
149 var params
= Array
.prototype.slice
.call(arguments
, 1);
150 var sendMessage
= base
.Promise
.as(
151 chrome
.runtime
.sendMessage
,
152 [null, new base
.Ipc
.Request_(methodName
, params
)]);
154 return sendMessage
.then(
155 /** @param {?{error: Error}} response */
157 if (response
&& response
.error
) {
158 return Promise
.reject(response
.error
);
160 return Promise
.resolve(response
);
166 /** @type {base.Ipc} */
167 var instance_
= null;
169 /** @return {base.Ipc} */
170 base
.Ipc
.getInstance = function() {
172 instance_
= new base
.Ipc();
177 base
.Ipc
.deleteInstance = function() {
179 instance_
.dispose_();