Update V8 to version 3.30.4 (based on bleeding_edge revision r24443)
[chromium-blink-merge.git] / remoting / webapp / background / it2me_helper_channel.js
blob84608be60b576a47744dfaeb15e05b15a719a643
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
8 * It2MeHelperChannel relays messages between Hangouts and Chrome Remote Desktop
9 * (webapp) for the helper (the Hangouts participant who is giving remote
10 * assistance).
12 * It runs in the background page and contains two chrome.runtime.Port objects,
13 * representing connections to the webapp and hangout, respectively.
15 * Connection is always initiated from Hangouts by calling
16 * var port = chrome.runtime.connect({name:'it2me.helper.hangout'}, extId).
17 * port.postMessage('hello')
18 * If the webapp is not installed, |port.onDisconnect| will fire.
19 * If the webapp is installed, Hangouts will receive a hello response with the
20 * list of supported features.
22 * Hangout It2MeHelperChannel Chrome Remote Desktop
23 * |-----runtime.connect() ------>| |
24 * |--------hello message-------->| |
25 * | |<-----helloResponse message-----|
26 * |-------connect message------->| |
27 * | |-------appLauncher.launch()---->|
28 * | |<------runtime.connect()------- |
29 * | |<-----sessionStateChanged------ |
30 * |<----sessionStateChanged------| |
32 * Disconnection can be initiated from either side:
33 * 1. In the normal flow initiated from hangout
34 * Hangout It2MeHelperChannel Chrome Remote Desktop
35 * |-----disconnect message------>| |
36 * |<-sessionStateChanged(CLOSED)-| |
37 * | |-----appLauncher.close()------>|
39 * 2. In the normal flow initiated from webapp
40 * Hangout It2MeHelperChannel Chrome Remote Desktop
41 * | |<-sessionStateChanged(CLOSED)--|
42 * | |<--------port.disconnect()-----|
43 * |<--------port.disconnect()----| |
45 * 2. If hangout crashes
46 * Hangout It2MeHelperChannel Chrome Remote Desktop
47 * |---------port.disconnect()--->| |
48 * | |--------port.disconnect()----->|
49 * | |------appLauncher.close()----->|
51 * 3. If webapp crashes
52 * Hangout It2MeHelperChannel Chrome Remote Desktop
53 * | |<-------port.disconnect()------|
54 * |<-sessionStateChanged(FAILED)-| |
55 * |<--------port.disconnect()----| |
58 'use strict';
60 /** @suppress {duplicate} */
61 var remoting = remoting || {};
63 /**
64 * @param {remoting.AppLauncher} appLauncher
65 * @param {chrome.runtime.Port} hangoutPort Represents an active connection to
66 * Hangouts.
67 * @param {function(remoting.It2MeHelperChannel)} onDisconnectCallback Callback
68 * to notify when the connection is torn down. IT2MeService uses this
69 * callback to dispose of the channel object.
70 * @constructor
72 remoting.It2MeHelperChannel =
73 function(appLauncher, hangoutPort, onDisconnectCallback) {
75 /**
76 * @type {remoting.AppLauncher}
77 * @private
79 this.appLauncher_ = appLauncher;
81 /**
82 * @type {chrome.runtime.Port}
83 * @private
85 this.hangoutPort_ = hangoutPort;
87 /**
88 * @type {chrome.runtime.Port}
89 * @private
91 this.webappPort_ = null;
93 /**
94 * @type {string}
95 * @private
97 this.instanceId_ = '';
99 /**
100 * @type {remoting.ClientSession.State}
101 * @private
103 this.sessionState_ = remoting.ClientSession.State.CONNECTING;
106 * @type {?function(remoting.It2MeHelperChannel)}
107 * @private
109 this.onDisconnectCallback_ = onDisconnectCallback;
111 this.onWebappMessageRef_ = this.onWebappMessage_.bind(this);
112 this.onWebappDisconnectRef_ = this.onWebappDisconnect_.bind(this);
113 this.onHangoutMessageRef_ = this.onHangoutMessage_.bind(this);
114 this.onHangoutDisconnectRef_ = this.onHangoutDisconnect_.bind(this);
117 /** @enum {string} */
118 remoting.It2MeHelperChannel.HangoutMessageTypes = {
119 HELLO: 'hello',
120 HELLO_RESPONSE: 'helloResponse',
121 CONNECT: 'connect',
122 DISCONNECT: 'disconnect',
123 ERROR: 'error'
126 /** @enum {string} */
127 remoting.It2MeHelperChannel.Features = {
128 REMOTE_ASSISTANCE: 'remoteAssistance'
131 /** @enum {string} */
132 remoting.It2MeHelperChannel.WebappMessageTypes = {
133 SESSION_STATE_CHANGED: 'sessionStateChanged'
136 remoting.It2MeHelperChannel.prototype.init = function() {
137 this.hangoutPort_.onMessage.addListener(this.onHangoutMessageRef_);
138 this.hangoutPort_.onDisconnect.addListener(this.onHangoutDisconnectRef_);
141 /** @return {string} */
142 remoting.It2MeHelperChannel.prototype.instanceId = function() {
143 return this.instanceId_;
147 * @param {{method:string, data:Object.<string,*>}} message
148 * @return {boolean} whether the message is handled or not.
149 * @private
151 remoting.It2MeHelperChannel.prototype.onHangoutMessage_ = function(message) {
152 try {
153 var MessageTypes = remoting.It2MeHelperChannel.HangoutMessageTypes;
154 switch (message.method) {
155 case MessageTypes.CONNECT:
156 this.launchWebapp_(message);
157 return true;
158 case MessageTypes.DISCONNECT:
159 this.closeWebapp_(message);
160 return true;
161 case MessageTypes.HELLO:
162 this.hangoutPort_.postMessage({
163 method: MessageTypes.HELLO_RESPONSE,
164 supportedFeatures: base.values(remoting.It2MeHelperChannel.Features)
166 return true;
168 throw new Error('Unknown message method=' + message.method);
169 } catch(e) {
170 var error = /** @type {Error} */ e;
171 this.sendErrorResponse_(this.hangoutPort_, error, message);
173 return false;
177 * Disconnect the existing connection to the helpee.
179 * @param {{method:string, data:Object.<string,*>}} message
180 * @private
182 remoting.It2MeHelperChannel.prototype.closeWebapp_ =
183 function(message) {
184 // TODO(kelvinp): Closing the v2 app currently doesn't disconnect the IT2me
185 // session (crbug.com/402137), so send an explicit notification to Hangouts.
186 this.sessionState_ = remoting.ClientSession.State.CLOSED;
187 this.hangoutPort_.postMessage({
188 method: 'sessionStateChanged',
189 state: this.sessionState_
191 this.appLauncher_.close(this.instanceId_);
195 * Launches the web app.
197 * @param {{method:string, data:Object.<string,*>}} message
198 * @private
200 remoting.It2MeHelperChannel.prototype.launchWebapp_ =
201 function(message) {
202 var accessCode = getStringAttr(message, 'accessCode');
203 if (!accessCode) {
204 throw new Error('Access code is missing');
207 // Launch the webapp.
208 this.appLauncher_.launch({
209 mode: 'hangout',
210 accessCode: accessCode
211 }).then(
213 * @this {remoting.It2MeHelperChannel}
214 * @param {string} instanceId
216 function(instanceId){
217 this.instanceId_ = instanceId;
218 }.bind(this));
222 * @private
224 remoting.It2MeHelperChannel.prototype.onHangoutDisconnect_ = function() {
225 this.appLauncher_.close(this.instanceId_);
226 this.unhookPorts_();
230 * @param {chrome.runtime.Port} port The port represents a connection to the
231 * webapp.
232 * @param {string} id The id of the tab or window that is hosting the webapp.
234 remoting.It2MeHelperChannel.prototype.onWebappConnect = function(port, id) {
235 base.debug.assert(id === this.instanceId_);
236 base.debug.assert(this.hangoutPort_ !== null);
238 // Hook listeners.
239 port.onMessage.addListener(this.onWebappMessageRef_);
240 port.onDisconnect.addListener(this.onWebappDisconnectRef_);
241 this.webappPort_ = port;
244 /** @param {chrome.runtime.Port} port The webapp port. */
245 remoting.It2MeHelperChannel.prototype.onWebappDisconnect_ = function(port) {
246 // If the webapp port got disconnected while the session is still connected,
247 // treat it as an error.
248 var States = remoting.ClientSession.State;
249 if (this.sessionState_ === States.CONNECTING ||
250 this.sessionState_ === States.CONNECTED) {
251 this.sessionState_ = States.FAILED;
252 this.hangoutPort_.postMessage({
253 method: 'sessionStateChanged',
254 state: this.sessionState_
257 this.unhookPorts_();
261 * @param {{method:string, data:Object.<string,*>}} message
262 * @private
264 remoting.It2MeHelperChannel.prototype.onWebappMessage_ = function(message) {
265 try {
266 console.log('It2MeHelperChannel id=' + this.instanceId_ +
267 ' incoming message method=' + message.method);
268 var MessageTypes = remoting.It2MeHelperChannel.WebappMessageTypes;
269 switch (message.method) {
270 case MessageTypes.SESSION_STATE_CHANGED:
271 var state = getNumberAttr(message, 'state');
272 this.sessionState_ =
273 /** @type {remoting.ClientSession.State} */ state;
274 this.hangoutPort_.postMessage(message);
275 return true;
277 throw new Error('Unknown message method=' + message.method);
278 } catch(e) {
279 var error = /** @type {Error} */ e;
280 this.sendErrorResponse_(this.webappPort_, error, message);
282 return false;
285 remoting.It2MeHelperChannel.prototype.unhookPorts_ = function() {
286 if (this.webappPort_) {
287 this.webappPort_.onMessage.removeListener(this.onWebappMessageRef_);
288 this.webappPort_.onDisconnect.removeListener(this.onWebappDisconnectRef_);
289 this.webappPort_.disconnect();
290 this.webappPort_ = null;
293 if (this.hangoutPort_) {
294 this.hangoutPort_.onMessage.removeListener(this.onHangoutMessageRef_);
295 this.hangoutPort_.onDisconnect.removeListener(this.onHangoutDisconnectRef_);
296 this.hangoutPort_.disconnect();
297 this.hangoutPort_ = null;
300 if (this.onDisconnectCallback_) {
301 this.onDisconnectCallback_(this);
302 this.onDisconnectCallback_ = null;
307 * @param {chrome.runtime.Port} port
308 * @param {string|Error} error
309 * @param {?{method:string, data:Object.<string,*>}=} opt_incomingMessage
310 * @private
312 remoting.It2MeHelperChannel.prototype.sendErrorResponse_ =
313 function(port, error, opt_incomingMessage) {
314 if (error instanceof Error) {
315 error = error.message;
318 console.error('Error responding to message method:' +
319 (opt_incomingMessage ? opt_incomingMessage.method : 'null') +
320 ' error:' + error);
321 port.postMessage({
322 method: remoting.It2MeHelperChannel.HangoutMessageTypes.ERROR,
323 message: error,
324 request: opt_incomingMessage