Chromium Code Reviews| Index: remoting/webapp/base/js/ipc.js |
| diff --git a/remoting/webapp/base/js/ipc.js b/remoting/webapp/base/js/ipc.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ea2359a32b7bedadf7812331ef327a2442056f2f |
| --- /dev/null |
| +++ b/remoting/webapp/base/js/ipc.js |
| @@ -0,0 +1,182 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +/** |
| +* @fileoverview |
| +* |
| +* In Chrome Apps, some platform APIs can only be called from the background |
| +* page (e.g. reloading a chrome.app.AppWindow). Likewise, some chrome API's |
| +* must be initiated by user interaction, which can only be called from the |
| +* foreground. |
| +* |
| +* This class provides helper functions to invoke methods on different pages |
| +* using chrome.runtime.sendMessage. Messages are passed in the following |
| +* format: |
| +* {methodName:{string}, params:{Array}} |
| +* |
| +* chrome.runtime.sendMessage allows multiple handlers to be registered on a |
| +* document, but only one handler can send a response. |
| +* This class uniquely identifies a method with the |methodName| and enforces |
| +* that only one handler can be registered per |methodName| in the document. |
| +* |
| +* For example, to call method foo() in the background page from the foreground |
| +* chrome.app.AppWindow, you can do the following. |
| +* In the background page: |
| +* base.IPC.getInstance().register('my.service.name', foo); |
| +* |
| +* In the AppWindow document: |
| +* base.IPC.invoke('my.service.name', arg1, arg2, ...).then( |
| +* function(result) { |
| +* console.log('The result is ' + result); |
| +* }); |
| +* |
| +* This will invoke foo() with the arg1, arg2 and ... . |
|
Jamie
2015/01/27 22:52:24
"with arg1, arg2, ...."
kelvinp
2015/01/28 01:31:51
Done.
|
| +* The return value of foo() will be passed back to the caller in the |
| +* form of a promise. |
| +*/ |
| + |
| +/** @suppress {duplicate} */ |
| +var base = base || {}; |
| + |
| +(function() { |
| + |
| +'use strict'; |
| + |
| +/** |
| + * @constructor |
| + */ |
| +base.IPC = function() { |
| + base.debug.assert(instance_ === null); |
| + /** |
| + * @type {!Object.<Function>} |
| + * @private |
| + */ |
| + this.handlers_ = {}; |
| + this.onMessageHandler_ = this.onMessage_.bind(this); |
| + chrome.runtime.onMessage.addListener(this.onMessageHandler_); |
| +}; |
| + |
| +/** @private */ |
| +base.IPC.prototype.dispose_ = function() { |
| + chrome.runtime.onMessage.removeListener(this.onMessageHandler_); |
| +}; |
| + |
| +/** @enum {string} */ |
| +base.IPC.Error = { |
| + UNSUPPORTED_REQUEST_TYPE: 'Unsupported method name.', |
| + INVALID_REQUEST_ORIGIN: |
| + 'base.IPC only accept incoming requests from the same extension.' |
| +}; |
| + |
| +/** |
| + * @constructor |
| + * @param {string} methodName |
| + * @param {?Array} params |
| + * @struct |
| + */ |
| +base.IPC.Request_ = function(methodName, params) { |
| + this.methodName = methodName; |
| + this.params = params; |
| +}; |
| + |
| + |
| +/** |
| + * @param {string} methodName |
| + * @param {Function} handler The handler can be invoked by calling |
| + * base.IPC.invoke(|methodName|, arg1, arg2, ...) |
| + * Async handlers that return promises are currently not supported. |
| + * @return {boolean} Whether the handler is successfully registered. |
| + */ |
| +base.IPC.prototype.register = function(methodName, handler) { |
| + if (methodName in this.handlers_) { |
| + console.error('service :' + methodName + ' is already registered.'); |
| + return false; |
| + } |
| + this.handlers_[methodName] = handler; |
| + return true; |
| +}; |
| + |
| +/** |
| + * @param {string} methodName |
| + */ |
| +base.IPC.prototype.unregister = function(methodName) { |
| + delete this.handlers_[methodName]; |
| +}; |
| + |
| +/** |
| + * @param {base.IPC.Request_} message |
| + * @param {chrome.runtime.MessageSender} sender |
| + * @param {function(*): void} sendResponse |
| + */ |
| +base.IPC.prototype.onMessage_ = function(message, sender, |
| + sendResponse) { |
| + var methodName = message.methodName; |
| + if (typeof methodName !== 'string') { |
| + return; |
| + } |
| + |
| + if (sender.id !== chrome.runtime.id) { |
| + sendResponse({error : base.IPC.Error.INVALID_REQUEST_ORIGIN}); |
| + return; |
| + } |
| + |
| + var remoteMethod = |
| + /** @type {function(*):void} */ (this.handlers_[methodName]); |
| + if (!remoteMethod) { |
| + sendResponse({error : base.IPC.Error.UNSUPPORTED_REQUEST_TYPE}); |
| + return; |
| + } |
| + |
| + try { |
| + sendResponse(remoteMethod.apply(null, message.params)); |
| + } catch (/** @type {Error} */ e) { |
| + sendResponse({error: e.message}); |
| + } |
| +}; |
| + |
| +/** |
| + * Invokes a method on a remote page |
| + * |
| + * @param {string} methodName |
| + * @param {...} var_args |
| + * @return A Promise that would resolve to the return value of the handler or |
| + * reject if the handler throws an exception. |
| + */ |
| +base.IPC.invoke = function(methodName, var_args) { |
| + var params = Array.prototype.slice.call(arguments, 1); |
| + var sendMessage = base.Promise.as( |
| + chrome.runtime.sendMessage, |
| + [null, new base.IPC.Request_(methodName, params)]); |
| + |
| + return sendMessage.then( |
| + /** @param {?{error: Error}} response */ |
| + function(response) { |
| + if (response && response.error) { |
| + return Promise.reject(response.error); |
| + } else { |
| + return Promise.resolve(response); |
| + } |
| + }); |
| +}; |
| + |
| + |
| +/** @type {base.IPC} */ |
| +var instance_ = null; |
| + |
| +/** @return {base.IPC} */ |
| +base.IPC.getInstance = function() { |
| + if (!instance_) { |
| + instance_ = new base.IPC(); |
| + } |
| + return instance_; |
| +}; |
| + |
| +base.IPC.deleteInstance = function() { |
| + if (instance_) { |
| + instance_.dispose_(); |
| + instance_ = null; |
| + } |
| +}; |
| + |
| +})(); |