Chromium Code Reviews| Index: remoting/webapp/base/js/remote_methods.js |
| diff --git a/remoting/webapp/base/js/remote_methods.js b/remoting/webapp/base/js/remote_methods.js |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..20c0a6aa6b7eb9998daa5dd51c67466913980460 |
| --- /dev/null |
| +++ b/remoting/webapp/base/js/remote_methods.js |
| @@ -0,0 +1,150 @@ |
| +// 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. For example, you can only reload an chrome.app.AppWindow from a |
| +* background page. 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: |
| +* {requestType:{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 request with the requestType and enforces |
| +* that only one handler can be registered per requestType 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: |
| +* var remoteMethods = new base.remoteMethods(); |
| +* remoteMethods.register('my.service.name', foo); |
| +* |
| +* In the AppWindow document: |
| +* 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
|
| +* function(result) { |
| +* console.log('The result is ' + result); |
| +* }); |
| +* |
| +* This will invoke foo() with the args [arg1, arg2]. |
| +* 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 |
| + * @implements {base.Disposable} |
| + */ |
| +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
|
| + /** |
| + * @type {!Object.<Function>} |
| + * @private |
| + */ |
| + this.handlers_ = {}; |
| + this.onMessageHandler_ = this.onMessage_.bind(this); |
| + chrome.runtime.onMessage.addListener(this.onMessageHandler_); |
| +}; |
| + |
| + |
| +/** |
| + * @constructor |
| + * @param {string} requestType |
| + * @param {?Array} params |
| + * @struct |
| + */ |
| +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
|
| + this.requestType = requestType; |
| + this.params = params; |
| +}; |
| + |
| +base.RemoteMethods.prototype.dispose = function() { |
| + chrome.runtime.onMessage.removeListener(this.onMessageHandler_); |
| +}; |
| + |
| +/** |
| + * @param {string} requestType |
|
Jamie
2015/01/23 23:33:26
Since we're talking about methods, I think methodN
|
| + * @param {Function} handler |
|
Jamie
2015/01/23 23:33:26
s/Function/function(*):void/
|
| + * @return {boolean} Whether the handler is successfully registered. |
| + */ |
| +base.RemoteMethods.prototype.register = function(requestType, handler) { |
| + if (requestType in this.handlers_) { |
| + console.error('service :' + requestType + ' is already registered.'); |
| + return false; |
| + } |
| + this.handlers_[requestType] = handler; |
| + return true; |
| +}; |
| + |
| +/** |
| + * @param {string} requestType |
| + */ |
| +base.RemoteMethods.prototype.unregister = function(requestType) { |
| + delete this.handlers_[requestType]; |
| +}; |
| + |
| +/** |
| + * @param {base.RemoteMethods.Request_} message |
| + * @param {chrome.runtime.MessageSender} sender |
| + * @param {function(*): void} sendResponse |
| + */ |
| +base.RemoteMethods.prototype.onMessage_ = function(message, sender, |
| + sendResponse) { |
| + if (sender.id !== chrome.runtime.id) { |
| + // We only handle incoming requests from our extension. |
| + return; |
| + } |
| + |
| + var requestType = message.requestType; |
| + if (typeof requestType !== 'string') { |
| + return; |
| + } |
| + |
| + var remoteMethod = |
| + /** @type {function(*):void} */ (this.handlers_[requestType]); |
| + if (!remoteMethod) { |
| + sendResponse({error : 'Unsupported request:= ' + requestType}); |
|
Jamie
2015/01/23 23:33:26
Nit: s/:=/:/
|
| + return; |
| + } |
| + |
| + try { |
| + sendResponse(remoteMethod.apply(null, message.params)); |
| + } catch (/** @type {Error} */ e) { |
| + sendResponse({error: e.message}); |
| + } |
| +}; |
| + |
| +/** |
| + * Invokes a method on a remote page |
|
Jamie
2015/01/23 23:33:26
Blank (comment) line after method description.
|
| + * @param {string} requestType |
| + * @param {Array} params |
|
Jamie
2015/01/23 23:33:26
@return?
|
| + */ |
| +base.RemoteMethods.invoke = function(requestType, params) { |
| + var sendMessage = base.Promise.as( |
| + chrome.runtime.sendMessage, |
| + [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
|
| + |
| + return sendMessage.then( |
| + /** @param {{error: Error}} response */ |
| + function(response) { |
| + if (response.error) { |
| + Promise.reject(response.error); |
| + } else { |
| + Promise.resolve(response); |
|
Jamie
2015/01/23 23:33:26
Don't these reject/resolves need to be returned?
|
| + } |
| + }); |
| +}; |
| + |
| +})(); |
|
Jamie
2015/01/23 23:33:26
Although we have too many globals in our code, I t
|