Home | History | Annotate | Download | only in webapp
      1 // Copyright 2013 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.
      4 
      5 /**
      6  * @fileoverview
      7  * This class provides an interface between the HostController and either the
      8  * NativeMessaging Host.
      9  */
     10 
     11 'use strict';
     12 
     13 /** @suppress {duplicate} */
     14 var remoting = remoting || {};
     15 
     16 /**
     17  * @constructor
     18  */
     19 remoting.HostDispatcher = function() {
     20   /** @type {remoting.HostNativeMessaging} @private */
     21   this.nativeMessagingHost_ = new remoting.HostNativeMessaging();
     22 
     23   /** @type {remoting.HostDispatcher.State} @private */
     24   this.state_ = remoting.HostDispatcher.State.UNKNOWN;
     25 
     26   /** @type {Array.<function()>} @private */
     27   this.pendingRequests_ = [];
     28 
     29   this.tryToInitialize_();
     30 }
     31 
     32 /** @enum {number} */
     33 remoting.HostDispatcher.State = {
     34   UNKNOWN: 0,
     35   NATIVE_MESSAGING: 1,
     36   NOT_INSTALLED: 2
     37 };
     38 
     39 remoting.HostDispatcher.prototype.tryToInitialize_ = function() {
     40   /** @type {remoting.HostDispatcher} */
     41   var that = this;
     42 
     43   if (this.state_ != remoting.HostDispatcher.State.UNKNOWN)
     44     return;
     45 
     46   function sendPendingRequests() {
     47     var pendingRequests = that.pendingRequests_;
     48     that.pendingRequests_ = [];
     49     for (var i = 0; i < pendingRequests.length; i++) {
     50       pendingRequests[i]();
     51     }
     52   }
     53 
     54   function onNativeMessagingInit() {
     55     that.state_ = remoting.HostDispatcher.State.NATIVE_MESSAGING;
     56     sendPendingRequests();
     57   }
     58 
     59   function onNativeMessagingFailed(error) {
     60     that.state_ = remoting.HostDispatcher.State.NOT_INSTALLED;
     61     sendPendingRequests();
     62   }
     63 
     64   this.nativeMessagingHost_.initialize(onNativeMessagingInit,
     65                                        onNativeMessagingFailed);
     66 };
     67 
     68 /**
     69  * @param {remoting.HostController.Feature} feature The feature to test for.
     70  * @param {function(boolean):void} onDone
     71  * @return {void}
     72  */
     73 remoting.HostDispatcher.prototype.hasFeature = function(
     74     feature, onDone) {
     75   switch (this.state_) {
     76     case remoting.HostDispatcher.State.UNKNOWN:
     77       this.pendingRequests_.push(
     78           this.hasFeature.bind(this, feature, onDone));
     79       break;
     80     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
     81       onDone(this.nativeMessagingHost_.hasFeature(feature));
     82       break;
     83     case remoting.HostDispatcher.State.NOT_INSTALLED:
     84       onDone(false);
     85       break;
     86   }
     87 };
     88 
     89 /**
     90  * @param {function(string):void} onDone
     91  * @param {function(remoting.Error):void} onError
     92  * @return {void}
     93  */
     94 remoting.HostDispatcher.prototype.getHostName = function(onDone, onError) {
     95   switch (this.state_) {
     96     case remoting.HostDispatcher.State.UNKNOWN:
     97       this.pendingRequests_.push(
     98           this.getHostName.bind(this, onDone, onError));
     99       break;
    100     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    101       this.nativeMessagingHost_.getHostName(onDone, onError);
    102       break;
    103     case remoting.HostDispatcher.State.NOT_INSTALLED:
    104       onError(remoting.Error.MISSING_PLUGIN);
    105       break;
    106   }
    107 };
    108 
    109 /**
    110  * @param {string} hostId
    111  * @param {string} pin
    112  * @param {function(string):void} onDone
    113  * @param {function(remoting.Error):void} onError
    114  * @return {void}
    115  */
    116 remoting.HostDispatcher.prototype.getPinHash =
    117     function(hostId, pin, onDone, onError) {
    118   switch (this.state_) {
    119     case remoting.HostDispatcher.State.UNKNOWN:
    120       this.pendingRequests_.push(
    121           this.getPinHash.bind(this, hostId, pin, onDone, onError));
    122       break;
    123     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    124       this.nativeMessagingHost_.getPinHash(hostId, pin, onDone, onError);
    125       break;
    126     case remoting.HostDispatcher.State.NOT_INSTALLED:
    127       onError(remoting.Error.MISSING_PLUGIN);
    128       break;
    129   }
    130 };
    131 
    132 /**
    133  * @param {function(string, string):void} onDone
    134  * @param {function(remoting.Error):void} onError
    135  * @return {void}
    136  */
    137 remoting.HostDispatcher.prototype.generateKeyPair = function(onDone, onError) {
    138   switch (this.state_) {
    139     case remoting.HostDispatcher.State.UNKNOWN:
    140       this.pendingRequests_.push(
    141           this.generateKeyPair.bind(this, onDone, onError));
    142       break;
    143     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    144       this.nativeMessagingHost_.generateKeyPair(onDone, onError);
    145       break;
    146     case remoting.HostDispatcher.State.NOT_INSTALLED:
    147       onError(remoting.Error.MISSING_PLUGIN);
    148       break;
    149   }
    150 };
    151 
    152 /**
    153  * @param {Object} config
    154  * @param {function(remoting.HostController.AsyncResult):void} onDone
    155  * @param {function(remoting.Error):void} onError
    156  * @return {void}
    157  */
    158 remoting.HostDispatcher.prototype.updateDaemonConfig =
    159     function(config, onDone, onError) {
    160   switch (this.state_) {
    161     case remoting.HostDispatcher.State.UNKNOWN:
    162       this.pendingRequests_.push(
    163           this.updateDaemonConfig.bind(this, config, onDone, onError));
    164       break;
    165     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    166       this.nativeMessagingHost_.updateDaemonConfig(config, onDone, onError);
    167       break;
    168     case remoting.HostDispatcher.State.NOT_INSTALLED:
    169       onError(remoting.Error.MISSING_PLUGIN);
    170       break;
    171   }
    172 };
    173 
    174 /**
    175  * @param {function(Object):void} onDone
    176  * @param {function(remoting.Error):void} onError
    177  * @return {void}
    178  */
    179 remoting.HostDispatcher.prototype.getDaemonConfig = function(onDone, onError) {
    180   switch (this.state_) {
    181     case remoting.HostDispatcher.State.UNKNOWN:
    182       this.pendingRequests_.push(
    183           this.getDaemonConfig.bind(this, onDone, onError));
    184       break;
    185     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    186       this.nativeMessagingHost_.getDaemonConfig(onDone, onError);
    187       break;
    188     case remoting.HostDispatcher.State.NOT_INSTALLED:
    189       onDone({});
    190       break;
    191   }
    192 };
    193 
    194 /**
    195  * @param {function(string):void} onDone
    196  * @param {function(remoting.Error):void} onError
    197  * @return {void}
    198  */
    199 remoting.HostDispatcher.prototype.getDaemonVersion = function(onDone, onError) {
    200   switch (this.state_) {
    201     case remoting.HostDispatcher.State.UNKNOWN:
    202       this.pendingRequests_.push(
    203           this.getDaemonVersion.bind(this, onDone, onError));
    204       break;
    205     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    206       onDone(this.nativeMessagingHost_.getDaemonVersion());
    207       break;
    208     case remoting.HostDispatcher.State.NOT_INSTALLED:
    209       onError(remoting.Error.MISSING_PLUGIN);
    210       break;
    211   }
    212 };
    213 
    214 /**
    215  * @param {function(boolean, boolean, boolean):void} onDone
    216  * @param {function(remoting.Error):void} onError
    217  * @return {void}
    218  */
    219 remoting.HostDispatcher.prototype.getUsageStatsConsent =
    220     function(onDone, onError) {
    221   switch (this.state_) {
    222     case remoting.HostDispatcher.State.UNKNOWN:
    223       this.pendingRequests_.push(
    224           this.getUsageStatsConsent.bind(this, onDone, onError));
    225       break;
    226     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    227       this.nativeMessagingHost_.getUsageStatsConsent(onDone, onError);
    228       break;
    229     case remoting.HostDispatcher.State.NOT_INSTALLED:
    230       onError(remoting.Error.MISSING_PLUGIN);
    231       break;
    232   }
    233 };
    234 
    235 /**
    236  * @param {Object} config
    237  * @param {boolean} consent
    238  * @param {function(remoting.HostController.AsyncResult):void} onDone
    239  * @param {function(remoting.Error):void} onError
    240  * @return {void}
    241  */
    242 remoting.HostDispatcher.prototype.startDaemon =
    243     function(config, consent, onDone, onError) {
    244   switch (this.state_) {
    245     case remoting.HostDispatcher.State.UNKNOWN:
    246       this.pendingRequests_.push(
    247           this.startDaemon.bind(this, config, consent, onDone, onError));
    248       break;
    249     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    250       this.nativeMessagingHost_.startDaemon(config, consent, onDone, onError);
    251       break;
    252     case remoting.HostDispatcher.State.NOT_INSTALLED:
    253       onError(remoting.Error.MISSING_PLUGIN);
    254       break;
    255   }
    256 };
    257 
    258 /**
    259  * @param {function(remoting.HostController.AsyncResult):void} onDone
    260  * @param {function(remoting.Error):void} onError
    261  * @return {void}
    262  */
    263 remoting.HostDispatcher.prototype.stopDaemon = function(onDone, onError) {
    264   switch (this.state_) {
    265     case remoting.HostDispatcher.State.UNKNOWN:
    266       this.pendingRequests_.push(this.stopDaemon.bind(this, onDone, onError));
    267       break;
    268     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    269       this.nativeMessagingHost_.stopDaemon(onDone, onError);
    270       break;
    271     case remoting.HostDispatcher.State.NOT_INSTALLED:
    272       onError(remoting.Error.MISSING_PLUGIN);
    273       break;
    274   }
    275 };
    276 
    277 /**
    278  * @param {function(remoting.HostController.State):void} onDone
    279  * @param {function(remoting.Error):void} onError
    280  * @return {void}
    281  */
    282 remoting.HostDispatcher.prototype.getDaemonState = function(onDone, onError) {
    283   // If the host was in not-initialized state try initializing it again in case
    284   // it was installed.
    285   if (this.state_ == remoting.HostDispatcher.State.NOT_INSTALLED) {
    286     this.state_ = remoting.HostDispatcher.State.UNKNOWN;
    287     this.tryToInitialize_();
    288   }
    289 
    290   this.getDaemonStateInternal_(onDone, onError);
    291 }
    292 
    293 /**
    294  * @param {function(remoting.HostController.State):void} onDone
    295  * @param {function(remoting.Error):void} onError
    296  * @return {void}
    297  */
    298 remoting.HostDispatcher.prototype.getDaemonStateInternal_ =
    299     function(onDone, onError) {
    300   switch (this.state_) {
    301     case remoting.HostDispatcher.State.UNKNOWN:
    302       this.pendingRequests_.push(
    303           this.getDaemonStateInternal_.bind(this, onDone, onError));
    304       break;
    305     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    306       this.nativeMessagingHost_.getDaemonState(onDone, onError);
    307       break;
    308     case remoting.HostDispatcher.State.NOT_INSTALLED:
    309       onDone(remoting.HostController.State.NOT_INSTALLED);
    310       break;
    311   }
    312 };
    313 
    314 /**
    315  * @param {function(Array.<remoting.PairedClient>):void} onDone
    316  * @param {function(remoting.Error):void} onError
    317  * @return {void}
    318  */
    319 remoting.HostDispatcher.prototype.getPairedClients = function(onDone, onError) {
    320   switch (this.state_) {
    321     case remoting.HostDispatcher.State.UNKNOWN:
    322       this.pendingRequests_.push(
    323           this.getPairedClients.bind(this, onDone, onError));
    324       break;
    325     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    326       this.nativeMessagingHost_.getPairedClients(onDone, onError);
    327       break;
    328     case remoting.HostDispatcher.State.NOT_INSTALLED:
    329       onError(remoting.Error.MISSING_PLUGIN);
    330       break;
    331   }
    332 };
    333 
    334 /**
    335  * The pairing API returns a boolean to indicate success or failure, but
    336  * the JS API is defined in terms of onDone and onError callbacks. This
    337  * function converts one to the other.
    338  *
    339  * @param {function():void} onDone Success callback.
    340  * @param {function(remoting.Error):void} onError Error callback.
    341  * @param {boolean} success True if the operation succeeded; false otherwise.
    342  * @private
    343  */
    344 remoting.HostDispatcher.runCallback_ = function(onDone, onError, success) {
    345   if (success) {
    346     onDone();
    347   } else {
    348     onError(remoting.Error.UNEXPECTED);
    349   }
    350 };
    351 
    352 /**
    353  * @param {function():void} onDone
    354  * @param {function(remoting.Error):void} onError
    355  * @return {void}
    356  */
    357 remoting.HostDispatcher.prototype.clearPairedClients =
    358     function(onDone, onError) {
    359   var callback =
    360       remoting.HostDispatcher.runCallback_.bind(null, onDone, onError);
    361   switch (this.state_) {
    362     case remoting.HostDispatcher.State.UNKNOWN:
    363       this.pendingRequests_.push(
    364         this.clearPairedClients.bind(this, onDone, onError));
    365       break;
    366     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    367       this.nativeMessagingHost_.clearPairedClients(callback, onError);
    368       break;
    369     case remoting.HostDispatcher.State.NOT_INSTALLED:
    370       onError(remoting.Error.MISSING_PLUGIN);
    371       break;
    372   }
    373 };
    374 
    375 /**
    376  * @param {string} client
    377  * @param {function():void} onDone
    378  * @param {function(remoting.Error):void} onError
    379  * @return {void}
    380  */
    381 remoting.HostDispatcher.prototype.deletePairedClient =
    382     function(client, onDone, onError) {
    383   var callback =
    384       remoting.HostDispatcher.runCallback_.bind(null, onDone, onError);
    385   switch (this.state_) {
    386     case remoting.HostDispatcher.State.UNKNOWN:
    387       this.pendingRequests_.push(
    388         this.deletePairedClient.bind(this, client, onDone, onError));
    389       break;
    390     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    391       this.nativeMessagingHost_.deletePairedClient(client, callback, onError);
    392       break;
    393     case remoting.HostDispatcher.State.NOT_INSTALLED:
    394       onError(remoting.Error.MISSING_PLUGIN);
    395       break;
    396   }
    397 };
    398 
    399 /**
    400  * @param {function(string):void} onDone
    401  * @param {function(remoting.Error):void} onError
    402  * @return {void}
    403  */
    404 remoting.HostDispatcher.prototype.getHostClientId =
    405     function(onDone, onError) {
    406   switch (this.state_) {
    407     case remoting.HostDispatcher.State.UNKNOWN:
    408       this.pendingRequests_.push(
    409           this.getHostClientId.bind(this, onDone, onError));
    410       break;
    411     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    412       this.nativeMessagingHost_.getHostClientId(onDone, onError);
    413       break;
    414     case remoting.HostDispatcher.State.NOT_INSTALLED:
    415       onError(remoting.Error.MISSING_PLUGIN);
    416       break;
    417   }
    418 };
    419 
    420 /**
    421  * @param {string} authorizationCode
    422  * @param {function(string, string):void} onDone
    423  * @param {function(remoting.Error):void} onError
    424  * @return {void}
    425  */
    426 remoting.HostDispatcher.prototype.getCredentialsFromAuthCode =
    427     function(authorizationCode, onDone, onError) {
    428   switch (this.state_) {
    429     case remoting.HostDispatcher.State.UNKNOWN:
    430       this.pendingRequests_.push(
    431           this.getCredentialsFromAuthCode.bind(
    432               this, authorizationCode, onDone, onError));
    433       break;
    434     case remoting.HostDispatcher.State.NATIVE_MESSAGING:
    435       this.nativeMessagingHost_.getCredentialsFromAuthCode(
    436           authorizationCode, onDone, onError);
    437       break;
    438     case remoting.HostDispatcher.State.NOT_INSTALLED:
    439       onError(remoting.Error.MISSING_PLUGIN);
    440       break;
    441   }
    442 };
    443