Bug 1140508 - "Update Shumway to version 0.10.225". r=till

--HG--
extra : rebase_source : 414e84de6b1a353556419d732c6a19a103e14f4f
This commit is contained in:
Yury Delendik 2015-03-06 11:45:00 +01:00
parent 0421d094b3
commit 6859d8d2da
16 changed files with 16479 additions and 15995 deletions

View File

@ -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;
}
};

View 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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</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];
};
}

View File

@ -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;
}
};

View 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;
}
};

View File

@ -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();

View File

@ -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));
});

View File

@ -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) {

View File

@ -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();

View File

@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</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

View File

@ -1,2 +1,2 @@
0.10.182
0195a96
0.10.225
510390b

View File

@ -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">

View File

@ -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 = {

View File

@ -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>

View File

@ -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);