/* Copyright 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /** * @fileoverview * The sandbox side of the application/sandbox WCS interface, used by the * sandbox to exchange messages with the application. */ 'use strict'; /** @suppress {duplicate} */ var remoting = remoting || {}; /** @constructor */ remoting.WcsSandboxContent = function() { /** * @type {Window} * @private */ this.parentWindow_ = null; /** * @type {number} * @private */ this.nextXhrId_ = 0; /** * @type {Object.<number, XMLHttpRequest>} * @private */ this.pendingXhrs_ = {}; window.addEventListener('message', this.onMessage_.bind(this), false); }; /** * Event handler to process messages from the application. * * @param {Event} event */ remoting.WcsSandboxContent.prototype.onMessage_ = function(event) { this.parentWindow_ = event.source; switch (event.data['command']) { case 'proxyXhrs': // Since the WCS driver code constructs XHRs directly, the only // mechanism for proxying them is to replace the XMLHttpRequest // constructor. XMLHttpRequest = remoting.XMLHttpRequestProxy; break; case 'sendIq': /** @type {string} */ var stanza = event.data['stanza']; if (stanza === undefined) { console.error('sendIq: missing IQ stanza.'); break; } if (remoting.wcs) { remoting.wcs.sendIq(stanza); } else { console.error('Dropping IQ stanza:', stanza); } break; case 'setAccessToken': /** @type {string} */ var token = event.data['token']; if (token === undefined) { console.error('setAccessToken: missing access token.'); break; } // The WCS driver JS requires that remoting.wcsLoader be a global // variable, so it can't be a member of this class. // TODO(jamiewalch): remoting.wcs doesn't need to be global and should // be made a member (http://crbug.com/172348). if (remoting.wcs) { remoting.wcs.updateAccessToken(token); } else if (!remoting.wcsLoader) { remoting.wcsLoader = new remoting.WcsLoader(); remoting.wcsLoader.start(token, this.onLocalJid_.bind(this), this.onError_.bind(this)); } break; case 'xhrStateChange': /** @type {number} */ var id = event.data['id']; if (id === undefined) { console.error('xhrStateChange: missing id.'); break; } var pendingXhr = this.pendingXhrs_[id]; if (!pendingXhr) { console.error('xhrStateChange: unrecognized id:', id); break; } /** @type {XMLHttpRequest} */ var xhr = event.data['xhr']; if (xhr === undefined) { console.error('xhrStateChange: missing xhr'); break; } for (var member in xhr) { pendingXhr[member] = xhr[member]; } if (xhr.readyState == 4) { delete this.pendingXhrs_[id]; } if (pendingXhr.onreadystatechange) { pendingXhr.onreadystatechange(); } break; default: console.error('Unexpected message:', event.data['command'], event.data); } }; /** * Callback method to indicate that the WCS driver has loaded and provide the * full JID of the client. * * @param {string} localJid The full JID of the WCS client. * @private */ remoting.WcsSandboxContent.prototype.onLocalJid_ = function(localJid) { remoting.wcs.setOnIq(this.onIq_.bind(this)); var message = { 'command': 'onLocalJid', 'localJid': localJid }; this.parentWindow_.postMessage(message, '*'); }; /** * Callback method to indicate that something went wrong loading the WCS driver. * * @param {remoting.Error} error Details of the error. * @private */ remoting.WcsSandboxContent.prototype.onError_ = function(error) { var message = { 'command': 'onError', 'error': error }; this.parentWindow_.postMessage(message, '*'); }; /** * Forward an XHR to the container process to send. This is analogous to XHR's * send method. * * @param {remoting.XMLHttpRequestProxy} xhr The XHR to send. * @return {number} The unique ID allocated to the XHR. Used to abort it. */ remoting.WcsSandboxContent.prototype.sendXhr = function(xhr) { var id = this.nextXhrId_++; this.pendingXhrs_[id] = xhr; var message = { 'command': 'sendXhr', 'id': id, 'parameters': xhr.sandbox_ipc }; this.parentWindow_.postMessage(message, '*'); delete xhr.sandbox_ipc; return id; }; /** * Abort a forwarded XHR. This is analogous to XHR's abort method. * * @param {number} id The unique ID of the XHR to abort, as returned by sendXhr. */ remoting.WcsSandboxContent.prototype.abortXhr = function(id) { if (!this.pendingXhrs_[id]) { // The XHR is removed when it reaches the "ready" state. Calling abort // subsequently is unusual, but legal, so just silently ignore the request // in this case. return; } var message = { 'command': 'abortXhr', 'id': id }; this.parentWindow_.postMessage(message, '*'); }; /** * Callback to indicate than an IQ stanza has been received from the WCS * driver, and should be forwarded to the main process. * * @param {string} stanza * @private */ remoting.WcsSandboxContent.prototype.onIq_ = function(stanza) { remoting.wcs.setOnIq(this.onIq_.bind(this)); var message = { 'command': 'onIq', 'stanza': stanza }; this.parentWindow_.postMessage(message, '*'); }; /** * Entry point for the WCS sandbox process. */ function onSandboxInit() { // The WCS code registers for a couple of events that aren't supported in // Apps V2, so ignore those for now. var oldAEL = window.addEventListener; window.addEventListener = function(type, listener, useCapture) { if (type == 'beforeunload' || type == 'unload') { return; } oldAEL(type, listener, useCapture); }; remoting.settings = new remoting.Settings(); remoting.sandboxContent = new remoting.WcsSandboxContent(); } window.addEventListener('load', onSandboxInit, false); /** @type {remoting.WcsSandboxContent} */ remoting.sandboxContent = null;