1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/extensions/shumway/content/web/avm-sandbox.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,322 @@ 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 +// Extension communication object 1.23 +var FirefoxCom = (function FirefoxComClosure() { 1.24 + return { 1.25 + /** 1.26 + * Creates an event that the extension is listening for and will 1.27 + * synchronously respond to. 1.28 + * NOTE: It is reccomended to use request() instead since one day we may not 1.29 + * be able to synchronously reply. 1.30 + * @param {String} action The action to trigger. 1.31 + * @param {String} data Optional data to send. 1.32 + * @return {*} The response. 1.33 + */ 1.34 + requestSync: function(action, data) { 1.35 + var e = document.createEvent('CustomEvent'); 1.36 + e.initCustomEvent('shumway.message', true, false, 1.37 + {action: action, data: data, sync: true}); 1.38 + document.dispatchEvent(e); 1.39 + return e.detail.response; 1.40 + }, 1.41 + /** 1.42 + * Creates an event that the extension is listening for and will 1.43 + * asynchronously respond by calling the callback. 1.44 + * @param {String} action The action to trigger. 1.45 + * @param {String} data Optional data to send. 1.46 + * @param {Function} callback Optional response callback that will be called 1.47 + * with one data argument. 1.48 + */ 1.49 + request: function(action, data, callback) { 1.50 + var e = document.createEvent('CustomEvent'); 1.51 + e.initCustomEvent('shumway.message', true, false, 1.52 + {action: action, data: data, sync: false}); 1.53 + if (callback) { 1.54 + if ('nextId' in FirefoxCom.request) { 1.55 + FirefoxCom.request.nextId = 1; 1.56 + } 1.57 + var cookie = "requestId" + (FirefoxCom.request.nextId++); 1.58 + e.detail.cookie = cookie; 1.59 + e.detail.callback = true; 1.60 + 1.61 + document.addEventListener('shumway.response', function listener(event) { 1.62 + if (cookie !== event.detail.cookie) 1.63 + return; 1.64 + 1.65 + document.removeEventListener('shumway.response', listener, false); 1.66 + 1.67 + var response = event.detail.response; 1.68 + return callback(response); 1.69 + }, false); 1.70 + } 1.71 + return document.dispatchEvent(e); 1.72 + }, 1.73 + initJS: function (callback) { 1.74 + FirefoxCom.request('externalCom', {action: 'init'}); 1.75 + document.addEventListener('shumway.remote', function (e) { 1.76 + e.detail.result = callback(e.detail.functionName, e.detail.args); 1.77 + }, false); 1.78 + } 1.79 + }; 1.80 +})(); 1.81 + 1.82 +function fallback() { 1.83 + FirefoxCom.requestSync('fallback', null) 1.84 +} 1.85 + 1.86 +var playerglobalInfo = { 1.87 + abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs", 1.88 + catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json" 1.89 +}; 1.90 + 1.91 +function runViewer() { 1.92 + var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null)); 1.93 + FileLoadingService.setBaseUrl(flashParams.baseUrl); 1.94 + 1.95 + movieUrl = flashParams.url; 1.96 + if (!movieUrl) { 1.97 + console.log("no movie url provided -- stopping here"); 1.98 + FirefoxCom.request('endActivation', null); 1.99 + return; 1.100 + } 1.101 + 1.102 + movieParams = flashParams.movieParams; 1.103 + objectParams = flashParams.objectParams; 1.104 + var isOverlay = flashParams.isOverlay; 1.105 + pauseExecution = flashParams.isPausedAtStart; 1.106 + 1.107 + console.log("url=" + movieUrl + ";params=" + uneval(movieParams)); 1.108 + if (movieParams.fmt_list && movieParams.url_encoded_fmt_stream_map) { 1.109 + // HACK removing FLVs from the fmt_list 1.110 + movieParams.fmt_list = movieParams.fmt_list.split(',').filter(function (s) { 1.111 + var fid = s.split('/')[0]; 1.112 + return fid !== '5' && fid !== '34' && fid !== '35'; // more? 1.113 + }).join(','); 1.114 + } 1.115 + 1.116 + parseSwf(movieUrl, movieParams, objectParams); 1.117 + 1.118 + if (isOverlay) { 1.119 + document.getElementById('overlay').className = 'enabled'; 1.120 + var fallbackDiv = document.getElementById('fallback'); 1.121 + fallbackDiv.addEventListener('click', function(e) { 1.122 + fallback(); 1.123 + e.preventDefault(); 1.124 + }); 1.125 + var reportDiv = document.getElementById('report'); 1.126 + reportDiv.addEventListener('click', function(e) { 1.127 + reportIssue(); 1.128 + e.preventDefault(); 1.129 + }); 1.130 + var fallbackMenu = document.getElementById('fallbackMenu'); 1.131 + fallbackMenu.removeAttribute('hidden'); 1.132 + fallbackMenu.addEventListener('click', fallback); 1.133 + } 1.134 + var showURLMenu = document.getElementById('showURLMenu'); 1.135 + showURLMenu.addEventListener('click', showURL); 1.136 + var inspectorMenu = document.getElementById('inspectorMenu'); 1.137 + inspectorMenu.addEventListener('click', showInInspector); 1.138 + var reportMenu = document.getElementById('reportMenu'); 1.139 + reportMenu.addEventListener('click', reportIssue); 1.140 + 1.141 + document.getElementById('copyProfileMenu').addEventListener('click', copyProfile); 1.142 +} 1.143 + 1.144 +function showURL() { 1.145 + window.prompt("Copy to clipboard", movieUrl); 1.146 +} 1.147 + 1.148 +function showInInspector() { 1.149 + var base = "http://www.areweflashyet.com/shumway/examples/inspector/inspector.html?rfile="; 1.150 + var params = ''; 1.151 + for (var k in movieParams) { 1.152 + params += '&' + k + '=' + encodeURIComponent(movieParams[k]); 1.153 + } 1.154 + window.open(base + encodeURIComponent(movieUrl) + params); 1.155 +} 1.156 + 1.157 +function reportIssue() { 1.158 + var duplicatesMap = Object.create(null); 1.159 + var prunedExceptions = []; 1.160 + avm2.exceptions.forEach(function(e) { 1.161 + var ident = e.source + e.message + e.stack; 1.162 + var entry = duplicatesMap[ident]; 1.163 + if (!entry) { 1.164 + entry = duplicatesMap[ident] = { 1.165 + source: e.source, 1.166 + message: e.message, 1.167 + stack: e.stack, 1.168 + count: 0 1.169 + }; 1.170 + prunedExceptions.push(entry); 1.171 + } 1.172 + entry.count++; 1.173 + }); 1.174 + FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions)); 1.175 +} 1.176 + 1.177 +function copyProfile() { 1.178 + function toArray(v) { 1.179 + var array = []; 1.180 + for (var i = 0; i < v.length; i++) { 1.181 + array.push(v[i]); 1.182 + } 1.183 + return array; 1.184 + } 1.185 + var profile = { 1.186 + loops: {counts: toArray($L), lines: $LL}, 1.187 + functions: {counts: toArray($F), lines: $FL}, 1.188 + allocations: {counts: toArray($A), lines: $AL} 1.189 + }; 1.190 + FirefoxCom.request('unsafeSetClipboard', JSON.stringify(profile)); 1.191 +} 1.192 + 1.193 +var movieUrl, movieParams, objectParams; 1.194 + 1.195 +window.addEventListener("message", function handlerMessage(e) { 1.196 + var args = e.data; 1.197 + switch (args.callback) { 1.198 + case "loadFile": 1.199 + var session = FileLoadingService.sessions[args.sessionId]; 1.200 + if (session) { 1.201 + session.notify(args); 1.202 + } 1.203 + break; 1.204 + } 1.205 +}, true); 1.206 + 1.207 +var TelemetryService = { 1.208 + reportTelemetry: function (data) { 1.209 + FirefoxCom.request('reportTelemetry', data, null); 1.210 + } 1.211 +}; 1.212 + 1.213 +var FileLoadingService = { 1.214 + get baseUrl() { return movieUrl; }, 1.215 + nextSessionId: 1, // 0 - is reserved 1.216 + sessions: [], 1.217 + createSession: function () { 1.218 + var sessionId = this.nextSessionId++; 1.219 + return this.sessions[sessionId] = { 1.220 + open: function (request) { 1.221 + var self = this; 1.222 + var path = FileLoadingService.resolveUrl(request.url); 1.223 + console.log('Session #' + sessionId +': loading ' + path); 1.224 + FirefoxCom.requestSync('loadFile', {url: path, method: request.method, 1.225 + mimeType: request.mimeType, postData: request.data, 1.226 + checkPolicyFile: request.checkPolicyFile, sessionId: sessionId}); 1.227 + }, 1.228 + notify: function (args) { 1.229 + switch (args.topic) { 1.230 + case "open": this.onopen(); break; 1.231 + case "close": 1.232 + this.onclose(); 1.233 + FileLoadingService.sessions[sessionId] = null; 1.234 + console.log('Session #' + sessionId +': closed'); 1.235 + break; 1.236 + case "error": 1.237 + this.onerror && this.onerror(args.error); 1.238 + break; 1.239 + case "progress": 1.240 + console.log('Session #' + sessionId + ': loaded ' + args.loaded + '/' + args.total); 1.241 + this.onprogress && this.onprogress(args.array, {bytesLoaded: args.loaded, bytesTotal: args.total}); 1.242 + break; 1.243 + } 1.244 + } 1.245 + }; 1.246 + }, 1.247 + setBaseUrl: function (url) { 1.248 + var a = document.createElement('a'); 1.249 + a.href = url || '#'; 1.250 + a.setAttribute('style', 'display: none;'); 1.251 + document.body.appendChild(a); 1.252 + FileLoadingService.baseUrl = a.href; 1.253 + document.body.removeChild(a); 1.254 + }, 1.255 + resolveUrl: function (url) { 1.256 + if (url.indexOf('://') >= 0) return url; 1.257 + 1.258 + var base = FileLoadingService.baseUrl; 1.259 + base = base.lastIndexOf('/') >= 0 ? base.substring(0, base.lastIndexOf('/') + 1) : ''; 1.260 + if (url.indexOf('/') === 0) { 1.261 + var m = /^[^:]+:\/\/[^\/]+/.exec(base); 1.262 + if (m) base = m[0]; 1.263 + } 1.264 + return base + url; 1.265 + } 1.266 +}; 1.267 + 1.268 +function parseSwf(url, movieParams, objectParams) { 1.269 + var enableVerifier = Shumway.AVM2.Runtime.enableVerifier; 1.270 + var EXECUTION_MODE = Shumway.AVM2.Runtime.EXECUTION_MODE; 1.271 + 1.272 + var compilerSettings = JSON.parse( 1.273 + FirefoxCom.requestSync('getCompilerSettings', null)); 1.274 + enableVerifier.value = compilerSettings.verifier; 1.275 + 1.276 + // init misc preferences 1.277 + turboMode.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false}); 1.278 + hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false}); 1.279 + forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false}); 1.280 + dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false}); 1.281 + 1.282 + console.log("Compiler settings: " + JSON.stringify(compilerSettings)); 1.283 + console.log("Parsing " + url + "..."); 1.284 + function loaded() { 1.285 + FirefoxCom.request('endActivation', null); 1.286 + } 1.287 + 1.288 + createAVM2(builtinPath, playerglobalInfo, avm1Path, 1.289 + compilerSettings.sysCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET, 1.290 + compilerSettings.appCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET, 1.291 + function (avm2) { 1.292 + console.time("Initialize Renderer"); 1.293 + SWF.embed(url, document, document.getElementById("viewer"), { 1.294 + url: url, 1.295 + movieParams: movieParams, 1.296 + objectParams: objectParams, 1.297 + onComplete: loaded, 1.298 + onBeforeFrame: frame 1.299 + }); 1.300 + }); 1.301 +} 1.302 + 1.303 +var pauseExecution = false; 1.304 +var initializeFrameControl = true; 1.305 +function frame(e) { 1.306 + if (initializeFrameControl) { 1.307 + // marking that movie is started 1.308 + document.body.classList.add("started"); 1.309 + 1.310 + TelemetryService.reportTelemetry({topic: "firstFrame"}); 1.311 + 1.312 + // skipping frame 0 1.313 + initializeFrameControl = false; 1.314 + return; 1.315 + } 1.316 + if (pauseExecution) { 1.317 + e.cancel = true; 1.318 + } 1.319 +} 1.320 + 1.321 +document.addEventListener('keydown', function (e) { 1.322 + if (e.keyCode == 119 && e.ctrlKey) { // Ctrl+F8 1.323 + pauseExecution = !pauseExecution; 1.324 + } 1.325 +}, false);