Bug 1171344 - [implement] One-off searches on about:home and about:newtab. r=adw
This commit is contained in:
2
CLOBBER
2
CLOBBER
@@ -22,4 +22,4 @@
|
|||||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||||
# don't change CLOBBER for WebIDL changes any more.
|
# don't change CLOBBER for WebIDL changes any more.
|
||||||
|
|
||||||
Bug 1178850 requires clobber for Android JNI header changes
|
Bug 1171344 requires a clobber due to renaming of a test file.
|
||||||
|
|||||||
@@ -49,78 +49,57 @@ a {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchForm,
|
#searchIconAndTextContainer,
|
||||||
#snippets {
|
#snippets {
|
||||||
width: 470px;
|
width: 470px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchForm {
|
#searchIconAndTextContainer {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
}
|
height: 36px;
|
||||||
|
position: relative;
|
||||||
#searchLogoContainer {
|
|
||||||
display: -moz-box;
|
|
||||||
-moz-box-align: center;
|
|
||||||
padding-top: 2px;
|
|
||||||
-moz-padding-end: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchLogoContainer[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchEngineLogo {
|
|
||||||
display: inline-block;
|
|
||||||
height: 28px;
|
|
||||||
width: 70px;
|
|
||||||
min-width: 70px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchIcon {
|
#searchIcon {
|
||||||
border: 1px solid transparent;
|
border: 1px transparent;
|
||||||
-moz-margin-end: 5px;
|
padding: 0;
|
||||||
height: 38px;
|
margin: 0;
|
||||||
width: 38px;
|
width: 36px;
|
||||||
background: url("chrome://browser/skin/magnifier.png") center center no-repeat;
|
height: 36px;
|
||||||
background-size: 26px;
|
background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
|
||||||
}
|
position: absolute;
|
||||||
|
|
||||||
#searchIcon[active],
|
|
||||||
#searchIcon:hover {
|
|
||||||
background-color: #e9e9e9;
|
|
||||||
border-color: rgb(226, 227, 229);
|
|
||||||
border-radius: 2.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html[searchUIConfiguration="oldsearchui"] #searchIcon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
html:not([searchUIConfiguration="oldsearchui"]) #searchText::-moz-placeholder {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchText {
|
#searchText {
|
||||||
|
margin-left: 0;
|
||||||
-moz-box-flex: 1;
|
-moz-box-flex: 1;
|
||||||
padding: 6px 8px;
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
padding-left: 34px;
|
||||||
|
padding-right: 8px;
|
||||||
background: hsla(0,0%,100%,.9) padding-box;
|
background: hsla(0,0%,100%,.9) padding-box;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
|
border-radius: 2px 0 0 2px;
|
||||||
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
||||||
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
|
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
|
||||||
0 0 2px hsla(210,65%,9%,.1) inset,
|
0 0 2px hsla(210,65%,9%,.1) inset,
|
||||||
0 1px 0 hsla(0,0%,100%,.2);
|
0 1px 0 hsla(0,0%,100%,.2);
|
||||||
border-radius: 2.5px 0 0 2.5px;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchText:-moz-dir(rtl) {
|
#searchText:-moz-dir(rtl) {
|
||||||
border-radius: 0 2.5px 2.5px 0;
|
border-radius: 0 2px 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#searchText[aria-expanded="true"] {
|
||||||
|
border-radius: 2px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchText[aria-expanded="true"]:-moz-dir(rtl) {
|
||||||
|
border-radius: 0 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchText[keepfocus],
|
||||||
#searchText:focus,
|
#searchText:focus,
|
||||||
#searchText[autofocus] {
|
#searchText[autofocus] {
|
||||||
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
|
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
|
||||||
@@ -128,25 +107,27 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||||||
|
|
||||||
#searchSubmit {
|
#searchSubmit {
|
||||||
-moz-margin-start: -1px;
|
-moz-margin-start: -1px;
|
||||||
background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
||||||
padding: 0 9px;
|
padding: 0;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
||||||
|
border-radius: 0 2px 2px 0;
|
||||||
-moz-border-start: 1px solid transparent;
|
-moz-border-start: 1px solid transparent;
|
||||||
border-radius: 0 2.5px 2.5px 0;
|
|
||||||
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
||||||
0 1px 0 hsla(0,0%,100%,.2);
|
0 1px 0 hsla(0,0%,100%,.2);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition-property: background-color, border-color, box-shadow;
|
transition-property: background-color, border-color, box-shadow;
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchSubmit:-moz-dir(rtl) {
|
#searchSubmit:-moz-dir(rtl) {
|
||||||
border-radius: 2.5px 0 0 2.5px;
|
border-radius: 2px 0 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchText:focus + #searchSubmit,
|
#searchText:focus + #searchSubmit,
|
||||||
|
#searchText[keepfocus] + #searchSubmit,
|
||||||
#searchText + #searchSubmit:hover,
|
#searchText + #searchSubmit:hover,
|
||||||
#searchText[autofocus] + #searchSubmit {
|
#searchText[autofocus] + #searchSubmit {
|
||||||
border-color: #59b5fc #45a3e7 #3294d5;
|
border-color: #59b5fc #45a3e7 #3294d5;
|
||||||
@@ -154,15 +135,16 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#searchText:focus + #searchSubmit,
|
#searchText:focus + #searchSubmit,
|
||||||
|
#searchText[keepfocus] + #searchSubmit,
|
||||||
#searchText[autofocus] + #searchSubmit {
|
#searchText[autofocus] + #searchSubmit {
|
||||||
background-image: linear-gradient(#4cb1ff, #1793e5);
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
|
||||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
||||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||||
0 1px 0 hsla(210,54%,20%,.03);
|
0 1px 0 hsla(210,54%,20%,.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchText + #searchSubmit:hover {
|
#searchText + #searchSubmit:hover {
|
||||||
background-image: linear-gradient(#66bdff, #0d9eff);
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#66bdff, #0d9eff);
|
||||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
||||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||||
0 1px 0 hsla(210,54%,20%,.03),
|
0 1px 0 hsla(210,54%,20%,.03),
|
||||||
@@ -180,9 +162,9 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||||||
#rightsSnippet {
|
#rightsSnippet {
|
||||||
display: block;
|
display: block;
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
background: 30px center no-repeat;
|
background: 0 center no-repeat;
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
-moz-padding-start: 79px;
|
-moz-padding-start: 49px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#rightsSnippet[hidden] {
|
#rightsSnippet[hidden] {
|
||||||
@@ -192,7 +174,7 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||||||
#defaultSnippet1:-moz-dir(rtl),
|
#defaultSnippet1:-moz-dir(rtl),
|
||||||
#defaultSnippet2:-moz-dir(rtl),
|
#defaultSnippet2:-moz-dir(rtl),
|
||||||
#rightsSnippet:-moz-dir(rtl) {
|
#rightsSnippet:-moz-dir(rtl) {
|
||||||
background-position: right 30px center;
|
background-position: right 0 center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#defaultSnippet1 {
|
#defaultSnippet1 {
|
||||||
@@ -247,7 +229,7 @@ body[narrow] #launcher[session] {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
background: transparent padding-box;
|
background: transparent padding-box;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 2.5px;
|
border-radius: 2px;
|
||||||
color: #525c66;
|
color: #525c66;
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -396,10 +378,6 @@ body[narrow] #restorePreviousSession::before {
|
|||||||
background-image: url("chrome://branding/content/about-logo@2x.png");
|
background-image: url("chrome://branding/content/about-logo@2x.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchIcon {
|
|
||||||
background-image: url("chrome://browser/skin/magnifier@2x.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
#defaultSnippet1,
|
#defaultSnippet1,
|
||||||
#defaultSnippet2,
|
#defaultSnippet2,
|
||||||
#rightsSnippet {
|
#rightsSnippet {
|
||||||
|
|||||||
@@ -4,138 +4,6 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const SEARCH_ENGINES = {
|
|
||||||
"Google": {
|
|
||||||
// This is the "2x" image designed for OS X retina resolution, Windows at 192dpi, etc.;
|
|
||||||
// it will be scaled down as necessary on lower-dpi displays.
|
|
||||||
// This needs to be defined in a single line to keep the JS parser from creating many
|
|
||||||
// intermediate strings in memory. See bug 986672.
|
|
||||||
image: "data:image/png;base64,\
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The process of adding a new default snippet involves:
|
// The process of adding a new default snippet involves:
|
||||||
// * add a new entity to aboutHome.dtd
|
// * add a new entity to aboutHome.dtd
|
||||||
// * add a <span/> for it in aboutHome.xhtml
|
// * add a <span/> for it in aboutHome.xhtml
|
||||||
@@ -158,8 +26,7 @@ const SNIPPETS_OBJECTSTORE_NAME = "snippets";
|
|||||||
let gInitialized = false;
|
let gInitialized = false;
|
||||||
let gObserver = new MutationObserver(function (mutations) {
|
let gObserver = new MutationObserver(function (mutations) {
|
||||||
for (let mutation of mutations) {
|
for (let mutation of mutations) {
|
||||||
if (mutation.attributeName == "searchEngineName") {
|
if (mutation.attributeName == "snippetsVersion") {
|
||||||
setupSearchEngine();
|
|
||||||
if (!gInitialized) {
|
if (!gInitialized) {
|
||||||
ensureSnippetsMapThen(loadSnippets);
|
ensureSnippetsMapThen(loadSnippets);
|
||||||
gInitialized = true;
|
gInitialized = true;
|
||||||
@@ -174,6 +41,7 @@ window.addEventListener("pageshow", function () {
|
|||||||
// later and may use asynchronous getters.
|
// later and may use asynchronous getters.
|
||||||
window.gObserver.observe(document.documentElement, { attributes: true });
|
window.gObserver.observe(document.documentElement, { attributes: true });
|
||||||
fitToWidth();
|
fitToWidth();
|
||||||
|
setupSearch();
|
||||||
window.addEventListener("resize", fitToWidth);
|
window.addEventListener("resize", fitToWidth);
|
||||||
|
|
||||||
// Ask chrome to update snippets.
|
// Ask chrome to update snippets.
|
||||||
@@ -300,52 +168,13 @@ function ensureSnippetsMapThen(aCallback)
|
|||||||
|
|
||||||
function onSearchSubmit(aEvent)
|
function onSearchSubmit(aEvent)
|
||||||
{
|
{
|
||||||
let searchText = document.getElementById("searchText");
|
gContentSearchController.search(aEvent);
|
||||||
let searchTerms = searchText.value;
|
|
||||||
let engineName = document.documentElement.getAttribute("searchEngineName");
|
|
||||||
|
|
||||||
if (engineName && searchTerms.length > 0) {
|
|
||||||
// Send an event that will perform a search and Firefox Health Report will
|
|
||||||
// record that a search from about:home has occurred.
|
|
||||||
let eventData = {
|
|
||||||
engineName: engineName,
|
|
||||||
searchTerms: searchTerms,
|
|
||||||
originalEvent: {
|
|
||||||
target: {
|
|
||||||
ownerDocument: null
|
|
||||||
},
|
|
||||||
shiftKey: aEvent.shiftKey,
|
|
||||||
ctrlKey: aEvent.ctrlKey,
|
|
||||||
metaKey: aEvent.metaKey,
|
|
||||||
altKey: aEvent.altKey,
|
|
||||||
button: aEvent.button,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (searchText.hasAttribute("selection-index")) {
|
|
||||||
eventData.selection = {
|
|
||||||
index: searchText.getAttribute("selection-index"),
|
|
||||||
kind: searchText.getAttribute("selection-kind")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
eventData = JSON.stringify(eventData);
|
|
||||||
|
|
||||||
let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
gSearchSuggestionController.addInputValueToFormHistory();
|
|
||||||
|
|
||||||
if (aEvent) {
|
|
||||||
aEvent.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let gSearchSuggestionController;
|
let gContentSearchController;
|
||||||
|
|
||||||
function setupSearchEngine()
|
function setupSearch()
|
||||||
{
|
{
|
||||||
// The "autofocus" attribute doesn't focus the form element
|
// The "autofocus" attribute doesn't focus the form element
|
||||||
// immediately when the element is first drawn, so the
|
// immediately when the element is first drawn, so the
|
||||||
@@ -355,29 +184,12 @@ function setupSearchEngine()
|
|||||||
searchText.removeEventListener("blur", searchText_onBlur);
|
searchText.removeEventListener("blur", searchText_onBlur);
|
||||||
searchText.removeAttribute("autofocus");
|
searchText.removeAttribute("autofocus");
|
||||||
});
|
});
|
||||||
|
|
||||||
let searchEngineName = document.documentElement.getAttribute("searchEngineName");
|
|
||||||
let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
|
|
||||||
let logoElt = document.getElementById("searchEngineLogo");
|
|
||||||
|
|
||||||
// Add search engine logo.
|
if (!gContentSearchController) {
|
||||||
if (searchEngineInfo && searchEngineInfo.image) {
|
gContentSearchController =
|
||||||
logoElt.parentNode.hidden = false;
|
new ContentSearchUIController(searchText, searchText.parentNode,
|
||||||
logoElt.src = searchEngineInfo.image;
|
"abouthome", "homepage");
|
||||||
logoElt.alt = searchEngineName;
|
|
||||||
searchText.placeholder = "";
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
logoElt.parentNode.hidden = true;
|
|
||||||
searchText.placeholder = searchEngineName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gSearchSuggestionController) {
|
|
||||||
gSearchSuggestionController =
|
|
||||||
new SearchSuggestionUIController(searchText, searchText.parentNode,
|
|
||||||
onSearchSubmit);
|
|
||||||
}
|
|
||||||
gSearchSuggestionController.engineName = searchEngineName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
<link rel="icon" type="image/png" id="favicon"
|
<link rel="icon" type="image/png" id="favicon"
|
||||||
href="chrome://branding/content/icon32.png"/>
|
href="chrome://branding/content/icon32.png"/>
|
||||||
<link rel="stylesheet" type="text/css" media="all"
|
<link rel="stylesheet" type="text/css" media="all"
|
||||||
href="chrome://browser/content/searchSuggestionUI.css"/>
|
href="chrome://browser/content/contentSearchUI.css"/>
|
||||||
<link rel="stylesheet" type="text/css" media="all" defer="defer"
|
<link rel="stylesheet" type="text/css" media="all" defer="defer"
|
||||||
href="chrome://browser/content/abouthome/aboutHome.css"/>
|
href="chrome://browser/content/abouthome/aboutHome.css"/>
|
||||||
|
|
||||||
<script type="text/javascript;version=1.8"
|
<script type="text/javascript;version=1.8"
|
||||||
src="chrome://browser/content/abouthome/aboutHome.js"/>
|
src="chrome://browser/content/abouthome/aboutHome.js"/>
|
||||||
<script type="text/javascript;version=1.8"
|
<script type="text/javascript;version=1.8"
|
||||||
src="chrome://browser/content/searchSuggestionUI.js"/>
|
src="chrome://browser/content/contentSearchUI.js"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body dir="&locale.dir;">
|
<body dir="&locale.dir;">
|
||||||
@@ -39,14 +39,12 @@
|
|||||||
<div id="topSection">
|
<div id="topSection">
|
||||||
<div id="brandLogo"></div>
|
<div id="brandLogo"></div>
|
||||||
|
|
||||||
<div id="searchContainer">
|
<div id="searchIconAndTextContainer">
|
||||||
<form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
|
<div id="searchIcon"/>
|
||||||
<div id="searchLogoContainer" hidden="true"><img id="searchEngineLogo"/></div>
|
<input type="text" name="q" value="" id="searchText" maxlength="256"
|
||||||
<button id="searchIcon" type="button" />
|
aria-label="&contentSearchInput.label;" autofocus="autofocus" dir="auto"/>
|
||||||
<input type="text" name="q" value="" id="searchText" maxlength="256"
|
<input id="searchSubmit" type="button" value="" onclick="onSearchSubmit(event)"
|
||||||
autofocus="autofocus" dir="auto"/>
|
aria-label="&contentSearchSubmit.label;"/>
|
||||||
<input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="snippetContainer">
|
<div id="snippetContainer">
|
||||||
|
|||||||
@@ -3467,9 +3467,8 @@ const BrowserSearch = {
|
|||||||
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
||||||
let url = gBrowser.currentURI.spec.toLowerCase();
|
let url = gBrowser.currentURI.spec.toLowerCase();
|
||||||
let mm = gBrowser.selectedBrowser.messageManager;
|
let mm = gBrowser.selectedBrowser.messageManager;
|
||||||
if (url === "about:home") {
|
if (url === "about:home" ||
|
||||||
AboutHome.focusInput(mm);
|
(url === "about:newtab" && NewTabUtils.allPages.enabled)) {
|
||||||
} else if (url === "about:newtab" && NewTabUtils.allPages.enabled) {
|
|
||||||
ContentSearch.focusInput(mm);
|
ContentSearch.focusInput(mm);
|
||||||
} else {
|
} else {
|
||||||
openUILinkIn("about:home", "current");
|
openUILinkIn("about:home", "current");
|
||||||
|
|||||||
152
browser/base/content/contentSearchUI.css
Normal file
152
browser/base/content/contentSearchUI.css
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
.contentSearchSuggestionTable {
|
||||||
|
background-color: hsla(0,0%,100%,.99);
|
||||||
|
border: 1px solid hsla(0, 0%, 0%, .2);
|
||||||
|
border-top: none;
|
||||||
|
box-shadow: 0 5px 10px hsla(0, 0%, 0%, .1);
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1001;
|
||||||
|
-moz-user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionsList {
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 92%);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionTable,
|
||||||
|
.contentSearchSuggestionsList {
|
||||||
|
border-spacing: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchHeaderRow,
|
||||||
|
.contentSearchSuggestionRow {
|
||||||
|
margin: 0;
|
||||||
|
max-width: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchHeaderRow > td > img,
|
||||||
|
.contentSearchSuggestionRow > td > .historyIcon {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionTable .historyIcon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: inline-block;
|
||||||
|
background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionRow.selected > td > .historyIcon {
|
||||||
|
background-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon-active");
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchHeader > img {
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionRow.remote > td > .historyIcon {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionRow.selected {
|
||||||
|
background-color: Highlight;
|
||||||
|
color: HighlightText;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchHeader,
|
||||||
|
.contentSearchSuggestionEntry {
|
||||||
|
margin: 0;
|
||||||
|
max-width: inherit;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 4px 10px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchHeader {
|
||||||
|
background-color: hsl(0, 0%, 97%);
|
||||||
|
color: #666;
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSuggestionsContainer {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSearchWithHeaderSearchText {
|
||||||
|
white-space: pre;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffItem {
|
||||||
|
-moz-appearance: none;
|
||||||
|
height: 32px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffItem > img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffItem:not(.last-row) {
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 92%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffItem.end-of-row {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffItem.selected {
|
||||||
|
background-color: Highlight;
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchOneOffsTable {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSettingsButton {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid hsla(0, 0%, 0%, .08);
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSettingsButton.selected {
|
||||||
|
background-color: hsl(0, 0%, 90%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentSearchSettingsButton:active {
|
||||||
|
background-color: hsl(0, 0%, 85%);
|
||||||
|
}
|
||||||
735
browser/base/content/contentSearchUI.js
Normal file
735
browser/base/content/contentSearchUI.js
Normal file
@@ -0,0 +1,735 @@
|
|||||||
|
/* 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";
|
||||||
|
|
||||||
|
this.ContentSearchUIController = (function () {
|
||||||
|
|
||||||
|
const MAX_DISPLAYED_SUGGESTIONS = 6;
|
||||||
|
const SUGGESTION_ID_PREFIX = "searchSuggestion";
|
||||||
|
const ONE_OFF_ID_PREFIX = "oneOff";
|
||||||
|
const CSS_URI = "chrome://browser/content/contentSearchUI.css";
|
||||||
|
|
||||||
|
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object that manages search suggestions and their UI for a text
|
||||||
|
* box.
|
||||||
|
*
|
||||||
|
* The UI consists of an html:table that's inserted into the DOM after the given
|
||||||
|
* text box and styled so that it appears as a dropdown below the text box.
|
||||||
|
*
|
||||||
|
* @param inputElement
|
||||||
|
* Search suggestions will be based on the text in this text box.
|
||||||
|
* Assumed to be an html:input. xul:textbox is untested but might work.
|
||||||
|
* @param tableParent
|
||||||
|
* The suggestion table is appended as a child to this element. Since
|
||||||
|
* the table is absolutely positioned and its top and left values are set
|
||||||
|
* to be relative to the top and left of the page, either the parent and
|
||||||
|
* all its ancestors should not be positioned elements (i.e., their
|
||||||
|
* positions should be "static"), or the parent's position should be the
|
||||||
|
* top left of the page.
|
||||||
|
* @param healthReportKey
|
||||||
|
* This will be sent with the search data for FHR to record the search.
|
||||||
|
* @param searchPurpose
|
||||||
|
* Sent with search data, see nsISearchEngine.getSubmission.
|
||||||
|
* @param idPrefix
|
||||||
|
* The IDs of elements created by the object will be prefixed with this
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
function ContentSearchUIController(inputElement, tableParent, healthReportKey,
|
||||||
|
searchPurpose, idPrefix="") {
|
||||||
|
this.input = inputElement;
|
||||||
|
this._idPrefix = idPrefix;
|
||||||
|
this._healthReportKey = healthReportKey;
|
||||||
|
this._searchPurpose = searchPurpose;
|
||||||
|
|
||||||
|
let tableID = idPrefix + "searchSuggestionTable";
|
||||||
|
this.input.autocomplete = "off";
|
||||||
|
this.input.setAttribute("aria-autocomplete", "true");
|
||||||
|
this.input.setAttribute("aria-controls", tableID);
|
||||||
|
tableParent.appendChild(this._makeTable(tableID));
|
||||||
|
|
||||||
|
this.input.addEventListener("keypress", this);
|
||||||
|
this.input.addEventListener("input", this);
|
||||||
|
this.input.addEventListener("focus", this);
|
||||||
|
this.input.addEventListener("blur", this);
|
||||||
|
window.addEventListener("ContentSearchService", this);
|
||||||
|
|
||||||
|
this._stickyInputValue = "";
|
||||||
|
this._hideSuggestions();
|
||||||
|
|
||||||
|
this._getSearchEngines();
|
||||||
|
this._getStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSearchUIController.prototype = {
|
||||||
|
|
||||||
|
// The timeout (ms) of the remote suggestions. Corresponds to
|
||||||
|
// SearchSuggestionController.remoteTimeout. Uses
|
||||||
|
// SearchSuggestionController's default timeout if falsey.
|
||||||
|
remoteTimeout: undefined,
|
||||||
|
_oneOffButtons: [],
|
||||||
|
|
||||||
|
get defaultEngine() {
|
||||||
|
return this._defaultEngine;
|
||||||
|
},
|
||||||
|
|
||||||
|
set defaultEngine(val) {
|
||||||
|
this._defaultEngine = val;
|
||||||
|
this._updateDefaultEngineHeader();
|
||||||
|
|
||||||
|
if (val && document.activeElement == this.input) {
|
||||||
|
this._speculativeConnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get engines() {
|
||||||
|
return this._engines;
|
||||||
|
},
|
||||||
|
|
||||||
|
set engines(val) {
|
||||||
|
this._engines = val;
|
||||||
|
this._setUpOneOffButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
// The selectedIndex is the index of the element with the "selected" class in
|
||||||
|
// the list obtained by concatenating the suggestion rows, one-off buttons, and
|
||||||
|
// search settings button.
|
||||||
|
get selectedIndex() {
|
||||||
|
let allElts = [...this._suggestionsList.children,
|
||||||
|
...this._oneOffButtons,
|
||||||
|
document.getElementById("contentSearchSettingsButton")];
|
||||||
|
for (let i = 0; i < allElts.length; ++i) {
|
||||||
|
let elt = allElts[i];
|
||||||
|
if (elt.classList.contains("selected")) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
set selectedIndex(idx) {
|
||||||
|
// Update the table's rows, and the input when there is a selection.
|
||||||
|
this._table.removeAttribute("aria-activedescendant");
|
||||||
|
this.input.removeAttribute("aria-activedescendant");
|
||||||
|
|
||||||
|
let allElts = [...this._suggestionsList.children,
|
||||||
|
...this._oneOffButtons,
|
||||||
|
document.getElementById("contentSearchSettingsButton")];
|
||||||
|
for (let i = 0; i < allElts.length; ++i) {
|
||||||
|
let elt = allElts[i];
|
||||||
|
let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
|
||||||
|
if (i == idx) {
|
||||||
|
elt.classList.add("selected");
|
||||||
|
ariaSelectedElt.setAttribute("aria-selected", "true");
|
||||||
|
this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
elt.classList.remove("selected");
|
||||||
|
ariaSelectedElt.setAttribute("aria-selected", "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get selectedEngineName() {
|
||||||
|
let selectedElt = this._table.querySelector(".selected");
|
||||||
|
if (selectedElt && selectedElt.engineName) {
|
||||||
|
return selectedElt.engineName;
|
||||||
|
}
|
||||||
|
return this.defaultEngine.name;
|
||||||
|
},
|
||||||
|
|
||||||
|
get numSuggestions() {
|
||||||
|
return this._suggestionsList.children.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
selectAndUpdateInput: function (idx) {
|
||||||
|
this.selectedIndex = idx;
|
||||||
|
let newValue = this.suggestionAtIndex(idx) || this._stickyInputValue;
|
||||||
|
// Setting the input value when the value has not changed commits the current
|
||||||
|
// IME composition, which we don't want to do.
|
||||||
|
if (this.input.value != newValue) {
|
||||||
|
this.input.value = newValue;
|
||||||
|
}
|
||||||
|
this._updateSearchWithHeader();
|
||||||
|
},
|
||||||
|
|
||||||
|
suggestionAtIndex: function (idx) {
|
||||||
|
let row = this._suggestionsList.children[idx];
|
||||||
|
return row ? row.textContent : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteSuggestionAtIndex: function (idx) {
|
||||||
|
// Only form history suggestions can be deleted.
|
||||||
|
if (this.isFormHistorySuggestionAtIndex(idx)) {
|
||||||
|
let suggestionStr = this.suggestionAtIndex(idx);
|
||||||
|
this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
|
||||||
|
this._suggestionsList.children[idx].remove();
|
||||||
|
this.selectAndUpdateInput(-1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isFormHistorySuggestionAtIndex: function (idx) {
|
||||||
|
let row = this._suggestionsList.children[idx];
|
||||||
|
return row && row.classList.contains("formHistory");
|
||||||
|
},
|
||||||
|
|
||||||
|
addInputValueToFormHistory: function () {
|
||||||
|
this._sendMsg("AddFormHistoryEntry", this.input.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent: function (event) {
|
||||||
|
this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCommand: function(aEvent) {
|
||||||
|
if (this.selectedIndex == this.numSuggestions + this._oneOffButtons.length) {
|
||||||
|
// Settings button was selected.
|
||||||
|
this._sendMsg("ManageEngines");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.search(aEvent);
|
||||||
|
|
||||||
|
if (aEvent) {
|
||||||
|
aEvent.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
search: function (aEvent) {
|
||||||
|
if (!this.defaultEngine) {
|
||||||
|
return; // Not initialized yet.
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchText = this.input;
|
||||||
|
let searchTerms;
|
||||||
|
if (this._table.hidden ||
|
||||||
|
aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") {
|
||||||
|
searchTerms = searchText.value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
searchTerms = this.suggestionAtIndex(this.selectedIndex) || searchText.value;
|
||||||
|
}
|
||||||
|
// Send an event that will perform a search and Firefox Health Report will
|
||||||
|
// record that a search from the healthReportKey passed to the constructor.
|
||||||
|
let eventData = {
|
||||||
|
engineName: this.selectedEngineName,
|
||||||
|
searchString: searchTerms,
|
||||||
|
healthReportKey: this._healthReportKey,
|
||||||
|
searchPurpose: this._searchPurpose,
|
||||||
|
originalEvent: {
|
||||||
|
shiftKey: aEvent.shiftKey,
|
||||||
|
ctrlKey: aEvent.ctrlKey,
|
||||||
|
metaKey: aEvent.metaKey,
|
||||||
|
altKey: aEvent.altKey,
|
||||||
|
button: aEvent.button,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.suggestionAtIndex(this.selectedIndex)) {
|
||||||
|
eventData.selection = {
|
||||||
|
index: this.selectedIndex,
|
||||||
|
kind: aEvent instanceof MouseEvent ? "mouse" :
|
||||||
|
aEvent instanceof KeyboardEvent ? "key" : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sendMsg("Search", eventData);
|
||||||
|
this.addInputValueToFormHistory();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onInput: function () {
|
||||||
|
if (!this.input.value) {
|
||||||
|
this._stickyInputValue = "";
|
||||||
|
this._hideSuggestions();
|
||||||
|
}
|
||||||
|
else if (this.input.value != this._stickyInputValue) {
|
||||||
|
// Only fetch new suggestions if the input value has changed.
|
||||||
|
this._getSuggestions();
|
||||||
|
this.selectAndUpdateInput(-1);
|
||||||
|
}
|
||||||
|
this._updateSearchWithHeader();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeypress: function (event) {
|
||||||
|
let selectedIndexDelta = 0;
|
||||||
|
switch (event.keyCode) {
|
||||||
|
case event.DOM_VK_UP:
|
||||||
|
if (!this._table.hidden) {
|
||||||
|
selectedIndexDelta = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_DOWN:
|
||||||
|
if (this._table.hidden) {
|
||||||
|
this._getSuggestions();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectedIndexDelta = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_RIGHT:
|
||||||
|
// Allow normal caret movement until the caret is at the end of the input.
|
||||||
|
if (this.input.selectionStart != this.input.selectionEnd ||
|
||||||
|
this.input.selectionEnd != this.input.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.numSuggestions && this.selectedIndex >= 0 &&
|
||||||
|
this.selectedIndex < this.numSuggestions) {
|
||||||
|
this.input.value = this.suggestionAtIndex(this.selectedIndex);
|
||||||
|
this.input.setAttribute("selection-index", this.selectedIndex);
|
||||||
|
this.input.setAttribute("selection-kind", "key");
|
||||||
|
} else {
|
||||||
|
// If we didn't select anything, make sure to remove the attributes
|
||||||
|
// in case they were populated last time.
|
||||||
|
this.input.removeAttribute("selection-index");
|
||||||
|
this.input.removeAttribute("selection-kind");
|
||||||
|
}
|
||||||
|
this._stickyInputValue = this.input.value;
|
||||||
|
this._hideSuggestions();
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_RETURN:
|
||||||
|
this._onCommand(event);
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_DELETE:
|
||||||
|
if (this.selectedIndex >= 0) {
|
||||||
|
this.deleteSuggestionAtIndex(this.selectedIndex);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case event.DOM_VK_ESCAPE:
|
||||||
|
if (!this._table.hidden) {
|
||||||
|
this._hideSuggestions();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedIndexDelta) {
|
||||||
|
// Update the selection.
|
||||||
|
let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
|
||||||
|
if (newSelectedIndex < -1) {
|
||||||
|
newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
|
||||||
|
}
|
||||||
|
else if (this.numSuggestions + this._oneOffButtons.length < newSelectedIndex) {
|
||||||
|
newSelectedIndex = -1;
|
||||||
|
}
|
||||||
|
this.selectAndUpdateInput(newSelectedIndex);
|
||||||
|
|
||||||
|
// Prevent the input's caret from moving.
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onFocus: function () {
|
||||||
|
if (this._mousedown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// When the input box loses focus to something in our table, we refocus it
|
||||||
|
// immediately. This causes the focus highlight to flicker, so we set a
|
||||||
|
// custom attribute which consumers should use for focus highlighting. This
|
||||||
|
// attribute is removed only when we do not immediately refocus the input
|
||||||
|
// box, thus eliminating flicker.
|
||||||
|
this.input.setAttribute("keepfocus", "true");
|
||||||
|
this._speculativeConnect();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onBlur: function () {
|
||||||
|
if (this._mousedown) {
|
||||||
|
// At this point, this.input has lost focus, but a new element has not yet
|
||||||
|
// received it. If we re-focus this.input directly, the new element will
|
||||||
|
// steal focus immediately, so we queue it instead.
|
||||||
|
setTimeout(() => this.input.focus(), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.input.removeAttribute("keepfocus");
|
||||||
|
this._hideSuggestions();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMousemove: function (event) {
|
||||||
|
this.selectedIndex = this._indexOfTableItem(event.target);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMouseup: function (event) {
|
||||||
|
if (event.button == 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._onCommand(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClick: function (event) {
|
||||||
|
this._onMouseup(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onContentSearchService: function (event) {
|
||||||
|
let methodName = "_onMsg" + event.detail.type;
|
||||||
|
if (methodName in this) {
|
||||||
|
this[methodName](event.detail.data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgFocusInput: function (event) {
|
||||||
|
this.input.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgSuggestions: function (suggestions) {
|
||||||
|
// Ignore the suggestions if their search string or engine doesn't match
|
||||||
|
// ours. Due to the async nature of message passing, this can easily happen
|
||||||
|
// when the user types quickly.
|
||||||
|
if (this._stickyInputValue != suggestions.searchString ||
|
||||||
|
this.defaultEngine.name != suggestions.engineName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearSuggestionRows();
|
||||||
|
|
||||||
|
// Position and size the table.
|
||||||
|
let { left } = this.input.getBoundingClientRect();
|
||||||
|
this._table.style.top = this.input.offsetHeight + "px";
|
||||||
|
this._table.style.minWidth = this.input.offsetWidth + "px";
|
||||||
|
this._table.style.maxWidth = (window.innerWidth - left - 40) + "px";
|
||||||
|
|
||||||
|
// Add the suggestions to the table.
|
||||||
|
let searchWords =
|
||||||
|
new Set(suggestions.searchString.trim().toLowerCase().split(/\s+/));
|
||||||
|
for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
|
||||||
|
let type, idx;
|
||||||
|
if (i < suggestions.formHistory.length) {
|
||||||
|
[type, idx] = ["formHistory", i];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let j = i - suggestions.formHistory.length;
|
||||||
|
if (j < suggestions.remote.length) {
|
||||||
|
[type, idx] = ["remote", j];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._suggestionsList.appendChild(
|
||||||
|
this._makeTableRow(type, suggestions[type][idx], i, searchWords));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._table.hidden) {
|
||||||
|
this.selectedIndex = -1;
|
||||||
|
this._table.hidden = false;
|
||||||
|
this.input.setAttribute("aria-expanded", "true");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgState: function (state) {
|
||||||
|
this.defaultEngine = {
|
||||||
|
name: state.currentEngine.name,
|
||||||
|
icon: this._getFaviconURIFromBuffer(state.currentEngine.iconBuffer),
|
||||||
|
};
|
||||||
|
this.engines = state.engines;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgCurrentState: function (state) {
|
||||||
|
this._onMsgState(state);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgCurrentEngine: function (engine) {
|
||||||
|
this.defaultEngine = {
|
||||||
|
name: engine.name,
|
||||||
|
icon: this._getFaviconURIFromBuffer(engine.iconBuffer),
|
||||||
|
};
|
||||||
|
this._setUpOneOffButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMsgStrings: function (strings) {
|
||||||
|
this._strings = strings;
|
||||||
|
this._updateDefaultEngineHeader();
|
||||||
|
this._updateSearchWithHeader();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDefaultEngineHeader: function () {
|
||||||
|
let header = document.getElementById("contentSearchDefaultEngineHeader");
|
||||||
|
if (this.defaultEngine.icon) {
|
||||||
|
header.firstChild.setAttribute("src", this.defaultEngine.icon);
|
||||||
|
}
|
||||||
|
if (!this._strings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (header.firstChild.nextSibling) {
|
||||||
|
header.firstChild.nextSibling.remove();
|
||||||
|
}
|
||||||
|
header.appendChild(document.createTextNode(
|
||||||
|
this._strings.searchHeader.replace("%S", this.defaultEngine.name)));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSearchWithHeader: function () {
|
||||||
|
if (!this._strings) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let searchWithHeader = document.getElementById("contentSearchSearchWithHeader");
|
||||||
|
while (searchWithHeader.firstChild) {
|
||||||
|
searchWithHeader.firstChild.remove();
|
||||||
|
}
|
||||||
|
if (this.input.value) {
|
||||||
|
searchWithHeader.appendChild(document.createTextNode(this._strings.searchFor));
|
||||||
|
let span = document.createElementNS(HTML_NS, "span");
|
||||||
|
span.setAttribute("class", "contentSearchSearchWithHeaderSearchText");
|
||||||
|
span.appendChild(document.createTextNode(" " + this.input.value + " "));
|
||||||
|
searchWithHeader.appendChild(span);
|
||||||
|
searchWithHeader.appendChild(document.createTextNode(this._strings.searchWith));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchWithHeader.appendChild(document.createTextNode(this._strings.searchWithHeader));
|
||||||
|
},
|
||||||
|
|
||||||
|
_speculativeConnect: function () {
|
||||||
|
if (this.defaultEngine) {
|
||||||
|
this._sendMsg("SpeculativeConnect", this.defaultEngine.name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeTableRow: function (type, suggestionStr, currentRow, searchWords) {
|
||||||
|
let row = document.createElementNS(HTML_NS, "tr");
|
||||||
|
row.dir = "auto";
|
||||||
|
row.classList.add("contentSearchSuggestionRow");
|
||||||
|
row.classList.add(type);
|
||||||
|
row.setAttribute("role", "presentation");
|
||||||
|
row.addEventListener("mousemove", this);
|
||||||
|
row.addEventListener("mouseup", this);
|
||||||
|
|
||||||
|
let entry = document.createElementNS(HTML_NS, "td");
|
||||||
|
let img = document.createElementNS(HTML_NS, "div");
|
||||||
|
img.setAttribute("class", "historyIcon");
|
||||||
|
entry.appendChild(img);
|
||||||
|
entry.classList.add("contentSearchSuggestionEntry");
|
||||||
|
entry.setAttribute("role", "option");
|
||||||
|
entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
|
||||||
|
entry.setAttribute("aria-selected", "false");
|
||||||
|
|
||||||
|
let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
|
||||||
|
for (let i = 0; i < suggestionWords.length; i++) {
|
||||||
|
let word = suggestionWords[i];
|
||||||
|
let wordSpan = document.createElementNS(HTML_NS, "span");
|
||||||
|
if (searchWords.has(word)) {
|
||||||
|
wordSpan.classList.add("typed");
|
||||||
|
}
|
||||||
|
wordSpan.textContent = word;
|
||||||
|
entry.appendChild(wordSpan);
|
||||||
|
if (i < suggestionWords.length - 1) {
|
||||||
|
entry.appendChild(document.createTextNode(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row.appendChild(entry);
|
||||||
|
return row;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Converts favicon array buffer into data URI of the right size and dpi.
|
||||||
|
_getFaviconURIFromBuffer: function (buffer) {
|
||||||
|
let blob = new Blob([buffer]);
|
||||||
|
let dpiSize = Math.round(16 * window.devicePixelRatio);
|
||||||
|
let sizeStr = dpiSize + "," + dpiSize;
|
||||||
|
return URL.createObjectURL(blob) + "#-moz-resolution=" + sizeStr;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSearchEngines: function () {
|
||||||
|
this._sendMsg("GetState");
|
||||||
|
},
|
||||||
|
|
||||||
|
_getStrings: function () {
|
||||||
|
this._sendMsg("GetStrings");
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSuggestions: function () {
|
||||||
|
this._stickyInputValue = this.input.value;
|
||||||
|
if (this.defaultEngine) {
|
||||||
|
this._sendMsg("GetSuggestions", {
|
||||||
|
engineName: this.defaultEngine.name,
|
||||||
|
searchString: this.input.value,
|
||||||
|
remoteTimeout: this.remoteTimeout,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearSuggestionRows: function() {
|
||||||
|
while (this._suggestionsList.firstElementChild) {
|
||||||
|
this._suggestionsList.firstElementChild.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_hideSuggestions: function () {
|
||||||
|
this.input.setAttribute("aria-expanded", "false");
|
||||||
|
this._table.hidden = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_indexOfTableItem: function (elt) {
|
||||||
|
if (elt.classList.contains("contentSearchOneOffItem")) {
|
||||||
|
return this.numSuggestions + this._oneOffButtons.indexOf(elt);
|
||||||
|
}
|
||||||
|
if (elt.classList.contains("contentSearchSettingsButton")) {
|
||||||
|
return this.numSuggestions + this._oneOffButtons.length;
|
||||||
|
}
|
||||||
|
while (elt && elt.localName != "tr") {
|
||||||
|
elt = elt.parentNode;
|
||||||
|
}
|
||||||
|
if (!elt) {
|
||||||
|
throw new Error("Element is not a row");
|
||||||
|
}
|
||||||
|
return elt.rowIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeTable: function (id) {
|
||||||
|
this._table = document.createElementNS(HTML_NS, "table");
|
||||||
|
this._table.id = id;
|
||||||
|
this._table.hidden = true;
|
||||||
|
this._table.classList.add("contentSearchSuggestionTable");
|
||||||
|
this._table.setAttribute("role", "presentation");
|
||||||
|
|
||||||
|
// When the search input box loses focus, we want to immediately give focus
|
||||||
|
// back to it if the blur was because the user clicked somewhere in the table.
|
||||||
|
// onBlur uses the _mousedown flag to detect this.
|
||||||
|
this._table.addEventListener("mousedown", () => { this._mousedown = true; });
|
||||||
|
document.addEventListener("mouseup", () => { delete this._mousedown; });
|
||||||
|
|
||||||
|
// Deselect the selected element on mouseout if it wasn't a suggestion.
|
||||||
|
this._table.addEventListener("mouseout", () => {
|
||||||
|
if (this.selectedIndex >= this.numSuggestions) {
|
||||||
|
this.selectAndUpdateInput(-1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If a search is loaded in the same tab, ensure the suggestions dropdown
|
||||||
|
// is hidden immediately when the page starts loading and not when it first
|
||||||
|
// appears, in order to provide timely feedback to the user.
|
||||||
|
window.addEventListener("beforeunload", () => { this._hideSuggestions(); });
|
||||||
|
|
||||||
|
let headerRow = document.createElementNS(HTML_NS, "tr");
|
||||||
|
let header = document.createElementNS(HTML_NS, "td");
|
||||||
|
headerRow.setAttribute("class", "contentSearchHeaderRow");
|
||||||
|
header.setAttribute("class", "contentSearchHeader");
|
||||||
|
let img = document.createElementNS(HTML_NS, "img");
|
||||||
|
img.setAttribute("src", "chrome://browser/skin/search-engine-placeholder.png");
|
||||||
|
header.appendChild(img);
|
||||||
|
header.id = "contentSearchDefaultEngineHeader";
|
||||||
|
headerRow.appendChild(header);
|
||||||
|
headerRow.addEventListener("click", this);
|
||||||
|
this._table.appendChild(headerRow);
|
||||||
|
|
||||||
|
let row = document.createElementNS(HTML_NS, "tr");
|
||||||
|
row.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
let cell = document.createElementNS(HTML_NS, "td");
|
||||||
|
cell.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
this._suggestionsList = document.createElementNS(HTML_NS, "table");
|
||||||
|
this._suggestionsList.setAttribute("class", "contentSearchSuggestionsList");
|
||||||
|
cell.appendChild(this._suggestionsList);
|
||||||
|
row.appendChild(cell);
|
||||||
|
this._table.appendChild(row);
|
||||||
|
this._suggestionsList.setAttribute("role", "listbox");
|
||||||
|
|
||||||
|
this._oneOffsTable = document.createElementNS(HTML_NS, "table");
|
||||||
|
this._oneOffsTable.setAttribute("class", "contentSearchOneOffsTable");
|
||||||
|
this._oneOffsTable.classList.add("contentSearchSuggestionsContainer");
|
||||||
|
this._oneOffsTable.setAttribute("role", "group");
|
||||||
|
this._table.appendChild(this._oneOffsTable);
|
||||||
|
|
||||||
|
headerRow = document.createElementNS(HTML_NS, "tr");
|
||||||
|
header = document.createElementNS(HTML_NS, "td");
|
||||||
|
headerRow.setAttribute("class", "contentSearchHeaderRow");
|
||||||
|
header.setAttribute("class", "contentSearchHeader");
|
||||||
|
headerRow.appendChild(header);
|
||||||
|
header.id = "contentSearchSearchWithHeader";
|
||||||
|
this._oneOffsTable.appendChild(headerRow);
|
||||||
|
|
||||||
|
let button = document.createElementNS(HTML_NS, "button");
|
||||||
|
button.appendChild(document.createTextNode("Change Search Settings"));
|
||||||
|
button.setAttribute("class", "contentSearchSettingsButton");
|
||||||
|
button.classList.add("contentSearchHeaderRow");
|
||||||
|
button.classList.add("contentSearchHeader");
|
||||||
|
button.id = "contentSearchSettingsButton";
|
||||||
|
button.addEventListener("click", this);
|
||||||
|
button.addEventListener("mousemove", this);
|
||||||
|
this._table.appendChild(button);
|
||||||
|
|
||||||
|
return this._table;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setUpOneOffButtons: function () {
|
||||||
|
// Sometimes we receive a CurrentEngine message from the ContentSearch service
|
||||||
|
// before we've received a State message - i.e. before we have our engines.
|
||||||
|
if (!this._engines) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this._oneOffsTable.firstChild.nextSibling) {
|
||||||
|
this._oneOffsTable.firstChild.nextSibling.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._oneOffButtons = [];
|
||||||
|
|
||||||
|
let engines = this._engines.filter(aEngine => aEngine.name != this.defaultEngine.name);
|
||||||
|
if (!engines.length) {
|
||||||
|
this._oneOffsTable.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const kDefaultButtonWidth = 49; // 48px + 1px border.
|
||||||
|
let rowWidth = this.input.offsetWidth - 2; // 2px border.
|
||||||
|
let enginesPerRow = Math.floor(rowWidth / kDefaultButtonWidth);
|
||||||
|
let buttonWidth = Math.floor(rowWidth / enginesPerRow);
|
||||||
|
|
||||||
|
let row = document.createElementNS(HTML_NS, "tr");
|
||||||
|
let cell = document.createElementNS(HTML_NS, "td");
|
||||||
|
row.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
cell.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
|
||||||
|
for (let i = 0; i < engines.length; ++i) {
|
||||||
|
let engine = engines[i];
|
||||||
|
if (i > 0 && i % enginesPerRow == 0) {
|
||||||
|
row.appendChild(cell);
|
||||||
|
this._oneOffsTable.appendChild(row);
|
||||||
|
row = document.createElementNS(HTML_NS, "tr");
|
||||||
|
cell = document.createElementNS(HTML_NS, "td");
|
||||||
|
row.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
cell.setAttribute("class", "contentSearchSuggestionsContainer");
|
||||||
|
}
|
||||||
|
let button = document.createElementNS(HTML_NS, "button");
|
||||||
|
button.setAttribute("class", "contentSearchOneOffItem");
|
||||||
|
let img = document.createElementNS(HTML_NS, "img");
|
||||||
|
let uri = "chrome://browser/skin/search-engine-placeholder.png";
|
||||||
|
if (engine.iconBuffer) {
|
||||||
|
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
|
||||||
|
}
|
||||||
|
img.setAttribute("src", uri);
|
||||||
|
button.appendChild(img);
|
||||||
|
button.style.width = buttonWidth + "px";
|
||||||
|
button.setAttribute("title", engine.name);
|
||||||
|
|
||||||
|
button.engineName = engine.name;
|
||||||
|
button.addEventListener("click", this);
|
||||||
|
button.addEventListener("mousemove", this);
|
||||||
|
|
||||||
|
if (engines.length - i <= enginesPerRow - (i % enginesPerRow)) {
|
||||||
|
button.classList.add("last-row");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i + 1) % enginesPerRow == 0) {
|
||||||
|
button.classList.add("end-of-row");
|
||||||
|
}
|
||||||
|
|
||||||
|
button.id = ONE_OFF_ID_PREFIX + i;
|
||||||
|
cell.appendChild(button);
|
||||||
|
this._oneOffButtons.push(button);
|
||||||
|
}
|
||||||
|
row.appendChild(cell);
|
||||||
|
this._oneOffsTable.appendChild(row);
|
||||||
|
this._oneOffsTable.hidden = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sendMsg: function (type, data=null) {
|
||||||
|
dispatchEvent(new CustomEvent("ContentSearchClient", {
|
||||||
|
detail: {
|
||||||
|
type: type,
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return ContentSearchUIController;
|
||||||
|
})();
|
||||||
@@ -330,7 +330,6 @@ input[type=button] {
|
|||||||
#newtab-search-container {
|
#newtab-search-container {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
-moz-box-align: center;
|
|
||||||
-moz-box-pack: center;
|
-moz-box-pack: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,104 +340,76 @@ input[type=button] {
|
|||||||
|
|
||||||
#newtab-search-form {
|
#newtab-search-form {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
|
position: relative;
|
||||||
|
height: 36px;
|
||||||
-moz-box-flex: 1;
|
-moz-box-flex: 1;
|
||||||
-moz-box-orient: horizontal;
|
|
||||||
-moz-box-align: center;
|
|
||||||
height: 44px; /* 32 + 6 logo top "padding" + 6 logo bottom "padding" */
|
|
||||||
margin: 26px 20px 10px; /* top: 32 - 6 search form top "padding", bottom: 32 - 16 tiles top margin - 6 logo bottom "padding" */
|
|
||||||
max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */
|
max-width: 600px; /* 2 * (290 cell width + 10 cell margin) */
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-logo {
|
#newtab-search-icon {
|
||||||
display: -moz-box;
|
border: 1px transparent;
|
||||||
width: 38px;
|
padding: 0;
|
||||||
height: 38px; /* 26 image height + 6 top "padding" + 6 bottom "padding" */
|
margin: 0;
|
||||||
border: 1px solid transparent;
|
width: 36px;
|
||||||
-moz-margin-end: 8px;
|
height: 36px;
|
||||||
background-repeat: no-repeat;
|
background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
|
||||||
background-position: center;
|
position: absolute;
|
||||||
background-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
|
||||||
background-size: 26px 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#newtab-search-logo.magnifier {
|
|
||||||
width: 38px; /* 26 image width + 6 left "padding" + 6 right "padding" */
|
|
||||||
-moz-margin-end: 5px;
|
|
||||||
background-size: 26px;
|
|
||||||
background-image: url("chrome://browser/skin/magnifier.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
@media not all and (max-resolution: 1dppx) {
|
|
||||||
#newtab-search-logo.magnifier {
|
|
||||||
background-image: url("chrome://browser/skin/magnifier@2x.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#newtab-search-logo[type="logo"] {
|
|
||||||
background-size: 65px 26px;
|
|
||||||
width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
|
|
||||||
}
|
|
||||||
|
|
||||||
#newtab-search-logo[type="favicon"] {
|
|
||||||
background-size: 16px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#newtab-search-logo[hidden] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#newtab-search-logo[active],
|
|
||||||
#newtab-search-logo:hover {
|
|
||||||
background-color: #e9e9e9;
|
|
||||||
border: 1px solid rgb(226, 227, 229);
|
|
||||||
border-radius: 2.5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-text {
|
#newtab-search-text {
|
||||||
height: 38px; /* same height as #newtab-search-logo */
|
|
||||||
-moz-box-flex: 1;
|
-moz-box-flex: 1;
|
||||||
|
padding-top: 6px;
|
||||||
padding: 0 8px;
|
padding-bottom: 6px;
|
||||||
|
padding-left: 34px;
|
||||||
|
padding-right: 8px;
|
||||||
background: hsla(0,0%,100%,.9) padding-box;
|
background: hsla(0,0%,100%,.9) padding-box;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-radius: 2px 0 0 2px;
|
||||||
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
||||||
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
|
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
|
||||||
0 0 2px hsla(210,65%,9%,.1) inset,
|
0 0 2px hsla(210,65%,9%,.1) inset,
|
||||||
0 1px 0 hsla(0,0%,100%,.2);
|
0 1px 0 hsla(0,0%,100%,.2);
|
||||||
border-radius: 2.5px 0 0 2.5px;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-text:-moz-dir(rtl) {
|
#newtab-search-text:-moz-dir(rtl) {
|
||||||
border-radius: 0 2.5px 2.5px 0;
|
border-radius: 0 2px 2px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#newtab-search-text[aria-expanded="true"] {
|
||||||
|
border-radius: 2px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newtab-search-text[aria-expanded="true"]:-moz-dir(rtl) {
|
||||||
|
border-radius: 0 2px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newtab-search-text[keepfocus],
|
||||||
#newtab-search-text:focus,
|
#newtab-search-text:focus,
|
||||||
#newtab-search-text[autofocus] {
|
#newtab-search-text[autofocus] {
|
||||||
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
|
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-submit {
|
#newtab-search-submit {
|
||||||
height: 38px; /* same height as #newtab-search-logo */
|
|
||||||
font-size: 13px !important;
|
|
||||||
|
|
||||||
-moz-margin-start: -1px;
|
-moz-margin-start: -1px;
|
||||||
background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go") center center no-repeat, linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
||||||
padding: 0 9px;
|
padding: 0;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
||||||
|
border-radius: 0 2px 2px 0;
|
||||||
-moz-border-start: 1px solid transparent;
|
-moz-border-start: 1px solid transparent;
|
||||||
border-radius: 0 2.5px 2.5px 0;
|
|
||||||
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
||||||
0 1px 0 hsla(0,0%,100%,.2);
|
0 1px 0 hsla(0,0%,100%,.2);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition-property: background-color, border-color, box-shadow;
|
transition-property: background-color, border-color, box-shadow;
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-submit:-moz-dir(rtl) {
|
#newtab-search-submit:-moz-dir(rtl) {
|
||||||
border-radius: 2.5px 0 0 2.5px;
|
border-radius: 2px 0 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-text:focus + #newtab-search-submit,
|
#newtab-search-text:focus + #newtab-search-submit,
|
||||||
@@ -449,15 +420,16 @@ input[type=button] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-text:focus + #newtab-search-submit,
|
#newtab-search-text:focus + #newtab-search-submit,
|
||||||
|
#newtab-search-text[keepfocus] + #newtab-search-submit,
|
||||||
#newtab-search-text[autofocus] + #newtab-search-submit {
|
#newtab-search-text[autofocus] + #newtab-search-submit {
|
||||||
background-image: linear-gradient(#4cb1ff, #1793e5);
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
|
||||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
||||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||||
0 1px 0 hsla(210,54%,20%,.03);
|
0 1px 0 hsla(210,54%,20%,.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-search-text + #newtab-search-submit:hover {
|
#newtab-search-text + #newtab-search-submit:hover {
|
||||||
background-image: linear-gradient(#66bdff, #0d9eff);
|
background: url("chrome://browser/skin/search-arrow-go.svg#search-arrow-go-inverted") center center no-repeat, linear-gradient(#4cb1ff, #1793e5);
|
||||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
|
||||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||||
0 1px 0 hsla(210,54%,20%,.03),
|
0 1px 0 hsla(210,54%,20%,.03),
|
||||||
@@ -544,14 +516,11 @@ input[type=button] {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent,
|
#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||||
#newtab-search-panel > .panel-arrowcontainer > .panel-arrowcontent {
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newtab-customize-panel-item,
|
.newtab-customize-panel-item {
|
||||||
.newtab-search-panel-engine,
|
|
||||||
#newtab-search-manage {
|
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
-moz-padding-start: 40px;
|
-moz-padding-start: 40px;
|
||||||
@@ -560,22 +529,12 @@ input[type=button] {
|
|||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newtab-customize-panel-item:not(:first-child),
|
.newtab-customize-panel-item:not(:first-child) {
|
||||||
.newtab-search-panel-engine {
|
|
||||||
border-top: 1px solid threedshadow;
|
border-top: 1px solid threedshadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newtab-search-panel-engine > image {
|
|
||||||
-moz-margin-end: 8px;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
|
|
||||||
}
|
|
||||||
|
|
||||||
.newtab-customize-panel-subitem > label,
|
.newtab-customize-panel-subitem > label,
|
||||||
.newtab-customize-panel-item > label,
|
.newtab-customize-panel-item > label,
|
||||||
.newtab-search-panel-engine > label,
|
|
||||||
#newtab-search-manage > label,
|
|
||||||
.newtab-customize-complex-option {
|
.newtab-customize-complex-option {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -625,8 +584,7 @@ input[type=button] {
|
|||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newtab-customize-panel-item[selected],
|
.newtab-customize-panel-item[selected] {
|
||||||
.newtab-search-panel-engine[selected] {
|
|
||||||
background: url("chrome://global/skin/menu/shared-menu-check-active.svg") no-repeat transparent;
|
background: url("chrome://global/skin/menu/shared-menu-check-active.svg") no-repeat transparent;
|
||||||
background-size: 16px 16px;
|
background-size: 16px 16px;
|
||||||
background-position: 15px 15px;
|
background-position: 15px 15px;
|
||||||
@@ -671,7 +629,7 @@ input[type=button] {
|
|||||||
border-top: 1px solid threedshadow;
|
border-top: 1px solid threedshadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchSuggestionTable {
|
.contentSearchSuggestionTable {
|
||||||
font: message-box;
|
font: message-box;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,13 @@
|
|||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||||
<?xml-stylesheet href="chrome://browser/content/searchSuggestionUI.css" type="text/css"?>
|
<?xml-stylesheet href="chrome://browser/content/contentSearchUI.css" type="text/css"?>
|
||||||
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
|
<?xml-stylesheet href="chrome://browser/content/newtab/newTab.css" type="text/css"?>
|
||||||
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
|
<?xml-stylesheet href="chrome://browser/skin/newtab/newTab.css" type="text/css"?>
|
||||||
|
|
||||||
<!DOCTYPE window [
|
<!DOCTYPE window [
|
||||||
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
||||||
%newTabDTD;
|
%newTabDTD;
|
||||||
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
|
|
||||||
%searchBarDTD;
|
|
||||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||||
%browserDTD;
|
%browserDTD;
|
||||||
]>
|
]>
|
||||||
@@ -22,13 +20,6 @@
|
|||||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
title="&newtab.pageTitle;">
|
title="&newtab.pageTitle;">
|
||||||
|
|
||||||
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
|
|
||||||
noautohide="true" hidden="true">
|
|
||||||
<xul:hbox id="newtab-search-manage">
|
|
||||||
<xul:label>&changeSearchSettings.button;</xul:label>
|
|
||||||
</xul:hbox>
|
|
||||||
</xul:panel>
|
|
||||||
|
|
||||||
<div class="newtab-customize-panel-container">
|
<div class="newtab-customize-panel-container">
|
||||||
<div id="newtab-customize-panel" orient="vertical">
|
<div id="newtab-customize-panel" orient="vertical">
|
||||||
<div id="newtab-customize-panel-anchor"></div>
|
<div id="newtab-customize-panel-anchor"></div>
|
||||||
@@ -108,13 +99,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="newtab-search-container">
|
<div id="newtab-search-container">
|
||||||
<form id="newtab-search-form" name="searchForm">
|
<div id="newtab-search-form">
|
||||||
<div id="newtab-search-logo"/>
|
<div id="newtab-search-icon"/>
|
||||||
<input type="text" name="q" value="" id="newtab-search-text"
|
<input type="text" name="q" value="" id="newtab-search-text"
|
||||||
maxlength="256" dir="auto"/>
|
aria-label="&contentSearchInput.label;" maxlength="256" dir="auto"/>
|
||||||
<input id="newtab-search-submit" type="submit"
|
<input id="newtab-search-submit" type="button" value=""
|
||||||
value="&searchEndCap.label;"/>
|
aria-label="&contentSearchSubmit.label;"/>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="newtab-horizontal-margin">
|
<div id="newtab-horizontal-margin">
|
||||||
@@ -133,7 +124,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<xul:script type="text/javascript;version=1.8"
|
<xul:script type="text/javascript;version=1.8"
|
||||||
src="chrome://browser/content/searchSuggestionUI.js"/>
|
src="chrome://browser/content/contentSearchUI.js"/>
|
||||||
<xul:script type="text/javascript;version=1.8"
|
<xul:script type="text/javascript;version=1.8"
|
||||||
src="chrome://browser/content/newtab/newTab.js"/>
|
src="chrome://browser/content/newtab/newTab.js"/>
|
||||||
</xul:window>
|
</xul:window>
|
||||||
|
|||||||
@@ -5,257 +5,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
let gSearch = {
|
let gSearch = {
|
||||||
|
|
||||||
currentEngineName: null,
|
|
||||||
|
|
||||||
get useNewUI() {
|
|
||||||
let newUI = Services.prefs.getBoolPref("browser.search.showOneOffButtons");
|
|
||||||
delete this.useNewUI;
|
|
||||||
this.useNewUI = newUI;
|
|
||||||
return newUI;
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
for (let idSuffix of this._nodeIDSuffixes) {
|
document.getElementById("newtab-search-submit")
|
||||||
this._nodes[idSuffix] =
|
.addEventListener("click", e => this._contentSearchController.search(e));
|
||||||
document.getElementById("newtab-search-" + idSuffix);
|
let textbox = document.getElementById("newtab-search-text");
|
||||||
}
|
this._contentSearchController =
|
||||||
|
new ContentSearchUIController(textbox, textbox.parentNode, "newtab", "newtab");
|
||||||
if (this.useNewUI) {
|
|
||||||
this._nodes.logo.classList.add("magnifier");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("ContentSearchService", this);
|
|
||||||
this._send("GetState");
|
|
||||||
},
|
|
||||||
|
|
||||||
showPanel: function () {
|
|
||||||
let panel = this._nodes.panel;
|
|
||||||
let logo = this._nodes.logo;
|
|
||||||
panel.hidden = false;
|
|
||||||
panel.openPopup(logo);
|
|
||||||
logo.setAttribute("active", "true");
|
|
||||||
panel.addEventListener("popuphidden", function onHidden() {
|
|
||||||
panel.removeEventListener("popuphidden", onHidden);
|
|
||||||
panel.hidden = true;
|
|
||||||
logo.removeAttribute("active");
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
search: function (event) {
|
|
||||||
if (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
let searchText = this._nodes.text;
|
|
||||||
let searchStr = searchText.value;
|
|
||||||
if (this.currentEngineName && searchStr.length) {
|
|
||||||
let useNewTab = event && event.button == 1;
|
|
||||||
let eventData = {
|
|
||||||
engineName: this.currentEngineName,
|
|
||||||
searchString: searchStr,
|
|
||||||
whence: "newtab",
|
|
||||||
originalEvent: {
|
|
||||||
target: {
|
|
||||||
ownerDocument: null
|
|
||||||
},
|
|
||||||
shiftKey: event.shiftKey,
|
|
||||||
ctrlKey: event.ctrlKey,
|
|
||||||
metaKey: event.metaKey,
|
|
||||||
altKey: event.altKey,
|
|
||||||
button: event.button,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchText.hasAttribute("selection-index")) {
|
|
||||||
eventData.selection = {
|
|
||||||
index: searchText.getAttribute("selection-index"),
|
|
||||||
kind: searchText.getAttribute("selection-kind")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this._send("Search", eventData);
|
|
||||||
}
|
|
||||||
this._suggestionController.addInputValueToFormHistory();
|
|
||||||
},
|
|
||||||
|
|
||||||
manageEngines: function () {
|
|
||||||
this._nodes.panel.hidePopup();
|
|
||||||
this._send("ManageEngines");
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEvent: function (event) {
|
|
||||||
let methodName = "on" + event.detail.type;
|
|
||||||
if (this.hasOwnProperty(methodName)) {
|
|
||||||
this[methodName](event.detail.data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onState: function (data) {
|
|
||||||
this._newEngines = data.engines;
|
|
||||||
this._setCurrentEngine(data.currentEngine);
|
|
||||||
this._initWhenInitalStateReceived();
|
|
||||||
},
|
|
||||||
|
|
||||||
onCurrentState: function (data) {
|
|
||||||
if (this._initialStateReceived) {
|
|
||||||
this._newEngines = data.engines;
|
|
||||||
this._setCurrentEngine(data.currentEngine);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onCurrentEngine: function (engineName) {
|
|
||||||
if (this._initialStateReceived) {
|
|
||||||
this._nodes.panel.hidePopup();
|
|
||||||
this._setCurrentEngine(engineName);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onFocusInput: function () {
|
|
||||||
this._nodes.text.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
_nodeIDSuffixes: [
|
|
||||||
"form",
|
|
||||||
"logo",
|
|
||||||
"manage",
|
|
||||||
"panel",
|
|
||||||
"text",
|
|
||||||
],
|
|
||||||
|
|
||||||
_nodes: {},
|
|
||||||
|
|
||||||
_initWhenInitalStateReceived: function () {
|
|
||||||
this._nodes.form.addEventListener("submit", e => this.search(e));
|
|
||||||
this._nodes.logo.addEventListener("click", e => this.showPanel());
|
|
||||||
this._nodes.manage.addEventListener("click", e => this.manageEngines());
|
|
||||||
this._nodes.panel.addEventListener("popupshowing", e => this._setUpPanel());
|
|
||||||
this._initialStateReceived = true;
|
|
||||||
this._initWhenInitalStateReceived = function () {};
|
|
||||||
},
|
|
||||||
|
|
||||||
_send: function (type, data=null) {
|
|
||||||
window.dispatchEvent(new CustomEvent("ContentSearchClient", {
|
|
||||||
detail: {
|
|
||||||
type: type,
|
|
||||||
data: data,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_setUpPanel: function () {
|
|
||||||
// The new search UI only contains the "manage" engine entry in the panel
|
|
||||||
if (this.useNewUI) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the panel if necessary.
|
|
||||||
if (this._newEngines) {
|
|
||||||
this._buildPanel(this._newEngines);
|
|
||||||
delete this._newEngines;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the selected states of the engines.
|
|
||||||
let panel = this._nodes.panel;
|
|
||||||
for (let box of panel.childNodes) {
|
|
||||||
if (box.getAttribute("engine") == this.currentEngineName) {
|
|
||||||
box.setAttribute("selected", "true");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
box.removeAttribute("selected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_buildPanel: function (engines) {
|
|
||||||
let panel = this._nodes.panel;
|
|
||||||
|
|
||||||
// Empty the panel except for the Manage Engines row.
|
|
||||||
let i = 0;
|
|
||||||
while (i < panel.childNodes.length) {
|
|
||||||
let node = panel.childNodes[i];
|
|
||||||
if (node != this._nodes.manage) {
|
|
||||||
panel.removeChild(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all the engines.
|
|
||||||
for (let engine of engines) {
|
|
||||||
panel.insertBefore(this._makePanelEngine(panel, engine),
|
|
||||||
this._nodes.manage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Converts favicon array buffer into data URI of the right size and dpi.
|
|
||||||
_getFaviconURIFromBuffer: function (buffer) {
|
|
||||||
let blob = new Blob([buffer]);
|
|
||||||
let dpiSize = Math.round(16 * window.devicePixelRatio);
|
|
||||||
let sizeStr = dpiSize + "," + dpiSize;
|
|
||||||
return URL.createObjectURL(blob) + "#-moz-resolution=" + sizeStr;
|
|
||||||
},
|
|
||||||
|
|
||||||
_makePanelEngine: function (panel, engine) {
|
|
||||||
let box = document.createElementNS(XUL_NAMESPACE, "hbox");
|
|
||||||
box.className = "newtab-search-panel-engine";
|
|
||||||
box.setAttribute("engine", engine.name);
|
|
||||||
|
|
||||||
box.addEventListener("click", () => {
|
|
||||||
this._send("SetCurrentEngine", engine.name);
|
|
||||||
panel.hidePopup();
|
|
||||||
this._nodes.text.focus();
|
|
||||||
});
|
|
||||||
|
|
||||||
let image = document.createElementNS(XUL_NAMESPACE, "image");
|
|
||||||
if (engine.iconBuffer) {
|
|
||||||
let uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
|
|
||||||
image.setAttribute("src", uri);
|
|
||||||
}
|
|
||||||
box.appendChild(image);
|
|
||||||
|
|
||||||
let label = document.createElementNS(XUL_NAMESPACE, "label");
|
|
||||||
label.setAttribute("value", engine.name);
|
|
||||||
box.appendChild(label);
|
|
||||||
|
|
||||||
return box;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setCurrentEngine: function (engine) {
|
|
||||||
this.currentEngineName = engine.name;
|
|
||||||
|
|
||||||
if (!this.useNewUI) {
|
|
||||||
let type = "";
|
|
||||||
let uri;
|
|
||||||
let logoBuf = window.devicePixelRatio >= 2 ?
|
|
||||||
engine.logo2xBuffer || engine.logoBuffer :
|
|
||||||
engine.logoBuffer || engine.logo2xBuffer;
|
|
||||||
if (logoBuf) {
|
|
||||||
uri = URL.createObjectURL(new Blob([logoBuf]));
|
|
||||||
type = "logo";
|
|
||||||
}
|
|
||||||
else if (engine.iconBuffer) {
|
|
||||||
uri = this._getFaviconURIFromBuffer(engine.iconBuffer);
|
|
||||||
type = "favicon";
|
|
||||||
}
|
|
||||||
this._nodes.logo.setAttribute("type", type);
|
|
||||||
|
|
||||||
if (uri) {
|
|
||||||
this._nodes.logo.style.backgroundImage = "url(" + uri + ")";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._nodes.logo.style.backgroundImage = "";
|
|
||||||
}
|
|
||||||
this._nodes.text.placeholder = engine.placeholder;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the suggestion controller.
|
|
||||||
if (!this._suggestionController) {
|
|
||||||
let parent = document.getElementById("newtab-scrollbox");
|
|
||||||
this._suggestionController =
|
|
||||||
new SearchSuggestionUIController(this._nodes.text, parent,
|
|
||||||
event => this.search(event));
|
|
||||||
}
|
|
||||||
this._suggestionController.engineName = engine.name;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,44 +0,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/. */
|
|
||||||
|
|
||||||
.searchSuggestionTable {
|
|
||||||
background-color: hsla(0,0%,100%,.99);
|
|
||||||
border: 1px solid;
|
|
||||||
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
|
|
||||||
border-spacing: 0;
|
|
||||||
border-top: 0;
|
|
||||||
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
|
|
||||||
0 0 2px hsla(210,65%,9%,.1) inset,
|
|
||||||
0 1px 0 hsla(0,0%,100%,.2);
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0;
|
|
||||||
position: absolute;
|
|
||||||
text-align: start;
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchSuggestionRow {
|
|
||||||
cursor: default;
|
|
||||||
margin: 0;
|
|
||||||
max-width: inherit;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchSuggestionRow.formHistory + .searchSuggestionRow.remote > td {
|
|
||||||
border-top: 1px solid GrayText;
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchSuggestionRow.selected {
|
|
||||||
background-color: hsl(210,100%,40%);
|
|
||||||
color: hsl(0,0%,100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchSuggestionEntry {
|
|
||||||
margin: 0;
|
|
||||||
max-width: inherit;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 6px 8px;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
@@ -1,400 +0,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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.SearchSuggestionUIController = (function () {
|
|
||||||
|
|
||||||
const MAX_DISPLAYED_SUGGESTIONS = 6;
|
|
||||||
const SUGGESTION_ID_PREFIX = "searchSuggestion";
|
|
||||||
const CSS_URI = "chrome://browser/content/searchSuggestionUI.css";
|
|
||||||
|
|
||||||
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new object that manages search suggestions and their UI for a text
|
|
||||||
* box.
|
|
||||||
*
|
|
||||||
* The UI consists of an html:table that's inserted into the DOM after the given
|
|
||||||
* text box and styled so that it appears as a dropdown below the text box.
|
|
||||||
*
|
|
||||||
* @param inputElement
|
|
||||||
* Search suggestions will be based on the text in this text box.
|
|
||||||
* Assumed to be an html:input. xul:textbox is untested but might work.
|
|
||||||
* @param tableParent
|
|
||||||
* The suggestion table is appended as a child to this element. Since
|
|
||||||
* the table is absolutely positioned and its top and left values are set
|
|
||||||
* to be relative to the top and left of the page, either the parent and
|
|
||||||
* all its ancestors should not be positioned elements (i.e., their
|
|
||||||
* positions should be "static"), or the parent's position should be the
|
|
||||||
* top left of the page.
|
|
||||||
* @param onClick
|
|
||||||
* A function that's called when a search suggestion is clicked. Ideally
|
|
||||||
* we could call submit() on inputElement's ancestor form, but that
|
|
||||||
* doesn't trigger submit listeners. The function is passed one argument,
|
|
||||||
* the click event.
|
|
||||||
* @param idPrefix
|
|
||||||
* The IDs of elements created by the object will be prefixed with this
|
|
||||||
* string.
|
|
||||||
*/
|
|
||||||
function SearchSuggestionUIController(inputElement, tableParent, onClick=null,
|
|
||||||
idPrefix="") {
|
|
||||||
this.input = inputElement;
|
|
||||||
this.onClick = onClick;
|
|
||||||
this._idPrefix = idPrefix;
|
|
||||||
|
|
||||||
let tableID = idPrefix + "searchSuggestionTable";
|
|
||||||
this.input.autocomplete = "off";
|
|
||||||
this.input.setAttribute("aria-autocomplete", "true");
|
|
||||||
this.input.setAttribute("aria-controls", tableID);
|
|
||||||
tableParent.appendChild(this._makeTable(tableID));
|
|
||||||
|
|
||||||
this.input.addEventListener("keypress", this);
|
|
||||||
this.input.addEventListener("input", this);
|
|
||||||
this.input.addEventListener("focus", this);
|
|
||||||
this.input.addEventListener("blur", this);
|
|
||||||
window.addEventListener("ContentSearchService", this);
|
|
||||||
|
|
||||||
this._stickyInputValue = "";
|
|
||||||
this._hideSuggestions();
|
|
||||||
|
|
||||||
this._ignoreInputEvent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchSuggestionUIController.prototype = {
|
|
||||||
|
|
||||||
// The timeout (ms) of the remote suggestions. Corresponds to
|
|
||||||
// SearchSuggestionController.remoteTimeout. Uses
|
|
||||||
// SearchSuggestionController's default timeout if falsey.
|
|
||||||
remoteTimeout: undefined,
|
|
||||||
|
|
||||||
get engineName() {
|
|
||||||
return this._engineName;
|
|
||||||
},
|
|
||||||
|
|
||||||
set engineName(val) {
|
|
||||||
this._engineName = val;
|
|
||||||
if (val && document.activeElement == this.input) {
|
|
||||||
this._speculativeConnect();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get selectedIndex() {
|
|
||||||
for (let i = 0; i < this._table.children.length; i++) {
|
|
||||||
let row = this._table.children[i];
|
|
||||||
if (row.classList.contains("selected")) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
set selectedIndex(idx) {
|
|
||||||
// Update the table's rows, and the input when there is a selection.
|
|
||||||
this._table.removeAttribute("aria-activedescendant");
|
|
||||||
for (let i = 0; i < this._table.children.length; i++) {
|
|
||||||
let row = this._table.children[i];
|
|
||||||
if (i == idx) {
|
|
||||||
row.classList.add("selected");
|
|
||||||
row.firstChild.setAttribute("aria-selected", "true");
|
|
||||||
this._table.setAttribute("aria-activedescendant", row.firstChild.id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
row.classList.remove("selected");
|
|
||||||
row.firstChild.setAttribute("aria-selected", "false");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get numSuggestions() {
|
|
||||||
return this._table.children.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
selectAndUpdateInput: function (idx) {
|
|
||||||
this.selectedIndex = idx;
|
|
||||||
this.input.value = idx >= 0 ? this.suggestionAtIndex(idx) :
|
|
||||||
this._stickyInputValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
suggestionAtIndex: function (idx) {
|
|
||||||
let row = this._table.children[idx];
|
|
||||||
return row ? row.textContent : null;
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteSuggestionAtIndex: function (idx) {
|
|
||||||
// Only form history suggestions can be deleted.
|
|
||||||
if (this.isFormHistorySuggestionAtIndex(idx)) {
|
|
||||||
let suggestionStr = this.suggestionAtIndex(idx);
|
|
||||||
this._sendMsg("RemoveFormHistoryEntry", suggestionStr);
|
|
||||||
this._table.children[idx].remove();
|
|
||||||
this.selectAndUpdateInput(-1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
isFormHistorySuggestionAtIndex: function (idx) {
|
|
||||||
let row = this._table.children[idx];
|
|
||||||
return row && row.classList.contains("formHistory");
|
|
||||||
},
|
|
||||||
|
|
||||||
addInputValueToFormHistory: function () {
|
|
||||||
this._sendMsg("AddFormHistoryEntry", this.input.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEvent: function (event) {
|
|
||||||
this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onInput: function (event) {
|
|
||||||
if (this._ignoreInputEvent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.input.value) {
|
|
||||||
this._getSuggestions();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._stickyInputValue = "";
|
|
||||||
this._hideSuggestions();
|
|
||||||
}
|
|
||||||
this.selectedIndex = -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onKeypress: function (event) {
|
|
||||||
let selectedIndexDelta = 0;
|
|
||||||
switch (event.keyCode) {
|
|
||||||
case event.DOM_VK_UP:
|
|
||||||
if (this.numSuggestions) {
|
|
||||||
selectedIndexDelta = -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case event.DOM_VK_DOWN:
|
|
||||||
if (this.numSuggestions) {
|
|
||||||
selectedIndexDelta = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._getSuggestions();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case event.DOM_VK_RIGHT:
|
|
||||||
// Allow normal caret movement until the caret is at the end of the input.
|
|
||||||
if (this.input.selectionStart != this.input.selectionEnd ||
|
|
||||||
this.input.selectionEnd != this.input.value.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// else, fall through
|
|
||||||
case event.DOM_VK_RETURN:
|
|
||||||
if (this.selectedIndex >= 0) {
|
|
||||||
this.input.value = this.suggestionAtIndex(this.selectedIndex);
|
|
||||||
this.input.setAttribute("selection-index", this.selectedIndex);
|
|
||||||
this.input.setAttribute("selection-kind", "key");
|
|
||||||
} else {
|
|
||||||
// If we didn't select anything, make sure to remove the attributes
|
|
||||||
// in case they were populated last time.
|
|
||||||
this.input.removeAttribute("selection-index");
|
|
||||||
this.input.removeAttribute("selection-kind");
|
|
||||||
}
|
|
||||||
this._stickyInputValue = this.input.value;
|
|
||||||
this._hideSuggestions();
|
|
||||||
break;
|
|
||||||
case event.DOM_VK_DELETE:
|
|
||||||
if (this.selectedIndex >= 0) {
|
|
||||||
this.deleteSuggestionAtIndex(this.selectedIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedIndexDelta) {
|
|
||||||
// Update the selection.
|
|
||||||
let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
|
|
||||||
if (newSelectedIndex < -1) {
|
|
||||||
newSelectedIndex = this.numSuggestions - 1;
|
|
||||||
}
|
|
||||||
else if (this.numSuggestions <= newSelectedIndex) {
|
|
||||||
newSelectedIndex = -1;
|
|
||||||
}
|
|
||||||
this.selectAndUpdateInput(newSelectedIndex);
|
|
||||||
|
|
||||||
// Prevent the input's caret from moving.
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFocus: function () {
|
|
||||||
this._speculativeConnect();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onBlur: function () {
|
|
||||||
this._hideSuggestions();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMousemove: function (event) {
|
|
||||||
this.selectedIndex = this._indexOfTableRowOrDescendent(event.target);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMousedown: function (event) {
|
|
||||||
if (event.button == 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let idx = this._indexOfTableRowOrDescendent(event.target);
|
|
||||||
let suggestion = this.suggestionAtIndex(idx);
|
|
||||||
this._stickyInputValue = suggestion;
|
|
||||||
|
|
||||||
// Setting value commits composition string forcibly. While IME commits
|
|
||||||
// composition, this needs to ignore input event at committed composition
|
|
||||||
// string which will be overwritten by the suggestion.
|
|
||||||
this._ignoreInputEvent = true;
|
|
||||||
this.input.value = suggestion;
|
|
||||||
this._ignoreInputEvent = false;
|
|
||||||
this.input.setAttribute("selection-index", idx);
|
|
||||||
this.input.setAttribute("selection-kind", "mouse");
|
|
||||||
this._hideSuggestions();
|
|
||||||
if (this.onClick) {
|
|
||||||
this.onClick.call(null, event);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onContentSearchService: function (event) {
|
|
||||||
let methodName = "_onMsg" + event.detail.type;
|
|
||||||
if (methodName in this) {
|
|
||||||
this[methodName](event.detail.data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_onMsgSuggestions: function (suggestions) {
|
|
||||||
// Ignore the suggestions if their search string or engine doesn't match
|
|
||||||
// ours. Due to the async nature of message passing, this can easily happen
|
|
||||||
// when the user types quickly.
|
|
||||||
if (this._stickyInputValue != suggestions.searchString ||
|
|
||||||
this.engineName != suggestions.engineName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty the table.
|
|
||||||
while (this._table.firstElementChild) {
|
|
||||||
this._table.firstElementChild.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position and size the table.
|
|
||||||
let { left, bottom } = this.input.getBoundingClientRect();
|
|
||||||
this._table.style.left = (left + window.scrollX) + "px";
|
|
||||||
this._table.style.top = (bottom + window.scrollY) + "px";
|
|
||||||
this._table.style.minWidth = this.input.offsetWidth + "px";
|
|
||||||
this._table.style.maxWidth = (window.innerWidth - left - 40) + "px";
|
|
||||||
|
|
||||||
// Add the suggestions to the table.
|
|
||||||
let searchWords =
|
|
||||||
new Set(suggestions.searchString.trim().toLowerCase().split(/\s+/));
|
|
||||||
for (let i = 0; i < MAX_DISPLAYED_SUGGESTIONS; i++) {
|
|
||||||
let type, idx;
|
|
||||||
if (i < suggestions.formHistory.length) {
|
|
||||||
[type, idx] = ["formHistory", i];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let j = i - suggestions.formHistory.length;
|
|
||||||
if (j < suggestions.remote.length) {
|
|
||||||
[type, idx] = ["remote", j];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._table.appendChild(this._makeTableRow(type, suggestions[type][idx],
|
|
||||||
i, searchWords));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._table.hidden = false;
|
|
||||||
this.input.setAttribute("aria-expanded", "true");
|
|
||||||
},
|
|
||||||
|
|
||||||
_speculativeConnect: function () {
|
|
||||||
if (this.engineName) {
|
|
||||||
this._sendMsg("SpeculativeConnect", this.engineName);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_makeTableRow: function (type, suggestionStr, currentRow, searchWords) {
|
|
||||||
let row = document.createElementNS(HTML_NS, "tr");
|
|
||||||
row.dir = "auto";
|
|
||||||
row.classList.add("searchSuggestionRow");
|
|
||||||
row.classList.add(type);
|
|
||||||
row.setAttribute("role", "presentation");
|
|
||||||
row.addEventListener("mousemove", this);
|
|
||||||
row.addEventListener("mousedown", this);
|
|
||||||
|
|
||||||
let entry = document.createElementNS(HTML_NS, "td");
|
|
||||||
entry.classList.add("searchSuggestionEntry");
|
|
||||||
entry.setAttribute("role", "option");
|
|
||||||
entry.id = this._idPrefix + SUGGESTION_ID_PREFIX + currentRow;
|
|
||||||
entry.setAttribute("aria-selected", "false");
|
|
||||||
|
|
||||||
let suggestionWords = suggestionStr.trim().toLowerCase().split(/\s+/);
|
|
||||||
for (let i = 0; i < suggestionWords.length; i++) {
|
|
||||||
let word = suggestionWords[i];
|
|
||||||
let wordSpan = document.createElementNS(HTML_NS, "span");
|
|
||||||
if (searchWords.has(word)) {
|
|
||||||
wordSpan.classList.add("typed");
|
|
||||||
}
|
|
||||||
wordSpan.textContent = word;
|
|
||||||
entry.appendChild(wordSpan);
|
|
||||||
if (i < suggestionWords.length - 1) {
|
|
||||||
entry.appendChild(document.createTextNode(" "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row.appendChild(entry);
|
|
||||||
return row;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getSuggestions: function () {
|
|
||||||
this._stickyInputValue = this.input.value;
|
|
||||||
if (this.engineName) {
|
|
||||||
this._sendMsg("GetSuggestions", {
|
|
||||||
engineName: this.engineName,
|
|
||||||
searchString: this.input.value,
|
|
||||||
remoteTimeout: this.remoteTimeout,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_hideSuggestions: function () {
|
|
||||||
this.input.setAttribute("aria-expanded", "false");
|
|
||||||
this._table.hidden = true;
|
|
||||||
while (this._table.firstElementChild) {
|
|
||||||
this._table.firstElementChild.remove();
|
|
||||||
}
|
|
||||||
this.selectAndUpdateInput(-1);
|
|
||||||
},
|
|
||||||
|
|
||||||
_indexOfTableRowOrDescendent: function (row) {
|
|
||||||
while (row && row.localName != "tr") {
|
|
||||||
row = row.parentNode;
|
|
||||||
}
|
|
||||||
if (!row) {
|
|
||||||
throw new Error("Element is not a row");
|
|
||||||
}
|
|
||||||
return row.rowIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
_makeTable: function (id) {
|
|
||||||
this._table = document.createElementNS(HTML_NS, "table");
|
|
||||||
this._table.id = id;
|
|
||||||
this._table.hidden = true;
|
|
||||||
this._table.classList.add("searchSuggestionTable");
|
|
||||||
this._table.setAttribute("role", "listbox");
|
|
||||||
return this._table;
|
|
||||||
},
|
|
||||||
|
|
||||||
_sendMsg: function (type, data=null) {
|
|
||||||
dispatchEvent(new CustomEvent("ContentSearchClient", {
|
|
||||||
detail: {
|
|
||||||
type: type,
|
|
||||||
data: data,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return SearchSuggestionUIController;
|
|
||||||
})();
|
|
||||||
@@ -114,12 +114,6 @@ let AboutHomeListener = {
|
|||||||
case "AboutHomeLoad":
|
case "AboutHomeLoad":
|
||||||
this.onPageLoad();
|
this.onPageLoad();
|
||||||
break;
|
break;
|
||||||
case "AboutHomeSearchEvent":
|
|
||||||
this.onSearch(aEvent);
|
|
||||||
break;
|
|
||||||
case "AboutHomeSearchPanel":
|
|
||||||
this.onOpenSearchPanel(aEvent);
|
|
||||||
break;
|
|
||||||
case "click":
|
case "click":
|
||||||
this.onClick(aEvent);
|
this.onClick(aEvent);
|
||||||
break;
|
break;
|
||||||
@@ -137,9 +131,6 @@ let AboutHomeListener = {
|
|||||||
case "AboutHome:Update":
|
case "AboutHome:Update":
|
||||||
this.onUpdate(aMessage.data);
|
this.onUpdate(aMessage.data);
|
||||||
break;
|
break;
|
||||||
case "AboutHome:FocusInput":
|
|
||||||
this.onFocusInput();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -150,13 +141,11 @@ let AboutHomeListener = {
|
|||||||
|
|
||||||
// Inject search engine and snippets URL.
|
// Inject search engine and snippets URL.
|
||||||
let docElt = doc.documentElement;
|
let docElt = doc.documentElement;
|
||||||
// set the following attributes BEFORE searchEngineName, which triggers to
|
// Set snippetsVersion last, which triggers to show the snippets when it's set.
|
||||||
// show the snippets when it's set.
|
|
||||||
docElt.setAttribute("snippetsURL", aData.snippetsURL);
|
docElt.setAttribute("snippetsURL", aData.snippetsURL);
|
||||||
if (aData.showKnowYourRights)
|
if (aData.showKnowYourRights)
|
||||||
docElt.setAttribute("showKnowYourRights", "true");
|
docElt.setAttribute("showKnowYourRights", "true");
|
||||||
docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
|
docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
|
||||||
docElt.setAttribute("searchEngineName", aData.defaultEngineName);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onPageLoad: function() {
|
onPageLoad: function() {
|
||||||
@@ -167,7 +156,6 @@ let AboutHomeListener = {
|
|||||||
|
|
||||||
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
||||||
addMessageListener("AboutHome:Update", this);
|
addMessageListener("AboutHome:Update", this);
|
||||||
addMessageListener("AboutHome:FocusInput", this);
|
|
||||||
addEventListener("click", this, true);
|
addEventListener("click", this, true);
|
||||||
addEventListener("pagehide", this, true);
|
addEventListener("pagehide", this, true);
|
||||||
|
|
||||||
@@ -176,8 +164,6 @@ let AboutHomeListener = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendAsyncMessage("AboutHome:RequestUpdate");
|
sendAsyncMessage("AboutHome:RequestUpdate");
|
||||||
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
|
|
||||||
doc.addEventListener("AboutHomeSearchPanel", this, true, true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function(aEvent) {
|
onClick: function(aEvent) {
|
||||||
@@ -228,10 +214,6 @@ let AboutHomeListener = {
|
|||||||
case "settings":
|
case "settings":
|
||||||
sendAsyncMessage("AboutHome:Settings");
|
sendAsyncMessage("AboutHome:Settings");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "searchIcon":
|
|
||||||
sendAsyncMessage("AboutHome:OpenSearchPanel", null, { anchor: originalTarget });
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -246,21 +228,6 @@ let AboutHomeListener = {
|
|||||||
aEvent.target.documentElement.removeAttribute("hasBrowserHandlers");
|
aEvent.target.documentElement.removeAttribute("hasBrowserHandlers");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onSearch: function(aEvent) {
|
|
||||||
sendAsyncMessage("AboutHome:Search", { searchData: aEvent.detail });
|
|
||||||
},
|
|
||||||
|
|
||||||
onOpenSearchPanel: function(aEvent) {
|
|
||||||
sendAsyncMessage("AboutHome:OpenSearchPanel");
|
|
||||||
},
|
|
||||||
|
|
||||||
onFocusInput: function () {
|
|
||||||
let searchInput = content.document.getElementById("searchText");
|
|
||||||
if (searchInput) {
|
|
||||||
searchInput.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
AboutHomeListener.init(this);
|
AboutHomeListener.init(this);
|
||||||
|
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ browser.jar:
|
|||||||
* content/browser/sanitize.xul (content/sanitize.xul)
|
* content/browser/sanitize.xul (content/sanitize.xul)
|
||||||
* content/browser/sanitizeDialog.js (content/sanitizeDialog.js)
|
* content/browser/sanitizeDialog.js (content/sanitizeDialog.js)
|
||||||
content/browser/sanitizeDialog.css (content/sanitizeDialog.css)
|
content/browser/sanitizeDialog.css (content/sanitizeDialog.css)
|
||||||
content/browser/searchSuggestionUI.js (content/searchSuggestionUI.js)
|
content/browser/contentSearchUI.js (content/contentSearchUI.js)
|
||||||
content/browser/searchSuggestionUI.css (content/searchSuggestionUI.css)
|
content/browser/contentSearchUI.css (content/contentSearchUI.css)
|
||||||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||||
content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<!ENTITY abouthome.pageTitle "&brandFullName; Start Page">
|
<!ENTITY abouthome.pageTitle "&brandFullName; Start Page">
|
||||||
|
|
||||||
<!ENTITY abouthome.searchEngineButton.label "Search">
|
<!ENTITY abouthome.search.placeholder "Search">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1):
|
<!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1):
|
||||||
text in <a/> will be linked to the Firefox features page on mozilla.com
|
text in <a/> will be linked to the Firefox features page on mozilla.com
|
||||||
|
|||||||
@@ -421,6 +421,12 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
|||||||
<!ENTITY searchFocus.commandkey2 "e">
|
<!ENTITY searchFocus.commandkey2 "e">
|
||||||
<!ENTITY searchFocusUnix.commandkey "j">
|
<!ENTITY searchFocusUnix.commandkey "j">
|
||||||
|
|
||||||
|
<!-- LOCALIZATION NOTE (contentSearchInput.label, contentSearchSubmit.label):
|
||||||
|
These are set as the aria-label attribute for the search input box and
|
||||||
|
submit button in the in-content search UI, to be used by screen readers. -->
|
||||||
|
<!ENTITY contentSearchInput.label "Search query">
|
||||||
|
<!ENTITY contentSearchSubmit.label "Submit search">
|
||||||
|
|
||||||
<!-- LOCALIZATION NOTE (searchFor.label, searchWith.label):
|
<!-- LOCALIZATION NOTE (searchFor.label, searchWith.label):
|
||||||
These two strings are used to build the header above the list of one-click
|
These two strings are used to build the header above the list of one-click
|
||||||
search providers: "Search for <used typed keywords> with:" -->
|
search providers: "Search for <used typed keywords> with:" -->
|
||||||
|
|||||||
@@ -32,3 +32,15 @@ cmd_addFoundEngine=Add "%S"
|
|||||||
# search panel using the cmd_addFoundEngine string, they will be
|
# search panel using the cmd_addFoundEngine string, they will be
|
||||||
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
|
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
|
||||||
cmd_addFoundEngineMenu=Add search engine
|
cmd_addFoundEngineMenu=Add search engine
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (searchFor, searchWith):
|
||||||
|
# These two strings are used to build the header above the list of one-click
|
||||||
|
# search providers: "Search for <user-typed keywords> with:"
|
||||||
|
searchFor=Search for
|
||||||
|
searchWith= with:
|
||||||
|
|
||||||
|
# LOCALIZATION NOTE (searchWithHeader):
|
||||||
|
# The wording of this string should be as close as possible to
|
||||||
|
# searchFor and searchWith. This string will be used instead of
|
||||||
|
# them when the user has not typed any keyword.
|
||||||
|
searchWithHeader=Search with:
|
||||||
|
|||||||
@@ -100,8 +100,6 @@ let AboutHome = {
|
|||||||
"AboutHome:Sync",
|
"AboutHome:Sync",
|
||||||
"AboutHome:Settings",
|
"AboutHome:Settings",
|
||||||
"AboutHome:RequestUpdate",
|
"AboutHome:RequestUpdate",
|
||||||
"AboutHome:Search",
|
|
||||||
"AboutHome:OpenSearchPanel",
|
|
||||||
],
|
],
|
||||||
|
|
||||||
init: function() {
|
init: function() {
|
||||||
@@ -110,16 +108,6 @@ let AboutHome = {
|
|||||||
for (let msg of this.MESSAGES) {
|
for (let msg of this.MESSAGES) {
|
||||||
mm.addMessageListener(msg, this);
|
mm.addMessageListener(msg, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Services.obs.addObserver(this, "browser-search-engine-modified", false);
|
|
||||||
},
|
|
||||||
|
|
||||||
observe: function(aEngine, aTopic, aVerb) {
|
|
||||||
switch (aTopic) {
|
|
||||||
case "browser-search-engine-modified":
|
|
||||||
this.sendAboutHomeData(null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
receiveMessage: function(aMessage) {
|
receiveMessage: function(aMessage) {
|
||||||
@@ -179,65 +167,6 @@ let AboutHome = {
|
|||||||
case "AboutHome:RequestUpdate":
|
case "AboutHome:RequestUpdate":
|
||||||
this.sendAboutHomeData(aMessage.target);
|
this.sendAboutHomeData(aMessage.target);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "AboutHome:Search":
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = JSON.parse(aMessage.data.searchData);
|
|
||||||
} catch(ex) {
|
|
||||||
Cu.reportError(ex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.search.init(function(status) {
|
|
||||||
if (!Components.isSuccessCode(status)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let engine = Services.search.currentEngine;
|
|
||||||
if (AppConstants.MOZ_SERVICES_HEALTHREPORT) {
|
|
||||||
window.BrowserSearch.recordSearchInHealthReport(engine, "abouthome", data.selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger a search through nsISearchEngine.getSubmission()
|
|
||||||
let submission = engine.getSubmission(data.searchTerms, null, "homepage");
|
|
||||||
let where = window.whereToOpenLink(data.originalEvent);
|
|
||||||
|
|
||||||
// There is a chance that by the time we receive the search message, the
|
|
||||||
// user has switched away from the tab that triggered the search. If,
|
|
||||||
// based on the event, we need to load the search in the same tab that
|
|
||||||
// triggered it (i.e. where == "current"), openUILinkIn will not work
|
|
||||||
// because that tab is no longer the current one. For this case we
|
|
||||||
// manually load the URI in the target browser.
|
|
||||||
if (where == "current") {
|
|
||||||
aMessage.target.loadURIWithFlags(submission.uri.spec,
|
|
||||||
Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
|
||||||
null, null, submission.postData);
|
|
||||||
} else {
|
|
||||||
let params = {
|
|
||||||
postData: submission.postData,
|
|
||||||
inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
|
|
||||||
};
|
|
||||||
window.openLinkIn(submission.uri.spec, where, params);
|
|
||||||
}
|
|
||||||
// Used for testing
|
|
||||||
let mm = aMessage.target.messageManager;
|
|
||||||
mm.sendAsyncMessage("AboutHome:SearchTriggered", aMessage.data.searchData);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "AboutHome:OpenSearchPanel":
|
|
||||||
let panel = window.document.getElementById("abouthome-search-panel");
|
|
||||||
let anchor = aMessage.objects.anchor;
|
|
||||||
panel.hidden = false;
|
|
||||||
panel.openPopup(anchor);
|
|
||||||
anchor.setAttribute("active", "true");
|
|
||||||
panel.addEventListener("popuphidden", function onHidden() {
|
|
||||||
panel.removeEventListener("popuphidden", onHidden);
|
|
||||||
anchor.removeAttribute("active");
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -250,24 +179,11 @@ let AboutHome = {
|
|||||||
let ss = wrapper.SessionStore;
|
let ss = wrapper.SessionStore;
|
||||||
|
|
||||||
ss.promiseInitialized.then(function() {
|
ss.promiseInitialized.then(function() {
|
||||||
let deferred = Promise.defer();
|
|
||||||
|
|
||||||
Services.search.init(function (status){
|
|
||||||
if (!Components.isSuccessCode(status)) {
|
|
||||||
deferred.reject(status);
|
|
||||||
} else {
|
|
||||||
deferred.resolve(Services.search.defaultEngine.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}).then(function(engineName) {
|
|
||||||
let data = {
|
let data = {
|
||||||
showRestoreLastSession: ss.canRestoreLastSession,
|
showRestoreLastSession: ss.canRestoreLastSession,
|
||||||
snippetsURL: AboutHomeUtils.snippetsURL,
|
snippetsURL: AboutHomeUtils.snippetsURL,
|
||||||
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
|
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
|
||||||
snippetsVersion: AboutHomeUtils.snippetsVersion,
|
snippetsVersion: AboutHomeUtils.snippetsVersion,
|
||||||
defaultEngineName: engineName
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (AboutHomeUtils.showKnowYourRights) {
|
if (AboutHomeUtils.showKnowYourRights) {
|
||||||
@@ -285,14 +201,5 @@ let AboutHome = {
|
|||||||
}).then(null, function onError(x) {
|
}).then(null, function onError(x) {
|
||||||
Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
|
Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Focuses the search input in the page with the given message manager.
|
|
||||||
* @param messageManager
|
|
||||||
* The MessageManager object of the selected browser.
|
|
||||||
*/
|
|
||||||
focusInput: function (messageManager) {
|
|
||||||
messageManager.sendAsyncMessage("AboutHome:FocusInput");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ const MAX_SUGGESTIONS = 6;
|
|||||||
* GetState
|
* GetState
|
||||||
* Retrieves the current search engine state.
|
* Retrieves the current search engine state.
|
||||||
* data: null
|
* data: null
|
||||||
|
* GetStrings
|
||||||
|
* Retrieves localized search UI strings.
|
||||||
|
* data: null
|
||||||
* ManageEngines
|
* ManageEngines
|
||||||
* Opens the search engine management window.
|
* Opens the search engine management window.
|
||||||
* data: null
|
* data: null
|
||||||
@@ -53,7 +56,7 @@ const MAX_SUGGESTIONS = 6;
|
|||||||
* data: the entry, a string
|
* data: the entry, a string
|
||||||
* Search
|
* Search
|
||||||
* Performs a search.
|
* Performs a search.
|
||||||
* data: { engineName, searchString, whence }
|
* data: { engineName, searchString, healthReportKey, searchPurpose }
|
||||||
* SetCurrentEngine
|
* SetCurrentEngine
|
||||||
* Sets the current engine.
|
* Sets the current engine.
|
||||||
* data: the name of the engine
|
* data: the name of the engine
|
||||||
@@ -72,6 +75,9 @@ const MAX_SUGGESTIONS = 6;
|
|||||||
* State
|
* State
|
||||||
* Sent in reply to GetState.
|
* Sent in reply to GetState.
|
||||||
* data: see _currentStateObj
|
* data: see _currentStateObj
|
||||||
|
* Strings
|
||||||
|
* Sent in reply to GetStrings
|
||||||
|
* data: Object containing string names and values for the current locale.
|
||||||
* Suggestions
|
* Suggestions
|
||||||
* Sent in reply to GetSuggestions.
|
* Sent in reply to GetSuggestions.
|
||||||
* data: see _onMessageGetSuggestions
|
* data: see _onMessageGetSuggestions
|
||||||
@@ -98,9 +104,24 @@ this.ContentSearch = {
|
|||||||
addMessageListener(INBOUND_MESSAGE, this);
|
addMessageListener(INBOUND_MESSAGE, this);
|
||||||
Services.obs.addObserver(this, "browser-search-engine-modified", false);
|
Services.obs.addObserver(this, "browser-search-engine-modified", false);
|
||||||
Services.obs.addObserver(this, "shutdown-leaks-before-check", false);
|
Services.obs.addObserver(this, "shutdown-leaks-before-check", false);
|
||||||
|
Services.prefs.addObserver("browser.search.hiddenOneOffs", this, false);
|
||||||
this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
|
this._stringBundle = Services.strings.createBundle("chrome://global/locale/autocomplete.properties");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get searchSuggestionUIStrings() {
|
||||||
|
if (this._searchSuggestionUIStrings) {
|
||||||
|
return this._searchSuggestionUIStrings;
|
||||||
|
}
|
||||||
|
this._searchSuggestionUIStrings = {};
|
||||||
|
let searchBundle = Services.strings.createBundle("chrome://browser/locale/search.properties");
|
||||||
|
let stringNames = ["searchHeader", "searchPlaceholder", "searchFor",
|
||||||
|
"searchWith", "searchWithHeader"];
|
||||||
|
for (let name of stringNames) {
|
||||||
|
this._searchSuggestionUIStrings[name] = searchBundle.GetStringFromName(name);
|
||||||
|
}
|
||||||
|
return this._searchSuggestionUIStrings;
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function () {
|
destroy: function () {
|
||||||
if (this._destroyedPromise) {
|
if (this._destroyedPromise) {
|
||||||
return this._destroyedPromise;
|
return this._destroyedPromise;
|
||||||
@@ -153,6 +174,7 @@ this.ContentSearch = {
|
|||||||
|
|
||||||
observe: function (subj, topic, data) {
|
observe: function (subj, topic, data) {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
|
case "nsPref:changed":
|
||||||
case "browser-search-engine-modified":
|
case "browser-search-engine-modified":
|
||||||
this._eventQueue.push({
|
this._eventQueue.push({
|
||||||
type: "Observe",
|
type: "Observe",
|
||||||
@@ -201,14 +223,19 @@ this.ContentSearch = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onMessageGetStrings: function (msg, data) {
|
||||||
|
this._reply(msg, "Strings", this.searchSuggestionUIStrings);
|
||||||
|
},
|
||||||
|
|
||||||
_onMessageSearch: function (msg, data) {
|
_onMessageSearch: function (msg, data) {
|
||||||
this._ensureDataHasProperties(data, [
|
this._ensureDataHasProperties(data, [
|
||||||
"engineName",
|
"engineName",
|
||||||
"searchString",
|
"searchString",
|
||||||
"whence",
|
"healthReportKey",
|
||||||
|
"searchPurpose",
|
||||||
]);
|
]);
|
||||||
let engine = Services.search.getEngineByName(data.engineName);
|
let engine = Services.search.getEngineByName(data.engineName);
|
||||||
let submission = engine.getSubmission(data.searchString, "", data.whence);
|
let submission = engine.getSubmission(data.searchString, "", data.searchPurpose);
|
||||||
let browser = msg.target;
|
let browser = msg.target;
|
||||||
let win;
|
let win;
|
||||||
try {
|
try {
|
||||||
@@ -239,7 +266,7 @@ this.ContentSearch = {
|
|||||||
};
|
};
|
||||||
win.openUILinkIn(submission.uri.spec, where, params);
|
win.openUILinkIn(submission.uri.spec, where, params);
|
||||||
}
|
}
|
||||||
win.BrowserSearch.recordSearchInHealthReport(engine, data.whence,
|
win.BrowserSearch.recordSearchInHealthReport(engine, data.healthReportKey,
|
||||||
data.selection || null);
|
data.selection || null);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
@@ -414,7 +441,12 @@ this.ContentSearch = {
|
|||||||
engines: [],
|
engines: [],
|
||||||
currentEngine: yield this._currentEngineObj(),
|
currentEngine: yield this._currentEngineObj(),
|
||||||
};
|
};
|
||||||
|
let pref = Services.prefs.getCharPref("browser.search.hiddenOneOffs");
|
||||||
|
let hiddenList = pref ? pref.split(",") : [];
|
||||||
for (let engine of Services.search.getVisibleEngines()) {
|
for (let engine of Services.search.getVisibleEngines()) {
|
||||||
|
if (hiddenList.indexOf(engine.name) != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let uri = engine.getIconURLBySize(16, 16);
|
let uri = engine.getIconURLBySize(16, 16);
|
||||||
state.engines.push({
|
state.engines.push({
|
||||||
name: engine.name,
|
name: engine.name,
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ browser.jar:
|
|||||||
skin/classic/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
|
skin/classic/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
|
||||||
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
||||||
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
||||||
|
skin/classic/browser/search-indicator-magnifying-glass.svg (../shared/search/search-indicator-magnifying-glass.svg)
|
||||||
|
skin/classic/browser/search-arrow-go.svg (../shared/search/search-arrow-go.svg)
|
||||||
skin/classic/browser/Security-broken.png
|
skin/classic/browser/Security-broken.png
|
||||||
skin/classic/browser/setDesktopBackground.css
|
skin/classic/browser/setDesktopBackground.css
|
||||||
skin/classic/browser/slowStartup-16.png
|
skin/classic/browser/slowStartup-16.png
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ browser.jar:
|
|||||||
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
||||||
skin/classic/browser/search-indicator-badge-add@2x.png (../shared/search/search-indicator-badge-add@2x.png)
|
skin/classic/browser/search-indicator-badge-add@2x.png (../shared/search/search-indicator-badge-add@2x.png)
|
||||||
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
||||||
|
skin/classic/browser/search-indicator-magnifying-glass.svg (../shared/search/search-indicator-magnifying-glass.svg)
|
||||||
|
skin/classic/browser/search-arrow-go.svg (../shared/search/search-arrow-go.svg)
|
||||||
skin/classic/browser/slowStartup-16.png
|
skin/classic/browser/slowStartup-16.png
|
||||||
skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
||||||
skin/classic/browser/theme-switcher-icon@2x.png (../shared/theme-switcher-icon@2x.png)
|
skin/classic/browser/theme-switcher-icon@2x.png (../shared/theme-switcher-icon@2x.png)
|
||||||
|
|||||||
22
browser/themes/shared/search/search-arrow-go.svg
Normal file
22
browser/themes/shared/search/search-arrow-go.svg
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<style>
|
||||||
|
use:not(:target) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
use {
|
||||||
|
fill: #616366;
|
||||||
|
}
|
||||||
|
use[id$="-inverted"] {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<defs>
|
||||||
|
<path id="search-arrow-go-glyph" d="M1,7v2.2C1,9.8,1.4,10,2,10h7.5l-3,3.1c-0.4,0.3-0.4,1,0,1.4l0.8,0.8 c0.4,0.4,1,0.4,1.4,0l6.6-6.6c0.4-0.4,0.4-1,0-1.4L8.7,0.7c-0.4-0.4-1-0.4-1.4,0L6.5,1.6C6.1,2,6.1,2.6,6.5,3l3,3H2C1.4,6,1,6.4,1,7z"/>
|
||||||
|
</defs>
|
||||||
|
<use id="search-arrow-go" xlink:href="#search-arrow-go-glyph"/>
|
||||||
|
<use id="search-arrow-go-inverted" xlink:href="#search-arrow-go-glyph"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 926 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- 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/. -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path fill="#808080" d="M21.7,20.3l-1.4,1.4l-5.4-5.4c-1.3,1-3,1.7-4.9,1.7 c-4.4,0-8-3.6-8-8c0-4.4,3.6-8,8-8c4.4,0,8,3.6,8,8c0,1.8-0.6,3.5-1.7,4.9L21.7,20.3z M10,4c-3.3,0-6,2.7-6,6s2.7,6,6,6s6-2.7,6-6 S13.3,4,10,4z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 561 B |
@@ -116,6 +116,8 @@ browser.jar:
|
|||||||
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
|
||||||
skin/classic/browser/search-indicator-badge-add@2x.png (../shared/search/search-indicator-badge-add@2x.png)
|
skin/classic/browser/search-indicator-badge-add@2x.png (../shared/search/search-indicator-badge-add@2x.png)
|
||||||
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
skin/classic/browser/search-history-icon.svg (../shared/search/history-icon.svg)
|
||||||
|
skin/classic/browser/search-indicator-magnifying-glass.svg (../shared/search/search-indicator-magnifying-glass.svg)
|
||||||
|
skin/classic/browser/search-arrow-go.svg (../shared/search/search-arrow-go.svg)
|
||||||
skin/classic/browser/setDesktopBackground.css
|
skin/classic/browser/setDesktopBackground.css
|
||||||
skin/classic/browser/slowStartup-16.png
|
skin/classic/browser/slowStartup-16.png
|
||||||
skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
||||||
|
|||||||
Reference in New Issue
Block a user