OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview | 6 * @fileoverview |
7 * | 7 * |
8 * It2MeHelpeeChannel relays messages between the Hangouts web page (Hangouts) | 8 * It2MeHelpeeChannel relays messages between the Hangouts web page (Hangouts) |
9 * and the It2Me Native Messaging Host (It2MeHost) for the helpee (the Hangouts | 9 * and the It2Me Native Messaging Host (It2MeHost) for the helpee (the Hangouts |
10 * participant who is receiving remoting assistance). | 10 * participant who is receiving remoting assistance). |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 | 243 |
244 /** | 244 /** |
245 * Connects to the It2Me Native messaging Host and retrieves the access code. | 245 * Connects to the It2Me Native messaging Host and retrieves the access code. |
246 * | 246 * |
247 * @param {{method:string, data:Object.<string,*>}} message | 247 * @param {{method:string, data:Object.<string,*>}} message |
248 * @private | 248 * @private |
249 */ | 249 */ |
250 remoting.It2MeHelpeeChannel.prototype.handleConnect_ = | 250 remoting.It2MeHelpeeChannel.prototype.handleConnect_ = |
251 function(message) { | 251 function(message) { |
252 var email = getStringAttr(message, 'email'); | 252 var email = getStringAttr(message, 'email'); |
253 var bounds = /** @type {Bounds} */ (message['parentBounds']); | |
Jamie
2015/02/02 19:07:40
getObjectAttr would be better (it does some minima
kelvinp
2015/02/03 01:15:33
Done.
| |
253 | 254 |
254 if (!email) { | 255 if (!email) { |
255 throw new Error('Missing required parameter: email'); | 256 throw new Error('Missing required parameter: email'); |
256 } | 257 } |
257 | 258 |
258 if (this.hostState_ !== remoting.HostSession.State.UNKNOWN) { | 259 if (this.hostState_ !== remoting.HostSession.State.UNKNOWN) { |
259 throw new Error('An existing connection is in progress.'); | 260 throw new Error('An existing connection is in progress.'); |
260 } | 261 } |
261 | 262 |
262 this.showConfirmDialog_().then( | 263 var that = this; |
264 this.showConfirmDialog_(bounds).then( | |
263 this.initializeHost_.bind(this) | 265 this.initializeHost_.bind(this) |
264 ).then( | 266 ).then( |
265 this.fetchOAuthToken_.bind(this) | 267 this.fetchOAuthToken_.bind(this) |
266 ).then( | 268 ).then( |
267 /** @type {function(*):void} */(this.connectToHost_.bind(this, email)), | 269 /** @type {function(*):void} */(this.connectToHost_.bind(this, email)) |
Jamie
2015/02/02 19:07:40
Why does this call need a cast, but the others don
kelvinp
2015/02/03 01:15:33
Gary added this check when we switched to the new
Jamie
2015/02/03 18:22:44
I'm still not sure why this one needs annotating,
kelvinp
2015/02/03 19:31:45
Because initializeHost_ and fetchOAuthToken_ doesn
| |
268 /** @type {function(*):void} */(this.sendErrorResponse_.bind(this, message)) | 270 ).catch( |
271 /** @param {*} reason */ | |
272 function(reason) { | |
273 var error = /** @type {Error} */ (reason); | |
274 that.sendErrorResponse_(message, error); | |
275 that.dispose(); | |
276 } | |
269 ); | 277 ); |
270 }; | 278 }; |
271 | 279 |
272 /** | 280 /** |
273 * Prompts the user before starting the It2Me Native Messaging Host. This | 281 * Prompts the user before starting the It2Me Native Messaging Host. This |
274 * ensures that even if Hangouts is compromised, an attacker cannot start the | 282 * ensures that even if Hangouts is compromised, an attacker cannot start the |
275 * host without explicit user confirmation. | 283 * host without explicit user confirmation. |
276 * | 284 * |
285 * @param {Bounds} bounds Bounds of the hangout window | |
277 * @return {Promise} A promise that resolves to a boolean value, indicating | 286 * @return {Promise} A promise that resolves to a boolean value, indicating |
278 * whether the user accepts the remote assistance or not. | 287 * whether the user accepts the remote assistance or not. |
279 * @private | 288 * @private |
280 */ | 289 */ |
281 remoting.It2MeHelpeeChannel.prototype.showConfirmDialog_ = function() { | 290 remoting.It2MeHelpeeChannel.prototype.showConfirmDialog_ = function(bounds) { |
282 if (base.isAppsV2()) { | 291 if (base.isAppsV2()) { |
283 return this.showConfirmDialogV2_(); | 292 return this.showConfirmDialogV2_(bounds); |
284 } else { | 293 } else { |
285 return this.showConfirmDialogV1_(); | 294 return this.showConfirmDialogV1_(); |
286 } | 295 } |
287 }; | 296 }; |
288 | 297 |
289 /** | 298 /** |
290 * @return {Promise} A promise that resolves to a boolean value, indicating | 299 * @return {Promise} A promise that resolves to a boolean value, indicating |
291 * whether the user accepts the remote assistance or not. | 300 * whether the user accepts the remote assistance or not. |
292 * @private | 301 * @private |
293 */ | 302 */ |
294 remoting.It2MeHelpeeChannel.prototype.showConfirmDialogV1_ = function() { | 303 remoting.It2MeHelpeeChannel.prototype.showConfirmDialogV1_ = function() { |
295 var messageHeader = l10n.getTranslationOrError( | 304 var messageHeader = l10n.getTranslationOrError( |
296 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_1'); | 305 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_1'); |
297 var message1 = l10n.getTranslationOrError( | 306 var message1 = l10n.getTranslationOrError( |
298 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_2'); | 307 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_2'); |
299 var message2 = l10n.getTranslationOrError( | 308 var message2 = l10n.getTranslationOrError( |
300 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_3'); | 309 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_MESSAGE_3'); |
301 var message = base.escapeHTML(messageHeader) + '\n' + | 310 var message = base.escapeHTML(messageHeader) + '\n' + |
302 '- ' + base.escapeHTML(message1) + '\n' + | 311 '- ' + base.escapeHTML(message1) + '\n' + |
303 '- ' + base.escapeHTML(message2) + '\n'; | 312 '- ' + base.escapeHTML(message2) + '\n'; |
304 | 313 |
305 if(window.confirm(message)) { | 314 if(window.confirm(message)) { |
306 return Promise.resolve(); | 315 return Promise.resolve(); |
307 } else { | 316 } else { |
308 return Promise.reject(new Error(remoting.Error.CANCELLED)); | 317 return Promise.reject(new Error(remoting.Error.NOT_AUTHORIZED)); |
Jamie
2015/02/02 19:07:40
I think CANCELLED is a better error here. Generall
kelvinp
2015/02/03 01:15:33
Done.
| |
309 } | 318 } |
310 }; | 319 }; |
311 | 320 |
312 /** | 321 /** |
313 * @return {Promise} A promise that resolves to a boolean value, indicating | 322 * @param {Bounds} bounds the bounds of the Hangouts Window. If set, the |
323 * confirm dialog will be centered upon bounds. | |
324 * @return {Promise} A promise that will resolve if the user accepts remote | |
325 * assistance or reject otherwise. | |
326 * @private | |
314 */ | 327 */ |
315 function buildConfirmationMessage() { | 328 remoting.It2MeHelpeeChannel.prototype.showConfirmDialogV2_ = function(bounds) { |
316 var getToken = | 329 var getToken = |
317 base.Promise.as(chrome.identity.getAuthToken, [{interactive: false}]); | 330 base.Promise.as(chrome.identity.getAuthToken, [{interactive: false}]); |
331 | |
318 return getToken.then( | 332 return getToken.then( |
319 /** @param {string} token */ | 333 /** @param {string} token */ |
320 function(token){ | 334 function(token) { |
321 var messageHeader = l10n.getTranslationOrError( | 335 return remoting.HangoutConsentDialog.getInstance().show(Boolean(token), |
322 /*i18n-content*/ 'HANGOUTS_CONFIRM_DIALOG_MESSAGE_1'); | 336 bounds); |
323 var message1 = l10n.getTranslationOrError( | |
324 /*i18n-content*/ 'HANGOUTS_CONFIRM_DIALOG_MESSAGE_2'); | |
325 var message2 = l10n.getTranslationOrError( | |
326 /*i18n-content*/ 'HANGOUTS_CONFIRM_DIALOG_MESSAGE_3'); | |
327 var message = | |
328 '<div>' + base.escapeHTML(messageHeader) + '</div>' + | |
329 '<ul class="insetList">' + | |
330 '<li>' + base.escapeHTML(message1) + '</li>' + | |
331 '<li>' + base.escapeHTML(message2) + '</li>'; | |
332 if (!token) { | |
333 message += '<li>To continue you must first grant extended access permiss ions to your computer. You only have to do this once.</li>'; | |
334 } | |
335 return message + '</ul>'; | |
336 }); | |
337 } | |
338 | |
339 /** | |
340 * @return {Promise} A promise that resolves to a boolean value, indicating | |
341 * whether the user accepts the remote assistance or not. | |
342 * @private | |
343 */ | |
344 remoting.It2MeHelpeeChannel.prototype.showConfirmDialogV2_ = function() { | |
345 return buildConfirmationMessage().then( | |
346 /** @param {string} message */ | |
347 function(message) { | |
348 /** | |
349 * @param {function(*=):void} resolve | |
350 * @param {function(*=):void} reject | |
351 */ | |
352 return new Promise(function(resolve, reject) { | |
353 /** @param {number} result */ | |
354 function confirmDialogCallback(result) { | |
355 if (result === 1) { | |
356 resolve(true); | |
357 } else { | |
358 reject(new Error(remoting.Error.CANCELLED)); | |
359 } | |
360 } | |
361 remoting.MessageWindow.showConfirmWindow( | |
362 '', // Empty string to use the package name as the dialog title. | |
363 message, | |
364 l10n.getTranslationOrError( | |
365 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_ACCEPT'), | |
366 l10n.getTranslationOrError( | |
367 /*i18n-content*/'HANGOUTS_CONFIRM_DIALOG_DECLINE'), | |
368 confirmDialogCallback | |
369 ); | |
370 }); | 337 }); |
371 }); | |
372 }; | 338 }; |
373 | 339 |
374 /** | 340 /** |
375 * @return {Promise} A promise that resolves when the host is initialized. | 341 * @return {Promise} A promise that resolves when the host is initialized. |
376 * @private | 342 * @private |
377 */ | 343 */ |
378 remoting.It2MeHelpeeChannel.prototype.initializeHost_ = function() { | 344 remoting.It2MeHelpeeChannel.prototype.initializeHost_ = function() { |
379 /** @type {remoting.It2MeHostFacade} */ | 345 /** @type {remoting.It2MeHostFacade} */ |
380 var host = this.host_; | 346 var host = this.host_; |
381 | 347 |
(...skipping 13 matching lines...) Expand all Loading... | |
395 | 361 |
396 /** | 362 /** |
397 * @return {Promise} Promise that resolves with the OAuth token as the value. | 363 * @return {Promise} Promise that resolves with the OAuth token as the value. |
398 */ | 364 */ |
399 remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() { | 365 remoting.It2MeHelpeeChannel.prototype.fetchOAuthToken_ = function() { |
400 if (base.isAppsV2()) { | 366 if (base.isAppsV2()) { |
401 /** | 367 /** |
402 * @param {function(*=):void} resolve | 368 * @param {function(*=):void} resolve |
403 */ | 369 */ |
404 return new Promise(function(resolve){ | 370 return new Promise(function(resolve){ |
405 // TODO(jamiewalch): Make this work with {interactive: true} as well. | |
406 chrome.identity.getAuthToken({'interactive': true}, resolve); | 371 chrome.identity.getAuthToken({'interactive': true}, resolve); |
407 }); | 372 }); |
408 } else { | 373 } else { |
409 /** | 374 /** |
410 * @param {function(*=):void} resolve | 375 * @param {function(*=):void} resolve |
376 * @param {function(*=):void} reject | |
411 */ | 377 */ |
412 return new Promise(function(resolve) { | 378 return new Promise(function(resolve, reject) { |
413 /** @type {remoting.OAuth2} */ | 379 /** @type {remoting.OAuth2} */ |
414 var oauth2 = new remoting.OAuth2(); | 380 var oauth2 = new remoting.OAuth2(); |
415 var onAuthenticated = function() { | |
416 oauth2.callWithToken( | |
417 resolve, | |
418 function() { throw new Error('Authentication failed.'); }); | |
419 }; | |
420 /** @param {remoting.Error} error */ | 381 /** @param {remoting.Error} error */ |
421 var onError = function(error) { | 382 var onError = function(error) { |
422 if (error != remoting.Error.NOT_AUTHENTICATED) { | 383 if (error === remoting.Error.NOT_AUTHENTICATED) { |
423 throw new Error('Unexpected error fetch auth token: ' + error); | 384 oauth2.doAuthRedirect(function() { |
385 oauth2.callWithToken(resolve, reject); | |
386 }); | |
387 return; | |
424 } | 388 } |
425 oauth2.removeCachedAuthToken(); | 389 reject(new Error(remoting.Error.NOT_AUTHENTICATED)); |
426 }; | 390 }; |
427 oauth2.callWithToken(resolve, onError); | 391 oauth2.callWithToken(resolve, onError); |
428 }); | 392 }); |
429 } | 393 } |
430 }; | 394 }; |
431 | 395 |
432 /** | 396 /** |
433 * Connects to the It2Me Native Messaging Host and retrieves the access code | 397 * Connects to the It2Me Native Messaging Host and retrieves the access code |
434 * in the |onHostStateChanged_| callback. | 398 * in the |onHostStateChanged_| callback. |
435 * | 399 * |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
508 | 472 |
509 console.error('Error responding to message method:' + | 473 console.error('Error responding to message method:' + |
510 (incomingMessage ? incomingMessage.method : 'null') + | 474 (incomingMessage ? incomingMessage.method : 'null') + |
511 ' error:' + error); | 475 ' error:' + error); |
512 this.hangoutPort_.postMessage({ | 476 this.hangoutPort_.postMessage({ |
513 method: remoting.It2MeHelpeeChannel.HangoutMessageTypes.ERROR, | 477 method: remoting.It2MeHelpeeChannel.HangoutMessageTypes.ERROR, |
514 message: error, | 478 message: error, |
515 request: incomingMessage | 479 request: incomingMessage |
516 }); | 480 }); |
517 }; | 481 }; |
OLD | NEW |