[Search]: Add Brave Search Widget to the New Tab Page (#22231)

This commit is contained in:
Jay Harris
2024-03-13 22:57:10 +01:00
committed by GitHub
parent c32ee6bda8
commit 4c3226c731
25 changed files with 648 additions and 56 deletions
+8
View File
@@ -12,6 +12,7 @@
#include "brave/browser/brave_features_internal_names.h"
#include "brave/browser/ethereum_remote_client/buildflags/buildflags.h"
#include "brave/browser/ethereum_remote_client/features.h"
#include "brave/browser/ui/brave_ui_features.h"
#include "brave/browser/ui/tabs/features.h"
#include "brave/components/ai_chat/core/common/buildflags/buildflags.h"
#include "brave/components/brave_ads/browser/ad_units/notification_ad/custom_notification_ad_feature.h"
@@ -500,6 +501,13 @@
FEATURE_VALUE_TYPE( \
ntp_background_images::features::kBraveNTPBrandedWallpaperDemo), \
}, \
{ \
"brave-ntp-search-widget", \
"Brave Search Widget on the NTP", \
"Enables searching directly from the New Tab Page", \
kOsDesktop, \
FEATURE_VALUE_TYPE(features::kBraveNtpSearchWidget), \
}, \
{ \
"brave-adblock-cname-uncloaking", \
"Enable CNAME uncloaking", \
+11 -11
View File
@@ -36,6 +36,7 @@
#include "brave/browser/profiles/brave_renderer_updater_factory.h"
#include "brave/browser/profiles/profile_util.h"
#include "brave/browser/skus/skus_service_factory.h"
#include "brave/browser/ui/brave_ui_features.h"
#include "brave/browser/ui/webui/skus_internals_ui.h"
#include "brave/components/ai_chat/core/common/buildflags/buildflags.h"
#include "brave/components/brave_federated/features.h"
@@ -236,6 +237,7 @@ using extensions::ChromeContentBrowserClientExtensionsPart;
#include "brave/components/brave_shields/core/common/cookie_list_opt_in.mojom.h"
#include "brave/components/commands/common/commands.mojom.h"
#include "brave/components/commands/common/features.h"
#include "components/omnibox/browser/omnibox.mojom.h"
#endif
#if BUILDFLAG(ENABLE_PLAYLIST)
@@ -627,6 +629,15 @@ void BraveContentBrowserClient::RegisterWebUIInterfaceBrokers(
}
#if !BUILDFLAG(IS_ANDROID)
auto ntp_registration =
registry.ForWebUI<BraveNewTabUI>()
.Add<brave_new_tab_page::mojom::PageHandlerFactory>()
.Add<brave_news::mojom::BraveNewsController>();
if (base::FeatureList::IsEnabled(features::kBraveNtpSearchWidget)) {
ntp_registration.Add<omnibox::mojom::PageHandler>();
}
if (base::FeatureList::IsEnabled(
brave_news::features::kBraveNewsFeedUpdate)) {
registry.ForWebUI<BraveNewsInternalsUI>()
@@ -825,22 +836,11 @@ void BraveContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
#endif
#endif
// Brave News
#if !BUILDFLAG(IS_ANDROID)
content::RegisterWebUIControllerInterfaceBinder<
brave_news::mojom::BraveNewsController, BraveNewTabUI>(map);
#endif
#if BUILDFLAG(ENABLE_SPEEDREADER) && !BUILDFLAG(IS_ANDROID)
content::RegisterWebUIControllerInterfaceBinder<
speedreader::mojom::ToolbarFactory, SpeedreaderToolbarUI>(map);
#endif
#if !BUILDFLAG(IS_ANDROID)
content::RegisterWebUIControllerInterfaceBinder<
brave_new_tab_page::mojom::PageHandlerFactory, BraveNewTabUI>(map);
#endif
#if BUILDFLAG(ENABLE_PLAYLIST)
map->Add<playlist::mojom::PlaylistMediaHandler>(
base::BindRepeating(&BindPlaylistMediaHandler));
+2
View File
@@ -36,6 +36,8 @@ source_set("ui") {
public_deps = []
sources = [
"brave_ui_features.cc",
"brave_ui_features.h",
"webui/brave_adblock_internals_ui.cc",
"webui/brave_adblock_internals_ui.h",
"webui/brave_adblock_ui.cc",
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
#include "brave/browser/ui/brave_ui_features.h"
namespace features {
BASE_FEATURE(kBraveNtpSearchWidget,
"BraveNtpSearchWidget",
base::FEATURE_DISABLED_BY_DEFAULT);
}
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
#ifndef BRAVE_BROWSER_UI_BRAVE_UI_FEATURES_H_
#define BRAVE_BROWSER_UI_BRAVE_UI_FEATURES_H_
#include "base/feature_list.h"
namespace features {
BASE_DECLARE_FEATURE(kBraveNtpSearchWidget);
}
#endif // BRAVE_BROWSER_UI_BRAVE_UI_FEATURES_H_
@@ -5,6 +5,7 @@
#include "brave/browser/ui/webui/new_tab_page/brave_new_tab_ui.h"
#include <memory>
#include <utility>
#include "base/check.h"
@@ -12,6 +13,7 @@
#include "brave/browser/brave_news/brave_news_controller_factory.h"
#include "brave/browser/new_tab/new_tab_shows_options.h"
#include "brave/browser/ntp_background/brave_ntp_custom_background_service_factory.h"
#include "brave/browser/ui/brave_ui_features.h"
#include "brave/browser/ui/webui/brave_webui_source.h"
#include "brave/browser/ui/webui/new_tab_page/brave_new_tab_message_handler.h"
#include "brave/browser/ui/webui/new_tab_page/brave_new_tab_page_handler.h"
@@ -25,11 +27,13 @@
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/common/pref_names.h"
#include "components/grit/brave_components_resources.h"
#include "components/omnibox/browser/omnibox.mojom.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_data_source.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
using ntp_background_images::NTPCustomImagesSource;
@@ -78,6 +82,10 @@ BraveNewTabUI::BraveNewTabUI(content::WebUI* web_ui, const std::string& name)
"featureFlagBraveNewsFeedV2Enabled",
base::FeatureList::IsEnabled(brave_news::features::kBraveNewsFeedUpdate));
source->AddBoolean(
"featureFlagSearchWidget",
base::FeatureList::IsEnabled(features::kBraveNtpSearchWidget));
web_ui->AddMessageHandler(base::WrapUnique(
BraveNewTabMessageHandler::Create(source, profile, was_restored)));
web_ui->AddMessageHandler(
@@ -116,6 +124,16 @@ void BraveNewTabUI::BindInterface(
page_factory_receiver_.Bind(std::move(pending_receiver));
}
void BraveNewTabUI::BindInterface(
mojo::PendingReceiver<omnibox::mojom::PageHandler> pending_page_handler) {
auto* profile = Profile::FromWebUI(web_ui());
DCHECK(profile);
realbox_handler_ = std::make_unique<RealboxHandler>(
std::move(pending_page_handler), profile, web_ui()->GetWebContents(),
nullptr, nullptr);
}
void BraveNewTabUI::CreatePageHandler(
mojo::PendingRemote<brave_new_tab_page::mojom::Page> pending_page,
mojo::PendingReceiver<brave_new_tab_page::mojom::PageHandler>
@@ -11,6 +11,8 @@
#include "brave/components/brave_new_tab_ui/brave_new_tab_page.mojom.h"
#include "brave/components/brave_news/common/brave_news.mojom.h"
#include "chrome/browser/ui/webui/realbox/realbox_handler.h"
#include "components/omnibox/browser/omnibox.mojom.h"
#include "content/public/browser/web_ui_controller.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -40,6 +42,9 @@ class BraveNewTabUI : public ui::MojoWebUIController,
mojo::PendingReceiver<brave_new_tab_page::mojom::PageHandlerFactory>
pending_receiver);
void BindInterface(
mojo::PendingReceiver<omnibox::mojom::PageHandler> pending_page_handler);
private:
// new_tab_page::mojom::PageHandlerFactory:
void CreatePageHandler(
@@ -48,6 +53,7 @@ class BraveNewTabUI : public ui::MojoWebUIController,
pending_page_handler) override;
std::unique_ptr<BraveNewTabPageHandler> page_handler_;
std::unique_ptr<RealboxHandler> realbox_handler_;
mojo::Receiver<brave_new_tab_page::mojom::PageHandlerFactory>
page_factory_receiver_;
@@ -0,0 +1,28 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
#include "chrome/browser/ui/webui/realbox/realbox_handler.h"
#include "brave/components/vector_icons/vector_icons.h"
#include "components/omnibox/browser/vector_icons.h"
namespace {
constexpr char kLeoWindowTabNewIconResourceName[] =
"//resources/brave-icons/window-tab-new.svg";
}
// This patches |PedalVectorIconToResourceName| to include the additional
// omnibox icons we add in Brave. Unfortunately, the method is used inside the
// file, so we can't just override the method. There is only one usage of
// kSwitchIcon in the file, so we add additional statements from it.
#define kSwitchIcon kSwitchIcon.name) {} \
if (icon.name == kLeoWindowTabNewIcon.name) { \
return kLeoWindowTabNewIconResourceName; \
} \
if (icon.name == omnibox::kSwitchIcon
#include "src/chrome/browser/ui/webui/realbox/realbox_handler.cc"
#undef kSwitchIcon
@@ -0,0 +1,10 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
#include "src/components/omnibox/browser/omnibox_client.cc"
bool OmniboxClient::IsAutocompleteEnabled() const {
return true;
}
@@ -0,0 +1,17 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_CLIENT_H_
#define BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_CLIENT_H_
#define CurrentPageExists \
IsAutocompleteEnabled() const; \
virtual bool CurrentPageExists
#include "src/components/omnibox/browser/omnibox_client.h" // IWYU pragma: export
#undef CurrentPageExists
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_OMNIBOX_BROWSER_OMNIBOX_CLIENT_H_
@@ -5,16 +5,13 @@
#include "components/omnibox/browser/omnibox_controller.h"
#include "brave/components/omnibox/browser/brave_omnibox_client.h"
#define StartAutocomplete StartAutocomplete_ChromiumImpl
#include "src/components/omnibox/browser/omnibox_controller.cc"
#undef StartAutocomplete
void OmniboxController::StartAutocomplete(
const AutocompleteInput& input) const {
auto* client = static_cast<BraveOmniboxClient*>(client_.get());
if (!client->IsAutocompleteEnabled()) {
if (!client_->IsAutocompleteEnabled()) {
ClearPopupKeywordMode();
return;
}
+2
View File
@@ -14,6 +14,8 @@ transpile_web_ui("brave_new_tab_ui") {
deps = [
":mojom_js",
"//brave/components/brave_news/common:mojom_js",
"//components/omnibox/browser:mojo_bindings",
"//components/omnibox/browser:mojo_bindings_js",
]
resource_name = "brave_new_tab"
}
@@ -7,6 +7,7 @@ import * as React from 'react'
import styled, { createGlobalStyle, css } from 'styled-components'
import { requestAnimationFrameThrottle } from '../../../../common/throttle'
import { defaultState } from '../../../storage/new_tab_storage'
import { spacing } from '@brave/leo/tokens/css'
const breakpointLargeBlocks = '980px'
const breakpointEveryBlock = '870px'
@@ -278,6 +279,10 @@ export const GridItemNavigationBraveNews = styled('div') <{}>`
[data-show-news-prompt] & {
bottom: 120px;
}
display: flex;
flex-direction: column;
gap: ${spacing.l};
`
export const Footer = styled('footer') <{}>`
@@ -0,0 +1,124 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
import Flex from '$web-common/Flex';
import Dropdown from '@brave/leo/react/dropdown';
import Icon from '@brave/leo/react/icon';
import Input from '@brave/leo/react/input';
import { icon, spacing } from '@brave/leo/tokens/css';
import { stringToMojoString16 } from 'chrome://resources/js/mojo_type_util.js';
import { AutocompleteResult, OmniboxPopupSelection, PageHandler, PageHandlerRemote, PageInterface, PageReceiver } from 'gen/components/omnibox/browser/omnibox.mojom.m';
import * as React from 'react';
import styled from 'styled-components';
interface SearchEngine {
name: string,
icon: string
}
const searchEngines: SearchEngine[] = [{
name: 'Brave',
icon: 'brave-icon-search-color'
}, {
name: 'Google',
icon: 'google-color'
}, {
name: 'DuckDuckGo',
icon: 'duckduckgo-color'
}, {
name: 'Bing',
icon: 'bing-color'
}]
const SearchInput = styled(Input)`
--leo-control-padding: 6px;
display: inline-block;
width: 540px;
`
const EnginePicker = styled(Dropdown)`
`
const SelectedIcon = styled(Icon)`
--leo-icon-size: ${icon.l};
`
const SearchIconContainer = styled.div`
padding-right: ${spacing.m};
`
const Option = styled.div`
display: flex;
gap: ${spacing.m};
`
export const omniboxController: PageHandlerRemote = PageHandler.getRemote();
(window as any).omnibox = omniboxController;
class SearchPage implements PageInterface {
private receiver = new PageReceiver(this)
private result: AutocompleteResult | undefined
private resultListeners: Array<(result?: AutocompleteResult) => void> = []
private selectionListeners: Array<(selection: OmniboxPopupSelection) => void> = []
constructor() {
omniboxController.setPage(this.receiver.$.bindNewPipeAndPassRemote())
}
addResultListener(listener: (result?: AutocompleteResult) => void) {
this.resultListeners.push(listener)
if (this.result) listener(this.result)
}
removeResultListener(listener: (result?: AutocompleteResult) => void) {
this.resultListeners = this.resultListeners.filter(r => r !== listener)
}
addSelectionListener(listener: (selection: OmniboxPopupSelection) => void) {
this.selectionListeners.push(listener)
}
removeSelectionListener(listener: (selection: OmniboxPopupSelection) => void) {
this.selectionListeners = this.selectionListeners.filter(s => s !== listener)
}
autocompleteResultChanged(result: AutocompleteResult) {
this.result = result;
for (const listener of this.resultListeners) listener(result)
}
updateSelection(selection: OmniboxPopupSelection) {
for (const listener of this.selectionListeners) listener(selection)
}
}
export const search = new SearchPage()
export default function SearchBox() {
const [query, setQuery] = React.useState('')
const [searchEngine, setSearchEngine] = React.useState('Brave')
React.useEffect(() => {
omniboxController.queryAutocomplete(stringToMojoString16(query), false);
}, [query])
const searchInput = React.useRef<HTMLElement>()
return <SearchInput tabIndex={0} type="text" ref={searchInput} value={query} onInput={e => setQuery(e.detail.value)} placeholder="Search the web privately">
<Flex slot="left-icon">
<EnginePicker positionStrategy='fixed' value={searchEngine} onChange={e => setSearchEngine(e.detail.value)}>
<div slot="value">
<SelectedIcon name={searchEngines.find(s => s.name === searchEngine)?.icon} />
</div>
{searchEngines.map(s => <leo-option value={s.name} key={s.name}>
<Option>
<Icon name={s.icon} />{s.name}
</Option>
</leo-option>)}
</EnginePicker>
</Flex>
<SearchIconContainer slot="right-icon">
<Icon name="search" />
</SearchIconContainer>
</SearchInput>
}
@@ -0,0 +1,140 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
import * as React from 'react';
import styled, { keyframes } from 'styled-components'
import SearchBox from './SearchBox';
import SearchResults from './SearchResults';
import { spacing } from '@brave/leo/tokens/css';
import { createPortal } from 'react-dom';
interface Props {
onClose: () => void
offsetY: number
}
const duration = '0.12s'
const enterDialog = keyframes`
from {
transform: translateY(calc(var(--offset-y) - var(--margin-top)));
}
to {
transform: translateY(0);
}
`
const exitDialog = keyframes`
from {
transform: translateY(0);
}
to {
transform: translateY(calc(var(--offset-y) - var(--margin-top)));
}
`
const enterBackdrop = keyframes`
from {
background: transparent;
backdrop-filter: blur(0);
}
to {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(64px);
}
`
const exitBackdrop = keyframes`
from {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(64px);
}
to {
background: transparent;
backdrop-filter: blur(0);
}
`
const Dialog = styled.dialog<{ offsetY: number }>`
--margin-top: ${spacing['9Xl']};
--offset-y: ${p => p.offsetY}px;
outline: none;
border: none;
background: transparent;
margin-top: var(--margin-top);
padding: 2px;
animation: ${enterDialog} ${duration} ease-in-out;
&::backdrop {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(64px);
animation: ${enterBackdrop} ${duration} ease-in-out;
}
&.closing {
animation: ${exitDialog} ${duration} ease-in-out;
&::backdrop {
animation: ${exitBackdrop} ${duration} ease-in-out;
}
}
`
export default function Component(props: Props) {
const ref = React.useRef<HTMLDialogElement>(null)
React.useEffect(() => {
ref.current?.showModal()
}, [])
const doClose = React.useCallback(() => {
const el = ref.current!
el.addEventListener('animationend', () => props.onClose());
el.classList.add('closing');
}, [])
React.useEffect(() => {
const el = ref.current
if (!el) return
const closeHandler = (e: CloseEvent) => {
e.preventDefault()
}
el.addEventListener('close', closeHandler)
el.addEventListener('cancel', e => e.preventDefault())
return () => {
el.removeEventListener('close', closeHandler)
el.removeEventListener('cancel', closeHandler)
}
}, [props.onClose])
React.useEffect(() => {
setTimeout(() => {
(ref.current?.querySelector('leo-input') as HTMLElement)?.focus()
})
}, [])
return createPortal(<Dialog ref={ref} offsetY={props.offsetY} onClick={e => {
const rect = e.currentTarget.getBoundingClientRect();
const clickedInDialog = (
rect.top <= e.clientY &&
e.clientY <= rect.top + rect.height &&
rect.left <= e.clientX &&
e.clientX <= rect.left + rect.width
);
if (!clickedInDialog) {
doClose()
}
}}>
<SearchBox />
<SearchResults />
</Dialog>, document.body)
}
@@ -0,0 +1,21 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
import * as React from 'react';
import SearchBox from './SearchBox';
import SearchDialog from './SearchDialog';
export default function SearchPlaceholder() {
const [open, setOpen] = React.useState(false)
const [boxPos, setBoxPos] = React.useState(0)
return <>
{!open && <div onClick={e => {
setOpen(true)
setBoxPos(e.currentTarget.getBoundingClientRect().y)
}}>
<SearchBox />
</div>}
{open && <SearchDialog offsetY={boxPos} onClose={() => setOpen(false)} />}
</>
}
@@ -0,0 +1,92 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
import { color, font, radius, spacing } from '@brave/leo/tokens/css';
import { mojoString16ToString } from 'chrome://resources/js/mojo_type_util.js';
import { AutocompleteMatch } from 'gen/components/omnibox/browser/omnibox.mojom.m';
import * as React from 'react';
import styled from 'styled-components';
import Flex from '../../../common/Flex';
import { omniboxController } from './SearchBox';
interface Props {
match: AutocompleteMatch
line: number
selected: boolean
}
const Container = styled.a`
padding: ${spacing.s} ${spacing['2Xl']};
display: flex;
flex-direction: row;
align-items: center;
gap: ${spacing.l};
text-decoration: none;
&[aria-selected=true] {
background: ${color.container.interactive};
}
&:hover {
background: ${color.container.highlight};
}
`
const IconContainer = styled.div`
border-radius: ${radius.m};
width: 32px;
height: 32px;
background: ${color.container.highlight};
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
`
const FavIcon = styled.img`
width: 20px;
height: 20px;
`
const Content = styled.span`
font: ${font.large.regular};
color: ${color.text.secondary};
`
const Description = styled.span`
font: ${font.small.regular};
color: ${color.text.tertiary};
`
const Hint = styled.span`
color: ${color.text.interactive};
`
export default function SearchResult({ match, line, selected }: Props) {
const contents = mojoString16ToString(match.swapContentsAndDescription ? match.description : match.contents)
const description = mojoString16ToString(match.swapContentsAndDescription ? match.contents : match.description)
const hint = description && match.destinationUrl.url
? description
: ''
const subtitle = match.destinationUrl.url || description
return <Container href={match.destinationUrl.url} aria-selected={selected} onClick={e => {
e.preventDefault()
omniboxController.openAutocompleteMatch(line, match.destinationUrl, true, e.button, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey)
}}>
<IconContainer>
<FavIcon src={match.iconUrl} />
</IconContainer>
<Flex direction='column'>
<Content>{contents}<Hint>{hint ? ` - ${hint}` : ''}</Hint></Content>
<Description>{subtitle}</Description>
</Flex>
</Container>
}
@@ -0,0 +1,111 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// 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 https://mozilla.org/MPL/2.0/.
import { color, radius, spacing } from '@brave/leo/tokens/css'
import { AutocompleteResult, OmniboxPopupSelection } from 'gen/components/omnibox/browser/omnibox.mojom.m'
import * as React from 'react'
import styled from 'styled-components'
import { omniboxController, search } from './SearchBox'
import SearchResult from './SearchResult'
const Container = styled.div`
margin-top: ${spacing.m};
background: ${color.container.background};
border-radius: ${radius.m};
padding: ${spacing.m} 0;
gap: ${spacing.m};
display: flex;
flex-direction: column;
width: 540px;
overflow: hidden;
text-wrap: nowrap;
`
export default function SearchResults() {
const [result, setResult] = React.useState<AutocompleteResult>()
const [selectedMatch, setSelectedMatch] = React.useState<number>();
React.useEffect(() => {
const listener = (result?: AutocompleteResult) => {
setResult(result)
setSelectedMatch(prev => {
if (!result) return undefined
if (!prev) {
const defaultMatchIndex = result.matches.findIndex(r => r.allowedToBeDefaultMatch)
if (defaultMatchIndex !== -1) return defaultMatchIndex
return undefined
}
if (prev >= result.matches.length) {
return result.matches.length - 1
}
return prev
})
}
search.addResultListener(listener)
return () => search.removeResultListener(listener)
}, [])
React.useEffect(() => {
const listener = (selection: OmniboxPopupSelection) => {
console.log("Set selection:", selection)
setSelectedMatch(selection.line)
}
search.addSelectionListener(listener)
return () => {
search.removeSelectionListener(listener)
}
}, [])
React.useEffect(() => {
const keyHandler = (e: KeyboardEvent) => {
const handledKeys = ['ArrowUp', 'ArrowDown']
if (!handledKeys.includes(e.key)) {
return
}
e.preventDefault()
const direction = e.key === 'ArrowUp' ? -1 : 1
setSelectedMatch(s => {
if (!result || result.matches.length === 0) return undefined
const start = s ?? -1
const next = start + direction
if (next < 0) return (result.matches.length - 1)
if (next >= result.matches.length) return 0
return next
})
}
document.addEventListener('keydown', keyHandler)
return () => {
document.removeEventListener('keydown', keyHandler)
}
}, [result])
React.useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key !== 'Enter') return
e.preventDefault()
const match = result?.matches[selectedMatch!]
if (!match) return
omniboxController.openAutocompleteMatch(selectedMatch!, match.destinationUrl, true, 0, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey);
}
document.addEventListener('keydown', handler)
return () => {
document.removeEventListener('keydown', handler)
}
}, [result, selectedMatch])
return result && result?.matches.length ? <Container>
{result?.matches.map((r, i) => <SearchResult key={i} selected={i === selectedMatch} line={i} match={r} />)}
</Container> : null
}
@@ -23,6 +23,7 @@ import Stats from './stats'
// Helpers
import { getLocale } from '$web-common/locale'
import VisibilityTimer from '$web-common/visibilityTimer'
import { loadTimeData } from '$web-common/loadTimeData'
import isReadableOnBackground from '../../helpers/colorUtil'
// Types
@@ -46,6 +47,7 @@ import * as style from './style'
import { defaultState } from '../../storage/new_tab_storage'
const BraveNewsPeek = React.lazy(() => import('../../../brave_news/browser/resources/Peek'))
const SearchWidget = React.lazy(() => import('../../components/search/SearchPlaceholder'))
interface Props {
newTabData: NewTab.State
@@ -702,6 +704,10 @@ class NewTabPage extends React.Component<Props, State> {
</Page.Footer>
{newTabData.showToday &&
<Page.GridItemNavigationBraveNews>
{loadTimeData.getBoolean('featureFlagSearchWidget')
&& <React.Suspense fallback={null}>
<SearchWidget />
</React.Suspense>}
{defaultState.featureFlagBraveNewsFeedV2Enabled
? <React.Suspense fallback={null}>
<BraveNewsPeek/>
@@ -1,10 +0,0 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* 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/. */
#include "brave/components/omnibox/browser/brave_omnibox_client.h"
bool BraveOmniboxClient::IsAutocompleteEnabled() const {
return true;
}
@@ -1,19 +0,0 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* 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/. */
#ifndef BRAVE_COMPONENTS_OMNIBOX_BROWSER_BRAVE_OMNIBOX_CLIENT_H_
#define BRAVE_COMPONENTS_OMNIBOX_BROWSER_BRAVE_OMNIBOX_CLIENT_H_
#include "components/omnibox/browser/omnibox_client.h"
class BraveOmniboxClient : public OmniboxClient {
public:
virtual bool IsAutocompleteEnabled() const;
protected:
~BraveOmniboxClient() override {}
};
#endif // BRAVE_COMPONENTS_OMNIBOX_BROWSER_BRAVE_OMNIBOX_CLIENT_H_
-2
View File
@@ -15,8 +15,6 @@ brave_components_omnibox_browser_sources = [
"//brave/components/omnibox/browser/brave_history_url_provider.h",
"//brave/components/omnibox/browser/brave_local_history_zero_suggest_provider.cc",
"//brave/components/omnibox/browser/brave_local_history_zero_suggest_provider.h",
"//brave/components/omnibox/browser/brave_omnibox_client.cc",
"//brave/components/omnibox/browser/brave_omnibox_client.h",
"//brave/components/omnibox/browser/brave_omnibox_prefs.cc",
"//brave/components/omnibox/browser/brave_omnibox_prefs.h",
"//brave/components/omnibox/browser/brave_search_provider.cc",
@@ -1,20 +1,13 @@
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_client.h b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
index db1e94ddecdefe05ef45d090722eeee3e5eb0105..d79e108c47a13155f0f0e558564d1baf29b7606c 100644
index db1e94ddecdef..4ad6d835f8d50 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_client.h
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_client.h
@@ -17,13 +17,14 @@
#include "components/omnibox/browser/favicon_cache.h"
#include "components/omnibox/browser/omnibox.mojom-shared.h"
#include "components/omnibox/browser/omnibox_client.h"
+#include "brave/components/omnibox/browser/brave_omnibox_client.h"
class Browser;
class GURL;
@@ -23,7 +23,7 @@ class GURL;
class LocationBar;
class Profile;
-class ChromeOmniboxClient final : public OmniboxClient {
+class ChromeOmniboxClient : public BraveOmniboxClient {
+class ChromeOmniboxClient : public OmniboxClient {
public:
ChromeOmniboxClient(LocationBar* location_bar,
Browser* browser,
@@ -10,3 +10,11 @@ index feb42d8d0a7f231d162085f0076092f0a022157a..c38fe2e842f9606aa0216651334ee6a7
configs += [ "//build/config/compiler:wexit_time_destructors" ]
@@ -929,6 +930,7 @@ fuzzer_test("search_suggestion_parser_fuzzer") {
mojom("mojo_bindings") {
sources = [ "omnibox.mojom" ]
+ generate_legacy_js_bindings = true
webui_module_path = "/"
public_deps = [
+4
View File
@@ -153,9 +153,11 @@ leo_icons = [
"autoplay-on.svg",
"backward.svg",
"bar-chart.svg",
"bing-color.svg",
"bluetooth-off.svg",
"bluetooth.svg",
"brave-icon-release-color.svg",
"brave-icon-search-color.svg",
"browser-extensions.svg",
"browser-ntp-widget.svg",
"carat-down.svg",
@@ -189,6 +191,7 @@ leo_icons = [
"discover.svg",
"download-off.svg",
"download.svg",
"duckduckgo-color.svg",
"edit-box.svg",
"edit-pencil.svg",
"erase.svg",
@@ -208,6 +211,7 @@ leo_icons = [
"font-size.svg",
"forward-15.svg",
"fullscreen-on.svg",
"google-color.svg",
"graph.svg",
"grid04.svg",
"hamburger-menu.svg",