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 5function NaClAMMessage() { 6 this.header = {}; 7 this.frames = new Array(); 8} 9 10NaClAMMessage.prototype.reset = function() { 11 this.header = {}; 12 this.frames = new Array(); 13} 14 15function 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 25NaClAM.prototype.enable = function() { 26 window.addEventListener('message', this.handleMesssage_, true); 27} 28 29NaClAM.prototype.disable = function() { 30 window.removeEventListener('message', this.handleMesssage_, true); 31} 32 33NaClAM.prototype.log_ = function(msg) { 34 console.log(msg); 35} 36 37NaClAM.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 76NaClAM.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 104NaClAM.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 127NaClAM.prototype.framesLength_ = function(frames) { 128 if (!frames) { 129 // No frames. 130 return 0; 131 } 132 return frames.length; 133} 134 135NaClAM.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 */ 170NaClAM.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 */ 189NaClAM.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 */ 212NaClAM.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