commit 2c71cdfa065c4b0fe9d0c598f3896459e569e4e2 parent 477a9f6298d4ae7f87ff59007f3cb89d7a82e857 Author: Cristian Tuns <ctuns@mozilla.com> Date: Wed, 7 Jan 2026 15:22:26 -0500 Revert "Bug 2002027 - Part 3: Embed ContentSearchHandoffUI component into about:privatebrowsing. r=desktop-theme-reviewers,Standard8,urlbar-reviewers,emilio" for causing bc failures in browser_urlbar_telemetry_searchmode.js DONTBUILD This reverts commit a0c4551d89f168be09eeed5e60367af1ac13174a. Revert "Bug 2002027 - Part 2: Add ContentSearchHandoffUI component and styling, and embed in about:newtab. r=search-reviewers,home-newtab-reviewers,urlbar-reviewers,Standard8,places-reviewers,frontend-codestyle-reviewers,nbarrett" This reverts commit 8b6a7f8464a618aab8a17b2e1aada5e9ca1cf7eb. Revert "Bug 2002027 - Part 1: Make contentSearchHandoffUI a module. r=search-reviewers,home-newtab-reviewers,Standard8,nbarrett" This reverts commit 705736c7f354698ca4aa80f484b41ce4624b9a83. Diffstat:
37 files changed, 539 insertions(+), 1202 deletions(-)
diff --git a/.stylelintrc.js b/.stylelintrc.js @@ -432,11 +432,6 @@ module.exports = { // into the HTML backup archive files that exist on a user's file system // and can be opened in any browser. "browser/components/backup/content/archive.css", - // Bug 2003877 - this is a centralization of a bunch of rules that had - // been spread across about:newtab and about:privatebrowsing. We'll - // fix these design tokens issues in a follow-up (presuming the - // replacement of the handoff bar doesn't land and remove this first). - "browser/components/search/content/contentSearchHandoffUI.css", ], rules: { "stylelint-plugin-mozilla/use-design-tokens": null, diff --git a/browser/actors/AboutNewTabChild.sys.mjs b/browser/actors/AboutNewTabChild.sys.mjs @@ -44,6 +44,7 @@ export class AboutNewTabChild extends RemotePageChild { // This list must match any similar ones in render-activity-stream-html.js. const scripts = [ + "chrome://browser/content/contentSearchHandoffUI.js", "chrome://browser/content/contentTheme.js", `chrome://global/content/vendor/react${debugString}.js`, `chrome://global/content/vendor/react-dom${debugString}.js`, diff --git a/browser/actors/ContentSearchParent.sys.mjs b/browser/actors/ContentSearchParent.sys.mjs @@ -5,7 +5,6 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", BrowserSearchTelemetry: "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs", BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", @@ -494,88 +493,6 @@ export let ContentSearch = { } }, - _onMessageSearchHandoff({ browser, data, actor }) { - let win = browser.ownerGlobal; - let text = data.text; - let urlBar = win.gURLBar; - let inPrivateBrowsing = lazy.PrivateBrowsingUtils.isBrowserPrivate(browser); - let searchEngine = inPrivateBrowsing - ? Services.search.defaultPrivateEngine - : Services.search.defaultEngine; - let isFirstChange = true; - - // It's possible that this is a handoff from about:home / about:newtab, - // in which case we want to include the newtab_session_id in our call to - // urlBar.handoff. We have to jump through some unfortunate hoops to get - // that. - let newtabSessionId = null; - let newtabActor = - browser.browsingContext?.currentWindowGlobal?.getExistingActor( - "AboutNewTab" - ); - if (newtabActor) { - const portID = newtabActor.getTabDetails()?.portID; - if (portID) { - newtabSessionId = lazy.AboutNewTab.activityStream.store.feeds - .get("feeds.telemetry") - ?.sessions.get(portID)?.session_id; - } - } - - if (!text) { - urlBar.setHiddenFocus(); - } else { - // Pass the provided text to the awesomebar - urlBar.handoff(text, searchEngine, newtabSessionId); - isFirstChange = false; - } - - let checkFirstChange = () => { - // Check if this is the first change since we hidden focused. If it is, - // remove hidden focus styles, prepend the search alias and hide the - // in-content search. - if (isFirstChange) { - isFirstChange = false; - urlBar.removeHiddenFocus(true); - urlBar.handoff("", searchEngine, newtabSessionId); - actor.sendAsyncMessage("DisableSearch"); - urlBar.removeEventListener("compositionstart", checkFirstChange); - urlBar.removeEventListener("paste", checkFirstChange); - } - }; - - let onKeydown = ev => { - // Check if the keydown will cause a value change. - if (ev.key.length === 1 && !ev.altKey && !ev.ctrlKey && !ev.metaKey) { - checkFirstChange(); - } - // If the Esc button is pressed, we are done. Show in-content search and cleanup. - if (ev.key === "Escape") { - onDone(); - } - }; - - let onDone = ev => { - // We are done. Show in-content search again and cleanup. - const forceSuppressFocusBorder = ev?.type === "mousedown"; - urlBar.removeHiddenFocus(forceSuppressFocusBorder); - - urlBar.removeEventListener("keydown", onKeydown); - urlBar.removeEventListener("mousedown", onDone); - urlBar.removeEventListener("blur", onDone); - urlBar.removeEventListener("compositionstart", checkFirstChange); - urlBar.removeEventListener("paste", checkFirstChange); - - actor.sendAsyncMessage("ShowSearch"); - }; - - urlBar.addEventListener("keydown", onKeydown); - urlBar.addEventListener("mousedown", onDone); - urlBar.addEventListener("blur", onDone); - urlBar.addEventListener("compositionstart", checkFirstChange); - urlBar.addEventListener("paste", checkFirstChange); - }, - async _onObserve(eventItem) { let engine; switch (eventItem.data) { diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js @@ -1863,17 +1863,6 @@ pref("browser.newtabpage.activity-stream.mobileDownloadModal.variant-c", false); // Show refined card layout on newtab pref("browser.newtabpage.activity-stream.discoverystream.refinedCardsLayout.enabled", true); -/** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ -pref("browser.newtabpage.activity-stream.search.useHandoffComponent", true); - // Mozilla Ad Routing Service (MARS) unified ads service pref("browser.newtabpage.activity-stream.unifiedAds.tiles.enabled", true); pref("browser.newtabpage.activity-stream.unifiedAds.spocs.enabled", true); diff --git a/browser/base/content/test/static/browser_parsable_css.js b/browser/base/content/test/static/browser_parsable_css.js @@ -145,10 +145,6 @@ let propNameAllowlist = [ { propName: "--panel-shadow", isFromDevTools: true }, { propName: "--panel-shadow-margin", isFromDevTools: true }, - // These variables are set in host CSS but consumed in shadow DOM CSS - // (content-search-handoff-ui component), which confuses the test. - { propName: /^--content-search-handoff-ui-/, isFromDevTools: false }, - // These variables are used in JS in viewer.mjs (PDF.js). { propName: "--scale-round-x", diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.html b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.html @@ -28,10 +28,7 @@ <link rel="localization" href="toolkit/branding/brandings.ftl" /> <link rel="localization" href="browser/aboutPrivateBrowsing.ftl" /> <script src="chrome://browser/content/aboutPrivateBrowsing.js"></script> - <script - type="module" - src="chrome://browser/content/contentSearchHandoffUI.mjs" - ></script> + <script src="chrome://browser/content/contentSearchHandoffUI.js"></script> </head> <body> @@ -76,7 +73,21 @@ <div class="wordmark"></div> </div> <div class="search-inner-wrapper"> - <content-search-handoff-ui></content-search-handoff-ui> + <button + id="search-handoff-button" + class="search-handoff-button" + tabindex="-1" + aria-hidden="true" + > + <div class="fake-textbox"></div> + <input + id="fake-editable" + class="fake-editable" + tabindex="-1" + aria-hidden="true" + /> + <div class="fake-caret"></div> + </button> </div> <div class="info-border"> <div class="info"> diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js @@ -351,4 +351,55 @@ document.addEventListener("DOMContentLoaded", function () { }; openSearchOptions.addEventListener("click", openSearchOptionsEvtHandler); openSearchOptions.addEventListener("keypress", openSearchOptionsEvtHandler); + + // Setup the search hand-off box. + let btn = document.getElementById("search-handoff-button"); + + let editable = document.getElementById("fake-editable"); + let DISABLE_SEARCH_TOPIC = "DisableSearch"; + let SHOW_SEARCH_TOPIC = "ShowSearch"; + let SEARCH_HANDOFF_TOPIC = "SearchHandoff"; + + function showSearch() { + btn.classList.remove("focused"); + btn.classList.remove("disabled"); + RPMRemoveMessageListener(SHOW_SEARCH_TOPIC, showSearch); + } + + function disableSearch() { + btn.classList.add("disabled"); + } + + function handoffSearch(text) { + RPMSendAsyncMessage(SEARCH_HANDOFF_TOPIC, { text }); + RPMAddMessageListener(SHOW_SEARCH_TOPIC, showSearch); + if (text) { + disableSearch(); + } else { + btn.classList.add("focused"); + RPMAddMessageListener(DISABLE_SEARCH_TOPIC, disableSearch); + } + } + btn.addEventListener("focus", function () { + handoffSearch(); + }); + btn.addEventListener("click", function () { + handoffSearch(); + }); + + // Hand-off any text that gets dropped or pasted + editable.addEventListener("drop", function (ev) { + ev.preventDefault(); + let text = ev.dataTransfer.getData("text"); + if (text) { + handoffSearch(text); + } + }); + editable.addEventListener("paste", function (ev) { + ev.preventDefault(); + handoffSearch(ev.clipboardData.getData("Text")); + }); + + // Load contentSearchUI so it sets the search engine icon and name for us. + new window.ContentSearchHandoffUIController(); }); diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js @@ -86,10 +86,8 @@ add_task(async function test_myths_link() { await BrowserTestUtils.closeWindow(win); }); -async function urlBarHasHiddenFocus(win) { - return TestUtils.waitForCondition(() => { - return win.gURLBar.focused && !win.gURLBar.hasAttribute("focused"); - }, "Urlbar has hidden focus"); +function urlBarHasHiddenFocus(win) { + return win.gURLBar.focused && !win.gURLBar.hasAttribute("focused"); } function urlBarHasNormalFocus(win) { @@ -140,16 +138,11 @@ add_task(async function test_search_handoff_on_keydown() { let { win, tab } = await openAboutPrivateBrowsing(); await SpecialPowers.spawn(tab, [], async function () { - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - let btn = handoffUI.shadowRoot.querySelector(".search-handoff-button"); + let btn = content.document.getElementById("search-handoff-button"); btn.click(); - await handoffUI.updateComplete; - ok( - handoffUI.hasAttribute("fakefocus"), - "in-content search has focus styles" - ); + ok(btn.classList.contains("focused"), "in-content search has focus styles"); }); - await urlBarHasHiddenFocus(win); + ok(urlBarHasHiddenFocus(win), "Urlbar has hidden focus"); // Expect two searches, one to enter search mode and then another in search // mode. @@ -159,8 +152,8 @@ add_task(async function test_search_handoff_on_keydown() { await SpecialPowers.spawn(tab, [], async function () { ok( content.document - .querySelector("content-search-handoff-ui") - .hasAttribute("disabled"), + .getElementById("search-handoff-button") + .classList.contains("disabled"), "in-content search is disabled" ); }); @@ -176,8 +169,8 @@ add_task(async function test_search_handoff_on_keydown() { await SpecialPowers.spawn(tab, [], async function () { ok( !content.document - .querySelector("content-search-handoff-ui") - .hasAttribute("disabled"), + .getElementById("search-handoff-button") + .classList.contains("disabled"), "in-content search is not disabled" ); }); @@ -192,12 +185,9 @@ add_task(async function test_search_handoff_on_composition_start() { let { win, tab } = await openAboutPrivateBrowsing(); await SpecialPowers.spawn(tab, [], async function () { - let btn = content.document - .querySelector("content-search-handoff-ui") - .shadowRoot.querySelector(".search-handoff-button"); - btn.click(); + content.document.getElementById("search-handoff-button").click(); }); - await urlBarHasHiddenFocus(win); + ok(urlBarHasHiddenFocus(win), "Urlbar has hidden focus"); await new Promise(r => EventUtils.synthesizeComposition({ type: "compositionstart" }, win, r) ); @@ -213,12 +203,9 @@ add_task(async function test_search_handoff_on_paste() { let { win, tab } = await openAboutPrivateBrowsing(); await SpecialPowers.spawn(tab, [], async function () { - content.document - .querySelector("content-search-handoff-ui") - .shadowRoot.querySelector(".search-handoff-button") - .click(); + content.document.getElementById("search-handoff-button").click(); }); - await urlBarHasHiddenFocus(win); + ok(urlBarHasHiddenFocus(win), "Urlbar has hidden focus"); var helper = SpecialPowers.Cc[ "@mozilla.org/widget/clipboardhelper;1" ].getService(SpecialPowers.Ci.nsIClipboardHelper); @@ -251,16 +238,11 @@ add_task(async function test_search_handoff_search_mode() { let { win, tab } = await openAboutPrivateBrowsing(); await SpecialPowers.spawn(tab, [], async function () { - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - let btn = handoffUI.shadowRoot.querySelector(".search-handoff-button"); + let btn = content.document.getElementById("search-handoff-button"); btn.click(); - await handoffUI.updateComplete; - ok( - handoffUI.hasAttribute("fakefocus"), - "in-content search has focus styles" - ); + ok(btn.classList.contains("focused"), "in-content search has focus styles"); }); - await urlBarHasHiddenFocus(win); + ok(urlBarHasHiddenFocus(win), "Urlbar has hidden focus"); // Expect two searches, one to enter search mode and then another in search // mode. @@ -268,11 +250,12 @@ add_task(async function test_search_handoff_search_mode() { await new Promise(r => EventUtils.synthesizeKey("f", {}, win, r)); await SpecialPowers.spawn(tab, [], async function () { - await ContentTaskUtils.waitForCondition(() => { - return content.document - .querySelector("content-search-handoff-ui") - .hasAttribute("disabled"); - }, "in-content search is disabled"); + ok( + content.document + .getElementById("search-handoff-button") + .classList.contains("disabled"), + "in-content search is disabled" + ); }); await searchPromise; ok(urlBarHasNormalFocus(win), "Urlbar has normal focus"); @@ -292,9 +275,8 @@ add_task(async function test_search_handoff_search_mode() { await SpecialPowers.spawn(tab, [], async function () { ok( !content.document - .querySelector("content-search-handoff-ui") - .shadowRoot.querySelector(".search-handoff-button") - .hasAttribute("disabled"), + .getElementById("search-handoff-button") + .classList.contains("disabled"), "in-content search is not disabled" ); }); diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_cookie_banners_promo.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_cookie_banners_promo.js @@ -26,6 +26,7 @@ add_task(async function test_cookie_banners_promo_user_set_prefs() { await ASRouter.onPrefChange(); const { win, tab } = await openTabAndWaitForRender(); + await SpecialPowers.spawn(tab, [promoImgSrc], async function (imgSrc) { const promoImage = content.document.querySelector( ".promo-image-large > img" diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_default_pin_promo.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_default_pin_promo.js @@ -9,7 +9,7 @@ const { sinon } = ChromeUtils.importESModule( const sandbox = sinon.createSandbox(); add_setup(async function () { - await ASRouter.resetMessageState(); + ASRouter.resetMessageState(); await SpecialPowers.pushPrefEnv({ set: [["browser.promo.pin.enabled", true]], }); diff --git a/browser/components/search/content/content-search-handoff-ui.stories.mjs b/browser/components/search/content/content-search-handoff-ui.stories.mjs @@ -1,89 +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/. */ - -// eslint-disable-next-line import/no-unresolved -import { html } from "lit.all.mjs"; -import "./contentSearchHandoffUI.mjs"; - -window.MozXULElement.insertFTLIfNeeded("branding/brand.ftl"); -window.MozXULElement.insertFTLIfNeeded("browser/newtab/newtab.ftl"); -window.MozXULElement.insertFTLIfNeeded("browser/aboutPrivateBrowsing.ftl"); - -export default { - title: "Domain-specific UI Widgets/Search/Handoff Search Bar", - component: "content-search-handoff-ui", - argTypes: {}, -}; - -/** - * This little dance lets us mock out the response that the ContentSearch - * parent/child actor pair returns when the ContentSearchHandoffUIController - * requests engine information. - */ -addEventListener("ContentSearchClient", e => { - switch (e.detail.type) { - case "GetEngine": { - // We use the setTimeout(0) to queue up the response to occur on the next - // tick of the event loop. - setTimeout(() => { - e.target.dispatchEvent( - new CustomEvent("ContentSearchService", { - detail: { - type: "Engine", - data: { - engine: { - name: "Google", - iconData: "chrome://global/skin/icons/search-glass.svg", - isConfigEngine: true, - }, - isPrivateEngine: false, - }, - }, - }) - ); - }, 0); - break; - } - } -}); - -const Template = ({ fakeFocus, disabled }) => html` - <style> - .search-inner-wrapper { - display: flex; - min-height: 52px; - margin: 0 auto; - width: 720px; - } - content-search-handoff-ui { - --content-search-handoff-ui-fill: light-dark(#000000, #ffffff); - height: 50px; - width: 100%; - } - </style> - - <div class="search-inner-wrapper"> - <content-search-handoff-ui - ?fakeFocus=${fakeFocus} - ?disabled=${disabled} - ></content-search-handoff-ui> - </div> -`; - -export const Focused = Template.bind({}); -Focused.args = { - fakeFocus: true, - disabled: false, -}; - -export const Unfocused = Template.bind({}); -Unfocused.args = { - fakeFocus: false, - disabled: false, -}; -export const Disabled = Template.bind({}); -Disabled.args = { - fakeFocus: true, - disabled: true, -}; diff --git a/browser/components/search/content/contentSearchHandoffUI.css b/browser/components/search/content/contentSearchHandoffUI.css @@ -1,104 +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/. */ - -:host { - display: flex; - width: 100%; - --content-search-handoff-ui-background-color: Field; - --content-search-handoff-ui-color: FieldText; - --content-search-handoff-ui-fill: FieldText; - --content-search-handoff-ui-caret-color: FieldText; - --content-search-handoff-ui-border-width: 1px; - --content-search-handoff-ui-unfocused-border-color: transparent; - --content-search-handoff-ui-fakefocus-border-color: SelectedItem; - --content-search-handoff-ui-fakefocus-box-shadow-inner: color-mix(in srgb, SelectedItem 25%, transparent); - --content-search-handoff-ui-fakefocus-box-shadow-outer: color-mix(in srgb, SelectedItem 25%, transparent); -} - -.fake-editable { - height: 100%; - opacity: 0; - position: absolute; - inset: 0; -} - -.fake-editable:focus { - outline: none; - caret-color: transparent; -} - -.fake-textbox { - opacity: 0.54; - text-align: start; - -webkit-line-clamp: 1; - overflow: hidden; - margin-inline-start: var(--space-xsmall); - - /** - * It's not clear to me why I need to do this, but for some reason I don't - * inherit the font-size through the shadow DOM... - */ - font-size: var(--font-size-root); -} - -.search-handoff-button { - position: relative; - background: var(--content-search-handoff-ui-background-color) var(--newtab-search-icon) 16px center no-repeat; - background-size: var(--size-item-medium); - padding-inline-start: calc(2 * var(--space-xlarge)); - padding-inline-end: var(--space-small); - padding-block: 0; - width: 100%; - box-shadow: var(--box-shadow-level-3); - border: var(--content-search-handoff-ui-border-width) solid var(--content-search-handoff-ui-unfocused-border-color); - border-radius: var(--border-radius-medium); - color: var(--content-search-handoff-ui-color); - -moz-context-properties: fill; - fill: var(--content-search-handoff-ui-fill); - - &:dir(rtl) { - background-position-x: right 16px; - } -} - -@keyframes caret-animation { - to { - visibility: hidden; - } -} - -.fake-caret { - /* To replicate the default caret blink rate of 567ms (https://searchfox.org/mozilla-central/source/widget/cocoa/nsLookAndFeel.mm#397): - - Multiply the blink time by 2 to cover both the visible and hidden states. - - Use steps(2, start) to divide the animation into 2 phases: - 1. First 567ms (Step 1) → Caret is visible - 2. Next 567ms (Step 2) → Caret is hidden - This gives a sharp ON/OFF effect instead of a smooth transition. */ - animation: caret-animation var(--caret-blink-time, 1134ms) steps(2, start) var(--caret-blink-count, infinite); - background: var(--content-search-handoff-ui-caret-color); - display: none; - inset-inline-start: calc(2 * var(--space-xlarge)); - width: 1px; - /** - * We use the negative margin trick here to overlap the same area as the - * fake-textbox. - */ - height: 17px; - margin-top: -17px; -} - -:host([fakefocus]:not([disabled])) .search-handoff-button { - border: var(--content-search-handoff-ui-border-width) solid var(--content-search-handoff-ui-fakefocus-border-color); - box-shadow: - 0 0 0 2px var(--content-search-handoff-ui-fakefocus-box-shadow-inner), - 0 0 0 4px var(--content-search-handoff-ui-fakefocus-box-shadow-outer); - - .fake-caret { - display: block; - } -} - -:host([disabled]) .search-handoff-button { - opacity: 0.5; -} diff --git a/browser/components/search/content/contentSearchHandoffUI.js b/browser/components/search/content/contentSearchHandoffUI.js @@ -0,0 +1,171 @@ +/* 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"; + +/** + * Handles handing off searches from an in-page search input field to the + * browser's main URL bar. Communicates with the parent via the ContentSearch + * actor, using custom events to talk to the child actor. + */ +class ContentSearchHandoffUIController { + constructor() { + this._isPrivateEngine = false; + this._engineIcon = null; + + window.addEventListener("ContentSearchService", this); + this._sendMsg("GetEngine"); + this._sendMsg("GetHandoffSearchModePrefs"); + } + + handleEvent(event) { + let methodName = "_onMsg" + event.detail.type; + if (methodName in this) { + this[methodName](event.detail.data); + } + } + + get defaultEngine() { + return this._defaultEngine; + } + + static privateBrowsingRegex = /^about:privatebrowsing([#?]|$)/i; + get _isAboutPrivateBrowsing() { + return ContentSearchHandoffUIController.privateBrowsingRegex.test( + document.location.href + ); + } + + _onMsgEngine({ isPrivateEngine, engine }) { + this._isPrivateEngine = isPrivateEngine; + this._updateEngine(engine); + } + + _onMsgCurrentEngine(engine) { + if (!this._isPrivateEngine) { + this._updateEngine(engine); + } + } + + _onMsgCurrentPrivateEngine(engine) { + if (this._isPrivateEngine) { + this._updateEngine(engine); + } + } + + _onMsgHandoffSearchModePrefs(pref) { + this._shouldHandOffToSearchMode = pref; + this._updatel10nIds(); + } + + _updateEngine(engine) { + this._defaultEngine = engine; + if (this._engineIcon) { + URL.revokeObjectURL(this._engineIcon); + } + + // We only show the engines icon for config engines, otherwise show + // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19 + if (!engine.isConfigEngine) { + this._engineIcon = "chrome://global/skin/icons/search-glass.svg"; + } else if (engine.iconData) { + this._engineIcon = this._getFaviconURIFromIconData(engine.iconData); + } else { + this._engineIcon = "chrome://global/skin/icons/defaultFavicon.svg"; + } + + document.body.style.setProperty( + "--newtab-search-icon", + "url(" + this._engineIcon + ")" + ); + this._updatel10nIds(); + } + + _updatel10nIds() { + let engine = this._defaultEngine; + let fakeButton = document.querySelector(".search-handoff-button"); + let fakeInput = document.querySelector(".fake-textbox"); + if (!fakeButton || !fakeInput) { + return; + } + if (!engine || this._shouldHandOffToSearchMode) { + document.l10n.setAttributes( + fakeButton, + this._isAboutPrivateBrowsing + ? "about-private-browsing-search-btn" + : "newtab-search-box-input" + ); + document.l10n.setAttributes( + fakeInput, + this._isAboutPrivateBrowsing + ? "about-private-browsing-search-placeholder" + : "newtab-search-box-text" + ); + } else if (!engine.isConfigEngine) { + document.l10n.setAttributes( + fakeButton, + this._isAboutPrivateBrowsing + ? "about-private-browsing-handoff-no-engine" + : "newtab-search-box-handoff-input-no-engine" + ); + document.l10n.setAttributes( + fakeInput, + this._isAboutPrivateBrowsing + ? "about-private-browsing-handoff-text-no-engine" + : "newtab-search-box-handoff-text-no-engine" + ); + } else { + document.l10n.setAttributes( + fakeButton, + this._isAboutPrivateBrowsing + ? "about-private-browsing-handoff" + : "newtab-search-box-handoff-input", + { + engine: engine.name, + } + ); + document.l10n.setAttributes( + fakeInput, + this._isAboutPrivateBrowsing + ? "about-private-browsing-handoff-text" + : "newtab-search-box-handoff-text", + { + engine: engine.name, + } + ); + } + } + + /** + * 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); + } + + _sendMsg(type, data = null) { + dispatchEvent( + new CustomEvent("ContentSearchClient", { + detail: { + type, + data, + }, + }) + ); + } +} + +window.ContentSearchHandoffUIController = ContentSearchHandoffUIController; diff --git a/browser/components/search/content/contentSearchHandoffUI.mjs b/browser/components/search/content/contentSearchHandoffUI.mjs @@ -1,269 +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/. */ - -import { html } from "chrome://global/content/vendor/lit.all.mjs"; -import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; - -/** - * Handles handing off searches from an in-page search input field to the - * browser's main URL bar. Communicates with the parent via the ContentSearch - * actor, using custom events to talk to the child actor. - */ -class ContentSearchHandoffUIController { - #ui = null; - #shadowRoot = null; - - constructor(ui) { - this._isPrivateEngine = false; - this._engineIcon = null; - this.#ui = ui; - this.#shadowRoot = ui.shadowRoot; - - window.addEventListener("ContentSearchService", this); - this._sendMsg("GetEngine"); - this._sendMsg("GetHandoffSearchModePrefs"); - } - - handleEvent(event) { - let methodName = "_onMsg" + event.detail.type; - if (methodName in this) { - this[methodName](event.detail.data); - } - } - - get defaultEngine() { - return this._defaultEngine; - } - - doSearchHandoff(text) { - this._sendMsg("SearchHandoff", { text }); - } - - static privateBrowsingRegex = /^about:privatebrowsing([#?]|$)/i; - get _isAboutPrivateBrowsing() { - return ContentSearchHandoffUIController.privateBrowsingRegex.test( - document.location.href - ); - } - - _onMsgEngine({ isPrivateEngine, engine }) { - this._isPrivateEngine = isPrivateEngine; - this._updateEngine(engine); - } - - _onMsgCurrentEngine(engine) { - if (!this._isPrivateEngine) { - this._updateEngine(engine); - } - } - - _onMsgCurrentPrivateEngine(engine) { - if (this._isPrivateEngine) { - this._updateEngine(engine); - } - } - - _onMsgHandoffSearchModePrefs(pref) { - this._shouldHandOffToSearchMode = pref; - this._updatel10nIds(); - } - - _onMsgDisableSearch() { - this.#ui.disabled = true; - } - - _onMsgShowSearch() { - this.#ui.disabled = false; - this.#ui.fakeFocus = false; - } - - _updateEngine(engine) { - this._defaultEngine = engine; - if (this._engineIcon) { - URL.revokeObjectURL(this._engineIcon); - } - - // We only show the engines icon for config engines, otherwise show - // a default. xref https://bugzilla.mozilla.org/show_bug.cgi?id=1449338#c19 - if (!engine.isConfigEngine) { - this._engineIcon = "chrome://global/skin/icons/search-glass.svg"; - } else if (engine.iconData) { - this._engineIcon = this._getFaviconURIFromIconData(engine.iconData); - } else { - this._engineIcon = "chrome://global/skin/icons/defaultFavicon.svg"; - } - - document.body.style.setProperty( - "--newtab-search-icon", - "url(" + this._engineIcon + ")" - ); - this._updatel10nIds(); - } - - _updatel10nIds() { - let engine = this._defaultEngine; - let fakeButton = this.#shadowRoot.querySelector(".search-handoff-button"); - let fakeInput = this.#shadowRoot.querySelector(".fake-textbox"); - if (!fakeButton || !fakeInput) { - return; - } - if (!engine || this._shouldHandOffToSearchMode) { - document.l10n.setAttributes( - fakeButton, - this._isAboutPrivateBrowsing - ? "about-private-browsing-search-btn" - : "newtab-search-box-input" - ); - document.l10n.setAttributes( - fakeInput, - this._isAboutPrivateBrowsing - ? "about-private-browsing-search-placeholder" - : "newtab-search-box-text" - ); - } else if (!engine.isConfigEngine) { - document.l10n.setAttributes( - fakeButton, - this._isAboutPrivateBrowsing - ? "about-private-browsing-handoff-no-engine" - : "newtab-search-box-handoff-input-no-engine" - ); - document.l10n.setAttributes( - fakeInput, - this._isAboutPrivateBrowsing - ? "about-private-browsing-handoff-text-no-engine" - : "newtab-search-box-handoff-text-no-engine" - ); - } else { - document.l10n.setAttributes( - fakeButton, - this._isAboutPrivateBrowsing - ? "about-private-browsing-handoff" - : "newtab-search-box-handoff-input", - { - engine: engine.name, - } - ); - document.l10n.setAttributes( - fakeInput, - this._isAboutPrivateBrowsing - ? "about-private-browsing-handoff-text" - : "newtab-search-box-handoff-text", - { - engine: engine.name, - } - ); - } - } - - /** - * 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); - } - - _sendMsg(type, data = null) { - dispatchEvent( - new CustomEvent("ContentSearchClient", { - detail: { - type, - data, - }, - }) - ); - } -} - -window.ContentSearchHandoffUIController = ContentSearchHandoffUIController; - -/** - * This custom element encapsulates the UI for the search handoff experience - * for about:newtab and about:privatebrowsing. It is a temporary component - * while we wait for the multi-context address bar (MCAB) to be available. - */ -class ContentSearchHandoffUI extends MozLitElement { - static queries = { - fakeCaret: ".fake-caret", - }; - - static properties = { - fakeFocus: { type: Boolean, reflect: true }, - disabled: { type: Boolean, reflect: true }, - }; - - #controller = null; - - #doSearchHandoff(text = "") { - this.fakeFocus = true; - this.#controller.doSearchHandoff(text); - } - - #onSearchHandoffClick(event) { - // When search hand-off is enabled, we render a big button that is styled to - // look like a search textbox. If the button is clicked, we style - // the button as if it was a focused search box and show a fake cursor but - // really focus the awesomebar without the focus styles ("hidden focus"). - event.preventDefault(); - this.#doSearchHandoff(); - } - - #onSearchHandoffPaste(event) { - event.preventDefault(); - this.#doSearchHandoff(event.clipboardData.getData("Text")); - } - - #onSearchHandoffDrop(event) { - event.preventDefault(); - let text = event.dataTransfer.getData("text"); - if (text) { - this.#doSearchHandoff(text); - } - } - - connectedCallback() { - super.connectedCallback(); - if (!this.#controller) { - this.#controller = new window.ContentSearchHandoffUIController(this); - } - } - - render() { - return html` - <link - rel="stylesheet" - href="chrome://browser/content/contentSearchHandoffUI.css" - /> - <button - class="search-handoff-button" - @click=${this.#onSearchHandoffClick} - tabindex="-1" - > - <div class="fake-textbox"></div> - <input - type="search" - class="fake-editable" - tabindex="-1" - aria-hidden="true" - @drop=${this.#onSearchHandoffDrop} - @paste=${this.#onSearchHandoffPaste} - /> - <div class="fake-caret"></div> - </button> - `; - } -} - -customElements.define("content-search-handoff-ui", ContentSearchHandoffUI); diff --git a/browser/components/search/jar.mn b/browser/components/search/jar.mn @@ -8,8 +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/contentSearchHandoffUI.mjs (content/contentSearchHandoffUI.mjs) - content/browser/contentSearchHandoffUI.css (content/contentSearchHandoffUI.css) + content/browser/contentSearchHandoffUI.js (content/contentSearchHandoffUI.js) search-extensions/ (extensions/**) % resource search-extensions %search-extensions/ contentaccessible=yes diff --git a/browser/components/search/test/browser/browser_contentSearchUI_default.js b/browser/components/search/test/browser/browser_contentSearchUI_default.js @@ -98,16 +98,10 @@ async function ensurePlaceholder(tab, expectedId, expectedEngine) { await ContentTaskUtils.waitForCondition(() => !content.document.hidden); await ContentTaskUtils.waitForCondition( - () => content.document.querySelector("content-search-handoff-ui"), - "content-search-handoff-ui not found." - ); - let handoffUI = content.document.querySelector( - "content-search-handoff-ui" - ); - await handoffUI.updateComplete; - let buttonNode = handoffUI.shadowRoot.querySelector( - ".search-handoff-button" + () => content.document.querySelector(".search-handoff-button"), + "l10n ID not set." ); + let buttonNode = content.document.querySelector(".search-handoff-button"); let expectedAttributes = { id, args: engine ? { engine } : null }; Assert.deepEqual( content.document.l10n.getAttributes(buttonNode), 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 @@ -283,14 +283,11 @@ add_task(async function test_source_urlbar_handoff() { await BrowserTestUtils.browserStopped(tab.linkedBrowser, "about:newtab"); info("Focus on search input in newtab content"); - await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { - let handoffUI = content.document.querySelector( - "content-search-handoff-ui" - ); - await handoffUI.updateComplete; - let fakeEditable = handoffUI.shadowRoot.querySelector(".fake-editable"); - fakeEditable.click(); - }); + await BrowserTestUtils.synthesizeMouseAtCenter( + ".fake-editable", + {}, + tab.linkedBrowser + ); info("Get suggestions"); for (const c of "searchSuggestion".split("")) { diff --git a/browser/components/storybook/.storybook/main.js b/browser/components/storybook/.storybook/main.js @@ -31,8 +31,6 @@ module.exports = { `${projectRoot}/browser/components/backup/content/**/*.stories.mjs`, // Settings components stories `${projectRoot}/browser/components/preferences/widgets/**/*.stories.mjs`, - // Search components stories - `${projectRoot}/browser/components/search/**/*.stories.mjs`, // Reader View components stories `${projectRoot}/toolkit/components/reader/**/*.stories.mjs`, // megalist components stories diff --git a/browser/components/storybook/component-status/components.json b/browser/components/storybook/component-status/components.json @@ -1,267 +0,0 @@ -{ - "generatedAt": "2025-12-18T15:49:39.588Z", - "count": 29, - "items": [ - { - "component": "moz-badge", - "title": "UI Widgets/Badge", - "status": "in-development", - "storyId": "ui-widgets-badge--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-badge--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-badge", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1987750" - }, - { - "component": "moz-box-button", - "title": "UI Widgets/Box Button", - "status": "in-development", - "storyId": "ui-widgets-box-button--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-box-button--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-box-button", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1855803" - }, - { - "component": "moz-box-group", - "title": "UI Widgets/Box Group", - "status": "in-development", - "storyId": "ui-widgets-box-group--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-box-group--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-box-group", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1941000" - }, - { - "component": "moz-box-item", - "title": "UI Widgets/Box Item", - "status": "in-development", - "storyId": "ui-widgets-box-item--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-box-item--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-box-item", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1956560" - }, - { - "component": "moz-box-link", - "title": "UI Widgets/Box Link", - "status": "in-development", - "storyId": "ui-widgets-box-link--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-box-link--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-box-link", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1941939" - }, - { - "component": "moz-breadcrumb-group", - "title": "UI Widgets/Breadcrumb Group", - "status": "in-development", - "storyId": "ui-widgets-breadcrumb-group--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-breadcrumb-group--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-breadcrumb-group", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1948410" - }, - { - "component": "moz-button", - "title": "UI Widgets/Button", - "status": "stable", - "storyId": "ui-widgets-button--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-button--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-button", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1855803" - }, - { - "component": "moz-button-group", - "title": "UI Widgets/Button Group", - "status": "stable", - "storyId": "ui-widgets-button-group--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-button-group--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-button-group", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1801325" - }, - { - "component": "moz-card", - "title": "UI Widgets/Card", - "status": "stable", - "storyId": "ui-widgets-card--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-card--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-card", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1846844" - }, - { - "component": "moz-checkbox", - "title": "UI Widgets/Checkbox", - "status": "in-development", - "storyId": "ui-widgets-checkbox--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-checkbox--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-checkbox", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894485" - }, - { - "component": "moz-fieldset", - "title": "UI Widgets/Fieldset", - "status": "in-development", - "storyId": "ui-widgets-fieldset--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-fieldset--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-fieldset", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1980498" - }, - { - "component": "moz-five-star", - "title": "UI Widgets/Five Star", - "status": "in-development", - "storyId": "ui-widgets-five-star--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-five-star--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-five-star", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1856385" - }, - { - "component": "moz-input-color", - "title": "UI Widgets/Input Color", - "status": "in-development", - "storyId": "ui-widgets-input-color--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-input-color--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-input-color", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1889950" - }, - { - "component": "moz-input-folder", - "title": "UI Widgets/Input Folder", - "status": "in-development", - "storyId": "ui-widgets-input-folder--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-input-folder--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-input-folder", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1941432" - }, - { - "component": "moz-input-password", - "title": "UI Widgets/Input Password", - "status": "in-development", - "storyId": "ui-widgets-input-password--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-input-password--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-input-password", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1941060" - }, - { - "component": "moz-input-search", - "title": "UI Widgets/Input Search", - "status": "in-development", - "storyId": "ui-widgets-input-search--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-input-search--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-input-search", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1801329" - }, - { - "component": "moz-input-text", - "title": "UI Widgets/Input Text", - "status": "in-development", - "storyId": "ui-widgets-input-text--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-input-text--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-input-text", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1912089" - }, - { - "component": "moz-label", - "title": "UI Widgets/Label", - "status": "stable", - "storyId": "ui-widgets-label--accesskey", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-label--accesskey", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-label", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1878744" - }, - { - "component": "moz-message-bar", - "title": "UI Widgets/Message Bar", - "status": "stable", - "storyId": "ui-widgets-message-bar--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-message-bar--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-message-bar", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1837469" - }, - { - "component": "moz-page-header", - "title": "UI Widgets/Page Header", - "status": "in-development", - "storyId": "ui-widgets-page-header--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-page-header--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-page-header", - "bugUrl": "" - }, - { - "component": "moz-page-nav", - "title": "UI Widgets/Page Nav", - "status": "in-development", - "storyId": "ui-widgets-page-nav--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-page-nav--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-page-nav", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1878042" - }, - { - "component": "moz-promo", - "title": "UI Widgets/Promo", - "status": "in-development", - "storyId": "ui-widgets-promo--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-promo--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-promo", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1966422" - }, - { - "component": "moz-radio-group", - "title": "UI Widgets/Radio Group", - "status": "in-development", - "storyId": "ui-widgets-radio-group--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-radio-group--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-radio-group", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894497" - }, - { - "component": "moz-reorderable-list", - "title": "UI Widgets/Reorderable List", - "status": "in-development", - "storyId": "ui-widgets-reorderable-list--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-reorderable-list--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-reorderable-list", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1828933" - }, - { - "component": "moz-select", - "title": "UI Widgets/Select", - "status": "in-development", - "storyId": "ui-widgets-select--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-select--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-select", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1931441" - }, - { - "component": "moz-support-link", - "title": "UI Widgets/Support Link", - "status": "stable", - "storyId": "ui-widgets-support-link--withamourl", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-support-link--withamourl", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-support-link", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1801924" - }, - { - "component": "moz-toggle", - "title": "UI Widgets/Toggle", - "status": "stable", - "storyId": "ui-widgets-toggle--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-toggle--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-toggle", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1799466" - }, - { - "component": "moz-visual-picker", - "title": "UI Widgets/Visual Picker", - "status": "in-development", - "storyId": "ui-widgets-visual-picker--default", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-visual-picker--default", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/moz-visual-picker", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1941063" - }, - { - "component": "panel-list", - "title": "UI Widgets/Panel List", - "status": "stable", - "storyId": "ui-widgets-panel-list--simple", - "storyUrl": "https://firefoxux.github.io/firefox-desktop-components/?path=/story/ui-widgets-panel-list--simple", - "sourceUrl": "https://searchfox.org/firefox-main/source/toolkit/content/widgets/panel-list", - "bugUrl": "https://bugzilla.mozilla.org/show_bug.cgi?id=1811282" - } - ] -} diff --git a/browser/components/urlbar/tests/browser/browser_suppressFocusBorder.js b/browser/components/urlbar/tests/browser/browser_suppressFocusBorder.js @@ -318,12 +318,10 @@ async function testInteractionFeature(interaction, win) { info("Click on search-handoff-button in newtab page"); await ContentTask.spawn(win.gBrowser.selectedBrowser, null, async () => { - await ContentTaskUtils.waitForCondition(() => { - return content.document.querySelector("content-search-handoff-ui"); - }, "Handoff UI has loaded"); - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - await handoffUI.updateComplete; - handoffUI.shadowRoot.querySelector(".search-handoff-button").click(); + await ContentTaskUtils.waitForCondition(() => + content.document.querySelector(".search-handoff-button") + ); + content.document.querySelector(".search-handoff-button").click(); }); await BrowserTestUtils.waitForCondition( @@ -389,7 +387,5 @@ async function openAboutNewTab(win = window) { () => win.gBrowser.tabs.length === tabCount + 1, "Waiting for background about:newtab to open." ); - let tab = win.gBrowser.tabs[win.gBrowser.tabs.length - 1]; - await BrowserTestUtils.browserLoaded(tab.linkedBrowser); - return tab; + return win.gBrowser.tabs[win.gBrowser.tabs.length - 1]; } diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_handoff.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_handoff.js @@ -77,19 +77,10 @@ add_task(async function test_search() { info("Focus on search input in newtab content"); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - await handoffUI.updateComplete; - let searchInput = handoffUI.shadowRoot.querySelector( - ".search-handoff-button" - ); + const searchInput = content.document.querySelector(".fake-editable"); searchInput.click(); }); - await BrowserTestUtils.waitForCondition( - () => window.gURLBar._hideFocus, - "Wait until _hideFocus will be true" - ); - info("Search and wait the result"); const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); EventUtils.synthesizeKey("q"); @@ -115,19 +106,10 @@ add_task(async function test_search_private_mode() { info("Focus on search input in newtab content"); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - await handoffUI.updateComplete; - let searchInput = handoffUI.shadowRoot.querySelector( - ".search-handoff-button" - ); + const searchInput = content.document.querySelector(".fake-editable"); searchInput.click(); }); - await BrowserTestUtils.waitForCondition( - () => privateWindow.gURLBar._hideFocus, - "Wait until _hideFocus will be true" - ); - info("Search and wait the result"); const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); EventUtils.synthesizeKey("q", {}, privateWindow); diff --git a/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_searchmode.js b/browser/components/urlbar/tests/browser/browser_urlbar_telemetry_searchmode.js @@ -487,12 +487,8 @@ add_task(async function test_handoff_pbm() { let tab = win.gBrowser.selectedBrowser; await SpecialPowers.spawn(tab, [], async function () { - await ContentTaskUtils.waitForCondition(() => - content.document.querySelector("content-search-handoff-ui") - ); - let handoffUI = content.document.querySelector("content-search-handoff-ui"); - await handoffUI.updateComplete; - handoffUI.shadowRoot.querySelector(".search-handoff-button").click(); + let btn = content.document.getElementById("search-handoff-button"); + btn.click(); }); let searchPromise = UrlbarTestUtils.promiseSearchComplete(win); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js b/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js @@ -29,15 +29,8 @@ async function doHandoffTest({ trigger, assert }) { await doTest(async browser => { BrowserTestUtils.startLoadingURIString(browser, "about:newtab"); await BrowserTestUtils.browserStopped(browser, "about:newtab"); - await SpecialPowers.spawn(browser, [], async function () { - await ContentTaskUtils.waitForCondition(() => - content.document.querySelector("content-search-handoff-ui") - ); - let handoffUI = content.document.querySelector( - "content-search-handoff-ui" - ); - await handoffUI.updateComplete; - const searchInput = handoffUI.shadowRoot.querySelector(".fake-editable"); + await SpecialPowers.spawn(browser, [], function () { + const searchInput = content.document.querySelector(".fake-editable"); searchInput.click(); }); EventUtils.synthesizeKey("x"); diff --git a/browser/extensions/newtab/bin/render-activity-stream-html.js b/browser/extensions/newtab/bin/render-activity-stream-html.js @@ -34,6 +34,7 @@ function templateHTML(options) { const debugString = options.debug ? "-dev" : ""; // This list must match any similar ones in AboutNewTabChild.sys.mjs const scripts = [ + "chrome://browser/content/contentSearchHandoffUI.js", "chrome://browser/content/contentTheme.js", `${options.baseVendorUrl}vendor/react${debugString}.js`, `${options.baseVendorUrl}vendor/react-dom${debugString}.js`, diff --git a/browser/extensions/newtab/content-src/components/Base/Base.jsx b/browser/extensions/newtab/content-src/components/Base/Base.jsx @@ -206,20 +206,6 @@ export class BaseContent extends React.PureComponent { global.addEventListener("keydown", this.handleOnKeyDown); const prefs = this.props.Prefs.values; const wallpapersEnabled = prefs["newtabWallpapers.enabled"]; - - if (prefs["search.useHandoffComponent"]) { - // Dynamically import the contentSearchHandoffUI module, but don't worry - // about webpacking this one. - import( - /* webpackIgnore: true */ "chrome://browser/content/contentSearchHandoffUI.mjs" - ); - } else { - const scriptURL = "chrome://browser/content/contentSearchHandoffUI.js"; - const scriptEl = document.createElement("script"); - scriptEl.src = scriptURL; - document.head.appendChild(scriptEl); - } - if (this.props.document.visibilityState === VISIBLE) { this.onVisible(); } else { diff --git a/browser/extensions/newtab/content-src/components/Search/Search.jsx b/browser/extensions/newtab/content-src/components/Search/Search.jsx @@ -4,16 +4,6 @@ /* globals ContentSearchHandoffUIController */ -/** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ - import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs"; import { connect } from "react-redux"; import { Logo } from "content-src/components/Logo/Logo"; @@ -71,42 +61,21 @@ export class _Search extends React.PureComponent { } componentDidMount() { - const { - caretBlinkCount, - caretBlinkTime, - "search.useHandoffComponent": useHandoffComponent, - } = this.props.Prefs.values; - - if (useHandoffComponent) { - const { handoffUI } = this; - if (handoffUI) { - // If caret blink count isn't defined, use the default infinite behavior for animation - handoffUI.style.setProperty( - "--caret-blink-count", - caretBlinkCount > -1 ? caretBlinkCount : "infinite" - ); - - // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) - handoffUI.style.setProperty( - "--caret-blink-time", - caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms` - ); - } - } else { - const caret = this.fakeCaret; - if (caret) { - // If caret blink count isn't defined, use the default infinite behavior for animation - caret.style.setProperty( - "--caret-blink-count", - caretBlinkCount > -1 ? caretBlinkCount : "infinite" - ); - - // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) - caret.style.setProperty( - "--caret-blink-time", - caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms` - ); - } + const caret = this.fakeCaret; + const { caretBlinkCount, caretBlinkTime } = this.props.Prefs.values; + + if (caret) { + // If caret blink count isn't defined, use the default infinite behavior for animation + caret.style.setProperty( + "--caret-blink-count", + caretBlinkCount > -1 ? caretBlinkCount : "infinite" + ); + + // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) + caret.style.setProperty( + "--caret-blink-time", + caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms` + ); } } @@ -129,24 +98,6 @@ export class _Search extends React.PureComponent { * in order to execute searches in various tests */ render() { - const useHandoffComponent = - this.props.Prefs.values["search.useHandoffComponent"]; - - if (useHandoffComponent) { - return ( - <div className="search-wrapper"> - {this.props.showLogo && <Logo />} - <div className="search-inner-wrapper"> - <content-search-handoff-ui - ref={el => { - this.handoffUI = el; - }} - ></content-search-handoff-ui> - </div> - </div> - ); - } - const wrapperClassName = [ "search-wrapper", this.props.disable && "search-disabled", diff --git a/browser/extensions/newtab/content-src/components/Search/_Search.scss b/browser/extensions/newtab/content-src/components/Search/_Search.scss @@ -67,15 +67,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg'); } } - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ .search-handoff-button { background: var(--newtab-background-color-secondary) var(--newtab-search-icon) $search-icon-padding center no-repeat; background-size: $search-icon-size; @@ -96,15 +87,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg'); } } - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ &.fake-focus:not(.search.disabled) { .search-handoff-button { border: 1px solid var(--newtab-primary-action-background); @@ -112,15 +94,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg'); } } - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ .search-handoff-button { padding-inline-end: var(--space-large); color: var(--newtab-text-primary-color); @@ -138,16 +111,6 @@ $glyph-forward: url('chrome://browser/skin/forward.svg'); } } - content-search-handoff-ui { - --content-search-handoff-ui-background-color: var(--newtab-background-color-secondary); - --content-search-handoff-ui-color: var(--newtab-text-primary-color); - --content-search-handoff-ui-fill: var(--newtab-text-secondary-color); - --content-search-handoff-ui-caret-color: var(--newtab-text-primary-color); - --content-search-handoff-ui-fakefocus-border-color: var(--newtab-primary-action-background); - --content-search-handoff-ui-fakefocus-box-shadow-inner: var(--newtab-primary-action-background-dimmed); - --content-search-handoff-ui-fakefocus-box-shadow-outer: transparent; - } - &.visible-logo { .logo-and-wordmark { .wordmark { diff --git a/browser/extensions/newtab/css/activity-stream.css b/browser/extensions/newtab/css/activity-stream.css @@ -1470,33 +1470,6 @@ main section { padding: 0; margin-block: var(--space-xxlarge); margin-block-start: 0; - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ - /** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ } .search-wrapper .logo-and-wordmark { margin-block-end: var(--space-xxlarge); @@ -1582,15 +1555,6 @@ main section { .search-wrapper .search-handoff-button .fake-caret:dir(rtl) { background-position-x: right 16px; } -.search-wrapper content-search-handoff-ui { - --content-search-handoff-ui-background-color: var(--newtab-background-color-secondary); - --content-search-handoff-ui-color: var(--newtab-text-primary-color); - --content-search-handoff-ui-fill: var(--newtab-text-secondary-color); - --content-search-handoff-ui-caret-color: var(--newtab-text-primary-color); - --content-search-handoff-ui-fakefocus-border-color: var(--newtab-primary-action-background); - --content-search-handoff-ui-fakefocus-box-shadow-inner: var(--newtab-primary-action-background-dimmed); - --content-search-handoff-ui-fakefocus-box-shadow-outer: transparent; -} .search-wrapper.visible-logo .logo-and-wordmark .wordmark { fill: var(--newtab-wordmark-color); } diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -14868,16 +14868,6 @@ function Logo() { /* globals ContentSearchHandoffUIController */ -/** - * @backward-compat { version 148 } - * - * Temporary dual implementation to support train hopping. The old handoff UI - * is kept alongside the new contentSearchHandoffUI.mjs custom element until - * the module lands on all channels. Controlled by the pref - * browser.newtabpage.activity-stream.search.useHandoffComponent. - * Remove the old implementation and the pref once this ships to Release. - */ - @@ -14938,31 +14928,17 @@ class _Search extends (external_React_default()).PureComponent { } } componentDidMount() { + const caret = this.fakeCaret; const { caretBlinkCount, - caretBlinkTime, - "search.useHandoffComponent": useHandoffComponent + caretBlinkTime } = this.props.Prefs.values; - if (useHandoffComponent) { - const { - handoffUI - } = this; - if (handoffUI) { - // If caret blink count isn't defined, use the default infinite behavior for animation - handoffUI.style.setProperty("--caret-blink-count", caretBlinkCount > -1 ? caretBlinkCount : "infinite"); - - // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) - handoffUI.style.setProperty("--caret-blink-time", caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms`); - } - } else { - const caret = this.fakeCaret; - if (caret) { - // If caret blink count isn't defined, use the default infinite behavior for animation - caret.style.setProperty("--caret-blink-count", caretBlinkCount > -1 ? caretBlinkCount : "infinite"); + if (caret) { + // If caret blink count isn't defined, use the default infinite behavior for animation + caret.style.setProperty("--caret-blink-count", caretBlinkCount > -1 ? caretBlinkCount : "infinite"); - // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) - caret.style.setProperty("--caret-blink-time", caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms`); - } + // Apply custom blink rate if set, else fallback to default (567ms on/off --> 1134ms total) + caret.style.setProperty("--caret-blink-time", caretBlinkTime > 0 ? `${caretBlinkTime * 2}ms` : `${1134}ms`); } } onInputMountHandoff(input) { @@ -14983,18 +14959,6 @@ class _Search extends (external_React_default()).PureComponent { * in order to execute searches in various tests */ render() { - const useHandoffComponent = this.props.Prefs.values["search.useHandoffComponent"]; - if (useHandoffComponent) { - return /*#__PURE__*/external_React_default().createElement("div", { - className: "search-wrapper" - }, this.props.showLogo && /*#__PURE__*/external_React_default().createElement(Logo, null), /*#__PURE__*/external_React_default().createElement("div", { - className: "search-inner-wrapper" - }, /*#__PURE__*/external_React_default().createElement("content-search-handoff-ui", { - ref: el => { - this.handoffUI = el; - } - }))); - } const wrapperClassName = ["search-wrapper", this.props.disable && "search-disabled", this.props.fakeFocus && "fake-focus"].filter(v => v).join(" "); return /*#__PURE__*/external_React_default().createElement("div", { className: wrapperClassName @@ -15741,16 +15705,6 @@ class BaseContent extends (external_React_default()).PureComponent { __webpack_require__.g.addEventListener("keydown", this.handleOnKeyDown); const prefs = this.props.Prefs.values; const wallpapersEnabled = prefs["newtabWallpapers.enabled"]; - if (prefs["search.useHandoffComponent"]) { - // Dynamically import the contentSearchHandoffUI module, but don't worry - // about webpacking this one. - import(/* webpackIgnore: true */"chrome://browser/content/contentSearchHandoffUI.mjs"); - } else { - const scriptURL = "chrome://browser/content/contentSearchHandoffUI.js"; - const scriptEl = document.createElement("script"); - scriptEl.src = scriptURL; - document.head.appendChild(scriptEl); - } if (this.props.document.visibilityState === Base_VISIBLE) { this.onVisible(); } else { diff --git a/browser/extensions/newtab/karma.mc.config.js b/browser/extensions/newtab/karma.mc.config.js @@ -354,12 +354,6 @@ module.exports = function (config) { functions: 31.2, branches: 31.2, }, - "content-src/components/Search/Search.jsx": { - statements: 38, - lines: 39, - functions: 28, - branches: 25, - }, "content-src/components/**/*.jsx": { statements: 51.1, lines: 52.38, diff --git a/browser/extensions/newtab/lib/PlacesFeed.sys.mjs b/browser/extensions/newtab/lib/PlacesFeed.sys.mjs @@ -323,13 +323,6 @@ export class PlacesFeed { ]; } - /** - * @backward-compat { version 148 } - * - * This, and all newtab-specific handoff searchbar handling can be removed - * once 147 is released, as all handoff UI and logic will be handled by - * contentSearchHandoffUI and the ContentSearch JSWindowActors. - */ handoffSearchToAwesomebar(action) { const { _target, data, meta } = action; const searchEngine = this._getDefaultSearchEngine( diff --git a/browser/extensions/newtab/lib/PrefsFeed.sys.mjs b/browser/extensions/newtab/lib/PrefsFeed.sys.mjs @@ -348,7 +348,6 @@ export class PrefsFeed { this._setStringPref(values, "discoverystream.spocs-endpoint", ""); this._setStringPref(values, "discoverystream.spocs-endpoint-query", ""); this._setStringPref(values, "newNewtabExperience.colors", ""); - this._setBoolPref(values, "search.useHandoffComponent", false); // Set the initial state of all prefs in redux this.store.dispatch( diff --git a/browser/extensions/newtab/prerendered/activity-stream-debug.html b/browser/extensions/newtab/prerendered/activity-stream-debug.html @@ -35,6 +35,7 @@ </head> <body class="activity-stream"> <div id="root"></div> + <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> <script src="chrome://global/content/vendor/react-dom-dev.js"></script> diff --git a/browser/extensions/newtab/prerendered/activity-stream.html b/browser/extensions/newtab/prerendered/activity-stream.html @@ -35,6 +35,7 @@ </head> <body class="activity-stream"> <div id="root"></div> + <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> <script src="chrome://global/content/vendor/react-dom.js"></script> diff --git a/browser/extensions/newtab/test/browser/browser_as_render.js b/browser/extensions/newtab/test/browser/browser_as_render.js @@ -2,17 +2,8 @@ test_newtab({ test: function test_render_search_handoff() { - const usingHandoffComponent = Services.prefs.getBoolPref( - "browser.newtabpage.activity-stream.search.useHandoffComponent", - false - ); - - const selector = usingHandoffComponent - ? "content-search-handoff-ui" - : ".search-handoff-button"; - - let search = content.document.querySelector(selector); - ok(search, "Got the content search handoff UI"); + let search = content.document.querySelector(".search-handoff-button"); + ok(search, "Got the search handoff button"); }, }); diff --git a/browser/extensions/newtab/test/unit/content-src/components/Search.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/Search.test.jsx @@ -6,7 +6,7 @@ import { Logo } from "content-src/components/Logo/Logo"; const DEFAULT_PROPS = { dispatch() {}, - Prefs: { values: { featureConfig: {}, "search.useHandoffComponent": true } }, + Prefs: { values: { featureConfig: {} } }, }; describe("<Search>", () => { @@ -48,7 +48,78 @@ describe("<Search>", () => { it("should render a Search hand-off element", () => { const wrapper = shallow(<Search {...DEFAULT_PROPS} />); assert.ok(wrapper.exists()); - assert.equal(wrapper.find("content-search-handoff-ui").length, 1); + assert.equal(wrapper.find(".search-handoff-button").length, 1); + }); + it("should hand-off search when button is clicked", () => { + const dispatch = sinon.spy(); + const wrapper = shallow( + <Search {...DEFAULT_PROPS} dispatch={dispatch} /> + ); + wrapper + .find(".search-handoff-button") + .simulate("click", { preventDefault: () => {} }); + assert.calledThrice(dispatch); + assert.calledWith(dispatch, { + data: { text: undefined }, + meta: { + from: "ActivityStream:Content", + skipLocal: true, + to: "ActivityStream:Main", + }, + type: "HANDOFF_SEARCH_TO_AWESOMEBAR", + }); + assert.calledWith(dispatch, { type: "FAKE_FOCUS_SEARCH" }); + const [action] = dispatch.thirdCall.args; + assert.isUserEventAction(action); + assert.propertyVal(action.data, "event", "SEARCH_HANDOFF"); + }); + it("should hand-off search on paste", () => { + const dispatch = sinon.spy(); + const wrapper = mount(<Search {...DEFAULT_PROPS} dispatch={dispatch} />); + wrapper.instance()._searchHandoffButton = { contains: () => true }; + wrapper.instance().onSearchHandoffPaste({ + clipboardData: { + getData: () => "some copied text", + }, + preventDefault: () => {}, + }); + assert.equal(dispatch.callCount, 4); + assert.calledWith(dispatch, { + data: { text: "some copied text" }, + meta: { + from: "ActivityStream:Content", + skipLocal: true, + to: "ActivityStream:Main", + }, + type: "HANDOFF_SEARCH_TO_AWESOMEBAR", + }); + assert.calledWith(dispatch, { type: "DISABLE_SEARCH" }); + const [action] = dispatch.thirdCall.args; + assert.isUserEventAction(action); + assert.propertyVal(action.data, "event", "SEARCH_HANDOFF"); + }); + it("should properly handle drop events", () => { + const dispatch = sinon.spy(); + const wrapper = mount(<Search {...DEFAULT_PROPS} dispatch={dispatch} />); + const preventDefault = sinon.spy(); + wrapper.find(".fake-editable").simulate("drop", { + dataTransfer: { getData: () => "dropped text" }, + preventDefault, + }); + assert.equal(dispatch.callCount, 4); + assert.calledWith(dispatch, { + data: { text: "dropped text" }, + meta: { + from: "ActivityStream:Content", + skipLocal: true, + to: "ActivityStream:Main", + }, + type: "HANDOFF_SEARCH_TO_AWESOMEBAR", + }); + assert.calledWith(dispatch, { type: "DISABLE_SEARCH" }); + const [action] = dispatch.thirdCall.args; + assert.isUserEventAction(action); + assert.propertyVal(action.data, "event", "SEARCH_HANDOFF"); }); }); }); diff --git a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css @@ -160,34 +160,162 @@ p { } } -content-search-handoff-ui { - --content-search-handoff-ui-background-color: white; - --content-search-handoff-ui-color: rgb(12, 12, 13); - --content-search-handoff-ui-fill: rgba(12, 12, 13, 0.4); - --content-search-handoff-ui-caret-color: rgb(12, 12, 13); - --content-search-handoff-ui-fakefocus-border-color: #0060df; - --content-search-handoff-ui-fakefocus-box-shadow-inner: #0060df; - --content-search-handoff-ui-fakefocus-box-shadow-outer: rgba(0, 96, 223, 0.3); - - /* stylelint-disable-next-line media-query-no-invalid */ - @media -moz-pref("browser.privatebrowsing.felt-privacy-v1") { - --content-search-handoff-ui-background-color: #321c64; - --content-search-handoff-ui-color: #fbfbfe; - --content-search-handoff-ui-fill: rgb(251, 251, 254); - --content-search-handoff-ui-caret-color: #fbfbfe; - --content-search-handoff-ui-border-width: 3px; - --content-search-handoff-ui-unfocused-border-color: rgb(149, 43, 185); - --content-search-handoff-ui-fakefocus-border-color: #0060df; - --content-search-handoff-ui-fakefocus-box-shadow-inner: #0060df; - --content-search-handoff-ui-fakefocus-box-shadow-outer: rgba(0, 96, 223, 0.3); +.search-handoff-button, +.search-handoff-button:active, +.search-handoff-button:enabled:hover:active { + background: white var(--newtab-search-icon) 12px center no-repeat; + background-size: 24px; + border: solid 1px rgba(249, 249, 250, 0.2); + border-radius: var(--border-radius-xsmall); + box-shadow: + 0 1px 4px 0 rgba(12, 12, 13, 0.2), + 0 0 0 1px rgba(0, 0, 0, 0.15); + cursor: text; + font-size: 15px; + font-weight: normal; + margin: 0; + padding-block: 0; + padding-inline: 46px 48px; + position: relative; + opacity: 1; + width: 100%; + -moz-context-properties: fill; + fill: rgba(12, 12, 13, 0.4); +} + +.search-handoff-button.focused:not(.disabled) { + border: solid 1px #0060df; + box-shadow: + 0 0 0 1px #0060df, + 0 0 0 4px rgba(0, 96, 223, 0.3); +} + +.search-handoff-button.disabled { + opacity: 0.5; + box-shadow: none; +} + +.search-handoff-button:dir(rtl), +.search-handoff-button:active:dir(rtl), +.search-handoff-button:enabled:hover:active:dir(rtl) { + background-position-x: right 12px; +} + +.search-inner-wrapper .search-handoff-button:hover, +.search-inner-wrapper .search-handoff-button:hover:active { + background-color: white; + color: inherit; +} + +.search-handoff-button.focused:not(.disabled) .fake-caret { + display: block; +} + +.fake-editable:focus { + outline: none; + caret-color: transparent; +} + +.fake-editable { + border: 0; + height: 100%; + top: 0; + inset-inline-start: 0; + opacity: 0; + padding: 0; + position: absolute; + width: 100%; +} + +.fake-textbox { + color: rgb(12, 12, 13); + opacity: 0.54; + text-align: start; +} + +@keyframes caret-animation { + to { + visibility: hidden; + } +} + +.fake-caret { + animation: caret-animation 1.3s steps(5, start) infinite; + background-color: rgb(12, 12, 13); + display: none; + inset-inline-start: 47px; + height: 17px; + position: absolute; + top: 16px; + width: 1px; +} + +@media (prefers-contrast) { + .fake-caret { + background-color: ButtonText; } +} + +/* stylelint-disable-next-line media-query-no-invalid */ +@media -moz-pref("browser.privatebrowsing.felt-privacy-v1") { + .search-handoff-button, + .search-handoff-button:hover:not(.focused), + .search-handoff-button:active, + .search-handoff-button:enabled:hover:active { + border: 3px solid ButtonText; + border-radius: var(--border-radius-small); + box-shadow: none; + + @media not (prefers-contrast) { + border-color: #952bb9; + } + } + + .search-handoff-button, + .search-handoff-button:hover:not(.focused), + .search-handoff-button:active, + .search-handoff-button:enabled:hover:active, + .search-inner-wrapper .search-handoff-button:hover, + .search-inner-wrapper .search-handoff-button:hover:active { + background-color: ButtonFace; + background-image: url(chrome://global/skin/icons/search-glass.svg); + background-repeat: no-repeat; + background-size: 16px; + fill: ButtonText; + + @media (prefers-contrast) { + border-color: ButtonText; + color: ButtonText; + } - @media (forced-colors) { - --content-search-handoff-ui-background-color: ButtonFace; - --content-search-handoff-ui-color: ButtonText; - --content-search-handoff-ui-fill: ButtonText; - --content-search-handoff-ui-caret-color: ButtonText; - --content-search-handoff-ui-fakefocus-border-color: ButtonText; + @media not (prefers-contrast) { + background-color: #321c64; + fill: rgb(251, 251, 254); + } + } + + .search-handoff-button.focused:not(.disabled) { + border-width: 3px; + + @media (prefers-contrast) { + outline: 2px solid SelectedItem; + } + } + + .fake-textbox { + color: ButtonText; + font-size: 0.87em; + opacity: 1; + + @media not (prefers-contrast) { + color: #fbfbfe; + } + } + + @media not (prefers-contrast) { + .fake-caret { + background-color: #fbfbfe; + } } }