Merge m-c to inbound, a=merge

MozReview-Commit-ID: 3PWdAnh0hk5
This commit is contained in:
Wes Kocher 2016-03-01 15:29:43 -08:00
commit 092655aa0a
147 changed files with 1215 additions and 77118 deletions

View File

@ -72,7 +72,6 @@ browser/components/tabview/**
browser/components/translation/**
browser/extensions/pdfjs/**
browser/extensions/pocket/content/panels/js/vendor/**
browser/extensions/shumway/**
browser/locales/**
# Ignore all of loop since it is imported from github and checked at source.
@ -152,10 +151,6 @@ mobile/android/locales/
# Pretty sure we're disabling this one anyway
mobile/android/modules/ContactService.jsm
# es7 proposed: array comprehensions
# https://github.com/eslint/espree/issues/125
mobile/android/modules/WebappManager.jsm
# Non-standard `(catch ex if ...)`
mobile/android/components/Snippets.js

View File

@ -1397,11 +1397,6 @@ pref("pdfjs.firstRun", true);
pref("pdfjs.previousHandler.preferredAction", 0);
pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
// Shumway is only bundled in Nightly.
#ifdef NIGHTLY_BUILD
pref("shumway.disabled", true);
#endif
// The maximum amount of decoded image data we'll willingly keep around (we
// might keep around more than this, but we'll try to get down to this value).
// (This is intentionally on the high side; see bug 746055.)

View File

@ -6648,6 +6648,18 @@ var gIdentityHandler = {
this._identityPopup.hidePopup();
},
removeCertException() {
if (!this._uriHasHost) {
Cu.reportError("Trying to revoke a cert exception on a URI without a host?");
return;
}
let host = this._uri.host;
let port = this._uri.port > 0 ? this._uri.port : 443;
this._overrideService.clearValidityOverride(host, port);
BrowserReloadSkipCache();
this._identityPopup.hidePopup();
},
/**
* Helper to parse out the important parts of _sslStatus (of the SSL cert in
* particular) for use in constructing identity UI strings

View File

@ -153,22 +153,28 @@ Sanitizer.prototype = {
};
// Array of objects in form { name, promise }.
// Name is the itemName and promise may be a promise, if the sanitization
// is asynchronous, or the function return value, if synchronous.
let promises = [];
// `name` is the item's name and `promise` may be a promise, if the
// sanitization is asynchronous, or the function return value, otherwise.
let handles = [];
for (let itemName of itemsToClear) {
let item = this.items[itemName];
// Workaround for bug 449811.
let name = itemName;
let item = this.items[name];
try {
// Note we need to catch errors here, otherwise Promise.all would stop
// at the first rejection.
promises.push(item.clear(range)
.then(() => progress[itemName] = "cleared",
ex => annotateError(itemName, ex)));
// Catch errors here, so later we can just loop through these.
handles.push({ name,
promise: item.clear(range)
.then(() => progress[name] = "cleared",
ex => annotateError(name, ex))
});
} catch (ex) {
annotateError(itemName, ex);
annotateError(name, ex);
}
}
yield Promise.all(promises);
for (let handle of handles) {
progress[handle.name] = "blocking";
yield handle.promise;
}
// Sanitization is complete.
TelemetryStopwatch.finish("FX_SANITIZE_TOTAL", refObj);

View File

@ -117,6 +117,12 @@
<description id="identity-popup-content-verifier"
when-connection="secure secure-ev secure-cert-user-overridden"/>
<!-- Remove Certificate Exception -->
<button when-connection="secure-cert-user-overridden"
label="&identity.removeCertException.label;"
accesskey="&identity.removeCertException.accesskey;"
oncommand="gIdentityHandler.removeCertException()"/>
<!-- Connection is Not Secure -->
<description when-connection="not-secure"
and-when-loginforms="secure">&identity.description.insecure;</description>

View File

@ -2,36 +2,208 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
PlatformInfo,
EventManager,
PlatformInfo,
} = ExtensionUtils;
// WeakMap[Extension -> Map[name => Command]]
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// WeakMap[Extension -> CommandList]
var commandsMap = new WeakMap();
function Command(description, shortcut) {
this.description = description;
this.shortcut = shortcut;
function CommandList(commandsObj, extensionID) {
this.commands = this.loadCommandsFromManifest(commandsObj);
this.keysetID = `ext-keyset-id-${makeWidgetId(extensionID)}`;
this.windowOpenListener = null;
this.register();
EventEmitter.decorate(this);
}
CommandList.prototype = {
/**
* Registers the commands to all open windows and to any which
* are later created.
*/
register() {
for (let window of WindowListManager.browserWindows()) {
this.registerKeysToDocument(window.document);
}
this.windowOpenListener = (window) => {
this.registerKeysToDocument(window.document);
};
WindowListManager.addOpenListener(this.windowOpenListener);
},
/**
* Unregisters the commands from all open windows and stops commands
* from being registered to windows which are later created.
*/
unregister() {
for (let window of WindowListManager.browserWindows()) {
let keyset = window.document.getElementById(this.keysetID);
if (keyset) {
keyset.remove();
}
}
WindowListManager.removeOpenListener(this.windowOpenListener);
},
/**
* Creates a Map from commands for each command in the manifest.commands object.
* @param {Object} commandsObj The manifest.commands JSON object.
*/
loadCommandsFromManifest(commandsObj) {
let commands = new Map();
// For Windows, chrome.runtime expects 'win' while chrome.commands
// expects 'windows'. We can special case this for now.
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
for (let name of Object.keys(commandsObj)) {
let command = commandsObj[name];
commands.set(name, {
description: command.description,
shortcut: command.suggested_key[os] || command.suggested_key.default,
});
}
return commands;
},
/**
* Registers the commands to a document.
* @param {Document} doc The XUL document to insert the Keyset.
*/
registerKeysToDocument(doc) {
let keyset = doc.createElementNS(XUL_NS, "keyset");
keyset.id = this.keysetID;
this.commands.forEach((command, name) => {
let keyElement = this.buildKey(doc, name, command.shortcut);
keyset.appendChild(keyElement);
});
doc.documentElement.appendChild(keyset);
},
/**
* Builds a XUL Key element and attaches an onCommand listener which
* emits a command event with the provided name when fired.
*
* @param {Document} doc The XUL document.
* @param {String} name The name of the command.
* @param {String} shortcut The shortcut provided in the manifest.
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
*
* @returns {Document} The newly created Key element.
*/
buildKey(doc, name, shortcut) {
let keyElement = this.buildKeyFromShortcut(doc, shortcut);
// We need to have the attribute "oncommand" for the "command" listener to fire,
// and it is currently ignored when set to the empty string.
keyElement.setAttribute("oncommand", "//");
/* eslint-disable mozilla/balanced-listeners */
// We remove all references to the key elements when the extension is shutdown,
// therefore the listeners for these elements will be garbage collected.
keyElement.addEventListener("command", (event) => {
this.emit("command", name);
});
/* eslint-enable mozilla/balanced-listeners */
return keyElement;
},
/**
* Builds a XUL Key element from the provided shortcut.
*
* @param {Document} doc The XUL document.
* @param {String} name The name of the command.
* @param {String} shortcut The shortcut provided in the manifest.
*
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
* @returns {Document} The newly created Key element.
*/
buildKeyFromShortcut(doc, shortcut) {
let keyElement = doc.createElementNS(XUL_NS, "key");
let parts = shortcut.split("+");
// The key is always the last element.
let chromeKey = parts.pop();
// The modifiers are the remaining elements.
keyElement.setAttribute("modifiers", this.getModifiersAttribute(parts));
if (/^[A-Z0-9]$/.test(chromeKey)) {
// We use the key attribute for all single digits and characters.
keyElement.setAttribute("key", chromeKey);
} else {
keyElement.setAttribute("keycode", this.getKeycodeAttribute(chromeKey));
}
return keyElement;
},
/**
* Determines the corresponding XUL keycode from the given chrome key.
*
* For example:
*
* input | output
* ---------------------------------------
* "PageUP" | "VK_PAGE_UP"
* "Delete" | "VK_DELETE"
*
* @param {String} key The chrome key (e.g. "PageUp", "Space", ...)
* @return The constructed value for the Key's 'keycode' attribute.
*/
getKeycodeAttribute(chromeKey) {
return `VK${chromeKey.replace(/([A-Z])/g, "_$&").toUpperCase()}`;
},
/**
* Determines the corresponding XUL modifiers from the chrome modifiers.
*
* For example:
*
* input | output
* ---------------------------------------
* ["Ctrl", "Shift"] | "accel shift"
* ["MacCtrl"] | "control"
*
* @param {Array} chromeModifiers The array of chrome modifiers.
* @return The constructed value for the Key's 'modifiers' attribute.
*/
getModifiersAttribute(chromeModifiers) {
let modifiersMap = {
"Alt" : "alt",
"Command" : "accel",
"Ctrl" : "accel",
"MacCtrl" : "control",
"Shift" : "shift",
};
return Array.from(chromeModifiers, modifier => {
return modifiersMap[modifier];
}).join(" ");
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_commands", (type, directive, extension, manifest) => {
let commands = new Map();
for (let name of Object.keys(manifest.commands)) {
let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
let manifestCommand = manifest.commands[name];
let description = manifestCommand.description;
let shortcut = manifestCommand.suggested_key[os] || manifestCommand.suggested_key.default;
let command = new Command(description, shortcut);
commands.set(name, command);
}
commandsMap.set(extension, commands);
commandsMap.set(extension, new CommandList(manifest.commands, extension.id));
});
extensions.on("shutdown", (type, extension) => {
commandsMap.delete(extension);
let commandsList = commandsMap.get(extension);
if (commandsList) {
commandsList.unregister();
commandsMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
@ -39,15 +211,24 @@ extensions.registerSchemaAPI("commands", null, (extension, context) => {
return {
commands: {
getAll() {
let commands = Array.from(commandsMap.get(extension), ([name, command]) => {
let commands = commandsMap.get(extension).commands;
return Promise.resolve(Array.from(commands, ([name, command]) => {
return ({
name,
description: command.description,
shortcut: command.shortcut,
});
});
return Promise.resolve(commands);
}));
},
onCommand: new EventManager(context, "commands.onCommand", fire => {
let listener = (event, name) => {
fire(name);
};
commandsMap.get(extension).on("command", listener);
return () => {
commandsMap.get(extension).off("command", listener);
};
}).api(),
},
};
});

View File

@ -828,12 +828,26 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
let gBrowser = tab.ownerDocument.defaultView.gBrowser;
let newTab = gBrowser.duplicateTab(tab);
gBrowser.moveTabTo(newTab, tab._tPos + 1);
gBrowser.selectTabAtIndex(newTab._tPos);
return new Promise(resolve => {
// We need to use SSTabRestoring because any attributes set before
// are ignored. SSTabRestored is too late and results in a jump in
// the UI. See http://bit.ly/session-store-api for more information.
newTab.addEventListener("SSTabRestoring", function listener() {
// As the tab is restoring, move it to the correct position.
newTab.removeEventListener("SSTabRestoring", listener);
// Pinned tabs that are duplicated are inserted
// after the existing pinned tab and pinned.
if (tab.pinned) {
gBrowser.pinTab(newTab);
}
gBrowser.moveTabTo(newTab, tab._tPos + 1);
});
newTab.addEventListener("SSTabRestored", function listener() {
// Once it has been restored, select it and return the promise.
newTab.removeEventListener("SSTabRestored", listener);
gBrowser.selectedTab = newTab;
return resolve(TabManager.convert(extension, newTab));
});
});

View File

@ -107,7 +107,6 @@
"events": [
{
"name": "onCommand",
"unsupported": true,
"description": "Fired when a registered command is activated using a keyboard shortcut.",
"type": "function",
"parameters": [

View File

@ -11,7 +11,6 @@ support-files =
file_iframe_document.sjs
[browser_ext_simple.js]
[browser_ext_commands.js]
[browser_ext_currentWindow.js]
[browser_ext_browserAction_simple.js]
[browser_ext_browserAction_pageAction_icon.js]
@ -22,6 +21,8 @@ support-files =
[browser_ext_browserAction_popup.js]
[browser_ext_popup_api_injection.js]
[browser_ext_contextMenus.js]
[browser_ext_commands_getAll.js]
[browser_ext_commands_onCommand.js]
[browser_ext_getViews.js]
[browser_ext_lastError.js]
[browser_ext_runtime_setUninstallURL.js]

View File

@ -0,0 +1,74 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(function* () {
// Create a window before the extension is loaded.
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.loadURI(win1.gBrowser.selectedBrowser, "about:robots");
yield BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"name": "Commands Extension",
"commands": {
"toggle-feature-using-alt-shift-3": {
"suggested_key": {
"default": "Alt+Shift+3",
},
},
"toggle-feature-using-alt-shift-comma": {
"suggested_key": {
"default": "Alt+Shift+Comma",
},
},
},
},
background: function() {
browser.commands.onCommand.addListener((message) => {
browser.test.sendMessage("oncommand", message);
});
browser.test.sendMessage("ready");
},
});
yield extension.startup();
yield extension.awaitMessage("ready");
// Create another window after the extension is loaded.
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
// Confirm the keysets have been added to both windows.
let keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
let keyset = win1.document.getElementById(keysetID);
is(keyset.childNodes.length, 2, "Expected keyset to exist and have 2 children");
keyset = win2.document.getElementById(keysetID);
is(keyset.childNodes.length, 2, "Expected keyset to exist and have 2 children");
// Confirm that the commands are registered to both windows.
yield focusWindow(win1);
EventUtils.synthesizeKey("3", {altKey: true, shiftKey: true});
let message = yield extension.awaitMessage("oncommand");
is(message, "toggle-feature-using-alt-shift-3", "Expected onCommand listener to fire with correct message");
yield focusWindow(win2);
EventUtils.synthesizeKey("VK_COMMA", {altKey: true, shiftKey: true});
message = yield extension.awaitMessage("oncommand");
is(message, "toggle-feature-using-alt-shift-comma", "Expected onCommand listener to fire with correct message");
yield extension.unload();
// Confirm that the keysets have been removed from both windows after the extension is unloaded.
keyset = win1.document.getElementById(keysetID);
is(keyset, null, "Expected keyset to be removed from the window");
keyset = win2.document.getElementById(keysetID);
is(keyset, null, "Expected keyset to be removed from the window");
yield BrowserTestUtils.closeWindow(win1);
yield BrowserTestUtils.closeWindow(win2);
});

View File

@ -35,10 +35,42 @@ add_task(function* testDuplicateTab() {
yield extension.awaitFinish("tabs.duplicate");
yield extension.unload();
while (window.gBrowser.tabs.length > 1) {
let tab = window.gBrowser.tabs[0];
if (tab.linkedBrowser.currentURI.spec === "http://example.net/") {
yield BrowserTestUtils.removeTab(tab);
}
while (gBrowser.tabs[0].linkedBrowser.currentURI.spec === "http://example.net/") {
yield BrowserTestUtils.removeTab(gBrowser.tabs[0]);
}
});
add_task(function* testDuplicatePinnedTab() {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
gBrowser.pinTab(tab);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
},
background: function() {
browser.tabs.query({
lastFocusedWindow: true,
}, function(tabs) {
// Duplicate the pinned tab, example.net.
browser.tabs.duplicate(tabs[0].id, (tab) => {
browser.test.assertEq("http://example.net/", tab.url);
// Should be the second tab, next to the one duplicated.
browser.test.assertEq(1, tab.index);
// Should be pinned.
browser.test.assertTrue(tab.pinned);
browser.test.notifyPass("tabs.duplicate.pinned");
});
});
},
});
yield extension.startup();
yield extension.awaitFinish("tabs.duplicate.pinned");
yield extension.unload();
while (gBrowser.tabs[0].linkedBrowser.currentURI.spec === "http://example.net/") {
yield BrowserTestUtils.removeTab(gBrowser.tabs[0]);
}
});

View File

@ -64,11 +64,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PdfJs",
XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor",
"resource:///modules/ProcessHangMonitor.jsm");
if (AppConstants.NIGHTLY_BUILD) {
XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
"resource://shumway/ShumwayUtils.jsm");
}
XPCOMUtils.defineLazyModuleGetter(this, "webrtcUI",
"resource:///modules/webrtcUI.jsm");
@ -995,13 +990,6 @@ BrowserGlue.prototype = {
// passively.
Services.ppmm.loadProcessScript("resource://pdf.js/pdfjschildbootstrap.js", true);
if (AppConstants.NIGHTLY_BUILD) {
// Registering Shumway bootstrap script the child processes.
Services.ppmm.loadProcessScript("chrome://shumway/content/bootstrap-content.js", true);
// Initializing Shumway (shall be run after child script registration).
ShumwayUtils.init();
}
if (AppConstants.platform == "win") {
// For Windows 7, initialize the jump list module.
const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";

View File

@ -33,7 +33,6 @@ support-files =
skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be the same. - Got 0, expected 1"
[browser_forgetthissite_single.js]
[browser_history_sidebar_search.js]
skip-if = e10s && (os == 'linux' || os == 'mac') # Bug 1116457
[browser_library_batch_delete.js]
[browser_library_commands.js]
[browser_library_downloads.js]

View File

@ -1,89 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(function* test () {
let sidebar = document.getElementById("sidebar");
/**
* Bug 392497 - search in history sidebar loses sort
*/
// Visited pages listed by descending visit date.
let pages = [
"http://sidebar.mozilla.org/a",
"http://sidebar.mozilla.org/b",
"http://sidebar.mozilla.org/c",
"http://www.mozilla.org/d",
];
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var bh = hs.QueryInterface(Ci.nsIBrowserHistory);
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
function uri(spec) {
return ios.newURI(spec, null, null);
}
// Number of pages that will be filtered out by the search.
const FILTERED_COUNT = 1;
var sidebar = document.getElementById("sidebar");
yield PlacesTestUtils.clearHistory();
// Visited pages listed by descending visit date.
var pages = [
"http://sidebar.mozilla.org/a",
"http://sidebar.mozilla.org/b",
"http://sidebar.mozilla.org/c",
"http://www.mozilla.org/d",
];
// Number of pages that will be filtered out by the search.
const FILTERED_COUNT = 1;
function test() {
waitForExplicitFinish();
// Cleanup.
PlacesTestUtils.clearHistory().then(continue_test);
}
function continue_test() {
// Add some visited page.
var time = Date.now();
var pagesLength = pages.length;
var places = [];
for (var i = 0; i < pagesLength; i++) {
places.push({uri: uri(pages[i]), visitDate: (time - i) * 1000,
transition: hs.TRANSITION_TYPED});
let time = Date.now();
let places = [];
for (let i = 0; i < pages.length; i++) {
places.push({ uri: NetUtil.newURI(pages[i]),
visitDate: (time - i) * 1000,
transition: PlacesUtils.history.TRANSITION_TYPED });
}
PlacesTestUtils.addVisits(places).then(() => {
SidebarUI.show("viewHistorySidebar");
yield PlacesTestUtils.addVisits(places);
yield withSidebarTree("history", function* () {
info("Set 'by last visited' view");
sidebar.contentDocument.getElementById("bylastvisited").doCommand();
let tree = sidebar.contentDocument.getElementById("historyTree");
check_tree_order(tree, pages);
// Set a search value.
let searchBox = sidebar.contentDocument.getElementById("search-box");
ok(searchBox, "search box is in context");
searchBox.value = "sidebar.mozilla";
searchBox.doCommand();
check_tree_order(tree, pages, -FILTERED_COUNT);
info("Reset the search");
searchBox.value = "";
searchBox.doCommand();
check_tree_order(tree, pages);
});
sidebar.addEventListener("load", function() {
sidebar.removeEventListener("load", arguments.callee, true);
executeSoon(function() {
// Set "by last visited" in the sidebar (sort by visit date descendind).
sidebar.contentDocument.getElementById("bylastvisited").doCommand();
check_sidebar_tree_order(pages.length);
var searchBox = sidebar.contentDocument.getElementById("search-box");
ok(searchBox, "search box is in context");
searchBox.value = "sidebar.mozilla";
searchBox.doCommand();
check_sidebar_tree_order(pages.length - FILTERED_COUNT);
searchBox.value = "";
searchBox.doCommand();
check_sidebar_tree_order(pages.length);
yield PlacesTestUtils.clearHistory();
});
// Cleanup.
SidebarUI.hide();
PlacesTestUtils.clearHistory().then(finish);
});
}, true);
}
function check_sidebar_tree_order(aExpectedRows) {
var tree = sidebar.contentDocument.getElementById("historyTree");
var treeView = tree.view;
var rc = treeView.rowCount;
var columns = tree.columns;
function check_tree_order(tree, pages, aNumberOfRowsDelta = 0) {
let treeView = tree.view;
let columns = tree.columns;
is(columns.count, 1, "There should be only 1 column in the sidebar");
var found = 0;
for (var r = 0; r < rc; r++) {
var node = treeView.nodeForTreeIndex(r);
// We could inherit visits from previous tests, skip them since they are
// not interesting for us.
let found = 0;
for (let i = 0; i < treeView.rowCount; i++) {
let node = treeView.nodeForTreeIndex(i);
// We could inherit delayed visits from previous tests, skip them.
if (!pages.includes(node.uri))
continue;
is(node.uri, pages[r], "Node is in correct position based on its visit date");
is(node.uri, pages[i], "Node is in correct position based on its visit date");
found++;
}
ok(found, aExpectedRows, "Found all expected results");
ok(found, pages.length + aNumberOfRowsDelta, "Found all expected results");
}

View File

@ -8,5 +8,4 @@ DIRS += [
'loop',
'pdfjs',
'pocket',
'shumway',
]

View File

@ -1,178 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,2 +0,0 @@
content shumway chrome/
resource shumway content/

View File

@ -1,147 +0,0 @@
/*
* 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');
// Helper class to forward external interface messages between chrome code and
// content window / object element.
function ExternalInterface(window, embedTag, callback) {
this.window = window;
this.embedTag = embedTag;
this.callback = callback;
this.externalComInitialized = false;
}
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;
this.initExternalCom(parentWindow, embedTag, this.callback);
return;
case 'getId':
return embedTag.id;
case 'eval':
return this.__flash__eval(data.expression);
case 'call':
return this.__flash__call(data.request);
case 'register':
return this.__flash__registerCallback(data.functionName);
case 'unregister':
return this.__flash__unregisterCallback(data.functionName);
}
},
initExternalCom: function (window, embedTag, onExternalCallback) {
var traceExternalInterface = getBoolPref('shumway.externalInterface.trace', false);
// Initialize convenience functions. Notice that these functions are
// exposed to the content via Cu.exportFunction, so be careful (e.g. don't
// use `this` pointer).
this.__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/>';
}
};
this.__flash__eval = function (expr) {
traceExternalInterface && window.console.log('__flash__eval: ' + expr);
// allowScriptAccess protects page from unwanted swf scripts,
// we can execute script in the page context without restrictions.
var result = window.eval(expr);
traceExternalInterface && window.console.log('__flash__eval (result): ' + result);
return result;
};
this.__flash__call = function (expr) {
traceExternalInterface && window.console.log('__flash__call (ignored): ' + expr);
};
this.__flash__registerCallback = function (functionName) {
traceExternalInterface && window.console.log('__flash__registerCallback: ' + functionName);
Components.utils.exportFunction(function () {
var args = Array.prototype.slice.call(arguments, 0);
traceExternalInterface && window.console.log('__flash__callIn: ' + functionName);
var result;
if (onExternalCallback) {
result = onExternalCallback({functionName: functionName, args: args});
traceExternalInterface && window.console.log('__flash__callIn (result): ' + result);
}
return window.eval(result);
}, embedTag.wrappedJSObject, {defineAs: functionName});
};
this.__flash__unregisterCallback = function (functionName) {
traceExternalInterface && window.console.log('__flash__unregisterCallback: ' + functionName);
delete embedTag.wrappedJSObject[functionName];
};
this.exportInterfaceFunctions();
},
exportInterfaceFunctions: function () {
var window = this.window;
// We need to export window functions only once.
if (!window.wrappedJSObject.__flash__initialized) {
window.wrappedJSObject.__flash__initialized = true;
// JavaScript eval'ed code use those functions.
// TODO find the case when JavaScript code patches those
// __flash__toXML will be called by the eval'ed code.
Components.utils.exportFunction(this.__flash__toXML, window.wrappedJSObject,
{defineAs: '__flash__toXML'});
// The functions below are used for compatibility and are not called by chrome code.
Components.utils.exportFunction(this.__flash__eval, window.wrappedJSObject,
{defineAs: '__flash__eval'});
Components.utils.exportFunction(this.__flash__call, window.wrappedJSObject,
{defineAs: '__flash__call'});
}
}
};
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}

View File

@ -1,323 +0,0 @@
/*
* 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 FileLoaderSession(sessionId) {
this.sessionId = sessionId;
this.xhr = null;
}
FileLoaderSession.prototype = {
abort() {
if (this.xhr) {
this.xhr.abort();
this.xhr = null;
}
}
};
function FileLoader(swfUrl, baseUrl, refererUrl, callback) {
this.swfUrl = swfUrl;
this.baseUrl = baseUrl;
this.refererUrl = refererUrl;
this.callback = callback;
this.activeSessions = Object.create(null);
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 sendReferer = canSendReferer(swfUrl, this.refererUrl);
var session = new FileLoaderSession(sessionId);
this.activeSessions[sessionId] = session;
var self = this;
var performXHR = function () {
// Load has been aborted before we reached this point.
if (!self.activeSessions[sessionId]) {
return;
}
var xhr = session.xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Components.interfaces.nsIXMLHttpRequest);
xhr.open(method, url, true);
xhr.responseType = "moz-chunked-arraybuffer";
if (sendReferer) {
// Setting the referer uri, some site doing checks if swf is embedded
// on the original page.
xhr.setRequestHeader("Referer", self.refererUrl);
}
// TODO apply range request headers if limit is specified
var lastPosition = 0;
xhr.onprogress = function (e) {
var position = e.loaded;
var total = e.total;
var data = new Uint8Array(xhr.response);
// The event's `loaded` and `total` properties are sometimes lower than the actual
// number of loaded bytes. In that case, increase them to that value.
position = Math.max(position, data.byteLength);
total = Math.max(total, data.byteLength);
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId,
topic: "progress", array: data, loaded: position, total: total});
lastPosition = position;
if (limit && total >= limit) {
xhr.abort();
}
};
xhr.onreadystatechange = function(event) {
if (xhr.readyState === 4) {
delete self.activeSessions[sessionId];
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);
delete self.activeSessions[sessionId];
notifyLoadFileListener({
callback: "loadFile", sessionId: sessionId, topic: "error",
error: "only original swf file or file from the same origin loading supported (XDOMAIN)"
});
});
},
abort: function(sessionId) {
var session = this.activeSessions[sessionId];
if (!session) {
log("Warning: trying to abort invalid session " + sessionId);
return;
}
session.abort();
delete this.activeSessions[sessionId];
}
};
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 canSendReferer(url, refererUrl) {
if (!refererUrl) {
return false;
}
// Allow sending HTTPS referer only to HTTPS.
var parsedUrl, parsedRefererUrl;
try {
parsedRefererUrl = NetUtil.newURI(refererUrl);
} catch (ex) { /* skipping invalid urls */ }
if (!parsedRefererUrl ||
parsedRefererUrl.scheme.toLowerCase() !== 'https') {
return true;
}
try {
parsedUrl = NetUtil.newURI(url);
} catch (ex) { /* skipping invalid urls */ }
return !!parsedUrl && parsedUrl.scheme.toLowerCase() === 'https';
}
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);
}

View File

@ -1,258 +0,0 @@
/*
* 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 = ['LocalConnectionService'];
Components.utils.import('resource://gre/modules/NetUtil.jsm');
Components.utils.import('resource://gre/modules/Services.jsm');
const localConnectionsRegistry = Object.create(null);
function isConnectionNameValid(connectionName) {
return typeof connectionName === 'string' &&
(connectionName[0] === '_' || connectionName.split(':').length === 2);
}
/**
* Creates a trusted qualified connection name from an already qualified name and a swfUrl.
*
* While connection names are already qualified at this point, the qualification happens in
* untrusted code. To ensure that the name is correctly qualified, this function compares the
* qualification domain with the current SWF's URL and substitutes that URL's domain if
* required. A warning is logged in that case.
*/
function _getQualifiedConnectionName(connectionName, swfUrl) {
// Already syntactically invalid connection names mustn't get here.
if (!isConnectionNameValid(connectionName)) {
// TODO: add telemetry
throw new Error('Syntactically invalid local-connection name encountered', connectionName,
swfUrl);
}
var [domain, name] = connectionName.split(':');
var parsedURL = NetUtil.newURI(swfUrl);
if (domain !== parsedURL.host) {
// TODO: add telemetry
log('Warning: invalid local-connection name qualification found: ' + connectionName);
return parsedURL.host + ':' + swfUrl;
}
return connectionName;
}
function _getLocalConnection(connectionName) {
// Treat invalid connection names as non-existent. This can only happen if player code
// misbehaves, though.
if (!isConnectionNameValid(connectionName)) {
// TODO: add telemetry
return null;
}
var connection = localConnectionsRegistry[connectionName];
if (connection && Components.utils.isDeadWrapper(connection.callback)) {
delete localConnectionsRegistry[connectionName];
return null;
}
return localConnectionsRegistry[connectionName];
}
function LocalConnectionService(content, environment) {
var traceLocalConnection = getBoolPref('shumway.localConnection.trace', false);
var api = {
createLocalConnection: function (connectionName, callback) {
connectionName = connectionName + '';
traceLocalConnection && content.console.log(`Creating local connection "${connectionName}" ` +
`for SWF with URL ${environment.swfUrl}`);
if (!isConnectionNameValid(connectionName)) {
// TODO: add telemetry
traceLocalConnection && content.console.warn(`Invalid localConnection name `);
return -1; // LocalConnectionConnectResult.InvalidName
}
if (typeof callback !== 'function') {
// TODO: add telemetry
traceLocalConnection && content.console.warn(`Invalid callback for localConnection`);
return -3; // LocalConnectionConnectResult.InvalidCallback
}
connectionName = _getQualifiedConnectionName(connectionName, environment.swfUrl);
if (_getLocalConnection(connectionName)) {
traceLocalConnection && content.console.log(`localConnection ` +
`name "${connectionName}" already taken`);
return -2; // LocalConnectionConnectResult.AlreadyTaken
}
var parsedURL = NetUtil.newURI(environment.swfUrl);
var connection = {
callback,
domain: parsedURL.host,
environment: environment,
secure: parsedURL.protocol === 'https:',
allowedSecureDomains: Object.create(null),
allowedInsecureDomains: Object.create(null)
};
localConnectionsRegistry[connectionName] = connection;
return 0; // LocalConnectionConnectResult.Success
},
hasLocalConnection: function (connectionName) {
connectionName = _getQualifiedConnectionName(connectionName + '', environment.swfUrl);
var result = !!_getLocalConnection(connectionName);
traceLocalConnection && content.console.log(`hasLocalConnection "${connectionName}"? ` +
result);
return result;
},
closeLocalConnection: function (connectionName) {
connectionName = _getQualifiedConnectionName(connectionName + '', environment.swfUrl);
traceLocalConnection && content.console.log(`Closing local connection "${connectionName}" ` +
`for SWF with URL ${environment.swfUrl}`);
var connection = _getLocalConnection(connectionName);
if (!connection) {
traceLocalConnection && content.console.log(`localConnection "${connectionName}" not ` +
`connected`);
return -1; // LocalConnectionCloseResult.NotConnected
} else if (connection.environment !== environment) {
// Attempts to close connections from a SWF instance that didn't create them shouldn't
// happen. If they do, we treat them as if the connection didn't exist.
traceLocalConnection && content.console.warn(`Ignored attempt to close localConnection ` +
`"${connectionName}" from SWF instance that ` +
`didn't create it`);
return -1; // LocalConnectionCloseResult.NotConnected
}
delete localConnectionsRegistry[connectionName];
return 0; // LocalConnectionCloseResult.Success
},
sendLocalConnectionMessage: function (connectionName, methodName, argsBuffer, sender,
senderDomain, senderIsSecure) {
connectionName = connectionName + '';
methodName = methodName + '';
senderDomain = senderDomain + '';
senderIsSecure = !!senderIsSecure;
// TODO: sanitize argsBuffer argument. Ask bholley how to do so.
traceLocalConnection && content.console.log(`sending localConnection message ` +
`"${methodName}" to "${connectionName}"`);
// Since we don't currently trust the sender information passed in here, we use the
// currently running SWF's URL instead.
var parsedURL = NetUtil.newURI(environment.swfUrl);
var parsedURLIsSecure = parsedURL.protocol === 'https:';
if (parsedURL.host !== senderDomain || parsedURLIsSecure !== senderIsSecure) {
traceLocalConnection && content.console.warn(`sending localConnection message ` +
`"${methodName}" to "${connectionName}"`);
}
senderDomain = parsedURL.host;
senderIsSecure = parsedURLIsSecure;
var connection = _getLocalConnection(connectionName);
if (!connection) {
traceLocalConnection && content.console.log(`localConnection "${connectionName}" not ` +
`connected`);
return;
}
try {
var allowed = false;
if (connection.secure) {
// If the receiver is secure, the sender has to be, too, or it has to be whitelisted
// with allowInsecureDomain.
if (senderIsSecure) {
if (senderDomain === connection.domain ||
senderDomain in connection.allowedSecureDomains ||
'*' in connection.allowedSecureDomains) {
allowed = true;
}
} else {
if (senderDomain in connection.allowedInsecureDomains ||
'*' in connection.allowedInsecureDomains) {
allowed = true;
}
}
} else {
// For non-secure connections, allowedSecureDomains is expected to contain all allowed
// domains, secure on non-secure, so we don't have to check both.
if (senderDomain === connection.domain ||
senderDomain in connection.allowedSecureDomains ||
'*' in connection.allowedSecureDomains) {
allowed = true;
}
}
if (!allowed) {
traceLocalConnection && content.console.warn(`LocalConnection message rejected: domain ` +
`${senderDomain} not allowed.`);
return {
name: 'SecurityError',
$Bgmessage: "The current security context does not allow this operation.",
_errorID: 3315
};
}
var callback = connection.callback;
var clonedArgs = Components.utils.cloneInto(argsBuffer, callback);
callback(methodName, clonedArgs);
} catch (e) {
// TODO: add telemetry
content.console.warn('Unexpected error encountered while sending LocalConnection message.');
}
},
allowDomainsForLocalConnection: function (connectionName, domains, secure) {
connectionName = _getQualifiedConnectionName(connectionName + '', environment.swfUrl);
secure = !!secure;
var connection = _getLocalConnection(connectionName);
if (!connection) {
return;
}
try {
domains = Components.utils.cloneInto(domains, connection);
} catch (e) {
log('error in allowDomainsForLocalConnection: ' + e);
return;
}
traceLocalConnection && content.console.log(`allowing ${secure ? '' : 'in'}secure domains ` +
`[${domains}] for localConnection ` +
`"${connectionName}"`);
function validateDomain(domain) {
if (typeof domain !== 'string') {
return false;
}
if (domain === '*') {
return true;
}
try {
var uri = NetUtil.newURI('http://' + domain);
return uri.host === domain;
} catch (e) {
return false;
}
}
if (!Array.isArray(domains) || !domains.every(validateDomain)) {
traceLocalConnection && content.console.warn(`Invalid domains rejected`);
return;
}
var allowedDomains = secure ?
connection.allowedSecureDomains :
connection.allowedInsecureDomains;
domains.forEach(domain => allowedDomains[domain] = true);
}
};
// Don't return `this` even though this function is treated as a ctor. Makes cloning into the
// content compartment an internal operation the client code doesn't have to worry about.
return Components.utils.cloneInto(api, content, {cloneFunctions:true});
}
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}

View File

@ -1,159 +0,0 @@
/*
* Copyright 2014 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 = ['RtmpUtils'];
Components.utils.import('resource://gre/modules/Services.jsm');
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
var RtmpUtils = {
get isRtmpEnabled() {
try {
return Services.prefs.getBoolPref('shumway.rtmp.enabled');
} catch (ex) {
return false;
}
},
createSocket: function (sandbox, params) {
var host = params.host, port = params.port, ssl = params.ssl;
var baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
var socket = baseSocket.open(host, port, {useSecureTransport: ssl, binaryType: 'arraybuffer'});
if (!socket) {
return null;
}
socket.onopen = function () {
if (wrapperOnOpen) {
wrapperOnOpen(new sandbox.Object());
}
};
socket.ondata = function (e) {
if (wrapperOnData) {
var wrappedE = new sandbox.Object();
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
wrapperOnData(wrappedE);
}
};
socket.ondrain = function () {
if (wrapperOnDrain) {
wrapperOnDrain(new sandbox.Object());
}
};
socket.onerror = function (e) {
if (wrapperOnError) {
var wrappedE = new sandbox.Object();
wrappedE.data = Components.utils.cloneInto(e.data, sandbox);
wrapperOnError(wrappedE);
}
};
socket.onclose = function () {
if (wrapperOnClose) {
wrapperOnClose(new sandbox.Object());
}
};
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: function (buffer, offset, count) {
return socket.send(buffer, offset, count);
},
close: function () {
socket.close();
}
}, sandbox, {cloneFunctions:true});
return wrapper;
},
createXHR: function (sandbox) {
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 (wrapperOnLoad) {
wrapperOnLoad(new sandbox.Object());
}
};
xhr.onerror = function () {
wrapper.status = xhr.status;
if (wrapperOnError) {
wrapperOnError(new sandbox.Object());
}
};
var wrapperOnLoad, wrapperOnError;
var wrapper = Components.utils.cloneInto({
status: 0,
response: undefined,
responseType: 'text',
setLoadCallback: function (callback) {
wrapperOnLoad = callback;
},
setErrorCallback: function (callback) {
wrapperOnError = callback;
},
open: function (method, path, async) {
if (method !== 'POST' || !path || (async !== undefined && !async)) {
throw new Error('invalid open() arguments');
}
// TODO check path
xhr.open('POST', path, true);
xhr.responseType = 'arraybuffer';
xhr.setRequestHeader('Content-Type', 'application/x-fcs');
},
setRequestHeader: function (header, value) {
if (header !== 'Content-Type' || value !== 'application/x-fcs') {
throw new Error('invalid setRequestHeader() arguments');
}
},
send: function (data) {
if (wrapper.responseType !== 'arraybuffer') {
throw new Error('Invalid responseType.');
}
xhr.send(data);
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

View File

@ -1,777 +0,0 @@
/*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var EXPORTED_SYMBOLS = ['ShumwayCom'];
Components.utils.import('resource://gre/modules/NetUtil.jsm');
Components.utils.import('resource://gre/modules/Promise.jsm');
Components.utils.import('resource://gre/modules/Services.jsm');
Components.utils.import('resource://gre/modules/XPCOMUtils.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');
Components.utils.import('chrome://shumway/content/LocalConnection.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
'resource://shumway/ShumwayTelemetry.jsm');
const MAX_USER_INPUT_TIMEOUT = 250; // ms
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function getCharPref(pref, def) {
try {
return Services.prefs.getCharPref(pref);
} catch (ex) {
return def;
}
}
function log(aMsg) {
let msg = 'ShumwayCom.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function sanitizeTelemetryArgs(args) {
var request = {
topic: String(args.topic)
};
switch (request.topic) {
case 'firstFrame':
break;
case 'parseInfo':
request.info = {
parseTime: +args.parseTime,
size: +args.bytesTotal,
swfVersion: args.swfVersion | 0,
frameRate: +args.frameRate,
width: args.width | 0,
height: args.height | 0,
bannerType: args.bannerType | 0,
isAvm2: !!args.isAvm2
};
break;
case 'feature':
request.featureType = args.feature | 0;
break;
case 'loadResource':
request.resultType = args.resultType | 0;
break;
case 'error':
request.errorType = args.error | 0;
break;
}
return request;
}
function sanitizeLoadFileArgs(args) {
return {
url: String(args.url || ''),
checkPolicyFile: !!args.checkPolicyFile,
sessionId: +args.sessionId,
limit: +args.limit || 0,
mimeType: String(args.mimeType || ''),
method: (args.method + '') || 'GET',
postData: args.postData || null
};
}
function sanitizeExternalComArgs(args) {
var request = {
action: String(args.action)
};
switch (request.action) {
case 'eval':
request.expression = String(args.expression);
break;
case 'call':
request.expression = String(args.request);
break;
case 'register':
case 'unregister':
request.functionName = String(args.functionName);
break;
}
return request;
}
var cloneIntoFromContent = (function () {
// waiveXrays are used due to bug 1150771, checking if we are affected
// TODO remove workaround after Firefox 40 is released (2015-08-11)
let sandbox1 = new Components.utils.Sandbox(null);
let sandbox2 = new Components.utils.Sandbox(null);
let arg = Components.utils.evalInSandbox('({buf: new ArrayBuffer(2)})', sandbox1);
let clonedArg = Components.utils.cloneInto(arg, sandbox2);
if (!Components.utils.waiveXrays(clonedArg).buf) {
return function (obj, contentSandbox) {
return Components.utils.cloneInto(
Components.utils.waiveXrays(obj), contentSandbox);
};
}
return function (obj, contentSandbox) {
return Components.utils.cloneInto(obj, contentSandbox);
};
})();
var ShumwayEnvironment = {
DEBUG: 'debug',
DEVELOPMENT: 'dev',
RELEASE: 'release',
TEST: 'test'
};
var ShumwayCom = {
environment: getCharPref('shumway.environment', 'dev'),
createAdapter: function (content, callbacks, hooks) {
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
// up Xray wrappers.
var wrapped = {
environment: ShumwayCom.environment,
enableDebug: function () {
callbacks.enableDebug()
},
fallback: function () {
callbacks.sendMessage('fallback', null, false);
},
getSettings: function () {
return Components.utils.cloneInto(
callbacks.sendMessage('getSettings', null, true), content);
},
getPluginParams: function () {
return Components.utils.cloneInto(
callbacks.sendMessage('getPluginParams', null, true), content);
},
reportIssue: function () {
callbacks.sendMessage('reportIssue', null, false);
},
reportTelemetry: function (args) {
var request = sanitizeTelemetryArgs(args);
callbacks.sendMessage('reportTelemetry', request, false);
},
setupGfxComBridge: function (gfxWindow) {
// Creates ShumwayCom adapter for the gfx iframe exposing only subset
// of the privileged function. Removing Xrays to setup the ShumwayCom
// property and for usage as a sandbox for cloneInto operations.
var gfxContent = gfxWindow.contentWindow.wrappedJSObject;
ShumwayCom.createGfxAdapter(gfxContent, callbacks, hooks);
setupUserInput(gfxWindow.contentWindow, callbacks);
},
setupPlayerComBridge: function (playerWindow) {
// Creates ShumwayCom adapter for the player iframe exposing only subset
// of the privileged function. Removing Xrays to setup the ShumwayCom
// property and for usage as a sandbox for cloneInto operations.
var playerContent = playerWindow.contentWindow.wrappedJSObject;
ShumwayCom.createPlayerAdapter(playerContent, callbacks, hooks);
}
};
var shumwayComAdapter = Components.utils.cloneInto(wrapped, content, {cloneFunctions:true});
content.ShumwayCom = shumwayComAdapter;
},
createGfxAdapter: function (content, callbacks, hooks) {
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
// up Xray wrappers.
var wrapped = {
environment: ShumwayCom.environment,
setFullscreen: function (value) {
value = !!value;
callbacks.sendMessage('setFullscreen', value, false);
},
reportTelemetry: function (args) {
var request = sanitizeTelemetryArgs(args);
callbacks.sendMessage('reportTelemetry', request, false);
},
postAsyncMessage: function (msg) {
if (hooks.onPlayerAsyncMessageCallback) {
hooks.onPlayerAsyncMessageCallback(msg);
}
},
setSyncMessageCallback: function (callback) {
if (typeof callback !== 'function') {
log('error: attempt to set non-callable as callback in setSyncMessageCallback');
return;
}
hooks.onGfxSyncMessageCallback = function (msg, sandbox) {
var reclonedMsg = cloneIntoFromContent(msg, content);
var result = callback(reclonedMsg);
return cloneIntoFromContent(result, sandbox);
};
},
setAsyncMessageCallback: function (callback) {
if (typeof callback !== 'function') {
log('error: attempt to set non-callable as callback in setAsyncMessageCallback');
return;
}
hooks.onGfxAsyncMessageCallback = function (msg) {
var reclonedMsg = cloneIntoFromContent(msg, content);
callback(reclonedMsg);
};
}
};
if (ShumwayCom.environment === ShumwayEnvironment.TEST) {
wrapped.processFrame = function () {
callbacks.sendMessage('processFrame');
};
wrapped.processFSCommand = function (command, args) {
callbacks.sendMessage('processFSCommand', command, args);
};
wrapped.setScreenShotCallback = function (callback) {
callbacks.sendMessage('setScreenShotCallback', callback);
};
}
var shumwayComAdapter = Components.utils.cloneInto(wrapped, content, {cloneFunctions:true});
content.ShumwayCom = shumwayComAdapter;
},
createPlayerAdapter: function (content, callbacks, hooks) {
// Exposing ShumwayCom object/adapter to the unprivileged content -- setting
// up Xray wrappers.
var wrapped = {
environment: ShumwayCom.environment,
externalCom: function (args) {
var request = sanitizeExternalComArgs(args);
var result = String(callbacks.sendMessage('externalCom', request, true));
return result;
},
loadFile: function (args) {
var request = sanitizeLoadFileArgs(args);
callbacks.sendMessage('loadFile', request, false);
},
abortLoad: function (sessionId) {
sessionId = sessionId|0;
callbacks.sendMessage('abortLoad', sessionId, false);
},
reportTelemetry: function (args) {
var request = sanitizeTelemetryArgs(args);
callbacks.sendMessage('reportTelemetry', request, false);
},
setClipboard: function (args) {
if (typeof args !== 'string') {
return; // ignore non-string argument
}
callbacks.sendMessage('setClipboard', args, false);
},
navigateTo: function (args) {
var request = {
url: String(args.url || ''),
target: String(args.target || '')
};
callbacks.sendMessage('navigateTo', request, false);
},
loadSystemResource: function (id) {
loadShumwaySystemResource(id).then(function (data) {
if (onSystemResourceCallback) {
onSystemResourceCallback(id, Components.utils.cloneInto(data, content));
}
});
},
sendSyncMessage: function (msg) {
var result;
if (hooks.onGfxSyncMessageCallback) {
result = hooks.onGfxSyncMessageCallback(msg, content);
}
return result;
},
postAsyncMessage: function (msg) {
if (hooks.onGfxAsyncMessageCallback) {
hooks.onGfxAsyncMessageCallback(msg);
}
},
setAsyncMessageCallback: function (callback) {
if (typeof callback !== 'function') {
log('error: attempt to set non-callable as callback in setAsyncMessageCallback');
return;
}
hooks.onPlayerAsyncMessageCallback = function (msg) {
var reclonedMsg = cloneIntoFromContent(msg, content);
callback(reclonedMsg);
};
},
createSpecialStorage: function () {
var environment = callbacks.getEnvironment();
return SpecialStorageUtils.createWrappedSpecialStorage(content,
environment.swfUrl, environment.privateBrowsing);
},
getWeakMapKeys: function (weakMap) {
var keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakMap);
var result = new content.Array();
keys.forEach(function (key) {
result.push(key);
});
return result;
},
setLoadFileCallback: function (callback) {
if (callback !== null && typeof callback !== 'function') {
return;
}
onLoadFileCallback = callback;
},
setExternalCallback: function (callback) {
if (callback !== null && typeof callback !== 'function') {
return;
}
onExternalCallback = callback;
},
setSystemResourceCallback: function (callback) {
if (callback !== null && typeof callback !== 'function') {
return;
}
onSystemResourceCallback = callback;
},
getLocalConnectionService: function() {
if (!wrappedLocalConnectionService) {
wrappedLocalConnectionService = new LocalConnectionService(content,
callbacks.getEnvironment());
}
return wrappedLocalConnectionService;
}
};
// Exposing createSpecialInflate function for DEFLATE stream decoding using
// Gecko API.
if (SpecialInflateUtils.isSpecialInflateEnabled) {
wrapped.createSpecialInflate = function () {
return SpecialInflateUtils.createWrappedSpecialInflate(content);
};
}
// Exposing createRtmpSocket/createRtmpXHR functions to support RTMP stream
// functionality.
if (RtmpUtils.isRtmpEnabled) {
wrapped.createRtmpSocket = function (params) {
return RtmpUtils.createSocket(content, params);
};
wrapped.createRtmpXHR = function () {
return RtmpUtils.createXHR(content);
};
}
if (ShumwayCom.environment === ShumwayEnvironment.TEST) {
wrapped.print = function(msg) {
callbacks.sendMessage('print', msg);
}
}
var onSystemResourceCallback = null;
var onExternalCallback = null;
var onLoadFileCallback = null;
var wrappedLocalConnectionService = null;
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) {
return new ShumwayChromeActions(startupInfo, window, document);
}
};
function loadShumwaySystemResource(id) {
var url, type;
switch (id) {
case 0: // BuiltinAbc
url = 'resource://shumway/libs/builtin.abc';
type = 'arraybuffer';
break;
case 1: // PlayerglobalAbcs
url = 'resource://shumway/playerglobal/playerglobal.abcs';
type = 'arraybuffer';
break;
case 2: // PlayerglobalManifest
url = 'resource://shumway/playerglobal/playerglobal.json';
type = 'json';
break;
default:
return Promise.reject('Unsupported system resource id');
}
var deferred = Promise.defer();
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
xhr.open('GET', url, true);
xhr.responseType = type;
xhr.onload = function () {
deferred.resolve(xhr.response);
};
xhr.send(null);
return deferred.promise;
}
function setupUserInput(contentWindow, callbacks) {
function notifyUserInput() {
callbacks.sendMessage('userInput', null, true);
}
// Ignoring the untrusted events by providing the 4th argument for addEventListener.
contentWindow.document.addEventListener('mousedown', notifyUserInput, true, false);
contentWindow.document.addEventListener('mouseup', notifyUserInput, true, false);
contentWindow.document.addEventListener('keydown', notifyUserInput, true, false);
contentWindow.document.addEventListener('keyup', notifyUserInput, true, false);
}
// All the privileged actions.
function ShumwayChromeActions(startupInfo, window, document) {
this.url = startupInfo.url;
this.objectParams = startupInfo.objectParams;
this.movieParams = startupInfo.movieParams;
this.baseUrl = startupInfo.baseUrl;
this.isOverlay = startupInfo.isOverlay;
this.embedTag = startupInfo.embedTag;
this.isPausedAtStart = startupInfo.isPausedAtStart;
this.initStartTime = startupInfo.initStartTime;
this.window = window;
this.document = document;
this.allowScriptAccess = startupInfo.allowScriptAccess;
this.lastUserInput = 0;
this.telemetry = {
startTime: Date.now(),
features: [],
errors: []
};
this.fileLoader = new FileLoader(startupInfo.url, startupInfo.baseUrl, startupInfo.refererUrl,
function (args) { this.onLoadFileCallback(args); }.bind(this));
this.onLoadFileCallback = null;
this.externalInterface = null;
this.onExternalCallback = null;
}
ShumwayChromeActions.prototype = {
// The method is created for convenience of routing messages from the OOP
// handler or remote debugger adapter. All method calls are originated from
// the ShumwayCom adapter (see above), or from the debugger adapter.
// See viewerWrapper.js for these usages near sendMessage calls.
invoke: function (name, args) {
return this[name].call(this, args);
},
getBoolPref: function (data) {
if (!/^shumway\./.test(data.pref)) {
return null;
}
return getBoolPref(data.pref, data.def);
},
getSettings: function getSettings() {
return {
compilerSettings: {
appCompiler: getBoolPref('shumway.appCompiler', true),
sysCompiler: getBoolPref('shumway.sysCompiler', false),
verifier: getBoolPref('shumway.verifier', true)
},
playerSettings: {
turboMode: getBoolPref('shumway.turboMode', false),
hud: getBoolPref('shumway.hud', false),
forceHidpi: getBoolPref('shumway.force_hidpi', false)
}
}
},
getPluginParams: function getPluginParams() {
return {
url: this.url,
baseUrl : this.baseUrl,
movieParams: this.movieParams,
objectParams: this.objectParams,
isOverlay: this.isOverlay,
isPausedAtStart: this.isPausedAtStart,
initStartTime: this.initStartTime,
isDebuggerEnabled: getBoolPref('shumway.debug.enabled', false)
};
},
loadFile: function loadFile(data) {
this.fileLoader.load(data);
},
abortLoad: function abortLoad(sessionId) {
this.fileLoader.abort(sessionId);
},
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;
}
var embedTag = this.embedTag;
var window = embedTag ? embedTag.ownerDocument.defaultView : this.window;
window.open(url, target);
},
fallback: function(automatic) {
automatic = !!automatic;
var event = this.document.createEvent('CustomEvent');
event.initCustomEvent('shumwayFallback', true, true, {
automatic: automatic
});
this.window.dispatchEvent(event);
},
userInput: function() {
// Recording time of last user input for isUserInputInProgress below.
this.lastUserInput = Date.now();
},
isUserInputInProgress: function () {
// We don't trust our Shumway non-privileged code just yet to verify the
// user input -- using userInput function above to track that.
if ((Date.now() - this.lastUserInput) > MAX_USER_INPUT_TIMEOUT) {
return false;
}
// TODO other security checks?
return true;
},
setClipboard: function (data) {
if (!this.isUserInputInProgress()) {
return;
}
let clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
.getService(Components.interfaces.nsIClipboardHelper);
clipboard.copyString(data);
},
setFullscreen: function (enabled) {
if (!this.isUserInputInProgress()) {
return;
}
var target = this.embedTag || this.document.body;
if (enabled) {
target.mozRequestFullScreen();
} else {
target.ownerDocument.mozCancelFullScreen();
}
},
reportTelemetry: function (request) {
switch (request.topic) {
case 'firstFrame':
var time = Date.now() - this.telemetry.startTime;
ShumwayTelemetry.onFirstFrame(time);
break;
case 'parseInfo':
ShumwayTelemetry.onParseInfo(request.info);
break;
case 'feature':
var featureType = request.featureType;
var MIN_FEATURE_TYPE = 0, MAX_FEATURE_TYPE = 999;
if (featureType >= MIN_FEATURE_TYPE && featureType <= MAX_FEATURE_TYPE &&
!this.telemetry.features[featureType]) {
this.telemetry.features[featureType] = true; // record only one feature per SWF
ShumwayTelemetry.onFeature(featureType);
}
break;
case 'loadResource':
var resultType = request.resultType;
var MIN_RESULT_TYPE = 0, MAX_RESULT_TYPE = 10;
if (resultType >= MIN_RESULT_TYPE && resultType <= MAX_RESULT_TYPE) {
ShumwayTelemetry.onLoadResource(resultType);
}
break;
case 'error':
var errorType = request.errorType;
var MIN_ERROR_TYPE = 0, MAX_ERROR_TYPE = 2;
if (errorType >= MIN_ERROR_TYPE && errorType <= MAX_ERROR_TYPE &&
!this.telemetry.errors[errorType]) {
this.telemetry.errors[errorType] = true; // record only one report per SWF
ShumwayTelemetry.onError(errorType);
}
break;
}
},
reportIssue: function (exceptions) {
var urlTemplate = "https://bugzilla.mozilla.org/enter_bug.cgi?op_sys=All&priority=--" +
"&rep_platform=All&target_milestone=---&version=Trunk&product=Firefox" +
"&component=Shumway&short_desc=&comment={comment}" +
"&bug_file_loc={url}";
var windowUrl = this.window.parent.location.href + '';
var url = urlTemplate.split('{url}').join(encodeURIComponent(windowUrl));
var params = {
swf: encodeURIComponent(this.url)
};
getVersionInfo().then(function (versions) {
params.versions = versions;
}).then(function () {
var ffbuild = params.versions.geckoVersion + ' (' + params.versions.geckoBuildID + ')';
//params.exceptions = encodeURIComponent(exceptions);
var comment = '+++ Initially filed via the problem reporting functionality in Shumway +++\n' +
'Please add any further information that you deem helpful here:\n\n\n\n' +
'----------------------\n\n' +
'Technical Information:\n' +
'Firefox version: ' + ffbuild + '\n' +
'Shumway version: ' + params.versions.shumwayVersion;
url = url.split('{comment}').join(encodeURIComponent(comment));
this.window.open(url);
}.bind(this));
},
externalCom: function (data) {
if (!this.allowScriptAccess)
return;
// TODO check more security stuff ?
if (!this.externalInterface) {
var parentWindow = this.window.parent; // host page -- parent of PlayPreview frame
var embedTag = this.embedTag;
this.externalInterface = new ExternalInterface(parentWindow, embedTag, function (call) {
return this.onExternalCallback(call);
}.bind(this));
}
return this.externalInterface.processAction(data);
},
postMessage: function (type, data) {
var embedTag = this.embedTag;
var event = embedTag.ownerDocument.createEvent('CustomEvent');
var detail = Components.utils.cloneInto({ type: type, data: data }, embedTag.ownerDocument.wrappedJSObject);
event.initCustomEvent('message', false, false, detail);
embedTag.dispatchEvent(event);
},
processFrame: function () {
this.postMessage('processFrame');
},
processFSCommand: function (command, data) {
this.postMessage('processFSCommand', { command: command, data: data });
},
print: function (msg) {
this.postMessage('print', msg);
},
setScreenShotCallback: function (callback) {
var embedTag = this.embedTag;
Components.utils.exportFunction(function () {
// `callback` can be wrapped in a CPOW and thus cause a slow synchronous cross-process operation.
var result = callback();
return Components.utils.cloneInto(result, embedTag.ownerDocument);
}, embedTag.wrappedJSObject, {defineAs: 'getCanvasData'});
}
};
function getVersionInfo() {
var deferred = Promise.defer();
var versionInfo = {
geckoVersion: 'unknown',
geckoBuildID: 'unknown',
shumwayVersion: 'unknown'
};
try {
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
versionInfo.geckoVersion = appInfo.version;
versionInfo.geckoBuildID = appInfo.appBuildID;
} catch (e) {
log('Error encountered while getting platform version info: ' + e);
}
var xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
xhr.open('GET', 'resource://shumway/version.txt', true);
xhr.overrideMimeType('text/plain');
xhr.onload = function () {
try {
// Trying to merge version.txt lines into something like:
// "version (sha) details"
var lines = xhr.responseText.split(/\n/g);
lines[1] = '(' + lines[1] + ')';
versionInfo.shumwayVersion = lines.join(' ');
} catch (e) {
log('Error while parsing version info: ' + e);
}
deferred.resolve(versionInfo);
};
xhr.onerror = function () {
log('Error while reading version info: ' + xhr.error);
deferred.resolve(versionInfo);
};
xhr.send();
return deferred.promise;
}

View File

@ -1,127 +0,0 @@
/*
* Copyright 2014 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 = ['SpecialInflate', 'SpecialInflateUtils'];
Components.utils.import('resource://gre/modules/Services.jsm');
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
function SimpleStreamListener() {
this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
.createInstance(Ci.nsIBinaryInputStream);
this.onData = null;
this.buffer = null;
}
SimpleStreamListener.prototype = {
QueryInterface: function (iid) {
if (iid.equals(Ci.nsIStreamListener) ||
iid.equals(Ci.nsIRequestObserver) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
onStartRequest: function (aRequest, aContext) {
return Cr.NS_OK;
},
onStopRequest: function (aRequest, aContext, sStatusCode) {
return Cr.NS_OK;
},
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
this.binaryStream.setInputStream(aInputStream);
if (!this.buffer || aCount > this.buffer.byteLength) {
this.buffer = new ArrayBuffer(aCount);
}
this.binaryStream.readArrayBuffer(aCount, this.buffer);
this.onData(new Uint8Array(this.buffer, 0, aCount));
return Cr.NS_OK;
}
};
function SpecialInflate() {
var listener = new SimpleStreamListener();
listener.onData = function (data) {
this.onData(data);
}.bind(this);
var converterService = Cc["@mozilla.org/streamConverters;1"].getService(Ci.nsIStreamConverterService);
var converter = converterService.asyncConvertData("deflate", "uncompressed", listener, null);
converter.onStartRequest(null, null);
this.converter = converter;
var binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
var pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
pipe.init(true, true, 0, 0xFFFFFFFF, null);
binaryStream.setOutputStream(pipe.outputStream);
this.binaryStream = binaryStream;
this.pipeInputStream = pipe.inputStream;
this.onData = null;
}
SpecialInflate.prototype = {
push: function (data) {
this.binaryStream.writeByteArray(data, data.length);
this.converter.onDataAvailable(null, null, this.pipeInputStream, 0, data.length);
},
close: function () {
this.binaryStream.close();
this.converter.onStopRequest(null, null, Cr.NS_OK);
}
};
var SpecialInflateUtils = {
get isSpecialInflateEnabled() {
try {
return Services.prefs.getBoolPref('shumway.specialInflate');
} catch (ex) {
return false; // TODO true;
}
},
createWrappedSpecialInflate: function (sandbox) {
var wrapped = new SpecialInflate();
var wrapperOnData;
wrapped.onData = function(data) {
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.cloneInto({
setDataCallback: function (callback) {
wrapperOnData = callback;
},
push: function (data) {
// Uint8Array is expected in the data parameter.
// SpecialInflate.push() fails with other argument types.
return wrapped.push(data);
},
close: function () {
return wrapped.close();
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

View File

@ -1,48 +0,0 @@
/*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var EXPORTED_SYMBOLS = ['SpecialStorageUtils'];
Components.utils.import('resource://gre/modules/Services.jsm');
var SpecialStorageUtils = {
createWrappedSpecialStorage: function (sandbox, swfUrl, privateBrowsing) {
// Creating internal localStorage object based on url and privateBrowsing setting.
var uri = Services.io.newURI(swfUrl, null, null);
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Components.interfaces.nsIScriptSecurityManager);
var principal = ssm.createCodebasePrincipal(uri, {});
var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
.getService(Components.interfaces.nsIDOMStorageManager);
var storage = dsm.createStorage(null, principal, privateBrowsing);
// We will return object created in the sandbox/content, with some exposed
// properties/methods, so we can send data between wrapped object and
// and sandbox/content.
var wrapper = Components.utils.cloneInto({
getItem: function (key) {
return storage.getItem(key);
},
setItem: function (key, value) {
storage.setItem(key, value);
},
removeItem: function (key) {
storage.removeItem(key);
}
}, sandbox, {cloneFunctions:true});
return wrapper;
}
};

View File

@ -1,186 +0,0 @@
/*
* 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 = ['getStartupInfo', 'parseQueryString', 'isContentWindowPrivate'];
Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
Components.utils.import('resource://gre/modules/Services.jsm');
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
function flashUnescape(s) {
return decodeURIComponent(s.split('+').join(' '));
}
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 pair = values[i], j = pair.indexOf('=');
if (j < 0) {
continue; // skipping invalid values
}
var key = pair.substring(0, j), value = pair.substring(j + 1);
obj[flashUnescape(key)] = flashUnescape(value);
}
return obj;
}
function isContentWindowPrivate(win) {
if (!('isContentWindowPrivate' in PrivateBrowsingUtils)) {
return PrivateBrowsingUtils.isWindowPrivate(win);
}
return PrivateBrowsingUtils.isContentWindowPrivate(win);
}
function isStandardEmbedWrapper(embedElement) {
try {
if (embedElement.tagName !== 'EMBED') {
return false;
}
var swfUrl = embedElement.src;
var document = embedElement.ownerDocument;
var docUrl = document.location.href;
if (swfUrl !== docUrl) {
return false; // document URL shall match embed src
}
if (document.body.children.length !== 1 ||
document.body.firstChild !== embedElement) {
return false; // not the only child
}
if (document.defaultView.top !== document.defaultView) {
return false; // not a top window
}
// Looks like a standard wrapper
return true;
} catch (e) {
// Declare that is not a standard fullscreen plugin wrapper for any error
return false;
}
}
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;
}
function getStartupInfo(element) {
var initStartTime = Date.now();
var baseUrl;
var pageUrl;
var isOverlay = false;
var objectParams = {};
// Getting absolute URL from the EMBED tag
var url = element.srcURI && element.srcURI.spec;
pageUrl = element.ownerDocument.location.href; // proper page url?
var tagName = element.nodeName;
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');
}
}
baseUrl = pageUrl;
if (objectParams.base) {
try {
// Verifying base URL, passed in object parameters. It shall be okay to
// ignore bad/corrupted base.
var parsedPageUrl = Services.io.newURI(pageUrl, null, null);
baseUrl = Services.io.newURI(objectParams.base, null, parsedPageUrl).spec;
} catch (e) { /* it's okay to ignore any exception */ }
}
var movieParams = {};
if (objectParams.flashvars) {
movieParams = parseQueryString(objectParams.flashvars);
}
var queryStringMatch = url && /\?([^#]+)/.exec(url);
if (queryStringMatch) {
var queryStringParams = parseQueryString(queryStringMatch[1]);
for (var i in queryStringParams) {
if (!(i in movieParams)) {
movieParams[i] = queryStringParams[i];
}
}
}
var allowScriptAccess = !!url &&
isScriptAllowed(objectParams.allowscriptaccess, url, pageUrl);
var isFullscreenSwf = isStandardEmbedWrapper(element);
var document = element.ownerDocument;
var window = document.defaultView;
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.refererUrl = !isFullscreenSwf ? baseUrl : null;
startupInfo.embedTag = element;
startupInfo.initStartTime = initStartTime;
startupInfo.allowScriptAccess = allowScriptAccess;
startupInfo.pageIndex = 0;
return startupInfo;
}

View File

@ -1,86 +0,0 @@
/*
* Copyright 2014 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';
(function contentScriptClosure() {
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cm = Components.manager;
const Cu = Components.utils;
const Cr = Components.results;
// we need to use closure here -- we are running in the global context
Cu.import('resource://gre/modules/Services.jsm');
var isRemote = Services.appinfo.processType ===
Services.appinfo.PROCESS_TYPE_CONTENT;
var isStarted = false;
function startup() {
if (isStarted) {
return;
}
isStarted = true;
Cu.import('resource://shumway/ShumwayBootstrapUtils.jsm');
ShumwayBootstrapUtils.register();
}
function shutdown() {
if (!isStarted) {
return;
}
isStarted = false;
ShumwayBootstrapUtils.unregister();
Cu.unload('resource://shumway/ShumwayBootstrapUtils.jsm');
}
function updateSettings() {
let mm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
var results = mm.sendSyncMessage('Shumway:Chrome:isEnabled');
var isEnabled = results.some(function (item) {
return item;
});
if (isEnabled) {
startup();
} else {
shutdown();
}
}
if (isRemote && typeof ShumwayBootstrapUtils !== 'undefined') {
// Treat content as non-remote when bootstrap.js or ShumwayUtils.jsm
// already registered the Shumway components for current content scope.
isRemote = false;
}
if (isRemote) {
addMessageListener('Shumway:Child:refreshSettings', updateSettings);
updateSettings();
addMessageListener('Shumway:Child:shutdown', function shutdownListener(e) {
removeMessageListener('Shumway:Child:refreshSettings', updateSettings);
removeMessageListener('Shumway:Child:shutdown', shutdownListener);
shutdown();
});
}
})();

View File

@ -1,56 +0,0 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: transparent;
}
iframe {
position:fixed !important;
left:0;top:0;bottom:0;right:0;
overflow: hidden;
line-height: 0;
border: 0px none;
}
body.remoteStopped {
background-color: red;
}
body.remoteDebug {
background-color: green;
}
body.remoteReload {
background-color: yellow;
}
</style>
</head>
<body>
<iframe id="viewer" src="resource://shumway/web/viewer.html" width="100%" height="100%"></iframe>
<script src="chrome://shumway/content/content.js"></script>
</body>
</html>

View File

@ -1,95 +0,0 @@
/*
* 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.
*/
Components.utils.import('resource://gre/modules/Services.jsm');
Components.utils.import('resource://gre/modules/Promise.jsm');
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
var messageManager, viewerReady;
// Checking if we loading content.js in the OOP/mozbrowser or jsplugins.
// TODO remove mozbrowser logic when we switch to jsplugins only support
if (typeof document === 'undefined') { // mozbrowser OOP frame script
messageManager = this;
viewerReady = Promise.resolve(content);
messageManager.sendAsyncMessage('Shumway:constructed', null);
} else { // jsplugins instance
messageManager = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDocShell)
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIContentFrameMessageManager);
var viewer = document.getElementById('viewer');
viewerReady = new Promise(function (resolve) {
viewer.addEventListener('load', function () {
messageManager.sendAsyncMessage('Shumway:constructed', null);
resolve(viewer.contentWindow);
});
});
}
var externalInterfaceWrapper = {
callback: function (call) {
if (!shumwayComAdapterHooks.onExternalCallback) {
return undefined;
}
return shumwayComAdapterHooks.onExternalCallback(
Components.utils.cloneInto(JSON.parse(call), content));
}
};
var shumwayComAdapterHooks = {};
function sendMessage(action, data, sync) {
var detail = {action: action, data: data, sync: sync};
if (!sync) {
messageManager.sendAsyncMessage('Shumway:message', detail);
return;
}
var result = String(messageManager.sendSyncMessage('Shumway:message', detail));
result = result == 'undefined' ? undefined : JSON.parse(result);
return Components.utils.cloneInto(result, content);
}
function enableDebug() {
messageManager.sendAsyncMessage('Shumway:enableDebug', null);
}
messageManager.addMessageListener('Shumway:init', function (message) {
var environment = message.data;
messageManager.sendAsyncMessage('Shumway:running', {}, {
externalInterface: externalInterfaceWrapper
});
viewerReady.then(function (viewerWindow) {
ShumwayCom.createAdapter(viewerWindow.wrappedJSObject, {
sendMessage: sendMessage,
enableDebug: enableDebug,
getEnvironment: function () { return environment; }
}, shumwayComAdapterHooks);
viewerWindow.wrappedJSObject.runViewer();
});
});
messageManager.addMessageListener('Shumway:loadFile', function (message) {
if (!shumwayComAdapterHooks.onLoadFileCallback) {
return;
}
shumwayComAdapterHooks.onLoadFileCallback(Components.utils.cloneInto(message.data, content));
});

View File

@ -1,130 +0,0 @@
/*
* 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.
*/
// Simple class for synchronous XHR communication.
// See also examples/inspector/debug/server.js.
var PingPongConnection = (function () {
function PingPongConnection(url, onlySend) {
this.url = url;
this.onData = null;
this.onError = null;
this.currentXhr = null;
this.closed = false;
if (!onlySend) {
this.idle();
}
}
PingPongConnection.prototype = {
idle: function () {
function requestIncoming(connection) {
var xhr = new XMLHttpRequest();
xhr.open('GET', connection.url + '?idle', true);
xhr.onload = function () {
if (xhr.status === 204 &&
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
requestIncoming(connection);
return;
}
if (xhr.status === 200) {
var result;
if (connection.onData) {
var response = xhr.responseText;
result = connection.onData(response ? JSON.parse(response) : undefined);
}
if (xhr.getResponseHeader('X-PingPong-Async') === '1') {
requestIncoming(connection);
} else {
sendResponse(connection, result);
}
return;
}
if (connection.onError) {
connection.onError(xhr.statusText);
}
};
xhr.onerror = function () {
if (connection.onError) {
connection.onError(xhr.error);
}
};
xhr.send();
connection.currentXhr = xhr;
}
function sendResponse(connection, result) {
var xhr = new XMLHttpRequest();
xhr.open('POST', connection.url + '?response', false);
xhr.onload = function () {
if (xhr.status !== 204) {
if (connection.onError) {
connection.onError(xhr.statusText);
}
}
requestIncoming(connection);
};
xhr.onerror = function () {
if (connection.onError) {
connection.onError(xhr.error);
}
};
xhr.send(result === undefined ? '' : JSON.stringify(result));
connection.currentXhr = xhr;
}
requestIncoming(this);
},
send: function (data, async, timeout) {
if (this.closed) {
throw new Error('connection closed');
}
async = !!async;
timeout |= 0;
var encoded = data === undefined ? '' : JSON.stringify(data);
if (async) {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url + '?async', true);
xhr.send(encoded);
return;
} else {
var xhr = new XMLHttpRequest();
xhr.open('POST', this.url, false);
if (timeout > 0) {
xhr.setRequestHeader('X-PingPong-Timeout', timeout);
}
xhr.send(encoded);
if (xhr.status === 204 &&
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
throw new Error('sync request timeout');
}
var response = xhr.responseText;
return response ? JSON.parse(response) : undefined;
}
},
close: function () {
if (this.currentXhr) {
this.currentXhr.abort();
this.currentXhr = null;
}
this.closed = true;
}
};
return PingPongConnection;
})();

View File

@ -1,115 +0,0 @@
/*
* 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.
*/
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('chrome://shumway/content/StartupInfo.jsm');
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
function log(str) {
var msg = 'plugin.js: ' + str;
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function runViewer() {
function handlerOOP() {
var frameLoader = pluginElement.frameLoader;
var messageManager = frameLoader.messageManager;
var externalInterface;
messageManager.addMessageListener('Shumway:running', function (message) {
externalInterface = message.objects.externalInterface;
});
messageManager.addMessageListener('Shumway:message', function (message) {
var data = message.data;
var result = shumwayActions.invoke(data.action, data.data);
if (message.sync) {
return result === undefined ? 'undefined' : JSON.stringify(result);
}
});
messageManager.addMessageListener('Shumway:enableDebug', function (message) {
enableDebug();
});
shumwayActions.onExternalCallback = function (call) {
return externalInterface.callback(JSON.stringify(call));
};
shumwayActions.onLoadFileCallback = function (args) {
messageManager.sendAsyncMessage('Shumway:loadFile', args);
};
messageManager.addMessageListener('Shumway:constructed', function (message) {
messageManager.sendAsyncMessage('Shumway:init', getEnvironment());
});
}
function getEnvironment() {
return {
swfUrl: startupInfo.url,
privateBrowsing: startupInfo.privateBrowsing
};
}
function enableDebug() {
DebugUtils.enableDebug(startupInfo.url);
setTimeout(function () {
// TODO fix plugin instance reloading for jsplugins
}, 1000);
}
var startupInfo = getStartupInfo(pluginElement);
if (!startupInfo.url) {
// Special case when movie URL is not specified, e.g. swfobject
// checks only version. No need to instantiate the flash plugin.
if (startupInfo.embedTag) {
setupSimpleExternalInterface(startupInfo.embedTag);
}
return;
}
var document = pluginElement.ownerDocument;
var window = document.defaultView;
var shumwayActions = ShumwayCom.createActions(startupInfo, window, document);
handlerOOP();
// TODO fix remote debugging for jsplugins
}
function setupSimpleExternalInterface(embedTag) {
Components.utils.exportFunction(function (variable) {
switch (variable) {
case '$version':
return 'SHUMWAY 10,0,0';
default:
log('Unsupported GetVariable() call: ' + variable);
return undefined;
}
}, embedTag.wrappedJSObject, {defineAs: 'GetVariable'});
}
runViewer();

View File

@ -1,58 +0,0 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: transparent;
}
iframe {
position:fixed !important;
left:0;top:0;bottom:0;right:0;
overflow: hidden;
line-height: 0;
border: 0px none;
}
body.remoteStopped {
background-color: red;
}
body.remoteDebug {
background-color: green;
}
body.remoteReload {
background-color: yellow;
}
</style>
</head>
<body>
<iframe id="viewer" src="resource://shumway/web/viewer.html" width="100%" height="100%" mozbrowser remote="true"></iframe>
<script src="chrome://shumway/content/pingpong.js"></script>
<script src="chrome://shumway/content/viewerDebugger.js"></script>
<script src="chrome://shumway/content/viewerWrapper.js"></script>
</body>
</html>

View File

@ -1,90 +0,0 @@
/*
* 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.
*/
Components.utils.import('resource://gre/modules/Services.jsm');
var DebugUtils = (function () {
var baseUrl = null;
function getBaseUrl() {
if (baseUrl === null) {
try {
baseUrl = Services.prefs.getCharPref('shumway.debug.url');
} catch (e) {
baseUrl = 'http://localhost:8010';
}
}
return baseUrl;
}
var uniqueId = (Date.now() % 888888) * 2 + 1;
function getEnabledDebuggerId(swfUrl) {
return new Promise(function (resolve) {
var url = getBaseUrl() + '/debugController/' + uniqueId;
var connection = new PingPongConnection(url);
connection.onData = function (data) {
if (data.action === 'setDebugger' && data.swfUrl === swfUrl) {
resolve(data.debuggerId);
}
};
try {
connection.send({action: 'getDebugger', swfUrl: swfUrl, swfId: uniqueId}, true);
} catch (e) {
// ignoring failed send request
}
setTimeout(function () {
resolve(0);
connection.close();
}, 500);
});
}
function enableDebug(swfUrl) {
var url = getBaseUrl() + '/debugController/' + uniqueId;
var connection = new PingPongConnection(url, true);
try {
connection.send({action: 'enableDebugging', swfUrl: swfUrl}, true);
} catch (e) {
// ignoring failed send request
}
connection.close();
}
function createDebuggerConnection(swfUrl) {
return getEnabledDebuggerId(swfUrl).then(function (debuggerId) {
if (!debuggerId) {
return null;
}
var url = getBaseUrl() + '/debug/' + uniqueId + '/' + debuggerId;
console.log('Starting remote debugger with ' + url);
return new PingPongConnection(url);
});
}
return {
get isEnabled() {
try {
return Services.prefs.getBoolPref('shumway.debug.enabled');
} catch (e) {
return false;
}
},
enableDebug: enableDebug,
createDebuggerConnection: createDebuggerConnection
};
})();

View File

@ -1,165 +0,0 @@
/*
* Copyright 2014 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 viewer = document.getElementById('viewer'), onLoaded;
var promise = new Promise(function (resolve) {
onLoaded = resolve;
});
viewer.addEventListener('load', function () {
onLoaded(false);
});
viewer.addEventListener('mozbrowserloadend', function () {
onLoaded(true);
});
Components.utils.import('chrome://shumway/content/ShumwayCom.jsm');
function runViewer() {
function handler() {
function sendMessage(action, data, sync) {
var result = shumwayActions.invoke(action, data);
return Components.utils.cloneInto(result, childWindow);
}
var childWindow = viewer.contentWindow.wrappedJSObject;
var shumwayComAdapterHooks = {};
ShumwayCom.createAdapter(childWindow, {
sendMessage: sendMessage,
enableDebug: enableDebug,
getEnvironment: getEnvironment,
}, shumwayComAdapterHooks);
shumwayActions.onExternalCallback = function (call) {
return shumwayComAdapterHooks.onExternalCallback(Components.utils.cloneInto(call, childWindow));
};
shumwayActions.onLoadFileCallback = function (args) {
shumwayComAdapterHooks.onLoadFileCallback(Components.utils.cloneInto(args, childWindow));
};
childWindow.runViewer();
}
function handlerOOP() {
var frameLoader = viewer.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
var messageManager = frameLoader.messageManager;
messageManager.loadFrameScript('chrome://shumway/content/content.js', false);
var externalInterface;
messageManager.addMessageListener('Shumway:running', function (message) {
externalInterface = message.objects.externalInterface;
});
messageManager.addMessageListener('Shumway:message', function (message) {
var data = message.data;
var result = shumwayActions.invoke(data.action, data.data);
if (message.sync) {
return result === undefined ? 'undefined' : JSON.stringify(result);
}
});
messageManager.addMessageListener('Shumway:enableDebug', function (message) {
enableDebug();
});
shumwayActions.onExternalCallback = function (call) {
return externalInterface.callback(JSON.stringify(call));
};
shumwayActions.onLoadFileCallback = function (args) {
messageManager.sendAsyncMessage('Shumway:loadFile', args);
};
messageManager.sendAsyncMessage('Shumway:init', getEnvironment());
}
function handleDebug(connection) {
viewer.parentNode.removeChild(viewer); // we don't need viewer anymore
document.body.className = 'remoteDebug';
function sendMessage(data) {
return shumwayActions.invoke(data.id, data.data);
}
connection.onData = function (data) {
switch (data.action) {
case 'sendMessage':
return sendMessage(data);
case 'reload':
document.body.className = 'remoteReload';
setTimeout(function () {
window.top.location.reload();
}, 1000);
return;
}
};
shumwayActions.onExternalCallback = function (call) {
return connection.send({action: 'onExternalCallback', detail: call});
};
shumwayActions.onLoadFileCallback = function (args) {
if (args.array) {
args.array = Array.prototype.slice.call(args.array, 0);
}
return connection.send({action: 'onLoadFileCallback', detail: args}, true);
};
connection.send({action: 'runViewer'}, true);
}
function getEnvironment() {
return {
swfUrl: window.shumwayStartupInfo.url,
privateBrowsing: window.shumwayStartupInfo.privateBrowsing
};
}
function enableDebug() {
DebugUtils.enableDebug(window.shumwayStartupInfo.url);
setTimeout(function () {
window.top.location.reload();
}, 1000);
}
var startupInfo = window.shumwayStartupInfo;
var shumwayActions = ShumwayCom.createActions(startupInfo, window, document);
promise.then(function (oop) {
if (DebugUtils.isEnabled) {
DebugUtils.createDebuggerConnection(window.shumwayStartupInfo.url).then(
function (debuggerConnection) {
if (debuggerConnection) {
handleDebug(debuggerConnection);
} else if (oop) {
handlerOOP();
} else {
handler();
}
});
return;
}
if (oop) {
handlerOOP();
} else {
handler();
}
});
}

View File

@ -1,158 +0,0 @@
/*
* 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.
*/
'use strict';
var EXPORTED_SYMBOLS = ['ShumwayBootstrapUtils'];
const PREF_PREFIX = 'shumway.';
const PREF_IGNORE_CTP = PREF_PREFIX + 'ignoreCTP';
const PREF_WHITELIST = PREF_PREFIX + 'swf.whitelist';
const SWF_CONTENT_TYPE = 'application/x-shockwave-flash';
const PLUGIN_HANLDER_URI = 'chrome://shumway/content/content.html';
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cm = Components.manager;
var Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
var Ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function getStringPref(pref, def) {
try {
return Services.prefs.getComplexValue(pref, Ci.nsISupportsString).data;
} catch (ex) {
return def;
}
}
function log(str) {
var msg = 'ShumwayBootstrapUtils.jsm: ' + str;
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
// Register/unregister a constructor as a factory.
function Factory() {}
Factory.prototype = {
register: function register(targetConstructor) {
var proto = targetConstructor.prototype;
this._classID = proto.classID;
var factory = XPCOMUtils._getFactory(targetConstructor);
this._factory = factory;
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(proto.classID, proto.classDescription,
proto.contractID, factory);
if (proto.classID2) {
this._classID2 = proto.classID2;
registrar.registerFactory(proto.classID2, proto.classDescription,
proto.contractID2, factory);
}
},
unregister: function unregister() {
var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
registrar.unregisterFactory(this._classID, this._factory);
if (this._classID2) {
registrar.unregisterFactory(this._classID2, this._factory);
}
}
};
function allowedPlatformForMedia() {
var oscpu = Cc["@mozilla.org/network/protocol;1?name=http"]
.getService(Ci.nsIHttpProtocolHandler).oscpu;
if (oscpu.indexOf('Windows NT') === 0) {
return oscpu.indexOf('Windows NT 5') < 0; // excluding Windows XP
}
if (oscpu.indexOf('Intel Mac OS X') === 0) {
return true;
}
// Other platforms are not supported yet.
return false;
}
var ShumwayBootstrapUtils = {
isRegistered: false,
isJSPluginsSupported: false,
register: function () {
if (this.isRegistered) {
return;
}
this.isRegistered = true;
// Register the components.
this.isJSPluginsSupported = !!Ph.registerFakePlugin &&
getBoolPref('shumway.jsplugins', false);
if (this.isJSPluginsSupported) {
let initPluginDict = {
handlerURI: PLUGIN_HANLDER_URI,
mimeEntries: [
{
type: SWF_CONTENT_TYPE,
description: 'Shockwave Flash',
extension: 'swf'
}
],
niceName: 'Shumway plugin',
name: 'Shumway',
supersedeExisting: true, // TODO verify when jsplugins (bug 558184) is implemented
sandboxScript: 'chrome://shumway/content/plugin.js', // TODO verify when jsplugins (bug 558184) is implemented
version: '10.0.0.0'
};
Ph.registerFakePlugin(initPluginDict);
} else {
Cu.import('resource://shumway/ShumwayStreamConverter.jsm');
let converterFactory = new Factory();
converterFactory.register(ShumwayStreamConverter);
this.converterFactory = converterFactory;
}
},
unregister: function () {
if (!this.isRegistered) {
return;
}
this.isRegistered = false;
// Remove the contract/component.
if (this.isJSPluginsSupported) {
Ph.unregisterFakePlugin(PLUGIN_HANLDER_URI);
} else {
this.converterFactory.unregister();
this.converterFactory = null;
}
}
};

View File

@ -1,415 +0,0 @@
/*
* 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.
*/
'use strict';
var EXPORTED_SYMBOLS = ['ShumwayStreamConverter', 'ShumwayStreamOverlayConverter'];
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');
Cu.import('resource://gre/modules/NetUtil.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ShumwayTelemetry',
'resource://shumway/ShumwayTelemetry.jsm');
Components.utils.import('chrome://shumway/content/StartupInfo.jsm');
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function log(aMsg) {
let msg = 'ShumwayStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function getDOMWindow(aChannel) {
var requestor = aChannel.notificationCallbacks ||
aChannel.loadGroup.notificationCallbacks;
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
return win;
}
function isShumwayEnabledFor(startupInfo) {
// disabled for PrivateBrowsing windows
if (isContentWindowPrivate(startupInfo.window) &&
!getBoolPref('shumway.enableForPrivate', false)) {
return false;
}
// disabled if embed tag specifies shumwaymode (for testing purpose)
if (startupInfo.objectParams['shumwaymode'] === 'off') {
return false;
}
var url = startupInfo.url;
var baseUrl = startupInfo.baseUrl;
// blacklisting well known sites with issues
if (/\.ytimg\.com\//i.test(url) /* youtube movies */ ||
/\/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 */) {
return false;
}
return true;
}
function activateShumwayScripts(window) {
function initScripts() {
window.wrappedJSObject.runViewer();
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);
}
if (viewerWindow) {
viewerWindow.addEventListener('mousedown', activate, true);
}
window.addEventListener('shumwayFallback', function (e) {
var automatic = !!e.detail.automatic;
fallbackToNativePlugin(window, !automatic, automatic);
});
}
if (window.document.readyState === "interactive" ||
window.document.readyState === "complete") {
initScripts();
} else {
window.document.addEventListener('DOMContentLoaded', initScripts);
}
}
function fallbackToNativePlugin(window, userAction, activateCTP) {
var obj = window.frameElement;
var doc = obj.ownerDocument;
var e = doc.createEvent("CustomEvent");
e.initCustomEvent("MozPlayPlugin", true, true, activateCTP);
obj.dispatchEvent(e);
ShumwayTelemetry.onFallback(userAction);
}
function ShumwayStreamConverterBase() {
}
ShumwayStreamConverterBase.prototype = {
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;
},
getStartupInfo: function(window, url) {
var initStartTime = Date.now();
var element = window.frameElement;
var isOverlay = false;
if (element) {
// 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;
while (tagName != 'EMBED' && tagName != 'OBJECT') {
// plugin overlay skipping until the target plugin is found
isOverlay = true;
containerElement = element;
element = element.parentNode;
if (!element) {
throw new Error('Plugin element is not found');
}
tagName = element.nodeName;
}
if (isOverlay) {
// 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%');
}
// 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) {
return getStartupInfo(element);
}
// Stream converter is used in top level window, just providing basic
// information about SWF.
var objectParams = {};
var movieParams = {};
var queryStringMatch = url && /\?([^#]+)/.exec(url);
if (queryStringMatch) {
var queryStringParams = parseQueryString(queryStringMatch[1]);
for (var i in queryStringParams) {
if (!(i in movieParams)) {
movieParams[i] = queryStringParams[i];
}
}
}
// Using the same data structure as we return in StartupInfo.jsm and
// assigning constant values for fields that is not applicable for
// the stream converter when it is used in a top level window.
var startupInfo = {};
startupInfo.window = window;
startupInfo.url = url;
startupInfo.privateBrowsing = isContentWindowPrivate(window);
startupInfo.objectParams = objectParams;
startupInfo.movieParams = movieParams;
startupInfo.baseUrl = url;
startupInfo.isOverlay = false;
startupInfo.refererUrl = null;
startupInfo.embedTag = null;
startupInfo.isPausedAtStart = /\bpaused=true$/.test(url);
startupInfo.initStartTime = initStartTime;
startupInfo.allowScriptAccess = false;
return startupInfo;
},
// 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);
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();
var originalURI = aRequest.URI;
// Create a new channel that loads the viewer as a chrome resource.
var viewerUrl = 'chrome://shumway/content/viewer.wrapper.html';
var channel = NetUtil.newChannel({
uri: viewerUrl,
loadUsingSystemPrincipal: true
});
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 = {
onStartRequest: function(request, context) {
listener.onStartRequest(aRequest, context);
},
onDataAvailable: function(request, context, inputStream, offset, count) {
listener.onDataAvailable(aRequest, context, inputStream, offset, count);
},
onStopRequest: function(request, context, statusCode) {
// Cancel the request so the viewer can handle it.
aRequest.resume();
aRequest.cancel(Cr.NS_BINDING_ABORTED);
var domWindow = getDOMWindow(channel);
let startupInfo = converter.getStartupInfo(domWindow, converter.getUrlHint(originalURI));
listener.onStopRequest(aRequest, context, statusCode);
if (!startupInfo.url) {
// Special case when movie URL is not specified, e.g. swfobject
// checks only version. No need to instantiate the flash plugin.
if (startupInfo.embedTag) {
setupSimpleExternalInterface(startupInfo.embedTag);
}
return;
}
if (!isShumwayEnabledFor(startupInfo)) {
fallbackToNativePlugin(domWindow, false, true);
return;
}
domWindow.shumwayStartupInfo = startupInfo;
// TODO Report telemetry on amount of swfs on the page
// ShumwayTelemetry.onPageIndex(pageIndex);
activateShumwayScripts(domWindow);
}
};
// Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI;
channel.loadGroup = aRequest.loadGroup;
// We can use all powerful principal: we are opening chrome:// web page,
// which will need lots of permission.
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var resourcePrincipal = securityManager.getSystemPrincipal();
aRequest.owner = resourcePrincipal;
channel.asyncOpen2(proxy);
},
// nsIRequestObserver::onStopRequest
onStopRequest: function(aRequest, aContext, aStatusCode) {
// Do nothing.
}
};
function setupSimpleExternalInterface(embedTag) {
Components.utils.exportFunction(function (variable) {
switch (variable) {
case '$version':
return 'SHUMWAY 10,0,0';
default:
log('Unsupported GetVariable() call: ' + variable);
return undefined;
}
}, embedTag.wrappedJSObject, {defineAs: 'GetVariable'});
}
// properties required for XPCOM registration:
function copyProperties(obj, template) {
for (var prop in template) {
obj[prop] = template[prop];
}
}
function ShumwayStreamConverter() {}
ShumwayStreamConverter.prototype = new ShumwayStreamConverterBase();
copyProperties(ShumwayStreamConverter.prototype, {
classID: Components.ID('{4c6030f7-e20a-264f-5b0e-ada3a9e97384}'),
classDescription: 'Shumway Content Converter Component',
contractID: '@mozilla.org/streamconv;1?from=application/x-shockwave-flash&to=*/*',
classID2: Components.ID('{4c6030f8-e20a-264f-5b0e-ada3a9e97384}'),
contractID2: '@mozilla.org/streamconv;1?from=application/x-shockwave-flash&to=text/html'
});
function ShumwayStreamOverlayConverter() {}
ShumwayStreamOverlayConverter.prototype = new ShumwayStreamConverterBase();
copyProperties(ShumwayStreamOverlayConverter.prototype, {
classID: Components.ID('{4c6030f7-e20a-264f-5f9b-ada3a9e97384}'),
classDescription: 'Shumway PlayPreview Component',
contractID: '@mozilla.org/streamconv;1?from=application/x-moz-playpreview&to=*/*'
});
ShumwayStreamOverlayConverter.prototype.getUrlHint = function (requestUrl) {
return '';
};

View File

@ -1,75 +0,0 @@
/* 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.
*/
/* jshint esnext:true */
'use strict';
this.EXPORTED_SYMBOLS = ['ShumwayTelemetry'];
const Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
const BANNER_SIZES = [
"88x31", "120x60", "120x90", "120x240", "120x600", "125x125", "160x600",
"180x150", "234x60", "240x400", "250x250", "300x100", "300x250", "300x600",
"300x1050", "336x280", "468x60", "550x480", "720x100", "728x90", "970x90",
"970x250"];
function getBannerType(width, height) {
return BANNER_SIZES.indexOf(width + 'x' + height) + 1;
}
this.ShumwayTelemetry = {
onFirstFrame: function (timeToDisplay) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_TIME_TO_VIEW_MS");
histogram.add(timeToDisplay);
},
onParseInfo: function (parseInfo) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_PARSING_MS");
histogram.add(parseInfo.parseTime);
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_SIZE_KB");
histogram.add(parseInfo.size / 1024);
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_VERSION");
histogram.add(parseInfo.swfVersion);
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_FRAME_RATE");
histogram.add(parseInfo.frameRate);
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_AREA");
histogram.add(parseInfo.width * parseInfo.height);
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_BANNER");
histogram.add(getBannerType(parseInfo.width, parseInfo.height));
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_AVM2");
histogram.add(parseInfo.isAvm2);
},
onError: function (errorType) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_ERROR");
histogram.add(errorType);
},
onPageIndex: function (pageIndex) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_SWF_INDEX_ON_PAGE");
histogram.add(pageIndex);
},
onFeature: function (featureType) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_FEATURE_USED");
histogram.add(featureType);
},
onLoadResource: function (resultType) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_LOAD_RESOURCE_RESULT");
histogram.add(resultType);
},
onFallback: function (userAction) {
var histogram = Services.telemetry.getHistogramById("SHUMWAY_FALLBACK");
histogram.add(userAction);
}
};

View File

@ -1,130 +0,0 @@
/* Copyright 2012 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 = ["ShumwayUtils"];
const PREF_PREFIX = 'shumway.';
const PREF_DISABLED = PREF_PREFIX + 'disabled';
const PREF_WHITELIST = PREF_PREFIX + 'swf.whitelist';
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cm = Components.manager;
var Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function log(str) {
dump(str + '\n');
}
var ShumwayUtils = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
_registered: false,
init: function init() {
this.migratePreferences();
if (this.enabled)
this._ensureRegistered();
else
this._ensureUnregistered();
Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster)
.addMessageListener('Shumway:Chrome:isEnabled', this);
// Listen for when shumway is completely disabled.
Services.prefs.addObserver(PREF_DISABLED, this, false);
},
migratePreferences: function migratePreferences() {
// At one point we had shumway.disabled set to true by default,
// and we are trying to replace it with shumway.swf.whitelist:
// checking if the user already changed it before to reset
// the whitelist to '*'.
if (Services.prefs.prefHasUserValue(PREF_DISABLED) &&
!Services.prefs.prefHasUserValue(PREF_WHITELIST) &&
!getBoolPref(PREF_DISABLED, false)) {
// The user is already using Shumway -- enabling all web sites.
Services.prefs.setCharPref(PREF_WHITELIST, '*');
}
},
// nsIObserver
observe: function observe(aSubject, aTopic, aData) {
if (this.enabled)
this._ensureRegistered();
else
this._ensureUnregistered();
},
receiveMessage: function(message) {
switch (message.name) {
case 'Shumway:Chrome:isEnabled':
return this.enabled;
}
},
/**
* shumway is only enabled if the global switch enabling is true.
* @return {boolean} Wether or not it's enabled.
*/
get enabled() {
return !getBoolPref(PREF_DISABLED, true);
},
_ensureRegistered: function _ensureRegistered() {
if (this._registered)
return;
// Load the component and register it.
Cu.import('resource://shumway/ShumwayBootstrapUtils.jsm');
ShumwayBootstrapUtils.register();
this._registered = true;
log('Shumway is registered');
let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIFrameScriptLoader);
globalMM.broadcastAsyncMessage('Shumway:Child:refreshSettings');
},
_ensureUnregistered: function _ensureUnregistered() {
if (!this._registered)
return;
// Remove the contract/component.
ShumwayBootstrapUtils.unregister();
Cu.unload('resource://shumway/ShumwayBootstrapUtils.jsm');
this._registered = false;
log('Shumway is unregistered');
let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIFrameScriptLoader);
globalMM.broadcastAsyncMessage('Shumway:Child:refreshSettings');
}
};

View File

@ -1,46 +0,0 @@
precision mediump float;
varying vec4 vColor;
uniform mat4 uColorMatrix;
uniform vec4 uColorVector;
uniform sampler2D uSampler[8];
varying vec2 vCoordinate;
varying float vKind;
varying float vSampler;
void main() {
vec4 color;
int kind = int(floor(vKind + 0.5));
if (kind == 0) {
color = vColor;
} else if (kind == 1 || kind == 2) {
int sampler = int(floor(vSampler + 0.5));
if (sampler == 0) {
color = vColor * texture2D(uSampler[0], vCoordinate);
} else if (sampler == 1) {
color = vColor * texture2D(uSampler[1], vCoordinate);
} else if (sampler == 2) {
color = vColor * texture2D(uSampler[2], vCoordinate);
} else if (sampler == 3) {
color = vColor * texture2D(uSampler[3], vCoordinate);
} else if (sampler == 4) {
color = vColor * texture2D(uSampler[4], vCoordinate);
} else if (sampler == 5) {
color = vColor * texture2D(uSampler[5], vCoordinate);
} else if (sampler == 6) {
color = vColor * texture2D(uSampler[6], vCoordinate);
} else if (sampler == 7) {
color = vColor * texture2D(uSampler[7], vCoordinate);
}
if (kind == 2) {
color = color * uColorMatrix + uColorVector;
}
} else {
color = vec4(1.0, 0.0, 0.0, 1.0);
}
// color.rgb *= color.a;
if (color.a < 0.01) {
discard;
}
gl_FragColor = color;
}

View File

@ -1,22 +0,0 @@
uniform vec2 uResolution;
uniform mat3 uTransformMatrix;
uniform mat4 uTransformMatrix3D;
attribute vec4 aPosition;
attribute vec4 aColor;
attribute vec2 aCoordinate;
attribute float aKind;
attribute float aSampler;
varying vec4 vColor;
varying vec2 vCoordinate;
varying float vKind;
varying float vSampler;
void main() {
gl_Position = uTransformMatrix3D * aPosition;
vColor = aColor;
vCoordinate = aCoordinate;
vKind = aKind;
vSampler = aSampler;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
0.11.422
137ba70

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: transparent;
line-height: 0;
}
#easelContainer {
position:fixed !important;
left:0;top:0;bottom:0;right:0;
overflow: hidden;
line-height: 0;
}
</style>
<script src='resource://shumway/shumway.gfx.js'></script>
</head>
<body contextmenu="shumwayMenu">
<div id="easelContainer"></div>
<menu type="context" id="shumwayMenu">
<menuitem label="Show URL" id="showURLMenu"></menuitem>
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Report Problems" id="reportMenu"></menuitem>
<menuitem label="Reload in Adobe Flash Player" id="fallbackMenu" hidden></menuitem>
<menuitem label="Debug this SWF" id="debugMenu"></menuitem>
<menuitem label="About Shumway %version%..." id="aboutMenu"></menuitem>
</menu>
<script src="viewerGfx.js"></script>
</body>
</html>

View File

@ -1,110 +0,0 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<base href=""/>
<style>
body {
margin: 0;
overflow: hidden;
background-color: transparent;
}
body.started #playerIframe {
display: none;
}
#gfxIframe {
position:fixed !important;
left:0; top:0;
width: 100%; height: 100%;
overflow: hidden;
border: 0 none;
}
#overlay {
display: none;
}
#overlay.enabled {
display: block;
position:fixed;
top: 0;
right: 0;
}
#report, #fallback {
float: right;
width: 70px; height: 16px;
padding: 8px 4px 4px;
color: white;
background-color: rgba(218, 56, 7, 0.63);
font: bold 10px sans-serif;
text-align: center;
text-decoration: none;
}
#report {
display: none;
width: 100px;
}
#overlay:hover #report {
display: block;
}
#fallback .icon {
display: none;
color: white;
}
#fallback:hover .icon {
display: inline-block;
}
#report:hover, #fallback:hover {
background-color: black;
}
#playerIframe {
position: absolute;
top: 0;
right: 0;
}
@media screen and (max-width: 100px), screen and (max-height: 40px) {
body.started #overlay {
display: none;
}
}
</style>
</head>
<body>
<iframe id="playerIframe" width="9" height="9" src="" sandbox="allow-scripts"></iframe>
<iframe id="gfxIframe" src="" sandbox="allow-scripts"></iframe>
<section>
<div id="overlay">
<a id="fallback" href="#">Shumway <span class="icon">&times;</span></a>
<a id="report" href="#">Report Problems</a>
</div>
</section>
<script src='resource://shumway/web/viewer.js'></script>
</body>
</html>

View File

@ -1,234 +0,0 @@
/*
* 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 movieUrl, movieParams;
function runViewer() {
var flashParams = ShumwayCom.getPluginParams();
movieUrl = flashParams.url;
if (!movieUrl) {
console.log("no movie url provided -- stopping here");
return;
}
movieParams = flashParams.movieParams;
var objectParams = flashParams.objectParams;
var baseUrl = flashParams.baseUrl;
var isOverlay = flashParams.isOverlay;
var isDebuggerEnabled = flashParams.isDebuggerEnabled;
var initStartTime = flashParams.initStartTime;
if (movieParams.fmt_list && movieParams.url_encoded_fmt_stream_map) {
// HACK removing FLVs from the fmt_list
movieParams.fmt_list = movieParams.fmt_list.split(',').filter(function (s) {
var fid = s.split('/')[0];
return fid !== '5' && fid !== '34' && fid !== '35'; // more?
}).join(',');
}
var backgroundColor;
if (objectParams) {
var m;
if (objectParams.bgcolor && (m = /#([0-9A-F]{6})/i.exec(objectParams.bgcolor))) {
var hexColor = parseInt(m[1], 16);
backgroundColor = hexColor << 8 | 0xff;
}
if (objectParams.wmode === 'transparent') {
backgroundColor = 0;
}
}
playerReady.then(function () {
var settings = ShumwayCom.getSettings();
var playerSettings = settings.playerSettings;
ShumwayCom.setupPlayerComBridge(document.getElementById('playerIframe'));
parseSwf(movieUrl, baseUrl, movieParams, objectParams, settings, initStartTime, backgroundColor);
if (isOverlay) {
if (isDebuggerEnabled) {
document.getElementById('overlay').className = 'enabled';
var fallbackDiv = document.getElementById('fallback');
fallbackDiv.addEventListener('click', function (e) {
fallback();
e.preventDefault();
});
var reportDiv = document.getElementById('report');
reportDiv.addEventListener('click', function (e) {
reportIssue();
e.preventDefault();
});
}
}
ShumwayCom.setupGfxComBridge(document.getElementById('gfxIframe'));
gfxWindow.postMessage({
type: 'prepareUI',
params: {
isOverlay: isOverlay,
isDebuggerEnabled: isDebuggerEnabled,
isHudOn: playerSettings.hud,
backgroundColor: backgroundColor
}
}, '*')
});
}
window.addEventListener("message", function handlerMessage(e) {
var args = e.data;
if (typeof args !== 'object' || args === null) {
return;
}
if (gfxWindow && e.source === gfxWindow) {
switch (args.callback) {
case 'displayParameters':
// The display parameters data will be send to the player window.
// TODO do we need sanitize it?
displayParametersResolved(args.params);
break;
case 'showURL':
showURL();
break;
case 'showInInspector':
showInInspector();
break;
case 'reportIssue':
reportIssue();
break;
case 'showAbout':
showAbout();
break;
case 'enableDebug':
enableDebug();
break;
case 'fallback':
fallback();
break;
default:
console.error('Unexpected message from gfx frame: ' + args.callback);
break;
}
}
if (playerWindow && e.source === playerWindow) {
switch (args.callback) {
case 'started':
document.body.classList.add('started');
break;
default:
console.error('Unexpected message from player frame: ' + args.callback);
break;
}
}
}, true);
function fallback() {
ShumwayCom.fallback();
}
function showURL() {
window.prompt("Copy to clipboard", movieUrl);
}
function showInInspector() {
var base = "http://www.areweflashyet.com/shumway/examples/inspector/inspector.html?rfile=";
var params = '';
for (var k in movieParams) {
params += '&' + k + '=' + encodeURIComponent(movieParams[k]);
}
window.open(base + encodeURIComponent(movieUrl) + params);
}
function reportIssue() {
//var duplicatesMap = Object.create(null);
//var prunedExceptions = [];
//avm2.exceptions.forEach(function(e) {
// var ident = e.source + e.message + e.stack;
// var entry = duplicatesMap[ident];
// if (!entry) {
// entry = duplicatesMap[ident] = {
// source: e.source,
// message: e.message,
// stack: e.stack,
// count: 0
// };
// prunedExceptions.push(entry);
// }
// entry.count++;
//});
//ShumwayCom.reportIssue(JSON.stringify(prunedExceptions));
ShumwayCom.reportIssue();
}
function showAbout() {
window.open('http://areweflashyet.com/');
}
function enableDebug() {
ShumwayCom.enableDebug();
}
var playerWindow, gfxWindow;
function parseSwf(url, baseUrl, movieParams, objectParams, settings,
initStartTime, backgroundColor) {
var compilerSettings = settings.compilerSettings;
var playerSettings = settings.playerSettings;
displayParametersReady.then(function (displayParameters) {
var data = {
type: 'runSwf',
flashParams: {
compilerSettings: compilerSettings,
movieParams: movieParams,
objectParams: objectParams,
displayParameters: displayParameters,
turboMode: playerSettings.turboMode,
env: playerSettings.env,
bgcolor: backgroundColor,
url: url,
baseUrl: baseUrl || url,
initStartTime: initStartTime
}
};
playerWindow.postMessage(data, '*');
});
}
// We need to wait for gfx window to report display parameters before we
// start SWF playback in the player window.
var displayParametersResolved;
var displayParametersReady = new Promise(function (resolve) {
displayParametersResolved = resolve;
});
var playerReady = new Promise(function (resolve) {
function iframeLoaded() {
if (--iframesToLoad > 0) {
return;
}
gfxWindow = document.getElementById('gfxIframe').contentWindow;
playerWindow = document.getElementById('playerIframe').contentWindow;
resolve();
}
var iframesToLoad = 2;
document.getElementById('gfxIframe').addEventListener('load', iframeLoaded);
document.getElementById('gfxIframe').src = 'resource://shumway/web/viewer.gfx.html';
document.getElementById('playerIframe').addEventListener('load', iframeLoaded);
document.getElementById('playerIframe').src = 'resource://shumway/web/viewer.player.html';
});

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<base href=""/>
// Loading the relooper from the asm.js cache takes 3x as long as compiling it, so no async.
<script src="../libs/relooper.js"></script>
<script src="../shumway.player.js"></script>
<script src="viewerPlayer.js"></script>
</head>
<body>
Shumway Player
</body>
</html>

View File

@ -1,122 +0,0 @@
/*
* 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.
*/
var SHUMWAY_ROOT = "resource://shumway/";
var easel;
function createEasel(backgroundColor) {
var Stage = Shumway.GFX.Stage;
var Easel = Shumway.GFX.Easel;
var Canvas2DRenderer = Shumway.GFX.Canvas2DRenderer;
Shumway.GFX.WebGL.SHADER_ROOT = SHUMWAY_ROOT + "gfx/gl/shaders/";
easel = new Easel(document.getElementById("easelContainer"), false, backgroundColor);
if (ShumwayCom.environment === 'test') {
ShumwayCom.setScreenShotCallback(function () {
// flush rendering buffers
easel.render();
return easel.screenShot(null, true, false).dataURL;
});
}
easel.startRendering();
return easel;
}
var easelHost;
function createEaselHost() {
var peer = new Shumway.Remoting.ShumwayComTransportPeer();
easelHost = new Shumway.GFX.Window.WindowEaselHost(easel, peer);
return easelHost;
}
function setHudVisible(visible) {
Shumway.GFX.hud.value = !!visible;
}
function fallback() {
parent.postMessage({callback: 'fallback'}, '*');
}
function showURL() {
parent.postMessage({callback: 'showURL'}, '*' );
}
function showInInspector() {
parent.postMessage({callback: 'showInInspector'}, '*');
}
function reportIssue() {
parent.postMessage({callback: 'reportIssue'}, '*');
}
function showAbout() {
parent.postMessage({callback: 'showAbout'}, '*');
}
function enableDebug() {
parent.postMessage({callback: 'enableDebug'}, '*');
}
function prepareUI(params) {
if (params.isOverlay) {
var fallbackMenu = document.getElementById('fallbackMenu');
fallbackMenu.removeAttribute('hidden');
fallbackMenu.addEventListener('click', fallback);
}
document.getElementById('showURLMenu').addEventListener('click', showURL);
document.getElementById('inspectorMenu').addEventListener('click', showInInspector);
document.getElementById('reportMenu').addEventListener('click', reportIssue);
document.getElementById('aboutMenu').addEventListener('click', showAbout);
var version = Shumway.version || '';
document.getElementById('aboutMenu').label =
document.getElementById('aboutMenu').label.replace('%version%', version);
if (params.isDebuggerEnabled) {
document.getElementById('debugMenu').addEventListener('click', enableDebug);
} else {
document.getElementById('debugMenu').remove();
}
setHudVisible(params.isHudOn);
createEasel(params.backgroundColor);
createEaselHost();
var displayParameters = easel.getDisplayParameters();
window.parent.postMessage({
callback: 'displayParameters',
params: displayParameters
}, '*');
}
window.addEventListener('message', function onWindowMessage(e) {
var data = e.data;
if (typeof data !== 'object' || data === null) {
console.error('Unexpected message for gfx frame.');
return;
}
switch (data.type) {
case "prepareUI":
prepareUI(data.params);
break;
default:
console.error('Unexpected message for gfx frame: ' + args.callback);
break;
}
}, true);

View File

@ -1,113 +0,0 @@
/*
* 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.
*/
window.print = function(msg) {
console.log(msg);
};
function runSwfPlayer(flashParams, settings) {
console.info('Time from init start to SWF player start: ' + (Date.now() - flashParams.initStartTime));
if (settings) {
Shumway.Settings.setSettings(settings);
}
setupServices();
var asyncLoading = true;
var baseUrl = flashParams.baseUrl;
var objectParams = flashParams.objectParams;
var movieUrl = flashParams.url;
if (ShumwayCom.environment === 'test') {
Shumway.frameRateOption.value = 60;
Shumway.dontSkipFramesOption.value = true;
window.print = function(msg) {
ShumwayCom.print(msg.toString());
};
Shumway.Random.reset();
Shumway.installTimeWarper();
} else {
Shumway.frameRateOption.value = flashParams.turboMode ? 60 : -1;
}
Shumway.createSecurityDomain(Shumway.AVM2LoadLibrariesFlags.Builtin | Shumway.AVM2LoadLibrariesFlags.Playerglobal).then(function (securityDomain) {
function runSWF(file, buffer, baseUrl) {
var peer = new Shumway.Remoting.ShumwayComTransportPeer();
var gfxService = new Shumway.Player.Window.WindowGFXService(securityDomain, peer);
var player = new Shumway.Player.Player(securityDomain, gfxService, flashParams.env);
player.defaultStageColor = flashParams.bgcolor;
player.movieParams = flashParams.movieParams;
player.stageAlign = (objectParams && (objectParams.salign || objectParams.align)) || '';
player.stageScale = (objectParams && objectParams.scale) || 'showall';
player.displayParameters = flashParams.displayParameters;
player.initStartTime = flashParams.initStartTime;
player.pageUrl = baseUrl;
console.info('Time from init start to SWF loading start: ' + (Date.now() - flashParams.initStartTime));
player.load(file, buffer);
playerStarted();
}
Shumway.FileLoadingService.instance.init(baseUrl);
if (asyncLoading) {
runSWF(movieUrl, undefined, baseUrl);
} else {
new Shumway.BinaryFileReader(movieUrl).readAll(null, function(buffer, error) {
if (!buffer) {
throw "Unable to open the file " + movieUrl + ": " + error;
}
runSWF(movieUrl, buffer, baseUrl);
});
}
});
}
function setupServices() {
Shumway.Telemetry.instance = new Shumway.Player.ShumwayComTelemetryService();
Shumway.ExternalInterfaceService.instance = new Shumway.Player.ShumwayComExternalInterface();
Shumway.ClipboardService.instance = new Shumway.Player.ShumwayComClipboardService();
Shumway.FileLoadingService.instance = new Shumway.Player.ShumwayComFileLoadingService();
Shumway.SystemResourcesLoadingService.instance = new Shumway.Player.ShumwayComResourcesLoadingService(true);
Shumway.LocalConnectionService.instance = new Shumway.Player.ShumwayComLocalConnectionService();
}
function playerStarted() {
document.body.style.backgroundColor = 'green';
window.parent.postMessage({
callback: 'started'
}, '*');
}
window.addEventListener('message', function onWindowMessage(e) {
var data = e.data;
if (typeof data !== 'object' || data === null) {
console.error('Unexpected message for player frame.');
return;
}
switch (data.type) {
case "runSwf":
if (data.settings) {
Shumway.Settings.setSettings(data.settings);
}
setupServices();
runSwfPlayer(data.flashParams, data.settings);
break;
default:
console.error('Unexpected message for player frame: ' + args.callback);
break;
}
}, true);

View File

@ -1,19 +0,0 @@
/*
* 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.
*/
importScripts(['../shumway.parser.js']);
var loader = new Shumway.SWF.ResourceLoader(this, true);

View File

@ -1,5 +0,0 @@
shumway.jar:
% content shumway %chrome/
% resource shumway %content/
chrome/ (chrome/*)
content/ (content/*)

View File

@ -1,7 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
JAR_MANIFESTS += ['jar.mn']

View File

@ -654,10 +654,6 @@
@RESPATH@/browser/chrome/browser.manifest
@RESPATH@/browser/chrome/pdfjs.manifest
@RESPATH@/browser/chrome/pdfjs/*
#ifdef NIGHTLY_BUILD
@RESPATH@/browser/chrome/shumway.manifest
@RESPATH@/browser/chrome/shumway/*
#endif
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.manifest
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf

View File

@ -777,6 +777,9 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY identity.disableMixedContentBlocking.accesskey "D">
<!ENTITY identity.learnMore "Learn More">
<!ENTITY identity.removeCertException.label "Remove Exception">
<!ENTITY identity.removeCertException.accesskey "R">
<!ENTITY identity.moreInfoLinkText2 "More Information">
<!ENTITY identity.permissions "Permissions">

View File

@ -51,6 +51,7 @@ button {
.target {
margin-top: 5px;
min-height: 34px;
display: flex;
flex-direction: row;
align-items: center;

View File

@ -38,6 +38,13 @@ module.exports = createClass({
dom.div({ className: "target-details" },
dom.div({ className: "target-name" }, target.name)
),
(isRunning && isServiceWorker ?
dom.button({
className: "push-button",
onClick: this.push
}, Strings.GetStringFromName("push")) :
null
),
(isRunning ?
dom.button({
className: "debug-button",
@ -72,6 +79,16 @@ module.exports = createClass({
}
},
push() {
let { client, target } = this.props;
if (target.workerActor) {
client.request({
to: target.workerActor,
type: "push"
});
}
},
openWorkerToolbox(workerActor) {
let { client } = this.props;
client.attachWorker(workerActor, (response, workerClient) => {

View File

@ -7,9 +7,12 @@ support-files =
addons/unpacked/install.rdf
service-workers/empty-sw.html
service-workers/empty-sw.js
service-workers/push-sw.html
service-workers/push-sw.js
[browser_addons_debugging_initial_state.js]
[browser_addons_install.js]
[browser_addons_toggle_debug.js]
[browser_service_workers.js]
[browser_service_workers_push.js]
[browser_service_workers_timeout.js]

View File

@ -0,0 +1,98 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable mozilla/no-cpows-in-tests */
/* global sendAsyncMessage */
"use strict";
// Test that clicking on the Push button next to a Service Worker works as
// intended in about:debugging.
// It should trigger a "push" notification in the worker.
// Service workers can't be loaded from chrome://, but http:// is ok with
// dom.serviceWorkers.testing.enabled turned on.
const HTTP_ROOT = CHROME_ROOT.replace(
"chrome://mochitests/content/", "http://mochi.test:8888/");
const SERVICE_WORKER = HTTP_ROOT + "service-workers/push-sw.js";
const TAB_URL = HTTP_ROOT + "service-workers/push-sw.html";
add_task(function* () {
info("Turn on workers via mochitest http.");
yield new Promise(done => {
let options = { "set": [
// Accept workers from mochitest's http.
["dom.serviceWorkers.testing.enabled", true],
]};
SpecialPowers.pushPrefEnv(options, done);
});
let { tab, document } = yield openAboutDebugging("workers");
// Listen for mutations in the service-workers list.
let serviceWorkersElement = document.getElementById("service-workers");
let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
// Open a tab that registers a push service worker.
let swTab = yield addTab(TAB_URL);
info("Make the test page notify us when the service worker sends a message.");
let frameScript = function() {
let win = content.wrappedJSObject;
win.navigator.serviceWorker.addEventListener("message", function(event) {
sendAsyncMessage(event.data);
}, false);
};
let mm = swTab.linkedBrowser.messageManager;
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
// Expect the service worker to claim the test window when activating.
let onClaimed = new Promise(done => {
mm.addMessageListener("sw-claimed", function listener() {
mm.removeMessageListener("sw-claimed", listener);
done();
});
});
// Wait for the service-workers list to update.
yield onMutation;
// Check that the service worker appears in the UI.
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
info("Ensure that the registration resolved before trying to interact with " +
"the service worker.");
yield waitForServiceWorkerRegistered(swTab);
ok(true, "Service worker registration resolved");
// Retrieve the Push button for the worker.
let names = [...document.querySelectorAll("#service-workers .target-name")];
let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
ok(name, "Found the service worker in the list");
let targetElement = name.parentNode.parentNode;
let pushBtn = targetElement.querySelector(".push-button");
ok(pushBtn, "Found its push button");
info("Wait for the service worker to claim the test window before " +
"proceeding.");
yield onClaimed;
info("Click on the Push button and wait for the service worker to receive " +
"a push notification");
let onPushNotification = new Promise(done => {
mm.addMessageListener("sw-pushed", function listener() {
mm.removeMessageListener("sw-pushed", listener);
done();
});
});
pushBtn.click();
yield onPushNotification;
ok(true, "Service worker received a push notification");
// Finally, unregister the service worker itself.
yield unregisterServiceWorker(swTab);
ok(true, "Service worker registration unregistered");
yield removeTab(swTab);
yield closeAboutDebugging(tab);
});

View File

@ -1,9 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable mozilla/no-cpows-in-tests */
/* global sendAsyncMessage */
"use strict";
// Service workers can't be loaded from chrome://,
@ -15,13 +12,6 @@ const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
const SW_TIMEOUT = 1000;
function assertHasWorker(expected, document, type, name) {
let names = [...document.querySelectorAll("#" + type + " .target-name")];
names = names.map(element => element.textContent);
is(names.includes(name), expected,
"The " + type + " url appears in the list: " + names);
}
add_task(function* () {
yield new Promise(done => {
let options = {"set": [
@ -42,25 +32,10 @@ add_task(function* () {
let serviceWorkersElement = document.getElementById("service-workers");
yield waitForMutation(serviceWorkersElement, { childList: true });
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
// Ensure that the registration resolved before trying to connect to the sw
let frameScript = function() {
// Retrieve the `sw` promise created in the html page
let { sw } = content.wrappedJSObject;
sw.then(function() {
sendAsyncMessage("sw-registered");
});
};
let mm = swTab.linkedBrowser.messageManager;
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
yield new Promise(done => {
mm.addMessageListener("sw-registered", function listener() {
mm.removeMessageListener("sw-registered", listener);
done();
});
});
yield waitForServiceWorkerRegistered(swTab);
ok(true, "Service worker registration resolved");
// Retrieve the DEBUG button for the worker
@ -88,7 +63,7 @@ add_task(function* () {
setTimeout(done, SW_TIMEOUT * 2);
});
assertHasWorker(true, document, "service-workers", SERVICE_WORKER);
assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
ok(targetElement.querySelector(".debug-button"),
"The debug button is still there");
@ -102,35 +77,14 @@ add_task(function* () {
ok(!targetElement.querySelector(".debug-button"),
"The debug button was removed when the worker was killed");
// Finally, unregister the service worker itself
// Use message manager to work with e10s
frameScript = function() {
// Retrieve the `sw` promise created in the html page
let { sw } = content.wrappedJSObject;
sw.then(function(registration) {
registration.unregister().then(function() {
sendAsyncMessage("sw-unregistered");
},
function(e) {
dump("SW not unregistered; " + e + "\n");
});
});
};
mm = swTab.linkedBrowser.messageManager;
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
yield new Promise(done => {
mm.addMessageListener("sw-unregistered", function listener() {
mm.removeMessageListener("sw-unregistered", listener);
done();
});
});
// Finally, unregister the service worker itself.
yield unregisterServiceWorker(swTab);
ok(true, "Service worker registration unregistered");
// Now ensure that the worker registration is correctly removed.
// The list should update once the registration is destroyed.
yield waitForMutation(serviceWorkersElement, { childList: true });
assertHasWorker(false, document, "service-workers", SERVICE_WORKER);
assertHasTarget(false, document, "service-workers", SERVICE_WORKER);
yield removeTab(swTab);
yield closeAboutDebugging(tab);

View File

@ -2,15 +2,18 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-env browser */
/* eslint-disable mozilla/no-cpows-in-tests */
/* exported openAboutDebugging, closeAboutDebugging, installAddon,
uninstallAddon, waitForMutation */
uninstallAddon, waitForMutation, assertHasTarget,
waitForServiceWorkerRegistered, unregisterServiceWorker */
/* global sendAsyncMessage */
"use strict";
var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
var { utils: Cu, classes: Cc, interfaces: Ci } = Components;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
DevToolsUtils.testing = true;
@ -146,3 +149,73 @@ function waitForMutation(target, mutationOptions) {
observer.observe(target, mutationOptions);
});
}
/**
* Checks if an about:debugging TargetList element contains a Target element
* corresponding to the specified name.
* @param {Boolean} expected
* @param {Document} document
* @param {String} type
* @param {String} name
*/
function assertHasTarget(expected, document, type, name) {
let names = [...document.querySelectorAll("#" + type + " .target-name")];
names = names.map(element => element.textContent);
is(names.includes(name), expected,
"The " + type + " url appears in the list: " + names);
}
/**
* Returns a promise that will resolve after the service worker in the page
* has successfully registered itself.
* @param {Tab} tab
*/
function waitForServiceWorkerRegistered(tab) {
// Make the test page notify us when the service worker is registered.
let frameScript = function() {
// Retrieve the `sw` promise created in the html page.
let { sw } = content.wrappedJSObject;
sw.then(function(registration) {
sendAsyncMessage("sw-registered");
});
};
let mm = tab.linkedBrowser.messageManager;
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
return new Promise(done => {
mm.addMessageListener("sw-registered", function listener() {
mm.removeMessageListener("sw-registered", listener);
done();
});
});
}
/**
* Asks the service worker within the test page to unregister, and returns a
* promise that will resolve when it has successfully unregistered itself.
* @param {Tab} tab
*/
function unregisterServiceWorker(tab) {
// Use message manager to work with e10s.
let frameScript = function() {
// Retrieve the `sw` promise created in the html page.
let { sw } = content.wrappedJSObject;
sw.then(function(registration) {
registration.unregister().then(function() {
sendAsyncMessage("sw-unregistered");
},
function(e) {
dump("SW not unregistered; " + e + "\n");
});
});
};
let mm = tab.linkedBrowser.messageManager;
mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
return new Promise(done => {
mm.addMessageListener("sw-unregistered", function listener() {
mm.removeMessageListener("sw-unregistered", listener);
done();
});
});
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Service worker push test</title>
</head>
<body>
<script type="text/javascript">
"use strict";
var sw = navigator.serviceWorker.register("push-sw.js");
sw.then(
function(registration) {
dump("SW registered\n");
},
function(error) {
dump("SW not registered: " + error + "\n");
}
);
</script>
</body>
</html>

View File

@ -0,0 +1,33 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-env worker */
/* global clients */
"use strict";
// Send a message to all controlled windows.
function postMessage(message) {
return clients.matchAll().then(function(clientlist) {
clientlist.forEach(function(client) {
client.postMessage(message);
});
});
}
// Don't wait for the next page load to become the active service worker.
self.addEventListener("install", function(event) {
event.waitUntil(self.skipWaiting());
});
// Claim control over the currently open test page when activating.
self.addEventListener("activate", function(event) {
event.waitUntil(self.clients.claim().then(function() {
return postMessage("sw-claimed");
}));
});
// Forward all "push" events to the controlled window.
self.addEventListener("push", function(event) {
event.waitUntil(postMessage("sw-pushed"));
});

View File

@ -13,6 +13,7 @@ support-files =
doc_markup_events-overflow.html
doc_markup_flashing.html
doc_markup_html_mixed_case.html
doc_markup_image_and_canvas.html
doc_markup_links.html
doc_markup_mutation.html
doc_markup_navigation.html

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,6 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
debug = Debug
push = Push
addons = Add-ons
addonDebugging.label = Enable add-on debugging

View File

@ -0,0 +1,8 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- LOCALIZATION NOTE : This file contains the Storage Inspector strings. -->
<!-- LOCALIZATION NOTE : Placeholder for the searchbox that allows you to filter the table items. -->
<!ENTITY searchBox.placeholder "Filter items">

View File

@ -4,6 +4,9 @@
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
@ -16,7 +19,7 @@
<link rel="stylesheet" href="chrome://devtools/skin/components-frame.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
</head>
<body class="theme-body">
<body class="theme-body" dir="&locale.dir;">
<div id="app"></div>
<script type="application/javascript;version=1.8"

View File

@ -29,7 +29,7 @@
<script type="application/javascript" src="views/recordings.js"/>
<popupset id="performance-options-popupset">
<menupopup id="performance-filter-menupopup"/>
<menupopup id="performance-filter-menupopup" position="before_start"/>
<menupopup id="performance-options-menupopup" position="before_end">
<menuitem id="option-show-platform-data"
type="checkbox"

View File

@ -455,6 +455,25 @@ var TestActor = exports.TestActor = protocol.ActorClass({
}
}),
/**
* Get an attribute on a DOM Node.
* @param {String} selector The node selector
* @param {String} attribute The attribute name
* @return {String} value The attribute value
*/
getAttribute: protocol.method(function (selector, attribute) {
let node = this._querySelector(selector);
return node.getAttribute(attribute);
}, {
request: {
selector: Arg(0, "string"),
property: Arg(1, "string")
},
response: {
value: RetVal("string")
}
}),
/**
* Set an attribute on a DOM Node.
* @param {String} selector The node selector

View File

@ -24,6 +24,7 @@ const EVENTS = {
ROW_CONTEXT_MENU: "row-context-menu",
ROW_SELECTED: "row-selected",
ROW_UPDATED: "row-updated",
TABLE_FILTERED: "table-filtered",
SCROLL_END: "scroll-end"
};
Object.defineProperty(this, "EVENTS", {
@ -353,6 +354,7 @@ TableWidget.prototype = {
if (key != this.sortedOn) {
column.insertAt(item, index);
}
column.updateZebra();
}
this.items.set(item[this.uniqueId], item);
this.tbody.removeAttribute("empty");
@ -440,6 +442,41 @@ TableWidget.prototype = {
}
},
/**
* Filters the table based on a specific value
*
* @param {String} value: The filter value
* @param {Array} ignoreProps: Props to ignore while filtering
*/
filterItems(value, ignoreProps = []) {
if (this.filteredValue == value) {
return;
}
if (!value) {
this.emit(EVENTS.TABLE_FILTERED, []);
return;
}
this.filteredValue = value;
// Shouldn't be case-sensitive
value = value.toLowerCase();
let itemsToHide = [...this.items.keys()];
// Loop through all items and hide unmatched items
for (let [id, val] of this.items) {
for (let prop in val) {
if (ignoreProps.includes(prop)) {
continue;
}
let propValue = val[prop].toString().toLowerCase();
if (propValue.includes(value)) {
itemsToHide.splice(itemsToHide.indexOf(id), 1);
break;
}
}
}
this.emit(EVENTS.TABLE_FILTERED, itemsToHide);
},
/**
* Calls the afterScroll function when the user has stopped scrolling
*/
@ -521,6 +558,9 @@ function Column(table, id, header) {
this.onRowUpdated = this.onRowUpdated.bind(this);
this.table.on(EVENTS.ROW_UPDATED, this.onRowUpdated);
this.onTableFiltered = this.onTableFiltered.bind(this);
this.table.on(EVENTS.TABLE_FILTERED, this.onTableFiltered);
this.onClick = this.onClick.bind(this);
this.onMousedown = this.onMousedown.bind(this);
this.onKeydown = this.onKeydown.bind(this);
@ -597,6 +637,21 @@ Column.prototype = {
} else {
this.sorted = 2;
}
this.updateZebra();
},
onTableFiltered: function(event, itemsToHide) {
this._updateItems();
if (!this.cells) {
return;
}
for (let cell of this.cells) {
cell.hidden = false;
}
for (let id of itemsToHide) {
this.cells[this.items[id]].hidden = true;
}
this.updateZebra();
},
/**
@ -612,12 +667,14 @@ Column.prototype = {
if (this.highlightUpdated && this.items[id] != null) {
this.cells[this.items[id]].flash();
}
this.updateZebra();
},
destroy: function() {
this.table.off(EVENTS.COLUMN_SORTED, this.onColumnSorted);
this.table.off(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
this.table.off(EVENTS.ROW_UPDATED, this.onRowUpdated);
this.table.off(EVENTS.TABLE_FILTERED, this.onTableFiltered);
this.splitter.remove();
this.column.parentNode.remove();
this.cells = null;
@ -718,6 +775,7 @@ Column.prototype = {
}
this.items[item[this.uniqueId]] = index;
this.cells.splice(index, 0, new Cell(this, item, this.cells[index]));
this.updateZebra();
},
/**
@ -846,9 +904,21 @@ Column.prototype = {
this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
}
this._itemsDirty = false;
this.updateZebra();
return items;
},
updateZebra() {
this._updateItems();
let i = 0;
for (let cell of this.cells) {
if (!cell.hidden) {
i++;
}
cell.toggleClass("even", !(i % 2));
}
},
/**
* Click event handler for the column. Used to detect click on header for
* for sorting.
@ -911,6 +981,12 @@ Column.prototype = {
if (this.header == prevRow) {
prevRow = this.column.lastChild;
}
while (prevRow.hasAttribute("hidden")) {
prevRow = prevRow.previousSibling;
if (this.header == prevRow) {
prevRow = this.column.lastChild;
}
}
this.table.emit(EVENTS.ROW_SELECTED, prevRow.getAttribute("data-id"));
break;
@ -918,6 +994,10 @@ Column.prototype = {
event.preventDefault();
let nextRow = event.originalTarget.nextSibling ||
this.header.nextSibling;
while (nextRow.hasAttribute("hidden")) {
nextRow = nextRow.nextSibling ||
this.header.nextSibling;
}
this.table.emit(EVENTS.ROW_SELECTED, nextRow.getAttribute("data-id"));
break;
}
@ -964,6 +1044,18 @@ Cell.prototype = {
return this._id;
},
get hidden() {
return this.label.hasAttribute("hidden");
},
set hidden(value) {
if (value) {
this.label.setAttribute("hidden", "hidden");
} else {
this.label.removeAttribute("hidden");
}
},
set value(value) {
this._value = value;
if (value == null) {
@ -993,8 +1085,8 @@ Cell.prototype = {
return this._value;
},
toggleClass: function(className) {
this.label.classList.toggle(className);
toggleClass: function(className, condition) {
this.label.classList.toggle(className, condition);
},
/**
@ -1005,6 +1097,11 @@ Cell.prototype = {
this.label.classList.remove("flash-out");
// Cause a reflow so that the animation retriggers on adding back the class
let a = this.label.parentNode.offsetWidth; // eslint-disable-line
let onAnimEnd = () => {
this.label.classList.remove("flash-out");
this.label.removeEventListener("animationend", onAnimEnd);
};
this.label.addEventListener("animationend", onAnimEnd);
this.label.classList.add("flash-out");
},

View File

@ -7,6 +7,11 @@
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/storage.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % storageDTD SYSTEM "chrome://devtools/locale/storage.dtd">
%storageDTD;
]>
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
@ -20,7 +25,15 @@
<box flex="1" class="devtools-responsive-container theme-body">
<vbox id="storage-tree"/>
<splitter class="devtools-side-splitter"/>
<vbox id="storage-table" class="theme-sidebar" flex="1"/>
<vbox flex="1">
<hbox id="storage-toolbar" class="devtools-toolbar">
<textbox id="storage-searchbox"
class="devtools-searchinput"
type="search"
placeholder="&searchBox.placeholder;"/>
</hbox>
<vbox id="storage-table" class="theme-sidebar" flex="1"/>
</vbox>
<splitter class="devtools-side-splitter"/>
<vbox id="storage-sidebar" class="devtools-sidebar-tabs" hidden="true">
<vbox flex="1"/>

View File

@ -5,6 +5,7 @@ support-files =
storage-complex-values.html
storage-listings.html
storage-overflow.html
storage-search.html
storage-secured-iframe.html
storage-unsecured-iframe.html
storage-updates.html
@ -13,6 +14,7 @@ support-files =
[browser_storage_basic.js]
[browser_storage_dynamic_updates.js]
[browser_storage_overflow.js]
[browser_storage_search.js]
[browser_storage_sidebar.js]
skip-if = (os == 'win' && os_version == '6.1' && e10s && !debug) # bug 1229272
[browser_storage_values.js]

View File

@ -0,0 +1,87 @@
// Tests the filter search box in the storage inspector
"use strict";
add_task(function*() {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
let $$ = sel => gPanelWindow.document.querySelectorAll(sel);
gUI.tree.expandAll();
yield selectTreeItem(["localStorage", "http://test1.example.org"]);
// Results: 0=hidden, 1=visible
let testcases = [
// Test that search isn't case-sensitive
{
value: "FoO",
results: [0, 0, 1, 1, 0, 1, 0]
},
{
value: "OR",
results: [0, 1, 0, 0, 0, 1, 0]
},
{
value: "aNImAl",
results: [0, 1, 0, 0, 0, 0, 0]
},
// Test numbers
{
value: "01",
results: [1, 0, 0, 0, 0, 0, 1]
},
{
value: "2016",
results: [0, 0, 0, 0, 0, 0, 1]
},
{
value: "56789",
results: [1, 0, 0, 0, 0, 0, 0]
},
// Test filtering by value
{
value: "horse",
results: [0, 1, 0, 0, 0, 0, 0]
},
{
value: "$$$",
results: [0, 0, 0, 0, 1, 0, 0]
},
{
value: "bar",
results: [0, 0, 1, 1, 0, 0, 0]
},
// Test input with whitespace
{
value: "energy b",
results: [0, 0, 0, 1, 0, 0, 0]
},
// Test no input at all
{
value: "",
results: [1, 1, 1, 1, 1, 1, 1]
},
// Test input that matches nothing
{
value: "input that matches nothing",
results: [0, 0, 0, 0, 0, 0, 0]
}
];
let names = $$("#name .table-widget-cell");
let rows = $$("#value .table-widget-cell");
for (let testcase of testcases) {
info(`Testing input: ${testcase.value}`);
gUI.searchBox.value = testcase.value;
gUI.filterItems();
for (let i = 0; i < rows.length; i++) {
info(`Testing row ${i}`);
info(`key: ${names[i].value}, value: ${rows[i].value}`);
let state = testcase.results[i] ? "visible" : "hidden";
is(rows[i].hasAttribute("hidden"), !testcase.results[i],
`Row ${i} should be ${state}`);
}
}
yield finishTests();
});

View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1224115 - Storage Inspector table filtering
-->
<head>
<meta charset="utf-8">
<title>Storage inspector table filtering test</title>
</head>
<body>
<script type="text/javascript;version=1.8">
"use strict";
window.clear = () => {
localStorage.clear();
};
localStorage.setItem("01234", "56789");
localStorage.setItem("ANIMAL", "hOrSe");
localStorage.setItem("FOO", "bArBaz");
localStorage.setItem("food", "energy bar");
localStorage.setItem("money", "##$$$**");
localStorage.setItem("sport", "football");
localStorage.setItem("year", "2016");
</script>
</body>
</html>

View File

@ -81,6 +81,10 @@ var StorageUI = this.StorageUI = function StorageUI(front, target, panelWin) {
this.view = new VariablesView(this.sidebar.firstChild,
GENERIC_VARIABLES_VIEW_SETTINGS);
this.searchBox = this._panelDoc.getElementById("storage-searchbox");
this.filterItems = this.filterItems.bind(this);
this.searchBox.addEventListener("input", this.filterItems);
this.front.listStores().then(storageTypes => {
this.populateStorageTree(storageTypes);
}).then(null, console.error);
@ -113,6 +117,8 @@ StorageUI.prototype = {
this.front.off("stores-update", this.onUpdate);
this.front.off("stores-cleared", this.onCleared);
this._panelDoc.removeEventListener("keypress", this.handleKeypress);
this.searchBox.removeEventListener("input", this.filterItems);
this.searchBox = null;
this._telemetry.toolClosed("storage");
},
@ -546,6 +552,7 @@ StorageUI.prototype = {
onHostSelect: function(event, item) {
this.table.clear();
this.hideSidebar();
this.searchBox.value = "";
let [type, host] = item;
let names = null;
@ -644,6 +651,15 @@ StorageUI.prototype = {
}
},
/**
* Handles filtering the table
*/
filterItems() {
let value = this.searchBox.value;
this.table.filterItems(value, ["valueActor"]);
this._panelDoc.documentElement.classList.toggle("filtering", !!value);
},
/**
* Handles endless scrolling for the table
*/

View File

@ -49,8 +49,7 @@
}
@media (max-width: 700px) {
#inspector-pane-toggle,
#inspector-pane-toggle[pane-collapsed] {
#inspector-pane-toggle > .toolbarbutton-icon {
transform: rotate(90deg);
}
}

View File

@ -1146,8 +1146,12 @@
color: inherit;
text-align: center;
font-weight: inherit !important;
border-bottom-width: 0 !important;
border-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%) 1 1;
border-image: linear-gradient(transparent 15%,
var(--theme-splitter-color) 15%,
var(--theme-splitter-color) 85%,
transparent 85%,
transparent calc(100% - 1px),
var(--theme-splitter-color) calc(100% - 1px)) 1 1;
background-repeat: no-repeat;
}
@ -1159,7 +1163,7 @@
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
box-shadow: -0.5px 0 0 0.5px var(--theme-splitter-color);
box-shadow: -0.5px -0.5px 0 0.5px var(--theme-splitter-color);
background-position: right 6px center;
}
@ -1185,17 +1189,12 @@
color: var(--theme-body-color);
}
.table-widget-column-header + .table-widget-cell {
border-top: 1px solid var(--theme-splitter-color);
.table-widget-cell[hidden] {
display: none;
}
.table-widget-cell:last-child {
border-bottom: 1px solid var(--table-splitter-color);
}
:root:not(.filtering) .table-widget-cell:nth-child(odd):not(.theme-selected),
.table-widget-cell:not(.theme-selected)[odd] {
background: var(--table-zebra-background);
.table-widget-cell.even:not(.theme-selected) {
background-color: var(--table-zebra-background);
}
:root:not(.no-animate) .table-widget-cell.flash-out {

View File

@ -5,6 +5,8 @@ var { DebuggerServer } = require("devtools/server/main");
const protocol = require("devtools/server/protocol");
const { Arg, method, RetVal } = protocol;
loader.lazyRequireGetter(this, "ChromeUtils");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(
@ -140,6 +142,20 @@ let WorkerActor = protocol.ActorClass({
response: RetVal("json")
}),
push: method(function () {
if (this._dbg.type !== Ci.nsIWorkerDebugger.TYPE_SERVICE) {
return { error: "wrongType" };
}
let registration = this._getServiceWorkerRegistrationInfo();
let originAttributes = ChromeUtils.originAttributesToSuffix(
this._dbg.principal.originAttributes);
swm.sendPushEvent(originAttributes, registration.scope);
return { type: "pushed" };
}, {
request: {},
response: RetVal("json")
}),
onClose: function () {
if (this._attached) {
this._detach();

View File

@ -32,6 +32,7 @@ var loaderModules = {
"Services": Object.create(Services),
"toolkit/loader": Loader,
PromiseDebugging,
ChromeUtils,
ThreadSafeChromeUtils,
HeapSnapshot,
};

View File

@ -1450,7 +1450,8 @@ WorkerClient.prototype = {
return this.request({
to: connectReponse.threadActor,
type: "attach"
type: "attach",
options: aOptions
}).then(attachResponse => {
if (attachResponse.error) {
aOnResponse(attachResponse, null);

View File

@ -366,6 +366,7 @@ var loader = {
var {
Debugger,
URL,
createSandbox,
dump,
rpc,
@ -428,6 +429,7 @@ var {
return {
Debugger,
URL: this.URL,
createSandbox,
dump: this.dump,
rpc,
@ -465,6 +467,7 @@ var {
return {
Debugger: this.Debugger,
URL: this.URL,
createSandbox: this.createSandbox,
dump: this.dump,
rpc: this.rpc,
@ -494,7 +497,7 @@ this.worker = new WorkerDebuggerLoader({
"Debugger": Debugger,
"PromiseDebugging": PromiseDebugging,
"Services": Object.create(null),
"URL": null,
"URL": URL,
"chrome": chrome,
"xpcInspector": xpcInspector
},

View File

@ -69,10 +69,6 @@ extern const wchar_t* kFlashFullscreenClass;
#include <ApplicationServices/ApplicationServices.h>
#endif // defined(XP_MACOSX)
// This is the pref used to determine whether to use Shumway on a Flash object
// (when Shumway is enabled).
static const char kShumwayWhitelistPref[] = "shumway.swf.whitelist";
using namespace mozilla::plugins;
using namespace mozilla::layers;
using namespace mozilla::gl;
@ -137,7 +133,6 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
, mUseSurrogate(true)
, mNPP(npp)
, mNPNIface(npniface)
, mIsWhitelistedForShumway(false)
, mWindowType(NPWindowTypeWindow)
, mDrawingModel(kDefaultDrawingModel)
, mLastRecordedDrawingModel(-1)

View File

@ -316,18 +316,6 @@ public:
aOutput = mSrcAttribute;
}
/**
* This function tells us whether this plugin instance would have been
* whitelisted for Shumway if Shumway had been enabled. This is being used
* for the purpose of gathering telemetry on Flash hangs that could
* potentially be avoided by using Shumway instead.
*/
bool
IsWhitelistedForShumway() const
{
return mIsWhitelistedForShumway;
}
virtual bool
AnswerPluginFocusChange(const bool& gotFocus) override;
@ -402,7 +390,6 @@ private:
NPP mNPP;
const NPNetscapeFuncs* mNPNIface;
nsCString mSrcAttribute;
bool mIsWhitelistedForShumway;
NPWindowType mWindowType;
int16_t mDrawingModel;
IntSize mWindowSize;

View File

@ -1135,24 +1135,6 @@ PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aA
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName);
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"),
mPluginVersion);
if (mIsFlashPlugin) {
bool isWhitelistedForShumway = false;
{ // Scope for lock
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
if (!mProtocolCallStack.IsEmpty()) {
mozilla::ipc::IProtocol* topProtocol =
mProtocolCallStack.LastElement();
PluginInstanceParent* instance =
GetManagingInstance(topProtocol);
if (instance) {
isWhitelistedForShumway =
instance->IsWhitelistedForShumway();
}
}
}
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginIsWhitelistedForShumway"),
isWhitelistedForShumway);
}
}
}

View File

@ -142,8 +142,8 @@ HTTP(..) == bug533251.html bug533251-ref.html
HTTP(..) == font-familiy-whitespace-1.html font-familiy-whitespace-1-ref.html
HTTP(..) != font-familiy-whitespace-1.html font-familiy-whitespace-1-notref.html
skip-if(B2G||Mulet) HTTP(..) == ivs-1.html ivs-1-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) HTTP(..) == cjkcisvs-1.html cjkcisvs-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet||Android) HTTP(..) == ivs-1.html ivs-1-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop # Android bug 1250229
skip-if(B2G||Mulet||Android) HTTP(..) == cjkcisvs-1.html cjkcisvs-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop # Android bug 1250229
skip-if(B2G||Mulet) HTTP(..) == missing-names.html missing-names-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop

View File

@ -169,7 +169,7 @@ fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(cocoaWidget,65,69) == 1193519-sideways-l
== 1243125-1-floats-overflowing.html 1243125-1-floats-overflowing-ref.html
HTTP(..) == 1248248-1-orientation-break-glyphrun.html 1248248-1-orientation-break-glyphrun-ref.html
skip-if(Android) HTTP(..) == 1248248-1-orientation-break-glyphrun.html 1248248-1-orientation-break-glyphrun-ref.html # Android bug 1250229
# Suite of tests from Gérard Talbot in bug 1079151
# Frequent Windows 7 load failed: timed out waiting for test to complete (waiting for onload scripts to complete), bug 1167155 and friends

View File

@ -27,7 +27,6 @@ globals:
TelemetryStopwatch: false
UITelemetry: false
UserAgentOverrides: 0
WebappManager: false
XPCOMUtils: false
ctypes: false
dump: false

View File

@ -865,30 +865,6 @@ pref("browser.snippets.enabled", true);
pref("browser.snippets.syncPromo.enabled", true);
pref("browser.snippets.firstrunHomepage.enabled", true);
// The URL of the APK factory from which we obtain APKs for webapps.
pref("browser.webapps.apkFactoryUrl", "https://controller.apk.firefox.com/application.apk");
// How frequently to check for webapp updates, in seconds (86400 is daily).
pref("browser.webapps.updateInterval", 86400);
// Whether or not to check for updates. Enabled by default, but the runtime
// disables it for webapp profiles on firstrun, so only the main Fennec process
// checks for updates (to avoid duplicate update notifications).
//
// In the future, we might want to make each webapp process check for updates
// for its own webapp, in which case we'll need to have a third state for this
// preference. Thus it's an integer rather than a boolean.
//
// Possible values:
// 0: don't check for updates
// 1: do check for updates
pref("browser.webapps.checkForUpdates", 1);
// The URL of the service that checks for updates.
// To test updates, set this to http://apk-update-checker.paas.allizom.org,
// which is a test server that always reports all apps as having updates.
pref("browser.webapps.updateCheckUrl", "https://controller.apk.firefox.com/app_updates");
// The mode of home provider syncing.
// 0: Sync always
// 1: Sync only when on wifi

View File

@ -184,44 +184,12 @@
</intent-filter>
</activity>
<activity android:name="org.mozilla.gecko.webapp.Dispatcher"
android:noHistory="true" >
<intent-filter>
<!-- catch links from synthetic apks -->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/webapp" />
</intent-filter>
</activity>
<receiver android:name="org.mozilla.gecko.webapp.UninstallListener" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name="org.mozilla.gecko.webapp.TaskKiller">
<intent-filter>
<action android:name="org.mozilla.webapp.TASK_REMOVED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver android:name="org.mozilla.gecko.restrictions.RestrictionProvider">
<intent-filter>
<action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
</intent-filter>
</receiver>
<!-- Declare a predefined number of Webapp<num> activities. These are
used so that each web app can launch in its own process. Keep
this number in sync with the total number of web apps handled in
WebappAllocator. -->
#define FRAGMENT WebappManifestFragment.xml.frag.in
#include WebappFragmentRepeater.inc
<!-- Masquerade as the Resolver so that we can be opened from the Marketplace. -->
<activity-alias
android:name="com.android.internal.app.ResolverActivity"
@ -254,6 +222,15 @@
<receiver android:name="org.mozilla.gecko.AlarmReceiver" >
</receiver>
<receiver
android:name="org.mozilla.gecko.notifications.WhatsNewReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" android:path="org.mozilla.gecko" />
</intent-filter>
</receiver>
#include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
#ifdef MOZ_ANDROID_SEARCH_ACTIVITY
#include ../search/manifests/SearchAndroidManifest_activities.xml.in

View File

@ -1,301 +0,0 @@
#define APPNUM 0
#include @FRAGMENT@
#define APPNUM 1
#include @FRAGMENT@
#define APPNUM 2
#include @FRAGMENT@
#define APPNUM 3
#include @FRAGMENT@
#define APPNUM 4
#include @FRAGMENT@
#define APPNUM 5
#include @FRAGMENT@
#define APPNUM 6
#include @FRAGMENT@
#define APPNUM 7
#include @FRAGMENT@
#define APPNUM 8
#include @FRAGMENT@
#define APPNUM 9
#include @FRAGMENT@
#define APPNUM 10
#include @FRAGMENT@
#define APPNUM 11
#include @FRAGMENT@
#define APPNUM 12
#include @FRAGMENT@
#define APPNUM 13
#include @FRAGMENT@
#define APPNUM 14
#include @FRAGMENT@
#define APPNUM 15
#include @FRAGMENT@
#define APPNUM 16
#include @FRAGMENT@
#define APPNUM 17
#include @FRAGMENT@
#define APPNUM 18
#include @FRAGMENT@
#define APPNUM 19
#include @FRAGMENT@
#define APPNUM 20
#include @FRAGMENT@
#define APPNUM 21
#include @FRAGMENT@
#define APPNUM 22
#include @FRAGMENT@
#define APPNUM 23
#include @FRAGMENT@
#define APPNUM 24
#include @FRAGMENT@
#define APPNUM 25
#include @FRAGMENT@
#define APPNUM 26
#include @FRAGMENT@
#define APPNUM 27
#include @FRAGMENT@
#define APPNUM 28
#include @FRAGMENT@
#define APPNUM 29
#include @FRAGMENT@
#define APPNUM 30
#include @FRAGMENT@
#define APPNUM 31
#include @FRAGMENT@
#define APPNUM 32
#include @FRAGMENT@
#define APPNUM 33
#include @FRAGMENT@
#define APPNUM 34
#include @FRAGMENT@
#define APPNUM 35
#include @FRAGMENT@
#define APPNUM 36
#include @FRAGMENT@
#define APPNUM 37
#include @FRAGMENT@
#define APPNUM 38
#include @FRAGMENT@
#define APPNUM 39
#include @FRAGMENT@
#define APPNUM 40
#include @FRAGMENT@
#define APPNUM 41
#include @FRAGMENT@
#define APPNUM 42
#include @FRAGMENT@
#define APPNUM 43
#include @FRAGMENT@
#define APPNUM 44
#include @FRAGMENT@
#define APPNUM 45
#include @FRAGMENT@
#define APPNUM 46
#include @FRAGMENT@
#define APPNUM 47
#include @FRAGMENT@
#define APPNUM 48
#include @FRAGMENT@
#define APPNUM 49
#include @FRAGMENT@
#define APPNUM 50
#include @FRAGMENT@
#define APPNUM 51
#include @FRAGMENT@
#define APPNUM 52
#include @FRAGMENT@
#define APPNUM 53
#include @FRAGMENT@
#define APPNUM 54
#include @FRAGMENT@
#define APPNUM 55
#include @FRAGMENT@
#define APPNUM 56
#include @FRAGMENT@
#define APPNUM 57
#include @FRAGMENT@
#define APPNUM 58
#include @FRAGMENT@
#define APPNUM 59
#include @FRAGMENT@
#define APPNUM 60
#include @FRAGMENT@
#define APPNUM 61
#include @FRAGMENT@
#define APPNUM 62
#include @FRAGMENT@
#define APPNUM 63
#include @FRAGMENT@
#define APPNUM 64
#include @FRAGMENT@
#define APPNUM 65
#include @FRAGMENT@
#define APPNUM 66
#include @FRAGMENT@
#define APPNUM 67
#include @FRAGMENT@
#define APPNUM 68
#include @FRAGMENT@
#define APPNUM 69
#include @FRAGMENT@
#define APPNUM 70
#include @FRAGMENT@
#define APPNUM 71
#include @FRAGMENT@
#define APPNUM 72
#include @FRAGMENT@
#define APPNUM 73
#include @FRAGMENT@
#define APPNUM 74
#include @FRAGMENT@
#define APPNUM 75
#include @FRAGMENT@
#define APPNUM 76
#include @FRAGMENT@
#define APPNUM 77
#include @FRAGMENT@
#define APPNUM 78
#include @FRAGMENT@
#define APPNUM 79
#include @FRAGMENT@
#define APPNUM 80
#include @FRAGMENT@
#define APPNUM 81
#include @FRAGMENT@
#define APPNUM 82
#include @FRAGMENT@
#define APPNUM 83
#include @FRAGMENT@
#define APPNUM 84
#include @FRAGMENT@
#define APPNUM 85
#include @FRAGMENT@
#define APPNUM 86
#include @FRAGMENT@
#define APPNUM 87
#include @FRAGMENT@
#define APPNUM 88
#include @FRAGMENT@
#define APPNUM 89
#include @FRAGMENT@
#define APPNUM 90
#include @FRAGMENT@
#define APPNUM 91
#include @FRAGMENT@
#define APPNUM 92
#include @FRAGMENT@
#define APPNUM 93
#include @FRAGMENT@
#define APPNUM 94
#include @FRAGMENT@
#define APPNUM 95
#include @FRAGMENT@
#define APPNUM 96
#include @FRAGMENT@
#define APPNUM 97
#include @FRAGMENT@
#define APPNUM 98
#include @FRAGMENT@
#define APPNUM 99
#include @FRAGMENT@
#undef APPNUM
#undef FRAGMENT

View File

@ -1,9 +0,0 @@
<activity android:name="org.mozilla.gecko.webapp.Webapps$Webapp@APPNUM@"
android:label="@string/webapp_generic_name"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:process=":@ANDROID_PACKAGE_NAME@.Webapp@APPNUM@"
android:theme="@style/Gecko.App"
android:launchMode="singleTop"
android:exported="true"
/>

View File

@ -679,6 +679,7 @@ public class BrowserApp extends GeckoApp
mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
"Gecko:DelayedStartup",
"Menu:Open",
"Menu:Update",
"LightweightTheme:Update",
@ -1390,6 +1391,7 @@ public class BrowserApp extends GeckoApp
}
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
"Gecko:DelayedStartup",
"Menu:Open",
"Menu:Update",
"LightweightTheme:Update",

View File

@ -43,8 +43,6 @@ import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.webapp.EventListener;
import org.mozilla.gecko.webapp.UninstallListener;
import org.mozilla.gecko.widget.ButtonToast;
import android.annotation.SuppressLint;
@ -144,7 +142,6 @@ public abstract class GeckoApp
NORMAL, /* normal application start */
URL, /* launched with a passed URL */
PREFETCH, /* launched with a passed URL that we prefetch */
WEBAPP, /* launched as a webapp runtime */
GUEST, /* launched in guest browsing */
RESTRICTED, /* launched with restricted profile */
SHORTCUT /* launched from a homescreen shortcut */
@ -214,8 +211,6 @@ public abstract class GeckoApp
private volatile HealthRecorder mHealthRecorder;
private volatile Locale mLastLocale;
private EventListener mWebappEventListener;
private Intent mRestartIntent;
abstract public int getLayout();
@ -691,9 +686,7 @@ public abstract class GeckoApp
@Override
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("Gecko:DelayedStartup")) {
ThreadUtils.postToBackgroundThread(new UninstallListener.DelayedStartupTask(this));
} else if (event.equals("Gecko:Ready")) {
if (event.equals("Gecko:Ready")) {
mGeckoReadyStartupTimer.stop();
geckoConnected();
@ -711,10 +704,6 @@ public abstract class GeckoApp
doShutdown();
return;
} else if ("NativeApp:IsDebuggable".equals(event)) {
JSONObject ret = new JSONObject();
ret.put("isDebuggable", getIsDebuggable());
EventDispatcher.sendResponse(message, ret);
} else if (event.equals("Accessibility:Event")) {
GeckoAccessibility.sendAccessibilityEvent(message);
}
@ -1268,10 +1257,8 @@ public abstract class GeckoApp
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
"Gecko:Ready",
"Gecko:DelayedStartup",
"Gecko:Exited",
"Accessibility:Event",
"NativeApp:IsDebuggable");
"Accessibility:Event");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
"Accessibility:Ready",
@ -1299,11 +1286,6 @@ public abstract class GeckoApp
EventDispatcher.getInstance().registerBackgroundThreadListener((BundleEventListener) this,
"History:GetPrePathLastVisitedTimeMilliseconds");
if (mWebappEventListener == null) {
mWebappEventListener = new EventListener();
mWebappEventListener.registerEvents();
}
GeckoThread.launch();
Bundle stateBundle = ContextUtils.getBundleExtra(getIntent(), EXTRA_STATE_BUNDLE);
@ -2103,10 +2085,8 @@ public abstract class GeckoApp
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
"Gecko:Ready",
"Gecko:DelayedStartup",
"Gecko:Exited",
"Accessibility:Event",
"NativeApp:IsDebuggable");
"Accessibility:Event");
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this,
"Accessibility:Ready",
@ -2133,11 +2113,6 @@ public abstract class GeckoApp
EventDispatcher.getInstance().unregisterBackgroundThreadListener((BundleEventListener) this,
"History:GetPrePathLastVisitedTimeMilliseconds");
if (mWebappEventListener != null) {
mWebappEventListener.unregisterEvents();
mWebappEventListener = null;
}
deleteTempFiles();
if (mDoorHangerPopup != null)
@ -2673,23 +2648,6 @@ public abstract class GeckoApp
return versionCode;
}
protected boolean getIsDebuggable() {
// Return false so Fennec doesn't appear to be debuggable. WebappImpl
// then overrides this and returns the value of android:debuggable for
// the webapp APK, so webapps get the behavior supported by this method
// (i.e. automatic configuration and enabling of the remote debugger).
return false;
// If we ever want to expose this for Fennec, here's how we would do it:
// int flags = 0;
// try {
// flags = getPackageManager().getPackageInfo(getPackageName(), 0).applicationInfo.flags;
// } catch (NameNotFoundException e) {
// Log.wtf(LOGTAG, getPackageName() + " not found", e);
// }
// return (flags & android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
// FHR reason code for a session end prior to a restart for a
// locale change.
private static final String SESSION_END_LOCALE_CHANGED = "L";

View File

@ -2835,9 +2835,4 @@ public class GeckoAppShell
final Display disp = wm.getDefaultDisplay();
return new Rect(0, 0, disp.getWidth(), disp.getHeight());
}
@JNITarget
static boolean isWebAppProcess() {
return GeckoProfile.get(getApplicationContext()).isWebAppProfile();
}
}

View File

@ -145,7 +145,6 @@ public class GeckoApplication extends Application
// Make sure that all browser-ish applications default to the real LocalBrowserDB.
// GeckoView consumers use their own Application class, so this doesn't affect them.
// WebappImpl overrides this on creation.
//
// We need to do this before any access to the profile; it controls
// which database class is used.

Some files were not shown because too many files have changed in this diff Show More