зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1171344 - [implement] One-off searches on about:home and about:newtab. r=adw
--HG-- rename : browser/base/content/searchSuggestionUI.css => browser/base/content/contentSearchUI.css rename : browser/base/content/searchSuggestionUI.js => browser/base/content/contentSearchUI.js extra : rebase_source : 430736ce8dab32c7aad917ee87586ba2416d4dd3
This commit is contained in:
Родитель
967e273030
Коммит
6e888d2fe5
2
CLOBBER
2
CLOBBER
|
@ -22,4 +22,4 @@
|
|||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# 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;
|
||||
}
|
||||
|
||||
#searchForm,
|
||||
#searchIconAndTextContainer,
|
||||
#snippets {
|
||||
width: 470px;
|
||||
}
|
||||
|
||||
#searchForm {
|
||||
#searchIconAndTextContainer {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
#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;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#searchIcon {
|
||||
border: 1px solid transparent;
|
||||
-moz-margin-end: 5px;
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
background: url("chrome://browser/skin/magnifier.png") center center no-repeat;
|
||||
background-size: 26px;
|
||||
}
|
||||
|
||||
#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;
|
||||
border: 1px transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#searchText {
|
||||
margin-left: 0;
|
||||
-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;
|
||||
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);
|
||||
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);
|
||||
border-radius: 2.5px 0 0 2.5px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#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[autofocus] {
|
||||
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 {
|
||||
-moz-margin-start: -1px;
|
||||
background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
||||
padding: 0 9px;
|
||||
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;
|
||||
border: 1px solid;
|
||||
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;
|
||||
border-radius: 0 2.5px 2.5px 0;
|
||||
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.2);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#searchSubmit:-moz-dir(rtl) {
|
||||
border-radius: 2.5px 0 0 2.5px;
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
|
||||
#searchText:focus + #searchSubmit,
|
||||
#searchText[keepfocus] + #searchSubmit,
|
||||
#searchText + #searchSubmit:hover,
|
||||
#searchText[autofocus] + #searchSubmit {
|
||||
border-color: #59b5fc #45a3e7 #3294d5;
|
||||
|
@ -154,15 +135,16 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||
}
|
||||
|
||||
#searchText:focus + #searchSubmit,
|
||||
#searchText[keepfocus] + #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,
|
||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||
0 1px 0 hsla(210,54%,20%,.03);
|
||||
}
|
||||
|
||||
#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,
|
||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||
0 1px 0 hsla(210,54%,20%,.03),
|
||||
|
@ -180,9 +162,9 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||
#rightsSnippet {
|
||||
display: block;
|
||||
min-height: 38px;
|
||||
background: 30px center no-repeat;
|
||||
background: 0 center no-repeat;
|
||||
padding: 6px 0;
|
||||
-moz-padding-start: 79px;
|
||||
-moz-padding-start: 49px;
|
||||
}
|
||||
|
||||
#rightsSnippet[hidden] {
|
||||
|
@ -192,7 +174,7 @@ html:not([searchUIConfiguration="oldsearchui"]) #searchLogoContainer {
|
|||
#defaultSnippet1:-moz-dir(rtl),
|
||||
#defaultSnippet2:-moz-dir(rtl),
|
||||
#rightsSnippet:-moz-dir(rtl) {
|
||||
background-position: right 30px center;
|
||||
background-position: right 0 center;
|
||||
}
|
||||
|
||||
#defaultSnippet1 {
|
||||
|
@ -247,7 +229,7 @@ body[narrow] #launcher[session] {
|
|||
white-space: normal;
|
||||
background: transparent padding-box;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2.5px;
|
||||
border-radius: 2px;
|
||||
color: #525c66;
|
||||
font-size: 75%;
|
||||
cursor: pointer;
|
||||
|
@ -396,10 +378,6 @@ body[narrow] #restorePreviousSession::before {
|
|||
background-image: url("chrome://branding/content/about-logo@2x.png");
|
||||
}
|
||||
|
||||
#searchIcon {
|
||||
background-image: url("chrome://browser/skin/magnifier@2x.png");
|
||||
}
|
||||
|
||||
#defaultSnippet1,
|
||||
#defaultSnippet2,
|
||||
#rightsSnippet {
|
||||
|
|
|
@ -4,138 +4,6 @@
|
|||
|
||||
"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:
|
||||
// * add a new entity to aboutHome.dtd
|
||||
// * add a <span/> for it in aboutHome.xhtml
|
||||
|
@ -158,8 +26,7 @@ const SNIPPETS_OBJECTSTORE_NAME = "snippets";
|
|||
let gInitialized = false;
|
||||
let gObserver = new MutationObserver(function (mutations) {
|
||||
for (let mutation of mutations) {
|
||||
if (mutation.attributeName == "searchEngineName") {
|
||||
setupSearchEngine();
|
||||
if (mutation.attributeName == "snippetsVersion") {
|
||||
if (!gInitialized) {
|
||||
ensureSnippetsMapThen(loadSnippets);
|
||||
gInitialized = true;
|
||||
|
@ -174,6 +41,7 @@ window.addEventListener("pageshow", function () {
|
|||
// later and may use asynchronous getters.
|
||||
window.gObserver.observe(document.documentElement, { attributes: true });
|
||||
fitToWidth();
|
||||
setupSearch();
|
||||
window.addEventListener("resize", fitToWidth);
|
||||
|
||||
// Ask chrome to update snippets.
|
||||
|
@ -300,52 +168,13 @@ function ensureSnippetsMapThen(aCallback)
|
|||
|
||||
function onSearchSubmit(aEvent)
|
||||
{
|
||||
let searchText = document.getElementById("searchText");
|
||||
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();
|
||||
}
|
||||
gContentSearchController.search(aEvent);
|
||||
}
|
||||
|
||||
|
||||
let gSearchSuggestionController;
|
||||
let gContentSearchController;
|
||||
|
||||
function setupSearchEngine()
|
||||
function setupSearch()
|
||||
{
|
||||
// The "autofocus" attribute doesn't focus the form element
|
||||
// immediately when the element is first drawn, so the
|
||||
|
@ -356,28 +185,11 @@ function setupSearchEngine()
|
|||
searchText.removeAttribute("autofocus");
|
||||
});
|
||||
|
||||
let searchEngineName = document.documentElement.getAttribute("searchEngineName");
|
||||
let searchEngineInfo = SEARCH_ENGINES[searchEngineName];
|
||||
let logoElt = document.getElementById("searchEngineLogo");
|
||||
|
||||
// Add search engine logo.
|
||||
if (searchEngineInfo && searchEngineInfo.image) {
|
||||
logoElt.parentNode.hidden = false;
|
||||
logoElt.src = searchEngineInfo.image;
|
||||
logoElt.alt = searchEngineName;
|
||||
searchText.placeholder = "";
|
||||
if (!gContentSearchController) {
|
||||
gContentSearchController =
|
||||
new ContentSearchUIController(searchText, searchText.parentNode,
|
||||
"abouthome", "homepage");
|
||||
}
|
||||
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"
|
||||
href="chrome://branding/content/icon32.png"/>
|
||||
<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"
|
||||
href="chrome://browser/content/abouthome/aboutHome.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/abouthome/aboutHome.js"/>
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/searchSuggestionUI.js"/>
|
||||
src="chrome://browser/content/contentSearchUI.js"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
@ -39,14 +39,12 @@
|
|||
<div id="topSection">
|
||||
<div id="brandLogo"></div>
|
||||
|
||||
<div id="searchContainer">
|
||||
<form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)">
|
||||
<div id="searchLogoContainer" hidden="true"><img id="searchEngineLogo"/></div>
|
||||
<button id="searchIcon" type="button" />
|
||||
<input type="text" name="q" value="" id="searchText" maxlength="256"
|
||||
autofocus="autofocus" dir="auto"/>
|
||||
<input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/>
|
||||
</form>
|
||||
<div id="searchIconAndTextContainer">
|
||||
<div id="searchIcon"/>
|
||||
<input type="text" name="q" value="" id="searchText" maxlength="256"
|
||||
aria-label="&contentSearchInput.label;" autofocus="autofocus" dir="auto"/>
|
||||
<input id="searchSubmit" type="button" value="" onclick="onSearchSubmit(event)"
|
||||
aria-label="&contentSearchSubmit.label;"/>
|
||||
</div>
|
||||
|
||||
<div id="snippetContainer">
|
||||
|
|
|
@ -3467,9 +3467,8 @@ const BrowserSearch = {
|
|||
if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
|
||||
let url = gBrowser.currentURI.spec.toLowerCase();
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
if (url === "about:home") {
|
||||
AboutHome.focusInput(mm);
|
||||
} else if (url === "about:newtab" && NewTabUtils.allPages.enabled) {
|
||||
if (url === "about:home" ||
|
||||
(url === "about:newtab" && NewTabUtils.allPages.enabled)) {
|
||||
ContentSearch.focusInput(mm);
|
||||
} else {
|
||||
openUILinkIn("about:home", "current");
|
||||
|
|
|
@ -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%);
|
||||
}
|
|
@ -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 {
|
||||
display: -moz-box;
|
||||
position: relative;
|
||||
-moz-box-align: center;
|
||||
-moz-box-pack: center;
|
||||
}
|
||||
|
||||
|
@ -341,104 +340,76 @@ input[type=button] {
|
|||
|
||||
#newtab-search-form {
|
||||
display: -moz-box;
|
||||
position: relative;
|
||||
height: 36px;
|
||||
-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) */
|
||||
}
|
||||
|
||||
#newtab-search-logo {
|
||||
display: -moz-box;
|
||||
width: 38px;
|
||||
height: 38px; /* 26 image height + 6 top "padding" + 6 bottom "padding" */
|
||||
border: 1px solid transparent;
|
||||
-moz-margin-end: 8px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
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-icon {
|
||||
border: 1px transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: url("chrome://browser/skin/search-indicator-magnifying-glass.svg") center center no-repeat;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#newtab-search-text {
|
||||
height: 38px; /* same height as #newtab-search-logo */
|
||||
-moz-box-flex: 1;
|
||||
|
||||
padding: 0 8px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 34px;
|
||||
padding-right: 8px;
|
||||
background: hsla(0,0%,100%,.9) padding-box;
|
||||
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);
|
||||
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);
|
||||
border-radius: 2.5px 0 0 2.5px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#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[autofocus] {
|
||||
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
|
||||
}
|
||||
|
||||
#newtab-search-submit {
|
||||
height: 38px; /* same height as #newtab-search-logo */
|
||||
font-size: 13px !important;
|
||||
|
||||
-moz-margin-start: -1px;
|
||||
background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
|
||||
padding: 0 9px;
|
||||
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;
|
||||
border: 1px solid;
|
||||
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;
|
||||
border-radius: 0 2.5px 2.5px 0;
|
||||
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
|
||||
0 1px 0 hsla(0,0%,100%,.2);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 150ms;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#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,
|
||||
|
@ -449,15 +420,16 @@ input[type=button] {
|
|||
}
|
||||
|
||||
#newtab-search-text:focus + #newtab-search-submit,
|
||||
#newtab-search-text[keepfocus] + #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,
|
||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||
0 1px 0 hsla(210,54%,20%,.03);
|
||||
}
|
||||
|
||||
#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,
|
||||
0 0 0 1px hsla(0,0%,100%,.1) inset,
|
||||
0 1px 0 hsla(210,54%,20%,.03),
|
||||
|
@ -544,14 +516,11 @@ input[type=button] {
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent,
|
||||
#newtab-search-panel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
#newtab-customize-panel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.newtab-customize-panel-item,
|
||||
.newtab-search-panel-engine,
|
||||
#newtab-search-manage {
|
||||
.newtab-customize-panel-item {
|
||||
line-height: 25px;
|
||||
padding: 15px;
|
||||
-moz-padding-start: 40px;
|
||||
|
@ -560,22 +529,12 @@ input[type=button] {
|
|||
max-width: 300px;
|
||||
}
|
||||
|
||||
.newtab-customize-panel-item:not(:first-child),
|
||||
.newtab-search-panel-engine {
|
||||
.newtab-customize-panel-item:not(:first-child) {
|
||||
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-item > label,
|
||||
.newtab-search-panel-engine > label,
|
||||
#newtab-search-manage > label,
|
||||
.newtab-customize-complex-option {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
@ -625,8 +584,7 @@ input[type=button] {
|
|||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.newtab-customize-panel-item[selected],
|
||||
.newtab-search-panel-engine[selected] {
|
||||
.newtab-customize-panel-item[selected] {
|
||||
background: url("chrome://global/skin/menu/shared-menu-check-active.svg") no-repeat transparent;
|
||||
background-size: 16px 16px;
|
||||
background-position: 15px 15px;
|
||||
|
@ -671,7 +629,7 @@ input[type=button] {
|
|||
border-top: 1px solid threedshadow;
|
||||
}
|
||||
|
||||
.searchSuggestionTable {
|
||||
.contentSearchSuggestionTable {
|
||||
font: message-box;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
- 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://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/skin/newtab/newTab.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
||||
%newTabDTD;
|
||||
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
|
||||
%searchBarDTD;
|
||||
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
|
||||
%browserDTD;
|
||||
]>
|
||||
|
@ -22,13 +20,6 @@
|
|||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
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 id="newtab-customize-panel" orient="vertical">
|
||||
<div id="newtab-customize-panel-anchor"></div>
|
||||
|
@ -108,13 +99,13 @@
|
|||
</div>
|
||||
|
||||
<div id="newtab-search-container">
|
||||
<form id="newtab-search-form" name="searchForm">
|
||||
<div id="newtab-search-logo"/>
|
||||
<div id="newtab-search-form">
|
||||
<div id="newtab-search-icon"/>
|
||||
<input type="text" name="q" value="" id="newtab-search-text"
|
||||
maxlength="256" dir="auto"/>
|
||||
<input id="newtab-search-submit" type="submit"
|
||||
value="&searchEndCap.label;"/>
|
||||
</form>
|
||||
aria-label="&contentSearchInput.label;" maxlength="256" dir="auto"/>
|
||||
<input id="newtab-search-submit" type="button" value=""
|
||||
aria-label="&contentSearchSubmit.label;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="newtab-horizontal-margin">
|
||||
|
@ -133,7 +124,7 @@
|
|||
</div>
|
||||
|
||||
<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"
|
||||
src="chrome://browser/content/newtab/newTab.js"/>
|
||||
</xul:window>
|
||||
|
|
|
@ -5,257 +5,11 @@
|
|||
#endif
|
||||
|
||||
let gSearch = {
|
||||
|
||||
currentEngineName: null,
|
||||
|
||||
get useNewUI() {
|
||||
let newUI = Services.prefs.getBoolPref("browser.search.showOneOffButtons");
|
||||
delete this.useNewUI;
|
||||
this.useNewUI = newUI;
|
||||
return newUI;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
for (let idSuffix of this._nodeIDSuffixes) {
|
||||
this._nodes[idSuffix] =
|
||||
document.getElementById("newtab-search-" + idSuffix);
|
||||
}
|
||||
|
||||
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;
|
||||
document.getElementById("newtab-search-submit")
|
||||
.addEventListener("click", e => this._contentSearchController.search(e));
|
||||
let textbox = document.getElementById("newtab-search-text");
|
||||
this._contentSearchController =
|
||||
new ContentSearchUIController(textbox, textbox.parentNode, "newtab", "newtab");
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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":
|
||||
this.onPageLoad();
|
||||
break;
|
||||
case "AboutHomeSearchEvent":
|
||||
this.onSearch(aEvent);
|
||||
break;
|
||||
case "AboutHomeSearchPanel":
|
||||
this.onOpenSearchPanel(aEvent);
|
||||
break;
|
||||
case "click":
|
||||
this.onClick(aEvent);
|
||||
break;
|
||||
|
@ -137,9 +131,6 @@ let AboutHomeListener = {
|
|||
case "AboutHome:Update":
|
||||
this.onUpdate(aMessage.data);
|
||||
break;
|
||||
case "AboutHome:FocusInput":
|
||||
this.onFocusInput();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -150,13 +141,11 @@ let AboutHomeListener = {
|
|||
|
||||
// Inject search engine and snippets URL.
|
||||
let docElt = doc.documentElement;
|
||||
// set the following attributes BEFORE searchEngineName, which triggers to
|
||||
// show the snippets when it's set.
|
||||
// Set snippetsVersion last, which triggers to show the snippets when it's set.
|
||||
docElt.setAttribute("snippetsURL", aData.snippetsURL);
|
||||
if (aData.showKnowYourRights)
|
||||
docElt.setAttribute("showKnowYourRights", "true");
|
||||
docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
|
||||
docElt.setAttribute("searchEngineName", aData.defaultEngineName);
|
||||
},
|
||||
|
||||
onPageLoad: function() {
|
||||
|
@ -167,7 +156,6 @@ let AboutHomeListener = {
|
|||
|
||||
doc.documentElement.setAttribute("hasBrowserHandlers", "true");
|
||||
addMessageListener("AboutHome:Update", this);
|
||||
addMessageListener("AboutHome:FocusInput", this);
|
||||
addEventListener("click", this, true);
|
||||
addEventListener("pagehide", this, true);
|
||||
|
||||
|
@ -176,8 +164,6 @@ let AboutHomeListener = {
|
|||
}
|
||||
|
||||
sendAsyncMessage("AboutHome:RequestUpdate");
|
||||
doc.addEventListener("AboutHomeSearchEvent", this, true, true);
|
||||
doc.addEventListener("AboutHomeSearchPanel", this, true, true);
|
||||
},
|
||||
|
||||
onClick: function(aEvent) {
|
||||
|
@ -228,10 +214,6 @@ let AboutHomeListener = {
|
|||
case "settings":
|
||||
sendAsyncMessage("AboutHome:Settings");
|
||||
break;
|
||||
|
||||
case "searchIcon":
|
||||
sendAsyncMessage("AboutHome:OpenSearchPanel", null, { anchor: originalTarget });
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -246,21 +228,6 @@ let AboutHomeListener = {
|
|||
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);
|
||||
|
||||
|
|
|
@ -144,8 +144,8 @@ browser.jar:
|
|||
* content/browser/sanitize.xul (content/sanitize.xul)
|
||||
* content/browser/sanitizeDialog.js (content/sanitizeDialog.js)
|
||||
content/browser/sanitizeDialog.css (content/sanitizeDialog.css)
|
||||
content/browser/searchSuggestionUI.js (content/searchSuggestionUI.js)
|
||||
content/browser/searchSuggestionUI.css (content/searchSuggestionUI.css)
|
||||
content/browser/contentSearchUI.js (content/contentSearchUI.js)
|
||||
content/browser/contentSearchUI.css (content/contentSearchUI.css)
|
||||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<!ENTITY abouthome.pageTitle "&brandFullName; Start Page">
|
||||
|
||||
<!ENTITY abouthome.searchEngineButton.label "Search">
|
||||
<!ENTITY abouthome.search.placeholder "Search">
|
||||
|
||||
<!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1):
|
||||
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 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):
|
||||
These two strings are used to build the header above the list of one-click
|
||||
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
|
||||
# grouped in a submenu using cmd_addFoundEngineMenu as a label.
|
||||
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:Settings",
|
||||
"AboutHome:RequestUpdate",
|
||||
"AboutHome:Search",
|
||||
"AboutHome:OpenSearchPanel",
|
||||
],
|
||||
|
||||
init: function() {
|
||||
|
@ -110,16 +108,6 @@ let AboutHome = {
|
|||
for (let msg of this.MESSAGES) {
|
||||
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) {
|
||||
|
@ -179,65 +167,6 @@ let AboutHome = {
|
|||
case "AboutHome:RequestUpdate":
|
||||
this.sendAboutHomeData(aMessage.target);
|
||||
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;
|
||||
|
||||
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 = {
|
||||
showRestoreLastSession: ss.canRestoreLastSession,
|
||||
snippetsURL: AboutHomeUtils.snippetsURL,
|
||||
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
|
||||
snippetsVersion: AboutHomeUtils.snippetsVersion,
|
||||
defaultEngineName: engineName
|
||||
};
|
||||
|
||||
if (AboutHomeUtils.showKnowYourRights) {
|
||||
|
@ -285,14 +201,5 @@ let AboutHome = {
|
|||
}).then(null, function onError(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
|
||||
* Retrieves the current search engine state.
|
||||
* data: null
|
||||
* GetStrings
|
||||
* Retrieves localized search UI strings.
|
||||
* data: null
|
||||
* ManageEngines
|
||||
* Opens the search engine management window.
|
||||
* data: null
|
||||
|
@ -53,7 +56,7 @@ const MAX_SUGGESTIONS = 6;
|
|||
* data: the entry, a string
|
||||
* Search
|
||||
* Performs a search.
|
||||
* data: { engineName, searchString, whence }
|
||||
* data: { engineName, searchString, healthReportKey, searchPurpose }
|
||||
* SetCurrentEngine
|
||||
* Sets the current engine.
|
||||
* data: the name of the engine
|
||||
|
@ -72,6 +75,9 @@ const MAX_SUGGESTIONS = 6;
|
|||
* State
|
||||
* Sent in reply to GetState.
|
||||
* data: see _currentStateObj
|
||||
* Strings
|
||||
* Sent in reply to GetStrings
|
||||
* data: Object containing string names and values for the current locale.
|
||||
* Suggestions
|
||||
* Sent in reply to GetSuggestions.
|
||||
* data: see _onMessageGetSuggestions
|
||||
|
@ -98,9 +104,24 @@ this.ContentSearch = {
|
|||
addMessageListener(INBOUND_MESSAGE, this);
|
||||
Services.obs.addObserver(this, "browser-search-engine-modified", 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");
|
||||
},
|
||||
|
||||
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 () {
|
||||
if (this._destroyedPromise) {
|
||||
return this._destroyedPromise;
|
||||
|
@ -153,6 +174,7 @@ this.ContentSearch = {
|
|||
|
||||
observe: function (subj, topic, data) {
|
||||
switch (topic) {
|
||||
case "nsPref:changed":
|
||||
case "browser-search-engine-modified":
|
||||
this._eventQueue.push({
|
||||
type: "Observe",
|
||||
|
@ -201,14 +223,19 @@ this.ContentSearch = {
|
|||
});
|
||||
},
|
||||
|
||||
_onMessageGetStrings: function (msg, data) {
|
||||
this._reply(msg, "Strings", this.searchSuggestionUIStrings);
|
||||
},
|
||||
|
||||
_onMessageSearch: function (msg, data) {
|
||||
this._ensureDataHasProperties(data, [
|
||||
"engineName",
|
||||
"searchString",
|
||||
"whence",
|
||||
"healthReportKey",
|
||||
"searchPurpose",
|
||||
]);
|
||||
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 win;
|
||||
try {
|
||||
|
@ -239,7 +266,7 @@ this.ContentSearch = {
|
|||
};
|
||||
win.openUILinkIn(submission.uri.spec, where, params);
|
||||
}
|
||||
win.BrowserSearch.recordSearchInHealthReport(engine, data.whence,
|
||||
win.BrowserSearch.recordSearchInHealthReport(engine, data.healthReportKey,
|
||||
data.selection || null);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
@ -414,7 +441,12 @@ this.ContentSearch = {
|
|||
engines: [],
|
||||
currentEngine: yield this._currentEngineObj(),
|
||||
};
|
||||
let pref = Services.prefs.getCharPref("browser.search.hiddenOneOffs");
|
||||
let hiddenList = pref ? pref.split(",") : [];
|
||||
for (let engine of Services.search.getVisibleEngines()) {
|
||||
if (hiddenList.indexOf(engine.name) != -1) {
|
||||
continue;
|
||||
}
|
||||
let uri = engine.getIconURLBySize(16, 16);
|
||||
state.engines.push({
|
||||
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/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-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/setDesktopBackground.css
|
||||
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@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-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/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
||||
skin/classic/browser/theme-switcher-icon@2x.png (../shared/theme-switcher-icon@2x.png)
|
||||
|
|
|
@ -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>
|
После Ширина: | Высота: | Размер: 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>
|
После Ширина: | Высота: | Размер: 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@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-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/slowStartup-16.png
|
||||
skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png)
|
||||
|
|
Загрузка…
Ссылка в новой задаче