1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
ash / quick_insert / model / quick_insert_link_suggester.cc [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/quick_insert/model/quick_insert_link_suggester.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
#include "ash/quick_insert/quick_insert_search_result.h"
#include "base/barrier_callback.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_service.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
namespace {
constexpr int kRecentDayRange = 7;
// Returns true if the given link is likely to be personalized to the user,
// which makes it unlikely that the URL works as intended when shared.
bool IsLinkLikelyPersonalized(const GURL& url) {
// TODO: b/366237507 - Add more domains.
static constexpr std::pair<std::string_view, std::string_view> kBlocklist[] =
{
{"mail.google.com", "/chat/"},
{"mail.google.com", "/mail/"},
};
for (const auto& [domain, path_prefix] : kBlocklist) {
if (url.DomainIs(domain) && base::StartsWith(url.path(), path_prefix)) {
return true;
}
}
return false;
}
} // namespace
QuickInsertLinkSuggester::QuickInsertLinkSuggester() = default;
QuickInsertLinkSuggester::~QuickInsertLinkSuggester() = default;
void QuickInsertLinkSuggester::GetSuggestedLinks(
history::HistoryService* history_service,
favicon::FaviconService* favicon_service,
size_t max_links,
SuggestedLinksCallback callback) {
CHECK(history_service);
history::QueryOptions options;
options.max_count = max_links;
options.SetRecentDayRange(kRecentDayRange);
history_service->QueryHistory(
std::u16string(), options,
base::BindOnce(&QuickInsertLinkSuggester::OnGetBrowsingHistory,
weak_factory_.GetWeakPtr(), favicon_service,
std::move(callback)),
&history_query_tracker_);
}
void QuickInsertLinkSuggester::OnGetBrowsingHistory(
favicon::FaviconService* favicon_service,
SuggestedLinksCallback callback,
history::QueryResults results) {
std::vector<history::URLResult> filtered_results;
base::ranges::copy_if(results, std::back_inserter(filtered_results),
[](const history::URLResult result) {
if (IsLinkLikelyPersonalized(result.url())) {
return false;
}
return result.url().SchemeIsHTTPOrHTTPS();
});
if (favicon_service) {
favicon_query_trackers_ =
std::vector<base::CancelableTaskTracker>(filtered_results.size());
auto barrier_callback = base::BarrierCallback<ash::QuickInsertSearchResult>(
/*num_callbacks=*/filtered_results.size(),
/*done_callback=*/std::move(callback));
for (size_t i = 0; i < filtered_results.size(); ++i) {
favicon_service->GetFaviconImageForPageURL(
filtered_results[i].url(),
base::BindOnce(&QuickInsertLinkSuggester::OnGetFaviconImage,
weak_factory_.GetWeakPtr(), filtered_results[i],
barrier_callback),
&favicon_query_trackers_[i]);
}
} else {
// Fallback to placeholder icon if favicon service is not available.
std::vector<ash::QuickInsertSearchResult> quick_insert_search_results;
for (const auto& result : filtered_results) {
quick_insert_search_results.push_back(
ash::QuickInsertBrowsingHistoryResult(
result.url(), result.title(),
ui::ImageModel::FromVectorIcon(ash::kOmniboxGenericIcon,
cros_tokens::kCrosSysOnSurface),
false));
}
std::move(callback).Run(quick_insert_search_results);
}
}
void QuickInsertLinkSuggester::OnGetFaviconImage(
history::URLResult result,
base::OnceCallback<void(ash::QuickInsertSearchResult)> callback,
const favicon_base::FaviconImageResult& favicon_image_result) {
std::move(callback).Run(ash::QuickInsertBrowsingHistoryResult(
result.url(), result.title(),
favicon_image_result.image.IsEmpty()
? ui::ImageModel::FromVectorIcon(ash::kOmniboxGenericIcon,
cros_tokens::kCrosSysOnSurface)
: ui::ImageModel::FromImage(favicon_image_result.image),
false));
}