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