Home | History | Annotate | Download | only in webapp
      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} */
     15 var remoting = remoting || {};
     16 
     17 /** @constructor */
     18 remoting.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  */
     43 remoting.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  */
    131 remoting.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  */
    146 remoting.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  */
    161 remoting.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  */
    179 remoting.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  */
    200 remoting.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  */
    212 function 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 
    227 window.addEventListener('load', onSandboxInit, false);
    228 
    229 /** @type {remoting.WcsSandboxContent} */
    230 remoting.sandboxContent = null;
    231