| // Copyright (c) 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. |
| |
| function NaClAMMessage() { |
| this.header = {}; |
| this.frames = new Array(); |
| } |
| |
| NaClAMMessage.prototype.reset = function() { |
| this.header = {}; |
| this.frames = new Array(); |
| } |
| |
| function NaClAM(embedId) { |
| this.embedId = embedId; |
| this.requestId = 0; |
| this.message = new NaClAMMessage(); |
| this.state = 0; |
| this.framesLeft = 0; |
| this.listeners_ = Object.create(null); |
| this.handleMesssage_ = this.handleMesssage_.bind(this); |
| } |
| |
| NaClAM.prototype.enable = function() { |
| window.addEventListener('message', this.handleMesssage_, true); |
| } |
| |
| NaClAM.prototype.disable = function() { |
| window.removeEventListener('message', this.handleMesssage_, true); |
| } |
| |
| NaClAM.prototype.log_ = function(msg) { |
| console.log(msg); |
| } |
| |
| NaClAM.prototype.handleMesssage_ = function(event) { |
| var STATE_WAITING_FOR_HEADER = 0; |
| var STATE_COLLECTING_FRAMES = 1; |
| if (this.state == STATE_WAITING_FOR_HEADER) { |
| var header; |
| try { |
| header = JSON.parse(String(event.data)); |
| } catch (e) { |
| console.log(e); |
| console.log(event.data); |
| return; |
| } |
| // Special case our log print command |
| if (header['cmd'] == 'NaClAMPrint') { |
| this.log_(header['print']) |
| return; |
| } |
| if (typeof(header['request']) != "number") { |
| console.log('Header message requestId is not a number.'); |
| return; |
| } |
| if (typeof(header['frames']) != "number") { |
| console.log('Header message frames is not a number.'); |
| return; |
| } |
| this.framesLeft = header['frames']; |
| this.state = STATE_COLLECTING_FRAMES; |
| this.message.header = header; |
| } else if (this.state == STATE_COLLECTING_FRAMES) { |
| this.framesLeft--; |
| this.message.frames.push(event.data); |
| } |
| if (this.state == STATE_COLLECTING_FRAMES && this.framesLeft == 0) { |
| this.dispatchEvent(this.message); |
| this.message.reset(); |
| this.state = STATE_WAITING_FOR_HEADER; |
| } |
| } |
| |
| NaClAM.prototype.messageHeaderIsValid_ = function(header) { |
| if (header['cmd'] == undefined) { |
| console.log('NaClAM: Message header does not contain cmd.'); |
| return false; |
| } |
| if (typeof(header['cmd']) != "string") { |
| console.log('NaClAm: Message cmd is not a string.'); |
| return false; |
| } |
| if (header['frames'] == undefined) { |
| console.log('NaClAM: Message header does not contain frames.'); |
| return false; |
| } |
| if (typeof(header['frames']) != "number") { |
| console.log('NaClAm: Message frames is not a number.'); |
| return false; |
| } |
| if (header['request'] == undefined) { |
| console.log('NaClAM: Message header does not contain request.'); |
| return false; |
| } |
| if (typeof(header['request']) != "number") { |
| console.log('NaClAm: Message request is not a number.'); |
| return false; |
| } |
| return true; |
| } |
| |
| NaClAM.prototype.framesIsValid_ = function(frames) { |
| var i; |
| if (!frames) { |
| // No frames. |
| return true; |
| } |
| if (Array.isArray(frames) == false) { |
| console.log('NaClAM: Frames must be an array.'); |
| return false; |
| } |
| for (i = 0; i < frames.length; i++) { |
| var e = frames[i]; |
| if (typeof(e) == "string") { |
| continue; |
| } |
| if ((e instanceof ArrayBuffer) == false) { |
| console.log('NaClAM: Frame is not a string or ArrayBuffer'); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| NaClAM.prototype.framesLength_ = function(frames) { |
| if (!frames) { |
| // No frames. |
| return 0; |
| } |
| return frames.length; |
| } |
| |
| NaClAM.prototype.sendMessage = function(cmdName, arguments, frames) { |
| if (this.framesIsValid_(frames) == false) { |
| console.log('NaClAM: Not sending message because frames is invalid.'); |
| return undefined; |
| } |
| var numFrames = this.framesLength_(frames); |
| this.requestId++; |
| var msgHeader = { |
| cmd: cmdName, |
| frames: numFrames, |
| request: this.requestId, |
| args: arguments |
| }; |
| if (this.messageHeaderIsValid_(msgHeader) == false) { |
| console.log('NaClAM: Not sending message because header is invalid.'); |
| return undefined; |
| } |
| var AM = document.getElementById(this.embedId); |
| if (!AM) { |
| console.log('NaClAM: Not sending message because Acceleration Module is not there.'); |
| return undefined; |
| } |
| AM.postMessage(JSON.stringify(msgHeader)); |
| var i; |
| for (i = 0; i < numFrames; i++) { |
| AM.postMessage(frames[i]); |
| } |
| return this.requestId; |
| } |
| |
| /** |
| * Adds an event listener to this Acceleration Module. |
| * @param {string} type The name of the command. |
| * @param handler The handler for the cmomand. This is called whenever the command is received. |
| */ |
| NaClAM.prototype.addEventListener = function(type, handler) { |
| if (!this.listeners_) { |
| this.listeners_ = Object.create(null); |
| } |
| if (!(type in this.listeners_)) { |
| this.listeners_[type] = [handler]; |
| } else { |
| var handlers = this.listeners_[type]; |
| if (handlers.indexOf(handler) < 0) { |
| handlers.push(handler); |
| } |
| } |
| } |
| |
| /** |
| * Removes an event listener from this Acceleration Module. |
| * @param {string} type The name of the command. |
| * @param handler The handler for the cmomand. This is called whenever the command is received. |
| */ |
| NaClAM.prototype.removeEventListener = function(type, handler) { |
| if (!this.listeners_) { |
| // No listeners |
| return; |
| } |
| if (type in this.listeners_) { |
| var handlers = this.listeners_[type]; |
| var index = handlers.indexOf(handler); |
| if (index >= 0) { |
| if (handlers.length == 1) { |
| // Listeners list would be empty, delete it |
| delete this.listeners_[type]; |
| } else { |
| // Remove the handler |
| handlers.splice(index, 1); |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| NaClAM.prototype.dispatchEvent = function(event) { |
| if (!this.listeners_) { |
| return true; |
| } |
| var type = event.header.cmd; |
| if (type in this.listeners_) { |
| // Make a copy to walk over |
| var handlers = this.listeners_[type].concat(); |
| for (var i = 0, handler; handler = handlers[i]; i++) { |
| handler.call(this, event); |
| } |
| } |
| } |