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