Home | History | Annotate | Download | only in bullet
      1 // Copyright (c) 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 function NaClAMMessage() {
      6   this.header = {};
      7   this.frames = new Array();
      8 }
      9 
     10 NaClAMMessage.prototype.reset = function() {
     11   this.header = {};
     12   this.frames = new Array();
     13 }
     14 
     15 function NaClAM(embedId) {
     16   this.embedId = embedId;
     17   this.requestId = 0;
     18   this.message = new NaClAMMessage();
     19   this.state = 0;
     20   this.framesLeft = 0;
     21   this.listeners_ = Object.create(null);
     22   this.handleMesssage_ = this.handleMesssage_.bind(this);
     23 }
     24 
     25 NaClAM.prototype.enable = function() {
     26   window.addEventListener('message', this.handleMesssage_, true);
     27 }
     28 
     29 NaClAM.prototype.disable = function() {
     30   window.removeEventListener('message', this.handleMesssage_, true);
     31 }
     32 
     33 NaClAM.prototype.log_ = function(msg) {
     34   console.log(msg);
     35 }
     36 
     37 NaClAM.prototype.handleMesssage_ = function(event) {
     38   var STATE_WAITING_FOR_HEADER = 0;
     39   var STATE_COLLECTING_FRAMES = 1;
     40   if (this.state == STATE_WAITING_FOR_HEADER) {
     41     var header;
     42     try {
     43       header = JSON.parse(String(event.data));
     44     } catch (e) {
     45       console.log(e);
     46       console.log(event.data);
     47       return;
     48     }
     49     // Special case our log print command
     50     if (header['cmd'] == 'NaClAMPrint') {
     51       this.log_(header['print'])
     52         return;
     53     }
     54     if (typeof(header['request']) != "number") {
     55       console.log('Header message requestId is not a number.');
     56       return;
     57     }
     58     if (typeof(header['frames']) != "number") {
     59       console.log('Header message frames is not a number.');
     60       return;
     61     }
     62     this.framesLeft = header['frames'];
     63     this.state = STATE_COLLECTING_FRAMES;
     64     this.message.header = header;
     65   } else if (this.state == STATE_COLLECTING_FRAMES) {
     66     this.framesLeft--;
     67     this.message.frames.push(event.data);
     68   }
     69   if (this.state == STATE_COLLECTING_FRAMES && this.framesLeft == 0) {
     70     this.dispatchEvent(this.message);
     71     this.message.reset();
     72     this.state = STATE_WAITING_FOR_HEADER;
     73   }
     74 }
     75 
     76 NaClAM.prototype.messageHeaderIsValid_ = function(header) {
     77   if (header['cmd'] == undefined) {
     78     console.log('NaClAM: Message header does not contain cmd.');
     79     return false;
     80   }
     81   if (typeof(header['cmd']) != "string") {
     82     console.log('NaClAm: Message cmd is not a string.');
     83     return false;
     84   }
     85   if (header['frames'] == undefined) {
     86     console.log('NaClAM: Message header does not contain frames.');
     87     return false;
     88   }
     89   if (typeof(header['frames']) != "number") {
     90     console.log('NaClAm: Message frames is not a number.');
     91     return false;
     92   }
     93   if (header['request'] == undefined) {
     94     console.log('NaClAM: Message header does not contain request.');
     95     return false;
     96   }
     97   if (typeof(header['request']) != "number") {
     98     console.log('NaClAm: Message request is not a number.');
     99     return false;
    100   }
    101   return true;
    102 }
    103 
    104 NaClAM.prototype.framesIsValid_ = function(frames) {
    105   var i;
    106   if (!frames) {
    107     // No frames.
    108     return true;
    109   }
    110   if (Array.isArray(frames) == false) {
    111     console.log('NaClAM: Frames must be an array.');
    112     return false;
    113   }
    114   for (i = 0; i < frames.length; i++) {
    115     var e = frames[i];
    116     if (typeof(e) == "string") {
    117       continue;
    118     }
    119     if ((e instanceof ArrayBuffer) == false) {
    120       console.log('NaClAM: Frame is not a string or ArrayBuffer');
    121       return false;
    122     }
    123   }
    124   return true;
    125 }
    126 
    127 NaClAM.prototype.framesLength_ = function(frames) {
    128   if (!frames) {
    129     // No frames.
    130     return 0;
    131   }
    132   return frames.length;
    133 }
    134 
    135 NaClAM.prototype.sendMessage = function(cmdName, arguments, frames) {
    136   if (this.framesIsValid_(frames) == false) {
    137     console.log('NaClAM: Not sending message because frames is invalid.');
    138     return undefined;
    139   }
    140   var numFrames = this.framesLength_(frames);
    141   this.requestId++;
    142   var msgHeader = {
    143     cmd: cmdName,
    144     frames: numFrames,
    145     request: this.requestId,
    146     args: arguments
    147   };
    148   if (this.messageHeaderIsValid_(msgHeader) == false) {
    149     console.log('NaClAM: Not sending message because header is invalid.');
    150     return undefined;
    151   }
    152   var AM = document.getElementById(this.embedId);
    153   if (!AM) {
    154     console.log('NaClAM: Not sending message because Acceleration Module is not there.');
    155     return undefined;
    156   }
    157   AM.postMessage(JSON.stringify(msgHeader));
    158   var i;
    159   for (i = 0; i < numFrames; i++) {
    160     AM.postMessage(frames[i]);
    161   }
    162   return this.requestId;
    163 }
    164 
    165 /**
    166  * Adds an event listener to this Acceleration Module.
    167  * @param {string} type The name of the command.
    168  * @param handler The handler for the cmomand. This is called whenever the command is received.
    169  */
    170 NaClAM.prototype.addEventListener = function(type, handler) {
    171   if (!this.listeners_) {
    172     this.listeners_ = Object.create(null);
    173   }
    174   if (!(type in this.listeners_)) {
    175     this.listeners_[type] = [handler];
    176   } else {
    177     var handlers = this.listeners_[type];
    178     if (handlers.indexOf(handler) < 0) {
    179       handlers.push(handler);
    180     }
    181   }
    182 }
    183 
    184 /**
    185  * Removes an event listener from this Acceleration Module.
    186  * @param {string} type The name of the command.
    187  * @param handler The handler for the cmomand. This is called whenever the command is received.
    188  */
    189 NaClAM.prototype.removeEventListener = function(type, handler) {
    190   if (!this.listeners_) {
    191     // No listeners
    192     return;
    193   }
    194   if (type in this.listeners_) {
    195     var handlers = this.listeners_[type];
    196     var index = handlers.indexOf(handler);
    197     if (index >= 0) {
    198       if (handlers.length == 1) {
    199         // Listeners list would be empty, delete it
    200         delete this.listeners_[type];
    201       } else {
    202         // Remove the handler
    203         handlers.splice(index, 1);
    204       }
    205     }
    206   }
    207 }
    208 
    209 /**
    210  *
    211  */
    212 NaClAM.prototype.dispatchEvent = function(event) {
    213   if (!this.listeners_) {
    214     return true;
    215   }
    216   var type = event.header.cmd;
    217   if (type in this.listeners_) {
    218     // Make a copy to walk over
    219     var handlers = this.listeners_[type].concat();
    220     for (var i = 0, handler; handler = handlers[i]; i++) {
    221       handler.call(this, event);
    222     }
    223   }
    224 }
    225