wcs_sandbox_container.js revision 58e6fbe4ee35d65e14b626c557d37565bf8ad179
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 application side of the application/sandbox WCS interface, used by the 9 * application to exchange messages with the sandbox. 10 */ 11 12'use strict'; 13 14/** @suppress {duplicate} */ 15var remoting = remoting || {}; 16 17/** 18 * @param {Window} sandbox The Javascript Window object representing the 19 * sandboxed WCS driver. 20 * @constructor 21 */ 22remoting.WcsSandboxContainer = function(sandbox) { 23 this.sandbox_ = sandbox; 24 /** @type {?function(string):void} */ 25 this.onLocalJid_ = null; 26 /** @type {?function(remoting.Error):void} */ 27 this.onError_ = null; 28 /** @type {?function(string):void} */ 29 this.onIq_ = null; 30 /** @type {Object.<number, XMLHttpRequest>} */ 31 this.pendingXhrs_ = {}; 32 33 window.addEventListener('message', this.onMessage_.bind(this), false); 34 35 if (remoting.isAppsV2) { 36 var message = { 37 'command': 'proxyXhrs' 38 }; 39 this.sandbox_.postMessage(message, '*'); 40 } 41}; 42 43/** 44 * @param {?function(string):void} onLocalJid Callback invoked with the client 45 * JID when the WCS code has loaded. Note that this may be called more than 46 * once (potentially with a different JID) if the WCS node is reloaded for 47 * any reason. 48 * @return {void} Nothing. 49 */ 50remoting.WcsSandboxContainer.prototype.setOnLocalJid = function(onLocalJid) { 51 this.onLocalJid_ = onLocalJid; 52}; 53 54/** 55 * @param {?function(remoting.Error):void} onError Callback invoked if the WCS 56 * code cannot be loaded. 57 * @return {void} Nothing. 58 */ 59remoting.WcsSandboxContainer.prototype.setOnError = function(onError) { 60 this.onError_ = onError; 61}; 62 63/** 64 * @param {?function(string):void} onIq Callback invoked when an IQ stanza is 65 * received. 66 * @return {void} Nothing. 67 */ 68remoting.WcsSandboxContainer.prototype.setOnIq = function(onIq) { 69 this.onIq_ = onIq; 70}; 71 72/** 73 * @param {string} token The access token. 74 * @return {void} 75 */ 76remoting.WcsSandboxContainer.prototype.setAccessToken = function(token) { 77 var message = { 78 'command': 'setAccessToken', 79 'token': token 80 }; 81 this.sandbox_.postMessage(message, '*'); 82}; 83 84/** 85 * @param {string} stanza The IQ stanza to send. 86 * @return {void} 87 */ 88remoting.WcsSandboxContainer.prototype.sendIq = function(stanza) { 89 var message = { 90 'command': 'sendIq', 91 'stanza': stanza 92 }; 93 this.sandbox_.postMessage(message, '*'); 94}; 95 96/** 97 * Event handler to process messages from the sandbox. 98 * 99 * @param {Event} event 100 */ 101remoting.WcsSandboxContainer.prototype.onMessage_ = function(event) { 102 switch (event.data['command']) { 103 104 case 'onLocalJid': 105 /** @type {string} */ 106 var clientJid = event.data['clientJid']; 107 if (clientJid === undefined) { 108 console.error('onReady: missing client JID'); 109 break; 110 } 111 if (this.onLocalJid_) { 112 this.onLocalJid_(clientJid); 113 } 114 break; 115 116 case 'onError': 117 /** @type {remoting.Error} */ 118 var error = event.data['error']; 119 if (error === undefined) { 120 console.error('onError: missing error code'); 121 break; 122 } 123 this.onError_(error); 124 break; 125 126 case 'onIq': 127 /** @type {string} */ 128 var stanza = event.data['stanza']; 129 if (stanza === undefined) { 130 console.error('onIq: missing IQ stanza'); 131 break; 132 } 133 if (this.onIq_) { 134 this.onIq_(stanza); 135 } 136 break; 137 138 case 'sendXhr': 139 /** @type {number} */ 140 var id = event.data['id']; 141 if (id === undefined) { 142 console.error('sendXhr: missing id'); 143 break; 144 } 145 /** @type {Object} */ 146 var parameters = event.data['parameters']; 147 if (parameters === undefined) { 148 console.error('sendXhr: missing parameters'); 149 break; 150 } 151 /** @type {string} */ 152 var method = parameters['method']; 153 if (method === undefined) { 154 console.error('sendXhr: missing method'); 155 break; 156 } 157 /** @type {string} */ 158 var url = parameters['url']; 159 if (url === undefined) { 160 console.error('sendXhr: missing url'); 161 break; 162 } 163 /** @type {string} */ 164 var data = parameters['data']; 165 if (data === undefined) { 166 console.error('sendXhr: missing data'); 167 break; 168 } 169 /** @type {string|undefined}*/ 170 var user = parameters['user']; 171 /** @type {string|undefined}*/ 172 var password = parameters['password']; 173 var xhr = new XMLHttpRequest; 174 this.pendingXhrs_[id] = xhr; 175 xhr.open(method, url, true, user, password); 176 /** @type {Object} */ 177 var headers = parameters['headers']; 178 if (headers) { 179 for (var header in headers) { 180 xhr.setRequestHeader(header, headers[header]); 181 } 182 } 183 xhr.onreadystatechange = this.onReadyStateChange_.bind(this, id); 184 xhr.send(data); 185 break; 186 187 case 'abortXhr': 188 var id = event.data['id']; 189 if (id === undefined) { 190 console.error('abortXhr: missing id'); 191 break; 192 } 193 var xhr = this.pendingXhrs_[id] 194 if (!xhr) { 195 // It's possible for an abort and a reply to cross each other on the 196 // IPC channel. In that case, we silently ignore the abort. 197 break; 198 } 199 xhr.abort(); 200 break; 201 202 default: 203 console.error('Unexpected message:', event.data['command'], event.data); 204 } 205}; 206 207/** 208 * Return a "copy" of an XHR object suitable for postMessage. Specifically, 209 * remove all non-serializable members such as functions. 210 * 211 * @param {XMLHttpRequest} xhr The XHR to serialize. 212 * @return {Object} A serializable version of the input. 213 */ 214function sanitizeXhr_(xhr) { 215 /** @type {Object} */ 216 var result = { 217 readyState: xhr.readyState, 218 response: xhr.response, 219 responseText: xhr.responseText, 220 responseType: xhr.responseType, 221 responseXML: xhr.responseXML, 222 status: xhr.status, 223 statusText: xhr.statusText, 224 withCredentials: xhr.withCredentials 225 }; 226 return result; 227} 228 229/** 230 * @param {number} id The unique ID of the XHR for which the state has changed. 231 * @private 232 */ 233remoting.WcsSandboxContainer.prototype.onReadyStateChange_ = function(id) { 234 var xhr = this.pendingXhrs_[id]; 235 if (!xhr) { 236 // XHRs are only removed when they have completed, in which case no 237 // further callbacks should be received. 238 console.error('Unexpected callback for xhr', id); 239 return; 240 } 241 var message = { 242 'command': 'xhrStateChange', 243 'id': id, 244 'xhr': sanitizeXhr_(xhr) 245 }; 246 this.sandbox_.postMessage(message, '*'); 247 if (xhr.readyState == 4) { 248 delete this.pendingXhrs_[id]; 249 } 250} 251 252/** @type {remoting.WcsSandboxContainer} */ 253remoting.wcsSandbox = null;