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
);