Bug 1142484 - Update Shumway to version 0.10.268. r=till

This commit is contained in:
Yury Delendik 2015-03-12 07:50:04 -05:00
Родитель b628218278
Коммит 58264b037b
12 изменённых файлов: 6026 добавлений и 5752 удалений

Просмотреть файл

@ -0,0 +1,125 @@
/*
* 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 = ['ExternalInterface'];
Components.utils.import('resource://gre/modules/Services.jsm');
function ExternalInterface(window, embedTag, callback) {
this.window = window;
this.embedTag = embedTag;
this.callback = callback;
this.externalComInitialized = false;
initExternalCom(window, embedTag, callback);
}
ExternalInterface.prototype = {
processAction: function (data) {
var parentWindow = this.window;
var embedTag = this.embedTag;
switch (data.action) {
case 'init':
if (this.externalComInitialized)
return;
this.externalComInitialized = true;
initExternalCom(parentWindow, embedTag, this.callback);
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 getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function initExternalCom(wrappedWindow, wrappedObject, onExternalCallback) {
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 (onExternalCallback) {
result = 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];
};
}

Просмотреть файл

@ -0,0 +1,261 @@
/*
* 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 = ['FileLoader'];
Components.utils.import('resource://gre/modules/Services.jsm');
Components.utils.import('resource://gre/modules/Promise.jsm');
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
Components.utils.import('resource://gre/modules/NetUtil.jsm');
function FileLoader(swfUrl, baseUrl, callback) {
this.swfUrl = swfUrl;
this.baseUrl = baseUrl;
this.callback = callback;
this.crossdomainRequestsCache = Object.create(null);
}
FileLoader.prototype = {
load: function (data) {
function notifyLoadFileListener(data) {
if (!onLoadFileCallback) {
return;
}
onLoadFileCallback(data);
}
var onLoadFileCallback = this.callback;
var crossdomainRequestsCache = this.crossdomainRequestsCache;
var baseUrl = this.baseUrl;
var swfUrl = this.swfUrl;
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 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, swfUrl, 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"});
});
}
};
function log(aMsg) {
let msg = 'FileLoader.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function getStringPref(pref, def) {
try {
return Services.prefs.getComplexValue(pref, Components.interfaces.nsISupportsString).data;
} catch (ex) {
return def;
}
}
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);
}

Просмотреть файл

@ -33,12 +33,6 @@ 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);
@ -48,89 +42,96 @@ var RtmpUtils = {
}
socket.onopen = function () {
if (wrapper.onopen) {
wrapper.onopen.call(wrapper, new sandbox.Object());
if (wrapperOnOpen) {
wrapperOnOpen(new sandbox.Object());
}
};
socket.ondata = function (e) {
if (wrapper.ondata) {
if (wrapperOnData) {
var wrappedE = new sandbox.Object();
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
wrapper.ondata.call(wrapper, wrappedE);
wrapperOnData(wrappedE);
}
};
socket.ondrain = function () {
if (wrapper.ondrain) {
wrapper.ondrain.call(wrapper, new sandbox.Object());
if (wrapperOnDrain) {
wrapperOnDrain(new sandbox.Object());
}
};
socket.onerror = function (e) {
if (wrapper.onerror) {
if (wrapperOnError) {
var wrappedE = new sandbox.Object();
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
wrapper.onerror.call(wrapper, wrappedE);
wrapperOnError(wrappedE);
}
};
socket.onclose = function () {
if (wrapper.onclose) {
wrapper.onclose.call(wrapper, new sandbox.Object());
if (wrapperOnClose) {
wrapperOnClose(new sandbox.Object());
}
};
var wrapper = Cu.createObjectIn(sandbox);
Object.defineProperties(wrapper, {
onopen: genPropDesc(null),
ondata: genPropDesc(null),
ondrain: genPropDesc(null),
onerror: genPropDesc(null),
onclose: genPropDesc(null),
var wrapperOnOpen, wrapperOnData, wrapperOnDrain, wrapperOnError, wrapperOnClose;
var wrapper = Components.utils.cloneInto({
setOpenCallback: function (callback) {
wrapperOnOpen = callback;
},
setDataCallback: function (callback) {
wrapperOnData = callback;
},
setDrainCallback: function (callback) {
wrapperOnDrain = callback;
},
setErrorCallback: function (callback) {
wrapperOnError = callback;
},
setCloseCallback: function (callback) {
wrapperOnClose = callback;
},
send: genPropDesc(function (buffer, offset, count) {
send: function (buffer, offset, count) {
return socket.send(buffer, offset, count);
}),
},
close: genPropDesc(function () {
close: function () {
socket.close();
})
});
Components.utils.makeObjectPropsNormal(wrapper);
}
}, sandbox, {cloneFunctions:true});
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);
xhr.onload = function () {
wrapper.status = xhr.status;
wrapper.response = Components.utils.cloneInto(xhr.response, sandbox);
if (wrapper.onload) {
wrapper.onload.call(wrapper, new sandbox.Object());
if (wrapperOnLoad) {
wrapperOnLoad(new sandbox.Object());
}
};
xhr.onerror = function () {
wrapper.status = xhr.status;
if (wrapper.onerror) {
wrapper.onerror.call(wrapper, new sandbox.Object());
if (wrapperOnError) {
wrapperOnError(new sandbox.Object());
}
};
var wrapper = Components.utils.createObjectIn(sandbox);
Object.defineProperties(wrapper, {
status: genPropDesc(0),
response: genPropDesc(undefined),
responseType: genPropDesc('text'),
var wrapperOnLoad, wrapperOnError;
var wrapper = Components.utils.cloneInto({
status: 0,
response: undefined,
responseType: 'text',
onload: genPropDesc(null),
onerror: genPropDesc(null),
setLoadCallback: function (callback) {
wrapperOnLoad = callback;
},
setErrorCallback: function (callback) {
wrapperOnError = callback;
},
open: genPropDesc(function (method, path, async) {
open: function (method, path, async) {
if (method !== 'POST' || !path || (async !== undefined && !async)) {
throw new Error('invalid open() arguments');
}
@ -138,22 +139,21 @@ var RtmpUtils = {
xhr.open('POST', path, true);
xhr.responseType = 'arraybuffer';
xhr.setRequestHeader('Content-Type', 'application/x-fcs');
}),
},
setRequestHeader: genPropDesc(function (header, value) {
setRequestHeader: function (header, value) {
if (header !== 'Content-Type' || value !== 'application/x-fcs') {
throw new Error('invalid setRequestHeader() arguments');
}
}),
},
send: genPropDesc(function (data) {
if (this.responseType !== 'arraybuffer') {
send: function (data) {
if (wrapper.responseType !== 'arraybuffer') {
throw new Error('Invalid responseType.');
}
xhr.send(data);
})
});
Components.utils.makeObjectPropsNormal(wrapper);
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

Просмотреть файл

@ -19,11 +19,12 @@ 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');
Components.utils.import('chrome://shumway/content/ExternalInterface.jsm');
Components.utils.import('chrome://shumway/content/FileLoader.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
'resource://shumway/ShumwayTelemetry.jsm');
@ -38,14 +39,6 @@ function getBoolPref(pref, 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);
@ -53,144 +46,169 @@ function log(aMsg) {
}
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
};
}
createAdapter: function (content, callbacks, hooks) {
// 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() {
var wrapped = {
enableDebug: function enableDebug() {
callbacks.enableDebug()
}),
setFullscreen: genPropDesc(function setFullscreen(value) {
},
setFullscreen: function setFullscreen(value) {
callbacks.sendMessage('setFullscreen', value, false);
}),
endActivation: genPropDesc(function endActivation() {
},
endActivation: function endActivation() {
callbacks.sendMessage('endActivation', null, false);
}),
fallback: genPropDesc(function fallback() {
},
fallback: function fallback() {
callbacks.sendMessage('fallback', null, false);
}),
getSettings: genPropDesc(function getSettings() {
},
getSettings: function getSettings() {
return Components.utils.cloneInto(
callbacks.sendMessage('getSettings', null, true), content);
}),
getPluginParams: genPropDesc(function getPluginParams() {
},
getPluginParams: function getPluginParams() {
return Components.utils.cloneInto(
callbacks.sendMessage('getPluginParams', null, true), content);
}),
reportIssue: genPropDesc(function reportIssue() {
},
reportIssue: function reportIssue() {
callbacks.sendMessage('reportIssue', null, false);
}),
externalCom: genPropDesc(function externalCom(args) {
},
reportTelemetry: function reportTelemetry(args) {
callbacks.sendMessage('reportTelemetry', args, false);
},
userInput: function userInput() {
callbacks.sendMessage('userInput', null, true);
},
setupComBridge: function setupComBridge(playerWindow) {
// postSyncMessage helper function to relay messages from the secondary
// window to the primary one.
function postSyncMessage(msg) {
if (onSyncMessageCallback) {
// the msg came from other content window
var reclonedMsg = Components.utils.cloneInto(Components.utils.waiveXrays(msg), content);
var result = onSyncMessageCallback(reclonedMsg);
// the result will be sent later to other content window
var waivedResult = Components.utils.waiveXrays(result);
return waivedResult;
}
}
// Creates secondary ShumwayCom adapter.
var playerContent = playerWindow.contentWindow.wrappedJSObject;
ShumwayCom.createPlayerAdapter(playerContent, postSyncMessage, callbacks, hooks);
},
setSyncMessageCallback: function (callback) {
onSyncMessageCallback = callback;
}
};
var onSyncMessageCallback;
var shumwayComAdapter = Components.utils.cloneInto(wrapped, content, {cloneFunctions:true});
content.ShumwayCom = shumwayComAdapter;
},
createPlayerAdapter: function (content, postSyncMessage, callbacks, hooks) {
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
// up Xray wrappers.
var wrapped = {
externalCom: function externalCom(args) {
var result = String(callbacks.sendMessage('externalCom', args, true));
return Components.utils.cloneInto(result, content);
}),
loadFile: genPropDesc(function loadFile(args) {
},
loadFile: function loadFile(args) {
callbacks.sendMessage('loadFile', args, false);
}),
reportTelemetry: genPropDesc(function reportTelemetry(args) {
},
reportTelemetry: function reportTelemetry(args) {
callbacks.sendMessage('reportTelemetry', args, false);
}),
setClipboard: genPropDesc(function setClipboard(args) {
},
setClipboard: function setClipboard(args) {
callbacks.sendMessage('setClipboard', args, false);
}),
navigateTo: genPropDesc(function navigateTo(args) {
},
navigateTo: function navigateTo(args) {
callbacks.sendMessage('navigateTo', args, false);
}),
userInput: genPropDesc(function userInput() {
callbacks.sendMessage('userInput', null, true);
}),
loadSystemResource: genPropDesc(function loadSystemResource(id) {
},
loadSystemResource: function loadSystemResource(id) {
loadShumwaySystemResource(id).then(function (data) {
if (shumwayComAdapter.onSystemResourceCallback) {
shumwayComAdapter.onSystemResourceCallback(id,
Components.utils.cloneInto(data, content));
if (onSystemResourceCallback) {
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)
});
postSyncMessage: function (msg) {
var result = postSyncMessage(msg);
return Components.utils.cloneInto(result, content)
},
Object.defineProperties(shumwayComAdapter, {
createSpecialStorage: genPropDesc(function () {
createSpecialStorage: function () {
var environment = callbacks.getEnvironment();
return SpecialStorageUtils.createWrappedSpecialStorage(content,
environment.swfUrl, environment.privateBrowsing);
})
});
},
setLoadFileCallback: function (callback) {
onLoadFileCallback = callback;
},
setExternalCallback: function (callback) {
onExternalCallback = callback;
},
setSystemResourceCallback: function (callback) {
onSystemResourceCallback = callback;
}
};
// Exposing createSpecialInflate function for DEFLATE stream decoding using
// Gecko API.
if (SpecialInflateUtils.isSpecialInflateEnabled) {
Object.defineProperties(shumwayComAdapter, {
createSpecialInflate: genPropDesc(function () {
return SpecialInflateUtils.createWrappedSpecialInflate(content);
})
});
wrapped.createSpecialInflate = 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);
})
});
wrapped.createRtmpSocket = function (params) {
return RtmpUtils.createSocket(content, params);
};
wrapped.createRtmpXHR = function () {
return RtmpUtils.createXHR(content);
};
}
Components.utils.makeObjectPropsNormal(shumwayComAdapter);
return shumwayComAdapter;
var onSystemResourceCallback;
var onExternalCallback;
var onLoadFileCallback;
hooks.onLoadFileCallback = function (arg) {
if (onLoadFileCallback) {
onLoadFileCallback(Components.utils.cloneInto(arg, content));
}
};
hooks.onExternalCallback = function (call) {
if (onExternalCallback) {
return onExternalCallback(Components.utils.cloneInto(call, content));
}
};
var shumwayComAdapter = Components.utils.cloneInto(wrapped, content, {cloneFunctions:true});
content.ShumwayCom = shumwayComAdapter;
},
createActions: function (startupInfo, window, document) {
@ -242,17 +260,20 @@ function ShumwayChromeActions(startupInfo, window, document) {
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.fileLoader = new FileLoader(startupInfo.url, startupInfo.baseUrl, function (args) {
this.onLoadFileCallback(args);
}.bind(this));
this.onLoadFileCallback = null;
this.externalInterface = null;
this.onExternalCallback = null;
}
@ -300,77 +321,27 @@ ShumwayChromeActions.prototype = {
},
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"});
});
this.fileLoader.load(data);
},
navigateTo: function (data) {
// Our restrictions are a little bit different from Flash's: let's enable
// only http(s) and only when script execution is allowed.
// See https://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html
var url = data.url || 'about:blank';
var target = data.target || '_self';
var isWhitelistedProtocol = /^(http|https):\/\//i.test(url);
if (!isWhitelistedProtocol || !this.allowScriptAccess) {
return;
}
// ...and only when user input is in-progress.
if (!this.isUserInputInProgress()) {
return;
}
log('!!navigateTo: ' + url + ' ... ' + target);
var embedTag = this.embedTag.wrappedJSObject;
var window = embedTag ? embedTag.ownerDocument.defaultView : this.window;
window.open(data.url, data.target || '_self');
window.open(url, target);
},
fallback: function(automatic) {
@ -507,176 +478,19 @@ ShumwayChromeActions.prototype = {
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);
// TODO check more security stuff ?
if (!this.externalInterface) {
var parentWindow = this.window.parent.wrappedJSObject;
var embedTag = this.embedTag.wrappedJSObject;
this.externalInterface = new ExternalInterface(parentWindow, embedTag, function (call) {
this.onExternalCallback(call);
}.bind(this));
}
return this.externalInterface.processAction(data);
}
};
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 = {
@ -716,67 +530,3 @@ function getVersionInfo() {
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];
};
}

Просмотреть файл

@ -96,36 +96,32 @@ var SpecialInflateUtils = {
},
createWrappedSpecialInflate: function (sandbox) {
function genPropDesc(value) {
return {
enumerable: true, configurable: true, writable: true, value: value
};
}
var wrapped = new SpecialInflate();
var wrapperOnData;
wrapped.onData = function(data) {
if (wrapper.onData) {
wrapper.onData.call(wrapper, Components.utils.cloneInto(data, sandbox));
if (wrapperOnData) {
wrapperOnData(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 = Components.utils.createObjectIn(sandbox);
Object.defineProperties(wrapper, {
onData: genPropDesc(null),
var wrapper = Components.utils.cloneInto({
setDataCallback: function (callback) {
wrapperOnData = callback;
},
push: genPropDesc(function (data) {
push: function (data) {
// Uint8Array is expected in the data parameter.
// SpecialInflate.push() fails with other argument types.
return wrapped.push(data);
}),
close: genPropDesc(function () {
},
close: function () {
return wrapped.close();
})
});
Components.utils.makeObjectPropsNormal(wrapper);
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

Просмотреть файл

@ -16,17 +16,10 @@
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"]
@ -39,19 +32,17 @@ var SpecialStorageUtils = {
// 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) {
var wrapper = Components.utils.cloneInto({
getItem: function (key) {
return storage.getItem(key);
}),
setItem: genPropDesc(function (key, value) {
},
setItem: function (key, value) {
storage.setItem(key, value);
}),
removeItem: genPropDesc(function (key) {
},
removeItem: function (key) {
storage.removeItem(key);
})
});
Components.utils.makeObjectPropsNormal(wrapper);
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

Просмотреть файл

@ -19,17 +19,15 @@ Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
var externalInterfaceWrapper = {
callback: function (call) {
if (!shumwayComAdapter.onExternalCallback) {
if (!shumwayComAdapterHooks.onExternalCallback) {
return undefined;
}
return shumwayComAdapter.onExternalCallback(
return shumwayComAdapterHooks.onExternalCallback(
Components.utils.cloneInto(JSON.parse(call), content));
}
};
// The object allows resending of external interface, clipboard and other
// control messages between unprivileged content and ShumwayStreamConverter.
var shumwayComAdapter;
var shumwayComAdapterHooks = {};
function sendMessage(action, data, sync) {
var detail = {action: action, data: data, sync: sync};
@ -53,18 +51,18 @@ addMessageListener('Shumway:init', function (message) {
externalInterface: externalInterfaceWrapper
});
shumwayComAdapter = ShumwayCom.createAdapter(content, {
ShumwayCom.createAdapter(content.wrappedJSObject, {
sendMessage: sendMessage,
enableDebug: enableDebug,
getEnvironment: function () { return environment; }
});
}, shumwayComAdapterHooks);
content.wrappedJSObject.runViewer();
});
addMessageListener('Shumway:loadFile', function (message) {
if (!shumwayComAdapter.onLoadFileCallback) {
if (!shumwayComAdapterHooks.onLoadFileCallback) {
return;
}
shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(message.data, content));
shumwayComAdapterHooks.onLoadFileCallback(Components.utils.cloneInto(message.data, content));
});

Просмотреть файл

@ -36,18 +36,19 @@ function runViewer() {
var childWindow = viewer.contentWindow.wrappedJSObject;
var shumwayComAdapter = ShumwayCom.createAdapter(childWindow, {
var shumwayComAdapterHooks = {};
ShumwayCom.createAdapter(childWindow, {
sendMessage: sendMessage,
enableDebug: enableDebug,
getEnvironment: getEnvironment,
});
}, shumwayComAdapterHooks);
shumwayActions.onExternalCallback = function (call) {
return shumwayComAdapter.onExternalCallback(Components.utils.cloneInto(call, childWindow));
return shumwayComAdapterHooks.onExternalCallback(Components.utils.cloneInto(call, childWindow));
};
shumwayActions.onLoadFileCallback = function (args) {
shumwayComAdapter.onLoadFileCallback(Components.utils.cloneInto(args, childWindow));
shumwayComAdapterHooks.onLoadFileCallback(Components.utils.cloneInto(args, childWindow));
};
childWindow.runViewer();

Просмотреть файл

@ -32,7 +32,6 @@ const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
@ -417,25 +416,7 @@ ShumwayStreamConverterBase.prototype = {
}
}
var allowScriptAccess = false;
switch (objectParams.allowscriptaccess || 'sameDomain') {
case 'always':
allowScriptAccess = true;
break;
case 'never':
allowScriptAccess = false;
break;
default:
if (!pageUrl)
break;
try {
// checking if page is in same domain (? same protocol and port)
allowScriptAccess =
Services.io.newURI('/', null, Services.io.newURI(pageUrl, null, null)).spec ==
Services.io.newURI('/', null, Services.io.newURI(url, null, null)).spec;
} catch (ex) {}
break;
}
var allowScriptAccess = isScriptAllowed(objectParams.allowscriptaccess, url, pageUrl);
var startupInfo = {};
startupInfo.window = window;
@ -482,7 +463,17 @@ ShumwayStreamConverterBase.prototype = {
// Create a new channel that loads the viewer as a chrome resource.
var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html';
var channel = Services.io.newChannel(viewerUrl, null, null);
// TODO use only newChannel2 after FF37 is released.
var channel = Services.io.newChannel2 ?
Services.io.newChannel2(viewerUrl,
null,
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER) :
Services.io.newChannel(viewerUrl, null, null);
var converter = this;
var listener = this.listener;
@ -548,6 +539,32 @@ ShumwayStreamConverterBase.prototype = {
}
};
function isScriptAllowed(allowScriptAccessParameter, url, pageUrl) {
if (!allowScriptAccessParameter) {
allowScriptAccessParameter = 'sameDomain';
}
var allowScriptAccess = false;
switch (allowScriptAccessParameter.toLowerCase()) { // ignoring case here
case 'always':
allowScriptAccess = true;
break;
case 'never':
allowScriptAccess = false;
break;
default: // 'samedomain'
if (!pageUrl)
break;
try {
// checking if page is in same domain (? same protocol and port)
allowScriptAccess =
Services.io.newURI('/', null, Services.io.newURI(pageUrl, null, null)).spec ==
Services.io.newURI('/', null, Services.io.newURI(url, null, null)).spec;
} catch (ex) {}
break;
}
return allowScriptAccess;
}
// properties required for XPCOM registration:
function copyProperties(obj, template) {
for (var prop in template) {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,2 +1,2 @@
0.10.225
510390b
0.10.268
02fd13d