|
1 /* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */ |
|
2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
|
3 /* |
|
4 * Copyright 2013 Mozilla Foundation |
|
5 * |
|
6 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
7 * you may not use this file except in compliance with the License. |
|
8 * You may obtain a copy of the License at |
|
9 * |
|
10 * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 * |
|
12 * Unless required by applicable law or agreed to in writing, software |
|
13 * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 * See the License for the specific language governing permissions and |
|
16 * limitations under the License. |
|
17 */ |
|
18 |
|
19 // Extension communication object |
|
20 var FirefoxCom = (function FirefoxComClosure() { |
|
21 return { |
|
22 /** |
|
23 * Creates an event that the extension is listening for and will |
|
24 * synchronously respond to. |
|
25 * NOTE: It is reccomended to use request() instead since one day we may not |
|
26 * be able to synchronously reply. |
|
27 * @param {String} action The action to trigger. |
|
28 * @param {String} data Optional data to send. |
|
29 * @return {*} The response. |
|
30 */ |
|
31 requestSync: function(action, data) { |
|
32 var e = document.createEvent('CustomEvent'); |
|
33 e.initCustomEvent('shumway.message', true, false, |
|
34 {action: action, data: data, sync: true}); |
|
35 document.dispatchEvent(e); |
|
36 return e.detail.response; |
|
37 }, |
|
38 /** |
|
39 * Creates an event that the extension is listening for and will |
|
40 * asynchronously respond by calling the callback. |
|
41 * @param {String} action The action to trigger. |
|
42 * @param {String} data Optional data to send. |
|
43 * @param {Function} callback Optional response callback that will be called |
|
44 * with one data argument. |
|
45 */ |
|
46 request: function(action, data, callback) { |
|
47 var e = document.createEvent('CustomEvent'); |
|
48 e.initCustomEvent('shumway.message', true, false, |
|
49 {action: action, data: data, sync: false}); |
|
50 if (callback) { |
|
51 if ('nextId' in FirefoxCom.request) { |
|
52 FirefoxCom.request.nextId = 1; |
|
53 } |
|
54 var cookie = "requestId" + (FirefoxCom.request.nextId++); |
|
55 e.detail.cookie = cookie; |
|
56 e.detail.callback = true; |
|
57 |
|
58 document.addEventListener('shumway.response', function listener(event) { |
|
59 if (cookie !== event.detail.cookie) |
|
60 return; |
|
61 |
|
62 document.removeEventListener('shumway.response', listener, false); |
|
63 |
|
64 var response = event.detail.response; |
|
65 return callback(response); |
|
66 }, false); |
|
67 } |
|
68 return document.dispatchEvent(e); |
|
69 }, |
|
70 initJS: function (callback) { |
|
71 FirefoxCom.request('externalCom', {action: 'init'}); |
|
72 document.addEventListener('shumway.remote', function (e) { |
|
73 e.detail.result = callback(e.detail.functionName, e.detail.args); |
|
74 }, false); |
|
75 } |
|
76 }; |
|
77 })(); |
|
78 |
|
79 function fallback() { |
|
80 FirefoxCom.requestSync('fallback', null) |
|
81 } |
|
82 |
|
83 var playerglobalInfo = { |
|
84 abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs", |
|
85 catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json" |
|
86 }; |
|
87 |
|
88 function runViewer() { |
|
89 var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null)); |
|
90 FileLoadingService.setBaseUrl(flashParams.baseUrl); |
|
91 |
|
92 movieUrl = flashParams.url; |
|
93 if (!movieUrl) { |
|
94 console.log("no movie url provided -- stopping here"); |
|
95 FirefoxCom.request('endActivation', null); |
|
96 return; |
|
97 } |
|
98 |
|
99 movieParams = flashParams.movieParams; |
|
100 objectParams = flashParams.objectParams; |
|
101 var isOverlay = flashParams.isOverlay; |
|
102 pauseExecution = flashParams.isPausedAtStart; |
|
103 |
|
104 console.log("url=" + movieUrl + ";params=" + uneval(movieParams)); |
|
105 if (movieParams.fmt_list && movieParams.url_encoded_fmt_stream_map) { |
|
106 // HACK removing FLVs from the fmt_list |
|
107 movieParams.fmt_list = movieParams.fmt_list.split(',').filter(function (s) { |
|
108 var fid = s.split('/')[0]; |
|
109 return fid !== '5' && fid !== '34' && fid !== '35'; // more? |
|
110 }).join(','); |
|
111 } |
|
112 |
|
113 parseSwf(movieUrl, movieParams, objectParams); |
|
114 |
|
115 if (isOverlay) { |
|
116 document.getElementById('overlay').className = 'enabled'; |
|
117 var fallbackDiv = document.getElementById('fallback'); |
|
118 fallbackDiv.addEventListener('click', function(e) { |
|
119 fallback(); |
|
120 e.preventDefault(); |
|
121 }); |
|
122 var reportDiv = document.getElementById('report'); |
|
123 reportDiv.addEventListener('click', function(e) { |
|
124 reportIssue(); |
|
125 e.preventDefault(); |
|
126 }); |
|
127 var fallbackMenu = document.getElementById('fallbackMenu'); |
|
128 fallbackMenu.removeAttribute('hidden'); |
|
129 fallbackMenu.addEventListener('click', fallback); |
|
130 } |
|
131 var showURLMenu = document.getElementById('showURLMenu'); |
|
132 showURLMenu.addEventListener('click', showURL); |
|
133 var inspectorMenu = document.getElementById('inspectorMenu'); |
|
134 inspectorMenu.addEventListener('click', showInInspector); |
|
135 var reportMenu = document.getElementById('reportMenu'); |
|
136 reportMenu.addEventListener('click', reportIssue); |
|
137 |
|
138 document.getElementById('copyProfileMenu').addEventListener('click', copyProfile); |
|
139 } |
|
140 |
|
141 function showURL() { |
|
142 window.prompt("Copy to clipboard", movieUrl); |
|
143 } |
|
144 |
|
145 function showInInspector() { |
|
146 var base = "http://www.areweflashyet.com/shumway/examples/inspector/inspector.html?rfile="; |
|
147 var params = ''; |
|
148 for (var k in movieParams) { |
|
149 params += '&' + k + '=' + encodeURIComponent(movieParams[k]); |
|
150 } |
|
151 window.open(base + encodeURIComponent(movieUrl) + params); |
|
152 } |
|
153 |
|
154 function reportIssue() { |
|
155 var duplicatesMap = Object.create(null); |
|
156 var prunedExceptions = []; |
|
157 avm2.exceptions.forEach(function(e) { |
|
158 var ident = e.source + e.message + e.stack; |
|
159 var entry = duplicatesMap[ident]; |
|
160 if (!entry) { |
|
161 entry = duplicatesMap[ident] = { |
|
162 source: e.source, |
|
163 message: e.message, |
|
164 stack: e.stack, |
|
165 count: 0 |
|
166 }; |
|
167 prunedExceptions.push(entry); |
|
168 } |
|
169 entry.count++; |
|
170 }); |
|
171 FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions)); |
|
172 } |
|
173 |
|
174 function copyProfile() { |
|
175 function toArray(v) { |
|
176 var array = []; |
|
177 for (var i = 0; i < v.length; i++) { |
|
178 array.push(v[i]); |
|
179 } |
|
180 return array; |
|
181 } |
|
182 var profile = { |
|
183 loops: {counts: toArray($L), lines: $LL}, |
|
184 functions: {counts: toArray($F), lines: $FL}, |
|
185 allocations: {counts: toArray($A), lines: $AL} |
|
186 }; |
|
187 FirefoxCom.request('unsafeSetClipboard', JSON.stringify(profile)); |
|
188 } |
|
189 |
|
190 var movieUrl, movieParams, objectParams; |
|
191 |
|
192 window.addEventListener("message", function handlerMessage(e) { |
|
193 var args = e.data; |
|
194 switch (args.callback) { |
|
195 case "loadFile": |
|
196 var session = FileLoadingService.sessions[args.sessionId]; |
|
197 if (session) { |
|
198 session.notify(args); |
|
199 } |
|
200 break; |
|
201 } |
|
202 }, true); |
|
203 |
|
204 var TelemetryService = { |
|
205 reportTelemetry: function (data) { |
|
206 FirefoxCom.request('reportTelemetry', data, null); |
|
207 } |
|
208 }; |
|
209 |
|
210 var FileLoadingService = { |
|
211 get baseUrl() { return movieUrl; }, |
|
212 nextSessionId: 1, // 0 - is reserved |
|
213 sessions: [], |
|
214 createSession: function () { |
|
215 var sessionId = this.nextSessionId++; |
|
216 return this.sessions[sessionId] = { |
|
217 open: function (request) { |
|
218 var self = this; |
|
219 var path = FileLoadingService.resolveUrl(request.url); |
|
220 console.log('Session #' + sessionId +': loading ' + path); |
|
221 FirefoxCom.requestSync('loadFile', {url: path, method: request.method, |
|
222 mimeType: request.mimeType, postData: request.data, |
|
223 checkPolicyFile: request.checkPolicyFile, sessionId: sessionId}); |
|
224 }, |
|
225 notify: function (args) { |
|
226 switch (args.topic) { |
|
227 case "open": this.onopen(); break; |
|
228 case "close": |
|
229 this.onclose(); |
|
230 FileLoadingService.sessions[sessionId] = null; |
|
231 console.log('Session #' + sessionId +': closed'); |
|
232 break; |
|
233 case "error": |
|
234 this.onerror && this.onerror(args.error); |
|
235 break; |
|
236 case "progress": |
|
237 console.log('Session #' + sessionId + ': loaded ' + args.loaded + '/' + args.total); |
|
238 this.onprogress && this.onprogress(args.array, {bytesLoaded: args.loaded, bytesTotal: args.total}); |
|
239 break; |
|
240 } |
|
241 } |
|
242 }; |
|
243 }, |
|
244 setBaseUrl: function (url) { |
|
245 var a = document.createElement('a'); |
|
246 a.href = url || '#'; |
|
247 a.setAttribute('style', 'display: none;'); |
|
248 document.body.appendChild(a); |
|
249 FileLoadingService.baseUrl = a.href; |
|
250 document.body.removeChild(a); |
|
251 }, |
|
252 resolveUrl: function (url) { |
|
253 if (url.indexOf('://') >= 0) return url; |
|
254 |
|
255 var base = FileLoadingService.baseUrl; |
|
256 base = base.lastIndexOf('/') >= 0 ? base.substring(0, base.lastIndexOf('/') + 1) : ''; |
|
257 if (url.indexOf('/') === 0) { |
|
258 var m = /^[^:]+:\/\/[^\/]+/.exec(base); |
|
259 if (m) base = m[0]; |
|
260 } |
|
261 return base + url; |
|
262 } |
|
263 }; |
|
264 |
|
265 function parseSwf(url, movieParams, objectParams) { |
|
266 var enableVerifier = Shumway.AVM2.Runtime.enableVerifier; |
|
267 var EXECUTION_MODE = Shumway.AVM2.Runtime.EXECUTION_MODE; |
|
268 |
|
269 var compilerSettings = JSON.parse( |
|
270 FirefoxCom.requestSync('getCompilerSettings', null)); |
|
271 enableVerifier.value = compilerSettings.verifier; |
|
272 |
|
273 // init misc preferences |
|
274 turboMode.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false}); |
|
275 hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false}); |
|
276 forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false}); |
|
277 dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false}); |
|
278 |
|
279 console.log("Compiler settings: " + JSON.stringify(compilerSettings)); |
|
280 console.log("Parsing " + url + "..."); |
|
281 function loaded() { |
|
282 FirefoxCom.request('endActivation', null); |
|
283 } |
|
284 |
|
285 createAVM2(builtinPath, playerglobalInfo, avm1Path, |
|
286 compilerSettings.sysCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET, |
|
287 compilerSettings.appCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET, |
|
288 function (avm2) { |
|
289 console.time("Initialize Renderer"); |
|
290 SWF.embed(url, document, document.getElementById("viewer"), { |
|
291 url: url, |
|
292 movieParams: movieParams, |
|
293 objectParams: objectParams, |
|
294 onComplete: loaded, |
|
295 onBeforeFrame: frame |
|
296 }); |
|
297 }); |
|
298 } |
|
299 |
|
300 var pauseExecution = false; |
|
301 var initializeFrameControl = true; |
|
302 function frame(e) { |
|
303 if (initializeFrameControl) { |
|
304 // marking that movie is started |
|
305 document.body.classList.add("started"); |
|
306 |
|
307 TelemetryService.reportTelemetry({topic: "firstFrame"}); |
|
308 |
|
309 // skipping frame 0 |
|
310 initializeFrameControl = false; |
|
311 return; |
|
312 } |
|
313 if (pauseExecution) { |
|
314 e.cancel = true; |
|
315 } |
|
316 } |
|
317 |
|
318 document.addEventListener('keydown', function (e) { |
|
319 if (e.keyCode == 119 && e.ctrlKey) { // Ctrl+F8 |
|
320 pauseExecution = !pauseExecution; |
|
321 } |
|
322 }, false); |