Commit 55ecd206 authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

[NTP][Realbox] Displays suggestion headers

- Propagates the suggestions' group information, i.e., group IDs,
  associated headers, and current visibilities (determined by the prefs)
  to JS.
- Renders grouped matches under their associated header.
- Allows showing/hiding grouped suggestions and updates the prefs.

screenshot/zHMaQioPKVF
screenshot/6uiibJ6BLhm

Bug: 1052514
Change-Id: I8982ca0f3ce4d84121fffc8054e9190742af73e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2145954
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarTommy Li <tommycli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758950}
parent 12b4a561
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.884 16.123a1.229 1.229 0 0 1-1.768 0l-6.25-6.428a1.3 1.3 0 0 1-.366-.91c0-.709.56-1.285 1.25-1.285.345 0 .657.144.884.377L12 13.397l5.366-5.52a1.23 1.23 0 0 1 .884-.377c.69 0 1.25.576 1.25 1.286a1.3 1.3 0 0 1-.366.909l-6.25 6.428z" fill="#5F6368"/></svg>
\ No newline at end of file
......@@ -413,6 +413,8 @@ window.chrome.embeddedSearch.newTabPage.openExtensionsPage;
window.chrome.embeddedSearch.searchBox;
/** @param {number} line */
window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch;
/** @param {number} suggestionGroupId */
window.chrome.embeddedSearch.searchBox.toggleSuggestionGroupIdVisibility;
window.chrome.embeddedSearch.searchBox.isKeyCaptureEnabled;
/** @param {number} latencyMs */
window.chrome.embeddedSearch.searchBox.logCharTypedToRepaintLatency;
......@@ -440,6 +442,7 @@ let ACMatchClassification;
* descriptionClass: !Array<!ACMatchClassification>,
* destinationUrl: string,
* fillIntoEdit: string,
* suggestionGroupId: number,
* iconUrl: string,
* imageDominantColor: string,
* imageUrl: string,
......@@ -452,9 +455,18 @@ let ACMatchClassification;
*/
let AutocompleteMatch;
/**
* @typedef {{
* header: string,
* hidden: boolean,
* }}
*/
let SuggestionGroup;
/**
* @typedef {{
* input: string,
* suggestionGroupsMap: !Object<!SuggestionGroup>,
* matches: !Array<!AutocompleteMatch>,
* }}
*/
......
......@@ -246,6 +246,7 @@ body.hide-fakebox #fakebox {
display: none;
left: 0;
overflow: hidden;
padding-bottom: 8px;
padding-top: var(--searchbox-height);
position: absolute;
right: 0;
......@@ -264,6 +265,7 @@ body.hide-fakebox #fakebox {
}
#realbox-matches a {
display: block;
font-size: 16px;
line-height: 1;
outline: none;
......@@ -278,6 +280,29 @@ body.hide-fakebox #fakebox {
white-space: nowrap;
}
#realbox-matches .header {
color: rgb(var(--GG700-rgb));
font-size: 13px;
font-weight: 500;
line-height: 16px;
padding-inline-start: 12px;
text-transform: uppercase;
}
#realbox-matches .header .remove-icon {
-webkit-mask-image: url(chevron.svg);
-webkit-transform: rotate(180deg);
background-color: var(--search-box-icon, rgb(var(--GG900-rgb)));
}
#realbox-matches .collapsed .header .remove-icon {
-webkit-transform: none;
}
#realbox-matches .collapsed a:not(.header) {
display: none;
}
[dir=rtl] #realbox-matches a {
background-position-x: calc(100% - 16px);
}
......@@ -304,10 +329,6 @@ body.hide-fakebox #fakebox {
padding-inline-end: 48px;
}
#realbox-matches a:last-of-type {
margin-bottom: 8px; /* Last result is tight with border-radius. */
}
#realbox-matches a:-webkit-any(:focus-within, .selected) .match-icon {
background-color: var(--search-box-icon-selected, rgb(117, 117, 117));
}
......
......@@ -547,6 +547,15 @@ void SearchIPCRouter::DeleteAutocompleteMatch(uint8_t line) {
delegate_->DeleteAutocompleteMatch(line);
}
void SearchIPCRouter::ToggleSuggestionGroupIdVisibility(
int32_t suggestion_group_id) {
if (!policy_->ShouldProcessToggleSuggestionGroupIdVisibility()) {
return;
}
delegate_->ToggleSuggestionGroupIdVisibility(suggestion_group_id);
}
void SearchIPCRouter::set_delegate_for_testing(Delegate* delegate) {
DCHECK(delegate);
delegate_ = delegate;
......
......@@ -182,6 +182,9 @@ class SearchIPCRouter : public content::WebContentsObserver,
bool shift_key) = 0;
virtual void DeleteAutocompleteMatch(uint8_t line) = 0;
virtual void ToggleSuggestionGroupIdVisibility(
int32_t suggestion_group_id) = 0;
};
// An interface to be implemented by consumers of SearchIPCRouter objects to
......@@ -230,6 +233,7 @@ class SearchIPCRouter : public content::WebContentsObserver,
virtual bool ShouldProcessOpenExtensionsPage() = 0;
virtual bool ShouldProcessOpenAutocompleteMatch(bool is_active_tab) = 0;
virtual bool ShouldProcessDeleteAutocompleteMatch() = 0;
virtual bool ShouldProcessToggleSuggestionGroupIdVisibility() = 0;
};
// Creates chrome::mojom::EmbeddedSearchClient connections on request.
......@@ -363,6 +367,7 @@ class SearchIPCRouter : public content::WebContentsObserver,
bool meta_key,
bool shift_key) override;
void DeleteAutocompleteMatch(uint8_t line) override;
void ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id) override;
void set_embedded_search_client_factory_for_testing(
std::unique_ptr<EmbeddedSearchClientFactory> factory) {
embedded_search_client_factory_ = std::move(factory);
......
......@@ -171,3 +171,8 @@ bool SearchIPCRouterPolicyImpl::ShouldProcessOpenAutocompleteMatch(
bool SearchIPCRouterPolicyImpl::ShouldProcessDeleteAutocompleteMatch() {
return !is_incognito_ && search::IsInstantNTP(web_contents_);
}
bool SearchIPCRouterPolicyImpl::
ShouldProcessToggleSuggestionGroupIdVisibility() {
return !is_incognito_ && search::IsInstantNTP(web_contents_);
}
......@@ -64,6 +64,7 @@ class SearchIPCRouterPolicyImpl : public SearchIPCRouter::Policy {
bool ShouldProcessOpenExtensionsPage() override;
bool ShouldProcessOpenAutocompleteMatch(bool is_active_tab) override;
bool ShouldProcessDeleteAutocompleteMatch() override;
bool ShouldProcessToggleSuggestionGroupIdVisibility() override;
// Used by unit tests.
void set_is_incognito(bool is_incognito) {
......
......@@ -126,6 +126,8 @@ class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate {
bool ctrl_key,
bool meta_key,
bool shift_key));
MOCK_METHOD1(ToggleSuggestionGroupIdVisibility,
void(int32_t suggestion_group_id));
};
class MockSearchIPCRouterPolicy : public SearchIPCRouter::Policy {
......@@ -169,6 +171,7 @@ class MockSearchIPCRouterPolicy : public SearchIPCRouter::Policy {
MOCK_METHOD0(ShouldProcessOpenExtensionsPage, bool());
MOCK_METHOD1(ShouldProcessOpenAutocompleteMatch, bool(bool));
MOCK_METHOD0(ShouldProcessDeleteAutocompleteMatch, bool());
MOCK_METHOD0(ShouldProcessToggleSuggestionGroupIdVisibility, bool());
};
class MockEmbeddedSearchClientFactory
......@@ -1104,6 +1107,7 @@ TEST_F(SearchIPCRouterTest, SendAutocompleteResultChanged) {
GetSearchIPCRouter().AutocompleteResultChanged(
chrome::mojom::AutocompleteResult::New(
base::string16(),
base::flat_map<int32_t, chrome::mojom::SuggestionGroupPtr>(),
std::vector<chrome::mojom::AutocompleteMatchPtr>()));
}
......@@ -1120,6 +1124,7 @@ TEST_F(SearchIPCRouterTest, IgnoreAutocompleteResultChanged) {
GetSearchIPCRouter().AutocompleteResultChanged(
chrome::mojom::AutocompleteResult::New(
base::string16(),
base::flat_map<int32_t, chrome::mojom::SuggestionGroupPtr>(),
std::vector<chrome::mojom::AutocompleteMatchPtr>()));
}
......@@ -1204,6 +1209,30 @@ TEST_F(SearchIPCRouterTest, IgnoreDeleteAutocompleteMatch) {
GetSearchIPCRouter().DeleteAutocompleteMatch(0u);
}
TEST_F(SearchIPCRouterTest, SendToggleSuggestionGroupIdVisibility) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
EXPECT_CALL(*mock_delegate(), ToggleSuggestionGroupIdVisibility(_)).Times(1);
EXPECT_CALL(*policy, ShouldProcessToggleSuggestionGroupIdVisibility())
.Times(1)
.WillOnce(Return(true));
GetSearchIPCRouter().ToggleSuggestionGroupIdVisibility(1u);
}
TEST_F(SearchIPCRouterTest, IgnoreToggleSuggestionGroupIdVisibility) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
EXPECT_CALL(*mock_delegate(), ToggleSuggestionGroupIdVisibility(_)).Times(0);
EXPECT_CALL(*policy, ShouldProcessToggleSuggestionGroupIdVisibility())
.Times(1)
.WillOnce(Return(false));
GetSearchIPCRouter().ToggleSuggestionGroupIdVisibility(1u);
}
TEST_F(SearchIPCRouterTest, IgnoreStopAutoComplete) {
NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
SetupMockDelegateAndPolicy();
......
......@@ -7,6 +7,7 @@
#include <memory>
#include "base/base64.h"
#include "base/containers/flat_map.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
......@@ -68,7 +69,9 @@
#include "components/omnibox/browser/omnibox_event_global_tracker.h"
#include "components/omnibox/browser/omnibox_log.h"
#include "components/omnibox/browser/omnibox_popup_model.h"
#include "components/omnibox/browser/omnibox_prefs.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "components/omnibox/browser/search_suggestion_parser.h"
#include "components/omnibox/browser/suggestion_answer.h"
#include "components/omnibox/browser/vector_icons.h"
#include "components/omnibox/common/omnibox_features.h"
......@@ -97,6 +100,22 @@
namespace {
base::flat_map<int32_t, chrome::mojom::SuggestionGroupPtr>
CreateSuggestionGroupsMap(
PrefService* prefs,
const SearchSuggestionParser::HeadersMap& headers_map) {
base::flat_map<int32_t, chrome::mojom::SuggestionGroupPtr> result_map;
for (const auto& pair : headers_map) {
chrome::mojom::SuggestionGroupPtr suggestion_group =
chrome::mojom::SuggestionGroup::New();
suggestion_group->header = pair.second;
suggestion_group->hidden =
omnibox::IsSuggestionGroupIdHidden(prefs, pair.first);
result_map.emplace(pair.first, std::move(suggestion_group));
}
return result_map;
}
std::vector<chrome::mojom::AutocompleteMatchPtr> CreateAutocompleteMatches(
const AutocompleteResult& result) {
std::vector<chrome::mojom::AutocompleteMatchPtr> matches;
......@@ -118,6 +137,7 @@ std::vector<chrome::mojom::AutocompleteMatchPtr> CreateAutocompleteMatches(
description_class.style));
}
mojom_match->destination_url = match.destination_url.spec();
mojom_match->suggestion_group_id = match.suggestion_group_id.value_or(0);
mojom_match->icon_url =
SearchTabHelper::AutocompleteMatchVectorIconToResourceName(
match.GetVectorIcon(false));
......@@ -533,6 +553,9 @@ void SearchTabHelper::OnResultChanged(AutocompleteController* controller,
ipc_router_.AutocompleteResultChanged(chrome::mojom::AutocompleteResult::New(
autocomplete_controller_->input().text(),
CreateSuggestionGroupsMap(
profile()->GetPrefs(),
autocomplete_controller_->result().headers_map()),
CreateAutocompleteMatches(autocomplete_controller_->result())));
BitmapFetcherService* bitmap_fetcher_service =
......@@ -825,6 +848,15 @@ void SearchTabHelper::StopAutocomplete(bool clear_result) {
time_of_first_autocomplete_query_ = base::TimeTicks();
}
void SearchTabHelper::ToggleSuggestionGroupIdVisibility(
int32_t suggestion_group_id) {
if (!autocomplete_controller_)
return;
omnibox::ToggleSuggestionGroupIdVisibility(profile()->GetPrefs(),
suggestion_group_id);
}
void SearchTabHelper::LogCharTypedToRepaintLatency(uint32_t latency_ms) {
UMA_HISTOGRAM_TIMES("NewTabPage.Realbox.CharTypedToRepaintLatency.ToPaint",
base::TimeDelta::FromMillisecondsD(latency_ms));
......
......@@ -147,6 +147,7 @@ class SearchTabHelper : public content::WebContentsObserver,
bool prevent_inline_autocomplete) override;
void DeleteAutocompleteMatch(uint8_t line) override;
void StopAutocomplete(bool clear_result) override;
void ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id) override;
void LogCharTypedToRepaintLatency(uint32_t latency_ms) override;
void BlocklistPromo(const std::string& promo_id) override;
void OpenExtensionsPage(double button,
......
......@@ -66,11 +66,20 @@ struct AutocompleteMatch {
bool is_search_type; // Result of AutocompleteMatch::IsSearchType().
string type; // Result of AutocompleteMatchType::ToString().
bool swap_contents_and_description;
// ID of the group the suggestion belongs to. 0 if it does not belong to any.
int32 suggestion_group_id;
bool supports_deletion;
};
struct SuggestionGroup {
mojo_base.mojom.String16 header; // Header for suggestion group.
bool hidden; // Whether suggestion group is allowed to appear in the results.
};
struct AutocompleteResult {
mojo_base.mojom.String16 input;
// Map of suggestion group IDs to their respective info.
map<int32, SuggestionGroup> suggestion_groups_map;
array<AutocompleteMatch> matches;
};
......@@ -225,6 +234,11 @@ interface EmbeddedSearch {
bool ctrl_key,
bool meta_key,
bool shift_key);
// Tells the browser to allow suggestions with the given suggestion group ID
// to appear in the results if they currently are not allowed to or to prevent
// them from appearing in the results if they are currently permitted to.
ToggleSuggestionGroupIdVisibility(int32 suggestion_group_id);
};
[Native]
......
......@@ -482,6 +482,11 @@ void SearchBox::OpenAutocompleteMatch(uint8_t line,
alt_key, ctrl_key, meta_key, shift_key);
}
void SearchBox::ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id) {
embedded_search_service_->ToggleSuggestionGroupIdVisibility(
suggestion_group_id);
}
void SearchBox::SetPageSequenceNumber(int page_seq_no) {
page_seq_no_ = page_seq_no;
}
......
......@@ -231,6 +231,11 @@ class SearchBox : public content::RenderFrameObserver,
bool meta_key,
bool shift_key);
// Tells the browser to allow suggestions with the given suggestion group ID
// to appear in the results if they currently are not allowed to or to prevent
// them from appearing in the results if they are currently permitted to.
void ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id);
bool is_focused() const { return is_focused_; }
bool is_input_in_progress() const { return is_input_in_progress_; }
bool is_key_capture_enabled() const { return is_key_capture_enabled_; }
......
......@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/i18n/rtl.h"
#include "base/json/json_writer.h"
#include "base/json/string_escape.h"
......@@ -452,6 +453,7 @@ base::Value CreateAutocompleteMatches(
}
dict.SetKey("descriptionClass", std::move(description_class));
dict.SetStringKey("destinationUrl", match->destination_url);
dict.SetIntKey("suggestionGroupId", match->suggestion_group_id);
dict.SetStringKey("inlineAutocompletion", match->inline_autocompletion);
dict.SetBoolKey("isSearchType", match->is_search_type);
dict.SetStringKey("fillIntoEdit", match->fill_into_edit);
......@@ -467,6 +469,20 @@ base::Value CreateAutocompleteMatches(
return list;
}
base::Value CreateSuggestionGroupsMap(
const base::flat_map<int32_t, chrome::mojom::SuggestionGroupPtr>&
suggestion_groups_map) {
base::Value result_map(base::Value::Type::DICTIONARY);
for (const auto& pair : suggestion_groups_map) {
base::Value suggestion_group(base::Value::Type::DICTIONARY);
suggestion_group.SetStringKey("header", pair.second->header);
suggestion_group.SetBoolKey("hidden", pair.second->hidden);
result_map.SetPath(base::NumberToString(pair.first),
std::move(suggestion_group));
}
return result_map;
}
static const char kDispatchFocusChangedScript[] =
"if (window.chrome &&"
" window.chrome.embeddedSearch &&"
......@@ -640,6 +656,7 @@ class SearchBoxBindings : public gin::Wrappable<SearchBoxBindings> {
bool ctrl_key,
bool meta_key,
bool shift_key);
static void ToggleSuggestionGroupIdVisibility(int32_t suggestion_group_id);
DISALLOW_COPY_AND_ASSIGN(SearchBoxBindings);
};
......@@ -659,17 +676,19 @@ gin::ObjectTemplateBuilder SearchBoxBindings::GetObjectTemplateBuilder(
&SearchBoxBindings::IsKeyCaptureEnabled)
.SetMethod("deleteAutocompleteMatch",
&SearchBoxBindings::DeleteAutocompleteMatch)
.SetMethod("logCharTypedToRepaintLatency",
&SearchBoxBindings::LogCharTypedToRepaintLatency)
.SetMethod("openAutocompleteMatch",
&SearchBoxBindings::OpenAutocompleteMatch)
.SetMethod("paste", &SearchBoxBindings::Paste)
.SetMethod("queryAutocomplete", &SearchBoxBindings::QueryAutocomplete)
.SetMethod("stopAutocomplete", &SearchBoxBindings::StopAutocomplete)
.SetMethod("logCharTypedToRepaintLatency",
&SearchBoxBindings::LogCharTypedToRepaintLatency)
.SetMethod("startCapturingKeyStrokes",
&SearchBoxBindings::StartCapturingKeyStrokes)
.SetMethod("stopCapturingKeyStrokes",
&SearchBoxBindings::StopCapturingKeyStrokes);
&SearchBoxBindings::StopCapturingKeyStrokes)
.SetMethod("toggleSuggestionGroupIdVisibility",
&SearchBoxBindings::ToggleSuggestionGroupIdVisibility);
}
// static
......@@ -698,6 +717,15 @@ void SearchBoxBindings::DeleteAutocompleteMatch(int line) {
search_box->DeleteAutocompleteMatch(line);
}
// static
void SearchBoxBindings::ToggleSuggestionGroupIdVisibility(
int32_t suggestion_group_id) {
SearchBox* search_box = GetSearchBoxForCurrentContext();
if (!search_box)
return;
search_box->ToggleSuggestionGroupIdVisibility(suggestion_group_id);
}
// static
void SearchBoxBindings::OpenAutocompleteMatch(
int line,
......@@ -1502,6 +1530,8 @@ void SearchBoxExtension::DispatchAutocompleteResultChanged(
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetStringKey("input", result->input);
dict.SetKey("matches", CreateAutocompleteMatches(result->matches));
dict.SetKey("suggestionGroupsMap",
CreateSuggestionGroupsMap(result->suggestion_groups_map));
std::string json;
base::JSONWriter::Write(dict, &json);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment