Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / remoting / webapp / crd / js / me2me_activity.js
blob1141d076ddda929f1ab7e97c4e40f6f3d687ad14
1 // Copyright 2015 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 /** @suppress {duplicate} */
6 var remoting = remoting || {};
8 (function() {
10 'use strict';
12 /**
13  * @param {remoting.Host} host
14  * @param {remoting.HostList} hostList
15  *
16  * @constructor
17  * @implements {remoting.Activity}
18  */
19 remoting.Me2MeActivity = function(host, hostList) {
20   /** @private */
21   this.host_ = host;
22   /** @private */
23   this.hostList_ = hostList;
24   /** @private */
25   this.pinDialog_ =
26       new remoting.PinDialog(document.getElementById('pin-dialog'), host);
27   /** @private */
28   this.hostUpdateDialog_ = new remoting.HostNeedsUpdateDialog(
29       document.getElementById('host-needs-update-dialog'), this.host_);
31   /** @private */
32   this.retryOnHostOffline_ = true;
34   /** @private {remoting.SmartReconnector} */
35   this.reconnector_ = null;
37   /** @private {remoting.SessionLogger} */
38   this.logger_ = null;
40   /** @private {remoting.DesktopRemotingActivity} */
41   this.desktopActivity_ = null;
44 remoting.Me2MeActivity.prototype.dispose = function() {
45   base.dispose(this.desktopActivity_);
46   this.desktopActivity_ = null;
49 remoting.Me2MeActivity.prototype.start = function() {
50   var webappVersion = chrome.runtime.getManifest().version;
51   var that = this;
53   var Event = remoting.ChromotingEvent;
54   this.logger_ = this.createLogger_(Event.SessionEntryPoint.CONNECT_BUTTON);
55   this.logger_.logSessionStateChange(Event.SessionState.STARTED,
56                                      Event.ConnectionError.NONE);
58   this.hostUpdateDialog_.showIfNecessary(webappVersion).then(function() {
59     return that.host_.options.load();
60   }).then(function() {
61     that.connect_();
62   }).catch(remoting.Error.handler(function(/** remoting.Error */ error) {
63     if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
64       remoting.setMode(remoting.AppMode.HOME);
65     }
66   }));
69 remoting.Me2MeActivity.prototype.stop = function() {
70   if (this.desktopActivity_) {
71     this.desktopActivity_.stop();
72   }
75 /** @return {remoting.DesktopRemotingActivity} */
76 remoting.Me2MeActivity.prototype.getDesktopActivity = function() {
77   return this.desktopActivity_;
80 /**
81  * @param {remoting.ChromotingEvent.SessionEntryPoint} entryPoint
82  * @return {!remoting.SessionLogger}
83  * @private
84  */
85 remoting.Me2MeActivity.prototype.createLogger_ = function(entryPoint) {
86   var Mode = remoting.ChromotingEvent.Mode;
87   var logger = remoting.SessionLogger.createForClient();
88   logger.setEntryPoint(entryPoint);
89   logger.setHostVersion(this.host_.hostVersion);
90   logger.setLogEntryMode(Mode.ME2ME);
91   return logger;
94 /**
95  * @param {remoting.ChromotingEvent.SessionEntryPoint} entryPoint
96  * @private
97  */
98 remoting.Me2MeActivity.prototype.reconnect_ = function(entryPoint) {
99   this.logger_ = this.createLogger_(entryPoint);
100   var Event = remoting.ChromotingEvent;
101   this.logger_.logSessionStateChange(Event.SessionState.STARTED,
102                                      Event.ConnectionError.NONE);
103   this.connect_();
107  * @private
108  */
109 remoting.Me2MeActivity.prototype.connect_ = function() {
110   base.dispose(this.desktopActivity_);
111   this.desktopActivity_ = new remoting.DesktopRemotingActivity(this);
112   this.desktopActivity_.getConnectingDialog().show();
113   this.desktopActivity_.start(this.host_, this.createCredentialsProvider_(),
114                               this.logger_);
118  * @return {remoting.CredentialsProvider}
119  * @private
120  */
121 remoting.Me2MeActivity.prototype.createCredentialsProvider_ = function() {
122   var host = this.host_;
123   var that = this;
125   /**
126    * @param {string} tokenUrl Token-issue URL received from the host.
127    * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
128    * @param {string} scope OAuth scope to request the token for.
129    * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
130    */
131   var fetchThirdPartyToken = function(
132       tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
133     var thirdPartyTokenFetcher = new remoting.ThirdPartyTokenFetcher(
134         tokenUrl, hostPublicKey, scope, host.tokenUrlPatterns,
135         onThirdPartyTokenFetched);
136     thirdPartyTokenFetcher.fetchToken();
137   };
139   /**
140    * @param {boolean} supportsPairing
141    * @param {function(string):void} onPinFetched
142    */
143   var requestPin = function(supportsPairing, onPinFetched) {
144     // Set time when PIN was requested.
145     var authStartTime = new Date().getTime();
146     that.desktopActivity_.getConnectingDialog().hide();
147     that.pinDialog_.show(supportsPairing).then(function(/** string */ pin) {
148       remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
149       // Done obtaining PIN information. Log time taken for PIN entry.
150       that.logger_.setAuthTotalTime(new Date().getTime() - authStartTime);
151       onPinFetched(pin);
152     }).catch(remoting.Error.handler(function(/** remoting.Error */ error) {
153       console.assert(error.hasTag(remoting.Error.Tag.CANCELLED),
154                     'Unexpected error: ' + error.getTag() + '.');
155       remoting.setMode(remoting.AppMode.HOME);
156       that.stop();
157     }));
158   };
160   return new remoting.CredentialsProvider({
161     fetchPin: requestPin,
162     pairingInfo: /** @type{remoting.PairingInfo} */ (
163         base.deepCopy(host.options.pairingInfo)),
164     fetchThirdPartyToken: fetchThirdPartyToken
165   });
169  * @param {!remoting.Error} error
170  * @private
171  */
172 remoting.Me2MeActivity.prototype.reconnectOnHostOffline_ = function(error) {
173   var that = this;
174   var onHostListRefresh = function(/** boolean */ success) {
175     if (success) {
176       var host = that.hostList_.getHostForId(that.host_.hostId);
177       var SessionEntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
179       // Reconnect if the host's JID changes.
180       if (Boolean(host) && host.jabberId != that.host_.jabberId) {
181         that.host_ = host;
182         that.reconnect_(SessionEntryPoint.AUTO_RECONNECT_ON_HOST_OFFLINE);
183         return;
184       }
185     }
186     that.showErrorMessage_(error);
187   };
189   this.retryOnHostOffline_ = false;
190   // The plugin will be re-created when the host list has been refreshed.
191   this.hostList_.refresh(onHostListRefresh);
195  * @param {!remoting.Error} error
196  */
197 remoting.Me2MeActivity.prototype.onConnectionFailed = function(error) {
198   base.dispose(this.desktopActivity_);
199   this.desktopActivity_ = null;
201   if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
202       this.retryOnHostOffline_) {
203     this.reconnectOnHostOffline_(error);
204   } else if (!error.isNone()) {
205     this.showErrorMessage_(error);
206   }
210  * @param {!remoting.ConnectionInfo} connectionInfo
211  */
212 remoting.Me2MeActivity.prototype.onConnected = function(connectionInfo) {
213   // Reset the refresh flag so that the next connection will retry if needed.
214   this.retryOnHostOffline_ = true;
216   var plugin = connectionInfo.plugin();
217   if (plugin.hasCapability(remoting.ClientSession.Capability.CAST)) {
218     plugin.extensions().register(new remoting.CastExtensionHandler());
219   }
220   plugin.extensions().register(new remoting.GnubbyAuthHandler());
221   this.pinDialog_.requestPairingIfNecessary(connectionInfo.plugin());
223   var SessionEntryPoint = remoting.ChromotingEvent.SessionEntryPoint;
225   base.dispose(this.reconnector_);
226   this.reconnector_ = new remoting.SmartReconnector(
227       this.desktopActivity_.getConnectingDialog(),
228       this.reconnect_.bind(
229           this, SessionEntryPoint.AUTO_RECONNECT_ON_CONNECTION_DROPPED),
230       this.stop.bind(this), connectionInfo.session());
233 remoting.Me2MeActivity.prototype.onDisconnected = function(error) {
234   if (error.isNone()) {
235     this.showFinishDialog_(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
236   } else {
237     this.reconnector_.onConnectionDropped(error);
238     this.showErrorMessage_(error);
239   }
241   base.dispose(this.desktopActivity_);
242   this.desktopActivity_ = null;
246  * @param {!remoting.Error} error
247  * @private
248  */
249 remoting.Me2MeActivity.prototype.showErrorMessage_ = function(error) {
250   var errorDiv = document.getElementById('connect-error-message');
251   l10n.localizeElementFromTag(errorDiv, error.getTag());
252   this.showFinishDialog_(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME);
256  * @param {remoting.AppMode} mode
257  * @private
258  */
259 remoting.Me2MeActivity.prototype.showFinishDialog_ = function(mode) {
260   var dialog = remoting.modalDialogFactory.createMessageDialog(
261       mode,
262       document.getElementById('client-finished-me2me-button'),
263       document.getElementById('client-reconnect-button'));
265   /** @typedef {remoting.MessageDialog.Result} */
266   var Result = remoting.MessageDialog.Result;
267   var that = this;
269   dialog.show().then(function(/** Result */result) {
270     if (result === Result.PRIMARY) {
271       remoting.setMode(remoting.AppMode.HOME);
272     } else {
273       that.reconnect_(
274           remoting.ChromotingEvent.SessionEntryPoint.RECONNECT_BUTTON);
275     }
276   });
280  * @param {HTMLElement} rootElement
281  * @param {remoting.Host} host
282  * @constructor
283  */
284 remoting.HostNeedsUpdateDialog = function(rootElement, host) {
285   /** @private */
286   this.host_ = host;
287   /** @private */
288   this.dialog_ = remoting.modalDialogFactory.createMessageDialog(
289       remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE,
290       rootElement.querySelector('.connect-button'),
291       rootElement.querySelector('.cancel-button'));
293   l10n.localizeElementFromTag(
294       rootElement.querySelector('.host-needs-update-message'),
295       /*i18n-content*/'HOST_NEEDS_UPDATE_TITLE', host.hostName);
299  * Shows the HostNeedsUpdateDialog if the user is trying to connect to a legacy
300  * host.
302  * @param {string} webappVersion
303  * @return {Promise}  Promise that resolves when no update is required or
304  *    when the user ignores the update warning.  Rejects with
305  *    |remoting.Error.CANCELLED| if the user cancels the connection.
306  */
307 remoting.HostNeedsUpdateDialog.prototype.showIfNecessary =
308     function(webappVersion) {
309   if (!remoting.Host.needsUpdate(this.host_, webappVersion)) {
310     return Promise.resolve();
311   }
312   /** @typedef {remoting.MessageDialog.Result} */
313   var Result = remoting.MessageDialog.Result;
314   return this.dialog_.show().then(function(/** Result */ result) {
315     if (result === Result.SECONDARY) {
316       return Promise.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
317     }
318   });
322  * @param {HTMLElement} rootElement
323  * @param {remoting.Host} host
324  * @constructor
325  */
326 remoting.PinDialog = function(rootElement, host) {
327   /** @private */
328   this.rootElement_ = rootElement;
329   /** @private */
330   this.pairingCheckbox_ = rootElement.querySelector('.pairing-checkbox');
331   /** @private */
332   this.pinInput_ = rootElement.querySelector('.pin-inputField');
333   /** @private */
334   this.host_ = host;
335   /** @private */
336   this.dialog_ = remoting.modalDialogFactory.createInputDialog(
337     remoting.AppMode.CLIENT_PIN_PROMPT,
338     this.rootElement_.querySelector('form'),
339     this.pinInput_,
340     this.rootElement_.querySelector('.cancel-button'));
345  * @param {boolean} supportsPairing
346  * @return {Promise<string>}  Promise that resolves with the PIN or rejects with
347  *    |remoting.Error.CANCELLED| if the user cancels the connection.
348  */
349 remoting.PinDialog.prototype.show = function(supportsPairing) {
350   // Reset the UI.
351   this.pairingCheckbox_.checked = false;
352   this.rootElement_.querySelector('.pairing-section').hidden = !supportsPairing;
353   var message = this.rootElement_.querySelector('.pin-message');
354   l10n.localizeElement(message, this.host_.hostName);
355   this.pinInput_.value = '';
356   return this.dialog_.show();
360  * @param {remoting.ClientPlugin} plugin
361  */
362 remoting.PinDialog.prototype.requestPairingIfNecessary = function(plugin) {
363   if (this.pairingCheckbox_.checked) {
364     var that = this;
365     /**
366      * @param {string} clientId
367      * @param {string} sharedSecret
368      */
369     var onPairingComplete = function(clientId, sharedSecret) {
370       that.host_.options.pairingInfo.clientId = clientId;
371       that.host_.options.pairingInfo.sharedSecret = sharedSecret;
372       that.host_.options.save();
373     };
375     // Use the platform name as a proxy for the local computer name.
376     // TODO(jamiewalch): Use a descriptive name for the local computer, for
377     // example, its Chrome Sync name.
378     var clientName = '';
379     if (remoting.platformIsMac()) {
380       clientName = 'Mac';
381     } else if (remoting.platformIsWindows()) {
382       clientName = 'Windows';
383     } else if (remoting.platformIsChromeOS()) {
384       clientName = 'ChromeOS';
385     } else if (remoting.platformIsLinux()) {
386       clientName = 'Linux';
387     } else {
388       console.log('Unrecognized client platform. Using navigator.platform.');
389       clientName = navigator.platform;
390     }
391     plugin.requestPairing(clientName, onPairingComplete);
392   }
395 })();