зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1142484 - Update Shumway to version 0.10.268. r=till
This commit is contained in:
Родитель
b628218278
Коммит
58264b037b
|
@ -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, '&').replace(/</g, '<').replace(/>/g, '>') + '</string>';
|
||||
case 'undefined':
|
||||
return '<undefined/>';
|
||||
}
|
||||
};
|
||||
wrappedWindow.__flash__eval = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__eval: ' + expr);
|
||||
// allowScriptAccess protects page from unwanted swf scripts,
|
||||
// we can execute script in the page context without restrictions.
|
||||
var result = this.eval(expr);
|
||||
traceExternalInterface && this.console.log('__flash__eval (result): ' + result);
|
||||
return result;
|
||||
}.bind(wrappedWindow);
|
||||
wrappedWindow.__flash__call = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__call (ignored): ' + expr);
|
||||
};
|
||||
}
|
||||
wrappedObject.__flash__registerCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__registerCallback: ' + functionName);
|
||||
Components.utils.exportFunction(function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn: ' + functionName);
|
||||
var result;
|
||||
if (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, '&').replace(/</g, '<').replace(/>/g, '>') + '</string>';
|
||||
case 'undefined':
|
||||
return '<undefined/>';
|
||||
}
|
||||
};
|
||||
wrappedWindow.__flash__eval = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__eval: ' + expr);
|
||||
// allowScriptAccess protects page from unwanted swf scripts,
|
||||
// we can execute script in the page context without restrictions.
|
||||
var result = this.eval(expr);
|
||||
traceExternalInterface && this.console.log('__flash__eval (result): ' + result);
|
||||
return result;
|
||||
}.bind(wrappedWindow);
|
||||
wrappedWindow.__flash__call = function (expr) {
|
||||
traceExternalInterface && this.console.log('__flash__call (ignored): ' + expr);
|
||||
};
|
||||
}
|
||||
wrappedObject.__flash__registerCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__registerCallback: ' + functionName);
|
||||
Components.utils.exportFunction(function () {
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn: ' + functionName);
|
||||
var result;
|
||||
if (actions.onExternalCallback) {
|
||||
result = actions.onExternalCallback({functionName: functionName, args: args});
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__callIn (result): ' + result);
|
||||
}
|
||||
return wrappedWindow.eval(result);
|
||||
}, this, { defineAs: functionName });
|
||||
};
|
||||
wrappedObject.__flash__unregisterCallback = function (functionName) {
|
||||
traceExternalInterface && wrappedWindow.console.log('__flash__unregisterCallback: ' + functionName);
|
||||
delete this[functionName];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -96,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
|
||||
|
|
Загрузка…
Ссылка в новой задаче