1 // Copyright 2014 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.
7 * Class to communicate with the host daemon via Native Messaging.
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
18 remoting.HostDaemonFacade = function() {
26 * @type {Object.<number, remoting.HostDaemonFacade.PendingReply>}
29 this.pendingReplies_ = {};
31 /** @type {?chrome.runtime.Port} @private */
34 /** @type {string} @private */
37 /** @type {Array.<remoting.HostController.Feature>} @private */
38 this.supportedFeatures_ = [];
40 /** @type {Array.<function(boolean):void>} @private */
41 this.afterInitializationTasks_ = [];
44 this.initializationFinished_ = false;
46 /** @type {remoting.Error} @private */
47 this.error_ = remoting.Error.NONE;
50 this.port_ = chrome.runtime.connectNative(
51 'com.google.chrome.remote_desktop');
52 this.port_.onMessage.addListener(this.onIncomingMessage_.bind(this));
53 this.port_.onDisconnect.addListener(this.onDisconnect_.bind(this));
54 this.postMessage_({type: 'hello'},
55 this.onInitialized_.bind(this, true),
56 this.onInitialized_.bind(this, false));
58 console.log('Native Messaging initialization failed: ',
59 /** @type {*} */ (err));
60 this.onInitialized_(false);
65 * Type used for entries of |pendingReplies_| list.
67 * @param {string} type Type of the originating request.
68 * @param {function(...):void} onDone Response callback. Parameters depend on
70 * @param {function(remoting.Error):void} onError Callback to call on error.
73 remoting.HostDaemonFacade.PendingReply = function(type, onDone, onError) {
76 this.onError = onError;
80 * @param {boolean} success
81 * @return {void} Nothing.
84 remoting.HostDaemonFacade.prototype.onInitialized_ = function(success) {
85 this.initializationFinished_ = true;
86 var afterInitializationTasks = this.afterInitializationTasks_;
87 this.afterInitializationTasks_ = [];
88 for (var id in afterInitializationTasks) {
89 afterInitializationTasks[/** @type {number} */(id)](success);
94 * @param {remoting.HostController.Feature} feature The feature to test for.
95 * @param {function(boolean):void} onDone Callback to return result.
96 * @return {boolean} True if the implementation supports the named feature.
98 remoting.HostDaemonFacade.prototype.hasFeature = function(feature, onDone) {
101 } else if (this.initializationFinished_) {
102 onDone(this.supportedFeatures_.indexOf(feature) >= 0);
104 /** @type remoting.HostDaemonFacade */
106 this.afterInitializationTasks_.push(
107 /** @param {boolean} success */
109 onDone(that.supportedFeatures_.indexOf(feature) >= 0);
115 * Attaches a new ID to the supplied message, and posts it to the Native
116 * Messaging port, adding |onDone| to the list of pending replies.
117 * |message| should have its 'type' field set, and any other fields set
118 * depending on the message type.
120 * @param {{type: string}} message The message to post.
121 * @param {function(...):void} onDone The callback, if any, to be triggered
123 * @param {function(remoting.Error):void} onError Callback to call on error.
124 * @return {void} Nothing.
127 remoting.HostDaemonFacade.prototype.postMessage_ =
128 function(message, onDone, onError) {
130 onError(this.error_);
133 var id = this.nextId_++;
135 this.pendingReplies_[id] = new remoting.HostDaemonFacade.PendingReply(
136 message.type + 'Response', onDone, onError);
137 this.port_.postMessage(message);
141 * Handler for incoming Native Messages.
143 * @param {Object} message The received message.
144 * @return {void} Nothing.
147 remoting.HostDaemonFacade.prototype.onIncomingMessage_ = function(message) {
148 /** @type {number} */
149 var id = message['id'];
150 if (typeof(id) != 'number') {
151 console.error('NativeMessaging: missing or non-numeric id');
154 var reply = this.pendingReplies_[id];
156 console.error('NativeMessaging: unexpected id: ', id);
159 delete this.pendingReplies_[id];
162 var type = getStringAttr(message, 'type');
163 if (type != reply.type) {
164 throw 'Expected reply type: ' + reply.type + ', got: ' + type;
167 this.handleIncomingMessage_(message, reply.onDone);
169 console.error('Error while processing native message' +
170 /** @type {*} */ (e));
171 reply.onError(remoting.Error.UNEXPECTED);
176 * Handler for incoming Native Messages.
178 * @param {Object} message The received message.
179 * @param {function(...):void} onDone Function to call when we're done
180 * processing the message.
181 * @return {void} Nothing.
184 remoting.HostDaemonFacade.prototype.handleIncomingMessage_ =
185 function(message, onDone) {
186 var type = getStringAttr(message, 'type');
189 case 'helloResponse':
190 this.version_ = getStringAttr(message, 'version');
191 // Old versions of the native messaging host do not return this list.
192 // Those versions default to the empty list of supported features.
193 this.supportedFeatures_ = getArrayAttr(message, 'supportedFeatures', []);
197 case 'getHostNameResponse':
198 onDone(getStringAttr(message, 'hostname'));
201 case 'getPinHashResponse':
202 onDone(getStringAttr(message, 'hash'));
205 case 'generateKeyPairResponse':
206 var privateKey = getStringAttr(message, 'privateKey');
207 var publicKey = getStringAttr(message, 'publicKey');
208 onDone(privateKey, publicKey);
211 case 'updateDaemonConfigResponse':
212 var result = remoting.HostController.AsyncResult.fromString(
213 getStringAttr(message, 'result'));
217 case 'getDaemonConfigResponse':
218 onDone(getObjectAttr(message, 'config'));
221 case 'getUsageStatsConsentResponse':
222 var supported = getBooleanAttr(message, 'supported');
223 var allowed = getBooleanAttr(message, 'allowed');
224 var setByPolicy = getBooleanAttr(message, 'setByPolicy');
225 onDone(supported, allowed, setByPolicy);
228 case 'startDaemonResponse':
229 case 'stopDaemonResponse':
230 var result = remoting.HostController.AsyncResult.fromString(
231 getStringAttr(message, 'result'));
235 case 'getDaemonStateResponse':
236 var state = remoting.HostController.State.fromString(
237 getStringAttr(message, 'state'));
241 case 'getPairedClientsResponse':
242 var pairedClients = remoting.PairedClient.convertToPairedClientArray(
243 message['pairedClients']);
244 if (pairedClients != null) {
245 onDone(pairedClients);
247 throw 'No paired clients!';
251 case 'clearPairedClientsResponse':
252 case 'deletePairedClientResponse':
253 onDone(getBooleanAttr(message, 'result'));
256 case 'getHostClientIdResponse':
257 onDone(getStringAttr(message, 'clientId'));
260 case 'getCredentialsFromAuthCodeResponse':
261 var userEmail = getStringAttr(message, 'userEmail');
262 var refreshToken = getStringAttr(message, 'refreshToken');
263 if (userEmail && refreshToken) {
264 onDone(userEmail, refreshToken);
266 throw 'Missing userEmail or refreshToken';
271 throw 'Unexpected native message: ' + message;
276 * @return {void} Nothing.
279 remoting.HostDaemonFacade.prototype.onDisconnect_ = function() {
280 console.error('Native Message port disconnected');
284 // If initialization hasn't finished then assume that the port was
285 // disconnected because Native Messaging host is not installed.
286 this.error_ = this.initializationFinished_ ? remoting.Error.UNEXPECTED :
287 remoting.Error.MISSING_PLUGIN;
289 // Notify the error-handlers of any requests that are still outstanding.
290 var pendingReplies = this.pendingReplies_;
291 this.pendingReplies_ = {};
292 for (var id in pendingReplies) {
293 pendingReplies[/** @type {number} */(id)].onError(this.error_);
298 * Gets local hostname.
300 * @param {function(string):void} onDone Callback to return result.
301 * @param {function(remoting.Error):void} onError Callback to call on error.
302 * @return {void} Nothing.
304 remoting.HostDaemonFacade.prototype.getHostName =
305 function(onDone, onError) {
306 this.postMessage_({type: 'getHostName'}, onDone, onError);
310 * Calculates PIN hash value to be stored in the config, passing the resulting
311 * hash value base64-encoded to the callback.
313 * @param {string} hostId The host ID.
314 * @param {string} pin The PIN.
315 * @param {function(string):void} onDone Callback to return result.
316 * @param {function(remoting.Error):void} onError Callback to call on error.
317 * @return {void} Nothing.
319 remoting.HostDaemonFacade.prototype.getPinHash =
320 function(hostId, pin, onDone, onError) {
329 * Generates new key pair to use for the host. The specified callback is called
330 * when the key is generated. The key is returned in format understood by the
331 * host (PublicKeyInfo structure encoded with ASN.1 DER, and then BASE64).
333 * @param {function(string, string):void} onDone Callback to return result.
334 * @param {function(remoting.Error):void} onError Callback to call on error.
335 * @return {void} Nothing.
337 remoting.HostDaemonFacade.prototype.generateKeyPair =
338 function(onDone, onError) {
339 this.postMessage_({type: 'generateKeyPair'}, onDone, onError);
343 * Updates host config with the values specified in |config|. All
344 * fields that are not specified in |config| remain
345 * unchanged. Following parameters cannot be changed using this
346 * function: host_id, xmpp_login. Error is returned if |config|
347 * includes these parameters. Changes take effect before the callback
350 * @param {Object} config The new config parameters.
351 * @param {function(remoting.HostController.AsyncResult):void} onDone
352 * Callback to be called when finished.
353 * @param {function(remoting.Error):void} onError Callback to call on error.
354 * @return {void} Nothing.
356 remoting.HostDaemonFacade.prototype.updateDaemonConfig =
357 function(config, onDone, onError) {
359 type: 'updateDaemonConfig',
365 * Loads daemon config. The config is passed as a JSON formatted string to the
368 * @param {function(Object):void} onDone Callback to return result.
369 * @param {function(remoting.Error):void} onError Callback to call on error.
370 * @return {void} Nothing.
372 remoting.HostDaemonFacade.prototype.getDaemonConfig =
373 function(onDone, onError) {
374 this.postMessage_({type: 'getDaemonConfig'}, onDone, onError);
378 * Retrieves daemon version. The version is passed to onDone as a dotted decimal
379 * string of the form major.minor.build.patch.
380 * @param {function(string):void} onDone Callback to be called to return result.
381 * @param {function(remoting.Error):void} onError Callback to call on error.
384 remoting.HostDaemonFacade.prototype.getDaemonVersion =
385 function(onDone, onError) {
387 onError(remoting.Error.UNEXPECTED);
388 } else if (this.initializationFinished_) {
389 onDone(this.version_);
391 /** @type remoting.HostDaemonFacade */
393 this.afterInitializationTasks_.push(
394 /** @param {boolean} success */
397 onDone(that.version_);
399 onError(that.error_);
406 * Get the user's consent to crash reporting. The consent flags are passed to
407 * the callback as booleans: supported, allowed, set-by-policy.
409 * @param {function(boolean, boolean, boolean):void} onDone Callback to return
411 * @param {function(remoting.Error):void} onError Callback to call on error.
412 * @return {void} Nothing.
414 remoting.HostDaemonFacade.prototype.getUsageStatsConsent =
415 function(onDone, onError) {
416 this.postMessage_({type: 'getUsageStatsConsent'}, onDone, onError);
420 * Starts the daemon process with the specified configuration.
422 * @param {Object} config Host configuration.
423 * @param {boolean} consent Consent to report crash dumps.
424 * @param {function(remoting.HostController.AsyncResult):void} onDone
425 * Callback to return result.
426 * @param {function(remoting.Error):void} onError Callback to call on error.
427 * @return {void} Nothing.
429 remoting.HostDaemonFacade.prototype.startDaemon =
430 function(config, consent, onDone, onError) {
439 * Stops the daemon process.
441 * @param {function(remoting.HostController.AsyncResult):void} onDone
442 * Callback to return result.
443 * @param {function(remoting.Error):void} onError Callback to call on error.
444 * @return {void} Nothing.
446 remoting.HostDaemonFacade.prototype.stopDaemon =
447 function(onDone, onError) {
448 this.postMessage_({type: 'stopDaemon'}, onDone, onError);
452 * Gets the installed/running state of the Host process.
454 * @param {function(remoting.HostController.State):void} onDone Callback to
456 * @param {function(remoting.Error):void} onError Callback to call on error.
457 * @return {void} Nothing.
459 remoting.HostDaemonFacade.prototype.getDaemonState =
460 function(onDone, onError) {
461 this.postMessage_({type: 'getDaemonState'}, onDone, onError);
465 * Retrieves the list of paired clients.
467 * @param {function(Array.<remoting.PairedClient>):void} onDone Callback to
469 * @param {function(remoting.Error):void} onError Callback to call on error.
471 remoting.HostDaemonFacade.prototype.getPairedClients =
472 function(onDone, onError) {
473 this.postMessage_({type: 'getPairedClients'}, onDone, onError);
477 * Clears all paired clients from the registry.
479 * @param {function(boolean):void} onDone Callback to be called when finished.
480 * @param {function(remoting.Error):void} onError Callback to call on error.
482 remoting.HostDaemonFacade.prototype.clearPairedClients =
483 function(onDone, onError) {
484 this.postMessage_({type: 'clearPairedClients'}, onDone, onError);
488 * Deletes a paired client referenced by client id.
490 * @param {string} client Client to delete.
491 * @param {function(boolean):void} onDone Callback to be called when finished.
492 * @param {function(remoting.Error):void} onError Callback to call on error.
494 remoting.HostDaemonFacade.prototype.deletePairedClient =
495 function(client, onDone, onError) {
497 type: 'deletePairedClient',
503 * Gets the API keys to obtain/use service account credentials.
505 * @param {function(string):void} onDone Callback to return result.
506 * @param {function(remoting.Error):void} onError Callback to call on error.
507 * @return {void} Nothing.
509 remoting.HostDaemonFacade.prototype.getHostClientId =
510 function(onDone, onError) {
511 this.postMessage_({type: 'getHostClientId'}, onDone, onError);
516 * @param {string} authorizationCode OAuth authorization code.
517 * @param {function(string, string):void} onDone Callback to return result.
518 * @param {function(remoting.Error):void} onError Callback to call on error.
519 * @return {void} Nothing.
521 remoting.HostDaemonFacade.prototype.getCredentialsFromAuthCode =
522 function(authorizationCode, onDone, onError) {
524 type: 'getCredentialsFromAuthCode',
525 authorizationCode: authorizationCode