| 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..07d175cb7497ecd8b09a0c2ac37ef3093c744728
|
| --- /dev/null
|
| +++ b/remoting/webapp/base/js/ipc.js
|
| @@ -0,0 +1,187 @@
|
| +// 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, ....
|
| +* 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
|
| + * @private
|
| + */
|
| +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_);
|
| +};
|
| +
|
| +/**
|
| + * The error strings are only used for debugging purposes and are not localized.
|
| + *
|
| + * @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
|
| + * @private
|
| + */
|
| +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;
|
| + }
|
| +};
|
| +
|
| +})();
|
|
|