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 || {};
13 * @param {remoting.Host} host
14 * @param {remoting.HostList} hostList
17 * @implements {remoting.Activity}
19 remoting.Me2MeActivity = function(host, hostList) {
23 this.hostList_ = hostList;
26 new remoting.PinDialog(document.getElementById('pin-dialog'), host);
28 this.hostUpdateDialog_ = new remoting.HostNeedsUpdateDialog(
29 document.getElementById('host-needs-update-dialog'), this.host_);
32 this.retryOnHostOffline_ = true;
34 /** @private {remoting.SmartReconnector} */
35 this.reconnector_ = null;
37 /** @private {remoting.DesktopRemotingActivity} */
38 this.desktopActivity_ = null;
41 remoting.Me2MeActivity.prototype.dispose = function() {
42 base.dispose(this.desktopActivity_);
43 this.desktopActivity_ = null;
46 remoting.Me2MeActivity.prototype.start = function() {
47 var webappVersion = chrome.runtime.getManifest().version;
50 this.hostUpdateDialog_.showIfNecessary(webappVersion).then(function() {
51 return that.host_.options.load();
54 }).catch(remoting.Error.handler(function(/** remoting.Error */ error) {
55 if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
56 remoting.setMode(remoting.AppMode.HOME);
61 remoting.Me2MeActivity.prototype.stop = function() {
62 if (this.desktopActivity_) {
63 this.desktopActivity_.stop();
67 /** @return {remoting.DesktopRemotingActivity} */
68 remoting.Me2MeActivity.prototype.getDesktopActivity = function() {
69 return this.desktopActivity_;
73 * @param {boolean} suppressHostOfflineError
76 remoting.Me2MeActivity.prototype.connect_ = function(suppressHostOfflineError) {
77 base.dispose(this.desktopActivity_);
78 this.desktopActivity_ = new remoting.DesktopRemotingActivity(this);
79 this.desktopActivity_.getConnectingDialog().show();
80 this.desktopActivity_.start(this.host_, this.createCredentialsProvider_(),
81 suppressHostOfflineError);
85 * @return {remoting.CredentialsProvider}
88 remoting.Me2MeActivity.prototype.createCredentialsProvider_ = function() {
89 var host = this.host_;
93 * @param {string} tokenUrl Token-issue URL received from the host.
94 * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
95 * @param {string} scope OAuth scope to request the token for.
96 * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
98 var fetchThirdPartyToken = function(
99 tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
100 var thirdPartyTokenFetcher = new remoting.ThirdPartyTokenFetcher(
101 tokenUrl, hostPublicKey, scope, host.tokenUrlPatterns,
102 onThirdPartyTokenFetched);
103 thirdPartyTokenFetcher.fetchToken();
107 * @param {boolean} supportsPairing
108 * @param {function(string):void} onPinFetched
110 var requestPin = function(supportsPairing, onPinFetched) {
111 // Set time when PIN was requested.
112 var authStartTime = new Date().getTime();
113 that.desktopActivity_.getConnectingDialog().hide();
114 that.pinDialog_.show(supportsPairing).then(function(/** string */ pin) {
115 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
116 // Done obtaining PIN information. Log time taken for PIN entry.
117 var logToServer = that.desktopActivity_.getSession().getLogger();
118 logToServer.setAuthTotalTime(new Date().getTime() - authStartTime);
120 }).catch(remoting.Error.handler(function(/** remoting.Error */ error) {
121 console.assert(error.hasTag(remoting.Error.Tag.CANCELLED),
122 'Unexpected error: ' + error.getTag() + '.');
123 remoting.setMode(remoting.AppMode.HOME);
128 return new remoting.CredentialsProvider({
129 fetchPin: requestPin,
130 pairingInfo: /** @type{remoting.PairingInfo} */ (
131 base.deepCopy(host.options.pairingInfo)),
132 fetchThirdPartyToken: fetchThirdPartyToken
137 * @param {!remoting.Error} error
139 remoting.Me2MeActivity.prototype.onConnectionFailed = function(error) {
140 if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) &&
141 this.retryOnHostOffline_) {
143 var onHostListRefresh = function(/** boolean */ success) {
145 // Get the host from the hostList for the refreshed JID.
146 that.host_ = that.hostList_.getHostForId(that.host_.hostId);
147 that.connect_(false);
150 that.showErrorMessage_(error);
152 this.retryOnHostOffline_ = false;
154 // The plugin will be re-created when the host finished refreshing
155 this.hostList_.refresh(onHostListRefresh);
156 } else if (!error.isNone()) {
157 this.showErrorMessage_(error);
160 base.dispose(this.desktopActivity_);
161 this.desktopActivity_ = null;
165 * @param {!remoting.ConnectionInfo} connectionInfo
167 remoting.Me2MeActivity.prototype.onConnected = function(connectionInfo) {
168 // Reset the refresh flag so that the next connection will retry if needed.
169 this.retryOnHostOffline_ = true;
171 var plugin = connectionInfo.plugin();
172 if (plugin.hasCapability(remoting.ClientSession.Capability.CAST)) {
173 plugin.extensions().register(new remoting.CastExtensionHandler());
175 plugin.extensions().register(new remoting.GnubbyAuthHandler());
176 this.pinDialog_.requestPairingIfNecessary(connectionInfo.plugin());
178 base.dispose(this.reconnector_);
179 this.reconnector_ = new remoting.SmartReconnector(
180 this.desktopActivity_.getConnectingDialog(),
181 this.connect_.bind(this, false),
182 this.stop.bind(this),
183 connectionInfo.session());
186 remoting.Me2MeActivity.prototype.onDisconnected = function(error) {
187 if (error.isNone()) {
188 this.showFinishDialog_(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
190 this.reconnector_.onConnectionDropped(error);
191 this.showErrorMessage_(error);
194 base.dispose(this.desktopActivity_);
195 this.desktopActivity_ = null;
199 * @param {!remoting.Error} error
202 remoting.Me2MeActivity.prototype.showErrorMessage_ = function(error) {
203 var errorDiv = document.getElementById('connect-error-message');
204 l10n.localizeElementFromTag(errorDiv, error.getTag());
205 this.showFinishDialog_(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME);
209 * @param {remoting.AppMode} mode
212 remoting.Me2MeActivity.prototype.showFinishDialog_ = function(mode) {
213 var dialog = new remoting.MessageDialog(
215 document.getElementById('client-finished-me2me-button'),
216 document.getElementById('client-reconnect-button'));
218 /** @typedef {remoting.MessageDialog.Result} */
219 var Result = remoting.MessageDialog.Result;
222 dialog.show().then(function(/** Result */result) {
223 if (result === Result.PRIMARY) {
224 remoting.setMode(remoting.AppMode.HOME);
232 * @param {HTMLElement} rootElement
233 * @param {remoting.Host} host
236 remoting.HostNeedsUpdateDialog = function(rootElement, host) {
240 this.dialog_ = new remoting.MessageDialog(
241 remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE,
242 rootElement.querySelector('.connect-button'),
243 rootElement.querySelector('.cancel-button'));
245 l10n.localizeElementFromTag(
246 rootElement.querySelector('.host-needs-update-message'),
247 /*i18n-content*/'HOST_NEEDS_UPDATE_TITLE', host.hostName);
251 * Shows the HostNeedsUpdateDialog if the user is trying to connect to a legacy
254 * @param {string} webappVersion
255 * @return {Promise} Promise that resolves when no update is required or
256 * when the user ignores the update warning. Rejects with
257 * |remoting.Error.CANCELLED| if the user cancels the connection.
259 remoting.HostNeedsUpdateDialog.prototype.showIfNecessary =
260 function(webappVersion) {
261 if (!remoting.Host.needsUpdate(this.host_, webappVersion)) {
262 return Promise.resolve();
264 /** @typedef {remoting.MessageDialog.Result} */
265 var Result = remoting.MessageDialog.Result;
266 return this.dialog_.show().then(function(/** Result */ result) {
267 if (result === Result.SECONDARY) {
268 return Promise.reject(new remoting.Error(remoting.Error.Tag.CANCELLED));
274 * @param {HTMLElement} rootElement
275 * @param {remoting.Host} host
278 remoting.PinDialog = function(rootElement, host) {
280 this.rootElement_ = rootElement;
282 this.pairingCheckbox_ = rootElement.querySelector('.pairing-checkbox');
284 this.pinInput_ = rootElement.querySelector('.pin-inputField');
288 this.dialog_ = new remoting.InputDialog(
289 remoting.AppMode.CLIENT_PIN_PROMPT,
290 this.rootElement_.querySelector('form'),
292 this.rootElement_.querySelector('.cancel-button'));
297 * @param {boolean} supportsPairing
298 * @return {Promise<string>} Promise that resolves with the PIN or rejects with
299 * |remoting.Error.CANCELLED| if the user cancels the connection.
301 remoting.PinDialog.prototype.show = function(supportsPairing) {
303 this.pairingCheckbox_.checked = false;
304 this.rootElement_.querySelector('.pairing-section').hidden = !supportsPairing;
305 var message = this.rootElement_.querySelector('.pin-message');
306 l10n.localizeElement(message, this.host_.hostName);
307 this.pinInput_.value = '';
308 return this.dialog_.show();
312 * @param {remoting.ClientPlugin} plugin
314 remoting.PinDialog.prototype.requestPairingIfNecessary = function(plugin) {
315 if (this.pairingCheckbox_.checked) {
318 * @param {string} clientId
319 * @param {string} sharedSecret
321 var onPairingComplete = function(clientId, sharedSecret) {
322 that.host_.options.pairingInfo.clientId = clientId;
323 that.host_.options.pairingInfo.sharedSecret = sharedSecret;
324 that.host_.options.save();
327 // Use the platform name as a proxy for the local computer name.
328 // TODO(jamiewalch): Use a descriptive name for the local computer, for
329 // example, its Chrome Sync name.
331 if (remoting.platformIsMac()) {
333 } else if (remoting.platformIsWindows()) {
334 clientName = 'Windows';
335 } else if (remoting.platformIsChromeOS()) {
336 clientName = 'ChromeOS';
337 } else if (remoting.platformIsLinux()) {
338 clientName = 'Linux';
340 console.log('Unrecognized client platform. Using navigator.platform.');
341 clientName = navigator.platform;
343 plugin.requestPairing(clientName, onPairingComplete);