2013-09-30 16:24:58 -07:00
|
|
|
/*
|
|
|
|
* Copyright 2013 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2013-10-17 14:11:52 -07:00
|
|
|
var EXPORTED_SYMBOLS = ['ShumwayStreamConverter', 'ShumwayStreamOverlayConverter'];
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cr = Components.results;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
const SHUMWAY_CONTENT_TYPE = 'application/x-shockwave-flash';
|
|
|
|
const EXPECTED_PLAYPREVIEW_URI_PREFIX = 'data:application/x-moz-playpreview;,' +
|
|
|
|
SHUMWAY_CONTENT_TYPE;
|
|
|
|
|
|
|
|
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
|
|
|
|
const SEAMONKEY_ID = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}';
|
|
|
|
|
|
|
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
|
|
|
Cu.import('resource://gre/modules/Services.jsm');
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
|
|
|
|
'resource://gre/modules/PrivateBrowsingUtils.jsm');
|
|
|
|
|
2013-10-21 08:59:43 -07:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
|
|
|
|
'resource://shumway/ShumwayTelemetry.jsm');
|
|
|
|
|
2013-09-30 16:24:58 -07:00
|
|
|
function getBoolPref(pref, def) {
|
|
|
|
try {
|
|
|
|
return Services.prefs.getBoolPref(pref);
|
|
|
|
} catch (ex) {
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function log(aMsg) {
|
2013-10-17 14:11:52 -07:00
|
|
|
let msg = 'ShumwayStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
|
2013-09-30 16:24:58 -07:00
|
|
|
Services.console.logStringMessage(msg);
|
|
|
|
dump(msg + '\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDOMWindow(aChannel) {
|
2013-11-26 06:16:32 -08:00
|
|
|
var requestor = aChannel.notificationCallbacks ||
|
|
|
|
aChannel.loadGroup.notificationCallbacks;
|
2013-09-30 16:24:58 -07:00
|
|
|
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
|
|
|
|
return win;
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseQueryString(qs) {
|
|
|
|
if (!qs)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
if (qs.charAt(0) == '?')
|
|
|
|
qs = qs.slice(1);
|
|
|
|
|
|
|
|
var values = qs.split('&');
|
|
|
|
var obj = {};
|
|
|
|
for (var i = 0; i < values.length; i++) {
|
|
|
|
var kv = values[i].split('=');
|
|
|
|
var key = kv[0], value = kv[1];
|
|
|
|
obj[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2015-01-30 12:53:50 -08:00
|
|
|
function isContentWindowPrivate(win) {
|
|
|
|
if (!('isContentWindowPrivate' in PrivateBrowsingUtils)) {
|
|
|
|
return PrivateBrowsingUtils.isWindowPrivate(win);
|
|
|
|
}
|
|
|
|
return PrivateBrowsingUtils.isContentWindowPrivate(win);
|
|
|
|
}
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
function isShumwayEnabledFor(startupInfo) {
|
2013-09-30 16:24:58 -07:00
|
|
|
// disabled for PrivateBrowsing windows
|
2015-03-06 02:45:00 -08:00
|
|
|
if (isContentWindowPrivate(startupInfo.window) &&
|
2015-01-30 12:53:50 -08:00
|
|
|
!getBoolPref('shumway.enableForPrivate', false)) {
|
2013-09-30 16:24:58 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// disabled if embed tag specifies shumwaymode (for testing purpose)
|
2015-03-06 02:45:00 -08:00
|
|
|
if (startupInfo.objectParams['shumwaymode'] === 'off') {
|
2013-09-30 16:24:58 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
var url = startupInfo.url;
|
|
|
|
var baseUrl = startupInfo.baseUrl;
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
// blacklisting well known sites with issues
|
|
|
|
if (/\.ytimg\.com\//i.test(url) /* youtube movies */ ||
|
2013-11-26 06:16:32 -08:00
|
|
|
/\/vui.swf\b/i.test(url) /* vidyo manager */ ||
|
|
|
|
/soundcloud\.com\/player\/assets\/swf/i.test(url) /* soundcloud */ ||
|
|
|
|
/sndcdn\.com\/assets\/swf/.test(url) /* soundcloud */ ||
|
|
|
|
/vimeocdn\.com/.test(url) /* vimeo */) {
|
2013-09-30 16:24:58 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
var ActivationQueue = {
|
|
|
|
nonActive: [],
|
|
|
|
initializing: -1,
|
|
|
|
activationTimeout: null,
|
|
|
|
get currentNonActive() {
|
2015-03-06 02:45:00 -08:00
|
|
|
return this.nonActive[this.initializing].startupInfo;
|
2013-09-30 16:24:58 -07:00
|
|
|
},
|
2015-03-06 02:45:00 -08:00
|
|
|
enqueue: function ActivationQueue_enqueue(startupInfo, callback) {
|
|
|
|
this.nonActive.push({startupInfo: startupInfo, callback: callback});
|
2013-09-30 16:24:58 -07:00
|
|
|
if (this.nonActive.length === 1) {
|
|
|
|
this.activateNext();
|
|
|
|
}
|
|
|
|
},
|
2013-10-21 08:59:43 -07:00
|
|
|
findLastOnPage: function ActivationQueue_findLastOnPage(baseUrl) {
|
|
|
|
for (var i = this.nonActive.length - 1; i >= 0; i--) {
|
2015-03-06 02:45:00 -08:00
|
|
|
if (this.nonActive[i].startupInfo.baseUrl === baseUrl) {
|
|
|
|
return this.nonActive[i].startupInfo;
|
2013-10-21 08:59:43 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2013-09-30 16:24:58 -07:00
|
|
|
activateNext: function ActivationQueue_activateNext() {
|
2015-03-06 02:45:00 -08:00
|
|
|
function weightInstance(startupInfo) {
|
2013-09-30 16:24:58 -07:00
|
|
|
// set of heuristics for find the most important instance to load
|
|
|
|
var weight = 0;
|
|
|
|
// using linear distance to the top-left of the view area
|
2015-03-06 02:45:00 -08:00
|
|
|
if (startupInfo.embedTag) {
|
|
|
|
var window = startupInfo.window;
|
|
|
|
var clientRect = startupInfo.embedTag.getBoundingClientRect();
|
2013-09-30 16:24:58 -07:00
|
|
|
weight -= Math.abs(clientRect.left - window.scrollX) +
|
|
|
|
Math.abs(clientRect.top - window.scrollY);
|
|
|
|
}
|
2015-03-06 02:45:00 -08:00
|
|
|
var doc = startupInfo.window.document;
|
2013-09-30 16:24:58 -07:00
|
|
|
if (!doc.hidden) {
|
|
|
|
weight += 100000; // might not be that important if hidden
|
|
|
|
}
|
2015-03-06 02:45:00 -08:00
|
|
|
if (startupInfo.embedTag &&
|
|
|
|
startupInfo.embedTag.ownerDocument.hasFocus()) {
|
2013-09-30 16:24:58 -07:00
|
|
|
weight += 10000; // parent document is focused
|
|
|
|
}
|
|
|
|
return weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.activationTimeout) {
|
|
|
|
this.activationTimeout.cancel();
|
|
|
|
this.activationTimeout = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.initializing >= 0) {
|
|
|
|
this.nonActive.splice(this.initializing, 1);
|
|
|
|
}
|
|
|
|
var weights = [];
|
|
|
|
for (var i = 0; i < this.nonActive.length; i++) {
|
|
|
|
try {
|
2015-03-06 02:45:00 -08:00
|
|
|
var weight = weightInstance(this.nonActive[i].startupInfo);
|
2013-09-30 16:24:58 -07:00
|
|
|
weights.push(weight);
|
|
|
|
} catch (ex) {
|
|
|
|
// unable to calc weight the instance, removing
|
|
|
|
log('Shumway instance weight calculation failed: ' + ex);
|
|
|
|
this.nonActive.splice(i, 1);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (this.nonActive.length === 0) {
|
|
|
|
this.initializing = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var maxWeightIndex = 0;
|
|
|
|
var maxWeight = weights[0];
|
|
|
|
for (var i = 1; i < weights.length; i++) {
|
|
|
|
if (maxWeight < weights[i]) {
|
|
|
|
maxWeight = weights[i];
|
|
|
|
maxWeightIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
this.initializing = maxWeightIndex;
|
2015-03-06 02:45:00 -08:00
|
|
|
this.nonActive[maxWeightIndex].callback();
|
2013-09-30 16:24:58 -07:00
|
|
|
break;
|
|
|
|
} catch (ex) {
|
|
|
|
// unable to initialize the instance, trying another one
|
|
|
|
log('Shumway instance initialization failed: ' + ex);
|
|
|
|
this.nonActive.splice(maxWeightIndex, 1);
|
|
|
|
weights.splice(maxWeightIndex, 1);
|
|
|
|
}
|
|
|
|
} while (true);
|
|
|
|
|
|
|
|
var ACTIVATION_TIMEOUT = 3000;
|
|
|
|
this.activationTimeout = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
|
|
this.activationTimeout.initWithCallback(function () {
|
|
|
|
log('Timeout during shumway instance initialization');
|
|
|
|
this.activateNext();
|
|
|
|
}.bind(this), ACTIVATION_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
function activateShumwayScripts(window) {
|
2013-09-30 16:24:58 -07:00
|
|
|
function initScripts() {
|
2015-01-30 12:53:50 -08:00
|
|
|
window.wrappedJSObject.runViewer();
|
2015-02-23 14:51:36 -08:00
|
|
|
|
|
|
|
var parentWindow = window.parent;
|
|
|
|
var viewerWindow = window.viewer.contentWindow;
|
|
|
|
|
|
|
|
function activate(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
viewerWindow.removeEventListener('mousedown', activate, true);
|
|
|
|
|
|
|
|
parentWindow.addEventListener('keydown', forwardKeyEvent, true);
|
|
|
|
parentWindow.addEventListener('keyup', forwardKeyEvent, true);
|
|
|
|
|
|
|
|
sendFocusEvent('focus');
|
|
|
|
|
|
|
|
parentWindow.addEventListener('blur', deactivate, true);
|
|
|
|
parentWindow.addEventListener('mousedown', deactivate, true);
|
|
|
|
viewerWindow.addEventListener('unload', deactivate, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function deactivate() {
|
|
|
|
parentWindow.removeEventListener('blur', deactivate, true);
|
|
|
|
parentWindow.removeEventListener('mousedown', deactivate, true);
|
|
|
|
viewerWindow.removeEventListener('unload', deactivate, true);
|
|
|
|
|
|
|
|
parentWindow.removeEventListener('keydown', forwardKeyEvent, true);
|
|
|
|
parentWindow.removeEventListener('keyup', forwardKeyEvent, true);
|
|
|
|
|
|
|
|
sendFocusEvent('blur');
|
|
|
|
|
|
|
|
viewerWindow.addEventListener('mousedown', activate, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
function forwardKeyEvent(e) {
|
|
|
|
var event = viewerWindow.document.createEvent('KeyboardEvent');
|
|
|
|
event.initKeyEvent(e.type,
|
|
|
|
e.bubbles,
|
|
|
|
e.cancelable,
|
|
|
|
e.view,
|
|
|
|
e.ctrlKey,
|
|
|
|
e.altKey,
|
|
|
|
e.shiftKey,
|
|
|
|
e.metaKey,
|
|
|
|
e.keyCode,
|
|
|
|
e.charCode);
|
|
|
|
viewerWindow.dispatchEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendFocusEvent(type) {
|
|
|
|
var event = viewerWindow.document.createEvent("UIEvent");
|
|
|
|
event.initEvent(type, false, true);
|
|
|
|
viewerWindow.dispatchEvent(event);
|
|
|
|
}
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
if (viewerWindow) {
|
|
|
|
viewerWindow.addEventListener('mousedown', activate, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener('shumwayFallback', function (e) {
|
|
|
|
var automatic = !!e.detail.automatic;
|
|
|
|
fallbackToNativePlugin(window, !automatic, automatic);
|
|
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('shumwayActivated', function (e) {
|
|
|
|
if (ActivationQueue.currentNonActive &&
|
|
|
|
ActivationQueue.currentNonActive.window === window) {
|
|
|
|
ActivationQueue.activateNext();
|
|
|
|
}
|
|
|
|
});
|
2013-09-30 16:24:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (window.document.readyState === "interactive" ||
|
|
|
|
window.document.readyState === "complete") {
|
|
|
|
initScripts();
|
|
|
|
} else {
|
|
|
|
window.document.addEventListener('DOMContentLoaded', initScripts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
function fallbackToNativePlugin(window, userAction, activateCTP) {
|
|
|
|
var obj = window.frameElement;
|
|
|
|
var doc = obj.ownerDocument;
|
|
|
|
var e = doc.createEvent("CustomEvent");
|
|
|
|
e.initCustomEvent("MozPlayPlugin", true, true, activateCTP);
|
|
|
|
obj.dispatchEvent(e);
|
|
|
|
|
|
|
|
ShumwayTelemetry.onFallback(userAction);
|
2013-09-30 16:24:58 -07:00
|
|
|
}
|
|
|
|
|
2013-10-17 14:11:52 -07:00
|
|
|
function ShumwayStreamConverterBase() {
|
2013-09-30 16:24:58 -07:00
|
|
|
}
|
|
|
|
|
2013-10-17 14:11:52 -07:00
|
|
|
ShumwayStreamConverterBase.prototype = {
|
2013-09-30 16:24:58 -07:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([
|
|
|
|
Ci.nsISupports,
|
|
|
|
Ci.nsIStreamConverter,
|
|
|
|
Ci.nsIStreamListener,
|
|
|
|
Ci.nsIRequestObserver
|
|
|
|
]),
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This component works as such:
|
|
|
|
* 1. asyncConvertData stores the listener
|
|
|
|
* 2. onStartRequest creates a new channel, streams the viewer and cancels
|
|
|
|
* the request so Shumway can do the request
|
|
|
|
* Since the request is cancelled onDataAvailable should not be called. The
|
|
|
|
* onStopRequest does nothing. The convert function just returns the stream,
|
|
|
|
* it's just the synchronous version of asyncConvertData.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// nsIStreamConverter::convert
|
|
|
|
convert: function(aFromStream, aFromType, aToType, aCtxt) {
|
|
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
},
|
|
|
|
|
|
|
|
getUrlHint: function(requestUrl) {
|
|
|
|
return requestUrl.spec;
|
|
|
|
},
|
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
getStartupInfo: function(window, urlHint) {
|
2013-11-26 06:16:32 -08:00
|
|
|
var url = urlHint;
|
2013-09-30 16:24:58 -07:00
|
|
|
var baseUrl;
|
|
|
|
var pageUrl;
|
|
|
|
var element = window.frameElement;
|
|
|
|
var isOverlay = false;
|
|
|
|
var objectParams = {};
|
|
|
|
if (element) {
|
2013-11-26 06:16:32 -08:00
|
|
|
// PlayPreview overlay "belongs" to the embed/object tag and consists of
|
|
|
|
// DIV and IFRAME. Starting from IFRAME and looking for first object tag.
|
|
|
|
var tagName = element.nodeName, containerElement;
|
2013-09-30 16:24:58 -07:00
|
|
|
while (tagName != 'EMBED' && tagName != 'OBJECT') {
|
|
|
|
// plugin overlay skipping until the target plugin is found
|
|
|
|
isOverlay = true;
|
2013-11-26 06:16:32 -08:00
|
|
|
containerElement = element;
|
2013-09-30 16:24:58 -07:00
|
|
|
element = element.parentNode;
|
2013-11-26 06:16:32 -08:00
|
|
|
if (!element) {
|
|
|
|
throw new Error('Plugin element is not found');
|
|
|
|
}
|
2013-09-30 16:24:58 -07:00
|
|
|
tagName = element.nodeName;
|
|
|
|
}
|
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
if (isOverlay) {
|
2015-01-30 12:53:50 -08:00
|
|
|
// HACK For Facebook, CSS embed tag rescaling -- iframe (our overlay)
|
|
|
|
// has no styling in document. Shall removed with jsplugins.
|
|
|
|
for (var child = window.frameElement; child !== element; child = child.parentNode) {
|
|
|
|
child.setAttribute('style', 'max-width: 100%; max-height: 100%');
|
|
|
|
}
|
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
// Checking if overlay is a proper PlayPreview overlay.
|
|
|
|
for (var i = 0; i < element.children.length; i++) {
|
|
|
|
if (element.children[i] === containerElement) {
|
|
|
|
throw new Error('Plugin element is invalid');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element) {
|
|
|
|
// Getting absolute URL from the EMBED tag
|
2015-01-30 12:53:50 -08:00
|
|
|
url = element.srcURI && element.srcURI.spec;
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
pageUrl = element.ownerDocument.location.href; // proper page url?
|
|
|
|
|
|
|
|
if (tagName == 'EMBED') {
|
|
|
|
for (var i = 0; i < element.attributes.length; ++i) {
|
|
|
|
var paramName = element.attributes[i].localName.toLowerCase();
|
|
|
|
objectParams[paramName] = element.attributes[i].value;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (var i = 0; i < element.childNodes.length; ++i) {
|
|
|
|
var paramElement = element.childNodes[i];
|
|
|
|
if (paramElement.nodeType != 1 ||
|
|
|
|
paramElement.nodeName != 'PARAM') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var paramName = paramElement.getAttribute('name').toLowerCase();
|
|
|
|
objectParams[paramName] = paramElement.getAttribute('value');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
if (!url) { // at this point url shall be known -- asserting
|
|
|
|
throw new Error('Movie url is not specified');
|
|
|
|
}
|
|
|
|
|
2015-02-07 07:53:12 -08:00
|
|
|
if (objectParams.base) {
|
|
|
|
baseUrl = Services.io.newURI(objectParams.base, null, pageUrl).spec;
|
|
|
|
} else {
|
|
|
|
baseUrl = pageUrl;
|
|
|
|
}
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
var movieParams = {};
|
|
|
|
if (objectParams.flashvars) {
|
|
|
|
movieParams = parseQueryString(objectParams.flashvars);
|
|
|
|
}
|
|
|
|
var queryStringMatch = /\?([^#]+)/.exec(url);
|
|
|
|
if (queryStringMatch) {
|
|
|
|
var queryStringParams = parseQueryString(queryStringMatch[1]);
|
|
|
|
for (var i in queryStringParams) {
|
|
|
|
if (!(i in movieParams)) {
|
|
|
|
movieParams[i] = queryStringParams[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-12 05:50:04 -07:00
|
|
|
var allowScriptAccess = isScriptAllowed(objectParams.allowscriptaccess, url, pageUrl);
|
2013-09-30 16:24:58 -07:00
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
var startupInfo = {};
|
|
|
|
startupInfo.window = window;
|
|
|
|
startupInfo.url = url;
|
|
|
|
startupInfo.privateBrowsing = isContentWindowPrivate(window);
|
|
|
|
startupInfo.objectParams = objectParams;
|
|
|
|
startupInfo.movieParams = movieParams;
|
|
|
|
startupInfo.baseUrl = baseUrl || url;
|
|
|
|
startupInfo.isOverlay = isOverlay;
|
|
|
|
startupInfo.embedTag = element;
|
|
|
|
startupInfo.isPausedAtStart = /\bpaused=true$/.test(urlHint);
|
|
|
|
startupInfo.allowScriptAccess = allowScriptAccess;
|
|
|
|
startupInfo.pageIndex = 0;
|
|
|
|
return startupInfo;
|
2013-09-30 16:24:58 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// nsIStreamConverter::asyncConvertData
|
|
|
|
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
|
|
|
|
// Store the listener passed to us
|
|
|
|
this.listener = aListener;
|
|
|
|
},
|
|
|
|
|
|
|
|
// nsIStreamListener::onDataAvailable
|
|
|
|
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
|
|
|
|
// Do nothing since all the data loading is handled by the viewer.
|
|
|
|
log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!');
|
|
|
|
},
|
|
|
|
|
|
|
|
// nsIRequestObserver::onStartRequest
|
|
|
|
onStartRequest: function(aRequest, aContext) {
|
|
|
|
// Setup the request so we can use it below.
|
|
|
|
aRequest.QueryInterface(Ci.nsIChannel);
|
2013-11-26 06:16:32 -08:00
|
|
|
|
|
|
|
aRequest.QueryInterface(Ci.nsIWritablePropertyBag);
|
|
|
|
|
|
|
|
// Change the content type so we don't get stuck in a loop.
|
|
|
|
aRequest.setProperty('contentType', aRequest.contentType);
|
|
|
|
aRequest.contentType = 'text/html';
|
|
|
|
|
|
|
|
// TODO For now suspending request, however we can continue fetching data
|
|
|
|
aRequest.suspend();
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
var originalURI = aRequest.URI;
|
|
|
|
|
2015-01-30 12:53:50 -08:00
|
|
|
// Create a new channel that loads the viewer as a chrome resource.
|
|
|
|
var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html';
|
2015-03-12 05:50:04 -07:00
|
|
|
// 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);
|
2013-09-30 16:24:58 -07:00
|
|
|
|
|
|
|
var converter = this;
|
|
|
|
var listener = this.listener;
|
|
|
|
// Proxy all the request observer calls, when it gets to onStopRequest
|
|
|
|
// we can get the dom window.
|
|
|
|
var proxy = {
|
2013-11-26 06:16:32 -08:00
|
|
|
onStartRequest: function(request, context) {
|
|
|
|
listener.onStartRequest(aRequest, context);
|
2013-09-30 16:24:58 -07:00
|
|
|
},
|
2013-11-26 06:16:32 -08:00
|
|
|
onDataAvailable: function(request, context, inputStream, offset, count) {
|
|
|
|
listener.onDataAvailable(aRequest, context, inputStream, offset, count);
|
2013-09-30 16:24:58 -07:00
|
|
|
},
|
2013-11-26 06:16:32 -08:00
|
|
|
onStopRequest: function(request, context, statusCode) {
|
|
|
|
// Cancel the request so the viewer can handle it.
|
|
|
|
aRequest.resume();
|
|
|
|
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
2013-09-30 16:24:58 -07:00
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
var domWindow = getDOMWindow(channel);
|
2015-03-06 02:45:00 -08:00
|
|
|
let startupInfo = converter.getStartupInfo(domWindow,
|
|
|
|
converter.getUrlHint(originalURI));
|
|
|
|
if (!isShumwayEnabledFor(startupInfo)) {
|
2014-01-09 08:46:09 -08:00
|
|
|
fallbackToNativePlugin(domWindow, false, true);
|
2013-11-26 06:16:32 -08:00
|
|
|
return;
|
|
|
|
}
|
2013-09-30 16:24:58 -07:00
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
domWindow.shumwayStartupInfo = startupInfo;
|
2015-02-23 14:51:36 -08:00
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
// Report telemetry on amount of swfs on the page
|
2015-03-06 02:45:00 -08:00
|
|
|
if (startupInfo.isOverlay) {
|
2013-11-26 06:16:32 -08:00
|
|
|
// Looking for last actions with same baseUrl
|
2015-03-06 02:45:00 -08:00
|
|
|
var prevPageStartupInfo = ActivationQueue.findLastOnPage(startupInfo.baseUrl);
|
|
|
|
var pageIndex = !prevPageStartupInfo ? 1 : (prevPageStartupInfo.pageIndex + 1);
|
|
|
|
startupInfo.pageIndex = pageIndex;
|
2013-11-26 06:16:32 -08:00
|
|
|
ShumwayTelemetry.onPageIndex(pageIndex);
|
|
|
|
} else {
|
|
|
|
ShumwayTelemetry.onPageIndex(0);
|
2013-09-30 16:24:58 -07:00
|
|
|
}
|
2013-11-26 06:16:32 -08:00
|
|
|
|
2015-03-06 02:45:00 -08:00
|
|
|
ActivationQueue.enqueue(startupInfo, function(domWindow) {
|
|
|
|
activateShumwayScripts(domWindow);
|
|
|
|
}.bind(null, domWindow));
|
2013-11-26 06:16:32 -08:00
|
|
|
|
|
|
|
listener.onStopRequest(aRequest, context, statusCode);
|
2013-09-30 16:24:58 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-26 06:16:32 -08:00
|
|
|
// Keep the URL the same so the browser sees it as the same.
|
|
|
|
channel.originalURI = aRequest.URI;
|
|
|
|
channel.loadGroup = aRequest.loadGroup;
|
|
|
|
|
2015-01-30 12:53:50 -08:00
|
|
|
// We can use all powerful principal: we are opening chrome:// web page,
|
|
|
|
// which will need lots of permission.
|
2013-11-26 06:16:32 -08:00
|
|
|
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
|
|
|
|
.getService(Ci.nsIScriptSecurityManager);
|
2015-01-30 12:53:50 -08:00
|
|
|
var resourcePrincipal = securityManager.getSystemPrincipal();
|
2013-11-26 06:16:32 -08:00
|
|
|
aRequest.owner = resourcePrincipal;
|
2013-09-30 16:24:58 -07:00
|
|
|
channel.asyncOpen(proxy, aContext);
|
|
|
|
},
|
|
|
|
|
|
|
|
// nsIRequestObserver::onStopRequest
|
|
|
|
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
|
|
|
// Do nothing.
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-03-12 05:50:04 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-09-30 16:24:58 -07:00
|
|
|
// properties required for XPCOM registration:
|
|
|
|
function copyProperties(obj, template) {
|
|
|
|
for (var prop in template) {
|
|
|
|
obj[prop] = template[prop];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-17 14:11:52 -07:00
|
|
|
function ShumwayStreamConverter() {}
|
|
|
|
ShumwayStreamConverter.prototype = new ShumwayStreamConverterBase();
|
|
|
|
copyProperties(ShumwayStreamConverter.prototype, {
|
2013-09-30 16:24:58 -07:00
|
|
|
classID: Components.ID('{4c6030f7-e20a-264f-5b0e-ada3a9e97384}'),
|
|
|
|
classDescription: 'Shumway Content Converter Component',
|
|
|
|
contractID: '@mozilla.org/streamconv;1?from=application/x-shockwave-flash&to=*/*'
|
|
|
|
});
|
|
|
|
|
2013-10-17 14:11:52 -07:00
|
|
|
function ShumwayStreamOverlayConverter() {}
|
|
|
|
ShumwayStreamOverlayConverter.prototype = new ShumwayStreamConverterBase();
|
|
|
|
copyProperties(ShumwayStreamOverlayConverter.prototype, {
|
2013-09-30 16:24:58 -07:00
|
|
|
classID: Components.ID('{4c6030f7-e20a-264f-5f9b-ada3a9e97384}'),
|
|
|
|
classDescription: 'Shumway PlayPreview Component',
|
|
|
|
contractID: '@mozilla.org/streamconv;1?from=application/x-moz-playpreview&to=*/*'
|
|
|
|
});
|
2013-10-17 14:11:52 -07:00
|
|
|
ShumwayStreamOverlayConverter.prototype.getUrlHint = function (requestUrl) {
|
2013-09-30 16:24:58 -07:00
|
|
|
return '';
|
|
|
|
};
|