tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 77be0140471e76f9ca64a872a660b30ec3aad33b
parent 9580a4e11372acab390407faa42d3d6e498f8873
Author: Mike Conley <mconley@mozilla.com>
Date:   Thu, 18 Dec 2025 17:24:37 +0000

Bug 1999334 - Part 2: Remove contentSearchUI (non-handoff content search mechanism) and tests. r=home-newtab-reviewers,search-reviewers,urlbar-reviewers,Standard8,frontend-codestyle-reviewers,thecount

Differential Revision: https://phabricator.services.mozilla.com/D273881

Diffstat:
Mbrowser/actors/AboutNewTabChild.sys.mjs | 1-
Mbrowser/app/profile/firefox.js | 3---
Dbrowser/components/search/content/contentSearchUI.css | 160-------------------------------------------------------------------------------
Dbrowser/components/search/content/contentSearchUI.js | 1023-------------------------------------------------------------------------------
Mbrowser/components/search/jar.mn | 2--
Dbrowser/components/search/test/browser/POSTSearchEngine.xml | 6------
Mbrowser/components/search/test/browser/browser.toml | 24++----------------------
Dbrowser/components/search/test/browser/browser_aboutHome_search_POST.js | 66------------------------------------------------------------------
Dbrowser/components/search/test/browser/browser_aboutHome_search_composing.js | 111-------------------------------------------------------------------------------
Dbrowser/components/search/test/browser/browser_aboutHome_search_suggestion.js | 74--------------------------------------------------------------------------
Dbrowser/components/search/test/browser/browser_contentSearchUI.js | 1134-------------------------------------------------------------------------------
Mbrowser/components/search/test/browser/browser_contentSearchUI_default.js | 56+++++++++++++++-----------------------------------------
Dbrowser/components/search/test/browser/contentSearchUI.html | 25-------------------------
Dbrowser/components/search/test/browser/contentSearchUI.js | 12------------
Dbrowser/components/search/test/browser/print_postdata.sjs | 25-------------------------
Mbrowser/components/search/test/browser/telemetry/browser.toml | 5-----
Dbrowser/components/search/test/browser/telemetry/browser_search_telemetry_aboutHome.js | 117-------------------------------------------------------------------------------
Mbrowser/components/search/test/browser/telemetry/browser_search_telemetry_content.js | 72------------------------------------------------------------------------
Mbrowser/components/search/test/browser/telemetry/browser_search_telemetry_sources.js | 4----
Dbrowser/components/search/test/browser/telemetry/browser_search_telemetry_sources_about.js | 224-------------------------------------------------------------------------------
Mbrowser/components/search/test/browser/telemetry/browser_search_telemetry_sources_webextension.js | 4----
Mbrowser/components/urlbar/tests/browser/browser_urlbar_telemetry_handoff.js | 9---------
Mbrowser/extensions/newtab/bin/render-activity-stream-html.js | 5-----
Mbrowser/extensions/newtab/data/content/abouthomecache/page.html.template | 1-
Mbrowser/extensions/newtab/prerendered/activity-stream-debug.html | 5-----
Mbrowser/extensions/newtab/prerendered/activity-stream-noscripts.html | 4----
Mbrowser/extensions/newtab/prerendered/activity-stream.html | 5-----
Mstylelint-rollouts.config.js | 2--
28 files changed, 17 insertions(+), 3162 deletions(-)

diff --git a/browser/actors/AboutNewTabChild.sys.mjs b/browser/actors/AboutNewTabChild.sys.mjs @@ -44,7 +44,6 @@ export class AboutNewTabChild extends RemotePageChild { // This list must match any similar ones in render-activity-stream-html.js. const scripts = [ - "chrome://browser/content/contentSearchUI.js", "chrome://browser/content/contentSearchHandoffUI.js", "chrome://browser/content/contentTheme.js", `chrome://global/content/vendor/react${debugString}.js`, diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js @@ -2088,9 +2088,6 @@ pref("browser.newtabpage.activity-stream.discoverystream.publisherFavicon.enable // User pref to show stories on newtab (feeds.system.topstories has to be set to true as well) pref("browser.newtabpage.activity-stream.feeds.section.topstories", true); -// The pref controls if search hand-off is enabled for Activity Stream. -pref("browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", true); - pref("browser.newtabpage.activity-stream.logowordmark.alwaysVisible", true); // URLs from the user's history that contain this search param will be hidden diff --git a/browser/components/search/content/contentSearchUI.css b/browser/components/search/content/contentSearchUI.css @@ -1,160 +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/. */ - -.contentSearchSuggestionTable { - background-color: hsla(0, 0%, 100%, 0.99); - color: black; - border: 1px solid hsla(0, 0%, 0%, 0.2); - border-top: none; - box-shadow: 0 5px 10px hsla(0, 0%, 0%, 0.1); - position: absolute; - inset-inline-start: 0; - z-index: 1001; - 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-inline-end: 8px; - margin-bottom: -3px; -} - -.contentSearchSuggestionTable .historyIcon { - width: 16px; - height: 16px; - display: inline-block; - background-image: url("chrome://browser/skin/history.svg"); - -moz-context-properties: fill; - fill: graytext; -} - -.contentSearchSuggestionRow.selected > td > .historyIcon { - fill: HighlightText; -} - -.contentSearchHeader > img { - height: 16px; - width: 16px; - margin: 0; - padding: 0; -} - -.contentSearchSuggestionRow.remote > td > .historyIcon { - visibility: hidden; -} - -.contentSearchSuggestionRow.selected { - background-color: SelectedItem; - color: SelectedItemText; -} - -.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: var(--font-weight-bold); -} - -.contentSearchOneOffItem { - 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:dir(rtl) { - background-position-x: left; -} - -.contentSearchOneOffItem > img { - width: 16px; - height: 16px; - margin-bottom: -2px; - pointer-events: none; -} - -.contentSearchOneOffItem:not(.last-row) { - border-bottom: 1px solid hsl(0, 0%, 92%); -} - -.contentSearchOneOffItem.end-of-row { - background-image: none; -} - -.contentSearchOneOffItem.selected { - background-color: SelectedItem; - background-image: none; -} - -.contentSearchOneOffsTable { - width: 100%; -} - -.contentSearchSettingsButton { - margin: 0; - padding: 0; - height: 32px; - border: none; - border-top: 1px solid hsla(0, 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%); -} diff --git a/browser/components/search/content/contentSearchUI.js b/browser/components/search/content/contentSearchUI.js @@ -1,1023 +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.ContentSearchUIController = (function () { - const MAX_DISPLAYED_SUGGESTIONS = 6; - const SUGGESTION_ID_PREFIX = "searchSuggestion"; - const ONE_OFF_ID_PREFIX = "oneOff"; - 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 {HTMLInputElement} inputElement - * Search suggestions will be based on the text in this text box. - * Assumed to be an html:input. - * @param {Element} 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 {string} healthReportKey - * This will be sent with the search data for BrowserUsageTelemetry to - * record the search. - * @param {string} idPrefix - * The IDs of elements created by the object will be prefixed with this - * string. - */ - function ContentSearchUIController( - inputElement, - tableParent, - healthReportKey, - idPrefix = "" - ) { - this.input = inputElement; - this._idPrefix = idPrefix; - this._healthReportKey = healthReportKey; - this._isPrivateEngine = false; - - 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("keydown", 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 = { - _oneOffButtons: [], - // Setting up the one off buttons causes an uninterruptible reflow. If we - // receive the list of engines while the newtab page is loading, this reflow - // may regress performance - so we set this flag and only set up the buttons - // if it's set when the suggestions table is actually opened. - _pendingOneOffRefresh: undefined, - - get defaultEngine() { - return this._defaultEngine; - }, - - set defaultEngine(engine) { - if (this._defaultEngine && this._defaultEngine.icon) { - URL.revokeObjectURL(this._defaultEngine.icon); - } - let icon; - if (engine.iconData) { - icon = this._getFaviconURIFromIconData(engine.iconData); - } else { - icon = "chrome://global/skin/icons/defaultFavicon.svg"; - } - this._defaultEngine = { - name: engine.name, - icon, - isConfigEngine: engine.isConfigEngine, - }; - this._updateDefaultEngineHeader(); - this._updateDefaultEngineIcon(); - - if (engine && document.activeElement == this.input) { - this._speculativeConnect(); - } - }, - - get engines() { - return this._engines; - }, - - set engines(val) { - this._engines = val; - this._pendingOneOffRefresh = true; - }, - - // 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"), - ]; - // If we are selecting a suggestion and a one-off is selected, don't deselect it. - let excludeIndex = - idx < this.numSuggestions && this.selectedButtonIndex > -1 - ? this.numSuggestions + this.selectedButtonIndex - : -1; - 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 if (i != excludeIndex) { - elt.classList.remove("selected"); - ariaSelectedElt.setAttribute("aria-selected", "false"); - } - } - }, - - get selectedButtonIndex() { - let elts = [ - ...this._oneOffButtons, - document.getElementById("contentSearchSettingsButton"), - ]; - for (let i = 0; i < elts.length; ++i) { - if (elts[i].classList.contains("selected")) { - return i; - } - } - return -1; - }, - - set selectedButtonIndex(idx) { - let elts = [ - ...this._oneOffButtons, - document.getElementById("contentSearchSettingsButton"), - ]; - for (let i = 0; i < elts.length; ++i) { - let elt = elts[i]; - if (i == idx) { - elt.classList.add("selected"); - elt.setAttribute("aria-selected", "true"); - } else { - elt.classList.remove("selected"); - elt.setAttribute("aria-selected", "false"); - } - } - }, - - get selectedEngineName() { - let selectedElt = this._oneOffsTable.querySelector(".selected"); - if (selectedElt) { - return selectedElt.engineName; - } - return this.defaultEngine.name; - }, - - get numSuggestions() { - return this._suggestionsList.children.length; - }, - - selectAndUpdateInput(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(idx) { - let row = this._suggestionsList.children[idx]; - return row ? row.textContent : null; - }, - - deleteSuggestionAtIndex(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(idx) { - let row = this._suggestionsList.children[idx]; - return row && row.classList.contains("formHistory"); - }, - - addInputValueToFormHistory() { - let entry = { - value: this.input.value, - engineName: this.selectedEngineName, - }; - this._sendMsg("AddFormHistoryEntry", entry); - return entry; - }, - - handleEvent(event) { - // The event handler is triggered by external events while the search - // element may no longer be present - if (!document.contains(this.input)) { - return; - } - this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event); - }, - - _onCommand(aEvent) { - if (this.selectedButtonIndex == this._oneOffButtons.length) { - // Settings button was selected. - this._sendMsg("ManageEngines"); - return; - } - - this.search(aEvent); - - if (aEvent) { - aEvent.preventDefault(); - } - }, - - search(aEvent) { - if (!this.defaultEngine) { - return; // Not initialized yet. - } - - let searchText = this.input; - let searchTerms; - if ( - this._table.hidden || - (aEvent.originalTarget && - aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") || - aEvent instanceof KeyboardEvent - ) { - 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, - originalEvent: { - shiftKey: aEvent.shiftKey, - ctrlKey: aEvent.ctrlKey, - metaKey: aEvent.metaKey, - altKey: aEvent.altKey, - }, - }; - if ("button" in aEvent) { - eventData.originalEvent.button = aEvent.button; - } - - if (this.suggestionAtIndex(this.selectedIndex)) { - eventData.selection = { - index: this.selectedIndex, - kind: undefined, - }; - if (aEvent instanceof MouseEvent) { - eventData.selection.kind = "mouse"; - } else if (aEvent instanceof KeyboardEvent) { - eventData.selection.kind = "key"; - } - } - - this._sendMsg("Search", eventData); - this.addInputValueToFormHistory(); - }, - - _onInput() { - 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(); - }, - - _onKeydown(event) { - let selectedIndexDelta = 0; - let selectedSuggestionDelta = 0; - let selectedOneOffDelta = 0; - - switch (event.keyCode) { - case event.DOM_VK_UP: - if (this._table.hidden) { - return; - } - if (event.getModifierState("Accel")) { - if (event.shiftKey) { - selectedSuggestionDelta = -1; - break; - } - this._cycleCurrentEngine(true); - break; - } - if (event.altKey) { - selectedOneOffDelta = -1; - break; - } - selectedIndexDelta = -1; - break; - case event.DOM_VK_DOWN: - if (this._table.hidden) { - this._getSuggestions(); - return; - } - if (event.getModifierState("Accel")) { - if (event.shiftKey) { - selectedSuggestionDelta = 1; - break; - } - this._cycleCurrentEngine(false); - break; - } - if (event.altKey) { - selectedOneOffDelta = 1; - break; - } - selectedIndexDelta = 1; - break; - case event.DOM_VK_TAB: - if (this._table.hidden) { - return; - } - // Shift+tab when either the first or no one-off is selected, as well as - // tab when the settings button is selected, should change focus as normal. - if ( - (this.selectedButtonIndex <= 0 && event.shiftKey) || - (this.selectedButtonIndex == this._oneOffButtons.length && - !event.shiftKey) - ) { - return; - } - selectedOneOffDelta = event.shiftKey ? -1 : 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(); - return; - case event.DOM_VK_RETURN: - this._onCommand(event); - return; - case event.DOM_VK_DELETE: - if (this.selectedIndex >= 0) { - this.deleteSuggestionAtIndex(this.selectedIndex); - } - return; - case event.DOM_VK_ESCAPE: - if (!this._table.hidden) { - this._hideSuggestions(); - } - return; - default: - return; - } - - let currentIndex = this.selectedIndex; - if (selectedIndexDelta) { - let newSelectedIndex = currentIndex + selectedIndexDelta; - if (newSelectedIndex < -1) { - newSelectedIndex = this.numSuggestions + this._oneOffButtons.length; - } - // If are moving up from the first one off, we have to deselect the one off - // manually because the selectedIndex setter tries to exclude the selected - // one-off (which is desirable for accel+shift+up/down). - if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) { - this.selectedButtonIndex = -1; - } - this.selectAndUpdateInput(newSelectedIndex); - } else if (selectedSuggestionDelta) { - let newSelectedIndex; - if (currentIndex >= this.numSuggestions || currentIndex == -1) { - // No suggestion already selected, select the first/last one appropriately. - newSelectedIndex = - selectedSuggestionDelta == 1 ? 0 : this.numSuggestions - 1; - } else { - newSelectedIndex = currentIndex + selectedSuggestionDelta; - } - if (newSelectedIndex >= this.numSuggestions) { - newSelectedIndex = -1; - } - this.selectAndUpdateInput(newSelectedIndex); - } else if (selectedOneOffDelta) { - let newSelectedIndex; - let currentButton = this.selectedButtonIndex; - if ( - currentButton == -1 || - currentButton == this._oneOffButtons.length - ) { - // No one-off already selected, select the first/last one appropriately. - newSelectedIndex = - selectedOneOffDelta == 1 ? 0 : this._oneOffButtons.length - 1; - } else { - newSelectedIndex = currentButton + selectedOneOffDelta; - } - // Allow selection of the settings button via the tab key. - if ( - newSelectedIndex == this._oneOffButtons.length && - event.keyCode != event.DOM_VK_TAB - ) { - newSelectedIndex = -1; - } - this.selectedButtonIndex = newSelectedIndex; - } - - // Prevent the input's caret from moving. - event.preventDefault(); - }, - - _currentEngineIndex: -1, - _cycleCurrentEngine(aReverse) { - if ( - (this._currentEngineIndex == this._engines.length - 1 && !aReverse) || - (this._currentEngineIndex == 0 && aReverse) - ) { - return; - } - this._currentEngineIndex += aReverse ? -1 : 1; - let engineName = this._engines[this._currentEngineIndex].name; - this._sendMsg("SetCurrentEngine", engineName); - }, - - _onFocus() { - 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() { - 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(event) { - let idx = this._indexOfTableItem(event.target); - if (idx >= this.numSuggestions) { - // Deselect any search suggestion that has been selected. - this.selectedIndex = -1; - this.selectedButtonIndex = idx - this.numSuggestions; - return; - } - this.selectedIndex = idx; - }, - - _onMouseup(event) { - if (event.button == 2) { - return; - } - this._onCommand(event); - }, - - _onMouseout(event) { - // We only deselect one-off buttons and the settings button when they are - // moused out. - let idx = this._indexOfTableItem(event.originalTarget); - if (idx >= this.numSuggestions) { - this.selectedButtonIndex = -1; - } - }, - - _onClick(event) { - this._onMouseup(event); - }, - - _onContentSearchService(event) { - let methodName = "_onMsg" + event.detail.type; - if (methodName in this) { - this[methodName](event.detail.data); - } - }, - - _onMsgFocusInput() { - this.input.focus(); - }, - - _onMsgBlur() { - this.input.blur(); - this._hideSuggestions(); - }, - - _onMsgSuggestions(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; - if (this._pendingOneOffRefresh) { - this._setUpOneOffButtons(); - delete this._pendingOneOffRefresh; - } - this._currentEngineIndex = this._engines.findIndex( - aEngine => aEngine.name == this.defaultEngine.name - ); - this._table.hidden = false; - this.input.setAttribute("aria-expanded", "true"); - } - }, - - _onMsgSuggestionsCancelled() { - if (!this._table.hidden) { - this._hideSuggestions(); - } - }, - - _onMsgState(state) { - // Not all state messages broadcast the windows' privateness info. - if ("isPrivateWindow" in state) { - this._isPrivateEngine = state.isPrivateEngine; - } - - this.engines = state.engines; - - let currentEngine = state.currentEngine; - if (this._isPrivateEngine) { - currentEngine = state.currentPrivateEngine; - } - - // No point updating the default engine (and the header) if there's no change. - if ( - this.defaultEngine && - this.defaultEngine.name == currentEngine.name && - this.defaultEngine.icon == currentEngine.icon - ) { - return; - } - this.defaultEngine = currentEngine; - }, - - _onMsgCurrentState(state) { - this._onMsgState(state); - }, - - _onMsgCurrentEngine(engine) { - if (this._isPrivateEngine) { - return; - } - this.defaultEngine = engine; - this._pendingOneOffRefresh = true; - }, - - _onMsgCurrentPrivateEngine(engine) { - if (!this._isPrivateEngine) { - return; - } - this.defaultEngine = engine; - this._pendingOneOffRefresh = true; - }, - - _onMsgStrings(strings) { - this._strings = strings; - this._updateDefaultEngineHeader(); - this._updateSearchWithHeader(); - document.getElementById("contentSearchSettingsButton").textContent = - this._strings.searchSettings; - }, - - _updateDefaultEngineIcon() { - // We only show the engine's own icon for config engines, otherwise show - // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19 - let icon = this.defaultEngine.isConfigEngine - ? this.defaultEngine.icon - : "chrome://global/skin/icons/search-glass.svg"; - - document.body.style.setProperty( - "--newtab-search-icon", - "url(" + icon + ")" - ); - }, - - _updateDefaultEngineHeader() { - let header = document.getElementById("contentSearchDefaultEngineHeader"); - 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() { - if (!this._strings) { - return; - } - let searchWithHeader = document.getElementById( - "contentSearchSearchWithHeader" - ); - let labels = searchWithHeader.querySelectorAll("label"); - if (this.input.value) { - let header = this._strings.searchForSomethingWith2; - // Translators can use both %S and %1$S. - header = header.replace("%1$S", "%S").split("%S"); - labels[0].textContent = header[0]; - labels[1].textContent = this.input.value; - labels[2].textContent = header[1]; - } else { - labels[0].textContent = this._strings.searchWithHeader; - labels[1].textContent = ""; - labels[2].textContent = ""; - } - }, - - _speculativeConnect() { - if (this.defaultEngine) { - this._sendMsg("SpeculativeConnect", this.defaultEngine.name); - } - }, - - _makeTableRow(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; - }, - - /** - * If the favicon is an iconData object, convert it into a Blob URI. - * Otherwise just return the plain URI. - * - * @param {string|iconData} data - * The icon's URL or an iconData object containing the icon data. - * @returns {string} - * A blob URL or the plain icon URI. - */ - _getFaviconURIFromIconData(data) { - if (typeof data == "string") { - return data; - } - - // If typeof(data) != "string", the iconData object is returned. - let blob = new Blob([data.icon], { type: data.mimeType }); - return URL.createObjectURL(blob); - }, - - // Adds "@2x" to the name of the given PNG url for "retina" screens. - _getImageURIForCurrentResolution(uri) { - if (window.devicePixelRatio > 1) { - return uri.replace(/\.png$/, "@2x.png"); - } - return uri; - }, - - _getSearchEngines() { - this._sendMsg("GetState"); - }, - - _getStrings() { - this._sendMsg("GetStrings"); - }, - - _getSuggestions() { - this._stickyInputValue = this.input.value; - if (this.defaultEngine) { - this._sendMsg("GetSuggestions", { - engineName: this.defaultEngine.name, - searchString: this.input.value, - }); - } - }, - - _clearSuggestionRows() { - while (this._suggestionsList.firstElementChild) { - this._suggestionsList.firstElementChild.remove(); - } - }, - - _hideSuggestions() { - this.input.setAttribute("aria-expanded", "false"); - this.selectedIndex = -1; - this.selectedButtonIndex = -1; - this._currentEngineIndex = -1; - this._table.hidden = true; - }, - - _indexOfTableItem(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(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", this); - - let headerRow = document.createElementNS(HTML_NS, "tr"); - let header = document.createElementNS(HTML_NS, "td"); - headerRow.setAttribute("class", "contentSearchHeaderRow"); - header.setAttribute("class", "contentSearchHeader"); - let iconImg = document.createElementNS(HTML_NS, "img"); - header.appendChild(iconImg); - 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"; - let start = document.createElement("label"); - let inputLabel = document.createElement("label"); - inputLabel.setAttribute( - "class", - "contentSearchSearchWithHeaderSearchText" - ); - let end = document.createElement("label"); - header.appendChild(start); - header.appendChild(inputLabel); - header.appendChild(end); - this._oneOffsTable.appendChild(headerRow); - - let button = document.createElementNS(HTML_NS, "button"); - 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() { - // 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) - .filter(aEngine => !aEngine.hidden); - 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; - if (engine.iconData) { - uri = this._getFaviconURIFromIconData(engine.iconData); - } else { - uri = this._getImageURIForCurrentResolution( - "chrome://browser/skin/search-engine-placeholder.png" - ); - } - img.setAttribute("src", uri); - img.addEventListener( - "load", - function () { - URL.revokeObjectURL(uri); - }, - { once: true } - ); - button.appendChild(img); - button.style.width = buttonWidth + "px"; - button.setAttribute("engine-name", 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(type, data = null) { - dispatchEvent( - new CustomEvent("ContentSearchClient", { - detail: { - type, - data, - }, - }) - ); - }, - }; - - return ContentSearchUIController; -})(); diff --git a/browser/components/search/jar.mn b/browser/components/search/jar.mn @@ -8,9 +8,7 @@ browser.jar: content/browser/search/addEngine.xhtml (content/addEngine.xhtml) content/browser/search/autocomplete-popup.js (content/autocomplete-popup.js) content/browser/search/searchbar.js (content/searchbar.js) - content/browser/contentSearchUI.js (content/contentSearchUI.js) content/browser/contentSearchHandoffUI.js (content/contentSearchHandoffUI.js) - content/browser/contentSearchUI.css (content/contentSearchUI.css) search-extensions/ (extensions/**) % resource search-extensions %search-extensions/ contentaccessible=yes diff --git a/browser/components/search/test/browser/POSTSearchEngine.xml b/browser/components/search/test/browser/POSTSearchEngine.xml @@ -1,6 +0,0 @@ -<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> - <ShortName>POST Search</ShortName> - <Url type="text/html" method="POST" template="http://mochi.test:8888/browser/browser/components/search/test/browser/print_postdata.sjs"> - <Param name="searchterms" value="{searchTerms}"/> - </Url> -</OpenSearchDescription> diff --git a/browser/components/search/test/browser/browser.toml b/browser/components/search/test/browser/browser.toml @@ -6,6 +6,8 @@ support-files = [ "discovery.html", "head.js", "opensearch.html", + "searchSuggestionEngine.xml", + "searchSuggestionEngine.sjs", "test_windows1252.html", "test.html", "testEngine.xml", @@ -20,21 +22,6 @@ prefs = ["browser.search.widget.new=false"] ["browser_426329.js"] support-files = ["test.html", "discovery.html", "426329.xml"] -["browser_aboutHome_search_POST.js"] -support-files = ["POSTSearchEngine.xml", "print_postdata.sjs"] - -["browser_aboutHome_search_composing.js"] -support-files = ["searchSuggestionEngine.xml", "searchSuggestionEngine.sjs"] - -["browser_aboutHome_search_suggestion.js"] -support-files = ["searchSuggestionEngine.xml", "searchSuggestionEngine.sjs"] -skip-if = [ - "os == 'mac' && os_version == '10.15' && arch == 'x86_64'", - "os == 'mac' && os_version == '14.70' && arch == 'x86_64'", - "os == 'mac' && os_version == '15.30' && arch == 'aarch64'", - "os == 'win' && os_version == '10.2009' && arch == 'x86_64' && opt", # Bug 1399648, bug 1402502 -] - ["browser_addSearchEngineFromForm.js"] support-files = ["test.html", "test_windows1252.html"] @@ -58,13 +45,6 @@ support-files = [ "testEngine_chromeicon.xml", ] -["browser_contentSearchUI.js"] -support-files = [ - "contentSearchUI.html", - "contentSearchUI.js", - "searchSuggestionEngine.sjs", -] - ["browser_contentSearchUI_default.js"] ["browser_contextSearchTabPosition.js"] diff --git a/browser/components/search/test/browser/browser_aboutHome_search_POST.js b/browser/components/search/test/browser/browser_aboutHome_search_POST.js @@ -1,66 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -ignoreAllUncaughtExceptions(); - -add_task(async function () { - info("Check POST search engine support"); - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - - let currEngine = await Services.search.getDefault(); - - await BrowserTestUtils.withNewTab( - { gBrowser, url: "about:home" }, - async browser => { - let engine; - await promiseContentSearchChange(browser, async () => { - engine = await SearchTestUtils.installOpenSearchEngine({ - url: getRootDirectory(gTestPath) + "POSTSearchEngine.xml", - setAsDefault: true, - }); - return engine.name; - }); - - // Ready to execute the tests! - let needle = "Search for something awesome."; - - let promise = BrowserTestUtils.browserLoaded(browser); - await SpecialPowers.spawn(browser, [{ needle }], async function (args) { - let doc = content.document; - let el = doc.querySelector(["#searchText", "#newtab-search-text"]); - el.value = args.needle; - doc.getElementById("searchSubmit").click(); - }); - - await promise; - - // When the search results load, check them for correctness. - await SpecialPowers.spawn(browser, [{ needle }], async function (args) { - let loadedText = content.document.body.textContent; - ok(loadedText, "search page loaded"); - is( - loadedText, - "searchterms=" + escape(args.needle.replace(/\s/g, "+")), - "Search text should arrive correctly" - ); - }); - - await Services.search.setDefault( - currEngine, - Ci.nsISearchService.CHANGE_REASON_UNKNOWN - ); - try { - await Services.search.removeEngine(engine); - } catch (ex) {} - } - ); - await SpecialPowers.popPrefEnv(); -}); diff --git a/browser/components/search/test/browser/browser_aboutHome_search_composing.js b/browser/components/search/test/browser/browser_aboutHome_search_composing.js @@ -1,111 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -ignoreAllUncaughtExceptions(); - -add_task(async function () { - info("Clicking suggestion list while composing"); - - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - - await BrowserTestUtils.withNewTab( - { gBrowser, url: "about:home" }, - async function (browser) { - // Add a test engine that provides suggestions and switch to it. - let engine; - await promiseContentSearchChange(browser, async () => { - engine = await SearchTestUtils.installOpenSearchEngine({ - url: getRootDirectory(gTestPath) + "searchSuggestionEngine.xml", - setAsDefault: true, - }); - return engine.name; - }); - - // Clear any search history results - await FormHistory.update({ op: "remove" }); - - await SpecialPowers.spawn(browser, [], async function () { - // Start composition and type "x" - let input = content.document.querySelector([ - "#searchText", - "#newtab-search-text", - ]); - input.focus(); - }); - - info("Setting up the mutation observer before synthesizing composition"); - let mutationPromise = SpecialPowers.spawn(browser, [], async function () { - let searchController = content.wrappedJSObject.gContentSearchController; - - // Wait for the search suggestions to become visible. - let table = searchController._suggestionsList; - let input = content.document.querySelector([ - "#searchText", - "#newtab-search-text", - ]); - - await ContentTaskUtils.waitForMutationCondition( - input, - { attributeFilter: ["aria-expanded"] }, - () => input.getAttribute("aria-expanded") == "true" - ); - ok(!table.hidden, "Search suggestion table unhidden"); - - let row = table.children[1]; - row.setAttribute("id", "TEMPID"); - - // ContentSearchUIController looks at the current selectedIndex when - // performing a search. Synthesizing the mouse event on the suggestion - // doesn't actually mouseover the suggestion and trigger it to be flagged - // as selected, so we manually select it first. - searchController.selectedIndex = 1; - }); - - // FYI: "compositionstart" will be dispatched automatically. - await BrowserTestUtils.synthesizeCompositionChange( - { - composition: { - string: "x", - clauses: [ - { length: 1, attr: Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE }, - ], - }, - caret: { start: 1, length: 0 }, - }, - browser - ); - - info("Waiting for search suggestion table unhidden"); - await mutationPromise; - - // Click the second suggestion. - let expectedURL = (await Services.search.getDefault()).getSubmission( - "xbar", - null, - "homepage" - ).uri.spec; - let loadPromise = BrowserTestUtils.waitForDocLoadAndStopIt( - expectedURL, - gBrowser.selectedBrowser - ); - await BrowserTestUtils.synthesizeMouseAtCenter( - "#TEMPID", - { - button: 0, - }, - browser - ); - await loadPromise; - } - ); - await FormHistory.update({ op: "remove" }); - await SpecialPowers.popPrefEnv(); -}); diff --git a/browser/components/search/test/browser/browser_aboutHome_search_suggestion.js b/browser/components/search/test/browser/browser_aboutHome_search_suggestion.js @@ -1,74 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -ignoreAllUncaughtExceptions(); - -add_task(async function () { - // See browser_contentSearchUI.js for comprehensive content search UI tests. - info("Search suggestion smoke test"); - - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - - await BrowserTestUtils.withNewTab( - { gBrowser, url: "about:home" }, - async function (browser) { - // Add a test engine that provides suggestions and switch to it. - let engine; - await promiseContentSearchChange(browser, async () => { - engine = await SearchTestUtils.installOpenSearchEngine({ - url: getRootDirectory(gTestPath) + "searchSuggestionEngine.xml", - setAsDefault: true, - }); - return engine.name; - }); - - await SpecialPowers.spawn(browser, [], async function () { - // Type an X in the search input. - let input = content.document.querySelector([ - "#searchText", - "#newtab-search-text", - ]); - input.focus(); - }); - - await BrowserTestUtils.synthesizeKey("x", {}, browser); - - await SpecialPowers.spawn(browser, [], async function () { - // Wait for the search suggestions to become visible. - let table = content.document.getElementById("searchSuggestionTable"); - let input = content.document.querySelector([ - "#searchText", - "#newtab-search-text", - ]); - - await ContentTaskUtils.waitForMutationCondition( - input, - { attributeFilter: ["aria-expanded"] }, - () => input.getAttribute("aria-expanded") == "true" - ); - ok(!table.hidden, "Search suggestion table unhidden"); - }); - - // Empty the search input, causing the suggestions to be hidden. - await BrowserTestUtils.synthesizeKey("a", { accelKey: true }, browser); - await BrowserTestUtils.synthesizeKey("VK_DELETE", {}, browser); - - await SpecialPowers.spawn(browser, [], async function () { - let table = content.document.getElementById("searchSuggestionTable"); - await ContentTaskUtils.waitForCondition( - () => table.hidden, - "Search suggestion table hidden" - ); - }); - } - ); - await SpecialPowers.popPrefEnv(); -}); diff --git a/browser/components/search/test/browser/browser_contentSearchUI.js b/browser/components/search/test/browser/browser_contentSearchUI.js @@ -1,1134 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -const TEST_PAGE_BASENAME = "contentSearchUI.html"; - -const TEST_ENGINE1 = { - name: "searchSuggestionEngine1", - id: "other-searchSuggestionEngine1", - loadPath: "[addon]searchsuggestionengine1@tests.mozilla.org", -}; -const TEST_ENGINE2 = { - name: "searchSuggestionEngine2", - id: "other-searchSuggestionEngine2", - loadPath: "[addon]searchsuggestionengine2@tests.mozilla.org", -}; - -const TEST_MSG = "ContentSearchUIControllerTest"; - -ChromeUtils.defineESModuleGetters(this, { - ContentSearch: "resource:///actors/ContentSearchParent.sys.mjs", - FormHistoryTestUtils: - "resource://testing-common/FormHistoryTestUtils.sys.mjs", - SearchSuggestionController: - "moz-src:///toolkit/components/search/SearchSuggestionController.sys.mjs", -}); - -requestLongerTimeout(2); - -function waitForSuggestions() { - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => - ContentTaskUtils.waitForCondition( - () => - Cu.waiveXrays(content).gController.input.getAttribute( - "aria-expanded" - ) == "true", - "Waiting for suggestions", - 200 // Increased interval to support long textruns. - ) - ); -} - -async function waitForSearch() { - await BrowserTestUtils.waitForContentEvent( - gBrowser.selectedBrowser, - "ContentSearchClient", - true, - event => { - if (event.detail.type == "Search") { - event.target._eventDetail = event.detail.data; - return true; - } - return false; - }, - true - ); - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - let eventDetail = content._eventDetail; - delete content._eventDetail; - return eventDetail; - }); -} - -async function waitForSearchSettings() { - await BrowserTestUtils.waitForContentEvent( - gBrowser.selectedBrowser, - "ContentSearchClient", - true, - event => { - if (event.detail.type == "ManageEngines") { - event.target._eventDetail = event.detail.data; - return true; - } - return false; - }, - true - ); - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - let eventDetail = content._eventDetail; - delete content._eventDetail; - return eventDetail; - }); -} - -function getCurrentState() { - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - let controller = Cu.waiveXrays(content).gController; - let state = { - selectedIndex: controller.selectedIndex, - selectedButtonIndex: controller.selectedButtonIndex, - numSuggestions: controller._table.hidden ? 0 : controller.numSuggestions, - suggestionAtIndex: [], - isFormHistorySuggestionAtIndex: [], - - tableHidden: controller._table.hidden, - - inputValue: controller.input.value, - ariaExpanded: controller.input.getAttribute("aria-expanded"), - }; - - if (state.numSuggestions) { - for (let i = 0; i < controller.numSuggestions; i++) { - state.suggestionAtIndex.push(controller.suggestionAtIndex(i)); - state.isFormHistorySuggestionAtIndex.push( - controller.isFormHistorySuggestionAtIndex(i) - ); - } - } - - return state; - }); -} - -async function msg(type, data = null) { - switch (type) { - case "reset": - // Reset both the input and suggestions by select all + delete. If there was - // no text entered, this won't have any effect, so also escape to ensure the - // suggestions table is closed. - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - Cu.waiveXrays(content).gController.input.focus(); - EventUtils.synthesizeKey("a", { accelKey: true }, content); - EventUtils.synthesizeKey("KEY_Delete", {}, content); - EventUtils.synthesizeKey("KEY_Escape", {}, content); - }); - break; - - case "key": { - let keyName = typeof data == "string" ? data : data.key; - await BrowserTestUtils.synthesizeKey( - keyName, - data.modifiers || {}, - gBrowser.selectedBrowser - ); - if (data?.waitForSuggestions) { - await waitForSuggestions(); - } - break; - } - case "text": { - await SpecialPowers.spawn( - gBrowser.selectedBrowser, - [data.value], - text => { - Cu.waiveXrays(content).gController.input.value = text.substring( - 0, - text.length - 1 - ); - EventUtils.synthesizeKey( - text.substring(text.length - 1), - {}, - content - ); - } - ); - if (data?.waitForSuggestions) { - await waitForSuggestions(); - } - break; - } - case "startComposition": - await BrowserTestUtils.synthesizeComposition( - "compositionstart", - gBrowser.selectedBrowser - ); - break; - case "changeComposition": { - await BrowserTestUtils.synthesizeCompositionChange( - { - composition: { - string: data.data, - clauses: [ - { - length: data.length, - attr: Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE, - }, - ], - }, - caret: { start: data.length, length: 0 }, - }, - gBrowser.selectedBrowser - ); - if (data?.waitForSuggestions) { - await waitForSuggestions(); - } - break; - } - case "commitComposition": - await BrowserTestUtils.synthesizeComposition( - "compositioncommitasis", - gBrowser.selectedBrowser - ); - break; - case "mousemove": - case "click": { - let event; - let index; - if (type == "mousemove") { - event = { - type: "mousemove", - clickcount: 0, - }; - index = data; - } else { - event = data.modifiers || null; - index = data.eltIdx; - } - - await SpecialPowers.spawn( - gBrowser.selectedBrowser, - [type, event, index], - (eventType, eventArgs, itemIndex) => { - let controller = Cu.waiveXrays(content).gController; - return new Promise(resolve => { - let row; - if (itemIndex == -1) { - row = controller._table.firstChild; - } else { - let allElts = [ - ...controller._suggestionsList.children, - ...controller._oneOffButtons, - content.document.getElementById("contentSearchSettingsButton"), - ]; - row = allElts[itemIndex]; - } - row.addEventListener(eventType, () => resolve(), { once: true }); - EventUtils.synthesizeMouseAtCenter(row, eventArgs, content); - }); - } - ); - break; - } - } - - return getCurrentState(); -} - -/** - * Focusses the in-content search bar. - * - * @returns {Promise} - * A promise that is resolved once the focus is complete. - */ -function focusContentSearchBar() { - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - Cu.waiveXrays(content).input.focus(); - }); -} - -let extension1; -let extension2; - -add_setup(async function () { - const pageURL = getRootDirectory(gTestPath) + TEST_PAGE_BASENAME; - - let cleanupAboutPage; - await BrowserTestUtils.registerAboutPage( - callback => (cleanupAboutPage = callback), - "test-about-content-search-ui", - pageURL, - Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | - Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD | - Ci.nsIAboutModule.ALLOW_SCRIPT | - Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS - ); - - let originalOnMessageSearch = ContentSearch._onMessageSearch; - let originalOnMessageManageEngines = ContentSearch._onMessageManageEngines; - - ContentSearch._onMessageSearch = () => {}; - ContentSearch._onMessageManageEngines = () => {}; - - let currentEngines = await Services.search.getVisibleEngines(); - - extension1 = await SearchTestUtils.installSearchExtension( - { - name: TEST_ENGINE1.name, - suggest_url: - "https://example.com/browser/browser/components/search/test/browser/searchSuggestionEngine.sjs", - suggest_url_get_params: "query={searchTerms}", - }, - { setAsDefault: true } - ); - extension2 = await SearchTestUtils.installSearchExtension({ - name: TEST_ENGINE2.name, - suggest_url: - "https://example.com/browser/browser/components/search/test/browser/searchSuggestionEngine.sjs", - suggest_url_get_params: "query={searchTerms}", - }); - - for (let engine of currentEngines) { - await Services.search.removeEngine(engine); - } - - registerCleanupFunction(async () => { - // Ensure tabs are closed before we continue on with the cleanup. - for (let tab of tabs) { - BrowserTestUtils.removeTab(tab); - } - Services.search.restoreDefaultEngines(); - - await TestUtils.waitForTick(); - - ContentSearch._onMessageSearch = originalOnMessageSearch; - ContentSearch._onMessageManageEngines = originalOnMessageManageEngines; - - if (cleanupAboutPage) { - await cleanupAboutPage(); - } - }); - - await promiseTab(); -}); - -add_task(async function emptyInput() { - await focusContentSearchBar(); - - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_BACK_SPACE"); - checkState(state, "", [], -1); - - await msg("reset"); -}); - -add_task(async function blur() { - await focusContentSearchBar(); - - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - Cu.waiveXrays(content).gController.input.blur(); - }); - state = await getCurrentState(); - checkState(state, "x", [], -1); - - await msg("reset"); -}); - -add_task(async function upDownKeys() { - await focusContentSearchBar(); - - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - // Cycle down the suggestions starting from no selection. - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "x", ["xfoo", "xbar"], 2); - - state = await msg("key", "VK_DOWN"); - checkState(state, "x", ["xfoo", "xbar"], 3); - - state = await msg("key", "VK_DOWN"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - // Cycle up starting from no selection. - state = await msg("key", "VK_UP"); - checkState(state, "x", ["xfoo", "xbar"], 3); - - state = await msg("key", "VK_UP"); - checkState(state, "x", ["xfoo", "xbar"], 2); - - state = await msg("key", "VK_UP"); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - state = await msg("key", "VK_UP"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0); - - state = await msg("key", "VK_UP"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await msg("reset"); -}); - -add_task(async function rightLeftKeys() { - await focusContentSearchBar(); - - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_LEFT"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_LEFT"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_RIGHT"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_RIGHT"); - checkState(state, "x", [], -1); - - state = await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0); - - // This should make the xfoo suggestion sticky. To make sure it sticks, - // trigger suggestions again and cycle through them by pressing Down until - // nothing is selected again. - state = await msg("key", "VK_RIGHT"); - checkState(state, "xfoo", [], -1); - - state = await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoofoo", ["xfoofoo", "xfoobar"], 0); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoobar", ["xfoofoo", "xfoobar"], 1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoofoo", "xfoobar"], 2); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoofoo", "xfoobar"], 3); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1); - - await msg("reset"); -}); - -add_task(async function tabKey() { - await focusContentSearchBar(); - await msg("key", { key: "x", waitForSuggestions: true }); - - let state = await msg("key", "VK_TAB"); - checkState(state, "x", ["xfoo", "xbar"], 2); - - state = await msg("key", "VK_TAB"); - checkState(state, "x", ["xfoo", "xbar"], 3); - - state = await msg("key", { key: "VK_TAB", modifiers: { shiftKey: true } }); - checkState(state, "x", ["xfoo", "xbar"], 2); - - state = await msg("key", { key: "VK_TAB", modifiers: { shiftKey: true } }); - checkState(state, "x", [], -1); - - await focusContentSearchBar(); - - await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - - for (let i = 0; i < 3; ++i) { - state = await msg("key", "VK_TAB"); - } - checkState(state, "x", [], -1); - - await focusContentSearchBar(); - - await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - state = await msg("key", "VK_DOWN"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0); - - state = await msg("key", "VK_TAB"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0, 0); - - state = await msg("key", "VK_TAB"); - checkState(state, "xfoo", ["xfoo", "xbar"], 0, 1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 1); - - state = await msg("key", "VK_DOWN"); - checkState(state, "x", ["xfoo", "xbar"], 2); - - state = await msg("key", "VK_UP"); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - state = await msg("key", "VK_TAB"); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 0); - - state = await msg("key", "VK_TAB"); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 1); - - state = await msg("key", "VK_TAB"); - checkState(state, "xbar", [], -1); - - await msg("reset"); -}); - -add_task(async function cycleSuggestions() { - await focusContentSearchBar(); - await msg("key", { key: "x", waitForSuggestions: true }); - - let cycle = async function (aSelectedButtonIndex) { - let modifiers = { - shiftKey: true, - accelKey: true, - }; - - let state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex); - }; - - await cycle(); - - // Repeat with a one-off selected. - let state = await msg("key", "VK_TAB"); - checkState(state, "x", ["xfoo", "xbar"], 2); - await cycle(0); - - // Repeat with the settings button selected. - state = await msg("key", "VK_TAB"); - checkState(state, "x", ["xfoo", "xbar"], 3); - await cycle(1); - - await msg("reset"); -}); - -add_task(async function cycleOneOffs() { - await focusContentSearchBar(); - await msg("key", { key: "x", waitForSuggestions: true }); - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - let btn = - Cu.waiveXrays(content).gController._oneOffButtons[ - Cu.waiveXrays(content).gController._oneOffButtons.length - 1 - ]; - let newBtn = btn.cloneNode(true); - btn.parentNode.appendChild(newBtn); - Cu.waiveXrays(content).gController._oneOffButtons.push(newBtn); - }); - - let state = await msg("key", "VK_DOWN"); - state = await msg("key", "VK_DOWN"); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - let modifiers = { - altKey: true, - }; - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 0); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 1); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 1); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 0); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1); - - // If the settings button is selected, pressing alt+up/down should select the - // last/first one-off respectively (and deselect the settings button). - await msg("key", "VK_TAB"); - await msg("key", "VK_TAB"); - state = await msg("key", "VK_TAB"); // Settings button selected. - checkState(state, "xbar", ["xfoo", "xbar"], 1, 2); - - state = await msg("key", { key: "VK_UP", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 1); - - state = await msg("key", "VK_TAB"); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 2); - - state = await msg("key", { key: "VK_DOWN", modifiers }); - checkState(state, "xbar", ["xfoo", "xbar"], 1, 0); - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - Cu.waiveXrays(content).gController._oneOffButtons.pop().remove(); - }); - await msg("reset"); -}); - -add_task(async function mouse() { - await focusContentSearchBar(); - - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("mousemove", 0); - checkState(state, "x", ["xfoo", "xbar"], 0); - - state = await msg("mousemove", 1); - checkState(state, "x", ["xfoo", "xbar"], 1); - - state = await msg("mousemove", 2); - checkState(state, "x", ["xfoo", "xbar"], 2, 0); - - state = await msg("mousemove", 3); - checkState(state, "x", ["xfoo", "xbar"], 3, 1); - - state = await msg("mousemove", -1); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await msg("reset"); - await focusContentSearchBar(); - - state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - state = await msg("mousemove", 0); - checkState(state, "x", ["xfoo", "xbar"], 0); - - state = await msg("mousemove", 2); - checkState(state, "x", ["xfoo", "xbar"], 2, 0); - - state = await msg("mousemove", -1); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await msg("reset"); -}); - -add_task(async function formHistory() { - await focusContentSearchBar(); - - // Type an X and add it to form history. - let state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - // Wait for Satchel to say it's been added to form history. - let observePromise = new Promise(resolve => { - Services.obs.addObserver(function onAdd(subj, topic, data) { - if (data == "formhistory-add") { - Services.obs.removeObserver(onAdd, "satchel-storage-changed"); - executeSoon(resolve); - } - }, "satchel-storage-changed"); - }); - - await FormHistoryTestUtils.clear("searchbar-history"); - let entry = await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - return Cu.waiveXrays(content).gController.addInputValueToFormHistory(); - }); - await observePromise; - Assert.greater( - await FormHistoryTestUtils.count("searchbar-history", { - source: entry.source, - }), - 0 - ); - - // Reset the input. - state = await msg("reset"); - checkState(state, "", [], -1); - - // Type an X again. The form history entry should appear. - state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState( - state, - "x", - [{ str: "x", type: "formHistory" }, "xfoo", "xbar"], - -1 - ); - - // Select the form history entry and delete it. - state = await msg("key", "VK_DOWN"); - checkState( - state, - "x", - [{ str: "x", type: "formHistory" }, "xfoo", "xbar"], - 0 - ); - - // Wait for Satchel. - observePromise = new Promise(resolve => { - Services.obs.addObserver(function onRemove(subj, topic, data) { - if (data == "formhistory-remove") { - Services.obs.removeObserver(onRemove, "satchel-storage-changed"); - executeSoon(resolve); - } - }, "satchel-storage-changed"); - }); - state = await msg("key", "VK_DELETE"); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await observePromise; - - // Reset the input. - state = await msg("reset"); - checkState(state, "", [], -1); - - // Type an X again. The form history entry should still be gone. - state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await msg("reset"); -}); - -add_task(async function formHistory_limit() { - info("Check long strings are not added to form history"); - await focusContentSearchBar(); - const gLongString = new Array( - SearchSuggestionController.SEARCH_HISTORY_MAX_VALUE_LENGTH + 1 - ) - .fill("x") - .join(""); - // Type and confirm a very long string. - let state = await msg("text", { - value: gLongString, - waitForSuggestions: true, - }); - checkState( - state, - gLongString, - [`${gLongString}foo`, `${gLongString}bar`], - -1 - ); - - await FormHistoryTestUtils.clear("searchbar-history"); - let entry = await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { - return Cu.waiveXrays(content).gController.addInputValueToFormHistory(); - }); - // There's nothing we can wait for, since addition should not be happening. - /* eslint-disable mozilla/no-arbitrary-setTimeout */ - await new Promise(resolve => setTimeout(resolve, 500)); - Assert.equal( - await FormHistoryTestUtils.count("searchbar-history", { - source: entry.source, - }), - 0 - ); - - await msg("reset"); -}); - -add_task(async function cycleEngines() { - await focusContentSearchBar(); - await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - - Services.telemetry.clearEvents(); - Services.fog.testResetFOG(); - - let p = SearchTestUtils.promiseSearchNotification( - "engine-default", - "browser-search-engine-modified" - ); - await msg("key", { key: "VK_DOWN", modifiers: { accelKey: true } }); - let newEngine = await p; - Assert.equal( - newEngine.name, - TEST_ENGINE2.name, - "Should have correctly cycled the engine" - ); - - let snapshot = await Glean.searchEngineDefault.changed.testGetValue(); - delete snapshot[0].timestamp; - Assert.deepEqual( - snapshot[0], - { - category: "search.engine.default", - name: "changed", - extra: { - new_load_path: TEST_ENGINE2.loadPath, - previous_engine_id: TEST_ENGINE1.id, - change_reason: "user_searchbar", - new_engine_id: TEST_ENGINE2.id, - new_display_name: TEST_ENGINE2.name, - new_submission_url: "", - }, - }, - "Should have received the correct event details" - ); - - p = SearchTestUtils.promiseSearchNotification( - "engine-default", - "browser-search-engine-modified" - ); - await msg("key", { key: "VK_UP", modifiers: { accelKey: true } }); - newEngine = await p; - Assert.equal( - newEngine.name, - TEST_ENGINE1.name, - "Should have correctly cycled the engine" - ); - - snapshot = await Glean.searchEngineDefault.changed.testGetValue(); - delete snapshot[1].timestamp; - Assert.deepEqual( - snapshot[1], - { - category: "search.engine.default", - name: "changed", - extra: { - new_load_path: TEST_ENGINE1.loadPath, - previous_engine_id: TEST_ENGINE2.id, - change_reason: "user_searchbar", - new_engine_id: TEST_ENGINE1.id, - new_display_name: TEST_ENGINE1.name, - new_submission_url: "", - }, - }, - "Should have received the correct event details" - ); - - await msg("reset"); -}); - -add_task(async function search() { - await focusContentSearchBar(); - - let modifiers = {}; - ["altKey", "ctrlKey", "metaKey", "shiftKey"].forEach( - k => (modifiers[k] = true) - ); - - // Test typing a query and pressing enter. - let p = waitForSearch(); - await msg("key", { key: "x", waitForSuggestions: true }); - await msg("key", { key: "VK_RETURN", modifiers }); - let mesg = await p; - let eventData = { - engineName: TEST_ENGINE1.name, - searchString: "x", - healthReportKey: "test", - originalEvent: modifiers, - }; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test typing a query, then selecting a suggestion and pressing enter. - p = waitForSearch(); - await msg("key", { key: "x", waitForSuggestions: true }); - await msg("key", "VK_DOWN"); - await msg("key", "VK_DOWN"); - await msg("key", { key: "VK_RETURN", modifiers }); - mesg = await p; - eventData.searchString = "xfoo"; - eventData.engineName = TEST_ENGINE1.name; - eventData.selection = { - index: 1, - kind: "key", - }; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test typing a query, then selecting a one-off button and pressing enter. - p = waitForSearch(); - await msg("key", { key: "x", waitForSuggestions: true }); - await msg("key", "VK_UP"); - await msg("key", "VK_UP"); - await msg("key", { key: "VK_RETURN", modifiers }); - mesg = await p; - delete eventData.selection; - eventData.searchString = "x"; - eventData.engineName = TEST_ENGINE2.name; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test typing a query and clicking the search engine header. - p = waitForSearch(); - modifiers.button = 0; - await msg("key", { key: "x", waitForSuggestions: true }); - await msg("mousemove", -1); - await msg("click", { eltIdx: -1, modifiers }); - mesg = await p; - eventData.originalEvent = modifiers; - eventData.engineName = TEST_ENGINE1.name; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test typing a query and then clicking a suggestion. - await msg("key", { key: "x", waitForSuggestions: true }); - p = waitForSearch(); - await msg("mousemove", 1); - await msg("click", { eltIdx: 1, modifiers }); - mesg = await p; - eventData.searchString = "xfoo"; - eventData.selection = { - index: 1, - kind: "mouse", - }; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test typing a query and then clicking a one-off button. - await msg("key", { key: "x", waitForSuggestions: true }); - p = waitForSearch(); - await msg("mousemove", 3); - await msg("click", { eltIdx: 3, modifiers }); - mesg = await p; - eventData.searchString = "x"; - eventData.engineName = TEST_ENGINE2.name; - delete eventData.selection; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test selecting a suggestion, then clicking a one-off without deselecting the - // suggestion, using the keyboard. - delete modifiers.button; - await msg("key", { key: "x", waitForSuggestions: true }); - p = waitForSearch(); - await msg("key", "VK_DOWN"); - await msg("key", "VK_DOWN"); - await msg("key", "VK_TAB"); - await msg("key", { key: "VK_RETURN", modifiers }); - mesg = await p; - eventData.searchString = "xfoo"; - eventData.selection = { - index: 1, - kind: "key", - }; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Test searching when using IME composition. - let state = await msg("startComposition", { data: "" }); - checkState(state, "", [], -1); - state = await msg("changeComposition", { - data: "x", - waitForSuggestions: true, - }); - checkState( - state, - "x", - [ - { str: "x", type: "formHistory" }, - { str: "xfoo", type: "formHistory" }, - "xbar", - ], - -1 - ); - await msg("commitComposition"); - delete modifiers.button; - p = waitForSearch(); - await msg("key", { key: "VK_RETURN", modifiers }); - mesg = await p; - eventData.searchString = "x"; - eventData.originalEvent = modifiers; - eventData.engineName = TEST_ENGINE1.name; - delete eventData.selection; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - state = await msg("startComposition", { data: "" }); - checkState(state, "", [], -1); - state = await msg("changeComposition", { - data: "x", - waitForSuggestions: true, - }); - checkState( - state, - "x", - [ - { str: "x", type: "formHistory" }, - { str: "xfoo", type: "formHistory" }, - "xbar", - ], - -1 - ); - - // Mouse over the first suggestion. - state = await msg("mousemove", 0); - checkState( - state, - "x", - [ - { str: "x", type: "formHistory" }, - { str: "xfoo", type: "formHistory" }, - "xbar", - ], - 0 - ); - - // Mouse over the second suggestion. - state = await msg("mousemove", 1); - checkState( - state, - "x", - [ - { str: "x", type: "formHistory" }, - { str: "xfoo", type: "formHistory" }, - "xbar", - ], - 1 - ); - - modifiers.button = 0; - p = waitForSearch(); - await msg("click", { eltIdx: 1, modifiers }); - mesg = await p; - eventData.searchString = "xfoo"; - eventData.originalEvent = modifiers; - eventData.selection = { - index: 1, - kind: "mouse", - }; - SimpleTest.isDeeply(eventData, mesg, "Search event data"); - - await promiseTab(); - await focusContentSearchBar(); - - // Remove form history entries. - // Wait for Satchel. - let observePromise = new Promise(resolve => { - let historyCount = 2; - Services.obs.addObserver(function onRemove(subj, topic, data) { - if (data == "formhistory-remove") { - if (--historyCount) { - return; - } - Services.obs.removeObserver(onRemove, "satchel-storage-changed"); - executeSoon(resolve); - } - }, "satchel-storage-changed"); - }); - - await msg("key", { key: "x", waitForSuggestions: true }); - await msg("key", "VK_DOWN"); - await msg("key", "VK_DOWN"); - await msg("key", "VK_DELETE"); - await msg("key", "VK_DOWN"); - await msg("key", "VK_DELETE"); - await observePromise; - - await msg("reset"); - state = await msg("key", { key: "x", waitForSuggestions: true }); - checkState(state, "x", ["xfoo", "xbar"], -1); - - await promiseTab(); - await focusContentSearchBar(); - await msg("reset"); -}); - -add_task(async function settings() { - await focusContentSearchBar(); - await msg("key", { key: "VK_DOWN", waitForSuggestions: true }); - await msg("key", "VK_UP"); - let p = waitForSearchSettings(); - await msg("key", "VK_RETURN"); - await p; - - await msg("reset"); -}); - -function checkState( - actualState, - expectedInputVal, - expectedSuggestions, - expectedSelectedIdx, - expectedSelectedButtonIdx -) { - expectedSuggestions = expectedSuggestions.map(sugg => { - return typeof sugg == "object" - ? sugg - : { - str: sugg, - type: "remote", - }; - }); - - if (expectedSelectedIdx == -1 && expectedSelectedButtonIdx != undefined) { - expectedSelectedIdx = - expectedSuggestions.length + expectedSelectedButtonIdx; - } - - let expectedState = { - selectedIndex: expectedSelectedIdx, - numSuggestions: expectedSuggestions.length, - suggestionAtIndex: expectedSuggestions.map(s => s.str), - isFormHistorySuggestionAtIndex: expectedSuggestions.map( - s => s.type == "formHistory" - ), - - tableHidden: !expectedSuggestions.length, - - inputValue: expectedInputVal, - ariaExpanded: !expectedSuggestions.length ? "false" : "true", - }; - if (expectedSelectedButtonIdx != undefined) { - expectedState.selectedButtonIndex = expectedSelectedButtonIdx; - } else if (expectedSelectedIdx < expectedSuggestions.length) { - expectedState.selectedButtonIndex = -1; - } else { - expectedState.selectedButtonIndex = - expectedSelectedIdx - expectedSuggestions.length; - } - - SimpleTest.isDeeply(actualState, expectedState, "State"); -} - -var gMsgMan; -var tabs = []; -async function promiseTab() { - let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); - tabs.push(tab); - - let loadedPromise = BrowserTestUtils.firstBrowserLoaded(window); - openTrustedLinkIn("about:test-about-content-search-ui", "current"); - await loadedPromise; -} diff --git a/browser/components/search/test/browser/browser_contentSearchUI_default.js b/browser/components/search/test/browser/browser_contentSearchUI_default.js @@ -2,9 +2,6 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ const TEST_ENGINE_NAME = "searchSuggestionEngine"; -const HANDOFF_PREF = - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar"; - let extension; let defaultEngine; let addedEngine; @@ -115,7 +112,7 @@ async function ensurePlaceholder(tab, expectedId, expectedEngine) { ); } -async function runNewTabTest(isHandoff) { +async function runNewTabTest() { let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:newtab", gBrowser, @@ -125,13 +122,11 @@ async function runNewTabTest(isHandoff) { let engineIcon = await defaultEngine.getIconURL(16); await ensureIcon(tab, engineIcon); - if (isHandoff) { - await ensurePlaceholder( - tab, - "newtab-search-box-handoff-input", - Services.search.defaultEngine.name - ); - } + await ensurePlaceholder( + tab, + "newtab-search-box-handoff-input", + Services.search.defaultEngine.name + ); await Services.search.setDefault( addedEngine, @@ -141,19 +136,15 @@ async function runNewTabTest(isHandoff) { // We only show the engine's own icon for config engines, otherwise show // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19 await ensureIcon(tab, "chrome://global/skin/icons/search-glass.svg"); - if (isHandoff) { - await ensurePlaceholder(tab, "newtab-search-box-handoff-input-no-engine"); - } + await ensurePlaceholder(tab, "newtab-search-box-handoff-input-no-engine"); // Disable suggestions in the Urlbar. This should update the placeholder // string since handoff will now enter search mode. - if (isHandoff) { - await SpecialPowers.pushPrefEnv({ - set: [["browser.urlbar.suggest.searches", false]], - }); - await ensurePlaceholder(tab, "newtab-search-box-input"); - await SpecialPowers.popPrefEnv(); - } + await SpecialPowers.pushPrefEnv({ + set: [["browser.urlbar.suggest.searches", false]], + }); + await ensurePlaceholder(tab, "newtab-search-box-input"); + await SpecialPowers.popPrefEnv(); await Services.search.setDefault( defaultEngine, @@ -164,21 +155,7 @@ async function runNewTabTest(isHandoff) { } add_task(async function test_content_search_attributes() { - await SpecialPowers.pushPrefEnv({ - set: [[HANDOFF_PREF, true]], - }); - - await runNewTabTest(true); - await SpecialPowers.popPrefEnv(); -}); - -add_task(async function test_content_search_attributes_no_handoff() { - await SpecialPowers.pushPrefEnv({ - set: [[HANDOFF_PREF, false]], - }); - - await runNewTabTest(false); - await SpecialPowers.popPrefEnv(); + await runNewTabTest(); }); add_task(async function test_content_search_attributes_in_private_window() { @@ -223,14 +200,11 @@ add_task(async function test_content_search_attributes_in_private_window() { add_task(async function test_content_search_permanent_private_browsing() { await SpecialPowers.pushPrefEnv({ - set: [ - [HANDOFF_PREF, true], - ["browser.privatebrowsing.autostart", true], - ], + set: [["browser.privatebrowsing.autostart", true]], }); let win = await BrowserTestUtils.openNewBrowserWindow(); - await runNewTabTest(true); + await runNewTabTest(); await BrowserTestUtils.closeWindow(win); await SpecialPowers.popPrefEnv(); }); diff --git a/browser/components/search/test/browser/contentSearchUI.html b/browser/components/search/test/browser/contentSearchUI.html @@ -1,25 +0,0 @@ -<!DOCTYPE html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - -<html> -<head> -<meta charset="utf-8"> -<script type="application/javascript" - src="chrome://browser/content/contentSearchUI.js"> -</script> -<link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css"/> -<meta http-equiv="Content-Security-Policy" content="default-src chrome:; img-src chrome: data:; object-src 'none'"/> -</head> -<body> - -<!-- Dummy Button is used to ensure pressing Shift+Tab on <input> will make the new focus - - remains in the same document, rather than the Chrome UI. --> -<button>Dummy Button</button> -<div id="container"><input type="text" value=""/></div> - -<script src="chrome://mochitests/content/browser/browser/components/search/test/browser/contentSearchUI.js"> -</script> - -</body> -</html> diff --git a/browser/components/search/test/browser/contentSearchUI.js b/browser/components/search/test/browser/contentSearchUI.js @@ -1,12 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -/* import-globals-from ../../content/contentSearchUI.js */ -var input = document.querySelector("input"); -var gController = new ContentSearchUIController( - input, - input.parentNode, - "test" -); diff --git a/browser/components/search/test/browser/print_postdata.sjs b/browser/components/search/test/browser/print_postdata.sjs @@ -1,25 +0,0 @@ -const CC = Components.Constructor; -const BinaryInputStream = CC( - "@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream" -); - -function handleRequest(request, response) { - response.setHeader("Content-Type", "text/plain", false); - if (request.method == "GET") { - response.write(request.queryString); - } else { - let body = new BinaryInputStream(request.bodyInputStream); - - let avail; - let bytes = []; - - while ((avail = body.available()) > 0) { - Array.prototype.push.apply(bytes, body.readByteArray(avail)); - } - - let data = String.fromCharCode.apply(null, bytes); - response.bodyOutputStream.write(data, data.length); - } -} diff --git a/browser/components/search/test/browser/telemetry/browser.toml b/browser/components/search/test/browser/telemetry/browser.toml @@ -18,8 +18,6 @@ support-files = [ "searchTelemetryAd_components_text.html", ] -["browser_search_telemetry_aboutHome.js"] - ["browser_search_telemetry_adImpression_component.js"] support-files = [ "searchTelemetryAd_components_carousel.html", @@ -205,9 +203,6 @@ support-files = ["searchTelemetry.html"] ["browser_search_telemetry_sources.js"] support-files = ["searchTelemetry.html", "searchTelemetryAd.html"] -["browser_search_telemetry_sources_about.js"] -support-files = ["searchTelemetry.html", "searchTelemetryAd.html"] - ["browser_search_telemetry_sources_ads.js"] support-files = ["searchTelemetry.html", "searchTelemetryAd.html"] diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_aboutHome.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_aboutHome.js @@ -1,117 +0,0 @@ -"use strict"; - -const SCALAR_ABOUT_HOME = "browser.engagement.navigation.about_home"; - -add_setup(async function () { - // about:home uses IndexedDB. However, the test finishes too quickly and doesn't - // allow it enougth time to save. So it throws. This disables all the uncaught - // exception in this file and that's the reason why we split about:home tests - // out of the other UsageTelemetry files. - ignoreAllUncaughtExceptions(); - - // Create two new search engines. Mark one as the default engine, so - // the test don't crash. We need to engines for this test as the searchbar - // in content doesn't display the default search engine among the one-off engines. - await SearchTestUtils.installSearchExtension( - { - name: "MozSearch", - keyword: "mozalias", - }, - { setAsDefault: true } - ); - await SearchTestUtils.installSearchExtension({ - name: "MozSearch2", - keyword: "mozalias2", - }); - - // Move the second engine at the beginning of the one-off list. - let engineOneOff = Services.search.getEngineByName("MozSearch2"); - await Services.search.moveEngine(engineOneOff, 0); - - // Enable local telemetry recording for the duration of the tests. - let oldCanRecord = Services.telemetry.canRecordExtended; - Services.telemetry.canRecordExtended = true; - - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - - registerCleanupFunction(async function () { - await PlacesUtils.history.clear(); - Services.telemetry.canRecordExtended = oldCanRecord; - }); -}); - -add_task(async function test_abouthome_activitystream_simpleQuery() { - // Let's reset the counts. - Services.telemetry.clearScalars(); - Services.telemetry.clearEvents(); - Services.fog.testResetFOG(); - TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"); - - let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); - - info("Load about:home."); - BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, "about:home"); - await BrowserTestUtils.browserStopped(tab.linkedBrowser, "about:home"); - - info("Wait for ContentSearchUI search provider to initialize."); - await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { - await ContentTaskUtils.waitForCondition( - () => content.wrappedJSObject.gContentSearchController.defaultEngine - ); - }); - - info("Trigger a simple search, just test + enter."); - let p = BrowserTestUtils.browserStopped( - tab.linkedBrowser, - "https://example.com/?q=test+query" - ); - await typeInSearchField( - tab.linkedBrowser, - "test query", - "newtab-search-text" - ); - await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser); - await p; - - // Check if the scalars contain the expected values. - const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); - TelemetryTestUtils.assertKeyedScalar( - scalars, - SCALAR_ABOUT_HOME, - "search_enter", - 1 - ); - Assert.equal( - Object.keys(scalars[SCALAR_ABOUT_HOME]).length, - 1, - "This search must only increment one entry in the scalar." - ); - - await SearchUITestUtils.assertSAPTelemetry({ - engineName: "MozSearch", - source: "abouthome", - count: 1, - }); - - // Also check Glean events. - const record = Glean.newtabSearch.issued.testGetValue(); - Assert.ok(!!record, "Must have recorded a search issuance"); - Assert.equal(record.length, 1, "One search, one event"); - Assert.deepEqual( - { - search_access_point: "about_home", - telemetry_id: "other-MozSearch", - }, - record[0].extra, - "Must have recorded the expected information." - ); - - BrowserTestUtils.removeTab(tab); -}); diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_content.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_content.js @@ -104,75 +104,3 @@ add_task(async function test_context_menu() { BrowserTestUtils.removeTab(gBrowser.selectedTab); BrowserTestUtils.removeTab(tab); }); - -add_task(async function test_about_newtab() { - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - // Let's reset the counts. - Services.telemetry.clearScalars(); - Services.telemetry.clearEvents(); - Services.fog.testResetFOG(); - TelemetryTestUtils.getAndClearKeyedHistogram("SEARCH_COUNTS"); - - let tab = await BrowserTestUtils.openNewForegroundTab( - gBrowser, - "about:newtab", - false - ); - await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { - await ContentTaskUtils.waitForCondition(() => !content.document.hidden); - }); - - info("Trigger a simple serch, just text + enter."); - let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); - await typeInSearchField( - tab.linkedBrowser, - "test query", - "newtab-search-text" - ); - await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser); - await p; - - // Check if the scalars contain the expected values. - const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false); - TelemetryTestUtils.assertKeyedScalar( - scalars, - SCALAR_ABOUT_NEWTAB, - "search_enter", - 1 - ); - Assert.equal( - Object.keys(scalars[SCALAR_ABOUT_NEWTAB]).length, - 1, - "This search must only increment one entry in the scalar." - ); - - // Make sure SAP telemetry has also been incremented. - await SearchUITestUtils.assertSAPTelemetry({ - engineName: "MozSearch", - source: "newtab", - count: 1, - }); - - // Also also check Glean events. - const record = Glean.newtabSearch.issued.testGetValue(); - Assert.ok(!!record, "Must have recorded a search issuance"); - Assert.equal(record.length, 1, "One search, one event"); - Assert.deepEqual( - { - search_access_point: "about_newtab", - telemetry_id: "other-MozSearch", - }, - record[0].extra, - "Must have recorded the expected information." - ); - - BrowserTestUtils.removeTab(tab); - await SpecialPowers.popPrefEnv(); -}); diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources.js @@ -61,10 +61,6 @@ add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [ ["browser.urlbar.suggest.searches", true], - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - true, - ], // Ensure to add search suggestion telemetry as search_suggestion not search_formhistory. ["browser.urlbar.maxHistoricalSearchSuggestions", 0], ], diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources_about.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources_about.js @@ -1,224 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -/* - * Main tests for SearchSERPTelemetry - general engine visiting and link - * clicking on about pages. - */ - -"use strict"; - -const TEST_PROVIDER_INFO = [ - { - telemetryId: "example", - searchPageRegexp: - /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/telemetry\/searchTelemetry(?:Ad)?/, - queryParamNames: ["s"], - codeParamName: "abc", - taggedCodes: ["ff"], - followOnParamNames: ["a"], - extraAdServersRegexps: [/^https:\/\/example\.com\/ad2?/], - components: [ - { - type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK, - default: true, - }, - ], - }, -]; - -/** - * Returns the index of the first search suggestion in the urlbar results. - * - * @returns {number} An index, or -1 if there are no search suggestions. - */ -async function getFirstSuggestionIndex() { - const matchCount = UrlbarTestUtils.getResultCount(window); - for (let i = 0; i < matchCount; i++) { - let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i); - if ( - result.type == UrlbarUtils.RESULT_TYPE.SEARCH && - result.searchParams.suggestion - ) { - return i; - } - } - return -1; -} - -SearchTestUtils.init(this); - -add_setup(async function () { - SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO); - await waitForIdle(); - await SpecialPowers.pushPrefEnv({ - set: [ - ["browser.urlbar.suggest.searches", true], - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - true, - ], - // Ensure to add search suggestion telemetry as search_suggestion not search_formhistory. - ["browser.urlbar.maxHistoricalSearchSuggestions", 0], - ], - }); - // Enable local telemetry recording for the duration of the tests. - let oldCanRecord = Services.telemetry.canRecordExtended; - Services.telemetry.canRecordExtended = true; - - await SearchTestUtils.installSearchExtension( - { - search_url: getPageUrl(true), - search_url_get_params: "s={searchTerms}&abc=ff", - suggest_url: - "https://example.org/browser/browser/components/search/test/browser/searchSuggestionEngine.sjs", - suggest_url_get_params: "query={searchTerms}", - }, - { setAsDefault: true } - ); - - await gCUITestUtils.addSearchBar(); - - registerCleanupFunction(async () => { - gCUITestUtils.removeSearchBar(); - SearchSERPTelemetry.overrideSearchTelemetryForTests(); - Services.telemetry.canRecordExtended = oldCanRecord; - resetTelemetry(); - }); -}); - -async function track_ad_click( - expectedHistogramSource, - expectedScalarSource, - searchAdsFn, - cleanupFn -) { - searchCounts.clear(); - Services.telemetry.clearScalars(); - - let expectedContentScalarKey = "example:tagged:ff"; - let expectedScalarKey = "example:tagged"; - let expectedHistogramSAPSourceKey = `other-Example.${expectedHistogramSource}`; - let expectedContentScalar = `browser.search.content.${expectedScalarSource}`; - let expectedWithAdsScalar = `browser.search.withads.${expectedScalarSource}`; - let expectedAdClicksScalar = `browser.search.adclicks.${expectedScalarSource}`; - - let adImpressionPromise = waitForPageWithAdImpressions(); - let tab = await searchAdsFn(); - - await assertSearchSourcesTelemetry( - { - [expectedHistogramSAPSourceKey]: 1, - }, - { - [expectedContentScalar]: { [expectedContentScalarKey]: 1 }, - [expectedWithAdsScalar]: { [expectedScalarKey]: 1 }, - } - ); - - await adImpressionPromise; - - let pageLoadPromise = BrowserTestUtils.waitForLocationChange(gBrowser); - BrowserTestUtils.synthesizeMouseAtCenter("#ad1", {}, tab.linkedBrowser); - await pageLoadPromise; - await promiseWaitForAdLinkCheck(); - - await assertSearchSourcesTelemetry( - { - [expectedHistogramSAPSourceKey]: 1, - }, - { - [expectedContentScalar]: { [expectedContentScalarKey]: 1 }, - [expectedWithAdsScalar]: { [expectedScalarKey]: 1 }, - [expectedAdClicksScalar]: { [expectedScalarKey]: 1 }, - } - ); - - assertSERPTelemetry([ - { - impression: { - provider: "example", - tagged: "true", - partner_code: "ff", - source: expectedScalarSource, - is_shopping_page: "false", - is_private: "false", - shopping_tab_displayed: "false", - is_signed_in: "false", - }, - engagements: [ - { - action: SearchSERPTelemetryUtils.ACTIONS.CLICKED, - target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK, - }, - ], - adImpressions: [ - { - component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK, - ads_loaded: "2", - ads_visible: "2", - ads_hidden: "0", - }, - ], - }, - ]); - - await cleanupFn(); - - Services.fog.testResetFOG(); -} - -async function checkAboutPage( - page, - expectedHistogramSource, - expectedScalarSource -) { - let tab; - await track_ad_click( - expectedHistogramSource, - expectedScalarSource, - async () => { - tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); - - BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, page); - await BrowserTestUtils.browserStopped(tab.linkedBrowser, page); - - // Wait for the full load. - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - false, - ], - ], - }); - await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { - await ContentTaskUtils.waitForCondition( - () => content.wrappedJSObject.gContentSearchController.defaultEngine - ); - }); - - let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser); - await typeInSearchField( - tab.linkedBrowser, - "test query", - "newtab-search-text" - ); - await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, tab.linkedBrowser); - await p; - return tab; - }, - async () => { - BrowserTestUtils.removeTab(tab); - await SpecialPowers.popPrefEnv(); - } - ); -} - -add_task(async function test_source_about_home() { - await checkAboutPage("about:home", "abouthome", "about_home"); -}); - -add_task(async function test_source_about_newtab() { - await checkAboutPage("about:newtab", "newtab", "about_newtab"); -}); diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources_webextension.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_sources_webextension.js @@ -35,10 +35,6 @@ add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [ ["browser.urlbar.suggest.searches", true], - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - true, - ], // Ensure to add search suggestion telemetry as search_suggestion not search_formhistory. ["browser.urlbar.maxHistoricalSearchSuggestions", 0], ], diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_handoff.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_handoff.js @@ -38,15 +38,6 @@ add_setup(async function () { SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO); await waitForIdle(); - await SpecialPowers.pushPrefEnv({ - set: [ - [ - "browser.newtabpage.activity-stream.improvesearch.handoffToAwesomebar", - true, - ], - ], - }); - await SearchTestUtils.installSearchExtension( { search_url: getPageUrl(true), diff --git a/browser/extensions/newtab/bin/render-activity-stream-html.js b/browser/extensions/newtab/bin/render-activity-stream-html.js @@ -34,7 +34,6 @@ function templateHTML(options) { const debugString = options.debug ? "-dev" : ""; // This list must match any similar ones in AboutNewTabChild.sys.mjs const scripts = [ - "chrome://browser/content/contentSearchUI.js", "chrome://browser/content/contentSearchHandoffUI.js", "chrome://browser/content/contentTheme.js", `${options.baseVendorUrl}vendor/react${debugString}.js`, @@ -87,10 +86,6 @@ function templateHTML(options) { /> <link rel="stylesheet" - href="chrome://browser/content/contentSearchUI.css" - /> - <link - rel="stylesheet" href="chrome://newtab/content/css/activity-stream.css" /> </head> diff --git a/browser/extensions/newtab/data/content/abouthomecache/page.html.template b/browser/extensions/newtab/data/content/abouthomecache/page.html.template @@ -32,7 +32,6 @@ <link rel="localization" href="browser/newtab/newtab.ftl" /> <link rel="localization" href="toolkit/global/mozMessageBar.ftl" /> <link rel="stylesheet" href="chrome://global/skin/design-system/tokens-brand.css"> - <link rel="stylesheet" href="chrome://browser/content/contentSearchUI.css" /> <link rel="stylesheet" href="chrome://newtab/content/css/activity-stream.css" /> </head> <!-- Cached: {{ CACHE_TIME }} --> diff --git a/browser/extensions/newtab/prerendered/activity-stream-debug.html b/browser/extensions/newtab/prerendered/activity-stream-debug.html @@ -30,16 +30,11 @@ /> <link rel="stylesheet" - href="chrome://browser/content/contentSearchUI.css" - /> - <link - rel="stylesheet" href="chrome://newtab/content/css/activity-stream.css" /> </head> <body class="activity-stream"> <div id="root"></div> - <script src="chrome://browser/content/contentSearchUI.js"></script> <script src="chrome://browser/content/contentSearchHandoffUI.js"></script> <script src="chrome://browser/content/contentTheme.js"></script> <script src="chrome://global/content/vendor/react-dev.js"></script> diff --git a/browser/extensions/newtab/prerendered/activity-stream-noscripts.html b/browser/extensions/newtab/prerendered/activity-stream-noscripts.html @@ -30,10 +30,6 @@ /> <link rel="stylesheet" - href="chrome://browser/content/contentSearchUI.css" - /> - <link - rel="stylesheet" href="chrome://newtab/content/css/activity-stream.css" /> </head> diff --git a/browser/extensions/newtab/prerendered/activity-stream.html b/browser/extensions/newtab/prerendered/activity-stream.html @@ -30,16 +30,11 @@ /> <link rel="stylesheet" - href="chrome://browser/content/contentSearchUI.css" - /> - <link - rel="stylesheet" href="chrome://newtab/content/css/activity-stream.css" /> </head> <body class="activity-stream"> <div id="root"></div> - <script src="chrome://browser/content/contentSearchUI.js"></script> <script src="chrome://browser/content/contentSearchHandoffUI.js"></script> <script src="chrome://browser/content/contentTheme.js"></script> <script src="chrome://global/content/vendor/react.js"></script> diff --git a/stylelint-rollouts.config.js b/stylelint-rollouts.config.js @@ -68,7 +68,6 @@ module.exports = [ "browser/components/protections/content/protections.css", "browser/components/screenshots/overlay/overlay.css", "browser/components/screenshots/screenshots-buttons.css", - "browser/components/search/content/contentSearchUI.css", "browser/components/search/test/browser/telemetry/serp.css", "browser/components/sidebar/sidebar-main.css", "browser/components/sidebar/sidebar-pins-promo.css", @@ -442,7 +441,6 @@ module.exports = [ "browser/components/screenshots/screenshots-buttons.css", "browser/components/screenshots/screenshots-preview.css", "browser/components/search/content/addEngine.css", - "browser/components/search/content/contentSearchUI.css", "browser/components/search/test/browser/telemetry/serp.css", "browser/components/security/unexpectedScriptLoad.css", "browser/components/sidebar/sidebar-customize.css",