From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser/browser_ext_browserAction_context.js | 1194 ++++++++++++++++++++ 1 file changed, 1194 insertions(+) create mode 100644 browser/components/extensions/test/browser/browser_ext_browserAction_context.js (limited to 'browser/components/extensions/test/browser/browser_ext_browserAction_context.js') diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js new file mode 100644 index 0000000000..c1f8184c78 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js @@ -0,0 +1,1194 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +async function runTests(options) { + async function background(getTests) { + let manifest = browser.runtime.getManifest(); + let { manifest_version } = manifest; + const action = manifest_version < 3 ? "browserAction" : "action"; + async function checkExtAPIDetails(expecting, details) { + let title = await browser[action].getTitle(details); + browser.test.assertEq( + expecting.title, + title, + "expected value from getTitle" + ); + + let popup = await browser[action].getPopup(details); + browser.test.assertEq( + expecting.popup, + popup, + "expected value from getPopup" + ); + + let badge = await browser[action].getBadgeText(details); + browser.test.assertEq( + expecting.badge, + badge, + "expected value from getBadge" + ); + + let badgeBackgroundColor = await browser[action].getBadgeBackgroundColor( + details + ); + browser.test.assertEq( + String(expecting.badgeBackgroundColor), + String(badgeBackgroundColor), + "expected value from getBadgeBackgroundColor" + ); + + let badgeTextColor = await browser[action].getBadgeTextColor(details); + browser.test.assertEq( + String(expecting.badgeTextColor), + String(badgeTextColor), + "expected value from getBadgeTextColor" + ); + + let enabled = await browser[action].isEnabled(details); + browser.test.assertEq( + expecting.enabled, + enabled, + "expected value from isEnabled" + ); + } + + let tabs = []; + let windows = []; + let tests = getTests(tabs, windows); + + { + let tabId = 0xdeadbeef; + let calls = [ + () => browser[action].enable(tabId), + () => browser[action].disable(tabId), + () => browser[action].setTitle({ tabId, title: "foo" }), + () => browser[action].setIcon({ tabId, path: "foo.png" }), + () => browser[action].setPopup({ tabId, popup: "foo.html" }), + () => browser[action].setBadgeText({ tabId, text: "foo" }), + () => + browser[action].setBadgeBackgroundColor({ + tabId, + color: [0xff, 0, 0, 0xff], + }), + () => + browser[action].setBadgeTextColor({ + tabId, + color: [0, 0xff, 0xff, 0xff], + }), + ]; + + for (let call of calls) { + await browser.test.assertRejects( + new Promise(resolve => resolve(call())), + RegExp(`Invalid tab ID: ${tabId}`), + "Expected invalid tab ID error" + ); + } + } + + // Runs the next test in the `tests` array, checks the results, + // and passes control back to the outer test scope. + function nextTest() { + let test = tests.shift(); + + test(async (expectTab, expectWindow, expectGlobal, expectDefault) => { + expectGlobal = { ...expectDefault, ...expectGlobal }; + expectWindow = { ...expectGlobal, ...expectWindow }; + expectTab = { ...expectWindow, ...expectTab }; + + // Check that the API returns the expected values, and then + // run the next test. + let [{ windowId, id: tabId }] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + await checkExtAPIDetails(expectTab, { tabId }); + await checkExtAPIDetails(expectWindow, { windowId }); + await checkExtAPIDetails(expectGlobal, {}); + + // Check that the actual icon has the expected values, then + // run the next test. + browser.test.sendMessage("nextTest", expectTab, windowId, tests.length); + }); + } + + browser.test.onMessage.addListener(msg => { + if (msg != "runNextTest") { + browser.test.fail("Expecting 'runNextTest' message"); + } + + nextTest(); + }); + + let [{ id, windowId }] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + tabs.push(id); + windows.push(windowId); + nextTest(); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: options.manifest, + + files: options.files || {}, + + background: `(${background})(${options.getTests})`, + }); + + function serializeColor([r, g, b, a]) { + if (a === 255) { + return `rgb(${r}, ${g}, ${b})`; + } + return `rgba(${r}, ${g}, ${b}, ${a / 255})`; + } + + let browserActionId; + async function checkWidgetDetails(details, windowId) { + let { document } = Services.wm.getOuterWindowWithId(windowId); + if (!browserActionId) { + browserActionId = `${makeWidgetId(extension.id)}-browser-action`; + } + + let node = document.getElementById(browserActionId); + let button = node.firstElementChild; + + ok(button, "button exists"); + + let title = details.title || options.manifest.name; + + // NOTE: resorting to waitForCondition to prevent frequent + // intermittent failures due to multiple action API calls + // being queued. + if (getListStyleImage(button) !== details.icon) { + info(`wait for action icon url to be set to ${details.icon}`); + await TestUtils.waitForCondition( + () => getListStyleImage(button) === details.icon, + "Wait for the expected icon URL to be set" + ); + } + + // NOTE: resorting to waitForCondition to prevent frequent + // intermittent failures due to multiple action API calls + // being queued. + if (button.getAttribute("tooltiptext") !== title) { + info(`wait for action tooltiptext to be set to ${title}`); + await TestUtils.waitForCondition( + () => button.getAttribute("tooltiptext") === title, + "Wait for expected title to be set" + ); + } + + is(getListStyleImage(button), details.icon, "icon URL is correct"); + is(button.getAttribute("tooltiptext"), title, "image title is correct"); + is(button.getAttribute("label"), title, "image label is correct"); + is(button.getAttribute("badge"), details.badge, "badge text is correct"); + is( + button.getAttribute("disabled") == "true", + !details.enabled, + "disabled state is correct" + ); + + if (details.badge) { + let badge = button.badgeLabel; + let style = window.getComputedStyle(badge); + let expected = { + backgroundColor: serializeColor(details.badgeBackgroundColor), + color: serializeColor(details.badgeTextColor), + }; + for (let [prop, value] of Object.entries(expected)) { + // NOTE: resorting to waitForCondition to prevent frequent + // intermittent failures due to multiple action API calls + // being queued. + if (style[prop] !== value) { + info(`wait for badge ${prop} to be set to ${value}`); + await TestUtils.waitForCondition( + () => window.getComputedStyle(badge)[prop] === value, + `Wait for expected badge ${prop} to be set` + ); + } + } + } + + // TODO: Popup URL. + } + + let awaitFinish = new Promise(resolve => { + extension.onMessage( + "nextTest", + async (expecting, windowId, testsRemaining) => { + await promiseAnimationFrame(); + await checkWidgetDetails(expecting, windowId); + + if (testsRemaining) { + extension.sendMessage("runNextTest"); + } else { + resolve(); + } + } + ); + }); + + await extension.startup(); + + await awaitFinish; + + await extension.unload(); +} + +let tabSwitchTestData = { + files: { + "_locales/en/messages.json": { + popup: { + message: "default.html", + description: "Popup", + }, + + title: { + message: "Title", + description: "Title", + }, + }, + + "default.png": imageBuffer, + "global.png": imageBuffer, + "1.png": imageBuffer, + "2.png": imageBuffer, + }, + + getTests: function (tabs, windows) { + let manifest = browser.runtime.getManifest(); + let { manifest_version } = manifest; + const action = manifest_version < 3 ? "browserAction" : "action"; + + let details = [ + { + icon: browser.runtime.getURL("default.png"), + popup: browser.runtime.getURL("default.html"), + title: "Default Title", + badge: "", + badgeBackgroundColor: [0xd9, 0, 0, 255], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + enabled: true, + }, + { icon: browser.runtime.getURL("1.png") }, + { + icon: browser.runtime.getURL("2.png"), + popup: browser.runtime.getURL("2.html"), + title: "Title 2", + badge: "2", + badgeBackgroundColor: [0xff, 0, 0, 0xff], + badgeTextColor: [0, 0xff, 0xff, 0xff], + enabled: false, + }, + { + icon: browser.runtime.getURL("global.png"), + popup: browser.runtime.getURL("global.html"), + title: "Global Title", + badge: "g", + badgeBackgroundColor: [0, 0xff, 0, 0xff], + badgeTextColor: [0xff, 0, 0xff, 0xff], + enabled: false, + }, + { + icon: browser.runtime.getURL("global.png"), + popup: browser.runtime.getURL("global.html"), + title: "Global Title", + badge: "g", + badgeBackgroundColor: [0, 0xff, 0, 0xff], + badgeTextColor: [0xff, 0, 0xff, 0xff], + }, + ]; + + let promiseTabLoad = details => { + return new Promise(resolve => { + browser.tabs.onUpdated.addListener(function listener(tabId, changed) { + if (tabId == details.id && changed.url == details.url) { + browser.tabs.onUpdated.removeListener(listener); + resolve(); + } + }); + }); + }; + + return [ + async expect => { + browser.test.log("Initial state, expect default properties."); + + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log( + "Change the icon in the current tab. Expect default properties excluding the icon." + ); + browser[action].setIcon({ tabId: tabs[0], path: "1.png" }); + + expect(details[1], null, null, details[0]); + }, + async expect => { + browser.test.log("Create a new tab. Expect default properties."); + let tab = await browser.tabs.create({ + active: true, + url: "about:blank?0", + }); + tabs.push(tab.id); + + browser.test.log("Await tab load."); + let promise = promiseTabLoad({ id: tabs[1], url: "about:blank?0" }); + let { url } = await browser.tabs.get(tabs[1]); + if (url === "about:blank") { + await promise; + } + + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Change properties. Expect new properties."); + let tabId = tabs[1]; + browser[action].setIcon({ tabId, path: "2.png" }); + browser[action].setPopup({ tabId, popup: "2.html" }); + browser[action].setTitle({ tabId, title: "Title 2" }); + browser[action].setBadgeText({ tabId, text: "2" }); + browser[action].setBadgeBackgroundColor({ + tabId, + color: "#ff0000", + }); + browser[action].setBadgeTextColor({ tabId, color: "#00ffff" }); + browser[action].disable(tabId); + + expect(details[2], null, null, details[0]); + }, + async expect => { + browser.test.log( + "Switch back to the first tab. Expect previously set properties." + ); + await browser.tabs.update(tabs[0], { active: true }); + expect(details[1], null, null, details[0]); + }, + async expect => { + browser.test.log( + "Change global values, expect those changes reflected." + ); + browser[action].setIcon({ path: "global.png" }); + browser[action].setPopup({ popup: "global.html" }); + browser[action].setTitle({ title: "Global Title" }); + browser[action].setBadgeText({ text: "g" }); + browser[action].setBadgeBackgroundColor({ + color: [0, 0xff, 0, 0xff], + }); + browser[action].setBadgeTextColor({ + color: [0xff, 0, 0xff, 0xff], + }); + browser[action].disable(); + + expect(details[1], null, details[3], details[0]); + }, + async expect => { + browser.test.log("Re-enable globally. Expect enabled."); + browser[action].enable(); + + expect(details[1], null, details[4], details[0]); + }, + async expect => { + browser.test.log( + "Switch back to tab 2. Expect former tab values, and new global values from previous steps." + ); + await browser.tabs.update(tabs[1], { active: true }); + + expect(details[2], null, details[4], details[0]); + }, + async expect => { + browser.test.log( + "Navigate to a new page. Expect tab-specific values to be cleared." + ); + + let promise = promiseTabLoad({ id: tabs[1], url: "about:blank?1" }); + browser.tabs.update(tabs[1], { url: "about:blank?1" }); + await promise; + + expect(null, null, details[4], details[0]); + }, + async expect => { + browser.test.log( + "Delete tab, switch back to tab 1. Expect previous results again." + ); + await browser.tabs.remove(tabs[1]); + expect(details[1], null, details[4], details[0]); + }, + async expect => { + browser.test.log("Create a new tab. Expect new global properties."); + let tab = await browser.tabs.create({ + active: true, + url: "about:blank?2", + }); + tabs.push(tab.id); + expect(null, null, details[4], details[0]); + }, + async expect => { + browser.test.log("Delete tab."); + await browser.tabs.remove(tabs[2]); + expect(details[1], null, details[4], details[0]); + }, + ]; + }, +}; + +add_task(async function testTabSwitchContext() { + await runTests({ + manifest: { + browser_action: { + default_icon: "default.png", + default_popup: "__MSG_popup__", + default_title: "Default __MSG_title__", + default_area: "navbar", + }, + + default_locale: "en", + + permissions: ["tabs"], + }, + ...tabSwitchTestData, + }); +}); + +add_task(async function testTabSwitchActionContext() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.manifestV3.enabled", true]], + }); + + await runTests({ + manifest: { + manifest_version: 3, + action: { + default_icon: "default.png", + default_popup: "__MSG_popup__", + default_title: "Default __MSG_title__", + default_area: "navbar", + }, + default_locale: "en", + permissions: ["tabs"], + }, + ...tabSwitchTestData, + }); +}); + +add_task(async function testDefaultTitle() { + await runTests({ + manifest: { + name: "Foo Extension", + + browser_action: { + default_icon: "icon.png", + default_area: "navbar", + }, + + permissions: ["tabs"], + }, + + files: { + "icon.png": imageBuffer, + }, + + getTests: function (tabs, windows) { + let details = [ + { + title: "Foo Extension", + popup: "", + badge: "", + badgeBackgroundColor: [0xd9, 0, 0, 255], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + icon: browser.runtime.getURL("icon.png"), + enabled: true, + }, + { title: "Foo Title" }, + { title: "Bar Title" }, + ]; + + return [ + async expect => { + browser.test.log("Initial state. Expect default properties."); + + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Change the tab title. Expect new title."); + browser.browserAction.setTitle({ + tabId: tabs[0], + title: "Foo Title", + }); + + expect(details[1], null, null, details[0]); + }, + async expect => { + browser.test.log("Change the global title. Expect same properties."); + browser.browserAction.setTitle({ title: "Bar Title" }); + + expect(details[1], null, details[2], details[0]); + }, + async expect => { + browser.test.log("Clear the tab title. Expect new global title."); + browser.browserAction.setTitle({ tabId: tabs[0], title: null }); + + expect(null, null, details[2], details[0]); + }, + async expect => { + browser.test.log("Clear the global title. Expect default title."); + browser.browserAction.setTitle({ title: null }); + + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.assertRejects( + browser.browserAction.setPopup({ popup: "about:addons" }), + /Access denied for URL about:addons/, + "unable to set popup to about:addons" + ); + + expect(null, null, null, details[0]); + }, + ]; + }, + }); +}); + +add_task(async function testBadgeColorPersistence() { + const extension = ExtensionTestUtils.loadExtension({ + background() { + browser.test.onMessage.addListener((msg, arg) => { + browser.browserAction[msg](arg); + }); + }, + manifest: { + browser_action: { + default_area: "navbar", + }, + }, + }); + await extension.startup(); + + function getBadgeForWindow(win) { + const widget = getBrowserActionWidget(extension).forWindow(win).node; + return widget.firstElementChild.badgeLabel; + } + + let badge = getBadgeForWindow(window); + const badgeChanged = new Promise(resolve => { + const observer = new MutationObserver(() => resolve()); + observer.observe(badge, { attributes: true, attributeFilter: ["style"] }); + }); + + extension.sendMessage("setBadgeText", { text: "hi" }); + extension.sendMessage("setBadgeBackgroundColor", { color: [0, 255, 0, 255] }); + + await badgeChanged; + + is(badge.textContent, "hi", "badge text is set in first window"); + is( + badge.style.backgroundColor, + "rgb(0, 255, 0)", + "badge color is set in first window" + ); + + let windowOpenedPromise = BrowserTestUtils.waitForNewWindow(); + let win = OpenBrowserWindow(); + await windowOpenedPromise; + + badge = getBadgeForWindow(win); + is(badge.textContent, "hi", "badge text is set in new window"); + is( + badge.style.backgroundColor, + "rgb(0, 255, 0)", + "badge color is set in new window" + ); + + await BrowserTestUtils.closeWindow(win); + await extension.unload(); +}); + +add_task(async function testPropertyRemoval() { + await runTests({ + manifest: { + name: "Generated extension", + browser_action: { + default_icon: "default.png", + default_popup: "default.html", + default_title: "Default Title", + default_area: "navbar", + }, + }, + + files: { + "default.png": imageBuffer, + "global.png": imageBuffer, + "global2.png": imageBuffer, + "window.png": imageBuffer, + "tab.png": imageBuffer, + }, + + getTests: function (tabs, windows) { + let defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg"; + let details = [ + { + icon: browser.runtime.getURL("default.png"), + popup: browser.runtime.getURL("default.html"), + title: "Default Title", + badge: "", + badgeBackgroundColor: [0xd9, 0x00, 0x00, 0xff], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + enabled: true, + }, + { + icon: browser.runtime.getURL("global.png"), + popup: browser.runtime.getURL("global.html"), + title: "global", + badge: "global", + badgeBackgroundColor: [0x11, 0x11, 0x11, 0xff], + badgeTextColor: [0x99, 0x99, 0x99, 0xff], + }, + { + icon: browser.runtime.getURL("window.png"), + popup: browser.runtime.getURL("window.html"), + title: "window", + badge: "window", + badgeBackgroundColor: [0x22, 0x22, 0x22, 0xff], + badgeTextColor: [0x88, 0x88, 0x88, 0xff], + }, + { + icon: browser.runtime.getURL("tab.png"), + popup: browser.runtime.getURL("tab.html"), + title: "tab", + badge: "tab", + badgeBackgroundColor: [0x33, 0x33, 0x33, 0xff], + badgeTextColor: [0x77, 0x77, 0x77, 0xff], + }, + { + icon: defaultIcon, + popup: "", + title: "", + badge: "", + badgeBackgroundColor: [0x33, 0x33, 0x33, 0xff], + badgeTextColor: [0x77, 0x77, 0x77, 0xff], + }, + { + icon: browser.runtime.getURL("global2.png"), + popup: browser.runtime.getURL("global2.html"), + title: "global2", + badge: "global2", + badgeBackgroundColor: [0x44, 0x44, 0x44, 0xff], + badgeTextColor: [0x66, 0x66, 0x66, 0xff], + }, + ]; + + return [ + async expect => { + browser.test.log("Initial state, expect default properties."); + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Set global values, expect the new values."); + browser.browserAction.setIcon({ path: "global.png" }); + browser.browserAction.setPopup({ popup: "global.html" }); + browser.browserAction.setTitle({ title: "global" }); + browser.browserAction.setBadgeText({ text: "global" }); + browser.browserAction.setBadgeBackgroundColor({ color: "#111" }); + browser.browserAction.setBadgeTextColor({ color: "#999" }); + expect(null, null, details[1], details[0]); + }, + async expect => { + browser.test.log("Set window values, expect the new values."); + let windowId = windows[0]; + browser.browserAction.setIcon({ windowId, path: "window.png" }); + browser.browserAction.setPopup({ windowId, popup: "window.html" }); + browser.browserAction.setTitle({ windowId, title: "window" }); + browser.browserAction.setBadgeText({ windowId, text: "window" }); + browser.browserAction.setBadgeBackgroundColor({ + windowId, + color: "#222", + }); + browser.browserAction.setBadgeTextColor({ windowId, color: "#888" }); + expect(null, details[2], details[1], details[0]); + }, + async expect => { + browser.test.log("Set tab values, expect the new values."); + let tabId = tabs[0]; + browser.browserAction.setIcon({ tabId, path: "tab.png" }); + browser.browserAction.setPopup({ tabId, popup: "tab.html" }); + browser.browserAction.setTitle({ tabId, title: "tab" }); + browser.browserAction.setBadgeText({ tabId, text: "tab" }); + browser.browserAction.setBadgeBackgroundColor({ + tabId, + color: "#333", + }); + browser.browserAction.setBadgeTextColor({ tabId, color: "#777" }); + expect(details[3], details[2], details[1], details[0]); + }, + async expect => { + browser.test.log( + "Set empty tab values, expect empty values except for colors." + ); + let tabId = tabs[0]; + browser.browserAction.setIcon({ tabId, path: "" }); + browser.browserAction.setPopup({ tabId, popup: "" }); + browser.browserAction.setTitle({ tabId, title: "" }); + browser.browserAction.setBadgeText({ tabId, text: "" }); + await browser.test.assertRejects( + browser.browserAction.setBadgeBackgroundColor({ tabId, color: "" }), + /^Invalid badge background color: ""$/, + "Expected invalid badge background color error" + ); + await browser.test.assertRejects( + browser.browserAction.setBadgeTextColor({ tabId, color: "" }), + /^Invalid badge text color: ""$/, + "Expected invalid badge text color error" + ); + expect(details[4], details[2], details[1], details[0]); + }, + async expect => { + browser.test.log("Remove tab values, expect window values."); + let tabId = tabs[0]; + browser.browserAction.setIcon({ tabId, path: null }); + browser.browserAction.setPopup({ tabId, popup: null }); + browser.browserAction.setTitle({ tabId, title: null }); + browser.browserAction.setBadgeText({ tabId, text: null }); + browser.browserAction.setBadgeBackgroundColor({ tabId, color: null }); + browser.browserAction.setBadgeTextColor({ tabId, color: null }); + expect(null, details[2], details[1], details[0]); + }, + async expect => { + browser.test.log("Remove window values, expect global values."); + let windowId = windows[0]; + browser.browserAction.setIcon({ windowId, path: null }); + browser.browserAction.setPopup({ windowId, popup: null }); + browser.browserAction.setTitle({ windowId, title: null }); + browser.browserAction.setBadgeText({ windowId, text: null }); + browser.browserAction.setBadgeBackgroundColor({ + windowId, + color: null, + }); + browser.browserAction.setBadgeTextColor({ windowId, color: null }); + expect(null, null, details[1], details[0]); + }, + async expect => { + browser.test.log("Change global values, expect the new values."); + browser.browserAction.setIcon({ path: "global2.png" }); + browser.browserAction.setPopup({ popup: "global2.html" }); + browser.browserAction.setTitle({ title: "global2" }); + browser.browserAction.setBadgeText({ text: "global2" }); + browser.browserAction.setBadgeBackgroundColor({ color: "#444" }); + browser.browserAction.setBadgeTextColor({ color: "#666" }); + expect(null, null, details[5], details[0]); + }, + async expect => { + browser.test.log("Remove global values, expect defaults."); + browser.browserAction.setIcon({ path: null }); + browser.browserAction.setPopup({ popup: null }); + browser.browserAction.setBadgeText({ text: null }); + browser.browserAction.setTitle({ title: null }); + browser.browserAction.setBadgeBackgroundColor({ color: null }); + browser.browserAction.setBadgeTextColor({ color: null }); + expect(null, null, null, details[0]); + }, + ]; + }, + }); +}); + +add_task(async function testMultipleWindows() { + await runTests({ + manifest: { + browser_action: { + default_icon: "default.png", + default_popup: "default.html", + default_title: "Default Title", + default_area: "navbar", + }, + }, + + files: { + "default.png": imageBuffer, + "window1.png": imageBuffer, + "window2.png": imageBuffer, + }, + + getTests: function (tabs, windows) { + let details = [ + { + icon: browser.runtime.getURL("default.png"), + popup: browser.runtime.getURL("default.html"), + title: "Default Title", + badge: "", + badgeBackgroundColor: [0xd9, 0x00, 0x00, 0xff], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + enabled: true, + }, + { + icon: browser.runtime.getURL("window1.png"), + popup: browser.runtime.getURL("window1.html"), + title: "window1", + badge: "w1", + badgeBackgroundColor: [0x11, 0x11, 0x11, 0xff], + badgeTextColor: [0x99, 0x99, 0x99, 0xff], + }, + { + icon: browser.runtime.getURL("window2.png"), + popup: browser.runtime.getURL("window2.html"), + title: "window2", + badge: "w2", + badgeBackgroundColor: [0x22, 0x22, 0x22, 0xff], + badgeTextColor: [0x88, 0x88, 0x88, 0xff], + }, + { title: "tab" }, + ]; + + return [ + async expect => { + browser.test.log("Initial state, expect default properties."); + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Set window values, expect the new values."); + let windowId = windows[0]; + browser.browserAction.setIcon({ windowId, path: "window1.png" }); + browser.browserAction.setPopup({ windowId, popup: "window1.html" }); + browser.browserAction.setTitle({ windowId, title: "window1" }); + browser.browserAction.setBadgeText({ windowId, text: "w1" }); + browser.browserAction.setBadgeBackgroundColor({ + windowId, + color: "#111", + }); + browser.browserAction.setBadgeTextColor({ windowId, color: "#999" }); + expect(null, details[1], null, details[0]); + }, + async expect => { + browser.test.log("Create a new tab, expect window values."); + let tab = await browser.tabs.create({ active: true }); + tabs.push(tab.id); + expect(null, details[1], null, details[0]); + }, + async expect => { + browser.test.log("Set a tab title, expect it."); + await browser.browserAction.setTitle({ + tabId: tabs[1], + title: "tab", + }); + expect(details[3], details[1], null, details[0]); + }, + async expect => { + browser.test.log("Open a new window, expect default values."); + let { id } = await browser.windows.create(); + windows.push(id); + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Set window values, expect the new values."); + let windowId = windows[1]; + browser.browserAction.setIcon({ windowId, path: "window2.png" }); + browser.browserAction.setPopup({ windowId, popup: "window2.html" }); + browser.browserAction.setTitle({ windowId, title: "window2" }); + browser.browserAction.setBadgeText({ windowId, text: "w2" }); + browser.browserAction.setBadgeBackgroundColor({ + windowId, + color: "#222", + }); + browser.browserAction.setBadgeTextColor({ windowId, color: "#888" }); + expect(null, details[2], null, details[0]); + }, + async expect => { + browser.test.log( + "Move tab from old window to the new one. Tab-specific data" + + " is preserved but inheritance is from the new window" + ); + await browser.tabs.move(tabs[1], { windowId: windows[1], index: -1 }); + await browser.tabs.update(tabs[1], { active: true }); + expect(details[3], details[2], null, details[0]); + }, + async expect => { + browser.test.log("Close the initial tab of the new window."); + let [{ id }] = await browser.tabs.query({ + windowId: windows[1], + index: 0, + }); + await browser.tabs.remove(id); + expect(details[3], details[2], null, details[0]); + }, + async expect => { + browser.test.log( + "Move the previous tab to a 3rd window, the 2nd one will close." + ); + await browser.windows.create({ tabId: tabs[1] }); + expect(details[3], null, null, details[0]); + }, + async expect => { + browser.test.log("Close the tab, go back to the 1st window."); + await browser.tabs.remove(tabs[1]); + expect(null, details[1], null, details[0]); + }, + async expect => { + browser.test.log( + "Assert failures for bad parameters. Expect no change" + ); + + let calls = { + setIcon: { path: "default.png" }, + setPopup: { popup: "default.html" }, + setTitle: { title: "Default Title" }, + setBadgeText: { text: "" }, + setBadgeBackgroundColor: { color: [0xd9, 0x00, 0x00, 0xff] }, + setBadgeTextColor: { color: [0xff, 0xff, 0xff, 0xff] }, + getPopup: {}, + getTitle: {}, + getBadgeText: {}, + getBadgeBackgroundColor: {}, + }; + for (let [method, arg] of Object.entries(calls)) { + browser.test.assertThrows( + () => browser.browserAction[method]({ ...arg, windowId: -3 }), + /-3 is too small \(must be at least -2\)/, + method + " with invalid windowId" + ); + await browser.test.assertRejects( + browser.browserAction[method]({ + ...arg, + tabId: tabs[0], + windowId: windows[0], + }), + /Only one of tabId and windowId can be specified/, + method + " with both tabId and windowId" + ); + } + + expect(null, details[1], null, details[0]); + }, + ]; + }, + }); +}); + +add_task(async function testDefaultBadgeTextColor() { + await runTests({ + manifest: { + browser_action: { + default_icon: "default.png", + default_popup: "default.html", + default_title: "Default Title", + default_area: "navbar", + }, + }, + + files: { + "default.png": imageBuffer, + "window1.png": imageBuffer, + "window2.png": imageBuffer, + }, + + getTests: function (tabs, windows) { + let details = [ + { + icon: browser.runtime.getURL("default.png"), + popup: browser.runtime.getURL("default.html"), + title: "Default Title", + badge: "", + badgeBackgroundColor: [0xd9, 0x00, 0x00, 0xff], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + enabled: true, + }, + { + badgeBackgroundColor: [0xff, 0xff, 0x00, 0xff], + badgeTextColor: [0x00, 0x00, 0x00, 0xff], + }, + { + badgeBackgroundColor: [0x00, 0x00, 0xff, 0xff], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + }, + { + badgeBackgroundColor: [0xff, 0xff, 0xff, 0x00], + badgeTextColor: [0x00, 0x00, 0x00, 0xff], + }, + { + badgeBackgroundColor: [0x00, 0x00, 0xff, 0xff], + badgeTextColor: [0xff, 0x00, 0xff, 0xff], + }, + { badgeBackgroundColor: [0xff, 0xff, 0xff, 0x00] }, + { + badgeBackgroundColor: [0x00, 0x00, 0x00, 0x00], + badgeTextColor: [0xff, 0xff, 0xff, 0xff], + }, + ]; + + return [ + async expect => { + browser.test.log("Initial state, expect default properties."); + expect(null, null, null, details[0]); + }, + async expect => { + browser.test.log("Set a global light bgcolor, expect black text."); + browser.browserAction.setBadgeBackgroundColor({ color: "#ff0" }); + expect(null, null, details[1], details[0]); + }, + async expect => { + browser.test.log( + "Set a window-specific dark bgcolor, expect white text." + ); + let windowId = windows[0]; + browser.browserAction.setBadgeBackgroundColor({ + windowId, + color: "#00f", + }); + expect(null, details[2], details[1], details[0]); + }, + async expect => { + browser.test.log( + "Set a tab-specific transparent-white bgcolor, expect black text." + ); + let tabId = tabs[0]; + browser.browserAction.setBadgeBackgroundColor({ + tabId, + color: "#fff0", + }); + expect(details[3], details[2], details[1], details[0]); + }, + async expect => { + browser.test.log( + "Set a window-specific text color, expect it in the tab." + ); + let windowId = windows[0]; + browser.browserAction.setBadgeTextColor({ windowId, color: "#f0f" }); + expect(details[5], details[4], details[1], details[0]); + }, + async expect => { + browser.test.log( + "Remove the window-specific text color, expect black again." + ); + let windowId = windows[0]; + browser.browserAction.setBadgeTextColor({ windowId, color: null }); + expect(details[3], details[2], details[1], details[0]); + }, + async expect => { + browser.test.log( + "Set a tab-specific transparent-black bgcolor, expect white text." + ); + let tabId = tabs[0]; + browser.browserAction.setBadgeBackgroundColor({ + tabId, + color: "#0000", + }); + expect(details[6], details[2], details[1], details[0]); + }, + ]; + }, + }); +}); + +add_task(async function testNavigationClearsData() { + let url = "http://example.com/"; + let default_title = "Default title"; + let tab_title = "Tab title"; + + const { + Management: { + global: { tabTracker }, + }, + } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs"); + let extension, + tabs = []; + async function addTab(...args) { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, ...args); + tabs.push(tab); + return tab; + } + async function sendMessage(method, param, expect, msg) { + extension.sendMessage({ method, param, expect, msg }); + await extension.awaitMessage("done"); + } + async function expectTabSpecificData(tab, msg) { + let tabId = tabTracker.getId(tab); + await sendMessage("getBadgeText", { tabId }, "foo", msg); + await sendMessage("getTitle", { tabId }, tab_title, msg); + } + async function expectDefaultData(tab, msg) { + let tabId = tabTracker.getId(tab); + await sendMessage("getBadgeText", { tabId }, "", msg); + await sendMessage("getTitle", { tabId }, default_title, msg); + } + async function setTabSpecificData(tab) { + let tabId = tabTracker.getId(tab); + await expectDefaultData( + tab, + "Expect default data before setting tab-specific data." + ); + await sendMessage("setBadgeText", { tabId, text: "foo" }); + await sendMessage("setTitle", { tabId, title: tab_title }); + await expectTabSpecificData( + tab, + "Expect tab-specific data after setting it." + ); + } + + info("Load a tab before installing the extension"); + let tab1 = await addTab(url, true, true); + + extension = ExtensionTestUtils.loadExtension({ + manifest: { + browser_action: { default_title }, + }, + background: function () { + browser.test.onMessage.addListener( + async ({ method, param, expect, msg }) => { + let result = await browser.browserAction[method](param); + if (expect !== undefined) { + browser.test.assertEq(expect, result, msg); + } + browser.test.sendMessage("done"); + } + ); + }, + }); + await extension.startup(); + + info("Set tab-specific data to the existing tab."); + await setTabSpecificData(tab1); + + info("Add a hash. Does not cause navigation."); + await navigateTab(tab1, url + "#hash"); + await expectTabSpecificData( + tab1, + "Adding a hash does not clear tab-specific data" + ); + + info("Remove the hash. Causes navigation."); + await navigateTab(tab1, url); + await expectDefaultData(tab1, "Removing hash clears tab-specific data"); + + info("Open a new tab, set tab-specific data to it."); + let tab2 = await addTab("about:newtab", false, false); + await setTabSpecificData(tab2); + + info("Load a page in that tab."); + await navigateTab(tab2, url); + await expectDefaultData(tab2, "Loading a page clears tab-specific data."); + + info("Set tab-specific data."); + await setTabSpecificData(tab2); + + info("Push history state. Does not cause navigation."); + await historyPushState(tab2, url + "/path"); + await expectTabSpecificData( + tab2, + "history.pushState() does not clear tab-specific data" + ); + + info("Navigate when the tab is not selected"); + gBrowser.selectedTab = tab1; + await navigateTab(tab2, url); + await expectDefaultData( + tab2, + "Navigating clears tab-specific data, even when not selected." + ); + + for (let tab of tabs) { + await BrowserTestUtils.removeTab(tab); + } + await extension.unload(); +}); -- cgit v1.2.3