Backed out 12 changesets (bug 1202482, bug 1202483, bug 1202481, bug 1202486, bug 1202479, bug 1202478, bug 1197475, bug 1203331, bug 1139860, bug 1202501, bug 1199473, bug 1190662) for Mulet mochitest-5 timeouts

CLOSED TREE

Backed out changeset 6503123e95dd (bug 1139860)
Backed out changeset b83bc163064d (bug 1203331)
Backed out changeset 2f501bd57cd2 (bug 1202481)
Backed out changeset 37e6ac7beb42 (bug 1202486)
Backed out changeset f9b6e99e620e (bug 1202483)
Backed out changeset 466af9f9baee (bug 1202482)
Backed out changeset 6be690e265a2 (bug 1202479)
Backed out changeset 57ff88bfccf4 (bug 1197475)
Backed out changeset 7e8c04ff6049 (bug 1202478)
Backed out changeset 525227997274 (bug 1202501)
Backed out changeset da317cdb79d3 (bug 1199473)
Backed out changeset 73b8ddd6dac9 (bug 1190662)
This commit is contained in:
Phil Ringnalda
2015-09-22 19:29:51 -07:00
parent 978242ee5b
commit aaf72d29b9
38 changed files with 188 additions and 1415 deletions

View File

@@ -14,11 +14,6 @@ var {
// WeakMap[Extension -> BrowserAction]
var browserActionMap = new WeakMap();
// WeakMap[Extension -> docshell]
// This map is a cache of the windowless browser that's used to render ImageData
// for the browser_action icon.
let imageRendererMap = new WeakMap();
function browserActionOf(extension)
{
return browserActionMap.get(extension);
@@ -46,6 +41,9 @@ function BrowserAction(options, extension)
this.icon = new DefaultWeakMap(options.default_icon);
this.popup = new DefaultWeakMap(options.default_popup);
// Make the default something that won't compare equal to anything.
this.prevPopups = new DefaultWeakMap({});
this.context = null;
}
@@ -65,14 +63,12 @@ BrowserAction.prototype = {
this.updateTab(null, node);
let tabbrowser = document.defaultView.gBrowser;
tabbrowser.tabContainer.addEventListener("TabSelect", this);
tabbrowser.ownerDocument.addEventListener("TabSelect", () => {
this.updateTab(tabbrowser.selectedTab, node);
});
node.addEventListener("command", event => {
let tab = tabbrowser.selectedTab;
let popup = this.getProperty(tab, "popup");
if (popup) {
this.togglePopup(node, popup);
} else {
if (node.getAttribute("type") != "panel") {
this.emit("click");
}
});
@@ -83,81 +79,6 @@ BrowserAction.prototype = {
this.widget = widget;
},
handleEvent(event) {
if (event.type == "TabSelect") {
let window = event.target.ownerDocument.defaultView;
let tabbrowser = window.gBrowser;
let instance = CustomizableUI.getWidget(this.id).forWindow(window);
if (instance) {
this.updateTab(tabbrowser.selectedTab, instance.node);
}
}
},
togglePopup(node, popupResource) {
let popupURL = this.extension.baseURI.resolve(popupResource);
let document = node.ownerDocument;
let panel = document.createElement("panel");
panel.setAttribute("class", "browser-action-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("flip", "slide");
node.appendChild(panel);
panel.addEventListener("popuphidden", () => {
this.context.unload();
this.context = null;
panel.remove();
});
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
panel.appendChild(browser);
let loadListener = () => {
panel.removeEventListener("load", loadListener);
this.context = new ExtensionPage(this.extension, {
type: "popup",
contentWindow: browser.contentWindow,
uri: Services.io.newURI(popupURL, null, null),
docShell: browser.docShell,
});
GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context);
browser.setAttribute("src", popupURL);
let contentLoadListener = () => {
browser.removeEventListener("load", contentLoadListener);
let contentViewer = browser.docShell.contentViewer;
let width = {}, height = {};
try {
contentViewer.getContentSize(width, height);
[width, height] = [width.value, height.value];
} catch (e) {
// getContentSize can throw
[width, height] = [400, 400];
}
let window = document.defaultView;
width /= window.devicePixelRatio;
height /= window.devicePixelRatio;
width = Math.min(width, 800);
height = Math.min(height, 800);
browser.setAttribute("width", width);
browser.setAttribute("height", height);
let anchor = document.getAnonymousElementByAttribute(node, "class", "toolbarbutton-icon");
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
};
browser.addEventListener("load", contentLoadListener, true);
};
panel.addEventListener("load", loadListener);
},
// Initialize the toolbar icon and popup given that |tab| is the
// current tab and |node| is the CustomizableUI node. Note: |tab|
// will be null if we don't know the current tab yet (during
@@ -197,6 +118,57 @@ BrowserAction.prototype = {
let iconURL = this.getIcon(tab, node);
node.setAttribute("image", iconURL);
let popup = this.getProperty(tab, "popup");
if (popup != this.prevPopups.get(window)) {
this.prevPopups.set(window, popup);
let panel = node.querySelector("panel");
if (panel) {
panel.remove();
}
if (popup) {
let popupURL = this.extension.baseURI.resolve(popup);
node.setAttribute("type", "panel");
let document = node.ownerDocument;
let panel = document.createElement("panel");
panel.setAttribute("class", "browser-action-panel");
panel.setAttribute("type", "arrow");
panel.setAttribute("flip", "slide");
node.appendChild(panel);
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = document.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("width", "500");
browser.setAttribute("height", "500");
panel.appendChild(browser);
let loadListener = () => {
panel.removeEventListener("load", loadListener);
if (this.context) {
this.context.unload();
}
this.context = new ExtensionPage(this.extension, {
type: "popup",
contentWindow: browser.contentWindow,
uri: Services.io.newURI(popupURL, null, null),
docShell: browser.docShell,
});
GlobalManager.injectInDocShell(browser.docShell, this.extension, this.context);
browser.setAttribute("src", popupURL);
};
panel.addEventListener("load", loadListener);
} else {
node.removeAttribute("type");
}
}
},
// Note: tab is allowed to be null here.
@@ -264,13 +236,6 @@ BrowserAction.prototype = {
},
shutdown() {
let widget = CustomizableUI.getWidget(this.id);
for (let instance of widget.instances) {
let window = instance.node.ownerDocument.defaultView;
let tabbrowser = window.gBrowser;
tabbrowser.tabContainer.removeEventListener("TabSelect", this);
}
CustomizableUI.destroyWidget(this.id);
},
};
@@ -288,37 +253,8 @@ extensions.on("shutdown", (type, extension) => {
browserActionMap.get(extension).shutdown();
browserActionMap.delete(extension);
}
imageRendererMap.delete(extension);
});
function convertImageDataToPNG(extension, imageData)
{
let webNav = imageRendererMap.get(extension);
if (!webNav) {
webNav = Services.appShell.createWindowlessBrowser(false);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(extension.baseURI,
{addonId: extension.id});
let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor);
let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
GlobalManager.injectInDocShell(docShell, extension, null);
docShell.createAboutBlankContentViewer(principal);
}
let document = webNav.document;
let canvas = document.createElement("canvas");
canvas.width = imageData.width;
canvas.height = imageData.height;
canvas.getContext("2d").putImageData(imageData, 0, 0);
let url = canvas.toDataURL("image/png");
canvas.remove();
return url;
}
extensions.registerAPI((extension, context) => {
return {
browserAction: {
@@ -347,11 +283,10 @@ extensions.registerAPI((extension, context) => {
setIcon: function(details, callback) {
let tab = details.tabId ? TabManager.getTab(details.tabId) : null;
if (details.imageData) {
let url = convertImageDataToPNG(extension, details.imageData);
browserActionOf(extension).setProperty(tab, "icon", url);
} else {
browserActionOf(extension).setProperty(tab, "icon", details.path);
// FIXME: Support the imageData attribute.
return;
}
browserActionOf(extension).setProperty(tab, "icon", details.path);
},
setBadgeText: function(details) {

View File

@@ -42,18 +42,11 @@ var pageDataMap = new WeakMap();
// in some tab-specific details and keep data around about the
// ExtensionPage.
extensions.on("page-load", (type, page, params, sender, delegate) => {
if (params.type == "tab" || params.type == "popup") {
if (params.type == "tab") {
let browser = params.docShell.chromeEventHandler;
let parentWindow = browser.ownerDocument.defaultView;
page.windowId = WindowManager.getId(parentWindow);
let tab = null;
if (params.type == "tab") {
tab = parentWindow.gBrowser.getTabForBrowser(browser);
sender.tabId = TabManager.getId(tab);
page.tabId = TabManager.getId(tab);
}
let tab = parentWindow.gBrowser.getTabForBrowser(browser);
sender.tabId = TabManager.getId(tab);
pageDataMap.set(page, {tab, parentWindow});
}
@@ -70,9 +63,7 @@ extensions.on("page-shutdown", (type, page) => {
let {tab, parentWindow} = pageDataMap.get(page);
pageDataMap.delete(page);
if (tab) {
parentWindow.gBrowser.removeTab(tab);
}
parentWindow.gBrowser.removeTab(tab);
}
});
@@ -86,15 +77,6 @@ extensions.on("fill-browser-data", (type, browser, data, result) => {
data.tabId = tabId;
});
global.currentWindow = function(context)
{
let pageData = pageDataMap.get(context);
if (pageData) {
return pageData.parentWindow;
}
return WindowManager.topWindow;
}
// TODO: activeTab permission
extensions.registerAPI((extension, context) => {
@@ -275,7 +257,7 @@ extensions.registerAPI((extension, context) => {
}
}
let window = "windowId" in createProperties ?
let window = createProperties.windowId ?
WindowManager.getWindow(createProperties.windowId) :
WindowManager.topWindow;
if (!window.gBrowser) {
@@ -400,7 +382,7 @@ extensions.registerAPI((extension, context) => {
if ("windowId" in queryInfo) {
if (queryInfo.windowId == WindowManager.WINDOW_ID_CURRENT) {
if (currentWindow(context) != window) {
if (context.contentWindow != window) {
return false;
}
} else {
@@ -411,7 +393,7 @@ extensions.registerAPI((extension, context) => {
}
if ("currentWindow" in queryInfo) {
let eq = window == currentWindow(context);
let eq = window == context.contentWindow;
if (queryInfo.currentWindow != eq) {
return false;
}
@@ -424,9 +406,6 @@ extensions.registerAPI((extension, context) => {
let e = Services.wm.getEnumerator("navigator:browser");
while (e.hasMoreElements()) {
let window = e.getNext();
if (window.document.readyState != "complete") {
continue;
}
let tabs = TabManager.getTabs(extension, window);
for (let tab of tabs) {
if (matches(window, tab)) {

View File

@@ -45,11 +45,7 @@ extensions.registerAPI((extension, context) => {
},
getCurrent: function(getInfo, callback) {
if (!callback) {
callback = getInfo;
getInfo = {};
}
let window = currentWindow(context);
let window = context.contentWindow;
runSafe(context, callback, WindowManager.convert(extension, window, getInfo));
},
@@ -69,9 +65,7 @@ extensions.registerAPI((extension, context) => {
let windows = [];
while (e.hasMoreElements()) {
let window = e.getNext();
if (window.document.readyState == "complete") {
windows.push(WindowManager.convert(extension, window, getInfo));
}
windows.push(WindowManager.convert(extension, window, getInfo));
}
runSafe(context, callback, windows);
},

View File

@@ -1,13 +1,8 @@
[DEFAULT]
skip-if = os == 'android' || buildapp == 'b2g' || os == 'mac'
support-files =
head.js
[browser_ext_simple.js]
[browser_ext_currentWindow.js]
[browser_extensions_simple.js]
[browser_ext_browserAction_simple.js]
[browser_ext_browserAction_icon.js]
[browser_ext_getViews.js]
[browser_ext_tabs_executeScript.js]
[browser_ext_tabs_query.js]
[browser_ext_tabs_update.js]

View File

@@ -1,40 +0,0 @@
add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"browser_action": {},
"background": {
"page": "background.html",
}
},
files: {
"background.html": `<canvas id="canvas" width="2" height="2">
<script src="background.js"></script>`,
"background.js": function() {
var canvas = document.getElementById("canvas");
var canvasContext = canvas.getContext("2d");
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.fillStyle = "green";
canvasContext.fillRect(0, 0, 1, 1);
var url = canvas.toDataURL("image/png");
var imageData = canvasContext.getImageData(0, 0, canvas.width, canvas.height);
browser.browserAction.setIcon({imageData});
browser.test.sendMessage("imageURL", url);
}
},
});
let [_, url] = yield Promise.all([extension.startup(), extension.awaitMessage("imageURL")]);
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
let image = node.getAttribute("image");
is(image, url, "image is correct");
yield extension.unload();
});

View File

@@ -22,31 +22,15 @@ add_task(function* () {
background: function() {
browser.runtime.onMessage.addListener(msg => {
browser.test.assertEq(msg, "from-popup", "correct message received");
browser.test.sendMessage("popup");
browser.test.notifyPass("browser_action.simple");
});
},
});
yield extension.startup();
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(window).node;
// Do this a few times to make sure the pop-up is reloaded each time.
for (let i = 0; i < 3; i++) {
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup");
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
}
// FIXME: Should really test opening the popup here.
yield extension.awaitFinish("browser_action.simple");
yield extension.unload();
});

View File

@@ -1,173 +0,0 @@
function* focusWindow(win)
{
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
if (fm.activeWindow == win) {
return;
}
let promise = new Promise(resolve => {
win.addEventListener("focus", function listener() {
win.removeEventListener("focus", listener, true);
resolve();
}, true);
});
win.focus();
yield promise;
}
function genericChecker()
{
var kind = "background";
var path = window.location.pathname;
if (path.indexOf("popup") != -1) {
kind = "popup";
} else if (path.indexOf("page") != -1) {
kind = "page";
}
browser.test.onMessage.addListener((msg, ...args) => {
if (msg == kind + "-check-current1") {
browser.tabs.query({
currentWindow: true
}, function(tabs) {
browser.test.sendMessage("result", tabs[0].windowId);
});
} else if (msg == kind + "-check-current2") {
browser.tabs.query({
windowId: browser.windows.WINDOW_ID_CURRENT
}, function(tabs) {
browser.test.sendMessage("result", tabs[0].windowId);
});
} else if (msg == kind + "-check-current3") {
browser.windows.getCurrent(function(window) {
browser.test.sendMessage("result", window.id);
});
} else if (msg == kind + "-open-page") {
browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("page.html")});
} else if (msg == kind + "-close-page") {
browser.tabs.query({
windowId: args[0],
}, tabs => {
var tab = tabs.find(tab => tab.url.indexOf("page.html") != -1);
browser.tabs.remove(tab.id, () => {
browser.test.sendMessage("closed");
});
});
}
});
browser.test.sendMessage(kind + "-ready");
}
add_task(function* () {
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
yield focusWindow(win2);
yield BrowserTestUtils.loadURI(win1.gBrowser.selectedBrowser, "about:robots");
yield BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
"browser_action": {
"default_popup": "popup.html"
},
},
files: {
"page.html": `
<!DOCTYPE html>
<html><body>
<script src="page.js"></script>
</body></html>
`,
"page.js": genericChecker,
"popup.html": `
<!DOCTYPE html>
<html><body>
<script src="popup.js"></script>
</body></html>
`,
"popup.js": genericChecker,
},
background: genericChecker,
});
yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let winId1 = WindowManager.getId(win1);
let winId2 = WindowManager.getId(win2);
function* checkWindow(kind, winId, name) {
extension.sendMessage(kind + "-check-current1");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 1) [${kind}]`);
extension.sendMessage(kind + "-check-current2");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 2) [${kind}]`);
extension.sendMessage(kind + "-check-current3");
is((yield extension.awaitMessage("result")), winId, `${name} is on top (check 3) [${kind}]`);
}
yield focusWindow(win1);
yield checkWindow("background", winId1, "win1");
yield focusWindow(win2);
yield checkWindow("background", winId2, "win2");
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
}
// Set focus to some other window.
yield focusWindow(window);
yield triggerPopup(win1, function*() {
yield checkWindow("popup", winId1, "win1");
});
yield triggerPopup(win2, function*() {
yield checkWindow("popup", winId2, "win2");
});
function* triggerPage(winId, name) {
extension.sendMessage("background-open-page", winId);
yield extension.awaitMessage("page-ready");
yield checkWindow("page", winId, name);
extension.sendMessage("background-close-page", winId);
yield extension.awaitMessage("closed");
}
yield triggerPage(winId1, "win1");
yield triggerPage(winId2, "win2");
yield extension.unload();
yield BrowserTestUtils.closeWindow(win1);
yield BrowserTestUtils.closeWindow(win2);
});

View File

@@ -1,178 +0,0 @@
function genericChecker()
{
var kind = "background";
var path = window.location.pathname;
if (path.indexOf("popup") != -1) {
kind = "popup";
} else if (path.indexOf("tab") != -1) {
kind = "tab";
}
window.kind = kind;
browser.test.onMessage.addListener((msg, ...args) => {
if (msg == kind + "-check-views") {
var views = browser.extension.getViews();
var counts = {
"background": 0,
"tab": 0,
"popup": 0
};
for (var i = 0; i < views.length; i++) {
var view = views[i];
browser.test.assertTrue(view.kind in counts, "view type is valid");
counts[view.kind]++;
if (view.kind == "background") {
browser.test.assertTrue(view === browser.extension.getBackgroundPage(),
"background page is correct");
}
}
browser.test.sendMessage("counts", counts);
} else if (msg == kind + "-open-tab") {
browser.tabs.create({windowId: args[0], url: chrome.runtime.getURL("tab.html")});
} else if (msg == kind + "-close-tab") {
browser.tabs.query({
windowId: args[0],
}, tabs => {
var tab = tabs.find(tab => tab.url.indexOf("tab.html") != -1);
browser.tabs.remove(tab.id, () => {
browser.test.sendMessage("closed");
});
});
}
});
browser.test.sendMessage(kind + "-ready");
}
add_task(function* () {
let win1 = yield BrowserTestUtils.openNewBrowserWindow();
let win2 = yield BrowserTestUtils.openNewBrowserWindow();
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
"browser_action": {
"default_popup": "popup.html"
},
},
files: {
"tab.html": `
<!DOCTYPE html>
<html><body>
<script src="tab.js"></script>
</body></html>
`,
"tab.js": genericChecker,
"popup.html": `
<!DOCTYPE html>
<html><body>
<script src="popup.js"></script>
</body></html>
`,
"popup.js": genericChecker,
},
background: genericChecker,
});
yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
info("started");
let {TabManager, WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
let winId1 = WindowManager.getId(win1);
let winId2 = WindowManager.getId(win2);
function* openTab(winId) {
extension.sendMessage("background-open-tab", winId);
yield extension.awaitMessage("tab-ready");
}
function* checkViews(kind, tabCount, popupCount) {
extension.sendMessage(kind + "-check-views");
let counts = yield extension.awaitMessage("counts");
is(counts.background, 1, "background count correct");
is(counts.tab, tabCount, "tab count correct");
is(counts.popup, popupCount, "popup count correct");
}
yield checkViews("background", 0, 0);
yield openTab(winId1);
yield checkViews("background", 1, 0);
yield checkViews("tab", 1, 0);
yield openTab(winId2);
yield checkViews("background", 2, 0);
function* triggerPopup(win, callback) {
let widgetId = makeWidgetId(extension.id) + "-browser-action";
let node = CustomizableUI.getWidget(widgetId).forWindow(win).node;
let evt = new CustomEvent("command", {
bubbles: true,
cancelable: true
});
node.dispatchEvent(evt);
yield extension.awaitMessage("popup-ready");
yield callback();
let panel = node.querySelector("panel");
if (panel) {
panel.hidePopup();
}
}
yield triggerPopup(win1, function*() {
yield checkViews("background", 2, 1);
yield checkViews("popup", 2, 1);
});
yield triggerPopup(win2, function*() {
yield checkViews("background", 2, 1);
yield checkViews("popup", 2, 1);
});
info("checking counts after popups");
yield checkViews("background", 2, 0);
info("closing one tab");
extension.sendMessage("background-close-tab", winId1);
yield extension.awaitMessage("closed");
info("one tab closed, one remains");
yield checkViews("background", 1, 0);
info("opening win1 popup");
yield triggerPopup(win1, function*() {
yield checkViews("background", 1, 1);
yield checkViews("tab", 1, 1);
yield checkViews("popup", 1, 1);
});
info("opening win2 popup");
yield triggerPopup(win2, function*() {
yield checkViews("background", 1, 1);
yield checkViews("tab", 1, 1);
yield checkViews("popup", 1, 1);
});
yield extension.unload();
yield BrowserTestUtils.closeWindow(win1);
yield BrowserTestUtils.closeWindow(win2);
});

View File

@@ -1,7 +0,0 @@
let {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm");
function makeWidgetId(id)
{
id = id.toLowerCase();
return id.replace(/[^a-z0-9_-]/g, "_");
}

View File

@@ -2189,7 +2189,7 @@ ContentParent::StartForceKillTimer()
return;
}
int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 0);
if (timeoutSecs > 0) {
mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
MOZ_ASSERT(mForceKillTimer);
@@ -3471,14 +3471,6 @@ ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParen
/* static */ void
ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
{
#ifdef ENABLE_TESTS
// We don't want to time out the content process during XPCShell tests. This
// is the easiest way to ensure that.
if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
return;
}
#endif
auto self = static_cast<ContentParent*>(aClosure);
self->KillHard("ShutDownKill");
}

View File

@@ -573,7 +573,7 @@ SpecialPowersObserverAPI.prototype = {
resourceURI: uri
});
} else {
extension = Extension.generate(id, ext);
extension = Extension.generate(ext);
}
let resultListener = (...args) => {

View File

@@ -2060,8 +2060,6 @@ SpecialPowersAPI.prototype = {
let sp = this;
let extension = {
id,
startup() {
sp._sendAsyncMessage("SPStartupExtension", {id});
return startupPromise;
@@ -2099,10 +2097,6 @@ SpecialPowersAPI.prototype = {
this._addMessageListener("SPExtensionMessage", listener);
return extension;
},
invalidateExtensionStorageCache: function() {
this.notifyObserversInParentProcess(null, "extension-invalidate-storage-cache", "");
},
};
this.SpecialPowersAPI = SpecialPowersAPI;

View File

@@ -278,9 +278,7 @@ var GlobalManager = {
if (this.docShells.has(docShell)) {
let {extension, context} = this.docShells.get(docShell);
if (context) {
inject(extension, context);
}
inject(extension, context);
return;
}
@@ -369,7 +367,7 @@ this.Extension = function(addonData)
* To make things easier, the value of "background" and "files"[] can
* be a function, which is converted to source that is run.
*/
this.Extension.generate = function(id, data)
this.Extension.generate = function(data)
{
let manifest = data.manifest;
if (!manifest) {
@@ -394,7 +392,9 @@ this.Extension.generate = function(id, data)
}
}
provide(manifest, ["applications", "gecko", "id"], id);
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
let uuid = uuidGenerator.generateUUID().number;
provide(manifest, ["applications", "gecko", "id"], uuid);
provide(manifest, ["name"], "Generated extension");
provide(manifest, ["manifest_version"], 2);
@@ -458,7 +458,7 @@ this.Extension.generate = function(id, data)
let jarURI = Services.io.newURI("jar:" + fileURI.spec + "!/", null, null);
return new Extension({
id,
id: uuid,
resourceURI: jarURI,
cleanupFile: file
});
@@ -638,20 +638,6 @@ Extension.prototype = {
return {};
},
broadcast(msg, data) {
return new Promise(resolve => {
let count = Services.ppmm.childCount;
Services.ppmm.addMessageListener(msg + "Complete", function listener() {
count--;
if (count == 0) {
Services.ppmm.removeMessageListener(msg + "Complete", listener);
resolve();
}
});
Services.ppmm.broadcastAsyncMessage(msg, data);
});
},
runManifest(manifest) {
let permissions = manifest.permissions || [];
let webAccessibleResources = manifest.web_accessible_resources || [];
@@ -682,8 +668,7 @@ Extension.prototype = {
}
let serial = this.serialize();
data["Extension:Extensions"].push(serial);
return this.broadcast("Extension:Startup", serial);
Services.ppmm.broadcastAsyncMessage("Extension:Startup", serial);
},
callOnClose(obj) {
@@ -713,7 +698,7 @@ Extension.prototype = {
Management.emit("startup", this);
return this.runManifest(manifest);
this.runManifest(manifest);
}).catch(e => {
dump(`Extension error: ${e} ${e.fileName}:${e.lineNumber}\n`);
Cu.reportError(e);
@@ -731,13 +716,19 @@ Extension.prototype = {
Services.obs.removeObserver(this, "xpcom-shutdown");
this.broadcast("Extension:FlushJarCache", {path: file.path}).then(() => {
// We can't delete this file until everyone using it has
// closed it (because Windows is dumb). So we wait for all the
// child processes (including the parent) to flush their JAR
// caches. These caches may keep the file open.
file.remove(false);
let count = Services.ppmm.childCount;
Services.ppmm.addMessageListener("Extension:FlushJarCacheComplete", function listener() {
count--;
if (count == 0) {
// We can't delete this file until everyone using it has
// closed it (because Windows is dumb). So we wait for all the
// child processes (including the parent) to flush their JAR
// caches. These caches may keep the file open.
file.remove(false);
}
});
Services.ppmm.broadcastAsyncMessage("Extension:FlushJarCache", {path: file.path});
},
shutdown() {

View File

@@ -30,7 +30,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
runSafeSyncWithoutClone,
runSafeWithoutClone,
MessageBroker,
Messenger,
ignoreEvent,
@@ -51,10 +51,6 @@ function isWhenBeforeOrSame(when1, when2)
var api = context => { return {
runtime: {
connect: function(extensionId, connectInfo) {
if (!connectInfo) {
connectInfo = extensionId;
extensionId = null;
}
let name = connectInfo && connectInfo.name || "";
let recipient = extensionId ? {extensionId} : {extensionId: context.extensionId};
return context.messenger.connect(context.messageManager, name, recipient);
@@ -64,7 +60,7 @@ var api = context => { return {
return context.extension.getManifest();
},
getURL: function(url) {
getURL: function(path) {
return context.extension.baseURI.resolve(url);
},
@@ -88,7 +84,7 @@ var api = context => { return {
},
extension: {
getURL: function(url) {
getURL: function(path) {
return context.extension.baseURI.resolve(url);
},
@@ -141,12 +137,12 @@ Script.prototype = {
for (let url of this.css) {
url = extension.baseURI.resolve(url);
runSafeSyncWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
}
if (this.options.cssCode) {
let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode);
runSafeSyncWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
}
}
@@ -166,7 +162,7 @@ Script.prototype = {
charset: "UTF-8",
async: AppConstants.platform == "gonk"
}
runSafeSyncWithoutClone(Services.scriptloader.loadSubScriptWithOptions, url, options);
Services.scriptloader.loadSubScriptWithOptions(url, options);
}
if (this.options.jsCode) {
@@ -198,8 +194,6 @@ function ExtensionContext(extensionId, contentWindow)
this.extensionId = extensionId;
this.contentWindow = contentWindow;
this.onClose = new Set();
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindowId = utils.outerWindowID;
@@ -234,6 +228,8 @@ function ExtensionContext(extensionId, contentWindow)
// reason. However, we waive here anyway in case that changes.
Cu.waiveXrays(this.sandbox).chrome = Cu.waiveXrays(this.sandbox).browser;
injectAPI(api(this), chromeObj);
this.onClose = new Set();
}
ExtensionContext.prototype = {
@@ -487,7 +483,6 @@ var ExtensionManager = {
extension = new BrowserExtensionContent(data);
this.extensions.set(data.id, extension);
DocumentManager.startupExtension(data.id);
Services.cpmm.sendAsyncMessage("Extension:StartupComplete");
break;
}

View File

@@ -95,13 +95,7 @@ this.ExtensionStorage = {
remove(extensionId, items) {
return this.read(extensionId).then(extData => {
let changes = {};
if (Array.isArray(items)) {
for (let prop of items) {
changes[prop] = {oldValue: extData[prop]};
delete extData[prop];
}
} else {
let prop = items;
for (let prop in items) {
changes[prop] = {oldValue: extData[prop]};
delete extData[prop];
}
@@ -122,7 +116,7 @@ this.ExtensionStorage = {
let result = {};
if (keys === null) {
Object.assign(result, extData);
} else if (typeof(keys) == "object" && !Array.isArray(keys)) {
} else if (typeof(keys) == "object") {
for (let prop in keys) {
if (prop in extData) {
result[prop] = extData[prop];
@@ -131,15 +125,10 @@ this.ExtensionStorage = {
}
}
} else if (typeof(keys) == "string") {
let prop = keys;
if (prop in extData) {
result[prop] = extData[prop];
}
result[prop] = extData[prop] || undefined;
} else {
for (let prop of keys) {
if (prop in extData) {
result[prop] = extData[prop];
}
result[prop] = extData[prop] || undefined;
}
}
@@ -157,20 +146,4 @@ this.ExtensionStorage = {
let listeners = this.listeners.get(extensionId);
listeners.delete(listener);
},
init() {
Services.obs.addObserver(this, "extension-invalidate-storage-cache", false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {
Services.obs.removeObserver(this, "extension-invalidate-storage-cache");
Services.obs.removeObserver(this, "xpcom-shutdown");
} else if (topic == "extension-invalidate-storage-cache") {
this.cache.clear();
}
},
};
ExtensionStorage.init();

View File

@@ -15,41 +15,16 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// Run a function and report exceptions.
function runSafeSyncWithoutClone(f, ...args)
function runSafeWithoutClone(f, ...args)
{
try {
return f(...args);
} catch (e) {
dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n[[Exception stack\n${e.stack}Current stack\n${Error().stack}]]\n`);
dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n${e.stack}\n${Error().stack}`);
Cu.reportError(e);
}
}
// Run a function and report exceptions.
function runSafeWithoutClone(f, ...args)
{
if (typeof(f) != "function") {
dump(`Extension error: expected function\n${Error().stack}`);
return;
}
Services.tm.currentThread.dispatch(function() {
runSafeSyncWithoutClone(f, ...args);
}, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
// Run a function, cloning arguments into context.cloneScope, and
// report exceptions. |f| is expected to be in context.cloneScope.
function runSafeSync(context, f, ...args)
{
try {
args = Cu.cloneInto(args, context.cloneScope);
} catch (e) {
dump(`runSafe failure\n${context.cloneScope}\n${Error().stack}`);
}
return runSafeSyncWithoutClone(f, ...args);
}
// Run a function, cloning arguments into context.cloneScope, and
// report exceptions. |f| is expected to be in context.cloneScope.
function runSafe(context, f, ...args)
@@ -121,11 +96,6 @@ function EventManager(context, name, register)
EventManager.prototype = {
addListener(callback) {
if (typeof(callback) != "function") {
dump(`Expected function\n${Error().stack}`);
return;
}
if (!this.registered) {
this.context.callOnClose(this);
@@ -162,7 +132,7 @@ EventManager.prototype = {
fireWithoutClone(...args) {
for (let callback of this.callbacks) {
runSafeSyncWithoutClone(callback, ...args);
runSafeWithoutClone(callback, ...args);
}
},
@@ -370,9 +340,6 @@ function Port(context, messageManager, name, id, sender)
this.disconnectName = `Extension:Disconnect-${this.id}`;
this.sender = sender;
this.disconnected = false;
this.messageManager.addMessageListener(this.disconnectName, this, true);
this.disconnectListeners = new Set();
}
Port.prototype = {
@@ -400,9 +367,9 @@ Port.prototype = {
}
};
this.disconnectListeners.add(listener);
this.messageManager.addMessageListener(this.disconnectName, listener, true);
return () => {
this.disconnectListeners.delete(listener);
this.messageManager.removeMessageListener(this.disconnectName, listener);
};
}).api(),
onMessage: new EventManager(this.context, "Port.onMessage", fire => {
@@ -427,31 +394,9 @@ Port.prototype = {
return portObj;
},
handleDisconnection() {
this.messageManager.removeMessageListener(this.disconnectName, this);
this.context.forgetOnClose(this);
this.disconnected = true;
},
receiveMessage(msg) {
if (msg.name == this.disconnectName) {
if (this.disconnected) {
return;
}
for (let listener of this.disconnectListeners) {
listener();
}
this.handleDisconnection();
}
},
disconnect() {
if (this.disconnected) {
throw "Attempt to disconnect() a disconnected port";
}
this.handleDisconnection();
this.context.forgetOnClose(this);
this.disconnect = true;
this.messageManager.sendAsyncMessage(this.disconnectName);
},
@@ -517,7 +462,7 @@ Messenger.prototype = {
},
onMessage(name) {
return new SingletonEventManager(this.context, name, callback => {
return new EventManager(this.context, name, fire => {
let listener = (type, target, message, sender, recipient) => {
message = Cu.cloneInto(message, this.context.cloneScope);
if (this.delegate) {
@@ -538,12 +483,12 @@ Messenger.prototype = {
};
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
let result = runSafeSyncWithoutClone(callback, message, sender, sendResponse);
let result = fire.withoutClone(message, sender, sendResponse);
if (result !== true) {
valid = false;
if (!sent) {
mm.sendAsyncMessage(replyName, {gotData: false});
}
}
if (!sent) {
mm.sendAsyncMessage(replyName, {gotData: false});
}
};
@@ -589,9 +534,7 @@ function flushJarCache(jarFile)
this.ExtensionUtils = {
runSafeWithoutClone,
runSafeSyncWithoutClone,
runSafe,
runSafeSync,
DefaultWeakMap,
EventManager,
SingletonEventManager,

View File

@@ -9,7 +9,7 @@ var {
// WeakMap[Extension -> Set[Alarm]]
var alarmsMap = new WeakMap();
// WeakMap[Extension -> Set[callback]]
// WeakMap[Extension -> callback]
var alarmCallbacksMap = new WeakMap();
// Manages an alarm created by the extension (alarms API).
@@ -46,8 +46,8 @@ Alarm.prototype = {
},
observe(subject, topic, data) {
for (let callback in alarmCallbacksMap.get(this.extension)) {
callback(this);
if (alarmCallbacksMap.has(this.extension)) {
alarmCallbacksMap.get(this.extension)(this);
}
if (this.canceled) {
return;
@@ -74,7 +74,6 @@ Alarm.prototype = {
extensions.on("startup", (type, extension) => {
alarmsMap.set(extension, new Set());
alarmCallbacksMap.set(extension, new Set());
});
extensions.on("shutdown", (type, extension) => {
@@ -82,7 +81,6 @@ extensions.on("shutdown", (type, extension) => {
alarm.clear();
}
alarmsMap.delete(extension);
alarmCallbacksMap.delete(extension);
});
extensions.registerAPI((extension, context) => {
@@ -162,9 +160,9 @@ extensions.registerAPI((extension, context) => {
fire(alarm.data);
};
alarmCallbacksMap.get(extension).add(callback);
alarmCallbacksMap.set(extension, callback);
return () => {
alarmCallbacksMap.get(extension).delete(callback);
alarmCallbacksMap.delete(extension);
};
}).api(),
},

View File

@@ -4,28 +4,6 @@ extensions.registerAPI((extension, context) => {
getURL: function(url) {
return extension.baseURI.resolve(url);
},
getViews: function(fetchProperties) {
let result = Cu.cloneInto([], context.cloneScope);
for (let view of extension.views) {
if (fetchProperties && "type" in fetchProperties) {
if (view.type != fetchProperties.type) {
continue;
}
}
if (fetchProperties && "windowId" in fetchProperties) {
if (view.windowId != fetchProperties.windowId) {
continue;
}
}
result.push(view.contentWindow);
}
return result;
},
},
};
});

View File

@@ -9,7 +9,7 @@ var {
// WeakMap[Extension -> Set[Notification]]
var notificationsMap = new WeakMap();
// WeakMap[Extension -> Set[callback]]
// WeakMap[Extension -> callback]
var notificationCallbacksMap = new WeakMap();
// Manages a notification popup (notifications API) created by the extension.
@@ -54,8 +54,8 @@ Notification.prototype = {
return;
}
for (let callback in notificationCallbacksMap.get(this.extension)) {
callback(this);
if (notificationCallbacksMap.has(this.extension)) {
notificationCallbackMap.get(this.extension)(this);
}
notificationsMap.get(this.extension).delete(this);
@@ -64,7 +64,6 @@ Notification.prototype = {
extensions.on("startup", (type, extension) => {
notificationsMap.set(extension, new Set());
notificationCallbacksMap.set(extension, new Set());
});
extensions.on("shutdown", (type, extension) => {
@@ -72,7 +71,6 @@ extensions.on("shutdown", (type, extension) => {
notification.clear();
}
notificationsMap.delete(extension);
notificationCallbacksMap.delete(extension);
});
var nextId = 0;
@@ -130,9 +128,9 @@ extensions.registerPrivilegedAPI("notifications", (extension, context) => {
fire(notification.id, true);
};
notificationCallbacksMap.get(extension).add(listener);
notificationCallbackMap.set(extension, listener);
return () => {
notificationCallbacksMap.get(extension).delete(listener);
notificationCallbackMap.delete(extension);
};
}).api(),

View File

@@ -3,21 +3,12 @@ var {
EventManager,
} = ExtensionUtils;
// WeakMap[Extension -> Set(callback)]
var messageHandlers = new WeakMap();
extensions.on("startup", (type, extension) => {
messageHandlers.set(extension, new Set());
});
extensions.on("shutdown", (type, extension) => {
messageHandlers.delete(extension);
});
extensions.on("test-message", (type, extension, ...args) => {
let handlers = messageHandlers.get(extension);
for (let handler of handlers) {
handler(...args);
let fire = messageHandlers.get(extension);
if (fire) {
fire(...args);
}
});
@@ -61,11 +52,9 @@ extensions.registerAPI((extension, context) => {
},
onMessage: new EventManager(context, "test.onMessage", fire => {
let handlers = messageHandlers.get(extension);
handlers.add(fire);
messageHandlers.set(extension, fire);
return () => {
handlers.delete(fire);
messageHandlers.delete(extension);
};
}).api(),
},

View File

@@ -45,7 +45,7 @@ function WebNavigationEventManager(context, eventName)
return;
}
runSafe(context, callback, data2);
return runSafe(context, callback, data2);
};
WebNavigation[eventName].addListener(listener);

View File

@@ -10,7 +10,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
SingletonEventManager,
runSafeSync,
runSafe,
} = ExtensionUtils;
// EventManager-like class specifically for WebRequest. Inherits from
@@ -53,7 +53,7 @@ function WebRequestEventManager(context, eventName)
}
}
return runSafeSync(context, callback, data2);
return runSafe(context, callback, data2);
};
let filter2 = {};

View File

@@ -6,22 +6,22 @@
"content_scripts": [
{
"matches": ["http://mochi.test/*/file_sample.html"],
"matches": ["http://mochi.test/tests/toolkit/components/extensions/test/mochitest/file_contentscript_*.html"],
"js": ["content_script_start.js"],
"run_at": "document_start"
},
{
"matches": ["http://mochi.test/*/file_sample.html"],
"matches": ["http://mochi.test/tests/toolkit/components/extensions/test/mochitest/file_contentscript_*.html"],
"js": ["content_script_end.js"],
"run_at": "document_end"
},
{
"matches": ["http://mochi.test/*/file_sample.html"],
"matches": ["http://mochi.test/tests/toolkit/components/extensions/test/mochitest/file_contentscript_*.html"],
"js": ["content_script_idle.js"],
"run_at": "document_idle"
},
{
"matches": ["http://mochi.test/*/file_sample.html"],
"matches": ["http://mochi.test/tests/toolkit/components/extensions/test/mochitest/file_contentscript_*.html"],
"js": ["content_script.js"],
"run_at": "document_idle"
}

View File

@@ -1,8 +0,0 @@
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function listener() {
win.removeEventListener("load", listener, true);
resolve();
}, true);
});
}

View File

@@ -1,7 +1,6 @@
[DEFAULT]
skip-if = os == 'android' || buildapp == 'b2g' || os == 'mac'
support-files =
head.js
file_WebRequest_page1.html
file_WebRequest_page2.html
file_image_good.png
@@ -14,16 +13,10 @@ support-files =
file_script_bad.js
file_script_redirect.js
file_script_xhr.js
file_sample.html
file_contentscript_page1.html
[test_ext_simple.html]
[test_ext_geturl.html]
[test_ext_contentscript.html]
[test_ext_webrequest.html]
[test_ext_generate.html]
[test_ext_runtime_connect.html]
[test_ext_runtime_disconnect.html]
[test_ext_sandbox_var.html]
[test_ext_sendmessage_reply.html]
[test_ext_sendmessage_doublereply.html]
[test_ext_storage.html]
[test_simple_extensions.html]
[test_extension_contentscript.html]
[test_extension_webrequest.html]
[test_generate_extension.html]
[test_sandbox_var.html]

View File

@@ -1,72 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
function backgroundScript() {
browser.runtime.onMessage.addListener(([url1, url2]) => {
var url3 = browser.runtime.getURL("test_file.html");
var url4 = browser.extension.getURL("test_file.html");
browser.test.assertTrue(url1 !== undefined, "url1 defined");
browser.test.assertTrue(url1.startsWith("moz-extension://"), "url1 has correct scheme");
browser.test.assertTrue(url1.endsWith("test_file.html"), "url1 has correct leaf name");
browser.test.assertEq(url1, url2, "url2 matches");
browser.test.assertEq(url1, url3, "url3 matches");
browser.test.assertEq(url1, url4, "url4 matches");
browser.test.notifyPass("geturl");
});
}
function contentScript() {
var url1 = browser.runtime.getURL("test_file.html");
var url2 = browser.extension.getURL("test_file.html");
browser.runtime.sendMessage([url1, url2]);
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("geturl")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -1,83 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
browser.runtime.onConnect.addListener(port => {
browser.test.assertEq(port.name, "ernie", "port name correct");
browser.test.assertTrue(port.sender.url.endsWith("file_sample.html"), "URL correct");
browser.test.assertTrue(port.sender.tab.url.endsWith("file_sample.html"), "tab URL correct");
var expected = "message 1";
port.onMessage.addListener(msg => {
browser.test.assertEq(msg, expected, "message is expected");
if (expected == "message 1") {
port.postMessage("message 2");
expected = "message 3";
} else if (expected == "message 3") {
expected = "disconnect";
browser.test.notifyPass("runtime.connect");
}
});
port.onDisconnect.addListener(() => {
browser.test.assertEq(expected, "disconnect", "got disconnection at right time");
});
});
}
function contentScript() {
var port = browser.runtime.connect({name: "ernie"});
port.postMessage("message 1");
port.onMessage.addListener(msg => {
if (msg == "message 2") {
port.postMessage("message 3");
port.disconnect();
}
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("runtime.connect")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -1,75 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
browser.runtime.onConnect.addListener(port => {
browser.test.assertEq(port.name, "ernie", "port name correct");
port.onDisconnect.addListener(() => {
browser.test.sendMessage("disconnected");
});
browser.test.sendMessage("connected");
});
}
function contentScript() {
browser.runtime.connect({name: "ernie"});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitMessage("connected")]);
win.close();
yield extension.awaitMessage("disconnected");
info("win.close() succeeded");
win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitMessage("connected")]);
// Add an "unload" listener so that we don't put the window in the
// bfcache. This way it gets destroyed immediately upon navigation.
win.addEventListener("unload", function() {});
win.location = "http://example.com";
yield extension.awaitMessage("disconnected");
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -1,102 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
// Add two listeners that both send replies. We're supposed to ignore all but one
// of them. Which one is chosen is non-deterministic.
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "getreply") {
sendReply("reply1");
}
});
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "getreply") {
sendReply("reply2");
}
});
function sleep(callback, n = 10) {
if (n == 0) {
callback();
} else {
setTimeout(function() { sleep(callback, n - 1); }, 0);
}
}
var done_count = 0;
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "done") {
done_count++;
browser.test.assertEq(done_count, 1, "got exactly one reply");
// Go through the event loop a few times to make sure we don't get multiple replies.
sleep(function() {
browser.test.notifyPass("sendmessage_doublereply");
});
}
});
}
function contentScript() {
browser.runtime.sendMessage("getreply", function(resp) {
if (resp != "reply1" && resp != "reply2") {
return; // test failed
}
browser.runtime.sendMessage("done");
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("sendmessage_doublereply")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -1,80 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == 0) {
sendReply("reply1");
} else if (msg == 1) {
window.setTimeout(function() {
sendReply("reply2");
}, 0);
return true;
} else if (msg == 2) {
browser.test.notifyPass("sendmessage_reply");
}
});
}
function contentScript() {
browser.runtime.sendMessage(0, function(resp1) {
if (resp1 != "reply1") {
return; // test failed
}
browser.runtime.sendMessage(1, function(resp2) {
if (resp2 != "reply2") {
return; // test failed
}
browser.runtime.sendMessage(2);
});
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("sendmessage_reply")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -1,172 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
function backgroundScript() {
function set(items) {
return new Promise(resolve => {
browser.storage.local.set(items, resolve);
});
}
function get(items) {
return new Promise(resolve => {
browser.storage.local.get(items, resolve);
});
}
function remove(items) {
return new Promise(resolve => {
browser.storage.local.remove(items, resolve);
});
}
function check(prop, value) {
return get(null).then(data => {
browser.test.assertEq(data[prop], value, "null getter worked for " + prop);
return get(prop);
}).then(data => {
browser.test.assertEq(data[prop], value, "string getter worked for " + prop);
return get([prop]);
}).then(data => {
browser.test.assertEq(data[prop], value, "array getter worked for " + prop);
return get({[prop]: undefined});
}).then(data => {
browser.test.assertEq(data[prop], value, "object getter worked for " + prop);
});
}
var globalChanges = {};
browser.storage.onChanged.addListener((changes, storage) => {
browser.test.assertEq(storage, "local", "storage is local");
Object.assign(globalChanges, changes);
});
function checkChanges(changes) {
function checkSub(obj1, obj2) {
for (var prop in obj1) {
browser.test.assertEq(obj1[prop].oldValue, obj2[prop].oldValue);
browser.test.assertEq(obj1[prop].newValue, obj2[prop].newValue);
}
}
checkSub(changes, globalChanges);
checkSub(globalChanges, changes);
globalChanges = {};
}
// Set some data and then test getters.
set({"test-prop1": "value1", "test-prop2": "value2"}).then(() => {
checkChanges({"test-prop1": {newValue: "value1"}, "test-prop2": {newValue: "value2"}});
return check("test-prop1", "value1");
}).then(() => {
return check("test-prop2", "value2");
}).then(() => {
return get({"test-prop1": undefined, "test-prop2": undefined, "other": "default"});
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
browser.test.assertEq(data["other"], "default", "other correct");
return get(["test-prop1", "test-prop2", "other"]);
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
browser.test.assertFalse("other" in data, "other correct");
// Remove data in various ways.
}).then(() => {
return remove("test-prop1");
}).then(() => {
checkChanges({"test-prop1": {oldValue: "value1"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertFalse("test-prop1" in data, "prop1 absent");
browser.test.assertTrue("test-prop2" in data, "prop2 present");
return set({"test-prop1": "value1"});
}).then(() => {
checkChanges({"test-prop1": {newValue: "value1"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertEq(data["test-prop1"], "value1", "prop1 correct");
browser.test.assertEq(data["test-prop2"], "value2", "prop2 correct");
}).then(() => {
return remove(["test-prop1", "test-prop2"]);
}).then(() => {
checkChanges({"test-prop1": {oldValue: "value1"}, "test-prop2": {oldValue: "value2"}});
return get(["test-prop1", "test-prop2"]);
}).then(data => {
browser.test.assertFalse("test-prop1" in data, "prop1 absent");
browser.test.assertFalse("test-prop2" in data, "prop2 absent");
// Test cache invalidation.
}).then(() => {
return set({"test-prop1": "value1", "test-prop2": "value2"});
}).then(() => {
globalChanges = {};
browser.test.sendMessage("invalidate");
return new Promise(resolve => browser.test.onMessage.addListener(resolve));
}).then(() => {
return check("test-prop1", "value1");
}).then(() => {
return check("test-prop2", "value2");
// Make sure we can store complex JSON data.
}).then(() => {
return set({"test-prop1": {str: "hello", bool: true, undef: undefined, obj: {}, arr: [1, 2]}});
}).then(() => {
browser.test.assertEq(globalChanges["test-prop1"].oldValue, "value1", "oldValue correct");
browser.test.assertEq(typeof(globalChanges["test-prop1"].newValue), "object", "newValue is obj");
globalChanges = {};
return get({"test-prop1": undefined});
}).then(data => {
var obj = data["test-prop1"];
browser.test.assertEq(obj.str, "hello", "string part correct");
browser.test.assertEq(obj.bool, true, "bool part correct");
browser.test.assertEq(obj.undef, undefined, "undefined part correct");
browser.test.assertEq(typeof(obj.obj), "object", "object part correct");
browser.test.assertTrue(Array.isArray(obj.arr), "array part present");
browser.test.assertEq(obj.arr[0], 1, "arr[0] part correct");
browser.test.assertEq(obj.arr[1], 2, "arr[1] part correct");
browser.test.assertEq(obj.arr.length, 2, "arr.length part correct");
}).then(() => {
browser.test.notifyPass("storage");
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
permissions: ["storage"]
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
yield extension.awaitMessage("invalidate");
SpecialPowers.invalidateExtensionStorageCache();
extension.sendMessage("invalidated");
yield extension.awaitFinish("storage");
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@@ -5,7 +5,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@@ -31,7 +30,13 @@ add_task(function* test_contentscript()
let chromeNamespacePromise = extension.awaitMessage("chrome-namespace-ok");
let win = window.open("file_sample.html");
yield new Promise(resolve => { setTimeout(resolve, 0); });
let win = window.open();
win.location = "file_contentscript_page1.html";
ok(true, "page loaded");
yield Promise.all([waitForLoad(win), completePromise, chromeNamespacePromise]);
info("test page loaded");
@@ -45,6 +50,15 @@ add_task(function* test_contentscript()
yield extension.unload();
info("extension unloaded");
});
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function listener() {
win.removeEventListener("load", listener, true);
resolve();
}, true);
});
}
</script>
</body>

View File

@@ -5,7 +5,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@@ -120,6 +119,15 @@ function* test_once()
// Run the test twice to make sure it works with caching.
add_task(test_once);
add_task(test_once);
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function listener() {
win.removeEventListener("load", listener, true);
resolve();
}, true);
});
}
</script>
</body>

View File

@@ -5,7 +5,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>

View File

@@ -5,7 +5,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
@@ -30,7 +29,7 @@ let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"matches": ["http://mochi.test/tests/toolkit/components/extensions/test/mochitest/file_contentscript_*.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
@@ -46,7 +45,11 @@ add_task(function* test_contentscript() {
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield new Promise(resolve => { setTimeout(resolve, 0); });
let win = window.open();
win.location = "file_contentscript_page1.html";
yield Promise.all([waitForLoad(win), extension.awaitFinish()]);
@@ -55,6 +58,15 @@ add_task(function* test_contentscript() {
yield extension.unload();
info("extension unloaded");
});
function waitForLoad(win) {
return new Promise(resolve => {
win.addEventListener("load", function listener() {
win.removeEventListener("load", listener, true);
resolve();
}, true);
});
}
</script>
</body>

View File

@@ -5,7 +5,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>