--- a/browser/modules/ThemeVariableMap.jsm
+++ b/browser/modules/ThemeVariableMap.jsm
@@ -20,9 +20,13 @@ const ThemeVariableMap = [
["--tabs-border-color", "toolbar_top_separator", "navigator-toolbox"],
["--lwt-toolbar-vertical-separator", "toolbar_vertical_separator"],
["--toolbox-border-bottom-color", "toolbar_bottom_separator"],
["--lwt-toolbarbutton-icon-fill", "icon_color"],
["--lwt-toolbarbutton-icon-fill-attention", "icon_attention_color"],
["--lwt-toolbarbutton-hover-background", "button_background_hover"],
["--lwt-toolbarbutton-active-background", "button_background_active"],
["--lwt-selected-tab-background-color", "tab_selected"],
+ ["--autocomplete-popup-background", "popup"],
+ ["--autocomplete-popup-color", "popup_text"],
+ ["--autocomplete-popup-highlight-background", "popup_highlight"],
+ ["--autocomplete-popup-highlight-color", "popup_highlight_text"],
];
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -40,16 +40,18 @@
--tab-line-color: highlight;
}
:root:-moz-lwtheme {
--toolbar-bgcolor: rgba(255,255,255,.4);
--toolbar-bgimage: none;
--toolbox-border-bottom-color: rgba(0,0,0,.3);
+
+ --panel-separator-color: hsla(210,4%,10%,.14);
}
#menubar-items {
-moz-box-orient: vertical; /* for flex hack */
}
#main-menubar {
-moz-box-flex: 1; /* make menu items expand to fill toolbar height */
--- a/browser/themes/shared/searchbar.inc.css
+++ b/browser/themes/shared/searchbar.inc.css
@@ -44,17 +44,17 @@
}
.search-panel-header {
font-weight: normal;
background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color);
margin: 0;
padding: 3px 6px;
- color: GrayText;
+ color: var(--autocomplete-popup-secondary-color);
}
.search-panel-header > label {
margin-top: 2px !important;
margin-bottom: 1px !important;
}
.search-panel-current-input > label {
@@ -79,17 +79,17 @@
min-width: 48px;
height: 32px;
margin: 0;
padding: 0;
background: linear-gradient(transparent 15%, var(--panel-separator-color) 15%, var(--panel-separator-color) 85%, transparent 85%);
background-size: 1px auto;
background-repeat: no-repeat;
background-position: right center;
- color: GrayText;
+ color: var(--autocomplete-popup-secondary-color);
}
.searchbar-engine-one-off-item:-moz-locale-dir(rtl) {
background-position-x: left;
}
.searchbar-engine-one-off-item:not(.last-row) {
box-sizing: content-box;
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -4,16 +4,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
%endif
:root {
--autocomplete-popup-background: -moz-field;
--autocomplete-popup-color: -moz-fieldtext;
--autocomplete-popup-highlight-background: Highlight;
--autocomplete-popup-highlight-color: HighlightText;
+ --autocomplete-popup-secondary-color: GrayText;
+}
+
+:root:-moz-lwtheme {
+ --autocomplete-popup-background: #fff;
+ --autocomplete-popup-color: #0c0c0d;
+ --autocomplete-popup-secondary-color: #737373;
+ --urlbar-popup-url-color: hsl(210, 77%, 47%);
+ --urlbar-popup-action-color: hsl(178, 100%, 28%);
+}
+
+:root[lwt-popup-brighttext] {
+ --urlbar-popup-url-color: #0a84ff;
+ --urlbar-popup-action-color: #30e60b;
}
#PopupAutoCompleteRichResult,
#PopupSearchAutoComplete {
background: var(--autocomplete-popup-background);
color: var(--autocomplete-popup-color);
}
@@ -43,17 +57,17 @@
:root[uidensity=touch] #PopupAutoCompleteRichResult .autocomplete-richlistitem {
min-height: 40px;
}
/* Awesomebar popup items */
.ac-separator:not([selected=true]) {
- color: GrayText;
+ color: var(--autocomplete-popup-secondary-color);
}
.ac-url:not([selected=true]) {
color: var(--urlbar-popup-url-color);
}
.ac-action:not([selected=true]) {
color: var(--urlbar-popup-action-color);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -62,16 +62,18 @@
}
}
:root:-moz-lwtheme {
--toolbar-bgcolor: rgba(255,255,255,.4);
--toolbar-bgimage: none;
--toolbox-border-bottom-color: rgba(0,0,0,.3);
+
+ --panel-separator-color: hsla(210,4%,10%,.14);
}
#navigator-toolbox:-moz-lwtheme {
--tabs-border-color: rgba(0,0,0,.3);
}
#menubar-items {
-moz-box-orient: vertical; /* for flex hack */
--- a/toolkit/components/extensions/ext-theme.js
+++ b/toolkit/components/extensions/ext-theme.js
@@ -159,16 +159,18 @@ class Theme {
case "toolbar_top_separator":
case "toolbar_bottom_separator":
case "toolbar_vertical_separator":
case "button_background_hover":
case "button_background_active":
case "popup":
case "popup_text":
case "popup_border":
+ case "popup_highlight":
+ case "popup_highlight_text":
this.lwtStyles[color] = cssColor;
break;
}
}
}
/**
* Helper method for loading images found in the extension's manifest.
--- a/toolkit/components/extensions/schemas/theme.json
+++ b/toolkit/components/extensions/schemas/theme.json
@@ -167,16 +167,24 @@
},
"popup_text": {
"$ref": "ThemeColor",
"optional": true
},
"popup_border": {
"$ref": "ThemeColor",
"optional": true
+ },
+ "popup_highlight": {
+ "$ref": "ThemeColor",
+ "optional": true
+ },
+ "popup_highlight_text": {
+ "$ref": "ThemeColor",
+ "optional": true
}
},
"additionalProperties": { "$ref": "UnrecognizedProperty" }
},
"icons": {
"type": "object",
"optional": true,
"properties": {
--- a/toolkit/components/extensions/test/browser/browser.ini
+++ b/toolkit/components/extensions/test/browser/browser.ini
@@ -18,8 +18,9 @@ support-files =
[browser_ext_themes_tab_loading.js]
[browser_ext_themes_tab_text.js]
[browser_ext_themes_toolbar_fields.js]
[browser_ext_themes_toolbars.js]
[browser_ext_themes_toolbarbutton_icons.js]
[browser_ext_themes_toolbarbutton_colors.js]
[browser_ext_themes_arrowpanels.js]
[browser_ext_themes_tab_selected.js]
+[browser_ext_themes_autocomplete_popup.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_autocomplete_popup.js
@@ -0,0 +1,233 @@
+"use strict";
+
+// This test checks whether applied WebExtension themes that attempt to change
+// popup properties are applied correctly to the autocomplete bar.
+const POPUP_COLOR = "#85A400";
+const POPUP_TEXT_COLOR_DARK = "#000000";
+const POPUP_TEXT_COLOR_BRIGHT = "#ffffff";
+const POPUP_SELECTED_COLOR = "#9400ff";
+const POPUP_SELECTED_TEXT_COLOR = "#09b9a6";
+
+const POPUP_URL_COLOR_DARK = "#1c78d4";
+const POPUP_ACTION_COLOR_DARK = "#008f8a";
+const POPUP_URL_COLOR_BRIGHT = "#0a84ff";
+const POPUP_ACTION_COLOR_BRIGHT = "#30e60b";
+
+const SEARCH_TERM = "urlbar-reflows-" + Date.now();
+const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches";
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
+});
+
+function promisePopupShown(popup) {
+ return new Promise(resolve => {
+ if (popup.state == "open") {
+ resolve();
+ } else {
+ popup.addEventListener("popupshown", function(event) {
+ resolve();
+ }, {once: true});
+ }
+ });
+}
+
+async function promiseAutocompleteResultPopup(inputText) {
+ gURLBar.focus();
+ gURLBar.value = inputText;
+ gURLBar.controller.startSearch(inputText);
+ await promisePopupShown(gURLBar.popup);
+ await BrowserTestUtils.waitForCondition(() => {
+ return gURLBar.controller.searchStatus >=
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH;
+ });
+}
+
+async function waitForAutocompleteResultAt(index) {
+ let searchString = gURLBar.controller.searchString;
+ await BrowserTestUtils.waitForCondition(
+ () => gURLBar.popup.richlistbox.children.length > index &&
+ gURLBar.popup.richlistbox.children[index].getAttribute("ac-text") == searchString,
+ `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`);
+ // Ensure the addition is complete, for proper mouse events on the entries.
+ await new Promise(resolve => window.requestIdleCallback(resolve, {timeout: 1000}));
+ return gURLBar.popup.richlistbox.children[index];
+}
+
+add_task(async function setup() {
+ await PlacesUtils.history.clear();
+ const NUM_VISITS = 10;
+ let visits = [];
+
+ for (let i = 0; i < NUM_VISITS; ++i) {
+ visits.push({
+ uri: `http://example.com/urlbar-reflows-${i}`,
+ title: `Reflow test for URL bar entry #${i} - ${SEARCH_TERM}`,
+ });
+ }
+
+ await PlacesTestUtils.addVisits(visits);
+
+ registerCleanupFunction(async function() {
+ await PlacesUtils.history.clear();
+ });
+});
+
+add_task(async function test_popup_url() {
+ // Load extension with brighttext not set
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "theme": {
+ "images": {
+ "headerURL": "image1.png",
+ },
+ "colors": {
+ "accentcolor": ACCENT_COLOR,
+ "textcolor": TEXT_COLOR,
+ "popup": POPUP_COLOR,
+ "popup_text": POPUP_TEXT_COLOR_DARK,
+ "popup_highlight": POPUP_SELECTED_COLOR,
+ "popup_highlight_text": POPUP_SELECTED_TEXT_COLOR,
+ },
+ },
+ },
+ files: {
+ "image1.png": BACKGROUND,
+ },
+ });
+
+ await extension.startup();
+
+ let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
+ Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true);
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
+ registerCleanupFunction(async function() {
+ await PlacesUtils.history.clear();
+ Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
+ await BrowserTestUtils.removeTab(tab);
+ });
+
+ let visits = [];
+
+ for (let i = 0; i < maxResults; i++) {
+ visits.push({uri: makeURI("http://example.com/autocomplete/?" + i)});
+ }
+
+ await PlacesTestUtils.addVisits(visits);
+ await promiseAutocompleteResultPopup("example.com/autocomplete");
+ await waitForAutocompleteResultAt(maxResults - 1);
+
+ let popup = gURLBar.popup;
+ let results = popup.richlistbox.children;
+ is(results.length, maxResults,
+ "Should get maxResults=" + maxResults + " results");
+
+ let popupCS = window.getComputedStyle(popup);
+
+ Assert.equal(popupCS.backgroundColor,
+ `rgb(${hexToRGB(POPUP_COLOR).join(", ")})`,
+ `Popup background color should be set to ${POPUP_COLOR}`);
+
+ Assert.equal(popupCS.color,
+ `rgb(${hexToRGB(POPUP_TEXT_COLOR_DARK).join(", ")})`,
+ `Popup color should be set to ${POPUP_TEXT_COLOR_DARK}`);
+
+ // Set the selected attribute to true to test the highlight popup properties
+ results[1].setAttribute("selected", "true");
+ let resultCS = window.getComputedStyle(results[1]);
+
+ Assert.equal(resultCS.backgroundColor,
+ `rgb(${hexToRGB(POPUP_SELECTED_COLOR).join(", ")})`,
+ `Popup highlight background color should be set to ${POPUP_SELECTED_COLOR}`);
+
+ Assert.equal(resultCS.color,
+ `rgb(${hexToRGB(POPUP_SELECTED_TEXT_COLOR).join(", ")})`,
+ `Popup highlight color should be set to ${POPUP_SELECTED_TEXT_COLOR}`);
+
+ results[1].removeAttribute("selected");
+
+ let urlText = document.getAnonymousElementByAttribute(results[1], "anonid", "url-text");
+ Assert.equal(window.getComputedStyle(urlText).color,
+ `rgb(${hexToRGB(POPUP_URL_COLOR_DARK).join(", ")})`,
+ `Urlbar popup url color should be set to ${POPUP_URL_COLOR_DARK}`);
+
+ let actionText = document.getAnonymousElementByAttribute(results[1], "anonid", "action-text");
+ Assert.equal(window.getComputedStyle(actionText).color,
+ `rgb(${hexToRGB(POPUP_ACTION_COLOR_DARK).join(", ")})`,
+ `Urlbar popup action color should be set to ${POPUP_ACTION_COLOR_DARK}`);
+
+ let root = document.documentElement;
+ Assert.equal(root.getAttribute("lwt-popup-brighttext"),
+ "",
+ "brighttext should not be set!");
+
+ await extension.unload();
+
+ // Load a manifest with popup_text being bright. Test for bright text properties.
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "theme": {
+ "images": {
+ "headerURL": "image1.png",
+ },
+ "colors": {
+ "accentcolor": ACCENT_COLOR,
+ "textcolor": TEXT_COLOR,
+ "popup": POPUP_COLOR,
+ "popup_text": POPUP_TEXT_COLOR_BRIGHT,
+ "popup_highlight": POPUP_SELECTED_COLOR,
+ "popup_highlight_text": POPUP_SELECTED_TEXT_COLOR,
+ },
+ },
+ },
+ files: {
+ "image1.png": BACKGROUND,
+ },
+ });
+
+ await extension.startup();
+
+ popupCS = window.getComputedStyle(popup);
+ Assert.equal(popupCS.color,
+ `rgb(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")})`,
+ `Popup color should be set to ${POPUP_TEXT_COLOR_BRIGHT}`);
+
+ urlText = document.getAnonymousElementByAttribute(results[1], "anonid", "url-text");
+ Assert.equal(window.getComputedStyle(urlText).color,
+ `rgb(${hexToRGB(POPUP_URL_COLOR_BRIGHT).join(", ")})`,
+ `Urlbar popup url color should be set to ${POPUP_URL_COLOR_BRIGHT}`);
+
+ actionText = document.getAnonymousElementByAttribute(results[1], "anonid", "action-text");
+ Assert.equal(window.getComputedStyle(actionText).color,
+ `rgb(${hexToRGB(POPUP_ACTION_COLOR_BRIGHT).join(", ")})`,
+ `Urlbar popup action color should be set to ${POPUP_ACTION_COLOR_BRIGHT}`);
+
+ // Since brighttext is enabled, the seperator color should be
+ // POPUP_TEXT_COLOR_BRIGHT with added alpha.
+ let separator = document.getAnonymousElementByAttribute(results[1], "anonid", "separator");
+ Assert.equal(window.getComputedStyle(separator).color,
+ `rgba(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")}, 0.5)`,
+ `Urlbar popup separator color should be set to ${POPUP_TEXT_COLOR_BRIGHT} with alpha`);
+
+ Assert.equal(root.getAttribute("lwt-popup-brighttext"),
+ "true",
+ "brighttext should be set to true!");
+
+ await extension.unload();
+
+ // Check to see if popup-brighttext and secondary color are not set after
+ // unload of theme
+ Assert.equal(root.getAttribute("lwt-popup-brighttext"),
+ "",
+ "brighttext should not be set!");
+
+ // Calculate what GrayText should be. May differ between platforms.
+ let span = document.createElement("span");
+ span.style.color = "GrayText";
+ let GRAY_TEXT = window.getComputedStyle(span).color;
+
+ separator = document.getAnonymousElementByAttribute(results[1], "anonid", "separator");
+ Assert.equal(window.getComputedStyle(separator).color,
+ GRAY_TEXT,
+ `Urlbar popup separator color should be set to ${GRAY_TEXT}`);
+});
--- a/toolkit/modules/LightweightThemeConsumer.jsm
+++ b/toolkit/modules/LightweightThemeConsumer.jsm
@@ -99,16 +99,18 @@ LightweightThemeConsumer.prototype = {
// or because we are applying a new theme and the data might be bogus CSS,
// so if we don't reset first, it'll keep the old value.
root.style.removeProperty("--lwt-text-color");
root.style.removeProperty("--lwt-accent-color");
let textcolor = aData.textcolor || "black";
_setProperty(root, active, "--lwt-text-color", textcolor);
_setProperty(root, active, "--lwt-accent-color", this._sanitizeCSSColor(aData.accentcolor) || "white");
+ _inferPopupColorsFromText(root, active, this._sanitizeCSSColor(aData.popup_text));
+
if (active) {
let dummy = this._doc.createElement("dummy");
dummy.style.color = textcolor;
let [r, g, b] = _parseRGB(this._win.getComputedStyle(dummy).color);
let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
root.setAttribute("lwthemetextcolor", luminance <= 110 ? "dark" : "bright");
root.setAttribute("lwtheme", "true");
} else {
@@ -188,8 +190,27 @@ function _setProperties(root, active, va
}
}
function _parseRGB(aColorString) {
var rgb = aColorString.match(/^rgba?\((\d+), (\d+), (\d+)/);
rgb.shift();
return rgb.map(x => parseInt(x));
}
+
+function _inferPopupColorsFromText(element, active, color) {
+ if (!color) {
+ element.removeAttribute("lwt-popup-brighttext");
+ _setProperty(element, active, "--autocomplete-popup-secondary-color");
+ return;
+ }
+
+ let [r, g, b] = _parseRGB(color);
+ let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
+
+ if (luminance <= 110) {
+ element.removeAttribute("lwt-popup-brighttext");
+ } else {
+ element.setAttribute("lwt-popup-brighttext", "true");
+ }
+
+ _setProperty(element, active, "--autocomplete-popup-secondary-color", `rgba(${r}, ${g}, ${b}, 0.5)`);
+}