Remove getCached* methods from remoting.Identity.
[chromium-blink-merge.git] / remoting / webapp / app_remoting / js / app_remoting.js
blob22e79e26dcb5e5149872f2d3a12529b7b0b07fb0
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
3 // found in the LICENSE file.
5 /**
6  * @fileoverview
7  * This class implements the functionality that is specific to application
8  * remoting ("AppRemoting" or AR).
9  */
11 'use strict';
13 /** @suppress {duplicate} */
14 var remoting = remoting || {};
16 /**
17  * @param {remoting.Application} app The main app that owns this delegate.
18  * @constructor
19  * @implements {remoting.Application.Delegate}
20  */
21 remoting.AppRemoting = function(app) {
22   app.setDelegate(this);
24   /**
25    * @type {remoting.ApplicationContextMenu}
26    * @private
27    */
28   this.contextMenu_ = null;
30   /**
31    * @type {remoting.KeyboardLayoutsMenu}
32    * @private
33    */
34   this.keyboardLayoutsMenu_ = null;
36   /**
37    * @type {remoting.WindowActivationMenu}
38    * @private
39    */
40   this.windowActivationMenu_ = null;
42    /**
43     * @type {number}
44     * @private
45     */
46    this.pingTimerId_ = 0;
49 /**
50  * Type definition for the RunApplicationResponse returned by the API.
51  *
52  * @constructor
53  * @private
54  */
55 remoting.AppRemoting.AppHostResponse = function() {
56   /** @type {string} */
57   this.status = '';
58   /** @type {string} */
59   this.hostJid = '';
60   /** @type {string} */
61   this.authorizationCode = '';
62   /** @type {string} */
63   this.sharedSecret = '';
65   this.host = {
66     /** @type {string} */
67     applicationId: '',
69     /** @type {string} */
70     hostId: ''};
73 /**
74  * Callback for when the userinfo (email and user name) is available from
75  * the identity API.
76  *
77  * @param {string} email The user's email address.
78  * @param {string} fullName The user's full name.
79  * @return {void} Nothing.
80  */
81 remoting.onUserInfoAvailable = function(email, fullName) {
84 /**
85  * Initialize the application and register all event handlers. After this
86  * is called, the app is running and waiting for user events.
87  *
88  * @param {remoting.SessionConnector} connector
89  * @return {void} Nothing.
90  */
91 remoting.AppRemoting.prototype.init = function(connector) {
92   remoting.initGlobalObjects();
93   remoting.initIdentity(remoting.onUserInfoAvailable);
95   // TODO(jamiewalch): Remove ClientSession's dependency on remoting.fullscreen
96   // so that this is no longer required.
97   remoting.fullscreen = new remoting.FullscreenAppsV2();
99   var restoreHostWindows = function() {
100     if (remoting.clientSession) {
101       remoting.clientSession.sendClientMessage('restoreAllWindows', '');
102     }
103   };
104   chrome.app.window.current().onRestored.addListener(restoreHostWindows);
106   remoting.windowShape.updateClientWindowShape();
108   // Initialize the context menus.
109   if (remoting.platformIsChromeOS()) {
110     var adapter = new remoting.ContextMenuChrome();
111   } else {
112     var adapter = new remoting.ContextMenuDom(
113         document.getElementById('context-menu'));
114   }
115   this.contextMenu_ = new remoting.ApplicationContextMenu(adapter);
116   this.keyboardLayoutsMenu_ = new remoting.KeyboardLayoutsMenu(adapter);
117   this.windowActivationMenu_ = new remoting.WindowActivationMenu(adapter);
119   /** @type {remoting.AppRemoting} */
120   var that = this;
122   /** @param {XMLHttpRequest} xhr */
123   var parseAppHostResponse = function(xhr) {
124     if (xhr.status == 200) {
125       var response = /** @type {remoting.AppRemoting.AppHostResponse} */
126           (base.jsonParseSafe(xhr.responseText));
127       if (response &&
128           response.status &&
129           response.status == 'done' &&
130           response.hostJid &&
131           response.authorizationCode &&
132           response.sharedSecret &&
133           response.host &&
134           response.host.hostId) {
135         var hostJid = response.hostJid;
136         that.contextMenu_.setHostId(response.host.hostId);
137         var host = new remoting.Host;
138         host.hostId = response.host.hostId;
139         host.jabberId = hostJid;
140         host.authorizationCode = response.authorizationCode;
141         host.sharedSecret = response.sharedSecret;
143         remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
145         var idleDetector = new remoting.IdleDetector(
146             document.getElementById('idle-dialog'),
147             remoting.disconnect);
149         /**
150          * @param {string} tokenUrl Token-issue URL received from the host.
151          * @param {string} hostPublicKey Host public key (DER and Base64
152          *     encoded).
153          * @param {string} scope OAuth scope to request the token for.
154          * @param {function(string, string):void} onThirdPartyTokenFetched
155          *     Callback.
156          */
157         var fetchThirdPartyToken = function(
158             tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
159           // Use the authentication tokens returned by the app-remoting server.
160           onThirdPartyTokenFetched(host['authorizationCode'],
161                                    host['sharedSecret']);
162         };
164         connector.connectMe2App(host, fetchThirdPartyToken);
165       } else if (response && response.status == 'pending') {
166         that.handleError(remoting.Error.SERVICE_UNAVAILABLE);
167       }
168     } else {
169       console.error('Invalid "runApplication" response from server.');
170       // TODO(garykac) Start using remoting.Error.fromHttpStatus once it has
171       // been updated to properly report 'unknown' errors (rather than
172       // reporting them as AUTHENTICATION_FAILED).
173       if (xhr.status == 0) {
174         that.handleError(remoting.Error.NETWORK_FAILURE);
175       } else if (xhr.status == 401) {
176         that.handleError(remoting.Error.AUTHENTICATION_FAILED);
177       } else if (xhr.status == 403) {
178         that.handleError(remoting.Error.APP_NOT_AUTHORIZED);
179       } else if (xhr.status == 502 || xhr.status == 503) {
180         that.handleError(remoting.Error.SERVICE_UNAVAILABLE);
181       } else {
182         that.handleError(remoting.Error.UNEXPECTED);
183       }
184     }
185   };
187   /** @param {string} token */
188   var getAppHost = function(token) {
189     remoting.xhr.start({
190       method: 'POST',
191       url: that.runApplicationUrl(),
192       onDone: parseAppHostResponse,
193       oauthToken: token,
194     });
195   };
197   /** @param {remoting.Error} error */
198   var onError = function(error) {
199     that.handleError(error);
200   };
202   remoting.LoadingWindow.show();
204   remoting.identity.getToken().then(getAppHost).
205       catch(remoting.Error.handler(onError));
209  * @return {string} Application product name to be used in UI.
210  */
211 remoting.AppRemoting.prototype.getApplicationName = function() {
212   var manifest = chrome.runtime.getManifest();
213   return manifest.name;
216 /** @return {string} */
217 remoting.AppRemoting.prototype.runApplicationUrl = function() {
218   return remoting.settings.APP_REMOTING_API_BASE_URL + '/applications/' +
219       remoting.settings.getAppRemotingApplicationId() + '/run';
223  * @return {string} The default remap keys for the current platform.
224  */
225 remoting.AppRemoting.prototype.getDefaultRemapKeys = function() {
226   // Map Cmd to Ctrl on Mac since hosts typically use Ctrl for keyboard
227   // shortcuts, but we want them to act as natively as possible.
228   if (remoting.platformIsMac()) {
229     return '0x0700e3>0x0700e0,0x0700e7>0x0700e4';
230   }
231   return '';
235  * Called when a new session has been connected.
237  * @param {remoting.ClientSession} clientSession
238  * @return {void} Nothing.
239  */
240 remoting.AppRemoting.prototype.handleConnected = function(clientSession) {
241   remoting.identity.getUserInfo().then(
242       function(userInfo) {
243         remoting.clientSession.sendClientMessage(
244             'setUserDisplayInfo',
245             JSON.stringify({fullName: userInfo.name}));
246       });
248   // Set up a ping at 10-second intervals to test the connection speed.
249   function ping() {
250     var message = { timestamp: new Date().getTime() };
251     clientSession.sendClientMessage('pingRequest', JSON.stringify(message));
252   };
253   ping();
254   this.pingTimerId_ = window.setInterval(ping, 10 * 1000);
258  * Called when the current session has been disconnected.
260  * @return {void} Nothing.
261  */
262 remoting.AppRemoting.prototype.handleDisconnected = function() {
263   // Cancel the ping when the connection closes.
264   window.clearInterval(this.pingTimerId_);
266   chrome.app.window.current().close();
270  * Called when the current session's connection has failed.
272  * @param {remoting.SessionConnector} connector
273  * @param {remoting.Error} error
274  * @return {void} Nothing.
275  */
276 remoting.AppRemoting.prototype.handleConnectionFailed = function(
277     connector, error) {
278   this.handleError(error);
282  * Called when the current session has reached the point where the host has
283  * started streaming video frames to the client.
285  * @return {void} Nothing.
286  */
287 remoting.AppRemoting.prototype.handleVideoStreamingStarted = function() {
288   remoting.LoadingWindow.close();
292  * Called when an extension message needs to be handled.
294  * @param {string} type The type of the extension message.
295  * @param {Object} message The parsed extension message data.
296  * @return {boolean} True if the extension message was recognized.
297  */
298 remoting.AppRemoting.prototype.handleExtensionMessage = function(
299     type, message) {
300   switch (type) {
302     case 'openURL':
303       // URL requests from the hosted app are untrusted, so disallow anything
304       // other than HTTP or HTTPS.
305       var url = getStringAttr(message, 'url');
306       if (url.indexOf('http:') != 0 && url.indexOf('https:') != 0) {
307         console.error('Bad URL: ' + url);
308       } else {
309         window.open(url);
310       }
311       return true;
313     case 'onWindowRemoved':
314       var id = getNumberAttr(message, 'id');
315       this.windowActivationMenu_.remove(id);
316       return true;
318     case 'onWindowAdded':
319       var id = getNumberAttr(message, 'id');
320       var title = getStringAttr(message, 'title');
321       this.windowActivationMenu_.add(id, title);
322       return true;
324     case 'onAllWindowsMinimized':
325       chrome.app.window.current().minimize();
326       return true;
328     case 'setKeyboardLayouts':
329       var supportedLayouts = getArrayAttr(message, 'supportedLayouts');
330       var currentLayout = getStringAttr(message, 'currentLayout');
331       console.log('Current host keyboard layout: ' + currentLayout);
332       console.log('Supported host keyboard layouts: ' + supportedLayouts);
333       this.keyboardLayoutsMenu_.setLayouts(supportedLayouts, currentLayout);
334       return true;
336     case 'pingResponse':
337       var then = getNumberAttr(message, 'timestamp');
338       var now = new Date().getTime();
339       this.contextMenu_.updateConnectionRTT(now - then);
340       return true;
341   }
343   return false;
347  * Called when an error needs to be displayed to the user.
349  * @param {remoting.Error} errorTag The error to be localized and displayed.
350  * @return {void} Nothing.
351  */
352 remoting.AppRemoting.prototype.handleError = function(errorTag) {
353   console.error('Connection failed: ' + errorTag);
354   remoting.LoadingWindow.close();
355   remoting.MessageWindow.showErrorMessage(
356       chrome.i18n.getMessage(/*i18n-content*/'CONNECTION_FAILED'),
357       chrome.i18n.getMessage(/** @type {string} */ (errorTag)));