Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / remoting / webapp / host_controller.js
blobaf05eab1cf7e5d57e3304d12b87bf4954fb057f9
1 // Copyright (c) 2012 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.
5 'use strict';
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
10 /** @constructor */
11 remoting.HostController = function() {
12 /** @return {remoting.HostPlugin} */
13 var createNpapiPlugin = function() {
14 var plugin = remoting.HostSession.createPlugin();
15 /** @type {HTMLElement} @private */
16 var container = document.getElementById('daemon-plugin-container');
17 container.appendChild(plugin);
18 return plugin;
21 /** @type {remoting.HostDispatcher} @private */
22 this.hostDispatcher_ = new remoting.HostDispatcher(createNpapiPlugin);
24 /** @param {string} version */
25 var printVersion = function(version) {
26 if (version == '') {
27 console.log('Host not installed.');
28 } else {
29 console.log('Host version: ' + version);
33 this.hostDispatcher_.getDaemonVersion(printVersion, function() {
34 console.log('Host version not available.');
35 });
38 // Note that the values in the enums below are copied from
39 // daemon_controller.h and must be kept in sync.
40 /** @enum {number} */
41 remoting.HostController.State = {
42 NOT_IMPLEMENTED: -1,
43 NOT_INSTALLED: 0,
44 INSTALLING: 1,
45 STOPPED: 2,
46 STARTING: 3,
47 STARTED: 4,
48 STOPPING: 5,
49 UNKNOWN: 6
52 /** @enum {number} */
53 remoting.HostController.AsyncResult = {
54 OK: 0,
55 FAILED: 1,
56 CANCELLED: 2,
57 FAILED_DIRECTORY: 3
60 /**
61 * Set of features for which hasFeature() can be used to test.
63 * @enum {string}
65 remoting.HostController.Feature = {
66 PAIRING_REGISTRY: 'pairingRegistry',
67 OAUTH_CLIENT: 'oauthClient'
70 /**
71 * @param {remoting.HostController.Feature} feature The feature to test for.
72 * @param {function(boolean):void} callback
73 * @return {void}
75 remoting.HostController.prototype.hasFeature = function(feature, callback) {
76 // TODO(rmsousa): This could synchronously return a boolean, provided it were
77 // only called after the dispatcher is completely initialized.
78 this.hostDispatcher_.hasFeature(feature, callback);
81 /**
82 * @param {function(boolean, boolean, boolean):void} onDone Callback to be
83 * called when done.
84 * @param {function(remoting.Error):void} onError Callback to be called on
85 * error.
87 remoting.HostController.prototype.getConsent = function(onDone, onError) {
88 this.hostDispatcher_.getUsageStatsConsent(onDone, onError);
91 /**
92 * Registers and starts the host.
94 * @param {string} hostPin Host PIN.
95 * @param {boolean} consent The user's consent to crash dump reporting.
96 * @param {function():void} onDone Callback to be called when done.
97 * @param {function(remoting.Error):void} onError Callback to be called on
98 * error.
99 * @return {void} Nothing.
101 remoting.HostController.prototype.start = function(hostPin, consent, onDone,
102 onError) {
103 /** @type {remoting.HostController} */
104 var that = this;
106 /** @return {string} */
107 function generateUuid() {
108 var random = new Uint16Array(8);
109 window.crypto.getRandomValues(random);
110 /** @type {Array.<string>} */
111 var e = new Array();
112 for (var i = 0; i < 8; i++) {
113 e[i] = (/** @type {number} */random[i] + 0x10000).
114 toString(16).substring(1);
116 return e[0] + e[1] + '-' + e[2] + '-' + e[3] + '-' +
117 e[4] + '-' + e[5] + e[6] + e[7];
120 var newHostId = generateUuid();
122 /** @param {remoting.Error} error */
123 function onStartError(error) {
124 // Unregister the host if we failed to start it.
125 remoting.HostList.unregisterHostById(newHostId);
126 onError(error);
130 * @param {string} hostName
131 * @param {string} publicKey
132 * @param {remoting.HostController.AsyncResult} result
134 function onStarted(hostName, publicKey, result) {
135 if (result == remoting.HostController.AsyncResult.OK) {
136 remoting.hostList.onLocalHostStarted(hostName, newHostId, publicKey);
137 onDone();
138 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
139 onStartError(remoting.Error.CANCELLED);
140 } else {
141 onStartError(remoting.Error.UNEXPECTED);
146 * @param {string} hostName
147 * @param {string} publicKey
148 * @param {string} privateKey
149 * @param {string} xmppLogin
150 * @param {string} refreshToken
151 * @param {string} hostSecretHash
153 function startHostWithHash(hostName, publicKey, privateKey,
154 xmppLogin, refreshToken, hostSecretHash) {
155 var hostConfig = {
156 xmpp_login: xmppLogin,
157 oauth_refresh_token: refreshToken,
158 host_id: newHostId,
159 host_name: hostName,
160 host_secret_hash: hostSecretHash,
161 private_key: privateKey
163 var hostOwner = remoting.identity.getCachedEmail();
164 if (hostOwner != xmppLogin) {
165 hostConfig['host_owner'] = hostOwner;
167 that.hostDispatcher_.startDaemon(hostConfig, consent,
168 onStarted.bind(null, hostName, publicKey),
169 onStartError);
173 * @param {string} hostName
174 * @param {string} publicKey
175 * @param {string} privateKey
176 * @param {string} email
177 * @param {string} refreshToken
179 function onServiceAccountCredentials(
180 hostName, publicKey, privateKey, email, refreshToken) {
181 that.hostDispatcher_.getPinHash(
182 newHostId, hostPin,
183 startHostWithHash.bind(
184 null, hostName, publicKey, privateKey, email, refreshToken),
185 onError);
189 * @param {string} hostName
190 * @param {string} publicKey
191 * @param {string} privateKey
192 * @param {XMLHttpRequest} xhr
194 function onRegistered(
195 hostName, publicKey, privateKey, xhr) {
196 var success = (xhr.status == 200);
198 if (success) {
199 var result = jsonParseSafe(xhr.responseText);
200 if ('data' in result && 'authorizationCode' in result['data']) {
201 that.hostDispatcher_.getCredentialsFromAuthCode(
202 result['data']['authorizationCode'],
203 onServiceAccountCredentials.bind(
204 null, hostName, publicKey, privateKey),
205 onError);
206 } else {
207 // No authorization code returned, use regular user credential flow.
208 that.hostDispatcher_.getPinHash(
209 newHostId, hostPin, startHostWithHash.bind(
210 null, hostName, publicKey, privateKey,
211 remoting.identity.getCachedEmail(),
212 remoting.oauth2.exportRefreshToken()),
213 onError);
215 } else {
216 console.log('Failed to register the host. Status: ' + xhr.status +
217 ' response: ' + xhr.responseText);
218 onError(remoting.Error.REGISTRATION_FAILED);
223 * @param {string} hostName
224 * @param {string} privateKey
225 * @param {string} publicKey
226 * @param {string} hostClientId
227 * @param {string} oauthToken
229 function doRegisterHost(
230 hostName, privateKey, publicKey, hostClientId, oauthToken) {
231 var headers = {
232 'Authorization': 'OAuth ' + oauthToken,
233 'Content-type' : 'application/json; charset=UTF-8'
236 var newHostDetails = { data: {
237 hostId: newHostId,
238 hostName: hostName,
239 publicKey: publicKey
240 } };
242 var registerHostUrl =
243 remoting.settings.DIRECTORY_API_BASE_URL + '/@me/hosts';
245 if (hostClientId) {
246 registerHostUrl += '?' + remoting.xhr.urlencodeParamHash(
247 { hostClientId: hostClientId });
250 remoting.xhr.post(
251 registerHostUrl,
252 onRegistered.bind(null, hostName, publicKey, privateKey),
253 JSON.stringify(newHostDetails),
254 headers);
258 * @param {string} hostName
259 * @param {string} privateKey
260 * @param {string} publicKey
261 * @param {string} hostClientId
263 function onHostClientId(
264 hostName, privateKey, publicKey, hostClientId) {
265 remoting.identity.callWithToken(
266 doRegisterHost.bind(
267 null, hostName, privateKey, publicKey, hostClientId), onError);
271 * @param {string} hostName
272 * @param {string} privateKey
273 * @param {string} publicKey
274 * @param {boolean} hasFeature
276 function onHasFeatureOAuthClient(
277 hostName, privateKey, publicKey, hasFeature) {
278 if (hasFeature) {
279 that.hostDispatcher_.getHostClientId(
280 onHostClientId.bind(null, hostName, privateKey, publicKey), onError);
281 } else {
282 remoting.identity.callWithToken(
283 doRegisterHost.bind(
284 null, hostName, privateKey, publicKey, null), onError);
289 * @param {string} hostName
290 * @param {string} privateKey
291 * @param {string} publicKey
293 function onKeyGenerated(hostName, privateKey, publicKey) {
294 that.hasFeature(
295 remoting.HostController.Feature.OAUTH_CLIENT,
296 onHasFeatureOAuthClient.bind(null, hostName, privateKey, publicKey));
300 * @param {string} hostName
301 * @return {void} Nothing.
303 function startWithHostname(hostName) {
304 that.hostDispatcher_.generateKeyPair(onKeyGenerated.bind(null, hostName),
305 onError);
308 this.hostDispatcher_.getHostName(startWithHostname, onError);
312 * Stop the daemon process.
313 * @param {function():void} onDone Callback to be called when done.
314 * @param {function(remoting.Error):void} onError Callback to be called on
315 * error.
316 * @return {void} Nothing.
318 remoting.HostController.prototype.stop = function(onDone, onError) {
319 /** @type {remoting.HostController} */
320 var that = this;
322 /** @param {string?} hostId The host id of the local host. */
323 function unregisterHost(hostId) {
324 if (hostId) {
325 remoting.HostList.unregisterHostById(hostId);
327 onDone();
330 /** @param {remoting.HostController.AsyncResult} result */
331 function onStopped(result) {
332 if (result == remoting.HostController.AsyncResult.OK) {
333 that.getLocalHostId(unregisterHost);
334 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
335 onError(remoting.Error.CANCELLED);
336 } else {
337 onError(remoting.Error.UNEXPECTED);
341 this.hostDispatcher_.stopDaemon(onStopped, onError);
345 * Check the host configuration is valid (non-null, and contains both host_id
346 * and xmpp_login keys).
347 * @param {Object} config The host configuration.
348 * @return {boolean} True if it is valid.
350 function isHostConfigValid_(config) {
351 return !!config && typeof config['host_id'] == 'string' &&
352 typeof config['xmpp_login'] == 'string';
356 * @param {string} newPin The new PIN to set
357 * @param {function():void} onDone Callback to be called when done.
358 * @param {function(remoting.Error):void} onError Callback to be called on
359 * error.
360 * @return {void} Nothing.
362 remoting.HostController.prototype.updatePin = function(newPin, onDone,
363 onError) {
364 /** @type {remoting.HostController} */
365 var that = this;
367 /** @param {remoting.HostController.AsyncResult} result */
368 function onConfigUpdated(result) {
369 if (result == remoting.HostController.AsyncResult.OK) {
370 onDone();
371 } else if (result == remoting.HostController.AsyncResult.CANCELLED) {
372 onError(remoting.Error.CANCELLED);
373 } else {
374 onError(remoting.Error.UNEXPECTED);
378 /** @param {string} pinHash */
379 function updateDaemonConfigWithHash(pinHash) {
380 var newConfig = {
381 host_secret_hash: pinHash
383 that.hostDispatcher_.updateDaemonConfig(newConfig, onConfigUpdated,
384 onError);
387 /** @param {Object} config */
388 function onConfig(config) {
389 if (!isHostConfigValid_(config)) {
390 onError(remoting.Error.UNEXPECTED);
391 return;
393 /** @type {string} */
394 var hostId = config['host_id'];
395 that.hostDispatcher_.getPinHash(hostId, newPin, updateDaemonConfigWithHash,
396 onError);
399 // TODO(sergeyu): When crbug.com/121518 is fixed: replace this call
400 // with an unprivileged version if that is necessary.
401 this.hostDispatcher_.getDaemonConfig(onConfig, onError);
405 * Get the state of the local host.
407 * @param {function(remoting.HostController.State):void} onDone Completion
408 * callback.
410 remoting.HostController.prototype.getLocalHostState = function(onDone) {
411 this.hostDispatcher_.getDaemonState(onDone, function() {
412 onDone(remoting.HostController.State.NOT_IMPLEMENTED);
417 * Get the id of the local host, or null if it is not registered.
419 * @param {function(string?):void} onDone Completion callback.
421 remoting.HostController.prototype.getLocalHostId = function(onDone) {
422 /** @type {remoting.HostController} */
423 var that = this;
424 /** @param {Object} config */
425 function onConfig(config) {
426 var hostId = null;
427 if (isHostConfigValid_(config)) {
428 hostId = /** @type {string} */ config['host_id'];
430 onDone(hostId);
433 this.hostDispatcher_.getDaemonConfig(onConfig, function(error) {
434 onDone(null);
439 * Fetch the list of paired clients for this host.
441 * @param {function(Array.<remoting.PairedClient>):void} onDone
442 * @param {function(remoting.Error):void} onError
443 * @return {void}
445 remoting.HostController.prototype.getPairedClients = function(onDone,
446 onError) {
447 this.hostDispatcher_.getPairedClients(onDone, onError);
451 * Delete a single paired client.
453 * @param {string} client The client id of the pairing to delete.
454 * @param {function():void} onDone Completion callback.
455 * @param {function(remoting.Error):void} onError Error callback.
456 * @return {void}
458 remoting.HostController.prototype.deletePairedClient = function(
459 client, onDone, onError) {
460 this.hostDispatcher_.deletePairedClient(client, onDone, onError);
464 * Delete all paired clients.
466 * @param {function():void} onDone Completion callback.
467 * @param {function(remoting.Error):void} onError Error callback.
468 * @return {void}
470 remoting.HostController.prototype.clearPairedClients = function(
471 onDone, onError) {
472 this.hostDispatcher_.clearPairedClients(onDone, onError);
475 /** @type {remoting.HostController} */
476 remoting.hostController = null;