1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/extensions/shumway/content/web/preview.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,219 @@ 1.4 +/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ 1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ 1.6 +/* 1.7 + * Copyright 2013 Mozilla Foundation 1.8 + * 1.9 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.10 + * you may not use this file except in compliance with the License. 1.11 + * You may obtain a copy of the License at 1.12 + * 1.13 + * http://www.apache.org/licenses/LICENSE-2.0 1.14 + * 1.15 + * Unless required by applicable law or agreed to in writing, software 1.16 + * distributed under the License is distributed on an "AS IS" BASIS, 1.17 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.18 + * See the License for the specific language governing permissions and 1.19 + * limitations under the License. 1.20 + */ 1.21 + 1.22 + 1.23 +// Extenstion communication object 1.24 +var FirefoxCom = (function FirefoxComClosure() { 1.25 + return { 1.26 + /** 1.27 + * Creates an event that the extension is listening for and will 1.28 + * synchronously respond to. 1.29 + * NOTE: It is reccomended to use request() instead since one day we may not 1.30 + * be able to synchronously reply. 1.31 + * @param {String} action The action to trigger. 1.32 + * @param {String} data Optional data to send. 1.33 + * @return {*} The response. 1.34 + */ 1.35 + requestSync: function(action, data) { 1.36 + var request = document.createTextNode(''); 1.37 + document.documentElement.appendChild(request); 1.38 + 1.39 + var sender = document.createEvent('CustomEvent'); 1.40 + sender.initCustomEvent('shumway.message', true, false, 1.41 + {action: action, data: data, sync: true}); 1.42 + request.dispatchEvent(sender); 1.43 + var response = sender.detail.response; 1.44 + document.documentElement.removeChild(request); 1.45 + return response; 1.46 + }, 1.47 + /** 1.48 + * Creates an event that the extension is listening for and will 1.49 + * asynchronously respond by calling the callback. 1.50 + * @param {String} action The action to trigger. 1.51 + * @param {String} data Optional data to send. 1.52 + * @param {Function} callback Optional response callback that will be called 1.53 + * with one data argument. 1.54 + */ 1.55 + request: function(action, data, callback) { 1.56 + var request = document.createTextNode(''); 1.57 + request.setUserData('action', action, null); 1.58 + request.setUserData('data', data, null); 1.59 + request.setUserData('sync', false, null); 1.60 + if (callback) { 1.61 + request.setUserData('callback', callback, null); 1.62 + 1.63 + document.addEventListener('shumway.response', function listener(event) { 1.64 + var node = event.target, 1.65 + response = event.detail.response; 1.66 + 1.67 + document.documentElement.removeChild(node); 1.68 + 1.69 + document.removeEventListener('shumway.response', listener, false); 1.70 + return callback(response); 1.71 + }, false); 1.72 + } 1.73 + document.documentElement.appendChild(request); 1.74 + 1.75 + var sender = document.createEvent('CustomEvent'); 1.76 + sender.initCustomEvent('shumway.message', true, false, 1.77 + {action: action, data: data, sync: false}); 1.78 + return request.dispatchEvent(sender); 1.79 + } 1.80 + }; 1.81 +})(); 1.82 + 1.83 +function fallback() { 1.84 + FirefoxCom.requestSync('fallback', null) 1.85 +} 1.86 + 1.87 +var BYTES_TO_LOAD = 32768; 1.88 +var BYTES_TO_PARSE = 32768; 1.89 + 1.90 +function runSniffer() { 1.91 + var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null)); 1.92 + document.head.getElementsByTagName('base')[0].href = flashParams.baseUrl; 1.93 + movieUrl = flashParams.url; 1.94 + document.getElementById('playbutton').addEventListener('click', function() { 1.95 + switchToFullMode(); 1.96 + }); 1.97 + document.getElementById('fullmode').addEventListener('click', function() { 1.98 + switchToFullMode(); 1.99 + return false; 1.100 + }); 1.101 + document.getElementById('fallback').addEventListener('click', function() { 1.102 + fallback(); 1.103 + return false; 1.104 + }); 1.105 + FirefoxCom.requestSync('loadFile', {url: movieUrl, sessionId: 0, limit: BYTES_TO_LOAD}); 1.106 +} 1.107 + 1.108 +var subscription, movieUrl, buffers = [];; 1.109 + 1.110 +addEventListener("message", function handlerMessage(e) { 1.111 + var args = e.data; 1.112 + switch (args.callback) { 1.113 + case "loadFile": 1.114 + if (args.sessionId != 0) { 1.115 + return; 1.116 + } 1.117 + switch (args.topic) { 1.118 + case "progress": 1.119 + buffers.push(args.array); 1.120 + break; 1.121 + case "error": 1.122 + console.error('Unable to download ' + movieUrl + ': ' + args.error); 1.123 + break; 1.124 + case "close": 1.125 + parseSwf(); 1.126 + break; 1.127 + } 1.128 + break; 1.129 + } 1.130 +}, true); 1.131 + 1.132 +function inflateData(bytes, outputLength) { 1.133 + verifyDeflateHeader(bytes); 1.134 + var stream = new Stream(bytes, 2); 1.135 + var output = { 1.136 + data: new Uint8Array(outputLength), 1.137 + available: 0, 1.138 + completed: false 1.139 + }; 1.140 + var state = {}; 1.141 + // inflate while we can 1.142 + try { 1.143 + do { 1.144 + inflateBlock(stream, output, state); 1.145 + } while (!output.completed && stream.pos < stream.end 1.146 + && output.available < outputLength); 1.147 + } catch (e) { 1.148 + console.log('inflate aborted: ' + e); 1.149 + } 1.150 + return new Stream(output.data, 0, Math.min(output.available, outputLength)); 1.151 +} 1.152 + 1.153 +function parseSwf() { 1.154 + var sum = 0; 1.155 + for (var i = 0; i < buffers.length; i++) 1.156 + sum += buffers[i].length; 1.157 + var data = new Uint8Array(sum), j = 0; 1.158 + for (var i = 0; i < buffers.length; i++) { 1.159 + data.set(buffers[i], j); j += buffers[i].length; 1.160 + } 1.161 + 1.162 + var backgroundColor; 1.163 + try { 1.164 + var magic1 = data[0]; 1.165 + var magic2 = data[1]; 1.166 + var magic3 = data[2]; 1.167 + if ((magic1 !== 70 && magic1 !== 67) || magic2 !== 87 || magic3 !== 83) 1.168 + throw new Error('unsupported file format'); 1.169 + 1.170 + var compressed = magic1 === 67; 1.171 + var stream = compressed ? inflateData(data.subarray(8), BYTES_TO_PARSE) : 1.172 + new Stream(data, 8, data.length - 8); 1.173 + var bytes = stream.bytes; 1.174 + 1.175 + var SWF_TAG_CODE_SET_BACKGROUND_COLOR = 9; 1.176 + var PREFETCH_SIZE = 17 /* RECT */ + 1.177 + 4 /* Frames rate and count */;; 1.178 + stream.ensure(PREFETCH_SIZE); 1.179 + var rectFieldSize = bytes[stream.pos] >> 3; 1.180 + stream.pos += ((5 + 4 * rectFieldSize + 7) >> 3) + 4; // skipping other header fields 1.181 + 1.182 + // for now just sniffing background color 1.183 + while (stream.pos < stream.end && 1.184 + !backgroundColor) { 1.185 + stream.ensure(2); 1.186 + var tagCodeAndLength = stream.getUint16(stream.pos, true); 1.187 + stream.pos += 2; 1.188 + var tagCode = tagCodeAndLength >> 6; 1.189 + var length = tagCodeAndLength & 0x3F; 1.190 + if (length == 0x3F) { 1.191 + stream.ensure(4); 1.192 + length = stream.getInt32(stream.pos, true); 1.193 + stream.pos += 4; 1.194 + if (length < 0) throw new Error('invalid length'); 1.195 + } 1.196 + stream.ensure(length); 1.197 + switch (tagCode) { 1.198 + case SWF_TAG_CODE_SET_BACKGROUND_COLOR: 1.199 + backgroundColor = 'rgb(' + bytes[stream.pos] + ', ' + 1.200 + bytes[stream.pos + 1] + ', ' + 1.201 + bytes[stream.pos + 2] + ')'; 1.202 + break; 1.203 + } 1.204 + stream.pos += length; 1.205 + } 1.206 + } catch (e) { 1.207 + console.log('parsing aborted: ' + e); 1.208 + } 1.209 + if (backgroundColor) { 1.210 + document.body.style.backgroundColor = backgroundColor; 1.211 + } 1.212 +} 1.213 + 1.214 +document.addEventListener('keydown', function (e) { 1.215 + if (e.keyCode == 119 && e.ctrlKey) { // Ctrl+F8 1.216 + window.location.replace("data:application/x-moz-playpreview;,application/x-shockwave-flash,full,paused=true"); 1.217 + } 1.218 +}, false); 1.219 + 1.220 +function switchToFullMode() { 1.221 + window.location.replace("data:application/x-moz-playpreview;,application/x-shockwave-flash,full"); 1.222 +}