mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
Bug 1140508 - "Update Shumway to version 0.10.225". r=till
--HG-- extra : rebase_source : 414e84de6b1a353556419d732c6a19a103e14f4f
This commit is contained in:
parent
0421d094b3
commit
6859d8d2da
@ -33,6 +33,12 @@ var RtmpUtils = {
|
||||
},
|
||||
|
||||
createSocket: function (sandbox, params) {
|
||||
function genPropDesc(value) {
|
||||
return {
|
||||
enumerable: true, configurable: true, writable: true, value: value
|
||||
};
|
||||
}
|
||||
|
||||
var host = params.host, port = params.port, ssl = params.ssl;
|
||||
|
||||
var baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
|
||||
@ -41,129 +47,90 @@ var RtmpUtils = {
|
||||
return null;
|
||||
}
|
||||
|
||||
var wrapperOnOpen = null, wrapperOnData = null, wrapperOnDrain = null;
|
||||
var wrapperOnError = null, wrapperOnClose = null;
|
||||
socket.onopen = function () {
|
||||
if (wrapperOnOpen) {
|
||||
wrapperOnOpen.call(wrapper, new sandbox.Object());
|
||||
if (wrapper.onopen) {
|
||||
wrapper.onopen.call(wrapper, new sandbox.Object());
|
||||
}
|
||||
};
|
||||
socket.ondata = function (e) {
|
||||
if (wrapperOnData) {
|
||||
if (wrapper.ondata) {
|
||||
var wrappedE = new sandbox.Object();
|
||||
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
|
||||
wrapperOnData.call(wrapper, wrappedE);
|
||||
wrapper.ondata.call(wrapper, wrappedE);
|
||||
}
|
||||
};
|
||||
socket.ondrain = function () {
|
||||
if (wrapperOnDrain) {
|
||||
wrapperOnDrain.call(wrapper, new sandbox.Object());
|
||||
if (wrapper.ondrain) {
|
||||
wrapper.ondrain.call(wrapper, new sandbox.Object());
|
||||
}
|
||||
};
|
||||
socket.onerror = function (e) {
|
||||
if (wrapperOnError) {
|
||||
if (wrapper.onerror) {
|
||||
var wrappedE = new sandbox.Object();
|
||||
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
|
||||
wrapperOnError.call(wrapper, wrappedE);
|
||||
wrapper.onerror.call(wrapper, wrappedE);
|
||||
}
|
||||
};
|
||||
socket.onclose = function () {
|
||||
if (wrapperOnClose) {
|
||||
wrapperOnClose.call(wrapper, new sandbox.Object());
|
||||
if (wrapper.onclose) {
|
||||
wrapper.onclose.call(wrapper, new sandbox.Object());
|
||||
}
|
||||
};
|
||||
|
||||
var wrapper = new sandbox.Object();
|
||||
var waived = Components.utils.waiveXrays(wrapper);
|
||||
Object.defineProperties(waived, {
|
||||
onopen: {
|
||||
get: function () { return wrapperOnOpen; },
|
||||
set: function (value) { wrapperOnOpen = value; },
|
||||
enumerable: true
|
||||
},
|
||||
ondata: {
|
||||
get: function () { return wrapperOnData; },
|
||||
set: function (value) { wrapperOnData = value; },
|
||||
enumerable: true
|
||||
},
|
||||
ondrain: {
|
||||
get: function () { return wrapperOnDrain; },
|
||||
set: function (value) { wrapperOnDrain = value; },
|
||||
enumerable: true
|
||||
},
|
||||
onerror: {
|
||||
get: function () { return wrapperOnError; },
|
||||
set: function (value) { wrapperOnError = value; },
|
||||
enumerable: true
|
||||
},
|
||||
onclose: {
|
||||
get: function () { return wrapperOnClose; },
|
||||
set: function (value) { wrapperOnClose = value; },
|
||||
enumerable: true
|
||||
},
|
||||
var wrapper = Cu.createObjectIn(sandbox);
|
||||
Object.defineProperties(wrapper, {
|
||||
onopen: genPropDesc(null),
|
||||
ondata: genPropDesc(null),
|
||||
ondrain: genPropDesc(null),
|
||||
onerror: genPropDesc(null),
|
||||
onclose: genPropDesc(null),
|
||||
|
||||
send: {
|
||||
value: function (buffer, offset, count) {
|
||||
send: genPropDesc(function (buffer, offset, count) {
|
||||
return socket.send(buffer, offset, count);
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
close: {
|
||||
value: function () {
|
||||
close: genPropDesc(function () {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(wrapper);
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
createXHR: function (sandbox) {
|
||||
function genPropDesc(value) {
|
||||
return {
|
||||
enumerable: true, configurable: true, writable: true, value: value
|
||||
};
|
||||
}
|
||||
|
||||
var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
var wrapperOnLoad = null, wrapperOnError = null;
|
||||
|
||||
xhr.onload = function () {
|
||||
if (wrapperOnLoad) {
|
||||
wrapperOnLoad.call(wrapper, new sandbox.Object());
|
||||
wrapper.status = xhr.status;
|
||||
wrapper.response = Components.utils.cloneInto(xhr.response, sandbox);
|
||||
if (wrapper.onload) {
|
||||
wrapper.onload.call(wrapper, new sandbox.Object());
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
if (wrappedOnError) {
|
||||
wrappedOnError.call(wrapper, new sandbox.Object());
|
||||
wrapper.status = xhr.status;
|
||||
if (wrapper.onerror) {
|
||||
wrapper.onerror.call(wrapper, new sandbox.Object());
|
||||
}
|
||||
};
|
||||
|
||||
var wrapper = new sandbox.Object();
|
||||
var waived = Components.utils.waiveXrays(wrapper);
|
||||
Object.defineProperties(waived, {
|
||||
status: {
|
||||
get: function () { return xhr.status; },
|
||||
enumerable: true
|
||||
},
|
||||
response: {
|
||||
get: function () { return Components.utils.cloneInto(xhr.response, sandbox); },
|
||||
enumerable: true
|
||||
},
|
||||
responseType: {
|
||||
get: function () { return xhr.responseType; },
|
||||
set: function (value) {
|
||||
if (value !== 'arraybuffer') {
|
||||
throw new Error('Invalid responseType.');
|
||||
}
|
||||
},
|
||||
enumerable: true
|
||||
},
|
||||
onload: {
|
||||
get: function () { return wrapperOnLoad; },
|
||||
set: function (value) { wrapperOnLoad = value; },
|
||||
enumerable: true
|
||||
},
|
||||
onerror: {
|
||||
get: function () { return wrapperOnError; },
|
||||
set: function (value) { wrapperOnError = value; },
|
||||
enumerable: true
|
||||
},
|
||||
open: {
|
||||
value: function (method, path, async) {
|
||||
var wrapper = Components.utils.createObjectIn(sandbox);
|
||||
Object.defineProperties(wrapper, {
|
||||
status: genPropDesc(0),
|
||||
response: genPropDesc(undefined),
|
||||
responseType: genPropDesc('text'),
|
||||
|
||||
onload: genPropDesc(null),
|
||||
onerror: genPropDesc(null),
|
||||
|
||||
open: genPropDesc(function (method, path, async) {
|
||||
if (method !== 'POST' || !path || (async !== undefined && !async)) {
|
||||
throw new Error('invalid open() arguments');
|
||||
}
|
||||
@ -171,22 +138,22 @@ var RtmpUtils = {
|
||||
xhr.open('POST', path, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-fcs');
|
||||
}
|
||||
},
|
||||
setRequestHeader: {
|
||||
value: function (header, value) {
|
||||
}),
|
||||
|
||||
setRequestHeader: genPropDesc(function (header, value) {
|
||||
if (header !== 'Content-Type' || value !== 'application/x-fcs') {
|
||||
throw new Error('invalid setRequestHeader() arguments');
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
|
||||
send: {
|
||||
value: function (data) {
|
||||
send: genPropDesc(function (data) {
|
||||
if (this.responseType !== 'arraybuffer') {
|
||||
throw new Error('Invalid responseType.');
|
||||
}
|
||||
xhr.send(data);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
};
|
||||
|
782
browser/extensions/shumway/chrome/ShumwayCom.jsm
Normal file
782
browser/extensions/shumway/chrome/ShumwayCom.jsm
Normal file
@ -0,0 +1,782 @@
|
||||
/*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var EXPORTED_SYMBOLS = ['ShumwayCom'];
|
||||
|
||||
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
Components.utils.import('resource://gre/modules/Promise.jsm');
|
||||
Components.utils.import('resource://gre/modules/NetUtil.jsm');
|
||||
|
||||
Components.utils.import('chrome://shumway/content/SpecialInflate.jsm');
|
||||
Components.utils.import('chrome://shumway/content/SpecialStorage.jsm');
|
||||
Components.utils.import('chrome://shumway/content/RtmpUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
|
||||
'resource://shumway/ShumwayTelemetry.jsm');
|
||||
|
||||
const MAX_USER_INPUT_TIMEOUT = 250; // ms
|
||||
|
||||
function getBoolPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(pref);
|
||||
} catch (ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
function getStringPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getComplexValue(pref, Components.interfaces.nsISupportsString).data;
|
||||
} catch (ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
function log(aMsg) {
|
||||
let msg = 'ShumwayCom.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
|
||||
Services.console.logStringMessage(msg);
|
||||
dump(msg + '\n');
|
||||
}
|
||||
|
||||
var ShumwayCom = {
|
||||
createAdapter: function (content, callbacks) {
|
||||
|
||||
function setupComBridge(playerWindow) {
|
||||
// Creates secondary ShumwayCom adapter and sets up the forwarders from
|
||||
// the callbacks to primary adapter.
|
||||
var playerContent = playerWindow.contentWindow;
|
||||
var secondaryAdapter = ShumwayCom.createAdapter(playerContent, callbacks);
|
||||
shumwayComAdapter.onLoadFileCallback = function (arg) {
|
||||
if (secondaryAdapter.onLoadFileCallback) {
|
||||
secondaryAdapter.onLoadFileCallback(Components.utils.cloneInto(arg, playerContent));
|
||||
}
|
||||
};
|
||||
shumwayComAdapter.onExternalCallback = function (call) {
|
||||
if (secondaryAdapter.onExternalCallback) {
|
||||
return secondaryAdapter.onExternalCallback(Components.utils.cloneInto(call, playerContent));
|
||||
}
|
||||
};
|
||||
shumwayComAdapter.onSystemResourceCallback = function (id, data) {
|
||||
if (secondaryAdapter.onSystemResourceCallback) {
|
||||
secondaryAdapter.onSystemResourceCallback(id, Components.utils.cloneInto(data, playerContent));
|
||||
}
|
||||
};
|
||||
// Sets up the _onSyncMessage helper that is used from postSyncMessage of
|
||||
// the secondary adapter.
|
||||
secondaryAdapter._onSyncMessage = function (msg) {
|
||||
if (shumwayComAdapter.onSyncMessage) {
|
||||
var waivedMsg = Components.utils.waiveXrays(msg); // for cloneInto
|
||||
return shumwayComAdapter.onSyncMessage(Components.utils.cloneInto(waivedMsg, content));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function genPropDesc(value) {
|
||||
return {
|
||||
enumerable: true, configurable: true, writable: true, value: value
|
||||
};
|
||||
}
|
||||
|
||||
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
|
||||
// up Xray wrappers.
|
||||
var shumwayComAdapter = Components.utils.createObjectIn(content, {defineAs: 'ShumwayCom'});
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
enableDebug: genPropDesc(function enableDebug() {
|
||||
callbacks.enableDebug()
|
||||
}),
|
||||
setFullscreen: genPropDesc(function setFullscreen(value) {
|
||||
callbacks.sendMessage('setFullscreen', value, false);
|
||||
}),
|
||||
endActivation: genPropDesc(function endActivation() {
|
||||
callbacks.sendMessage('endActivation', null, false);
|
||||
}),
|
||||
fallback: genPropDesc(function fallback() {
|
||||
callbacks.sendMessage('fallback', null, false);
|
||||
}),
|
||||
getSettings: genPropDesc(function getSettings() {
|
||||
return Components.utils.cloneInto(
|
||||
callbacks.sendMessage('getSettings', null, true), content);
|
||||
}),
|
||||
getPluginParams: genPropDesc(function getPluginParams() {
|
||||
return Components.utils.cloneInto(
|
||||
callbacks.sendMessage('getPluginParams', null, true), content);
|
||||
}),
|
||||
reportIssue: genPropDesc(function reportIssue() {
|
||||
callbacks.sendMessage('reportIssue', null, false);
|
||||
}),
|
||||
externalCom: genPropDesc(function externalCom(args) {
|
||||
var result = String(callbacks.sendMessage('externalCom', args, true));
|
||||
return Components.utils.cloneInto(result, content);
|
||||
}),
|
||||
loadFile: genPropDesc(function loadFile(args) {
|
||||
callbacks.sendMessage('loadFile', args, false);
|
||||
}),
|
||||
reportTelemetry: genPropDesc(function reportTelemetry(args) {
|
||||
callbacks.sendMessage('reportTelemetry', args, false);
|
||||
}),
|
||||
setClipboard: genPropDesc(function setClipboard(args) {
|
||||
callbacks.sendMessage('setClipboard', args, false);
|
||||
}),
|
||||
navigateTo: genPropDesc(function navigateTo(args) {
|
||||
callbacks.sendMessage('navigateTo', args, false);
|
||||
}),
|
||||
userInput: genPropDesc(function userInput() {
|
||||
callbacks.sendMessage('userInput', null, true);
|
||||
}),
|
||||
loadSystemResource: genPropDesc(function loadSystemResource(id) {
|
||||
loadShumwaySystemResource(id).then(function (data) {
|
||||
if (shumwayComAdapter.onSystemResourceCallback) {
|
||||
shumwayComAdapter.onSystemResourceCallback(id,
|
||||
Components.utils.cloneInto(data, content));
|
||||
}
|
||||
});
|
||||
}),
|
||||
setupComBridge: genPropDesc(setupComBridge),
|
||||
postSyncMessage: genPropDesc(function postSyncMessage(msg) {
|
||||
return Components.utils.cloneInto(shumwayComAdapter._onSyncMessage(msg), content);
|
||||
})
|
||||
});
|
||||
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
onLoadFileCallback: genPropDesc(null),
|
||||
onExternalCallback: genPropDesc(null),
|
||||
onSystemResourceCallback: genPropDesc(null),
|
||||
onSyncMessage: genPropDesc(null)
|
||||
});
|
||||
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
createSpecialStorage: genPropDesc(function () {
|
||||
var environment = callbacks.getEnvironment();
|
||||
return SpecialStorageUtils.createWrappedSpecialStorage(content,
|
||||
environment.swfUrl, environment.privateBrowsing);
|
||||
})
|
||||
});
|
||||
|
||||
// Exposing createSpecialInflate function for DEFLATE stream decoding using
|
||||
// Gecko API.
|
||||
if (SpecialInflateUtils.isSpecialInflateEnabled) {
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
createSpecialInflate: genPropDesc(function () {
|
||||
return SpecialInflateUtils.createWrappedSpecialInflate(content);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Exposing createRtmpSocket/createRtmpXHR functions to support RTMP stream
|
||||
// functionality.
|
||||
if (RtmpUtils.isRtmpEnabled) {
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
createRtmpSocket: genPropDesc(function (params) {
|
||||
return RtmpUtils.createSocket(content, params);
|
||||
}),
|
||||
createRtmpXHR: genPropDesc(function () {
|
||||
return RtmpUtils.createXHR(content);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Components.utils.makeObjectPropsNormal(shumwayComAdapter);
|
||||
return shumwayComAdapter;
|
||||
},
|
||||
|
||||
createActions: function (startupInfo, window, document) {
|
||||
return new ShumwayChromeActions(startupInfo, window, document);
|
||||
}
|
||||
};
|
||||
|
||||
function loadShumwaySystemResource(id) {
|
||||
var url, type;
|
||||
switch (id) {
|
||||
case 0: // BuiltinAbc
|
||||
url = 'resource://shumway/libs/builtin.abc';
|
||||
type = 'arraybuffer';
|
||||
break;
|
||||
case 1: // PlayerglobalAbcs
|
||||
url = 'resource://shumway/playerglobal/playerglobal.abcs';
|
||||
type = 'arraybuffer';
|
||||
break;
|
||||
case 2: // PlayerglobalManifest
|
||||
url = 'resource://shumway/playerglobal/playerglobal.json';
|
||||
type = 'json';
|
||||
break;
|
||||
default:
|
||||
return Promise.reject('Unsupported system resource id');
|
||||
}
|
||||
|
||||
var deferred = Promise.defer();
|
||||
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = type;
|
||||
xhr.onload = function () {
|
||||
deferred.resolve(xhr.response);
|
||||
};
|
||||
xhr.send(null);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// All the privileged actions.
|
||||
function ShumwayChromeActions(startupInfo, window, document) {
|
||||
this.url = startupInfo.url;
|
||||
this.objectParams = startupInfo.objectParams;
|
||||
this.movieParams = startupInfo.movieParams;
|
||||
this.baseUrl = startupInfo.baseUrl;
|
||||
this.isOverlay = startupInfo.isOverlay;
|
||||
this.embedTag = startupInfo.embedTag;
|
||||
this.isPausedAtStart = startupInfo.isPausedAtStart;
|
||||
this.window = window;
|
||||
this.document = document;
|
||||
this.externalComInitialized = false;
|
||||
this.allowScriptAccess = startupInfo.allowScriptAccess;
|
||||
this.lastUserInput = 0;
|
||||
this.crossdomainRequestsCache = Object.create(null);
|
||||
this.telemetry = {
|
||||
startTime: Date.now(),
|
||||
features: [],
|
||||
errors: []
|
||||
};
|
||||
|
||||
this.onLoadFileCallback = null;
|
||||
this.onExternalCallback = null;
|
||||
}
|
||||
|
||||
ShumwayChromeActions.prototype = {
|
||||
// The method is created for convenience of routing messages from the OOP
|
||||
// handler or remote debugger adapter. All method calls are originated from
|
||||
// the ShumwayCom adapter (see above), or from the debugger adapter.
|
||||
// See viewerWrapper.js for these usages near sendMessage calls.
|
||||
invoke: function (name, args) {
|
||||
return this[name].call(this, args);
|
||||
},
|
||||
|
||||
getBoolPref: function (data) {
|
||||
if (!/^shumway\./.test(data.pref)) {
|
||||
return null;
|
||||
}
|
||||
return getBoolPref(data.pref, data.def);
|
||||
},
|
||||
|
||||
getSettings: function getSettings() {
|
||||
return {
|
||||
compilerSettings: {
|
||||
appCompiler: getBoolPref('shumway.appCompiler', true),
|
||||
sysCompiler: getBoolPref('shumway.sysCompiler', false),
|
||||
verifier: getBoolPref('shumway.verifier', true)
|
||||
},
|
||||
playerSettings: {
|
||||
turboMode: getBoolPref('shumway.turboMode', false),
|
||||
hud: getBoolPref('shumway.hud', false),
|
||||
forceHidpi: getBoolPref('shumway.force_hidpi', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getPluginParams: function getPluginParams() {
|
||||
return {
|
||||
url: this.url,
|
||||
baseUrl : this.baseUrl,
|
||||
movieParams: this.movieParams,
|
||||
objectParams: this.objectParams,
|
||||
isOverlay: this.isOverlay,
|
||||
isPausedAtStart: this.isPausedAtStart,
|
||||
isDebuggerEnabled: getBoolPref('shumway.debug.enabled', false)
|
||||
};
|
||||
},
|
||||
|
||||
loadFile: function loadFile(data) {
|
||||
function notifyLoadFileListener(data) {
|
||||
if (!actions.onLoadFileCallback) {
|
||||
return;
|
||||
}
|
||||
actions.onLoadFileCallback(data);
|
||||
}
|
||||
|
||||
var actions = this;
|
||||
var url = data.url;
|
||||
var checkPolicyFile = data.checkPolicyFile;
|
||||
var sessionId = data.sessionId;
|
||||
var limit = data.limit || 0;
|
||||
var method = data.method || "GET";
|
||||
var mimeType = data.mimeType;
|
||||
var postData = data.postData || null;
|
||||
|
||||
var win = this.window;
|
||||
var baseUrl = this.baseUrl;
|
||||
|
||||
var performXHR = function () {
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
xhr.open(method, url, true);
|
||||
xhr.responseType = "moz-chunked-arraybuffer";
|
||||
|
||||
if (baseUrl) {
|
||||
// Setting the referer uri, some site doing checks if swf is embedded
|
||||
// on the original page.
|
||||
xhr.setRequestHeader("Referer", baseUrl);
|
||||
}
|
||||
|
||||
// TODO apply range request headers if limit is specified
|
||||
|
||||
var lastPosition = 0;
|
||||
xhr.onprogress = function (e) {
|
||||
var position = e.loaded;
|
||||
var data = new Uint8Array(xhr.response);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId,
|
||||
topic: "progress", array: data, loaded: position, total: e.total});
|
||||
lastPosition = position;
|
||||
if (limit && e.total >= limit) {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
xhr.onreadystatechange = function(event) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200 && xhr.status !== 0) {
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error", error: xhr.statusText});
|
||||
}
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "close"});
|
||||
}
|
||||
};
|
||||
if (mimeType)
|
||||
xhr.setRequestHeader("Content-Type", mimeType);
|
||||
xhr.send(postData);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "open"});
|
||||
};
|
||||
|
||||
canDownloadFile(url, checkPolicyFile, this.url, this.crossdomainRequestsCache).then(function () {
|
||||
performXHR();
|
||||
}, function (reason) {
|
||||
log("data access is prohibited to " + url + " from " + baseUrl);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error",
|
||||
error: "only original swf file or file from the same origin loading supported"});
|
||||
});
|
||||
},
|
||||
|
||||
navigateTo: function (data) {
|
||||
var embedTag = this.embedTag.wrappedJSObject;
|
||||
var window = embedTag ? embedTag.ownerDocument.defaultView : this.window;
|
||||
window.open(data.url, data.target || '_self');
|
||||
},
|
||||
|
||||
fallback: function(automatic) {
|
||||
automatic = !!automatic;
|
||||
var event = this.document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('shumwayFallback', true, true, {
|
||||
automatic: automatic
|
||||
});
|
||||
this.window.dispatchEvent(event);
|
||||
},
|
||||
|
||||
userInput: function() {
|
||||
var win = this.window;
|
||||
var winUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (winUtils.isHandlingUserInput) {
|
||||
this.lastUserInput = Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
isUserInputInProgress: function () {
|
||||
// TODO userInput does not work for OOP
|
||||
if (!getBoolPref('shumway.userInputSecurity', true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't trust our Shumway non-privileged code just yet to verify the
|
||||
// user input -- using userInput function above to track that.
|
||||
if ((Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
// TODO other security checks?
|
||||
return true;
|
||||
},
|
||||
|
||||
setClipboard: function (data) {
|
||||
if (typeof data !== 'string' || !this.isUserInputInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Components.interfaces.nsIClipboardHelper);
|
||||
clipboard.copyString(data);
|
||||
},
|
||||
|
||||
setFullscreen: function (enabled) {
|
||||
enabled = !!enabled;
|
||||
|
||||
if (!this.isUserInputInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = this.embedTag || this.document.body;
|
||||
if (enabled) {
|
||||
target.mozRequestFullScreen();
|
||||
} else {
|
||||
target.ownerDocument.mozCancelFullScreen();
|
||||
}
|
||||
},
|
||||
|
||||
endActivation: function () {
|
||||
var event = this.document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('shumwayActivated', true, true, null);
|
||||
this.window.dispatchEvent(event);
|
||||
},
|
||||
|
||||
reportTelemetry: function (data) {
|
||||
var topic = data.topic;
|
||||
switch (topic) {
|
||||
case 'firstFrame':
|
||||
var time = Date.now() - this.telemetry.startTime;
|
||||
ShumwayTelemetry.onFirstFrame(time);
|
||||
break;
|
||||
case 'parseInfo':
|
||||
ShumwayTelemetry.onParseInfo({
|
||||
parseTime: +data.parseTime,
|
||||
size: +data.bytesTotal,
|
||||
swfVersion: data.swfVersion|0,
|
||||
frameRate: +data.frameRate,
|
||||
width: data.width|0,
|
||||
height: data.height|0,
|
||||
bannerType: data.bannerType|0,
|
||||
isAvm2: !!data.isAvm2
|
||||
});
|
||||
break;
|
||||
case 'feature':
|
||||
var featureType = data.feature|0;
|
||||
var MIN_FEATURE_TYPE = 0, MAX_FEATURE_TYPE = 999;
|
||||
if (featureType >= MIN_FEATURE_TYPE && featureType <= MAX_FEATURE_TYPE &&
|
||||
!this.telemetry.features[featureType]) {
|
||||
this.telemetry.features[featureType] = true; // record only one feature per SWF
|
||||
ShumwayTelemetry.onFeature(featureType);
|
||||
}
|
||||
break;
|
||||
case 'error':
|
||||
var errorType = data.error|0;
|
||||
var MIN_ERROR_TYPE = 0, MAX_ERROR_TYPE = 2;
|
||||
if (errorType >= MIN_ERROR_TYPE && errorType <= MAX_ERROR_TYPE &&
|
||||
!this.telemetry.errors[errorType]) {
|
||||
this.telemetry.errors[errorType] = true; // record only one report per SWF
|
||||
ShumwayTelemetry.onError(errorType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
reportIssue: function (exceptions) {
|
||||
var urlTemplate = "https://bugzilla.mozilla.org/enter_bug.cgi?op_sys=All&priority=--" +
|
||||
"&rep_platform=All&target_milestone=---&version=Trunk&product=Firefox" +
|
||||
"&component=Shumway&short_desc=&comment={comment}" +
|
||||
"&bug_file_loc={url}";
|
||||
var windowUrl = this.window.parent.wrappedJSObject.location + '';
|
||||
var url = urlTemplate.split('{url}').join(encodeURIComponent(windowUrl));
|
||||
var params = {
|
||||
swf: encodeURIComponent(this.url)
|
||||
};
|
||||
getVersionInfo().then(function (versions) {
|
||||
params.versions = versions;
|
||||
}).then(function () {
|
||||
var ffbuild = params.versions.geckoVersion + ' (' + params.versions.geckoBuildID + ')';
|
||||
//params.exceptions = encodeURIComponent(exceptions);
|
||||
var comment = '+++ Initially filed via the problem reporting functionality in Shumway +++\n' +
|
||||
'Please add any further information that you deem helpful here:\n\n\n\n' +
|
||||
'----------------------\n\n' +
|
||||
'Technical Information:\n' +
|
||||
'Firefox version: ' + ffbuild + '\n' +
|
||||
'Shumway version: ' + params.versions.shumwayVersion;
|
||||
url = url.split('{comment}').join(encodeURIComponent(comment));
|
||||
this.window.open(url);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
externalCom: function (data) {
|
||||
if (!this.allowScriptAccess)
|
||||
return;
|
||||
|
||||
// TODO check security ?
|
||||
var parentWindow = this.window.parent.wrappedJSObject;
|
||||
var embedTag = this.embedTag.wrappedJSObject;
|
||||
switch (data.action) {
|
||||
case 'init':
|
||||
if (this.externalComInitialized)
|
||||
return;
|
||||
|
||||
this.externalComInitialized = true;
|
||||
initExternalCom(parentWindow, embedTag, this);
|
||||
return;
|
||||
case 'getId':
|
||||
return embedTag.id;
|
||||
case 'eval':
|
||||
return parentWindow.__flash__eval(data.expression);
|
||||
case 'call':
|
||||
return parentWindow.__flash__call(data.request);
|
||||
case 'register':
|
||||
return embedTag.__flash__registerCallback(data.functionName);
|
||||
case 'unregister':
|
||||
return embedTag.__flash__unregisterCallback(data.functionName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function disableXHRRedirect(xhr) {
|
||||
var listener = {
|
||||
asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
|
||||
// TODO perform URL check?
|
||||
callback.onRedirectVerifyCallback(Components.results.NS_ERROR_ABORT);
|
||||
},
|
||||
getInterface: function(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIChannelEventSink])
|
||||
};
|
||||
xhr.channel.notificationCallbacks = listener;
|
||||
}
|
||||
|
||||
function canDownloadFile(url, checkPolicyFile, swfUrl, cache) {
|
||||
// TODO flash cross-origin request
|
||||
if (url === swfUrl) {
|
||||
// Allows downloading for the original file.
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// Allows downloading from the same origin.
|
||||
var parsedUrl, parsedBaseUrl;
|
||||
try {
|
||||
parsedUrl = NetUtil.newURI(url);
|
||||
} catch (ex) { /* skipping invalid urls */ }
|
||||
try {
|
||||
parsedBaseUrl = NetUtil.newURI(swfUrl);
|
||||
} catch (ex) { /* skipping invalid urls */ }
|
||||
|
||||
if (parsedUrl && parsedBaseUrl &&
|
||||
parsedUrl.prePath === parsedBaseUrl.prePath) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
// Additionally using internal whitelist.
|
||||
var whitelist = getStringPref('shumway.whitelist', '');
|
||||
if (whitelist && parsedUrl) {
|
||||
var whitelisted = whitelist.split(',').some(function (i) {
|
||||
return domainMatches(parsedUrl.host, i);
|
||||
});
|
||||
if (whitelisted) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
if (!parsedUrl || !parsedBaseUrl) {
|
||||
return Promise.reject('Invalid or non-specified URL or Base URL.');
|
||||
}
|
||||
|
||||
if (!checkPolicyFile) {
|
||||
return Promise.reject('Check of the policy file is not allowed.');
|
||||
}
|
||||
|
||||
// We can request crossdomain.xml.
|
||||
return fetchPolicyFile(parsedUrl.prePath + '/crossdomain.xml', cache).
|
||||
then(function (policy) {
|
||||
|
||||
if (policy.siteControl === 'none') {
|
||||
throw 'Site control is set to \"none\"';
|
||||
}
|
||||
// TODO assuming master-only, there are also 'by-content-type', 'all', etc.
|
||||
|
||||
var allowed = policy.allowAccessFrom.some(function (i) {
|
||||
return domainMatches(parsedBaseUrl.host, i.domain) &&
|
||||
(!i.secure || parsedBaseUrl.scheme.toLowerCase() === 'https');
|
||||
});
|
||||
if (!allowed) {
|
||||
throw 'crossdomain.xml does not contain source URL.';
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function domainMatches(host, pattern) {
|
||||
if (!pattern) return false;
|
||||
if (pattern === '*') return true;
|
||||
host = host.toLowerCase();
|
||||
var parts = pattern.toLowerCase().split('*');
|
||||
if (host.indexOf(parts[0]) !== 0) return false;
|
||||
var p = parts[0].length;
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
var j = host.indexOf(parts[i], p);
|
||||
if (j === -1) return false;
|
||||
p = j + parts[i].length;
|
||||
}
|
||||
return parts[parts.length - 1] === '' || p === host.length;
|
||||
}
|
||||
|
||||
function fetchPolicyFile(url, cache) {
|
||||
if (url in cache) {
|
||||
return cache[url];
|
||||
}
|
||||
|
||||
var deferred = Promise.defer();
|
||||
|
||||
log('Fetching policy file at ' + url);
|
||||
var MAX_POLICY_SIZE = 8192;
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
xhr.open('GET', url, true);
|
||||
disableXHRRedirect(xhr);
|
||||
xhr.overrideMimeType('text/xml');
|
||||
xhr.onprogress = function (e) {
|
||||
if (e.loaded >= MAX_POLICY_SIZE) {
|
||||
xhr.abort();
|
||||
cache[url] = false;
|
||||
deferred.reject('Max policy size');
|
||||
}
|
||||
};
|
||||
xhr.onreadystatechange = function(event) {
|
||||
if (xhr.readyState === 4) {
|
||||
// TODO disable redirects
|
||||
var doc = xhr.responseXML;
|
||||
if (xhr.status !== 200 || !doc) {
|
||||
deferred.reject('Invalid HTTP status: ' + xhr.statusText);
|
||||
return;
|
||||
}
|
||||
// parsing params
|
||||
var params = doc.documentElement.childNodes;
|
||||
var policy = { siteControl: null, allowAccessFrom: []};
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
switch (params[i].localName) {
|
||||
case 'site-control':
|
||||
policy.siteControl = params[i].getAttribute('permitted-cross-domain-policies');
|
||||
break;
|
||||
case 'allow-access-from':
|
||||
var access = {
|
||||
domain: params[i].getAttribute('domain'),
|
||||
security: params[i].getAttribute('security') === 'true'
|
||||
};
|
||||
policy.allowAccessFrom.push(access);
|
||||
break;
|
||||
default:
|
||||
// TODO allow-http-request-headers-from and other
|
||||
break;
|
||||
}
|
||||
}
|
||||
deferred.resolve(policy);
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
return (cache[url] = deferred.promise);
|
||||
}
|
||||
|
||||
function getVersionInfo() {
|
||||
var deferred = Promise.defer();
|
||||
var versionInfo = {
|
||||
geckoVersion: 'unknown',
|
||||
geckoBuildID: 'unknown',
|
||||
shumwayVersion: 'unknown'
|
||||
};
|
||||
try {
|
||||
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Components.interfaces.nsIXULAppInfo);
|
||||
versionInfo.geckoVersion = appInfo.version;
|
||||
versionInfo.geckoBuildID = appInfo.appBuildID;
|
||||
} catch (e) {
|
||||
log('Error encountered while getting platform version info: ' + e);
|
||||
}
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
xhr.open('GET', 'resource://shumway/version.txt', true);
|
||||
xhr.overrideMimeType('text/plain');
|
||||
xhr.onload = function () {
|
||||
try {
|
||||
// Trying to merge version.txt lines into something like:
|
||||
// "version (sha) details"
|
||||
var lines = xhr.responseText.split(/\n/g);
|
||||
lines[1] = '(' + lines[1] + ')';
|
||||
versionInfo.shumwayVersion = lines.join(' ');
|
||||
} catch (e) {
|
||||
log('Error while parsing version info: ' + e);
|
||||
}
|
||||
deferred.resolve(versionInfo);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
log('Error while reading version info: ' + xhr.error);
|
||||
deferred.resolve(versionInfo);
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function initExternalCom(wrappedWindow, wrappedObject, actions) {
|
||||
var traceExternalInterface = getBoolPref('shumway.externalInterface.trace', false);
|
||||
if (!wrappedWindow.__flash__initialized) {
|
||||
wrappedWindow.__flash__initialized = true;
|
||||
wrappedWindow.__flash__toXML = function __flash__toXML(obj) {
|
||||
switch (typeof obj) {
|
||||
case 'boolean':
|
||||
return obj ? '<true/>' : '<false/>';
|
||||
case 'number':
|
||||
return '<number>' + obj + '</number>';
|
||||
case 'object':
|
||||
if (obj === null) {
|
||||
return '<null/>';
|
||||
}
|
||||
if ('hasOwnProperty' in obj && obj.hasOwnProperty('length')) {
|
||||
// array
|
||||
var xml = '<array>';
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
xml += '<property id="' + i + '">' + __flash__toXML(obj[i]) + '</property>';
|
||||
}
|
||||
return xml + '</array>';
|
||||
}
|
||||
var xml = '<object>';
|
||||
for (var i in obj) {
|
||||
xml += '<property id="' + i + '">' + __flash__toXML(obj[i]) + '</property>';
|
||||
}
|
||||
return xml + '</object>';
|
||||
case 'string':
|
||||
return '<string>' + obj.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</string>';
|
||||
case 'undefined':
|
||||
return '<undefined/>';
|
||||
}
|
||||
};
|
||||
wrappedWindow.__flash__eval = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__eval: ' + expr);
|
||||
// allowScriptAccess protects page from unwanted swf scripts,
|
||||
// we can execute script in the page context without restrictions.
|
||||
var result = this.eval(expr);
|
||||
traceExternalInterface && this.console.log('__flash__eval (result): ' + result);
|
||||
return result;
|
||||
}.bind(wrappedWindow);
|
||||
wrappedWindow.__flash__call = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__call (ignored): ' + expr);
|
||||
};
|
||||
}
|
||||
wrappedObject.__flash__registerCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__registerCallback: ' + functionName);
|
||||
Components.utils.exportFunction(function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn: ' + functionName);
|
||||
var result;
|
||||
if (actions.onExternalCallback) {
|
||||
result = actions.onExternalCallback({functionName: functionName, args: args});
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn (result): ' + result);
|
||||
}
|
||||
return wrappedWindow.eval(result);
|
||||
}, this, { defineAs: functionName });
|
||||
};
|
||||
wrappedObject.__flash__unregisterCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__unregisterCallback: ' + functionName);
|
||||
delete this[functionName];
|
||||
};
|
||||
}
|
@ -96,37 +96,36 @@ var SpecialInflateUtils = {
|
||||
},
|
||||
|
||||
createWrappedSpecialInflate: function (sandbox) {
|
||||
function genPropDesc(value) {
|
||||
return {
|
||||
enumerable: true, configurable: true, writable: true, value: value
|
||||
};
|
||||
}
|
||||
|
||||
var wrapped = new SpecialInflate();
|
||||
var wrapperOnData = null;
|
||||
wrapped.onData = function(data) {
|
||||
if (wrapperOnData) {
|
||||
wrapperOnData.call(wrapper, Components.utils.cloneInto(data, sandbox));
|
||||
if (wrapper.onData) {
|
||||
wrapper.onData.call(wrapper, Components.utils.cloneInto(data, sandbox));
|
||||
}
|
||||
};
|
||||
|
||||
// We will return object created in the sandbox/content, with some exposed
|
||||
// properties/methods, so we can send data between wrapped object and
|
||||
// and sandbox/content.
|
||||
var wrapper = new sandbox.Object();
|
||||
var waived = Components.utils.waiveXrays(wrapper);
|
||||
Object.defineProperties(waived, {
|
||||
onData: {
|
||||
get: function () { return wrapperOnData; },
|
||||
set: function (value) { wrapperOnData = value; },
|
||||
enumerable: true
|
||||
},
|
||||
push: {
|
||||
value: function (data) {
|
||||
var wrapper = Components.utils.createObjectIn(sandbox);
|
||||
Object.defineProperties(wrapper, {
|
||||
onData: genPropDesc(null),
|
||||
|
||||
push: genPropDesc(function (data) {
|
||||
// Uint8Array is expected in the data parameter.
|
||||
// SpecialInflate.push() fails with other argument types.
|
||||
return wrapped.push(data);
|
||||
}
|
||||
},
|
||||
close: {
|
||||
value: function () {
|
||||
}),
|
||||
close: genPropDesc(function () {
|
||||
return wrapped.close();
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
};
|
||||
|
57
browser/extensions/shumway/chrome/SpecialStorage.jsm
Normal file
57
browser/extensions/shumway/chrome/SpecialStorage.jsm
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var EXPORTED_SYMBOLS = ['SpecialStorageUtils'];
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
var SpecialStorageUtils = {
|
||||
createWrappedSpecialStorage: function (sandbox, swfUrl, privateBrowsing) {
|
||||
function genPropDesc(value) {
|
||||
return {
|
||||
enumerable: true, configurable: true, writable: true, value: value
|
||||
};
|
||||
}
|
||||
|
||||
// Creating internal localStorage object based on url and privateBrowsing setting.
|
||||
var uri = Services.io.newURI(swfUrl, null, null);
|
||||
var principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager)
|
||||
.getNoAppCodebasePrincipal(uri);
|
||||
var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
var storage = dsm.createStorage(null, principal, privateBrowsing);
|
||||
|
||||
// We will return object created in the sandbox/content, with some exposed
|
||||
// properties/methods, so we can send data between wrapped object and
|
||||
// and sandbox/content.
|
||||
var wrapper = Components.utils.createObjectIn(sandbox);
|
||||
Object.defineProperties(wrapper, {
|
||||
getItem: genPropDesc(function (key) {
|
||||
return storage.getItem(key);
|
||||
}),
|
||||
setItem: genPropDesc(function (key, value) {
|
||||
storage.setItem(key, value);
|
||||
}),
|
||||
removeItem: genPropDesc(function (key) {
|
||||
storage.removeItem(key);
|
||||
})
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
};
|
@ -66,6 +66,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (isRemote && typeof ShumwayBootstrapUtils !== 'undefined') {
|
||||
// Treat content as non-remote when bootstrap.js or ShumwayUtils.jsm
|
||||
// already registered the Shumway components for current content scope.
|
||||
isRemote = false;
|
||||
}
|
||||
|
||||
if (isRemote) {
|
||||
addMessageListener('Shumway:Child:refreshSettings', updateSettings);
|
||||
updateSettings();
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
Components.utils.import('chrome://shumway/content/SpecialInflate.jsm');
|
||||
Components.utils.import('chrome://shumway/content/RtmpUtils.jsm');
|
||||
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
|
||||
|
||||
var externalInterfaceWrapper = {
|
||||
callback: function (call) {
|
||||
@ -32,17 +31,14 @@ var externalInterfaceWrapper = {
|
||||
// control messages between unprivileged content and ShumwayStreamConverter.
|
||||
var shumwayComAdapter;
|
||||
|
||||
function sendMessage(action, data, sync, callbackCookie) {
|
||||
function sendMessage(action, data, sync) {
|
||||
var detail = {action: action, data: data, sync: sync};
|
||||
if (callbackCookie !== undefined) {
|
||||
detail.callback = true;
|
||||
detail.cookie = callbackCookie;
|
||||
}
|
||||
if (!sync) {
|
||||
sendAsyncMessage('Shumway:message', detail);
|
||||
return;
|
||||
}
|
||||
var result = sendSyncMessage('Shumway:message', detail);
|
||||
var result = String(sendSyncMessage('Shumway:message', detail));
|
||||
result = result == 'undefined' ? undefined : JSON.parse(result);
|
||||
return Components.utils.cloneInto(result, content);
|
||||
}
|
||||
|
||||
@ -51,38 +47,17 @@ function enableDebug() {
|
||||
}
|
||||
|
||||
addMessageListener('Shumway:init', function (message) {
|
||||
var environment = message.data;
|
||||
|
||||
sendAsyncMessage('Shumway:running', {}, {
|
||||
externalInterface: externalInterfaceWrapper
|
||||
});
|
||||
|
||||
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
|
||||
// up Xray wrappers.
|
||||
shumwayComAdapter = Components.utils.createObjectIn(content, {defineAs: 'ShumwayCom'});
|
||||
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
|
||||
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
onLoadFileCallback: { value: null, writable: true },
|
||||
onExternalCallback: { value: null, writable: true },
|
||||
onMessageCallback: { value: null, writable: true }
|
||||
shumwayComAdapter = ShumwayCom.createAdapter(content, {
|
||||
sendMessage: sendMessage,
|
||||
enableDebug: enableDebug,
|
||||
getEnvironment: function () { return environment; }
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(shumwayComAdapter);
|
||||
|
||||
// Exposing createSpecialInflate function for DEFLATE stream decoding using
|
||||
// Gecko API.
|
||||
if (SpecialInflateUtils.isSpecialInflateEnabled) {
|
||||
Components.utils.exportFunction(function () {
|
||||
return SpecialInflateUtils.createWrappedSpecialInflate(content);
|
||||
}, content, {defineAs: 'createSpecialInflate'});
|
||||
}
|
||||
|
||||
if (RtmpUtils.isRtmpEnabled) {
|
||||
Components.utils.exportFunction(function (params) {
|
||||
return RtmpUtils.createSocket(content, params);
|
||||
}, content, {defineAs: 'createRtmpSocket'});
|
||||
Components.utils.exportFunction(function () {
|
||||
return RtmpUtils.createXHR(content);
|
||||
}, content, {defineAs: 'createRtmpXHR'});
|
||||
}
|
||||
|
||||
content.wrappedJSObject.runViewer();
|
||||
});
|
||||
@ -93,11 +68,3 @@ addMessageListener('Shumway:loadFile', function (message) {
|
||||
}
|
||||
shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(message.data, content));
|
||||
});
|
||||
|
||||
addMessageListener('Shumway:messageCallback', function (message) {
|
||||
if (!shumwayComAdapter.onMessageCallback) {
|
||||
return;
|
||||
}
|
||||
shumwayComAdapter.onMessageCallback(message.data.cookie,
|
||||
Components.utils.cloneInto(message.data.response, content));
|
||||
});
|
||||
|
@ -14,11 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
window.notifyShumwayMessage = function (detail) { };
|
||||
window.onExternalCallback = null;
|
||||
window.onMessageCallback = null;
|
||||
window.onLoadFileCallback = null;
|
||||
|
||||
var viewer = document.getElementById('viewer'), onLoaded;
|
||||
var promise = new Promise(function (resolve) {
|
||||
onLoaded = resolve;
|
||||
@ -30,63 +25,28 @@ viewer.addEventListener('mozbrowserloadend', function () {
|
||||
onLoaded(true);
|
||||
});
|
||||
|
||||
Components.utils.import('chrome://shumway/content/SpecialInflate.jsm');
|
||||
Components.utils.import('chrome://shumway/content/RtmpUtils.jsm');
|
||||
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
|
||||
|
||||
function runViewer() {
|
||||
function handler() {
|
||||
function sendMessage(action, data, sync, callbackCookie) {
|
||||
var detail = {action: action, data: data, sync: sync};
|
||||
if (callbackCookie !== undefined) {
|
||||
detail.callback = true;
|
||||
detail.cookie = callbackCookie;
|
||||
}
|
||||
var result = window.notifyShumwayMessage(detail);
|
||||
function sendMessage(action, data, sync) {
|
||||
var result = shumwayActions.invoke(action, data);
|
||||
return Components.utils.cloneInto(result, childWindow);
|
||||
}
|
||||
|
||||
var childWindow = viewer.contentWindow.wrappedJSObject;
|
||||
|
||||
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
|
||||
// up Xray wrappers. This allows resending of external interface, clipboard
|
||||
// and other control messages between unprivileged content and
|
||||
// ShumwayStreamConverter.
|
||||
var shumwayComAdapter = Components.utils.createObjectIn(childWindow, {defineAs: 'ShumwayCom'});
|
||||
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
|
||||
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
onLoadFileCallback: { value: null, writable: true },
|
||||
onExternalCallback: { value: null, writable: true },
|
||||
onMessageCallback: { value: null, writable: true }
|
||||
var shumwayComAdapter = ShumwayCom.createAdapter(childWindow, {
|
||||
sendMessage: sendMessage,
|
||||
enableDebug: enableDebug,
|
||||
getEnvironment: getEnvironment,
|
||||
});
|
||||
Components.utils.makeObjectPropsNormal(shumwayComAdapter);
|
||||
|
||||
// Exposing createSpecialInflate function for DEFLATE stream decoding using
|
||||
// Gecko API.
|
||||
if (SpecialInflateUtils.isSpecialInflateEnabled) {
|
||||
Components.utils.exportFunction(function () {
|
||||
return SpecialInflateUtils.createWrappedSpecialInflate(childWindow);
|
||||
}, childWindow, {defineAs: 'createSpecialInflate'});
|
||||
}
|
||||
|
||||
if (RtmpUtils.isRtmpEnabled) {
|
||||
Components.utils.exportFunction(function (params) {
|
||||
return RtmpUtils.createSocket(childWindow, params);
|
||||
}, childWindow, {defineAs: 'createRtmpSocket'});
|
||||
Components.utils.exportFunction(function () {
|
||||
return RtmpUtils.createXHR(childWindow);
|
||||
}, childWindow, {defineAs: 'createRtmpXHR'});
|
||||
}
|
||||
|
||||
window.onExternalCallback = function (call) {
|
||||
shumwayActions.onExternalCallback = function (call) {
|
||||
return shumwayComAdapter.onExternalCallback(Components.utils.cloneInto(call, childWindow));
|
||||
};
|
||||
|
||||
window.onMessageCallback = function (response) {
|
||||
shumwayComAdapter.onMessageCallback(Components.utils.cloneInto(response, childWindow));
|
||||
};
|
||||
|
||||
window.onLoadFileCallback = function (args) {
|
||||
shumwayActions.onLoadFileCallback = function (args) {
|
||||
shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(args, childWindow));
|
||||
};
|
||||
|
||||
@ -105,39 +65,26 @@ function runViewer() {
|
||||
});
|
||||
|
||||
messageManager.addMessageListener('Shumway:message', function (message) {
|
||||
var detail = {
|
||||
action: message.data.action,
|
||||
data: message.data.data,
|
||||
sync: message.data.sync
|
||||
};
|
||||
if (message.data.callback) {
|
||||
detail.callback = true;
|
||||
detail.cookie = message.data.cookie;
|
||||
var data = message.data;
|
||||
var result = shumwayActions.invoke(data.action, data.data);
|
||||
if (message.sync) {
|
||||
return result === undefined ? 'undefined' : JSON.stringify(result);
|
||||
}
|
||||
|
||||
return window.notifyShumwayMessage(detail);
|
||||
});
|
||||
|
||||
messageManager.addMessageListener('Shumway:enableDebug', function (message) {
|
||||
enableDebug();
|
||||
});
|
||||
|
||||
window.onExternalCallback = function (call) {
|
||||
shumwayActions.onExternalCallback = function (call) {
|
||||
return externalInterface.callback(JSON.stringify(call));
|
||||
};
|
||||
|
||||
window.onMessageCallback = function (response) {
|
||||
messageManager.sendAsyncMessage('Shumway:messageCallback', {
|
||||
cookie: response.cookie,
|
||||
response: response.response
|
||||
});
|
||||
};
|
||||
|
||||
window.onLoadFileCallback = function (args) {
|
||||
shumwayActions.onLoadFileCallback = function (args) {
|
||||
messageManager.sendAsyncMessage('Shumway:loadFile', args);
|
||||
};
|
||||
|
||||
messageManager.sendAsyncMessage('Shumway:init', {});
|
||||
messageManager.sendAsyncMessage('Shumway:init', getEnvironment());
|
||||
}
|
||||
|
||||
|
||||
@ -146,22 +93,13 @@ function runViewer() {
|
||||
document.body.className = 'remoteDebug';
|
||||
|
||||
function sendMessage(data) {
|
||||
var detail = {
|
||||
action: data.action,
|
||||
data: data.data,
|
||||
sync: data.sync
|
||||
};
|
||||
if (data.callback) {
|
||||
detail.callback = true;
|
||||
detail.cookie = data.cookie;
|
||||
}
|
||||
return window.notifyShumwayMessage(detail);
|
||||
return shumwayActions.invoke(data.id, data.data);
|
||||
}
|
||||
|
||||
connection.onData = function (data) {
|
||||
switch (data.action) {
|
||||
case 'sendMessage':
|
||||
return sendMessage(data.detail);
|
||||
return sendMessage(data);
|
||||
case 'reload':
|
||||
document.body.className = 'remoteReload';
|
||||
setTimeout(function () {
|
||||
@ -171,15 +109,11 @@ function runViewer() {
|
||||
}
|
||||
};
|
||||
|
||||
window.onExternalCallback = function (call) {
|
||||
shumwayActions.onExternalCallback = function (call) {
|
||||
return connection.send({action: 'onExternalCallback', detail: call});
|
||||
};
|
||||
|
||||
window.onMessageCallback = function (response) {
|
||||
return connection.send({action: 'onMessageCallback', detail: response});
|
||||
};
|
||||
|
||||
window.onLoadFileCallback = function (args) {
|
||||
shumwayActions.onLoadFileCallback = function (args) {
|
||||
if (args.array) {
|
||||
args.array = Array.prototype.slice.call(args.array, 0);
|
||||
}
|
||||
@ -189,16 +123,27 @@ function runViewer() {
|
||||
connection.send({action: 'runViewer'}, true);
|
||||
}
|
||||
|
||||
function getEnvironment() {
|
||||
return {
|
||||
swfUrl: window.shumwayStartupInfo.url,
|
||||
privateBrowsing: window.shumwayStartupInfo.privateBrowsing
|
||||
};
|
||||
}
|
||||
|
||||
function enableDebug() {
|
||||
DebugUtils.enableDebug(window.swfUrlLoading);
|
||||
DebugUtils.enableDebug(window.shumwayStartupInfo.url);
|
||||
setTimeout(function () {
|
||||
window.top.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
var startupInfo = window.shumwayStartupInfo;
|
||||
var shumwayActions = ShumwayCom.createActions(startupInfo, window, document);
|
||||
|
||||
promise.then(function (oop) {
|
||||
if (DebugUtils.isEnabled) {
|
||||
DebugUtils.createDebuggerConnection(window.swfUrlLoading).then(function (debuggerConnection) {
|
||||
DebugUtils.createDebuggerConnection(window.shumwayStartupInfo.url).then(
|
||||
function (debuggerConnection) {
|
||||
if (debuggerConnection) {
|
||||
handleDebug(debuggerConnection);
|
||||
} else if (oop) {
|
||||
|
@ -95,7 +95,11 @@ function allowedPlatformForMedia() {
|
||||
}
|
||||
|
||||
var ShumwayBootstrapUtils = {
|
||||
isRegistered: false,
|
||||
|
||||
register: function () {
|
||||
this.isRegistered = true;
|
||||
|
||||
// Register the components.
|
||||
converterFactory.register(ShumwayStreamConverter);
|
||||
overlayConverterFactory.register(ShumwayStreamOverlayConverter);
|
||||
@ -117,6 +121,8 @@ var ShumwayBootstrapUtils = {
|
||||
},
|
||||
|
||||
unregister: function () {
|
||||
this.isRegistered = false;
|
||||
|
||||
// Remove the contract/component.
|
||||
converterFactory.unregister();
|
||||
overlayConverterFactory.unregister();
|
||||
|
@ -30,30 +30,16 @@ const EXPECTED_PLAYPREVIEW_URI_PREFIX = 'data:application/x-moz-playpreview;,' +
|
||||
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
|
||||
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
|
||||
|
||||
const MAX_CLIPBOARD_DATA_SIZE = 8000;
|
||||
const MAX_USER_INPUT_TIMEOUT = 250; // ms
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
Cu.import('resource://gre/modules/Promise.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
|
||||
'resource://gre/modules/PrivateBrowsingUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'AddonManager',
|
||||
'resource://gre/modules/AddonManager.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
|
||||
'resource://shumway/ShumwayTelemetry.jsm');
|
||||
|
||||
let Svc = {};
|
||||
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
|
||||
'@mozilla.org/mime;1', 'nsIMIMEService');
|
||||
|
||||
let StringInputStream = Cc["@mozilla.org/io/string-input-stream;1"];
|
||||
let MimeInputStream = Cc["@mozilla.org/network/mime-input-stream;1"];
|
||||
|
||||
function getBoolPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(pref);
|
||||
@ -62,14 +48,6 @@ function getBoolPref(pref, def) {
|
||||
}
|
||||
}
|
||||
|
||||
function getStringPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getComplexValue(pref, Ci.nsISupportsString).data;
|
||||
} catch (ex) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
function log(aMsg) {
|
||||
let msg = 'ShumwayStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
|
||||
Services.console.logStringMessage(msg);
|
||||
@ -83,22 +61,6 @@ function getDOMWindow(aChannel) {
|
||||
return win;
|
||||
}
|
||||
|
||||
function makeContentReadable(obj, window) {
|
||||
if (Cu.cloneInto) {
|
||||
return Cu.cloneInto(obj, window);
|
||||
}
|
||||
// TODO remove for Firefox 32+
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
var expose = {};
|
||||
for (let k in obj) {
|
||||
expose[k] = "rw";
|
||||
}
|
||||
obj.__exposedProps__ = expose;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function parseQueryString(qs) {
|
||||
if (!qs)
|
||||
return {};
|
||||
@ -117,73 +79,6 @@ function parseQueryString(qs) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function domainMatches(host, pattern) {
|
||||
if (!pattern) return false;
|
||||
if (pattern === '*') return true;
|
||||
host = host.toLowerCase();
|
||||
var parts = pattern.toLowerCase().split('*');
|
||||
if (host.indexOf(parts[0]) !== 0) return false;
|
||||
var p = parts[0].length;
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
var j = host.indexOf(parts[i], p);
|
||||
if (j === -1) return false;
|
||||
p = j + parts[i].length;
|
||||
}
|
||||
return parts[parts.length - 1] === '' || p === host.length;
|
||||
}
|
||||
|
||||
function fetchPolicyFile(url, cache, callback) {
|
||||
if (url in cache) {
|
||||
return callback(cache[url]);
|
||||
}
|
||||
|
||||
log('Fetching policy file at ' + url);
|
||||
var MAX_POLICY_SIZE = 8192;
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open('GET', url, true);
|
||||
xhr.overrideMimeType('text/xml');
|
||||
xhr.onprogress = function (e) {
|
||||
if (e.loaded >= MAX_POLICY_SIZE) {
|
||||
xhr.abort();
|
||||
cache[url] = false;
|
||||
callback(null, 'Max policy size');
|
||||
}
|
||||
};
|
||||
xhr.onreadystatechange = function(event) {
|
||||
if (xhr.readyState === 4) {
|
||||
// TODO disable redirects
|
||||
var doc = xhr.responseXML;
|
||||
if (xhr.status !== 200 || !doc) {
|
||||
cache[url] = false;
|
||||
return callback(null, 'Invalid HTTP status: ' + xhr.statusText);
|
||||
}
|
||||
// parsing params
|
||||
var params = doc.documentElement.childNodes;
|
||||
var policy = { siteControl: null, allowAccessFrom: []};
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
switch (params[i].localName) {
|
||||
case 'site-control':
|
||||
policy.siteControl = params[i].getAttribute('permitted-cross-domain-policies');
|
||||
break;
|
||||
case 'allow-access-from':
|
||||
var access = {
|
||||
domain: params[i].getAttribute('domain'),
|
||||
security: params[i].getAttribute('security') === 'true'
|
||||
};
|
||||
policy.allowAccessFrom.push(access);
|
||||
break;
|
||||
default:
|
||||
// TODO allow-http-request-headers-from and other
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(cache[url] = policy);
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function isContentWindowPrivate(win) {
|
||||
if (!('isContentWindowPrivate' in PrivateBrowsingUtils)) {
|
||||
return PrivateBrowsingUtils.isWindowPrivate(win);
|
||||
@ -191,19 +86,19 @@ function isContentWindowPrivate(win) {
|
||||
return PrivateBrowsingUtils.isContentWindowPrivate(win);
|
||||
}
|
||||
|
||||
function isShumwayEnabledFor(actions) {
|
||||
function isShumwayEnabledFor(startupInfo) {
|
||||
// disabled for PrivateBrowsing windows
|
||||
if (isContentWindowPrivate(actions.window) &&
|
||||
if (isContentWindowPrivate(startupInfo.window) &&
|
||||
!getBoolPref('shumway.enableForPrivate', false)) {
|
||||
return false;
|
||||
}
|
||||
// disabled if embed tag specifies shumwaymode (for testing purpose)
|
||||
if (actions.objectParams['shumwaymode'] === 'off') {
|
||||
if (startupInfo.objectParams['shumwaymode'] === 'off') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var url = actions.url;
|
||||
var baseUrl = actions.baseUrl;
|
||||
var url = startupInfo.url;
|
||||
var baseUrl = startupInfo.baseUrl;
|
||||
|
||||
// blacklisting well known sites with issues
|
||||
if (/\.ytimg\.com\//i.test(url) /* youtube movies */ ||
|
||||
@ -217,458 +112,44 @@ function isShumwayEnabledFor(actions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getVersionInfo() {
|
||||
var deferred = Promise.defer();
|
||||
var versionInfo = {
|
||||
geckoVersion: 'unknown',
|
||||
geckoBuildID: 'unknown',
|
||||
shumwayVersion: 'unknown'
|
||||
};
|
||||
try {
|
||||
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Components.interfaces.nsIXULAppInfo);
|
||||
versionInfo.geckoVersion = appInfo.version;
|
||||
versionInfo.geckoBuildID = appInfo.appBuildID;
|
||||
} catch (e) {
|
||||
log('Error encountered while getting platform version info: ' + e);
|
||||
}
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open('GET', 'resource://shumway/version.txt', true);
|
||||
xhr.overrideMimeType('text/plain');
|
||||
xhr.onload = function () {
|
||||
try {
|
||||
// Trying to merge version.txt lines into something like:
|
||||
// "version (sha) details"
|
||||
var lines = xhr.responseText.split(/\n/g);
|
||||
lines[1] = '(' + lines[1] + ')';
|
||||
versionInfo.shumwayVersion = lines.join(' ');
|
||||
} catch (e) {
|
||||
log('Error while parsing version info: ' + e);
|
||||
}
|
||||
deferred.resolve(versionInfo);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
log('Error while reading version info: ' + xhr.error);
|
||||
deferred.resolve(versionInfo);
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function fallbackToNativePlugin(window, userAction, activateCTP) {
|
||||
var obj = window.frameElement;
|
||||
var doc = obj.ownerDocument;
|
||||
var e = doc.createEvent("CustomEvent");
|
||||
e.initCustomEvent("MozPlayPlugin", true, true, activateCTP);
|
||||
obj.dispatchEvent(e);
|
||||
|
||||
ShumwayTelemetry.onFallback(userAction);
|
||||
}
|
||||
|
||||
// All the priviledged actions.
|
||||
function ChromeActions(url, window, document) {
|
||||
this.url = url;
|
||||
this.objectParams = null;
|
||||
this.movieParams = null;
|
||||
this.baseUrl = url;
|
||||
this.isOverlay = false;
|
||||
this.isPausedAtStart = false;
|
||||
this.window = window;
|
||||
this.document = document;
|
||||
this.externalComInitialized = false;
|
||||
this.allowScriptAccess = false;
|
||||
this.lastUserInput = 0;
|
||||
this.crossdomainRequestsCache = Object.create(null);
|
||||
this.telemetry = {
|
||||
startTime: Date.now(),
|
||||
features: [],
|
||||
errors: [],
|
||||
pageIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
ChromeActions.prototype = {
|
||||
getBoolPref: function (data) {
|
||||
if (!/^shumway\./.test(data.pref)) {
|
||||
return null;
|
||||
}
|
||||
return getBoolPref(data.pref, data.def);
|
||||
},
|
||||
getCompilerSettings: function getCompilerSettings() {
|
||||
return {
|
||||
appCompiler: getBoolPref('shumway.appCompiler', true),
|
||||
sysCompiler: getBoolPref('shumway.sysCompiler', false),
|
||||
verifier: getBoolPref('shumway.verifier', true)
|
||||
};
|
||||
},
|
||||
addProfilerMarker: function (marker) {
|
||||
if ('nsIProfiler' in Ci) {
|
||||
let profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
|
||||
profiler.AddMarker(marker);
|
||||
}
|
||||
},
|
||||
getPluginParams: function getPluginParams() {
|
||||
return {
|
||||
url: this.url,
|
||||
baseUrl : this.baseUrl,
|
||||
movieParams: this.movieParams,
|
||||
objectParams: this.objectParams,
|
||||
isOverlay: this.isOverlay,
|
||||
isPausedAtStart: this.isPausedAtStart
|
||||
};
|
||||
},
|
||||
_canDownloadFile: function canDownloadFile(data, callback) {
|
||||
var url = data.url, checkPolicyFile = data.checkPolicyFile;
|
||||
|
||||
// TODO flash cross-origin request
|
||||
if (url === this.url) {
|
||||
// allow downloading for the original file
|
||||
return callback({success: true});
|
||||
}
|
||||
|
||||
// allows downloading from the same origin
|
||||
var parsedUrl, parsedBaseUrl;
|
||||
try {
|
||||
parsedUrl = NetUtil.newURI(url);
|
||||
} catch (ex) { /* skipping invalid urls */ }
|
||||
try {
|
||||
parsedBaseUrl = NetUtil.newURI(this.url);
|
||||
} catch (ex) { /* skipping invalid urls */ }
|
||||
|
||||
if (parsedUrl && parsedBaseUrl &&
|
||||
parsedUrl.prePath === parsedBaseUrl.prePath) {
|
||||
return callback({success: true});
|
||||
}
|
||||
|
||||
// additionally using internal whitelist
|
||||
var whitelist = getStringPref('shumway.whitelist', '');
|
||||
if (whitelist && parsedUrl) {
|
||||
var whitelisted = whitelist.split(',').some(function (i) {
|
||||
return domainMatches(parsedUrl.host, i);
|
||||
});
|
||||
if (whitelisted) {
|
||||
return callback({success: true});
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkPolicyFile || !parsedUrl || !parsedBaseUrl) {
|
||||
return callback({success: false});
|
||||
}
|
||||
|
||||
// we can request crossdomain.xml
|
||||
fetchPolicyFile(parsedUrl.prePath + '/crossdomain.xml', this.crossdomainRequestsCache,
|
||||
function (policy, error) {
|
||||
|
||||
if (!policy || policy.siteControl === 'none') {
|
||||
return callback({success: false});
|
||||
}
|
||||
// TODO assuming master-only, there are also 'by-content-type', 'all', etc.
|
||||
|
||||
var allowed = policy.allowAccessFrom.some(function (i) {
|
||||
return domainMatches(parsedBaseUrl.host, i.domain) &&
|
||||
(!i.secure || parsedBaseUrl.scheme.toLowerCase() === 'https');
|
||||
});
|
||||
return callback({success: allowed});
|
||||
}.bind(this));
|
||||
},
|
||||
loadFile: function loadFile(data) {
|
||||
function notifyLoadFileListener(data) {
|
||||
if (!win.wrappedJSObject.onLoadFileCallback) {
|
||||
return;
|
||||
}
|
||||
win.wrappedJSObject.onLoadFileCallback(data);
|
||||
}
|
||||
|
||||
var url = data.url;
|
||||
var checkPolicyFile = data.checkPolicyFile;
|
||||
var sessionId = data.sessionId;
|
||||
var limit = data.limit || 0;
|
||||
var method = data.method || "GET";
|
||||
var mimeType = data.mimeType;
|
||||
var postData = data.postData || null;
|
||||
|
||||
var win = this.window;
|
||||
var baseUrl = this.baseUrl;
|
||||
|
||||
var performXHR = function () {
|
||||
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
xhr.open(method, url, true);
|
||||
xhr.responseType = "moz-chunked-arraybuffer";
|
||||
|
||||
if (baseUrl) {
|
||||
// Setting the referer uri, some site doing checks if swf is embedded
|
||||
// on the original page.
|
||||
xhr.setRequestHeader("Referer", baseUrl);
|
||||
}
|
||||
|
||||
// TODO apply range request headers if limit is specified
|
||||
|
||||
var lastPosition = 0;
|
||||
xhr.onprogress = function (e) {
|
||||
var position = e.loaded;
|
||||
var data = new Uint8Array(xhr.response);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId,
|
||||
topic: "progress", array: data, loaded: position, total: e.total});
|
||||
lastPosition = position;
|
||||
if (limit && e.total >= limit) {
|
||||
xhr.abort();
|
||||
}
|
||||
};
|
||||
xhr.onreadystatechange = function(event) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200 && xhr.status !== 0) {
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error", error: xhr.statusText});
|
||||
}
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "close"});
|
||||
}
|
||||
};
|
||||
if (mimeType)
|
||||
xhr.setRequestHeader("Content-Type", mimeType);
|
||||
xhr.send(postData);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "open"});
|
||||
};
|
||||
|
||||
this._canDownloadFile({url: url, checkPolicyFile: checkPolicyFile}, function (data) {
|
||||
if (data.success) {
|
||||
performXHR();
|
||||
} else {
|
||||
log("data access id prohibited to " + url + " from " + baseUrl);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId, topic: "error",
|
||||
error: "only original swf file or file from the same origin loading supported"});
|
||||
}
|
||||
});
|
||||
},
|
||||
navigateTo: function (data) {
|
||||
var embedTag = this.embedTag.wrappedJSObject;
|
||||
var window = embedTag ? embedTag.ownerDocument.defaultView : this.window;
|
||||
window.open(data.url, data.target || '_self');
|
||||
},
|
||||
fallback: function(automatic) {
|
||||
automatic = !!automatic;
|
||||
fallbackToNativePlugin(this.window, !automatic, automatic);
|
||||
},
|
||||
userInput: function() {
|
||||
var win = this.window;
|
||||
var winUtils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (winUtils.isHandlingUserInput) {
|
||||
this.lastUserInput = Date.now();
|
||||
}
|
||||
},
|
||||
isUserInputInProgress: function () {
|
||||
// TODO userInput does not work for OOP
|
||||
if (!getBoolPref('shumway.userInputSecurity', true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't trust our Shumway non-privileged code just yet to verify the
|
||||
// user input -- using userInput function above to track that.
|
||||
if ((Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
// TODO other security checks?
|
||||
return true;
|
||||
},
|
||||
setClipboard: function (data) {
|
||||
if (typeof data !== 'string' || !this.isUserInputInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(data);
|
||||
},
|
||||
setFullscreen: function (enabled) {
|
||||
enabled = !!enabled;
|
||||
|
||||
if (!this.isUserInputInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var target = this.embedTag || this.document.body;
|
||||
if (enabled) {
|
||||
target.mozRequestFullScreen();
|
||||
} else {
|
||||
target.ownerDocument.mozCancelFullScreen();
|
||||
}
|
||||
},
|
||||
endActivation: function () {
|
||||
if (ActivationQueue.currentNonActive === this) {
|
||||
ActivationQueue.activateNext();
|
||||
}
|
||||
},
|
||||
reportTelemetry: function (data) {
|
||||
var topic = data.topic;
|
||||
switch (topic) {
|
||||
case 'firstFrame':
|
||||
var time = Date.now() - this.telemetry.startTime;
|
||||
ShumwayTelemetry.onFirstFrame(time);
|
||||
break;
|
||||
case 'parseInfo':
|
||||
ShumwayTelemetry.onParseInfo({
|
||||
parseTime: +data.parseTime,
|
||||
size: +data.bytesTotal,
|
||||
swfVersion: data.swfVersion|0,
|
||||
frameRate: +data.frameRate,
|
||||
width: data.width|0,
|
||||
height: data.height|0,
|
||||
bannerType: data.bannerType|0,
|
||||
isAvm2: !!data.isAvm2
|
||||
});
|
||||
break;
|
||||
case 'feature':
|
||||
var featureType = data.feature|0;
|
||||
var MIN_FEATURE_TYPE = 0, MAX_FEATURE_TYPE = 999;
|
||||
if (featureType >= MIN_FEATURE_TYPE && featureType <= MAX_FEATURE_TYPE &&
|
||||
!this.telemetry.features[featureType]) {
|
||||
this.telemetry.features[featureType] = true; // record only one feature per SWF
|
||||
ShumwayTelemetry.onFeature(featureType);
|
||||
}
|
||||
break;
|
||||
case 'error':
|
||||
var errorType = data.error|0;
|
||||
var MIN_ERROR_TYPE = 0, MAX_ERROR_TYPE = 2;
|
||||
if (errorType >= MIN_ERROR_TYPE && errorType <= MAX_ERROR_TYPE &&
|
||||
!this.telemetry.errors[errorType]) {
|
||||
this.telemetry.errors[errorType] = true; // record only one report per SWF
|
||||
ShumwayTelemetry.onError(errorType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
reportIssue: function(exceptions) {
|
||||
var urlTemplate = "https://bugzilla.mozilla.org/enter_bug.cgi?op_sys=All&priority=--" +
|
||||
"&rep_platform=All&target_milestone=---&version=Trunk&product=Firefox" +
|
||||
"&component=Shumway&short_desc=&comment={comment}" +
|
||||
"&bug_file_loc={url}";
|
||||
var windowUrl = this.window.parent.wrappedJSObject.location + '';
|
||||
var url = urlTemplate.split('{url}').join(encodeURIComponent(windowUrl));
|
||||
var params = {
|
||||
swf: encodeURIComponent(this.url)
|
||||
};
|
||||
getVersionInfo().then(function (versions) {
|
||||
params.versions = versions;
|
||||
}).then(function () {
|
||||
var ffbuild = params.versions.geckoVersion + ' (' + params.versions.geckoBuildID + ')';
|
||||
//params.exceptions = encodeURIComponent(exceptions);
|
||||
var comment = '+++ Initially filed via the problem reporting functionality in Shumway +++\n' +
|
||||
'Please add any further information that you deem helpful here:\n\n\n\n' +
|
||||
'----------------------\n\n' +
|
||||
'Technical Information:\n' +
|
||||
'Firefox version: ' + ffbuild + '\n' +
|
||||
'Shumway version: ' + params.versions.shumwayVersion;
|
||||
url = url.split('{comment}').join(encodeURIComponent(comment));
|
||||
this.window.open(url);
|
||||
}.bind(this));
|
||||
},
|
||||
externalCom: function (data) {
|
||||
if (!this.allowScriptAccess)
|
||||
return;
|
||||
|
||||
// TODO check security ?
|
||||
var parentWindow = this.window.parent.wrappedJSObject;
|
||||
var embedTag = this.embedTag.wrappedJSObject;
|
||||
switch (data.action) {
|
||||
case 'init':
|
||||
if (this.externalComInitialized)
|
||||
return;
|
||||
|
||||
this.externalComInitialized = true;
|
||||
initExternalCom(parentWindow, embedTag, this.window);
|
||||
return;
|
||||
case 'getId':
|
||||
return embedTag.id;
|
||||
case 'eval':
|
||||
return parentWindow.__flash__eval(data.expression);
|
||||
case 'call':
|
||||
return parentWindow.__flash__call(data.request);
|
||||
case 'register':
|
||||
return embedTag.__flash__registerCallback(data.functionName);
|
||||
case 'unregister':
|
||||
return embedTag.__flash__unregisterCallback(data.functionName);
|
||||
}
|
||||
},
|
||||
getWindowUrl: function() {
|
||||
return this.window.parent.wrappedJSObject.location + '';
|
||||
}
|
||||
};
|
||||
|
||||
// Event listener to trigger chrome privedged code.
|
||||
function RequestListener(actions) {
|
||||
this.actions = actions;
|
||||
}
|
||||
// Receive an event and synchronously or asynchronously responds.
|
||||
RequestListener.prototype.receive = function(detail) {
|
||||
var action = detail.action;
|
||||
var data = detail.data;
|
||||
var sync = detail.sync;
|
||||
var actions = this.actions;
|
||||
if (!(action in actions)) {
|
||||
log('Unknown action: ' + action);
|
||||
return;
|
||||
}
|
||||
if (sync) {
|
||||
var response = actions[action].call(this.actions, data);
|
||||
return response === undefined ? undefined : JSON.stringify(response);
|
||||
}
|
||||
|
||||
var responseCallback;
|
||||
if (detail.callback) {
|
||||
var cookie = detail.cookie;
|
||||
response = function sendResponse(response) {
|
||||
var win = actions.window;
|
||||
if (win.wrappedJSObject.onMessageCallback) {
|
||||
win.wrappedJSObject.onMessageCallback({
|
||||
response: response === undefined ? undefined : JSON.stringify(response),
|
||||
cookie: cookie
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
actions[action].call(this.actions, data, responseCallback);
|
||||
};
|
||||
|
||||
var ActivationQueue = {
|
||||
nonActive: [],
|
||||
initializing: -1,
|
||||
activationTimeout: null,
|
||||
get currentNonActive() {
|
||||
return this.nonActive[this.initializing];
|
||||
return this.nonActive[this.initializing].startupInfo;
|
||||
},
|
||||
enqueue: function ActivationQueue_enqueue(actions) {
|
||||
this.nonActive.push(actions);
|
||||
enqueue: function ActivationQueue_enqueue(startupInfo, callback) {
|
||||
this.nonActive.push({startupInfo: startupInfo, callback: callback});
|
||||
if (this.nonActive.length === 1) {
|
||||
this.activateNext();
|
||||
}
|
||||
},
|
||||
findLastOnPage: function ActivationQueue_findLastOnPage(baseUrl) {
|
||||
for (var i = this.nonActive.length - 1; i >= 0; i--) {
|
||||
if (this.nonActive[i].baseUrl === baseUrl) {
|
||||
return this.nonActive[i];
|
||||
if (this.nonActive[i].startupInfo.baseUrl === baseUrl) {
|
||||
return this.nonActive[i].startupInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
activateNext: function ActivationQueue_activateNext() {
|
||||
function weightInstance(actions) {
|
||||
function weightInstance(startupInfo) {
|
||||
// set of heuristics for find the most important instance to load
|
||||
var weight = 0;
|
||||
// using linear distance to the top-left of the view area
|
||||
if (actions.embedTag) {
|
||||
var window = actions.window;
|
||||
var clientRect = actions.embedTag.getBoundingClientRect();
|
||||
if (startupInfo.embedTag) {
|
||||
var window = startupInfo.window;
|
||||
var clientRect = startupInfo.embedTag.getBoundingClientRect();
|
||||
weight -= Math.abs(clientRect.left - window.scrollX) +
|
||||
Math.abs(clientRect.top - window.scrollY);
|
||||
}
|
||||
var doc = actions.document;
|
||||
var doc = startupInfo.window.document;
|
||||
if (!doc.hidden) {
|
||||
weight += 100000; // might not be that important if hidden
|
||||
}
|
||||
if (actions.embedTag &&
|
||||
actions.embedTag.ownerDocument.hasFocus()) {
|
||||
if (startupInfo.embedTag &&
|
||||
startupInfo.embedTag.ownerDocument.hasFocus()) {
|
||||
weight += 10000; // parent document is focused
|
||||
}
|
||||
return weight;
|
||||
@ -685,7 +166,7 @@ var ActivationQueue = {
|
||||
var weights = [];
|
||||
for (var i = 0; i < this.nonActive.length; i++) {
|
||||
try {
|
||||
var weight = weightInstance(this.nonActive[i]);
|
||||
var weight = weightInstance(this.nonActive[i].startupInfo);
|
||||
weights.push(weight);
|
||||
} catch (ex) {
|
||||
// unable to calc weight the instance, removing
|
||||
@ -711,7 +192,7 @@ var ActivationQueue = {
|
||||
}
|
||||
try {
|
||||
this.initializing = maxWeightIndex;
|
||||
this.nonActive[maxWeightIndex].activationCallback();
|
||||
this.nonActive[maxWeightIndex].callback();
|
||||
break;
|
||||
} catch (ex) {
|
||||
// unable to initialize the instance, trying another one
|
||||
@ -730,30 +211,8 @@ var ActivationQueue = {
|
||||
}
|
||||
};
|
||||
|
||||
function activateShumwayScripts(window, requestListener) {
|
||||
function loadScripts(scripts, callback) {
|
||||
function loadScript(i) {
|
||||
if (i >= scripts.length) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var script = document.createElement('script');
|
||||
script.type = "text/javascript";
|
||||
script.src = scripts[i];
|
||||
script.onload = function () {
|
||||
loadScript(i + 1);
|
||||
};
|
||||
head.appendChild(script);
|
||||
}
|
||||
var document = window.document.wrappedJSObject;
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
loadScript(0);
|
||||
}
|
||||
|
||||
function activateShumwayScripts(window) {
|
||||
function initScripts() {
|
||||
window.wrappedJSObject.notifyShumwayMessage = function () {
|
||||
return requestListener.receive.apply(requestListener, arguments);
|
||||
};
|
||||
window.wrappedJSObject.runViewer();
|
||||
|
||||
var parentWindow = window.parent;
|
||||
@ -807,9 +266,23 @@ function activateShumwayScripts(window, requestListener) {
|
||||
viewerWindow.dispatchEvent(event);
|
||||
}
|
||||
|
||||
if (viewerWindow) {
|
||||
viewerWindow.addEventListener('mousedown', activate, true);
|
||||
}
|
||||
|
||||
window.addEventListener('shumwayFallback', function (e) {
|
||||
var automatic = !!e.detail.automatic;
|
||||
fallbackToNativePlugin(window, !automatic, automatic);
|
||||
});
|
||||
|
||||
window.addEventListener('shumwayActivated', function (e) {
|
||||
if (ActivationQueue.currentNonActive &&
|
||||
ActivationQueue.currentNonActive.window === window) {
|
||||
ActivationQueue.activateNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (window.document.readyState === "interactive" ||
|
||||
window.document.readyState === "complete") {
|
||||
initScripts();
|
||||
@ -818,68 +291,14 @@ function activateShumwayScripts(window, requestListener) {
|
||||
}
|
||||
}
|
||||
|
||||
function initExternalCom(wrappedWindow, wrappedObject, targetWindow) {
|
||||
var traceExternalInterface = getBoolPref('shumway.externalInterface.trace', false);
|
||||
if (!wrappedWindow.__flash__initialized) {
|
||||
wrappedWindow.__flash__initialized = true;
|
||||
wrappedWindow.__flash__toXML = function __flash__toXML(obj) {
|
||||
switch (typeof obj) {
|
||||
case 'boolean':
|
||||
return obj ? '<true/>' : '<false/>';
|
||||
case 'number':
|
||||
return '<number>' + obj + '</number>';
|
||||
case 'object':
|
||||
if (obj === null) {
|
||||
return '<null/>';
|
||||
}
|
||||
if ('hasOwnProperty' in obj && obj.hasOwnProperty('length')) {
|
||||
// array
|
||||
var xml = '<array>';
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
xml += '<property id="' + i + '">' + __flash__toXML(obj[i]) + '</property>';
|
||||
}
|
||||
return xml + '</array>';
|
||||
}
|
||||
var xml = '<object>';
|
||||
for (var i in obj) {
|
||||
xml += '<property id="' + i + '">' + __flash__toXML(obj[i]) + '</property>';
|
||||
}
|
||||
return xml + '</object>';
|
||||
case 'string':
|
||||
return '<string>' + obj.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') + '</string>';
|
||||
case 'undefined':
|
||||
return '<undefined/>';
|
||||
}
|
||||
};
|
||||
wrappedWindow.__flash__eval = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__eval: ' + expr);
|
||||
// allowScriptAccess protects page from unwanted swf scripts,
|
||||
// we can execute script in the page context without restrictions.
|
||||
var result = this.eval(expr);
|
||||
traceExternalInterface && this.console.log('__flash__eval (result): ' + result);
|
||||
return result;
|
||||
}.bind(wrappedWindow);
|
||||
wrappedWindow.__flash__call = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__call (ignored): ' + expr);
|
||||
};
|
||||
}
|
||||
wrappedObject.__flash__registerCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__registerCallback: ' + functionName);
|
||||
Components.utils.exportFunction(function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn: ' + functionName);
|
||||
var result;
|
||||
if (targetWindow.wrappedJSObject.onExternalCallback) {
|
||||
result = targetWindow.wrappedJSObject.onExternalCallback({functionName: functionName, args: args});
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn (result): ' + result);
|
||||
}
|
||||
return wrappedWindow.eval(result);
|
||||
}, this, { defineAs: functionName });
|
||||
};
|
||||
wrappedObject.__flash__unregisterCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__unregisterCallback: ' + functionName);
|
||||
delete this[functionName];
|
||||
};
|
||||
function fallbackToNativePlugin(window, userAction, activateCTP) {
|
||||
var obj = window.frameElement;
|
||||
var doc = obj.ownerDocument;
|
||||
var e = doc.createEvent("CustomEvent");
|
||||
e.initCustomEvent("MozPlayPlugin", true, true, activateCTP);
|
||||
obj.dispatchEvent(e);
|
||||
|
||||
ShumwayTelemetry.onFallback(userAction);
|
||||
}
|
||||
|
||||
function ShumwayStreamConverterBase() {
|
||||
@ -912,7 +331,7 @@ ShumwayStreamConverterBase.prototype = {
|
||||
return requestUrl.spec;
|
||||
},
|
||||
|
||||
createChromeActions: function(window, document, urlHint) {
|
||||
getStartupInfo: function(window, urlHint) {
|
||||
var url = urlHint;
|
||||
var baseUrl;
|
||||
var pageUrl;
|
||||
@ -1018,15 +437,19 @@ ShumwayStreamConverterBase.prototype = {
|
||||
break;
|
||||
}
|
||||
|
||||
var actions = new ChromeActions(url, window, document);
|
||||
actions.objectParams = objectParams;
|
||||
actions.movieParams = movieParams;
|
||||
actions.baseUrl = baseUrl || url;
|
||||
actions.isOverlay = isOverlay;
|
||||
actions.embedTag = element;
|
||||
actions.isPausedAtStart = /\bpaused=true$/.test(urlHint);
|
||||
actions.allowScriptAccess = allowScriptAccess;
|
||||
return actions;
|
||||
var startupInfo = {};
|
||||
startupInfo.window = window;
|
||||
startupInfo.url = url;
|
||||
startupInfo.privateBrowsing = isContentWindowPrivate(window);
|
||||
startupInfo.objectParams = objectParams;
|
||||
startupInfo.movieParams = movieParams;
|
||||
startupInfo.baseUrl = baseUrl || url;
|
||||
startupInfo.isOverlay = isOverlay;
|
||||
startupInfo.embedTag = element;
|
||||
startupInfo.isPausedAtStart = /\bpaused=true$/.test(urlHint);
|
||||
startupInfo.allowScriptAccess = allowScriptAccess;
|
||||
startupInfo.pageIndex = 0;
|
||||
return startupInfo;
|
||||
},
|
||||
|
||||
// nsIStreamConverter::asyncConvertData
|
||||
@ -1078,35 +501,29 @@ ShumwayStreamConverterBase.prototype = {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
var domWindow = getDOMWindow(channel);
|
||||
let actions = converter.createChromeActions(domWindow,
|
||||
domWindow.document,
|
||||
let startupInfo = converter.getStartupInfo(domWindow,
|
||||
converter.getUrlHint(originalURI));
|
||||
|
||||
if (!isShumwayEnabledFor(actions)) {
|
||||
if (!isShumwayEnabledFor(startupInfo)) {
|
||||
fallbackToNativePlugin(domWindow, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
domWindow.swfUrlLoading = actions.url;
|
||||
domWindow.shumwayStartupInfo = startupInfo;
|
||||
|
||||
// Report telemetry on amount of swfs on the page
|
||||
if (actions.isOverlay) {
|
||||
if (startupInfo.isOverlay) {
|
||||
// Looking for last actions with same baseUrl
|
||||
var prevPageActions = ActivationQueue.findLastOnPage(actions.baseUrl);
|
||||
var pageIndex = !prevPageActions ? 1 : (prevPageActions.telemetry.pageIndex + 1);
|
||||
actions.telemetry.pageIndex = pageIndex;
|
||||
var prevPageStartupInfo = ActivationQueue.findLastOnPage(startupInfo.baseUrl);
|
||||
var pageIndex = !prevPageStartupInfo ? 1 : (prevPageStartupInfo.pageIndex + 1);
|
||||
startupInfo.pageIndex = pageIndex;
|
||||
ShumwayTelemetry.onPageIndex(pageIndex);
|
||||
} else {
|
||||
ShumwayTelemetry.onPageIndex(0);
|
||||
}
|
||||
|
||||
let requestListener = new RequestListener(actions);
|
||||
|
||||
actions.activationCallback = function(domWindow, requestListener) {
|
||||
delete this.activationCallback;
|
||||
activateShumwayScripts(domWindow, requestListener);
|
||||
}.bind(actions, domWindow, requestListener);
|
||||
ActivationQueue.enqueue(actions);
|
||||
ActivationQueue.enqueue(startupInfo, function(domWindow) {
|
||||
activateShumwayScripts(domWindow);
|
||||
}.bind(null, domWindow));
|
||||
|
||||
listener.onStopRequest(aRequest, context, statusCode);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,2 @@
|
||||
0.10.182
|
||||
0195a96
|
||||
0.10.225
|
||||
510390b
|
||||
|
@ -99,7 +99,7 @@ limitations under the License.
|
||||
</head>
|
||||
|
||||
<body contextmenu="shumwayMenu">
|
||||
<iframe id="playerWindow" width="9" height="9" src=""></iframe>
|
||||
<iframe id="playerWindow" width="9" height="9" src="" sandbox="allow-scripts"></iframe>
|
||||
<div id="easelContainer"></div>
|
||||
<section>
|
||||
<div id="overlay">
|
||||
|
@ -14,63 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Extension communication object
|
||||
var FirefoxCom = (function FirefoxComClosure() {
|
||||
return {
|
||||
/**
|
||||
* Creates an event that the extension is listening for and will
|
||||
* synchronously respond to.
|
||||
* NOTE: It is recommended to use request() instead since one day we may not
|
||||
* be able to synchronously reply.
|
||||
* @param {String} action The action to trigger.
|
||||
* @param {String} data Optional data to send.
|
||||
* @return {*} The response.
|
||||
*/
|
||||
requestSync: function(action, data) {
|
||||
var result = String(ShumwayCom.sendMessage(action, data, true, undefined));
|
||||
return result !== 'undefined' ? JSON.parse(result) : undefined;
|
||||
},
|
||||
/**
|
||||
* Creates an event that the extension is listening for and will
|
||||
* asynchronously respond by calling the callback.
|
||||
* @param {String} action The action to trigger.
|
||||
* @param {String} data Optional data to send.
|
||||
* @param {Function} callback Optional response callback that will be called
|
||||
* with one data argument.
|
||||
*/
|
||||
request: function(action, data, callback) {
|
||||
var cookie = undefined;
|
||||
if (callback) {
|
||||
cookie = "requestId" + (this._nextRequestId++);
|
||||
|
||||
if (!ShumwayCom.onMessageCallback) {
|
||||
ShumwayCom.onMessageCallback = this._notifyMessageCallback.bind(this);
|
||||
}
|
||||
this._requestCallbacks[cookie] = callback;
|
||||
}
|
||||
ShumwayCom.sendMessage(action, data, false, cookie);
|
||||
},
|
||||
_notifyMessageCallback: function (cookie, response) {
|
||||
var callback = this._requestCallbacks[cookie];
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
delete this._requestCallbacks[cookie];
|
||||
callback(response !== 'undefined' ? JSON.parse(response) : undefined);
|
||||
},
|
||||
_nextRequestId: 1,
|
||||
_requestCallbacks: Object.create(null),
|
||||
initJS: function (callback) {
|
||||
FirefoxCom.request('externalCom', {action: 'init'});
|
||||
ShumwayCom.onExternalCallback = function (call) {
|
||||
return callback(call.functionName, call.args);
|
||||
};
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
function notifyUserInput() {
|
||||
ShumwayCom.sendMessage('userInput', null, true, undefined);
|
||||
ShumwayCom.userInput();
|
||||
}
|
||||
|
||||
document.addEventListener('mousedown', notifyUserInput, true);
|
||||
@ -79,7 +24,7 @@ document.addEventListener('keydown', notifyUserInput, true);
|
||||
document.addEventListener('keyup', notifyUserInput, true);
|
||||
|
||||
function fallback() {
|
||||
FirefoxCom.requestSync('fallback', null)
|
||||
ShumwayCom.fallback();
|
||||
}
|
||||
|
||||
window.print = function(msg) {
|
||||
@ -88,37 +33,23 @@ window.print = function(msg) {
|
||||
|
||||
var SHUMWAY_ROOT = "resource://shumway/";
|
||||
|
||||
var viewerPlayerglobalInfo = {
|
||||
abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs",
|
||||
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
|
||||
};
|
||||
|
||||
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
|
||||
|
||||
var playerWindow;
|
||||
var playerWindowLoaded = new Promise(function(resolve) {
|
||||
var playerWindowIframe = document.getElementById("playerWindow");
|
||||
playerWindowIframe.addEventListener('load', function () {
|
||||
playerWindow = playerWindowIframe.contentWindow;
|
||||
resolve();
|
||||
resolve(playerWindowIframe);
|
||||
});
|
||||
playerWindowIframe.src = 'resource://shumway/web/viewer.player.html';
|
||||
});
|
||||
|
||||
function runViewer() {
|
||||
ShumwayCom.onLoadFileCallback = function (data) {
|
||||
playerWindow.postMessage({
|
||||
type: "loadFileResponse",
|
||||
args: data
|
||||
}, '*');
|
||||
};
|
||||
|
||||
var flashParams = FirefoxCom.requestSync('getPluginParams', null);
|
||||
var flashParams = ShumwayCom.getPluginParams();
|
||||
|
||||
movieUrl = flashParams.url;
|
||||
if (!movieUrl) {
|
||||
console.log("no movie url provided -- stopping here");
|
||||
FirefoxCom.request('endActivation', null);
|
||||
ShumwayCom.endActivation();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -127,6 +58,7 @@ function runViewer() {
|
||||
var baseUrl = flashParams.baseUrl;
|
||||
var isOverlay = flashParams.isOverlay;
|
||||
pauseExecution = flashParams.isPausedAtStart;
|
||||
var isDebuggerEnabled = flashParams.isDebuggerEnabled;
|
||||
|
||||
console.log("url=" + movieUrl + ";params=" + uneval(movieParams));
|
||||
if (movieParams.fmt_list && movieParams.url_encoded_fmt_stream_map) {
|
||||
@ -137,7 +69,8 @@ function runViewer() {
|
||||
}).join(',');
|
||||
}
|
||||
|
||||
playerWindowLoaded.then(function () {
|
||||
playerWindowLoaded.then(function (playerWindowIframe) {
|
||||
ShumwayCom.setupComBridge(playerWindowIframe);
|
||||
parseSwf(movieUrl, baseUrl, movieParams, objectParams);
|
||||
});
|
||||
|
||||
@ -166,8 +99,7 @@ function runViewer() {
|
||||
document.getElementById('aboutMenu').label =
|
||||
document.getElementById('aboutMenu').label.replace('%version%', version);
|
||||
|
||||
var debugMenuEnabled = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.debug.enabled', def: false});
|
||||
if (debugMenuEnabled) {
|
||||
if (isDebuggerEnabled) {
|
||||
document.getElementById('debugMenu').addEventListener('click', enableDebug);
|
||||
} else {
|
||||
document.getElementById('debugMenu').remove();
|
||||
@ -204,8 +136,8 @@ function reportIssue() {
|
||||
// }
|
||||
// entry.count++;
|
||||
//});
|
||||
//FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions));
|
||||
FirefoxCom.requestSync('reportIssue');
|
||||
//ShumwayCom.reportIssue(JSON.stringify(prunedExceptions));
|
||||
ShumwayCom.reportIssue();
|
||||
}
|
||||
|
||||
function showAbout() {
|
||||
@ -221,18 +153,6 @@ var movieUrl, movieParams, objectParams;
|
||||
window.addEventListener("message", function handlerMessage(e) {
|
||||
var args = e.data;
|
||||
switch (args.callback) {
|
||||
case 'loadFileRequest':
|
||||
FirefoxCom.request('loadFile', args.data, null);
|
||||
break;
|
||||
case 'reportTelemetry':
|
||||
FirefoxCom.request('reportTelemetry', args.data, null);
|
||||
break;
|
||||
case 'setClipboard':
|
||||
FirefoxCom.request('setClipboard', args.data, null);
|
||||
break;
|
||||
case 'navigateTo':
|
||||
FirefoxCom.request('navigateTo', args.data, null);
|
||||
break;
|
||||
case 'started':
|
||||
document.body.classList.add('started');
|
||||
break;
|
||||
@ -241,35 +161,19 @@ window.addEventListener("message", function handlerMessage(e) {
|
||||
|
||||
var easelHost;
|
||||
|
||||
function processExternalCommand(command) {
|
||||
switch (command.action) {
|
||||
case 'isEnabled':
|
||||
command.result = true;
|
||||
break;
|
||||
case 'initJS':
|
||||
FirefoxCom.initJS(function (functionName, args) {
|
||||
return easelHost.sendExernalCallback(functionName, args);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
command.result = FirefoxCom.requestSync('externalCom', command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function parseSwf(url, baseUrl, movieParams, objectParams) {
|
||||
var compilerSettings = FirefoxCom.requestSync('getCompilerSettings', null);
|
||||
var settings = ShumwayCom.getSettings();
|
||||
var compilerSettings = settings.compilerSettings;
|
||||
|
||||
// init misc preferences
|
||||
var turboMode = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false});
|
||||
Shumway.GFX.hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false});
|
||||
//forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false});
|
||||
//dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false});
|
||||
var turboMode = settings.playerSettings.turboMode;
|
||||
Shumway.GFX.hud.value = settings.playerSettings.hud;
|
||||
//forceHidpi.value = settings.playerSettings.forceHidpi;
|
||||
|
||||
console.info("Compiler settings: " + JSON.stringify(compilerSettings));
|
||||
console.info("Parsing " + url + "...");
|
||||
function loaded() {
|
||||
FirefoxCom.request('endActivation', null);
|
||||
ShumwayCom.endActivation();
|
||||
}
|
||||
|
||||
var backgroundColor;
|
||||
@ -286,7 +190,6 @@ function parseSwf(url, baseUrl, movieParams, objectParams) {
|
||||
|
||||
var easel = createEasel(backgroundColor);
|
||||
easelHost = new Shumway.GFX.Window.WindowEaselHost(easel, playerWindow, window);
|
||||
easelHost.processExternalCommand = processExternalCommand;
|
||||
|
||||
var displayParameters = easel.getDisplayParameters();
|
||||
var data = {
|
||||
|
@ -20,6 +20,8 @@ limitations under the License.
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<base href=""/>
|
||||
|
||||
// Loading the relooper from the asm.js cache takes 3x as long as compiling it, so no async.
|
||||
<script src="../libs/relooper.js"></script>
|
||||
<script src="../shumway.player.js"></script>
|
||||
<script src="viewerPlayer.js"></script>
|
||||
</head>
|
||||
|
@ -15,14 +15,6 @@
|
||||
*/
|
||||
|
||||
var release = true;
|
||||
var SHUMWAY_ROOT = "resource://shumway/";
|
||||
|
||||
var viewerPlayerglobalInfo = {
|
||||
abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs",
|
||||
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
|
||||
};
|
||||
|
||||
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
|
||||
|
||||
window.print = function(msg) {
|
||||
console.log(msg);
|
||||
@ -42,21 +34,21 @@ function runSwfPlayer(flashParams) {
|
||||
Shumway.frameRateOption.value = flashParams.turboMode ? 60 : -1;
|
||||
Shumway.AVM2.Verifier.enabled.value = compilerSettings.verifier;
|
||||
|
||||
Shumway.createAVM2(builtinPath, viewerPlayerglobalInfo, sysMode, appMode, function (avm2) {
|
||||
Shumway.createAVM2(Shumway.AVM2LoadLibrariesFlags.Builtin | Shumway.AVM2LoadLibrariesFlags.Playerglobal, sysMode, appMode).then(function (avm2) {
|
||||
function runSWF(file, buffer, baseUrl) {
|
||||
var player = new Shumway.Player.Window.WindowPlayer(window, window.parent);
|
||||
var gfxService = new Shumway.Player.Window.WindowGFXService(window, window.parent);
|
||||
var player = new Shumway.Player.Player(gfxService);
|
||||
player.defaultStageColor = flashParams.bgcolor;
|
||||
player.movieParams = flashParams.movieParams;
|
||||
player.stageAlign = (objectParams && (objectParams.salign || objectParams.align)) || '';
|
||||
player.stageScale = (objectParams && objectParams.scale) || 'showall';
|
||||
player.displayParameters = flashParams.displayParameters;
|
||||
|
||||
Shumway.ExternalInterfaceService.instance = player.createExternalInterfaceService();
|
||||
|
||||
player.pageUrl = baseUrl;
|
||||
player.load(file, buffer);
|
||||
}
|
||||
Shumway.FileLoadingService.instance.setBaseUrl(baseUrl);
|
||||
|
||||
Shumway.FileLoadingService.instance.init(baseUrl);
|
||||
if (asyncLoading) {
|
||||
runSWF(movieUrl, undefined, baseUrl);
|
||||
} else {
|
||||
@ -70,100 +62,12 @@ function runSwfPlayer(flashParams) {
|
||||
});
|
||||
}
|
||||
|
||||
var LOADER_WORKER_PATH = SHUMWAY_ROOT + 'web/worker.js';
|
||||
|
||||
function setupServices() {
|
||||
Shumway.Telemetry.instance = {
|
||||
reportTelemetry: function (data) {
|
||||
window.parent.postMessage({
|
||||
callback: 'reportTelemetry',
|
||||
data: data
|
||||
}, '*');
|
||||
}
|
||||
};
|
||||
|
||||
Shumway.ClipboardService.instance = {
|
||||
setClipboard: function (data) {
|
||||
window.parent.postMessage({
|
||||
callback: 'setClipboard',
|
||||
data: data
|
||||
}, '*');
|
||||
}
|
||||
};
|
||||
|
||||
Shumway.FileLoadingService.instance = {
|
||||
baseUrl: null,
|
||||
nextSessionId: 1, // 0 - is reserved
|
||||
sessions: [],
|
||||
createSession: function () {
|
||||
var sessionId = this.nextSessionId++;
|
||||
return this.sessions[sessionId] = {
|
||||
open: function (request) {
|
||||
var self = this;
|
||||
var path = Shumway.FileLoadingService.instance.resolveUrl(request.url);
|
||||
console.log('Session #' + sessionId + ': loading ' + path);
|
||||
window.parent.postMessage({
|
||||
callback: 'loadFileRequest',
|
||||
data: {url: path, method: request.method,
|
||||
mimeType: request.mimeType, postData: request.data,
|
||||
checkPolicyFile: request.checkPolicyFile, sessionId: sessionId}
|
||||
}, '*');
|
||||
},
|
||||
notify: function (args) {
|
||||
switch (args.topic) {
|
||||
case "open":
|
||||
this.onopen();
|
||||
break;
|
||||
case "close":
|
||||
this.onclose();
|
||||
Shumway.FileLoadingService.instance.sessions[sessionId] = null;
|
||||
console.log('Session #' + sessionId + ': closed');
|
||||
break;
|
||||
case "error":
|
||||
this.onerror && this.onerror(args.error);
|
||||
break;
|
||||
case "progress":
|
||||
console.log('Session #' + sessionId + ': loaded ' + args.loaded + '/' + args.total);
|
||||
this.onprogress && this.onprogress(args.array, {bytesLoaded: args.loaded, bytesTotal: args.total});
|
||||
break;
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
if (Shumway.FileLoadingService.instance.sessions[sessionId]) {
|
||||
// TODO send abort
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
setBaseUrl: function (url) {
|
||||
Shumway.FileLoadingService.instance.baseUrl = url;
|
||||
},
|
||||
resolveUrl: function (url) {
|
||||
return new URL(url, Shumway.FileLoadingService.instance.baseUrl).href;
|
||||
},
|
||||
navigateTo: function (url, target) {
|
||||
window.parent.postMessage({
|
||||
callback: 'navigateTo',
|
||||
data: {
|
||||
url: this.resolveUrl(url),
|
||||
target: target
|
||||
}
|
||||
}, '*');
|
||||
}
|
||||
};
|
||||
|
||||
// Using SpecialInflate when chrome code provides it.
|
||||
if (parent.createSpecialInflate) {
|
||||
window.SpecialInflate = function () {
|
||||
return parent.createSpecialInflate();
|
||||
};
|
||||
}
|
||||
|
||||
// Using createRtmpXHR/createRtmpSocket when chrome code provides it.
|
||||
if (parent.createRtmpXHR) {
|
||||
window.createRtmpSocket = parent.createRtmpSocket;
|
||||
window.createRtmpXHR = parent.createRtmpXHR;
|
||||
}
|
||||
Shumway.Telemetry.instance = new Shumway.Player.ShumwayComTelemetryService();
|
||||
Shumway.ExternalInterfaceService.instance = new Shumway.Player.ShumwayComExternalInterface();
|
||||
Shumway.ClipboardService.instance = new Shumway.Player.ShumwayComClipboardService();
|
||||
Shumway.FileLoadingService.instance = new Shumway.Player.ShumwayComFileLoadingService();
|
||||
Shumway.SystemResourcesLoadingService.instance = new Shumway.Player.ShumwayComResourcesLoadingService(true);
|
||||
}
|
||||
|
||||
window.addEventListener('message', function onWindowMessage(e) {
|
||||
@ -172,13 +76,6 @@ window.addEventListener('message', function onWindowMessage(e) {
|
||||
return;
|
||||
}
|
||||
switch (data.type) {
|
||||
case "loadFileResponse":
|
||||
var args = data.args;
|
||||
var session = Shumway.FileLoadingService.instance.sessions[args.sessionId];
|
||||
if (session) {
|
||||
session.notify(args);
|
||||
}
|
||||
break;
|
||||
case "runSwf":
|
||||
if (data.settings) {
|
||||
Shumway.Settings.setSettings(data.settings);
|
||||
|
Loading…
Reference in New Issue
Block a user