Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / remoting / webapp / background / it2me_helper_channel.js
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.
4
5 /**
6  * @fileoverview
7  *
8  * It2MeHelperChannel relays messages between Hangouts and Chrome Remote Desktop
9  * (webapp) for the helper (the Hangouts participant who is giving remote
10  * assistance).
11  *
12  * It runs in the background page and contains two chrome.runtime.Port objects,
13  * respresenting connections to the webapp and hangout, respectively.
14  *
15  * Connection is always initiated from Hangouts.
16  *
17  *   Hangout                     It2MeHelperChannel        Chrome Remote Desktop
18  *      |-----runtime.connect() ------>|                                |
19  *      |------connect message-------->|                                |
20  *      |                              |-------appLauncher.launch()---->|
21  *      |                              |<------runtime.connect()------- |
22  *      |                              |<-----sessionStateChanged------ |
23  *      |<----sessionStateChanged------|                                |
24  *
25  * Disconnection can be initiated from either side:
26  * 1. In the normal flow initiated from hangout
27  *    Hangout                    It2MeHelperChannel        Chrome Remote Desktop
28  *       |-----disconnect message------>|                               |
29  *       |<-sessionStateChanged(CLOSED)-|                               |
30  *       |                              |-----appLauncher.close()------>|
31  *
32  * 2. In the normal flow initiated from webapp
33  *    Hangout                    It2MeHelperChannel        Chrome Remote Desktop
34  *       |                              |<-sessionStateChanged(CLOSED)--|
35  *       |                              |<--------port.disconnect()-----|
36  *       |<--------port.disconnect()----|                               |
37  *
38  * 2. If hangout crashes
39  *    Hangout                    It2MeHelperChannel        Chrome Remote Desktop
40  *       |---------port.disconnect()--->|                               |
41  *       |                              |--------port.disconnect()----->|
42  *       |                              |------appLauncher.close()----->|
43  *
44  * 3. If webapp crashes
45  *    Hangout                    It2MeHelperChannel        Chrome Remote Desktop
46  *       |                              |<-------port.disconnect()------|
47  *       |<-sessionStateChanged(FAILED)-|                               |
48  *       |<--------port.disconnect()----|                               |
49  */
50
51 'use strict';
52
53 /** @suppress {duplicate} */
54 var remoting = remoting || {};
55
56 /**
57  * @param {remoting.AppLauncher} appLauncher
58  * @param {chrome.runtime.Port} hangoutPort Represents an active connection to
59  *     Hangouts.
60  * @param {function(remoting.It2MeHelperChannel)} onDisconnectCallback Callback
61  *     to notify when the connection is torn down.  IT2MeService uses this
62  *     callback to dispose of the channel object.
63  * @constructor
64  */
65 remoting.It2MeHelperChannel =
66     function(appLauncher, hangoutPort, onDisconnectCallback) {
67
68   /**
69    * @type {remoting.AppLauncher}
70    * @private
71    */
72   this.appLauncher_ = appLauncher;
73
74   /**
75    * @type {chrome.runtime.Port}
76    * @private
77    */
78   this.hangoutPort_ = hangoutPort;
79
80   /**
81    * @type {chrome.runtime.Port}
82    * @private
83    */
84   this.webappPort_ = null;
85
86   /**
87    * @type {string}
88    * @private
89    */
90   this.instanceId_ = '';
91
92   /**
93    * @type {remoting.ClientSession.State}
94    * @private
95    */
96   this.sessionState_ = remoting.ClientSession.State.CONNECTING;
97
98   /**
99    * @type {?function(remoting.It2MeHelperChannel)}
100    * @private
101    */
102   this.onDisconnectCallback_ = onDisconnectCallback;
103
104   this.onWebappMessageRef_ = this.onWebappMessage_.bind(this);
105   this.onWebappDisconnectRef_ = this.onWebappDisconnect_.bind(this);
106   this.onHangoutMessageRef_ = this.onHangoutMessage_.bind(this);
107   this.onHangoutDisconnectRef_ = this.onHangoutDisconnect_.bind(this);
108 };
109
110 /** @enum {string} */
111 remoting.It2MeHelperChannel.HangoutMessageTypes = {
112   CONNECT: 'connect',
113   DISCONNECT: 'disconnect'
114 };
115
116 /** @enum {string} */
117 remoting.It2MeHelperChannel.WebappMessageTypes = {
118   SESSION_STATE_CHANGED: 'sessionStateChanged'
119 };
120
121 remoting.It2MeHelperChannel.prototype.init = function() {
122   this.hangoutPort_.onMessage.addListener(this.onHangoutMessageRef_);
123   this.hangoutPort_.onDisconnect.addListener(this.onHangoutDisconnectRef_);
124 };
125
126 /** @return {string} */
127 remoting.It2MeHelperChannel.prototype.instanceId = function() {
128   return this.instanceId_;
129 };
130
131 /**
132  * @param {{method:string, data:Object.<string,*>}} message
133  * @return {boolean} whether the message is handled or not.
134  * @private
135  */
136 remoting.It2MeHelperChannel.prototype.onHangoutMessage_ = function(message) {
137   try {
138     var MessageTypes = remoting.It2MeHelperChannel.HangoutMessageTypes;
139     switch (message.method) {
140       case MessageTypes.CONNECT:
141         this.launchWebapp_(message);
142         return true;
143       case MessageTypes.DISCONNECT:
144         this.closeWebapp_(message);
145         return true;
146     }
147   } catch(e) {
148     var error = /** @type {Error} */ e;
149     console.error(error);
150     this.hangoutPort_.postMessage({
151       method: message.method + 'Response',
152       error: error.message
153     });
154   }
155   return false;
156 };
157
158 /**
159  * Disconnect the existing connection to the helpee.
160  *
161  * @param {{method:string, data:Object.<string,*>}} message
162  * @private
163  */
164 remoting.It2MeHelperChannel.prototype.closeWebapp_ =
165     function(message) {
166   // TODO(kelvinp): Closing the v2 app currently doesn't disconnect the IT2me
167   // session (crbug.com/402137), so send an explicit notification to Hangouts.
168   this.sessionState_ = remoting.ClientSession.State.CLOSED;
169   this.hangoutPort_.postMessage({
170     method: 'sessionStateChanged',
171     state: this.sessionState_
172   });
173   this.appLauncher_.close(this.instanceId_);
174 };
175
176 /**
177  * Launches the web app.
178  *
179  * @param {{method:string, data:Object.<string,*>}} message
180  * @private
181  */
182 remoting.It2MeHelperChannel.prototype.launchWebapp_ =
183     function(message) {
184   var accessCode = getStringAttr(message, 'accessCode');
185   if (!accessCode) {
186     throw new Error('Access code is missing');
187   }
188
189   // Launch the webapp.
190   this.appLauncher_.launch({
191     mode: 'hangout',
192     accessCode: accessCode
193   }).then(
194     /**
195      * @this {remoting.It2MeHelperChannel}
196      * @param {string} instanceId
197      */
198     function(instanceId){
199       this.instanceId_ = instanceId;
200     }.bind(this));
201 };
202
203 /**
204  * @private
205  */
206 remoting.It2MeHelperChannel.prototype.onHangoutDisconnect_ = function() {
207   this.appLauncher_.close(this.instanceId_);
208   this.unhookPorts_();
209 };
210
211 /**
212  * @param {chrome.runtime.Port} port The port represents a connection to the
213  *     webapp.
214  * @param {string} id The id of the tab or window that is hosting the webapp.
215  */
216 remoting.It2MeHelperChannel.prototype.onWebappConnect = function(port, id) {
217   base.debug.assert(id === this.instanceId_);
218   base.debug.assert(this.hangoutPort_ !== null);
219
220   // Hook listeners.
221   port.onMessage.addListener(this.onWebappMessageRef_);
222   port.onDisconnect.addListener(this.onWebappDisconnectRef_);
223   this.webappPort_ = port;
224 };
225
226 /** @param {chrome.runtime.Port} port The webapp port. */
227 remoting.It2MeHelperChannel.prototype.onWebappDisconnect_ = function(port) {
228   // If the webapp port got disconnected while the session is still connected,
229   // treat it as an error.
230   var States = remoting.ClientSession.State;
231   if (this.sessionState_ === States.CONNECTING ||
232       this.sessionState_ === States.CONNECTED) {
233     this.sessionState_ = States.FAILED;
234     this.hangoutPort_.postMessage({
235       method: 'sessionStateChanged',
236       state: this.sessionState_
237     });
238   }
239   this.unhookPorts_();
240 };
241
242 /**
243  * @param {{method:string, data:Object.<string,*>}} message
244  * @private
245  */
246 remoting.It2MeHelperChannel.prototype.onWebappMessage_ = function(message) {
247   try {
248     console.log('It2MeHelperChannel id=' + this.instanceId_ +
249                 ' incoming message method=' + message.method);
250     var MessageTypes = remoting.It2MeHelperChannel.WebappMessageTypes;
251     switch (message.method) {
252       case MessageTypes.SESSION_STATE_CHANGED:
253         var state = getNumberAttr(message, 'state');
254         this.sessionState_ =
255             /** @type {remoting.ClientSession.State} */ state;
256         this.hangoutPort_.postMessage(message);
257         return true;
258     }
259     throw new Error('Unknown message method=' + message.method);
260   } catch(e) {
261     var error = /** @type {Error} */ e;
262     console.error(error);
263     this.webappPort_.postMessage({
264       method: message.method + 'Response',
265       error: error.message
266     });
267   }
268   return false;
269 };
270
271 remoting.It2MeHelperChannel.prototype.unhookPorts_ = function() {
272   if (this.webappPort_) {
273     this.webappPort_.onMessage.removeListener(this.onWebappMessageRef_);
274     this.webappPort_.onDisconnect.removeListener(this.onWebappDisconnectRef_);
275     this.webappPort_.disconnect();
276     this.webappPort_ = null;
277   }
278
279   if (this.hangoutPort_) {
280     this.hangoutPort_.onMessage.removeListener(this.onHangoutMessageRef_);
281     this.hangoutPort_.onDisconnect.removeListener(this.onHangoutDisconnectRef_);
282     this.hangoutPort_.disconnect();
283     this.hangoutPort_ = null;
284   }
285
286   if (this.onDisconnectCallback_) {
287     this.onDisconnectCallback_(this);
288     this.onDisconnectCallback_  = null;
289   }
290 };