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 * It2MeService listens to incoming connections requests from Hangouts 8 * and the webapp and creates a It2MeHelperChannel between them. 9 * It supports multiple helper sessions, but only a single helpee. 10 */ 11 12'use strict'; 13 14/** @suppress {duplicate} */ 15var remoting = remoting || {}; 16 17/** 18 * @param {remoting.AppLauncher} appLauncher 19 * @constructor 20 */ 21remoting.It2MeService = function(appLauncher) { 22 /** 23 * @type {remoting.AppLauncher} 24 * @private 25 */ 26 this.appLauncher_ = appLauncher; 27 28 /** 29 * @type {Array.<remoting.It2MeHelperChannel>} 30 * @private 31 */ 32 this.helpers_ = []; 33 34 /** @private */ 35 this.helpee_ = null; 36 37 this.onWebappConnectRef_ = this.onWebappConnect_.bind(this); 38 this.onConnectExternalRef_ = this.onConnectExternal_.bind(this); 39}; 40 41/** @enum {string} */ 42remoting.It2MeService.ConnectionTypes = { 43 HELPER_HANGOUT: 'it2me.helper.hangout', 44 HELPEE_HANGOUT: 'it2me.helpee.hangout', 45 HELPER_WEBAPP: 'it2me.helper.webapp' 46}; 47 48/** 49 * Starts listening to external connection from Hangouts and the webapp. 50 */ 51remoting.It2MeService.prototype.init = function() { 52 chrome.runtime.onConnect.addListener(this.onWebappConnectRef_); 53 chrome.runtime.onConnectExternal.addListener(this.onConnectExternalRef_); 54}; 55 56remoting.It2MeService.prototype.dispose = function() { 57 chrome.runtime.onConnect.removeListener(this.onWebappConnectRef_); 58 chrome.runtime.onConnectExternal.removeListener( 59 this.onConnectExternalRef_); 60}; 61 62/** 63 * This function is called when Hangouts connects via chrome.runtime.connect. 64 * Only web pages that are white-listed in the manifest are allowed to connect. 65 * 66 * @param {chrome.runtime.Port} port 67 * @private 68 */ 69remoting.It2MeService.prototype.onConnectExternal_ = function(port) { 70 var ConnectionTypes = remoting.It2MeService.ConnectionTypes; 71 try { 72 switch (port.name) { 73 case ConnectionTypes.HELPER_HANGOUT: 74 this.handleExternalHelperConnection_(port); 75 return true; 76 case ConnectionTypes.HELPEE_HANGOUT: 77 this.handleExternalHelpeeConnection_(port); 78 return true; 79 default: 80 throw new Error('Unsupported port - ' + port.name); 81 } 82 } catch (e) { 83 var error = /**@type {Error} */ e; 84 console.error(error); 85 port.disconnect(); 86 } 87 return false; 88}; 89 90/** 91 * @param {chrome.runtime.Port} port 92 * @private 93 */ 94remoting.It2MeService.prototype.onWebappConnect_ = function(port) { 95 try { 96 console.log('Incoming helper connection from webapp.'); 97 98 // The senderId (tabId or windowId) of the webapp is embedded in the port 99 // name with the format port_name@senderId. 100 var parts = port.name.split('@'); 101 var portName = parts[0]; 102 var senderId = parts[1]; 103 var ConnectionTypes = remoting.It2MeService.ConnectionTypes; 104 if (portName === ConnectionTypes.HELPER_WEBAPP && senderId !== undefined) { 105 for (var i = 0; i < this.helpers_.length; i++) { 106 var helper = this.helpers_[i]; 107 if (helper.instanceId() === senderId) { 108 helper.onWebappConnect(port, senderId); 109 return; 110 } 111 } 112 } 113 throw new Error('No matching hangout connection found for ' + port.name); 114 } catch (e) { 115 var error = /** @type {Error} */ e; 116 console.error(error); 117 port.disconnect(); 118 } 119}; 120 121/** 122 * @param {remoting.It2MeHelperChannel} helper 123 */ 124remoting.It2MeService.prototype.onHelperChannelDisconnected = function(helper) { 125 for (var i = 0; i < this.helpers_.length; i++) { 126 if (helper === this.helpers_[i]) { 127 this.helpers_.splice(i, 1); 128 } 129 } 130}; 131 132remoting.It2MeService.prototype.onHelpeeChannelDisconnected = function() { 133 base.debug.assert(this.helpee_ !== null); 134 this.helpee_ = null; 135}; 136 137/** 138 * @param {chrome.runtime.Port} port 139 * @private 140 */ 141remoting.It2MeService.prototype.handleExternalHelperConnection_ = 142 function(port) { 143 if (this.helpee_) { 144 console.error( 145 'Cannot start a helper session while a helpee session is in process.'); 146 port.disconnect(); 147 return; 148 } 149 150 console.log('Incoming helper connection from Hangouts'); 151 var helper = new remoting.It2MeHelperChannel( 152 this.appLauncher_, port, this.onHelperChannelDisconnected.bind(this)); 153 helper.init(); 154 this.helpers_.push(helper); 155}; 156 157/** 158 * @param {chrome.runtime.Port} hangoutPort Represents a connection to Hangouts. 159 * @private 160 */ 161remoting.It2MeService.prototype.handleExternalHelpeeConnection_ = 162 function(hangoutPort) { 163 if (this.helpee_) { 164 console.error('An existing helpee session is in process.'); 165 hangoutPort.disconnect(); 166 return; 167 } 168 169 console.log('Incoming helpee connection from Hangouts'); 170 this.helpee_ = new remoting.It2MeHelpeeChannel( 171 hangoutPort, 172 new remoting.It2MeHostFacade(), 173 new remoting.HostInstaller(), 174 this.onHelpeeChannelDisconnected.bind(this)); 175 this.helpee_.init(); 176};