Bug 1088660 - Improve the search bar UI to support one-off searches, r=felipe.

This commit is contained in:
Florian Quèze 2014-11-18 00:59:11 +01:00
parent d06778dbd3
commit 1076501bfb
39 changed files with 1249 additions and 27 deletions

View File

@ -422,6 +422,8 @@ pref("browser.search.update.interval", 21600);
// enable search suggestions by default
pref("browser.search.suggest.enabled", true);
pref("browser.search.showOneOffButtons", false);
#ifdef MOZ_OFFICIAL_BRANDING
// {moz:official} expands to "official"
pref("browser.search.official", true);

View File

@ -415,6 +415,15 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup");
}
#PopupSearchAutoComplete {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#browser-search-autocomplete-result-popup");
margin-left: -23px;
}
searchbar[oneoffui] {
-moz-binding: url("chrome://browser/content/search/search.xml#searchbar-flare") !important;
}
#PopupAutoCompleteRichResult {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#urlbar-rich-result-popup");
}

View File

@ -140,6 +140,9 @@
<!-- for search and content formfill/pw manager -->
<panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
<!-- for search with one-off buttons -->
<panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
<!-- for url bar autocomplete -->
<panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>

View File

@ -951,6 +951,175 @@
</implementation>
</binding>
<!-- Note: this binding is applied to the autocomplete popup used in the Search bar -->
<binding id="browser-search-autocomplete-result-popup" extends="chrome://browser/content/urlbarBindings.xml#browser-autocomplete-result-popup">
<resources>
<stylesheet src="chrome://browser/skin/searchbar.css"/>
</resources>
<content ignorekeys="true" level="top" consumeoutsideclicks="false">
<xul:hbox class="search-panel-header search-panel-current-engine">
<xul:image class="searchbar-engine-image" xbl:inherits="src"/>
<xul:label anonid="searchbar-engine-name"/>
</xul:hbox>
<xul:tree anonid="tree" class="autocomplete-tree plain search-panel-tree" hidecolumnpicker="true" flex="1" seltype="single">
<xul:treecols anonid="treecols">
<xul:treecol id="treecolAutoCompleteValue" class="autocomplete-treecol" flex="1" overflow="true"/>
</xul:treecols>
<xul:treechildren class="autocomplete-treebody"/>
</xul:tree>
<xul:hbox anonid="search-panel-one-offs-header"
class="search-panel-header search-panel-current-input">
<xul:label anonid="searchbar-oneoffheader-before" value="Search for "/>
<xul:label anonid="searchbar-oneoffheader-searchtext" flex="1" crop="end" class="search-panel-input-value"/>
<xul:label anonid="searchbar-oneoffheader-after" flex="10000" value=" on:"/>
</xul:hbox>
<xul:description anonid="search-panel-one-offs" class="search-panel-one-offs"/>
<xul:button anonid="search-settings"
oncommand="openPreferences('paneSearch')"
class="search-setting-button search-panel-header"
label="Change Search Settings"/>
</content>
<handlers>
<handler event="popupshowing"><![CDATA[
let currentEngine = Services.search.currentEngine;
let uri = currentEngine.iconURI;
if (uri)
uri = uri.spec;
this.setAttribute("src", PlacesUtils.getImageURLForResolution(window, uri));
document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
.setAttribute("value", currentEngine.name + " Search");
let headerSearchText =
document.getAnonymousElementByAttribute(this, "anonid",
"searchbar-oneoffheader-searchtext");
let textbox = document.getElementById("searchbar").textbox;
let keyPressHandler = function() {
headerSearchText.setAttribute("value", textbox.value);
};
textbox.addEventListener("keyup", keyPressHandler);
this.addEventListener("popuphiding", function hiding() {
textbox.removeEventListener("keyup", keyPressHandler);
this.removeEventListener("popuphiding", hiding);
});
keyPressHandler();
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let list = document.getAnonymousElementByAttribute(this, "anonid",
"search-panel-one-offs")
while (list.firstChild)
list.firstChild.remove();
let hiddenList;
try {
let pref =
Services.prefs.getCharPref("browser.search.hiddenOneOffs");
hiddenList = pref ? pref.split(",") : [];
} catch(e) {
hiddenList = [];
}
let engines = Services.search.getVisibleEngines()
.filter(e => e.name != currentEngine.name &&
hiddenList.indexOf(e.name) == -1);
let header = document.getAnonymousElementByAttribute(this, "anonid",
"search-panel-one-offs-header")
header.collapsed = list.collapsed = !engines.length;
if (!engines.length)
return;
let panel = document.getElementById("PopupSearchAutoComplete");
let minWidth = parseInt(panel.width) + 23;
panel.setAttribute("style", "min-width: " + minWidth + "px");
// 49px is the min-width of each search engine button,
// adapt this const when changing the css.
// It's actually 48px + 1px of right border.
// The + 1 is because the last button doesn't have a right border.
let panelWidth = parseInt(panel.clientWidth);
let enginesPerRow = Math.floor((panelWidth + 1) / 49);
let buttonWidth = Math.floor(panelWidth / enginesPerRow);
// There will be an emtpy area of:
// panelWidth - enginesPerRow * buttonWidth px
// at the end of each row.
// If the <description> tag with the list of search engines doesn't have
// a fixed height, the panel will be sized incorrectly, causing the bottom
// of the suggestion <tree> to be hidden.
let rowCount = Math.ceil(engines.length / enginesPerRow);
let height = rowCount * 33; // 32px per row, 1px border.
list.setAttribute("height", height + "px");
let dummyItems = enginesPerRow - (engines.length % enginesPerRow || enginesPerRow);
for (let i = 0; i < engines.length; ++i) {
let engine = engines[i];
if (!engine.iconURI)
continue;
let button = document.createElementNS(kXULNS, "button");
let uri = PlacesUtils.getImageURLForResolution(window, engine.iconURI.spec);
button.setAttribute("label", engine.name);
button.setAttribute("image", uri);
button.setAttribute("class", "searchbar-engine-one-off-item");
button.setAttribute("tooltiptext", engine.name);
button.setAttribute("width", buttonWidth);
button.engine = engine;
if ((i + 1) % enginesPerRow == 0)
button.classList.add("last-of-row");
if (i >= engines.length + dummyItems - enginesPerRow)
button.classList.add("last-row");
list.appendChild(button);
}
while (dummyItems) {
let button = document.createElementNS(kXULNS, "button");
button.setAttribute("class", "searchbar-engine-one-off-item dummy last-row");
button.setAttribute("width", buttonWidth);
if (!--dummyItems)
button.classList.add("last-of-row");
list.appendChild(button);
}
]]></handler>
<handler event="mousedown"><![CDATA[
// Required to receive click events from the buttons on Linux.
event.preventDefault();
]]></handler>
<handler event="mouseover"><![CDATA[
let target = event.originalTarget;
if (target.localName == "button" &&
target.classList.contains("searchbar-engine-one-off-item") &&
!target.classList.contains("dummy")) {
let list = document.getAnonymousElementByAttribute(this, "anonid",
"search-panel-one-offs")
for (let button = list.firstChild; button; button = button.nextSibling)
button.removeAttribute("selected");
}
]]></handler>
<handler event="click"><![CDATA[
if (event.button == 2)
return; // ignore right clicks.
let button = event.originalTarget;
if (button.localName != "button" || !button.engine)
return;
let searchbar = document.getElementById("searchbar");
searchbar.handleSearchCommand(event, button.engine);
]]></handler>
</handlers>
</binding>
<binding id="urlbar-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
<implementation>
<field name="_maxResults">0</field>

View File

@ -67,6 +67,13 @@ add_task(function*() {
yield PanelUI.show();
yield waitForCondition(() => "value" in searchbar && searchbar.value === "");
// Focusing a non-empty searchbox will cause us to open the
// autocomplete panel and search for suggestions, which would
// trigger network requests. Temporarily disable suggestions.
let suggestEnabled =
Services.prefs.getBoolPref("browser.search.suggest.enabled");
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
searchbar.value = "foo";
searchbar.focus();
// Reaching into this context menu is pretty evil, but hey... it's a test.
@ -83,12 +90,17 @@ add_task(function*() {
EventUtils.synthesizeMouseAtCenter(selectAll, {});
yield contextMenuHidden;
// Hide the suggestion panel.
searchbar.textbox.popup.hidePopup();
ok(isPanelUIOpen(), "Panel should still be open");
let hiddenPanelPromise = promisePanelHidden(window);
EventUtils.synthesizeKey("VK_ESCAPE", {});
yield hiddenPanelPromise;
ok(!isPanelUIOpen(), "Panel should no longer be open");
Services.prefs.setBoolPref("browser.search.suggest.enabled", suggestEnabled);
});
add_task(function*() {
@ -121,4 +133,3 @@ registerCleanupFunction(function() {
PanelUI.hide();
}
});

View File

@ -14,3 +14,5 @@ browser.jar:
content/browser/preferences/in-content/content.js
content/browser/preferences/in-content/sync.js
content/browser/preferences/in-content/security.js
content/browser/preferences/in-content/search.css
content/browser/preferences/in-content/search.js

View File

@ -24,6 +24,7 @@ function init_all() {
gSubDialog.init();
gMainPane.init();
gSearchPane.init();
gPrivacyPane.init();
gAdvancedPane.init();
gApplicationsPane.init();

View File

@ -14,6 +14,7 @@
<?xml-stylesheet
href="chrome://browser/content/preferences/handlers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/in-content/search.css"?>
<!DOCTYPE page [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
@ -96,6 +97,16 @@
<label class="category-name" flex="1">&paneGeneral.title;</label>
</richlistitem>
<richlistitem id="category-search"
class="category"
value="paneSearch"
helpTopic="prefs-main"
tooltiptext="Search"
align="center">
<image class="category-icon"/>
<label class="category-name" flex="1">Search</label>
</richlistitem>
<richlistitem id="category-content"
class="category"
value="paneContent"
@ -162,6 +173,7 @@
<vbox class="main-content" flex="1">
<prefpane id="mainPrefPane">
#include main.xul
#include search.xul
#include privacy.xul
#include advanced.xul
#include applications.xul

View File

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#oneClickProvidersList richlistitem {
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
}
.checkbox-label-box {
-moz-box-align: center;
}
.checkbox-icon {
-moz-margin-end: 8px;
}

View File

@ -0,0 +1,73 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var gSearchPane = {
init: function ()
{
if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
document.getElementById("category-search").hidden = true;
if (document.location.hash == "#search")
document.location.hash = "";
return;
}
let list = document.getElementById("defaultEngine");
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
let item = list.appendItem(e.name);
item.setAttribute("class", "menuitem-iconic");
item.setAttribute("image", e.iconURI.spec);
item.engine = e;
if (e.name == currentEngine)
list.selectedItem = item;
});
this.displayOneClickEnginesList();
document.getElementById("oneClickProvidersList")
.addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
},
displayOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
let hiddenList = pref ? pref.split(",") : [];
while (richlistbox.firstChild)
richlistbox.firstChild.remove();
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
if (e.name == currentEngine)
return;
let item = document.createElement("richlistitem");
item.setAttribute("label", e.name);
if (hiddenList.indexOf(e.name) == -1)
item.setAttribute("checked", "true");
item.setAttribute("src", e.iconURI.spec);
richlistbox.appendChild(item);
});
},
saveOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let hiddenList = [];
for (let child of richlistbox.childNodes) {
if (!child.checked)
hiddenList.push(child.getAttribute("label"));
}
document.getElementById("browser.search.hiddenOneOffs").value =
hiddenList.join(",");
},
setDefaultEngine: function () {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
this.displayOneClickEnginesList();
}
};

View File

@ -0,0 +1,48 @@
<preferences id="searchPreferences">
<!-- Suggest -->
<preference id="browser.search.suggest.enabled"
name="browser.search.suggest.enabled"
type="bool"/>
<!-- One off providers -->
<preference id="browser.search.hiddenOneOffs"
name="browser.search.hiddenOneOffs"
type="string"/>
</preferences>
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/search.js"/>
<hbox id="header-search"
class="header"
hidden="true"
data-category="paneSearch">
<label class="header-name">Search</label>
</hbox>
<!-- Default Search Engine -->
<groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
<caption label="Default Search Engine"/>
<label>Choose your default search engine. &brandShortName; uses it in the location bar, search bar, and start pages.</label>
<menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
<menupopup/>
</menulist>
<checkbox id="suggestionsInSearchFieldsCheckbox"
label="Provide search suggestions"
accesskey="s"
preference="browser.search.suggest.enabled"/>
</groupbox>
<groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
<caption label="One-click search engines"/>
<label>The search bar lets you search alternate engines directly. Choose which ones to display.</label>
<richlistbox id="oneClickProvidersList" style="min-height: 50px;"/>
<hbox pack="end" style="margin-bottom: 2em">
<label id="addEngines" class="text-link" value="Add more search providers…"
onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
</hbox>
</groupbox>

View File

@ -31,6 +31,7 @@ browser.jar:
* content/browser/preferences/permissions.xul
* content/browser/preferences/permissions.js
* content/browser/preferences/preferences.xul
content/browser/preferences/preferences.js
content/browser/preferences/privacy.xul
* content/browser/preferences/privacy.js
content/browser/preferences/sanitize.xul
@ -43,6 +44,9 @@ browser.jar:
content/browser/preferences/sync.xul
content/browser/preferences/sync.js
#endif
content/browser/preferences/search.xul
content/browser/preferences/search.css
content/browser/preferences/search.js
* content/browser/preferences/tabs.xul
* content/browser/preferences/tabs.js
* content/browser/preferences/translation.xul

View File

@ -0,0 +1,19 @@
/* - This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
- You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
addEventListener("load", function onLoad() {
removeEventListener("load", onLoad);
let pane =
document.getAnonymousElementByAttribute(document.documentElement,
"pane", "paneSearch");
pane.hidden = true;
if (pane.selected)
document.documentElement.showPane(document.getElementById("paneMain"));
});
}

View File

@ -16,6 +16,7 @@
-->
<?xml-stylesheet href="chrome://browser/content/preferences/handlers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
<?xml-stylesheet href="chrome://browser/content/preferences/search.css"?>
<!DOCTYPE prefwindow [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
@ -66,6 +67,8 @@
src="chrome://browser/content/preferences/main.xul"/>
<prefpane id="paneTabs" label="&paneTabs.title;"
src="chrome://browser/content/preferences/tabs.xul"/>
<prefpane id="paneSearch" label="Search"
src="chrome://browser/content/preferences/search.xul"/>
<prefpane id="paneContent" label="&paneContent.title;"
src="chrome://browser/content/preferences/content.xul"/>
<prefpane id="paneApplications" label="&paneApplications.title;"
@ -81,6 +84,8 @@
<prefpane id="paneAdvanced" label="&paneAdvanced.title;"
src="chrome://browser/content/preferences/advanced.xul"/>
<script type="application/javascript"
src="chrome://browser/content/preferences/preferences.js"/>
#ifdef XP_MACOSX
#include ../../base/content/browserMountPoints.inc
#endif

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#oneClickProvidersList richlistitem {
-moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
-moz-padding-start: 5px;
height: 22px; /* setting the height of checkboxes is required to let the
window auto-sizing code work. */
}
.checkbox-label-box {
-moz-box-align: center;
}
.checkbox-icon {
margin: 3px 3px;
}

View File

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var gSearchPane = {
init: function ()
{
let list = document.getElementById("defaultEngine");
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
let item = list.appendItem(e.name);
item.setAttribute("class", "menuitem-iconic");
item.setAttribute("image", e.iconURI.spec);
item.engine = e;
if (e.name == currentEngine)
list.selectedItem = item;
});
this.displayOneClickEnginesList();
document.getElementById("oneClickProvidersList")
.addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
},
displayOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let pref = document.getElementById("browser.search.hiddenOneOffs").value;
let hiddenList = pref ? pref.split(",") : [];
while (richlistbox.firstChild)
richlistbox.firstChild.remove();
let currentEngine = Services.search.currentEngine.name;
Services.search.getVisibleEngines().forEach(e => {
if (e.name == currentEngine)
return;
let item = document.createElement("richlistitem");
item.setAttribute("label", e.name);
if (hiddenList.indexOf(e.name) == -1)
item.setAttribute("checked", "true");
item.setAttribute("src", e.iconURI.spec);
richlistbox.appendChild(item);
});
},
saveOneClickEnginesList: function () {
let richlistbox = document.getElementById("oneClickProvidersList");
let hiddenList = [];
for (let child of richlistbox.childNodes) {
if (!child.checked)
hiddenList.push(child.getAttribute("label"));
}
document.getElementById("browser.search.hiddenOneOffs").value =
hiddenList.join(",");
},
setDefaultEngine: function () {
Services.search.currentEngine =
document.getElementById("defaultEngine").selectedItem.engine;
this.displayOneClickEnginesList();
}
};

View File

@ -0,0 +1,61 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE overlay [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
]>
<overlay id="SearchPaneOverlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<prefpane id="paneSearch" helpTopic="prefs-search"
onpaneload="gSearchPane.init();">
<preferences id="searchPreferences">
<!-- Suggest -->
<preference id="browser.search.suggest.enabled"
name="browser.search.suggest.enabled"
type="bool"/>
<!-- One off providers -->
<preference id="browser.search.hiddenOneOffs"
name="browser.search.hiddenOneOffs"
type="string"/>
</preferences>
<script type="application/javascript" src="chrome://browser/content/preferences/search.js"/>
<!-- Default Search Engine -->
<groupbox id="defaultEngineGroup" align="start">
<caption label="Default Search Engine"/>
<label>Choose your default search engine. &brandShortName; uses it in the location bar, search bar, and start pages.</label>
<menulist id="defaultEngine" oncommand="gSearchPane.setDefaultEngine();">
<menupopup/>
</menulist>
<checkbox id="suggestionsInSearchFieldsCheckbox"
label="Provide search suggestions"
accesskey="s"
preference="browser.search.suggest.enabled"/>
</groupbox>
<groupbox id="oneClickSearchProvidersGroup">
<caption label="One-click search engines"/>
<label>The search bar lets you search alternate engines directly. Choose which ones to display.</label>
<richlistbox id="oneClickProvidersList" style="min-height: 50px;"/>
<hbox pack="end" style="margin-bottom: 2em">
<label id="addEngines" class="text-link" value="Add more search providers…"
onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
</hbox>
</groupbox>
</prefpane>
</overlay>

View File

@ -73,6 +73,12 @@
<implementation implements="nsIObserver">
<constructor><![CDATA[
if (!this.hasAttribute("oneoffui") &&
Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
this.setAttribute("oneoffui", "true");
return;
}
if (this.parentNode.parentNode.localName == "toolbarpaletteitem")
return;
// Make sure we rebuild the popup in onpopupshowing
@ -100,6 +106,11 @@
]]></constructor>
<destructor><![CDATA[
this.destroy();
]]></destructor>
<method name="destroy">
<body><![CDATA[
if (this._initialized) {
this._initialized = false;
@ -112,7 +123,8 @@
// the world. But make sure it's actually pointing to us.
if (this._textbox.mController.input == this)
this._textbox.mController.input = null;
]]></destructor>
]]></body>
</method>
<field name="_stringBundle">document.getAnonymousElementByAttribute(this,
"anonid", "searchbar-stringbundle");</field>
@ -304,7 +316,12 @@
var name = this.currentEngine.name;
var text = this._stringBundle.getFormattedString("searchtip", [name]);
this._textbox.placeholder = name;
if (Services.prefs.getBoolPref("browser.search.showOneOffButtons"))
this._textbox.placeholder = "Search";
else
this._textbox.placeholder = name;
this._textbox.label = text;
this._textbox.tooltipText = text;
]]></body>
@ -449,6 +466,7 @@
<method name="handleSearchCommand">
<parameter name="aEvent"/>
<parameter name="aEngine"/>
<body><![CDATA[
var textBox = this._textbox;
var textValue = textBox.value;
@ -465,13 +483,14 @@
where = "tab";
}
this.doSearch(textValue, where);
this.doSearch(textValue, where, aEngine);
]]></body>
</method>
<method name="doSearch">
<parameter name="aData"/>
<parameter name="aWhere"/>
<parameter name="aEngine"/>
<body><![CDATA[
var textBox = this._textbox;
@ -485,10 +504,11 @@
Components.utils.reportError("Saving search to form history failed: " + aError.message);
}});
}
let engine = aEngine || this.currentEngine;
var submission = engine.getSubmission(aData, null, "searchbar");
BrowserSearch.recordSearchInHealthReport(engine, "searchbar");
// null parameter below specifies HTML response for search
var submission = this.currentEngine.getSubmission(aData, null, "searchbar");
BrowserSearch.recordSearchInHealthReport(this.currentEngine, "searchbar");
openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
]]></body>
</method>
@ -532,10 +552,104 @@
// Speculatively connect to the current engine's search URI (and
// suggest URI, if different) to reduce request latency
this.currentEngine.speculativeConnect({window: window});
if (this._textbox.value && !this._textbox.open) {
this._textbox.showHistoryPopup();
// showHistoryPopup does a startSearch("") call, ensure the
// controller handles the text from the input box instead:
this._textbox.mController.handleText();
}
]]></handler>
</handlers>
</binding>
<binding id="searchbar-flare" extends="chrome://browser/content/search/search.xml#searchbar">
<resources>
<stylesheet src="chrome://browser/content/search/searchbarBindings.css"/>
<stylesheet src="chrome://browser/skin/searchbar.css"/>
</resources>
<content>
<xul:stringbundle src="chrome://browser/locale/search.properties"
anonid="searchbar-stringbundle"/>
<!--
There is a dependency between "maxrows" attribute and
"SuggestAutoComplete._historyLimit" (nsSearchSuggestions.js). Changing
one of them requires changing the other one.
-->
<xul:textbox class="searchbar-textbox"
anonid="searchbar-textbox"
type="autocomplete"
flex="1"
autocompletepopup="PopupSearchAutoComplete"
autocompletesearch="search-autocomplete"
autocompletesearchparam="searchbar-history"
maxrows="10"
completeselectedindex="true"
tabscrolling="true"
xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
<!--
Empty <box> to properly position the icon within the autocomplete
binding's anonymous children (the autocomplete binding positions <box>
children differently)
-->
<xul:box>
<xul:hbox class="searchbar-search-button-container">
<xul:image class="searchbar-search-button"
anonid="searchbar-search-button"
tooltiptext="&searchEndCap.label;"/>
</xul:hbox>
<xul:button class="searchbar-engine-button"
type="menu"
anonid="searchbar-engine-button">
<xul:image class="searchbar-engine-image" xbl:inherits="src"/>
<xul:image class="searchbar-dropmarker-image"/>
<xul:menupopup class="searchbar-popup"
anonid="searchbar-popup">
<xul:menuseparator/>
<xul:menuitem class="open-engine-manager"
anonid="open-engine-manager"
label="&cmd_engineManager.label;"
oncommand="openManager(event);"/>
</xul:menupopup>
</xul:button>
</xul:box>
<xul:hbox class="search-go-container">
<xul:image class="search-go-button" hidden="true"
anonid="search-go-button"
onclick="handleSearchCommand(event);"
tooltiptext="&searchEndCap.label;"/>
</xul:hbox>
</xul:textbox>
</content>
<implementation>
<destructor><![CDATA[
// For some reason the destructor of the base binding is called
// automatically when the window is closed, but now when the node
// is removed from the DOM.
this.destroy();
]]></destructor>
<method name="selectEngine">
<body><![CDATA[
// Override this method to avoid accidentally changing the default
// engine using the keyboard shortcuts of the old UI.
]]></body>
</method>
<method name="updateGoButtonVisibility">
<body><![CDATA[
document.getAnonymousElementByAttribute(this, "anonid",
"search-go-button")
.hidden = !this._textbox.value;
]]></body>
</method>
</implementation>
<handlers>
<handler event="input" action="this.updateGoButtonVisibility();"/>
<handler event="drop" action="this.updateGoButtonVisibility();"/>
</handlers>
</binding>
<binding id="searchbar-textbox"
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation implements="nsIObserver">
@ -731,11 +845,145 @@
<parameter name="aEvent"/>
<body><![CDATA[
var evt = aEvent || this.mEnterEvent;
document.getBindingParent(this).handleSearchCommand(evt);
let engine;
let oneOff = this.getSelectedOneOff();
if (oneOff)
engine = oneOff.engine;
document.getBindingParent(this).handleSearchCommand(evt, engine);
this.mEnterEvent = null;
]]></body>
</method>
<method name="getSelectedOneOff">
<body><![CDATA[
let list = document.getAnonymousElementByAttribute(this.popup, "anonid",
"search-panel-one-offs");
if (!list)
return null;
for (let button = list.firstChild; button; button = button.nextSibling) {
if (button.hasAttribute("selected"))
return button;
}
return null;
]]></body>
</method>
<method name="handleKeyboardNavigation">
<parameter name="aEvent"/>
<body><![CDATA[
// XXXFlorian This method could likely be shortened with a helper
// handling moving the selection within the one-off list and
// returning a boolean indicating if the event should be stopped.
let popup = this.popup;
if (!popup.popupOpen)
return;
let list = document.getAnonymousElementByAttribute(popup, "anonid",
"search-panel-one-offs");
if (!list)
return;
let selectedButton = this.getSelectedOneOff();
// If the last suggestion is selected, DOWN selects the first one-off.
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN &&
popup.selectedIndex + 1 == popup.view.rowCount) {
if (selectedButton)
selectedButton.removeAttribute("selected");
selectedButton = list.firstChild;
if (selectedButton)
selectedButton.setAttribute("selected", "true");
}
// If no suggestion is selected and a one-off is selected,
// UP and DOWN cycle through one-off buttons.
if (popup.selectedIndex == -1 && selectedButton &&
(aEvent.keyCode == KeyEvent.DOM_VK_DOWN ||
aEvent.keyCode == KeyEvent.DOM_VK_UP)) {
selectedButton.removeAttribute("selected");
if (aEvent.keyCode == KeyEvent.DOM_VK_DOWN)
selectedButton = selectedButton.nextSibling;
else
selectedButton = selectedButton.previousSibling;
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
if (selectedButton) {
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();
}
else {
// Set the selectedIndex to something that will make
// handleKeyNavigation (called by autocomplete.xml's onKeyPress
// method) reset the text field value to what the user typed.
if (aEvent.keyCode == KeyEvent.DOM_VK_UP)
popup.selectedIndex = popup.view.rowCount;
else
popup.selectedIndex = popup.view.rowCount - 1;
}
}
// If nothing is selected, UP selects the last one-off button.
if (aEvent.keyCode == KeyEvent.DOM_VK_UP &&
popup.selectedIndex == -1 && !selectedButton) {
selectedButton = list.lastChild;
while (selectedButton.classList.contains("dummy"))
selectedButton = selectedButton.previousSibling;
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();
}
if (aEvent.keyCode == KeyEvent.DOM_VK_TAB) {
if (selectedButton) {
// TAB cycles though the list of one-off buttons.
selectedButton.removeAttribute("selected");
if (aEvent.shiftKey)
selectedButton = selectedButton.previousSibling;
else
selectedButton = selectedButton.nextSibling;
// Avoid selecting dummy buttons.
if (selectedButton && selectedButton.classList.contains("dummy"))
selectedButton = null;
// If we are out of the list, revert the text field to what the user typed.
if (!selectedButton) {
// Set the selectedIndex to something that will make
// handleKeyNavigation (called by autocomplete.xml's onKeyPress
// method) reset the text field value to what the user typed.
popup.selectedIndex = aEvent.shiftKey ? 0 : popup.view.rowCount - 1;
return;
}
}
else {
// If no selection, select the first or last one-off button.
if (aEvent.shiftKey) {
selectedButton = list.lastChild;
while (selectedButton.classList.contains("dummy"))
selectedButton = selectedButton.previousSibling;
}
else {
selectedButton = list.firstChild;
}
}
selectedButton.setAttribute("selected", "true");
aEvent.preventDefault();
aEvent.stopPropagation();
}
]]></body>
</method>
<!-- nsIController -->
<field name="searchbarController" readonly="true"><![CDATA[({
_self: this,
@ -772,6 +1020,9 @@
</implementation>
<handlers>
<handler event="keypress" phase="capturing"
action="return this.handleKeyboardNavigation(event);"/>
<handler event="keypress" keycode="VK_UP" modifiers="accel"
phase="capturing"
action="document.getBindingParent(this).selectEngine(event, false);"/>

View File

@ -1,7 +1,10 @@
function test() {
var searchBar = BrowserSearch.searchBar;
ok(searchBar, "got search bar");
if (searchBar.getAttribute("oneoffui"))
return; // The oneoffui removes the menu that's being tested here.
searchBar.focus();
var pbo = searchBar._popup.popupBoxObject;

View File

@ -228,11 +228,10 @@ add_task(function testShiftMiddleClick() {
add_task(function testDropText() {
yield prepareTest();
let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
// drop on the search button so that we don't need to worry about the
// default handlers for textboxes.
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
yield promisePreventPopup;
let searchButton = document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-search-button");
ChromeUtils.synthesizeDrop(searchButton, searchButton, [[ {type: "text/plain", data: "Some Text" } ]], "copy", window);
let event = yield promiseOnLoad();
is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropText opened correct search page");
is(searchBar.value, "Some Text", "drop text/plain on searchbar");
@ -240,9 +239,8 @@ add_task(function testDropText() {
add_task(function testDropInternalText() {
yield prepareTest();
let promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
yield promisePreventPopup;
let searchButton = document.getAnonymousElementByAttribute(searchBar, "anonid", "searchbar-search-button");
ChromeUtils.synthesizeDrop(searchButton, searchButton, [[ {type: "text/x-moz-text-internal", data: "More Text" } ]], "copy", window);
let event = yield promiseOnLoad();
is(event.originalTarget.URL, expectedURL(searchBar.value), "testDropInternalText opened correct search page");
is(searchBar.value, "More Text", "drop text/x-moz-text-internal on searchbar");
@ -251,9 +249,7 @@ add_task(function testDropInternalText() {
// were merged so that if testDropInternalText failed it wouldn't cause testDropLink
// to fail unexplainably.
yield prepareTest();
promisePreventPopup = promiseEvent(searchBar, "popupshowing", true);
ChromeUtils.synthesizeDrop(searchBar.searchButton, searchBar.searchButton, [[ {type: "text/uri-list", data: "http://www.mozilla.org" } ]], "copy", window);
yield promisePreventPopup;
is(searchBar.value, "More Text", "drop text/uri-list on searchbar shouldn't change anything");
});
@ -268,6 +264,9 @@ add_task(function testRightClick() {
deferred.resolve();
}, 5000);
yield deferred.promise;
// The click in the searchbox focuses it, which opens the suggestion
// panel. Clean up after ourselves.
searchBar.textbox.popup.hidePopup();
});
add_task(function testSearchHistory() {
@ -302,4 +301,3 @@ add_task(function asyncCleanup() {
content.location.href = "about:blank";
yield promiseRemoveEngine();
});

View File

@ -5,3 +5,4 @@
#filter substitution
pref("general.useragent.locale", "@AB_CD@");
pref("browser.search.showOneOffButtons", true);

View File

@ -221,8 +221,13 @@ let tests = [
gContentAPI.showHighlight("urlbar");
waitForElementToBeVisible(highlight, () => {
let searchbar = document.getElementById("searchbar");
if (searchbar.getAttribute("oneoffui")) {
done();
return; // The oneoffui removes the menu that's being tested here.
}
gContentAPI.showMenu("searchEngines", function() {
let searchbar = document.getElementById("searchbar");
isnot(searchbar, null, "Should have found searchbar");
let searchPopup = document.getAnonymousElementByAttribute(searchbar,
"anonid",

View File

@ -62,6 +62,7 @@ browser.jar:
skin/classic/browser/privatebrowsing-mask.png
skin/classic/browser/reload-stop-go.png
skin/classic/browser/searchbar.css
skin/classic/browser/search-pref.png (../shared/search-pref.png)
skin/classic/browser/Secure.png
skin/classic/browser/Security-broken.png
skin/classic/browser/setDesktopBackground.css

View File

@ -20,6 +20,14 @@ radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px)
}
#BrowserPreferences radio[pane=paneSearch] {
list-style-image: url("chrome://browser/skin/search-pref.png");
}
.checkbox-check {
-moz-appearance: checkbox;
}
radio[pane=paneContent] {
-moz-image-region: rect(0px, 96px, 32px, 64px)
}

View File

@ -67,3 +67,139 @@ menuitem[cmd="cmd_clearhistory"][disabled] {
list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
}
.searchbar-search-button-container {
-moz-box-align: center;
padding: 2px 3px;
-moz-padding-end: 2px;
}
.searchbar-search-button {
list-style-image: url("chrome://global/skin/icons/Search-glass.png");
-moz-image-region: rect(0px 16px 16px 0px);
}
searchbar[oneoffui] .search-go-button {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 42px, 14px, 28px);
}
searchbar[oneoffui] .search-go-button:hover {
-moz-image-region: rect(14px, 42px, 28px, 28px);
}
searchbar[oneoffui] .search-go-button:hover:active {
-moz-image-region: rect(28px, 42px, 42px, 28px);
}
searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
.search-panel-current-engine {
border-top: none !important;
border-bottom: 1px solid #ccc;
}
.search-panel-header {
font-size: 10px;
font-weight: normal;
background-color: rgb(245, 245, 245);
border-top: 1px solid #ccc;
margin: 0 1px;
padding: 3px 5px;
color: #666;
}
.search-panel-current-input > label {
margin: 0 0 !important;
}
.search-panel-input-value {
color: black;
}
.search-panel-one-offs {
margin: 0 0 !important;
border-top: 1px solid #ccc;
}
.searchbar-engine-one-off-item {
-moz-appearance: none;
display: inline-block;
border: none;
min-width: 48px;
height: 32px;
margin: 0 0;
padding: 0 0;
background: none;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: right center;
}
.searchbar-engine-one-off-item:not(.last-row) {
box-sizing: padding-box;
border-bottom: 1px solid #ccc;
}
.searchbar-engine-one-off-item.last-of-row {
background-image: none;
}
.searchbar-engine-one-off-item:hover:not(.dummy),
.searchbar-engine-one-off-item[selected] {
background-color: Highlight;
background-image: none;
}
.searchbar-engine-one-off-item > .button-box {
border: none;
padding: 0 0;
}
.searchbar-engine-one-off-item > .button-box > .button-text {
display: none;
}
.searchbar-engine-one-off-item > .button-box > .button-icon {
display: -moz-box;
-moz-margin-end: 0;
width: 16px;
height: 16px;
}
searchbar[oneoffui] .searchbar-engine-button {
display: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
-moz-padding-start: 16px;
border-top: none !important;
}
.searchbar-engine-image {
-moz-margin-start: -1px;
}
.search-setting-button {
-moz-appearance: none;
border-bottom: none;
border-left: none;
border-right: none;
-moz-border-top-colors: none;
min-height: 32px;
}
.search-setting-button:hover {
background-color: hsla(210,4%,10%,.07);
}
.search-setting-button:hover:active {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

View File

@ -100,6 +100,7 @@ browser.jar:
skin/classic/browser/searchbar.css
skin/classic/browser/Search.png
skin/classic/browser/Search@2x.png
skin/classic/browser/search-pref.png (../shared/search-pref.png)
skin/classic/browser/Secure-Glyph.png
skin/classic/browser/Secure-Glyph@2x.png
skin/classic/browser/slowStartup-16.png

View File

@ -32,6 +32,12 @@ radio[pane=paneTabs] {
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
/* ----- SEARCH BUTTON ----- */
radio[pane=paneSearch] {
list-style-image: url("chrome://browser/skin/search-pref.png");
}
/* ----- CONTENT BUTTON ----- */
radio[pane=paneContent] {

View File

@ -47,10 +47,30 @@
-moz-padding-end: 6px;
}
.searchbar-search-button-container {
-moz-box-align: center;
-moz-padding-start: 6px;
-moz-padding-end: 4px;
}
.searchbar-search-button,
.search-go-button {
list-style-image: url("chrome://browser/skin/Search.png");
}
searchbar[oneoffui] .search-go-button {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 42px, 14px, 28px);
}
searchbar[oneoffui] .search-go-button:hover:active {
-moz-image-region: rect(14px, 42px, 28px, 28px);
}
searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
@media (min-resolution: 2dppx) {
.searchbar-engine-image {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
@ -61,8 +81,116 @@
width: 7px;
}
.search-go-button {
.search-go-button,
.searchbar-search-button {
list-style-image: url("chrome://browser/skin/Search@2x.png");
width: 14px;
}
searchbar[oneoffui] .search-go-button {
list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
-moz-image-region: rect(0, 84px, 28px, 56px);
width: 14px;
}
searchbar[oneoffui] .search-go-button:hover:active {
list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
-moz-image-region: rect(28px, 84px, 56px, 56px);
width: 14px;
}
}
.search-panel-current-engine {
border-top: none !important;
border-bottom: 1px solid #ccc;
border-radius: 4px 4px 0 0;
}
.search-panel-header {
font-size: 10px;
font-weight: normal;
background-color: rgb(245, 245, 245);
border-top: 1px solid #ccc;
margin: 0;
padding: 3px 6px;
color: #666;
}
.search-panel-current-input > label {
margin: 0 0 !important;
}
.search-panel-input-value {
color: black;
}
.search-panel-one-offs {
border-top: 1px solid #ccc;
margin-bottom: 0 !important;
}
.searchbar-engine-one-off-item {
-moz-appearance: none;
display: inline-block;
min-width: 48px;
height: 32px;
margin: 0 0;
padding: 0 0;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: right center;
}
.searchbar-engine-one-off-item:not(.last-row) {
box-sizing: padding-box;
border-bottom: 1px solid #ccc;
}
.searchbar-engine-one-off-item.last-of-row {
background-image: none;
}
.searchbar-engine-one-off-item:hover:not(.dummy),
.searchbar-engine-one-off-item[selected] {
background-color: Highlight;
background-image: none;
}
.searchbar-engine-one-off-item > .button-box > .button-text {
display: none;
}
.searchbar-engine-one-off-item > .button-box > .button-icon {
-moz-margin-start: 0;
width: 16px;
height: 16px;
}
searchbar[oneoffui] .searchbar-engine-button {
display: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
-moz-padding-start: 22px;
border-top: none !important;
}
#PopupSearchAutoComplete {
border-radius: 4px;
}
.search-setting-button {
-moz-appearance: none;
border-radius: 0 0 4px 4px;
min-height: 32px;
}
.search-setting-button:hover {
background-color: hsla(210,4%,10%,.07);
}
.search-setting-button:hover:active {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -33,6 +33,10 @@ treecol {
-moz-image-region: rect(0, 24px, 24px, 0);
}
#category-search > .category-icon {
-moz-image-region: rect(0, 194px, 24px, 168px);
}
#category-content > .category-icon {
-moz-image-region: rect(0, 48px, 24px, 24px);
}
@ -66,6 +70,10 @@ treecol {
-moz-image-region: rect(0, 48px, 48px, 0);
}
#category-search > .category-icon {
-moz-image-region: rect(0, 384px, 48px, 336px);
}
#category-content > .category-icon {
-moz-image-region: rect(0, 96px, 48px, 48px);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -77,6 +77,7 @@ browser.jar:
skin/classic/browser/reload-stop-go.png
skin/classic/browser/searchbar.css
skin/classic/browser/searchbar-dropdown-arrow.png
skin/classic/browser/search-pref.png (../shared/search-pref.png)
skin/classic/browser/Secure24.png
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/slowStartup-16.png
@ -511,6 +512,7 @@ browser.jar:
skin/classic/aero/browser/reload-stop-go.png
skin/classic/aero/browser/searchbar.css
skin/classic/aero/browser/searchbar-dropdown-arrow.png (searchbar-dropdown-arrow-aero.png)
skin/classic/aero/browser/search-pref.png (../shared/search-pref.png)
skin/classic/aero/browser/Secure24.png (Secure24-aero.png)
skin/classic/aero/browser/setDesktopBackground.css
skin/classic/aero/browser/slowStartup-16.png

View File

@ -19,6 +19,10 @@ radio[pane=paneTabs] {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
#BrowserPreferences radio[pane=paneSearch] {
list-style-image: url("chrome://browser/skin/search-pref.png");
}
radio[pane=paneContent] {
-moz-image-region: rect(0, 96px, 32px, 64px);
}

View File

@ -78,3 +78,137 @@
.searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
font-weight: bold;
}
.searchbar-search-button-container {
-moz-box-align: center;
padding: 3px 4px;
-moz-padding-end: 2px;
}
.searchbar-search-button {
list-style-image: url("chrome://global/skin/icons/Search-glass.png");
-moz-image-region: rect(0px 16px 16px 0px);
}
searchbar[oneoffui] .search-go-button {
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-moz-image-region: rect(0, 42px, 14px, 28px);
}
searchbar[oneoffui] .search-go-button:hover {
-moz-image-region: rect(14px, 42px, 28px, 28px);
}
searchbar[oneoffui] .search-go-button:hover:active {
-moz-image-region: rect(28px, 42px, 42px, 28px);
}
searchbar[oneoffui] .search-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
transform: scaleX(-1);
}
.search-panel-current-engine {
border-top: none !important;
border-bottom: 1px solid #ccc;
}
.search-panel-header {
font-size: 10px;
font-weight: normal;
background-color: rgb(245, 245, 245);
border-top: 1px solid #ccc;
margin: 0;
padding: 3px 6px;
color: #666;
}
.search-panel-current-input > label {
margin: 0 0 !important;
}
.search-panel-input-value {
color: black;
}
.search-panel-one-offs {
margin: 0 0 !important;
border-top: 1px solid #ccc;
}
.searchbar-engine-one-off-item {
-moz-appearance: none;
display: inline-block;
border: none;
min-width: 48px;
height: 32px;
margin: 0 0;
padding: 0 0;
background: none;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-position: right center;
}
.searchbar-engine-one-off-item:not(.last-row) {
box-sizing: padding-box;
border-bottom: 1px solid #ccc;
}
.searchbar-engine-one-off-item.last-of-row {
background-image: none;
}
.searchbar-engine-one-off-item:hover:not(.dummy),
.searchbar-engine-one-off-item[selected] {
background-color: Highlight;
background-image: none;
}
.searchbar-engine-one-off-item > .button-box {
border: none;
padding: 0 0;
}
.searchbar-engine-one-off-item > .button-box > .button-text {
display: none;
}
.searchbar-engine-one-off-item > .button-box > .button-icon {
width: 16px;
height: 16px;
}
searchbar[oneoffui] .searchbar-engine-button {
display: none;
}
.search-panel-tree > .autocomplete-treebody::-moz-tree-cell {
-moz-padding-start: 15px;
border-top: none !important;
}
.searchbar-engine-image {
-moz-margin-start: -1px;
}
.search-setting-button {
-moz-appearance: none;
border-bottom: none;
border-left: none;
border-right: none;
-moz-border-top-colors: none;
min-height: 32px;
}
.search-setting-button:hover {
background-color: hsla(210,4%,10%,.07);
}
.search-setting-button:hover:active {
outline: 1px solid hsla(210,4%,10%,.12);
background-color: hsla(210,4%,10%,.12);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
}

View File

@ -50,10 +50,12 @@ this.SearchSuggestionController.prototype = {
* The maximum number of local form history results to return. This limit is
* only enforced if remote results are also returned.
*/
maxLocalResults: 7,
maxLocalResults: 5,
/**
* The maximum number of remote search engine results to return.
* We'll actually only display at most
* maxRemoteResults - <displayed local results count> remote results.
*/
maxRemoteResults: 10,
@ -353,7 +355,8 @@ this.SearchSuggestionController.prototype = {
}
// Trim the number of results to the maximum requested (now that we've pruned dupes).
results.remote = results.remote.slice(0, this.maxRemoteResults);
results.remote =
results.remote.slice(0, this.maxRemoteResults - results.local.length);
if (this._callback) {
this._callback(results);

View File

@ -73,6 +73,12 @@ SuggestAutoComplete.prototype = {
finalComments = finalComments.concat(comments);
}
// If no result, add the search term so that the panel is shown anyway.
if (!finalResults.length) {
finalResults.push(results.term);
finalComments.push("");
}
// Notify the FE of our new results
this.onResultsReady(results.term, finalResults, finalComments, results.formHistoryResult);
},

View File

@ -261,8 +261,8 @@ add_task(function* both_identical_with_more_than_max_results() {
for (let i = 0; i < controller.maxLocalResults; i++) {
do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
}
do_check_eq(result.remote.length, 10);
for (let i = 0; i < controller.maxRemoteResults; i++) {
do_check_eq(result.local.length + result.remote.length, 10);
for (let i = 0; i < result.remote.length; i++) {
do_check_eq(result.remote[i],
"letter " + String.fromCharCode("A".charCodeAt() + controller.maxLocalResults + i));
}
@ -284,7 +284,7 @@ add_task(function* noremote_maxLocal() {
add_task(function* someremote_maxLocal() {
let controller = new SearchSuggestionController();
controller.maxLocalResults = 2;
controller.maxRemoteResults = 2;
controller.maxRemoteResults = 4;
let result = yield controller.fetch("letter ", false, getEngine);
do_check_eq(result.term, "letter ");
do_check_eq(result.local.length, 2);
@ -301,7 +301,7 @@ add_task(function* someremote_maxLocal() {
add_task(function* one_of_each() {
let controller = new SearchSuggestionController();
controller.maxLocalResults = 1;
controller.maxRemoteResults = 1;
controller.maxRemoteResults = 2;
let result = yield controller.fetch("letter ", false, getEngine);
do_check_eq(result.term, "letter ");
do_check_eq(result.local.length, 1);
@ -344,7 +344,7 @@ add_task(function* one_of_each_disabled_before_creation_enabled_after_creation_o
Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
let controller = new SearchSuggestionController();
controller.maxLocalResults = 1;
controller.maxRemoteResults = 1;
controller.maxRemoteResults = 2;
Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
let result = yield controller.fetch("letter ", false, getEngine);
do_check_eq(result.term, "letter ");

View File

@ -177,6 +177,10 @@ xul|menulist > xul|*.menulist-label-box {
padding-left: 10px !important;
}
xul|menulist > xul|*.menulist-label-box > xul|*.menulist-icon {
-moz-margin-end: 5px;
}
xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
-moz-appearance: none;
margin: 1px 0;
@ -427,6 +431,10 @@ xul|*.checkbox-label-box {
}
}
xul|richlistitem > xul|*.checkbox-check {
margin: 3px 6px;
}
xul|*.radio-check {
-moz-appearance: none;
width: 23px;