Bug 1113747 - New search UI breaks if too many open search providers are offered. r?florian
Based on an earlier patch by Nihanth Subramanya <nhnt11@gmail.com>
MozReview-Commit-ID: 4TZzFgovIJm
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -485,17 +485,18 @@ toolbar:not(#TabsToolbar) > #personal-bo
}
#PopupSearchAutoComplete {
-moz-binding: url("chrome://browser/content/search/search.xml#browser-search-autocomplete-result-popup");
}
/* Overlay a badge on top of the icon of additional open search providers
in the search panel. */
-.addengine-item > .button-box > .button-icon {
+.addengine-item > .button-box > .button-icon,
+.addengine-item[type="menu"] > .button-box > .box-inherit > .button-icon {
-moz-binding: url("chrome://browser/content/search/search.xml#addengine-icon");
display: -moz-stack;
}
#PopupAutoCompleteRichResult {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
}
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -737,16 +737,34 @@
document.getBindingParent(this).openSuggestionsPanel();
return false;
}
return true;
]]>
</body>
</method>
+ <method name="handleEnter">
+ <parameter name="event"/>
+ <body><![CDATA[
+ // Toggle the open state of the add-engine menu button if it's
+ // selected. We're using handleEnter for this instead of listening
+ // for the command event because a command event isn't fired.
+ if (this.selectedButton &&
+ this.selectedButton.getAttribute("anonid") ==
+ "addengine-menu-button") {
+ this.selectedButton.open = !this.selectedButton.open;
+ return true;
+ }
+ // Otherwise, "call super": do what the autocomplete binding's
+ // handleEnter implementation does.
+ return this.mController.handleEnter(false, event || null);
+ ]]></body>
+ </method>
+
<!-- override |onTextEntered| in autocomplete.xml -->
<method name="onTextEntered">
<parameter name="aEvent"/>
<body><![CDATA[
let engine;
let oneOff = this.selectedButton;
if (oneOff) {
if (!oneOff.engine) {
@@ -1355,52 +1373,17 @@
this._updateAfterQueryChanged();
let list = document.getAnonymousElementByAttribute(this, "anonid",
"search-panel-one-offs");
// Handle opensearch items. This needs to be done before building the
// list of one off providers, as that code will return early if all the
// alternative engines are hidden.
- let addEngineList =
- document.getAnonymousElementByAttribute(this, "anonid", "add-engines");
- while (addEngineList.firstChild)
- addEngineList.firstChild.remove();
-
- const kXULNS =
- "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
- // Add a button for each engine that the page in the selected browser
- // offers. But not when the one-offs are compact. Compact one-offs
- // are shown in the urlbar, and the add-engine buttons span the width
- // of the popup, so if we added all the engines that a site offers, it
- // could effectively break the urlbar popup by offering a ton of
- // engines. We should probably make a smaller version of the buttons
- // for compact one-offs.
- if (!this.compact) {
- for (let engine of gBrowser.selectedBrowser.engines || []) {
- let button = document.createElementNS(kXULNS, "button");
- let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
- [engine.title], 1);
- button.id = this.telemetryOrigin + "-add-engine-" +
- engine.title.replace(/ /g, '-');
- button.setAttribute("class", "addengine-item");
- button.setAttribute("label", label);
- button.setAttribute("pack", "start");
-
- button.setAttribute("crop", "end");
- button.setAttribute("tooltiptext", engine.uri);
- button.setAttribute("uri", engine.uri);
- if (engine.icon) {
- button.setAttribute("image", engine.icon);
- }
- button.setAttribute("title", engine.title);
- addEngineList.appendChild(button);
- }
- }
+ this._rebuildAddEngineList();
let settingsButton =
document.getAnonymousElementByAttribute(this, "anonid",
"search-settings-compact");
// Finally, build the list of one-off buttons.
while (list.firstChild != settingsButton)
list.firstChild.remove();
// Remove the trailing empty text node introduced by the binding's
@@ -1448,16 +1431,19 @@
// Ensure we can refer to the settings buttons by ID:
let settingsEl = document.getAnonymousElementByAttribute(this, "anonid", "search-settings");
settingsEl.id = this.telemetryOrigin + "-anon-search-settings";
let compactSettingsEl = document.getAnonymousElementByAttribute(this, "anonid", "search-settings-compact");
compactSettingsEl.id = this.telemetryOrigin +
"-anon-search-settings-compact";
+ const kXULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
let dummyItems = enginesPerRow - (oneOffCount % enginesPerRow || enginesPerRow);
for (let i = 0; i < engines.length; ++i) {
let engine = engines[i];
let button = document.createElementNS(kXULNS, "button");
button.id = this._buttonIDForEngine(engine);
let uri = "chrome://browser/skin/search-engine-placeholder.png";
if (engine.iconURI) {
uri = engine.iconURI.spec;
@@ -1504,21 +1490,137 @@
let width = remainder + buttonWidth;
let lastDummyItem = this.settingsButton.previousSibling;
lastDummyItem.setAttribute("width", width);
}
}
]]></body>
</method>
+ <!-- If a page offers more than this number of engines, the add-engines
+ menu button is shown, instead of showing the engines directly in the
+ popup. -->
+ <field name="_addEngineMenuThreshold">5</field>
+
+ <method name="_rebuildAddEngineList">
+ <body><![CDATA[
+ let list = document.getAnonymousElementByAttribute(this, "anonid",
+ "add-engines");
+ while (list.firstChild) {
+ list.firstChild.remove();
+ }
+
+ // Add a button for each engine that the page in the selected browser
+ // offers, but with the following exceptions:
+ //
+ // (1) Not when the one-offs are compact. Compact one-offs are shown in
+ // the urlbar, and the add-engine buttons span the width of the popup,
+ // so if we added all the engines that a page offers, it could break the
+ // urlbar popup by offering a ton of engines. We should probably make a
+ // smaller version of the buttons for compact one-offs.
+ //
+ // (2) Not when there are too many offered engines. The popup isn't
+ // designed to handle too many (by scrolling for example), so a page
+ // could break the popup by offering too many. Instead, add a single
+ // menu button with a submenu of all the engines.
+
+ if (this.compact || !gBrowser.selectedBrowser.engines) {
+ return;
+ }
+
+ const kXULNS =
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ let engines = gBrowser.selectedBrowser.engines;
+ let tooManyEngines = engines.length > this._addEngineMenuThreshold;
+
+ if (tooManyEngines) {
+ // Make the top-level menu button.
+ let button = document.createElementNS(kXULNS, "button");
+ list.appendChild(button);
+ button.classList.add("addengine-item");
+ button.setAttribute("anonid", "addengine-menu-button");
+ button.setAttribute("type", "menu");
+ button.setAttribute("label",
+ this.bundle.GetStringFromName("cmd_addFoundEngineMenu"));
+ button.setAttribute("crop", "end");
+ button.setAttribute("pack", "start");
+
+ // Set the menu button's image to the image of the first engine. The
+ // offered engines may have differing images, so there's no perfect
+ // choice here.
+ let engine = engines[0];
+ if (engine.icon) {
+ button.setAttribute("image", engine.icon);
+ }
+
+ // Now make the button's child menupopup.
+ list = document.createElementNS(kXULNS, "menupopup");
+ button.appendChild(list);
+ list.setAttribute("anonid", "addengine-menu");
+ list.setAttribute("position", "topright topleft");
+
+ // Events from child menupopups bubble up to the autocomplete binding,
+ // which breaks it, so prevent these events from propagating.
+ let suppressEventTypes = [
+ "popupshowing",
+ "popuphiding",
+ "popupshown",
+ "popuphidden",
+ ];
+ for (let type of suppressEventTypes) {
+ list.addEventListener(type, event => {
+ event.stopPropagation();
+ });
+ }
+ }
+
+ // Finally, add the engines to the list. If there aren't too many
+ // engines, the list is the add-engines vbox. Otherwise it's the
+ // menupopup created earlier. In the latter case, create menuitem
+ // elements instead of buttons, because buttons don't get keyboard
+ // handling for free inside menupopups.
+ let eltType = tooManyEngines ? "menuitem" : "button";
+ for (let engine of engines) {
+ let button = document.createElementNS(kXULNS, eltType);
+ button.classList.add("addengine-item");
+ button.id = this.telemetryOrigin + "-add-engine-" +
+ this._fixUpEngineNameForID(engine.title);
+ let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
+ [engine.title], 1);
+ button.setAttribute("label", label);
+ button.setAttribute("crop", "end");
+ button.setAttribute("tooltiptext", engine.uri);
+ button.setAttribute("uri", engine.uri);
+ button.setAttribute("title", engine.title);
+ if (engine.icon) {
+ button.setAttribute("image", engine.icon);
+ }
+ if (tooManyEngines) {
+ button.classList.add("menuitem-iconic");
+ } else {
+ button.setAttribute("pack", "start");
+ }
+ list.appendChild(button);
+ }
+ ]]></body>
+ </method>
+
<method name="_buttonIDForEngine">
<parameter name="engine"/>
<body><![CDATA[
return this.telemetryOrigin + "-engine-one-off-item-" +
- engine.name.replace(/ /g, '-');
+ this._fixUpEngineNameForID(engine.name);
+ ]]></body>
+ </method>
+
+ <method name="_fixUpEngineNameForID">
+ <parameter name="name"/>
+ <body><![CDATA[
+ return name.replace(/ /g, "-");
]]></body>
</method>
<method name="_buttonForEngine">
<parameter name="engine"/>
<body><![CDATA[
return document.getElementById(this._buttonIDForEngine(engine));
]]></body>
@@ -1837,16 +1939,32 @@
// The autocomplete controller should handle this case.
}
}
} else {
stopEvent = this.advanceSelection(true, true, true);
}
}
+ // If the add-engine overflow menu item is selected and the user
+ // presses the right arrow key, open the submenu. Unfortunately
+ // handling the left arrow key -- to close the popup -- isn't
+ // straightforward. Once the popup is open, it consumes all key
+ // events. Setting ignorekeys=handled on it doesn't help, since the
+ // popup handles all arrow keys. Setting ignorekeys=true on it does
+ // mean that the popup no longer consumes the left arrow key, but then
+ // it no longer handles up/down keys to select items in the popup.
+ else if (this.selectedButton &&
+ this.selectedButton.getAttribute("anonid") ==
+ "addengine-menu-button" &&
+ event.keyCode == KeyEvent.DOM_VK_RIGHT) {
+ this.selectedButton.open = true;
+ stopEvent = true;
+ }
+
if (stopEvent) {
event.preventDefault();
event.stopPropagation();
return true;
}
return false;
]]></body>
</method>
@@ -1910,42 +2028,88 @@
aOpenUILinkParams.inBackground;
let where = tabBackground ? "tab-background" : aOpenUILinkWhere;
BrowserSearch.recordOneoffSearchInTelemetry(engine, source, type,
where);
return true;
]]></body>
</method>
+ <!-- All this stuff is to make the add-engines menu button behave like an
+ actual menu. The add-engines menu button is shown when there are
+ many engines offered by the current site. -->
+ <field name="_addEngineMenuTimeoutMs">200</field>
+ <field name="_addEngineMenuTimeout">null</field>
+ <field name="_addEngineMenuShouldBeOpen">false</field>
+
+ <method name="_resetAddEngineMenuTimeout">
+ <body><![CDATA[
+ if (this._addEngineMenuTimeout) {
+ clearTimeout(this._addEngineMenuTimeout);
+ }
+ this._addEngineMenuTimeout = setTimeout(() => {
+ delete this._addEngineMenuTimeout;
+ let button = document.getAnonymousElementByAttribute(
+ this, "anonid", "addengine-menu-button"
+ );
+ button.open = this._addEngineMenuShouldBeOpen;
+ }, this._addEngineMenuTimeoutMs);
+ ]]></body>
+ </method>
+
</implementation>
<handlers>
<handler event="mousedown"><![CDATA[
+ let target = event.originalTarget;
+ if (!target.classList.contains("searchbar-engine-one-off-item")) {
+ return;
+ }
// Required to receive click events from the buttons on Linux.
event.preventDefault();
]]></handler>
<handler event="mousemove"><![CDATA[
let target = event.originalTarget;
if (target.localName != "button")
return;
// Ignore mouse events when the context menu is open.
if (this._ignoreMouseEvents)
return;
- if ((target.classList.contains("searchbar-engine-one-off-item") &&
- !target.classList.contains("dummy")) ||
+ let isOneOff =
+ target.classList.contains("searchbar-engine-one-off-item") &&
+ !target.classList.contains("dummy");
+ if (isOneOff ||
target.classList.contains("addengine-item") ||
target.classList.contains("search-setting-button")) {
this._changeVisuallySelectedButton(target);
}
]]></handler>
+ <handler event="mouseenter"><![CDATA[
+ let target = event.originalTarget;
+ if (target.getAttribute("anonid") == "addengine-menu-button") {
+ this._addEngineMenuShouldBeOpen = true;
+ this._resetAddEngineMenuTimeout();
+ return;
+ }
+ ]]></handler>
+
+ <handler event="mouseleave"><![CDATA[
+ let target = event.originalTarget;
+ if (target.getAttribute("anonid") == "addengine-menu-button") {
+ this._addEngineMenuShouldBeOpen = false;
+ this._resetAddEngineMenuTimeout();
+ return;
+ }
+ ]]></handler>
+
<handler event="mouseout"><![CDATA[
let target = event.originalTarget;
if (target.localName != "button") {
return;
}
// Don't deselect the current button if the context menu is open.
if (this._ignoreMouseEvents)
--- a/browser/components/search/test/browser.ini
+++ b/browser/components/search/test/browser.ini
@@ -6,16 +6,17 @@ support-files =
483086-2.xml
head.js
opensearch.html
test.html
testEngine.xml
testEngine_diacritics.xml
testEngine_dupe.xml
testEngine_mozsearch.xml
+ tooManyEnginesOffered.html
webapi.html
[browser_426329.js]
[browser_483086.js]
[browser_addEngine.js]
[browser_amazon.js]
[browser_amazon_behavior.js]
[browser_bing.js]
@@ -38,8 +39,9 @@ skip-if = os == "mac" # bug 967013
[browser_abouthome_behavior.js]
skip-if = true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
[browser_aboutSearchReset.js]
[browser_searchbar_openpopup.js]
skip-if = os == "linux" # Linux has different focus behaviours.
[browser_searchbar_keyboard_navigation.js]
[browser_searchbar_smallpanel_keyboard_navigation.js]
[browser_webapi.js]
+[browser_tooManyEnginesOffered.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_tooManyEnginesOffered.js
@@ -0,0 +1,88 @@
+"use strict";
+
+// This test makes sure that when a page offers many search engines, the search
+// popup shows a submenu that lists them instead of showing them in the popup
+// itself.
+
+const searchbar = document.getElementById("searchbar");
+const searchPopup = document.getElementById("PopupSearchAutoComplete");
+const oneOffsContainer =
+ document.getAnonymousElementByAttribute(searchPopup, "anonid",
+ "search-one-off-buttons");
+
+add_task(function* test() {
+ let rootDir = getRootDirectory(gTestPath);
+ let url = rootDir + "tooManyEnginesOffered.html";
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+
+ // Open the search popup.
+ let promise = promiseEvent(searchPopup, "popupshown");
+ info("Opening search panel");
+ searchbar.focus();
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ yield promise;
+
+ // Make sure it has only one add-engine menu button item.
+ let items = getOpenSearchItems();
+ Assert.equal(items.length, 1, "A single button")
+ let menuButton = items[0];
+ Assert.equal(menuButton.type, "menu", "A menu button");
+
+ // Mouse over the menu button to open it.
+ let buttonPopup = menuButton.firstChild;
+ promise = promiseEvent(buttonPopup, "popupshown");
+ EventUtils.synthesizeMouse(menuButton, 5, 5, { type: "mouseover" });
+ yield promise;
+
+ Assert.ok(menuButton.open, "Submenu should be open");
+
+ // Check the engines inside the submenu.
+ Assert.equal(buttonPopup.childNodes.length, 6, "Expected number of engines");
+ for (let i = 0; i < buttonPopup.childNodes.length; i++) {
+ let item = buttonPopup.childNodes[i];
+ Assert.equal(item.getAttribute("title"), "engine" + (i + 1),
+ "Expected engine title");
+ }
+
+ // Mouse out of the menu button to close it.
+ promise = promiseEvent(buttonPopup, "popuphidden");
+ EventUtils.synthesizeMouse(searchbar, 5, 5, { type: "mousemove" });
+ yield promise;
+
+ Assert.ok(!menuButton.open, "Submenu should be closed");
+
+ // Key up until the menu button is selected.
+ for (let button = null;
+ button != menuButton;
+ button = searchbar.textbox.popup.oneOffButtons.selectedButton) {
+ EventUtils.synthesizeKey("VK_UP", {});
+ }
+
+ // Press the Right arrow key. The submenu should open.
+ promise = promiseEvent(buttonPopup, "popupshown");
+ EventUtils.synthesizeKey("VK_RIGHT", {});
+ yield promise;
+
+ Assert.ok(menuButton.open, "Submenu should be open");
+
+ // Press the Esc key. The submenu should close.
+ promise = promiseEvent(buttonPopup, "popuphidden");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield promise;
+
+ Assert.ok(!menuButton.open, "Submenu should be closed");
+
+ gBrowser.removeCurrentTab();
+});
+
+function getOpenSearchItems() {
+ let os = [];
+
+ let addEngineList =
+ document.getAnonymousElementByAttribute(oneOffsContainer, "anonid",
+ "add-engines");
+ for (let item = addEngineList.firstChild; item; item = item.nextSibling)
+ os.push(item);
+
+ return os;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/tooManyEnginesOffered.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<link rel="search" type="application/opensearchdescription+xml" title="engine1" href="http://mochi.test:8888/browser/browser/components/search/test/engine1.xml">
+<link rel="search" type="application/opensearchdescription+xml" title="engine2" href="http://mochi.test:8888/browser/browser/components/search/test/engine2.xml">
+<link rel="search" type="application/opensearchdescription+xml" title="engine3" href="http://mochi.test:8888/browser/browser/components/search/test/engine3.xml">
+<link rel="search" type="application/opensearchdescription+xml" title="engine4" href="http://mochi.test:8888/browser/browser/components/search/test/engine4.xml">
+<link rel="search" type="application/opensearchdescription+xml" title="engine5" href="http://mochi.test:8888/browser/browser/components/search/test/engine5.xml">
+<link rel="search" type="application/opensearchdescription+xml" title="engine6" href="http://mochi.test:8888/browser/browser/components/search/test/engine6.xml">
+</head>
+<body></body>
+</html>
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -238,28 +238,35 @@ menuitem[cmd="cmd_clearhistory"][disable
border-top: 1px solid var(--panel-separator-color);
}
.addengine-item[selected] {
background-color: Highlight;
color: HighlightText;
}
+.addengine-item[type=menu][selected],
+.addengine-item[type=menu][open] {
+ color: inherit;
+ background-color: var(--arrowpanel-dimmed-further);
+}
+
.addengine-icon {
width: 16px;
}
.addengine-badge {
width: 16px;
height: 16px;
margin: -7px -9px 7px 9px;
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
-.addengine-item > .button-box > .button-text {
+.addengine-item > .button-box > .button-text,
+.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder.png");
}
@@ -269,16 +276,22 @@ menuitem[cmd="cmd_clearhistory"][disable
list-style-image: url("chrome://browser/skin/badge-add-engine@2x.png");
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder@2x.png");
}
}
+.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
+ display: -moz-box;
+ -moz-appearance: menuarrow !important;
+ list-style-image: none;
+}
+
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell-text {
padding-inline-start: 4px;
}
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -219,28 +219,35 @@
border-top: 1px solid var(--panel-separator-color);
}
.addengine-item[selected] {
background-color: Highlight;
color: HighlightText;
}
+.addengine-item[type=menu][selected],
+.addengine-item[type=menu][open] {
+ color: inherit;
+ background-color: var(--arrowpanel-dimmed-further);
+}
+
.addengine-icon {
width: 16px;
}
.addengine-badge {
width: 16px;
height: 16px;
margin: -7px -9px 7px 9px;
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
-.addengine-item > .button-box > .button-text {
+.addengine-item > .button-box > .button-text,
+.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder.png");
}
@@ -250,16 +257,22 @@
list-style-image: url("chrome://browser/skin/badge-add-engine@2x.png");
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder@2x.png");
}
}
+.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
+ display: -moz-box;
+ -moz-appearance: menuarrow !important;
+ list-style-image: none;
+}
+
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-image {
padding-inline-start: 4px;
padding-inline-end: 2px;
width: 14px;
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -231,28 +231,35 @@
border-top: 1px solid var(--panel-separator-color);
}
.addengine-item[selected] {
background-color: Highlight;
color: HighlightText;
}
+.addengine-item[type=menu][selected],
+.addengine-item[type=menu][open] {
+ color: inherit;
+ background-color: var(--arrowpanel-dimmed-further);
+}
+
.addengine-icon {
width: 16px;
}
.addengine-badge {
width: 16px;
height: 16px;
margin: -7px -9px 7px 9px;
list-style-image: url("chrome://browser/skin/badge-add-engine.png");
}
-.addengine-item > .button-box > .button-text {
+.addengine-item > .button-box > .button-text,
+.addengine-item[type=menu] > .button-box > .box-inherit > .button-text {
-moz-box-flex: 1;
text-align: start;
padding-inline-start: 10px;
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder.png");
}
@@ -262,16 +269,22 @@
list-style-image: url("chrome://browser/skin/badge-add-engine@2x.png");
}
.addengine-item:not([image]) {
list-style-image: url("chrome://browser/skin/search-engine-placeholder@2x.png");
}
}
+.addengine-item[type=menu] > .button-box > .button-menu-dropmarker {
+ display: -moz-box;
+ -moz-appearance: menuarrow !important;
+ list-style-image: none;
+}
+
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
border-top: none !important;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell-text {
padding-inline-start: 4px;
}