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