| // Copyright 2013 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 |
| * This class provides an interface between the HostController and either the |
| * NativeMessaging Host or the Host NPAPI plugin, depending on whether |
| * NativeMessaging is supported. Since the test for NativeMessaging support is |
| * asynchronous, this class stores any requests on a queue, pending the result |
| * of the test. |
| * Once the test is complete, the pending requests are performed on either the |
| * NativeMessaging host, or the NPAPI plugin. |
| * |
| * If necessary, the HostController is instructed (via a callback) to |
| * instantiate the NPAPI plugin, and return a reference to it here. |
| */ |
| |
| 'use strict'; |
| |
| /** @suppress {duplicate} */ |
| var remoting = remoting || {}; |
| |
| /** |
| * @constructor |
| * @param {function():remoting.HostPlugin} createPluginCallback Callback to |
| * instantiate the NPAPI plugin when NativeMessaging is determined to be |
| * unsupported. |
| */ |
| remoting.HostDispatcher = function(createPluginCallback) { |
| /** @type {remoting.HostDispatcher} */ |
| var that = this; |
| |
| /** @type {remoting.HostNativeMessaging} @private */ |
| this.nativeMessagingHost_ = new remoting.HostNativeMessaging(); |
| |
| /** @type {remoting.HostPlugin} @private */ |
| this.npapiHost_ = null; |
| |
| /** @type {remoting.HostDispatcher.State} */ |
| this.state_ = remoting.HostDispatcher.State.UNKNOWN; |
| |
| /** @type {Array.<function()>} */ |
| this.pendingRequests_ = []; |
| |
| /** @param {boolean} success */ |
| var onNativeMessagingInit = function(success) { |
| if (success) { |
| console.log('Native Messaging supported.'); |
| that.state_ = remoting.HostDispatcher.State.NATIVE_MESSAGING; |
| } else { |
| console.log('Native Messaging unsupported, falling back to NPAPI.'); |
| that.npapiHost_ = createPluginCallback(); |
| that.state_ = remoting.HostDispatcher.State.NPAPI; |
| } |
| // Send pending requests. |
| for (var i = 0; i < that.pendingRequests_.length; i++) { |
| that.pendingRequests_[i](); |
| } |
| that.pendingRequests_ = null; |
| }; |
| |
| this.nativeMessagingHost_.initialize(onNativeMessagingInit); |
| }; |
| |
| /** @enum {number} */ |
| remoting.HostDispatcher.State = { |
| UNKNOWN: 0, |
| NATIVE_MESSAGING: 1, |
| NPAPI: 2 |
| }; |
| |
| /** |
| * @param {function(string):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getHostName = function(callback, onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getHostName.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.getHostName(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.getHostName(callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {string} hostId |
| * @param {string} pin |
| * @param {function(string):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getPinHash = function(hostId, pin, callback, |
| onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getPinHash.bind(this, hostId, pin, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.getPinHash(hostId, pin, callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.getPinHash(hostId, pin, callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(string, string):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.generateKeyPair = function(callback, |
| onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.generateKeyPair.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.generateKeyPair(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.generateKeyPair(callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {Object} config |
| * @param {function(remoting.HostController.AsyncResult):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.updateDaemonConfig = function(config, |
| callback, |
| onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.updateDaemonConfig.bind(this, config, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.updateDaemonConfig(config, callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.updateDaemonConfig(JSON.stringify(config), callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(Object):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getDaemonConfig = function(callback, |
| onError) { |
| /** |
| * Converts the config string from the NPAPI plugin to an Object, to pass to |
| * |callback|. |
| * @param {string} configStr |
| * @return {void} |
| */ |
| function callbackForNpapi(configStr) { |
| var config = jsonParseSafe(configStr); |
| if (typeof(config) != 'object') { |
| onError(remoting.Error.UNEXPECTED); |
| } else { |
| callback(/** @type {Object} */ (config)); |
| } |
| } |
| |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getDaemonConfig.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.getDaemonConfig(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.getDaemonConfig(callbackForNpapi); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(string):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getDaemonVersion = function(callback, |
| onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getDaemonVersion.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| callback(this.nativeMessagingHost_.getDaemonVersion()); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.getDaemonVersion(callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(boolean, boolean, boolean):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getUsageStatsConsent = function(callback, |
| onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getUsageStatsConsent.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.getUsageStatsConsent(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.getUsageStatsConsent(callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {Object} config |
| * @param {boolean} consent |
| * @param {function(remoting.HostController.AsyncResult):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.startDaemon = function(config, consent, |
| callback, onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.startDaemon.bind(this, config, consent, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.startDaemon(config, consent, callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.startDaemon(JSON.stringify(config), consent, callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(remoting.HostController.AsyncResult):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.stopDaemon = function(callback, onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push(this.stopDaemon.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.stopDaemon(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| try { |
| this.npapiHost_.stopDaemon(callback); |
| } catch (err) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } |
| break; |
| } |
| }; |
| |
| /** |
| * @param {function(remoting.HostController.State):void} callback |
| * @param {function(remoting.Error):void} onError |
| * @return {void} |
| */ |
| remoting.HostDispatcher.prototype.getDaemonState = function(callback, onError) { |
| switch (this.state_) { |
| case remoting.HostDispatcher.State.UNKNOWN: |
| this.pendingRequests_.push( |
| this.getDaemonState.bind(this, callback, onError)); |
| break; |
| case remoting.HostDispatcher.State.NATIVE_MESSAGING: |
| this.nativeMessagingHost_.getDaemonState(callback, onError); |
| break; |
| case remoting.HostDispatcher.State.NPAPI: |
| // Call the callback directly, since NPAPI exposes the state directly as |
| // a field member, rather than an asynchronous method. |
| var state = this.npapiHost_.daemonState; |
| if (state === undefined) { |
| onError(remoting.Error.MISSING_PLUGIN); |
| } else { |
| callback(state); |
| } |
| break; |
| } |
| } |