diff options
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.js | 321 |
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(); +}); |