1/* Copyright 2013 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/** 7 * @fileoverview 8 * The sandbox side of the application/sandbox WCS interface, used by the 9 * sandbox to exchange messages with the application. 10 */ 11 12'use strict'; 13 14/** @suppress {duplicate} */ 15var remoting = remoting || {}; 16 17/** @constructor */ 18remoting.WcsSandboxContent = function() { 19 /** 20 * @type {Window} 21 * @private 22 */ 23 this.parentWindow_ = null; 24 /** 25 * @type {number} 26 * @private 27 */ 28 this.nextXhrId_ = 0; 29 /** 30 * @type {Object.<number, XMLHttpRequest>} 31 * @private 32 */ 33 this.pendingXhrs_ = {}; 34 35 window.addEventListener('message', this.onMessage_.bind(this), false); 36}; 37 38/** 39 * Event handler to process messages from the application. 40 * 41 * @param {Event} event 42 */ 43remoting.WcsSandboxContent.prototype.onMessage_ = function(event) { 44 this.parentWindow_ = event.source; 45 46 switch (event.data['command']) { 47 48 case 'proxyXhrs': 49 // Since the WCS driver code constructs XHRs directly, the only 50 // mechanism for proxying them is to replace the XMLHttpRequest 51 // constructor. 52 XMLHttpRequest = remoting.XMLHttpRequestProxy; 53 break; 54 55 case 'sendIq': 56 /** @type {string} */ 57 var stanza = event.data['stanza']; 58 if (stanza === undefined) { 59 console.error('sendIq: missing IQ stanza.'); 60 break; 61 } 62 if (remoting.wcs) { 63 remoting.wcs.sendIq(stanza); 64 } else { 65 console.error('Dropping IQ stanza:', stanza); 66 } 67 break; 68 69 case 'setAccessToken': 70 /** @type {string} */ 71 var token = event.data['token']; 72 if (token === undefined) { 73 console.error('setAccessToken: missing access token.'); 74 break; 75 } 76 // The WCS driver JS requires that remoting.wcsLoader be a global 77 // variable, so it can't be a member of this class. 78 // TODO(jamiewalch): remoting.wcs doesn't need to be global and should 79 // be made a member (http://crbug.com/172348). 80 if (remoting.wcs) { 81 remoting.wcs.updateAccessToken(token); 82 } else if (!remoting.wcsLoader) { 83 remoting.wcsLoader = new remoting.WcsLoader(); 84 remoting.wcsLoader.start(token, 85 this.onLocalJid_.bind(this), 86 this.onError_.bind(this)); 87 } 88 break; 89 90 case 'xhrStateChange': 91 /** @type {number} */ 92 var id = event.data['id']; 93 if (id === undefined) { 94 console.error('xhrStateChange: missing id.'); 95 break; 96 } 97 var pendingXhr = this.pendingXhrs_[id]; 98 if (!pendingXhr) { 99 console.error('xhrStateChange: unrecognized id:', id); 100 break; 101 } 102 /** @type {XMLHttpRequest} */ 103 var xhr = event.data['xhr']; 104 if (xhr === undefined) { 105 console.error('xhrStateChange: missing xhr'); 106 break; 107 } 108 for (var member in xhr) { 109 pendingXhr[member] = xhr[member]; 110 } 111 if (xhr.readyState == 4) { 112 delete this.pendingXhrs_[id]; 113 } 114 if (pendingXhr.onreadystatechange) { 115 pendingXhr.onreadystatechange(); 116 } 117 break; 118 119 default: 120 console.error('Unexpected message:', event.data['command'], event.data); 121 } 122}; 123 124/** 125 * Callback method to indicate that the WCS driver has loaded and provide the 126 * full JID of the client. 127 * 128 * @param {string} localJid The full JID of the WCS client. 129 * @private 130 */ 131remoting.WcsSandboxContent.prototype.onLocalJid_ = function(localJid) { 132 remoting.wcs.setOnIq(this.onIq_.bind(this)); 133 var message = { 134 'command': 'onLocalJid', 135 'localJid': localJid 136 }; 137 this.parentWindow_.postMessage(message, '*'); 138}; 139 140/** 141 * Callback method to indicate that something went wrong loading the WCS driver. 142 * 143 * @param {remoting.Error} error Details of the error. 144 * @private 145 */ 146remoting.WcsSandboxContent.prototype.onError_ = function(error) { 147 var message = { 148 'command': 'onError', 149 'error': error 150 }; 151 this.parentWindow_.postMessage(message, '*'); 152}; 153 154/** 155 * Forward an XHR to the container process to send. This is analogous to XHR's 156 * send method. 157 * 158 * @param {remoting.XMLHttpRequestProxy} xhr The XHR to send. 159 * @return {number} The unique ID allocated to the XHR. Used to abort it. 160 */ 161remoting.WcsSandboxContent.prototype.sendXhr = function(xhr) { 162 var id = this.nextXhrId_++; 163 this.pendingXhrs_[id] = xhr; 164 var message = { 165 'command': 'sendXhr', 166 'id': id, 167 'parameters': xhr.sandbox_ipc 168 }; 169 this.parentWindow_.postMessage(message, '*'); 170 delete xhr.sandbox_ipc; 171 return id; 172}; 173 174/** 175 * Abort a forwarded XHR. This is analogous to XHR's abort method. 176 * 177 * @param {number} id The unique ID of the XHR to abort, as returned by sendXhr. 178 */ 179remoting.WcsSandboxContent.prototype.abortXhr = function(id) { 180 if (!this.pendingXhrs_[id]) { 181 // The XHR is removed when it reaches the "ready" state. Calling abort 182 // subsequently is unusual, but legal, so just silently ignore the request 183 // in this case. 184 return; 185 } 186 var message = { 187 'command': 'abortXhr', 188 'id': id 189 }; 190 this.parentWindow_.postMessage(message, '*'); 191}; 192 193/** 194 * Callback to indicate than an IQ stanza has been received from the WCS 195 * driver, and should be forwarded to the main process. 196 * 197 * @param {string} stanza 198 * @private 199 */ 200remoting.WcsSandboxContent.prototype.onIq_ = function(stanza) { 201 remoting.wcs.setOnIq(this.onIq_.bind(this)); 202 var message = { 203 'command': 'onIq', 204 'stanza': stanza 205 }; 206 this.parentWindow_.postMessage(message, '*'); 207}; 208 209/** 210 * Entry point for the WCS sandbox process. 211 */ 212function onSandboxInit() { 213 // The WCS code registers for a couple of events that aren't supported in 214 // Apps V2, so ignore those for now. 215 var oldAEL = window.addEventListener; 216 window.addEventListener = function(type, listener, useCapture) { 217 if (type == 'beforeunload' || type == 'unload') { 218 return; 219 } 220 oldAEL(type, listener, useCapture); 221 }; 222 223 remoting.settings = new remoting.Settings(); 224 remoting.sandboxContent = new remoting.WcsSandboxContent(); 225} 226 227window.addEventListener('load', onSandboxInit, false); 228 229/** @type {remoting.WcsSandboxContent} */ 230remoting.sandboxContent = null; 231