summaryrefslogtreecommitdiffstats
path: root/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js')
-rw-r--r--browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js321
1 files changed, 321 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
new file mode 100644
index 000000000..9665d6832
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -0,0 +1,321 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// Test that various combinations of icon details specs, for both paths
+// and ImageData objects, result in the correct image being displayed in
+// all display resolutions.
+add_task(function* testDetailsObjects() {
+ function background() {
+ function getImageData(color) {
+ let canvas = document.createElement("canvas");
+ canvas.width = 2;
+ canvas.height = 2;
+ let canvasContext = canvas.getContext("2d");
+
+ canvasContext.clearRect(0, 0, canvas.width, canvas.height);
+ canvasContext.fillStyle = color;
+ canvasContext.fillRect(0, 0, 1, 1);
+
+ return {
+ url: canvas.toDataURL("image/png"),
+ imageData: canvasContext.getImageData(0, 0, canvas.width, canvas.height),
+ };
+ }
+
+ let imageData = {
+ red: getImageData("red"),
+ green: getImageData("green"),
+ };
+
+ /* eslint-disable comma-dangle, indent */
+ let iconDetails = [
+ // Only paths.
+ {details: {"path": "a.png"},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a.png")}},
+ {details: {"path": "/a.png"},
+ resolutions: {
+ "1": browser.runtime.getURL("a.png"),
+ "2": browser.runtime.getURL("a.png")}},
+ {details: {"path": {"19": "a.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a.png")}},
+ {details: {"path": {"38": "a.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a.png")}},
+ {details: {"path": {"19": "a.png", "38": "a-x2.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a-x2.png")}},
+
+ // Test that CSS strings are escaped properly.
+ {details: {"path": 'a.png#" \\'},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png#%22%20%5C"),
+ "2": browser.runtime.getURL("data/a.png#%22%20%5C")}},
+
+ // Only ImageData objects.
+ {details: {"imageData": imageData.red.imageData},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": imageData.red.url}},
+ {details: {"imageData": {"19": imageData.red.imageData}},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": imageData.red.url}},
+ {details: {"imageData": {"38": imageData.red.imageData}},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": imageData.red.url}},
+ {details: {"imageData": {
+ "19": imageData.red.imageData,
+ "38": imageData.green.imageData}},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": imageData.green.url}},
+
+ // Mixed path and imageData objects.
+ //
+ // The behavior is currently undefined if both |path| and
+ // |imageData| specify icons of the same size.
+ {details: {
+ "path": {"19": "a.png"},
+ "imageData": {"38": imageData.red.imageData}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": imageData.red.url}},
+ {details: {
+ "path": {"38": "a.png"},
+ "imageData": {"19": imageData.red.imageData}},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": browser.runtime.getURL("data/a.png")}},
+
+ // A path or ImageData object by itself is treated as a 19px icon.
+ {details: {
+ "path": "a.png",
+ "imageData": {"38": imageData.red.imageData}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": imageData.red.url}},
+ {details: {
+ "path": {"38": "a.png"},
+ "imageData": imageData.red.imageData},
+ resolutions: {
+ "1": imageData.red.url,
+ "2": browser.runtime.getURL("data/a.png")}},
+
+ // Various resolutions
+ {details: {"path": {"18": "a.png", "36": "a-x2.png"}},
+ legacy: true,
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a-x2.png")}},
+ {details: {"path": {"16": "a.png", "30": "a-x2.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/a.png"),
+ "2": browser.runtime.getURL("data/a-x2.png")}},
+ {details: {"path": {"16": "16.png", "100": "100.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/16.png"),
+ "2": browser.runtime.getURL("data/100.png")}},
+ {details: {"path": {"2": "2.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/2.png"),
+ "2": browser.runtime.getURL("data/2.png")}},
+ {details: {"path": {
+ "16": "16.svg",
+ "18": "18.svg"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/16.svg"),
+ "2": browser.runtime.getURL("data/18.svg")}},
+ {details: {"path": {
+ "6": "6.png",
+ "18": "18.png",
+ "36": "36.png",
+ "48": "48.png",
+ "128": "128.png"}},
+ legacy: true,
+ resolutions: {
+ "1": browser.runtime.getURL("data/18.png"),
+ "2": browser.runtime.getURL("data/36.png")},
+ menuResolutions: {
+ "1": browser.runtime.getURL("data/36.png"),
+ "2": browser.runtime.getURL("data/128.png")}},
+ {details: {"path": {
+ "16": "16.png",
+ "18": "18.png",
+ "32": "32.png",
+ "48": "48.png",
+ "64": "64.png",
+ "128": "128.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/16.png"),
+ "2": browser.runtime.getURL("data/32.png")},
+ menuResolutions: {
+ "1": browser.runtime.getURL("data/32.png"),
+ "2": browser.runtime.getURL("data/64.png")}},
+ {details: {"path": {
+ "18": "18.png",
+ "32": "32.png",
+ "48": "48.png",
+ "128": "128.png"}},
+ resolutions: {
+ "1": browser.runtime.getURL("data/32.png"),
+ "2": browser.runtime.getURL("data/32.png")}},
+ ];
+
+ // Allow serializing ImageData objects for logging.
+ ImageData.prototype.toJSON = () => "<ImageData>";
+
+ let tabId;
+
+ browser.test.onMessage.addListener((msg, test) => {
+ if (msg != "setIcon") {
+ browser.test.fail("expecting 'setIcon' message");
+ }
+
+ let details = iconDetails[test.index];
+
+ let detailString = JSON.stringify(details);
+ browser.test.log(`Setting browerAction/pageAction to ${detailString} expecting URLs ${JSON.stringify(details.resolutions)}`);
+
+ Promise.all([
+ browser.browserAction.setIcon(Object.assign({tabId}, details.details)),
+ browser.pageAction.setIcon(Object.assign({tabId}, details.details)),
+ ]).then(() => {
+ browser.test.sendMessage("iconSet");
+ });
+ });
+
+ // Generate a list of tests and resolutions to send back to the test
+ // context.
+ //
+ // This process is a bit convoluted, because the outer test context needs
+ // to handle checking the button nodes and changing the screen resolution,
+ // but it can't pass us icon definitions with ImageData objects. This
+ // shouldn't be a problem, since structured clones should handle ImageData
+ // objects without issue. Unfortunately, |cloneInto| implements a slightly
+ // different algorithm than we use in web APIs, and does not handle them
+ // correctly.
+ let tests = [];
+ for (let [idx, icon] of iconDetails.entries()) {
+ tests.push({
+ index: idx,
+ legacy: !!icon.legacy,
+ menuResolutions: icon.menuResolutions,
+ resolutions: icon.resolutions,
+ });
+ }
+
+ // Sort by resolution, so we don't needlessly switch back and forth
+ // between each test.
+ tests.sort(test => test.resolution);
+
+ browser.tabs.query({active: true, currentWindow: true}, tabs => {
+ tabId = tabs[0].id;
+ browser.pageAction.show(tabId).then(() => {
+ browser.test.sendMessage("ready", tests);
+ });
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "browser_action": {},
+ "page_action": {},
+ "background": {
+ "page": "data/background.html",
+ }
+ },
+
+ files: {
+ "data/background.html": `<script src="background.js"></script>`,
+ "data/background.js": background,
+
+ "data/16.svg": imageBuffer,
+ "data/18.svg": imageBuffer,
+
+ "data/16.png": imageBuffer,
+ "data/18.png": imageBuffer,
+ "data/32.png": imageBuffer,
+ "data/36.png": imageBuffer,
+ "data/48.png": imageBuffer,
+ "data/64.png": imageBuffer,
+ "data/128.png": imageBuffer,
+
+ "a.png": imageBuffer,
+ "data/2.png": imageBuffer,
+ "data/100.png": imageBuffer,
+ "data/a.png": imageBuffer,
+ "data/a-x2.png": imageBuffer,
+ },
+ });
+
+ const RESOLUTION_PREF = "layout.css.devPixelsPerPx";
+
+ yield extension.startup();
+
+ let pageActionId = `${makeWidgetId(extension.id)}-page-action`;
+ let browserActionWidget = getBrowserActionWidget(extension);
+
+ let tests = yield extension.awaitMessage("ready");
+ for (let test of tests) {
+ extension.sendMessage("setIcon", test);
+ yield extension.awaitMessage("iconSet");
+
+ let browserActionButton = browserActionWidget.forWindow(window).node;
+ let pageActionImage = document.getElementById(pageActionId);
+
+
+ // Test icon sizes in the toolbar/urlbar.
+ for (let resolution of Object.keys(test.resolutions)) {
+ yield SpecialPowers.pushPrefEnv({set: [[RESOLUTION_PREF, resolution]]});
+
+ is(window.devicePixelRatio, +resolution, "window has the required resolution");
+
+ let imageURL = test.resolutions[resolution];
+ is(getListStyleImage(browserActionButton), imageURL, `browser action has the correct image at ${resolution}x resolution`);
+ is(getListStyleImage(pageActionImage), imageURL, `page action has the correct image at ${resolution}x resolution`);
+
+ let isLegacy = browserActionButton.classList.contains("toolbarbutton-legacy-addon");
+ is(isLegacy, test.legacy, "Legacy class should be present?");
+
+ yield SpecialPowers.popPrefEnv();
+ }
+
+ if (!test.menuResolutions) {
+ continue;
+ }
+
+
+ // Test icon sizes in the menu panel.
+ CustomizableUI.addWidgetToArea(browserActionWidget.id,
+ CustomizableUI.AREA_PANEL);
+
+ yield showBrowserAction(extension);
+ browserActionButton = browserActionWidget.forWindow(window).node;
+
+ for (let resolution of Object.keys(test.menuResolutions)) {
+ yield SpecialPowers.pushPrefEnv({set: [[RESOLUTION_PREF, resolution]]});
+
+ is(window.devicePixelRatio, +resolution, "window has the required resolution");
+
+ let imageURL = test.menuResolutions[resolution];
+ is(getListStyleImage(browserActionButton), imageURL, `browser action has the correct menu image at ${resolution}x resolution`);
+
+ yield SpecialPowers.popPrefEnv();
+ }
+
+ yield closeBrowserAction(extension);
+
+ CustomizableUI.addWidgetToArea(browserActionWidget.id,
+ CustomizableUI.AREA_NAVBAR);
+ }
+
+ yield extension.unload();
+});