blob: d7e12511b39425f47b644dfb0b61303aa94aa391 [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2021 The Chromium Authors
Scott Violet3ff87d062021-12-21 06:10:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/font_prewarmer_tab_helper.h"
6
7#include <set>
8#include <string>
9
Keishi Hattorie175ac52022-06-07 06:24:5710#include "base/memory/raw_ptr.h"
Scott Violet3ff87d062021-12-21 06:10:1011#include "base/memory/weak_ptr.h"
12#include "base/values.h"
13#include "build/build_config.h"
14#include "chrome/browser/history/history_service_factory.h"
15#include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
Sreeja Kamishettyab9948ab2022-07-20 23:20:2316#include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
Scott Violet3ff87d062021-12-21 06:10:1017#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/search_engines/template_url_service_factory.h"
19#include "chrome/common/font_prewarmer.mojom.h"
20#include "components/history/content/browser/history_context_helper.h"
21#include "components/history/core/browser/history_constants.h"
22#include "components/history/core/browser/history_service.h"
23#include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
24#include "components/pref_registry/pref_registry_syncable.h"
25#include "components/prefs/pref_service.h"
26#include "components/search_engines/template_url_service.h"
Will Harriscd57b832023-01-05 20:03:1027#include "content/public/browser/child_process_host.h"
Scott Violet3ff87d062021-12-21 06:10:1028#include "content/public/browser/navigation_entry.h"
29#include "content/public/browser/navigation_handle.h"
30#include "content/public/browser/render_frame_host.h"
31#include "content/public/browser/render_process_host.h"
32#include "content/public/browser/render_process_host_observer.h"
33#include "content/public/browser/web_contents.h"
34#include "mojo/public/cpp/bindings/remote.h"
35#include "third_party/abseil-cpp/absl/types/optional.h"
36#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
37
38namespace {
39
40const char kSearchResultsPagePrimaryFontsPref[] =
41 "cached_fonts.search_results_page.primary";
42const char kSearchResultsPageFallbackFontsPref[] =
43 "cached_fonts.search_results_page.fallback";
44
45// Key used to associate FontPrewarmerProfileState with BrowserContext.
46const void* const kUserDataKey = &kUserDataKey;
47
48// Returns the font names previously stored to the specified key.
49std::vector<std::string> GetFontNamesFromPrefsForKey(Profile* profile,
50 const char* pref_name) {
Matt Menke26ec8262022-07-18 19:12:5351 const base::Value::List& font_name_list =
Roland Bock6edc4b72022-09-12 17:33:0152 profile->GetPrefs()->GetList(pref_name);
Matt Menke26ec8262022-07-18 19:12:5353 if (font_name_list.empty())
Scott Violet3ff87d062021-12-21 06:10:1054 return {};
55
56 std::vector<std::string> font_names;
Matt Menke26ec8262022-07-18 19:12:5357 for (const auto& font_name_value : font_name_list) {
Scott Violet3ff87d062021-12-21 06:10:1058 if (const std::string* font_name = font_name_value.GetIfString())
59 font_names.push_back(*font_name);
60 }
61 return font_names;
62}
63
64// Saves font names to prefs.
65void SaveFontNamesToPref(Profile* profile,
66 const char* pref_name,
67 const std::vector<std::string>& font_family_names) {
Matt Menke26ec8262022-07-18 19:12:5368 base::Value::List font_family_names_values;
Scott Violet3ff87d062021-12-21 06:10:1069 for (auto& name : font_family_names)
Matt Menke26ec8262022-07-18 19:12:5370 font_family_names_values.Append(name);
71 profile->GetPrefs()->SetList(pref_name, std::move(font_family_names_values));
Scott Violet3ff87d062021-12-21 06:10:1072}
73
74// FontPrewarmerCoordinator is responsible for coordinating with the renderer
75// to request the fonts used by a page as well as prewarm the last set of fonts
76// used. There is one FontPrewarmerCoordinator per Profile.
77class FontPrewarmerCoordinator : public base::SupportsUserData::Data,
78 public content::RenderProcessHostObserver {
79 public:
80 using RemoteFontPrewarmer = mojo::Remote<chrome::mojom::FontPrewarmer>;
81
82 explicit FontPrewarmerCoordinator(Profile* profile) : profile_(profile) {}
83
84 FontPrewarmerCoordinator(const FontPrewarmerCoordinator&) = delete;
85 FontPrewarmerCoordinator& operator=(const FontPrewarmerCoordinator&) = delete;
86
87 ~FontPrewarmerCoordinator() override {
88 for (content::RenderProcessHost* rph : prewarmed_hosts_)
89 rph->RemoveObserver(this);
90 }
91
92 static FontPrewarmerCoordinator& ForProfile(Profile* profile) {
93 FontPrewarmerCoordinator* instance = static_cast<FontPrewarmerCoordinator*>(
94 profile->GetUserData(kUserDataKey));
95 if (!instance) {
96 profile->SetUserData(kUserDataKey,
97 std::make_unique<FontPrewarmerCoordinator>(profile));
98 instance = static_cast<FontPrewarmerCoordinator*>(
99 profile->GetUserData(kUserDataKey));
100 }
101 return *instance;
102 }
103
104 // Requests the renderer to prewarm the last set of fonts used for displaying
105 // a search page. Prewarming is done at most once per RenderProcessHost.
106 void SendFontsToPrewarm(content::RenderProcessHost* rph) {
107 // Only need to prewarm a particular host once.
108 if (prewarmed_hosts_.count(rph))
109 return;
110
111 // The following code may early out. Insert the entry to ensure an early out
112 // doesn't attempt to send the fonts again.
113 prewarmed_hosts_.insert(rph);
114 rph->AddObserver(this);
115
116 std::vector<std::string> primary_font_names = GetFontNamesFromPrefsForKey(
117 profile_, kSearchResultsPagePrimaryFontsPref);
118 std::vector<std::string> fallback_font_names = GetFontNamesFromPrefsForKey(
119 profile_, kSearchResultsPageFallbackFontsPref);
120 if (primary_font_names.empty() && fallback_font_names.empty())
121 return;
122
123 RemoteFontPrewarmer remote_font_prewarmer;
124 rph->BindReceiver(remote_font_prewarmer.BindNewPipeAndPassReceiver());
125 remote_font_prewarmer->PrewarmFonts(std::move(primary_font_names),
126 std::move(fallback_font_names));
127 }
128
129 // Requests the set of fonts needed to display a search page from `rfh`.
130 void RequestFonts(content::RenderFrameHost* rfh) {
131 mojo::AssociatedRemote<chrome::mojom::RenderFrameFontFamilyAccessor>
132 font_family_accessor;
133 rfh->GetRemoteAssociatedInterfaces()->GetInterface(&font_family_accessor);
134 auto* font_family_accessor_raw = font_family_accessor.get();
135 // Pass ownership of the remote to the callback as otherwise the callback
136 // will never be run (because the mojo connection was destroyed).
137 font_family_accessor_raw->GetFontFamilyNames(base::BindOnce(
138 &FontPrewarmerCoordinator::OnGotFontsForFrame,
139 weak_factory_.GetWeakPtr(), std::move(font_family_accessor)));
140 }
141
142 private:
143 void OnGotFontsForFrame(
144 mojo::AssociatedRemote<chrome::mojom::RenderFrameFontFamilyAccessor>
145 font_family_accessor,
146 const std::vector<std::string>& primary_family_names,
147 const std::vector<std::string>& fallback_family_names) {
148 // TODO(sky): add some metrics here so that we know how often the
149 // fonts change.
150 SaveFontNamesToPref(profile_, kSearchResultsPagePrimaryFontsPref,
151 primary_family_names);
152 SaveFontNamesToPref(profile_, kSearchResultsPageFallbackFontsPref,
153 fallback_family_names);
154 }
155
156 // content::RenderProcessHostObserver:
157 void RenderProcessHostDestroyed(content::RenderProcessHost* host) override {
158 host->RemoveObserver(this);
159 prewarmed_hosts_.erase(host);
160 }
161
Keishi Hattorie175ac52022-06-07 06:24:57162 raw_ptr<Profile> profile_;
Scott Violet3ff87d062021-12-21 06:10:10163 // Set of hosts that were requested to be prewarmed.
164 std::set<content::RenderProcessHost*> prewarmed_hosts_;
165 base::WeakPtrFactory<FontPrewarmerCoordinator> weak_factory_{this};
166};
167
168} // namespace
169
170// static
171void FontPrewarmerTabHelper::RegisterProfilePrefs(
172 user_prefs::PrefRegistrySyncable* registry) {
173 registry->RegisterListPref(kSearchResultsPagePrimaryFontsPref);
174 registry->RegisterListPref(kSearchResultsPageFallbackFontsPref);
175}
176
177FontPrewarmerTabHelper::~FontPrewarmerTabHelper() = default;
178
179FontPrewarmerTabHelper::FontPrewarmerTabHelper(
180 content::WebContents* web_contents)
181 : content::WebContentsObserver(web_contents),
182 content::WebContentsUserData<FontPrewarmerTabHelper>(*web_contents) {}
183
184// static
185std::string FontPrewarmerTabHelper::GetSearchResultsPagePrimaryFontsPref() {
186 return kSearchResultsPagePrimaryFontsPref;
187}
188
189// static
190std::vector<std::string> FontPrewarmerTabHelper::GetPrimaryFontNames(
191 Profile* profile) {
192 return GetFontNamesFromPrefsForKey(profile,
193 kSearchResultsPagePrimaryFontsPref);
194}
195
196Profile* FontPrewarmerTabHelper::GetProfile() {
197 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
198}
199
200bool FontPrewarmerTabHelper::IsSearchResultsPageNavigation(
201 content::NavigationHandle* navigation_handle) {
202 if (!navigation_handle->IsInPrimaryMainFrame())
203 return false;
204
205 TemplateURLService* template_url_service =
206 TemplateURLServiceFactory::GetForProfile(GetProfile());
207 return template_url_service &&
208 template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
209 navigation_handle->GetURL());
210}
211
Scott Violetef4f8482021-12-21 21:49:37212void FontPrewarmerTabHelper::DidStartNavigation(
213 content::NavigationHandle* navigation_handle) {
214 if (!IsSearchResultsPageNavigation(navigation_handle))
215 return;
216
217 const int expected_render_process_host_id =
218 navigation_handle->GetExpectedRenderProcessHostId();
219 if (expected_render_process_host_id ==
220 content::ChildProcessHost::kInvalidUniqueID) {
221 expected_render_process_host_id_.reset();
222 } else {
223 expected_render_process_host_id_ = expected_render_process_host_id;
224 content::RenderProcessHost* rph =
225 content::RenderProcessHost::FromID(expected_render_process_host_id);
226 DCHECK(rph);
227 FontPrewarmerCoordinator::ForProfile(GetProfile()).SendFontsToPrewarm(rph);
228 }
229}
230
Scott Violet3ff87d062021-12-21 06:10:10231void FontPrewarmerTabHelper::ReadyToCommitNavigation(
232 content::NavigationHandle* navigation_handle) {
233 if (!IsSearchResultsPageNavigation(navigation_handle))
234 return;
235
236 content::RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
237 DCHECK(rfh);
238 FontPrewarmerCoordinator& coordinator =
239 FontPrewarmerCoordinator::ForProfile(GetProfile());
Scott Violetef4f8482021-12-21 21:49:37240 if (expected_render_process_host_id_ != rfh->GetProcess()->GetID())
241 coordinator.SendFontsToPrewarm(rfh->GetProcess());
Scott Violet3ff87d062021-12-21 06:10:10242 coordinator.RequestFonts(rfh);
243}
244
245WEB_CONTENTS_USER_DATA_KEY_IMPL(FontPrewarmerTabHelper);