1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/font_prewarmer_tab_helper.h"
10 #include "base/memory/raw_ptr.h"
11 #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"
16 #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
17 #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"
27 #include "content/public/browser/child_process_host.h"
28 #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"
40 const char kSearchResultsPagePrimaryFontsPref[] =
41 "cached_fonts.search_results_page.primary";
42 const char kSearchResultsPageFallbackFontsPref[] =
43 "cached_fonts.search_results_page.fallback";
45 // Key used to associate FontPrewarmerProfileState with BrowserContext.
46 const void* const kUserDataKey = &kUserDataKey;
48 // Returns the font names previously stored to the specified key.
49 std::vector<std::string> GetFontNamesFromPrefsForKey(Profile* profile,
50 const char* pref_name) {
51 const base::Value::List& font_name_list =
52 profile->GetPrefs()->GetList(pref_name);
53 if (font_name_list.empty())
56 std::vector<std::string> font_names;
57 for (const auto& font_name_value : font_name_list) {
58 if (const std::string* font_name = font_name_value.GetIfString())
59 font_names.push_back(*font_name);
64 // Saves font names to prefs.
65 void SaveFontNamesToPref(Profile* profile,
66 const char* pref_name,
67 const std::vector<std::string>& font_family_names) {
68 base::Value::List font_family_names_values;
69 for (auto& name : font_family_names)
70 font_family_names_values.Append(name);
71 profile->GetPrefs()->SetList(pref_name, std::move(font_family_names_values));
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.
77 class FontPrewarmerCoordinator : public base::SupportsUserData::Data,
78 public content::RenderProcessHostObserver {
80 using RemoteFontPrewarmer = mojo::Remote<chrome::mojom::FontPrewarmer>;
82 explicit FontPrewarmerCoordinator(Profile* profile) : profile_(profile) {}
84 FontPrewarmerCoordinator(const FontPrewarmerCoordinator&) = delete;
85 FontPrewarmerCoordinator& operator=(const FontPrewarmerCoordinator&) = delete;
87 ~FontPrewarmerCoordinator() override {
88 for (content::RenderProcessHost* rph : prewarmed_hosts_)
89 rph->RemoveObserver(this);
92 static FontPrewarmerCoordinator& ForProfile(Profile* profile) {
93 FontPrewarmerCoordinator* instance = static_cast<FontPrewarmerCoordinator*>(
94 profile->GetUserData(kUserDataKey));
96 profile->SetUserData(kUserDataKey,
97 std::make_unique<FontPrewarmerCoordinator>(profile));
98 instance = static_cast<FontPrewarmerCoordinator*>(
99 profile->GetUserData(kUserDataKey));
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))
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);
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())
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));
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)));
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
150 SaveFontNamesToPref(profile_, kSearchResultsPagePrimaryFontsPref,
151 primary_family_names);
152 SaveFontNamesToPref(profile_, kSearchResultsPageFallbackFontsPref,
153 fallback_family_names);
156 // content::RenderProcessHostObserver:
157 void RenderProcessHostDestroyed(content::RenderProcessHost* host) override {
158 host->RemoveObserver(this);
159 prewarmed_hosts_.erase(host);
162 raw_ptr<Profile> profile_;
163 // Set of hosts that were requested to be prewarmed.
164 std::set<content::RenderProcessHost*> prewarmed_hosts_;
165 base::WeakPtrFactory<FontPrewarmerCoordinator> weak_factory_{this};
171 void FontPrewarmerTabHelper::RegisterProfilePrefs(
172 user_prefs::PrefRegistrySyncable* registry) {
173 registry->RegisterListPref(kSearchResultsPagePrimaryFontsPref);
174 registry->RegisterListPref(kSearchResultsPageFallbackFontsPref);
177 FontPrewarmerTabHelper::~FontPrewarmerTabHelper() = default;
179 FontPrewarmerTabHelper::FontPrewarmerTabHelper(
180 content::WebContents* web_contents)
181 : content::WebContentsObserver(web_contents),
182 content::WebContentsUserData<FontPrewarmerTabHelper>(*web_contents) {}
185 std::string FontPrewarmerTabHelper::GetSearchResultsPagePrimaryFontsPref() {
186 return kSearchResultsPagePrimaryFontsPref;
190 std::vector<std::string> FontPrewarmerTabHelper::GetPrimaryFontNames(
192 return GetFontNamesFromPrefsForKey(profile,
193 kSearchResultsPagePrimaryFontsPref);
196 Profile* FontPrewarmerTabHelper::GetProfile() {
197 return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
200 bool FontPrewarmerTabHelper::IsSearchResultsPageNavigation(
201 content::NavigationHandle* navigation_handle) {
202 if (!navigation_handle->IsInPrimaryMainFrame())
205 TemplateURLService* template_url_service =
206 TemplateURLServiceFactory::GetForProfile(GetProfile());
207 return template_url_service &&
208 template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
209 navigation_handle->GetURL());
212 void FontPrewarmerTabHelper::DidStartNavigation(
213 content::NavigationHandle* navigation_handle) {
214 if (!IsSearchResultsPageNavigation(navigation_handle))
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();
223 expected_render_process_host_id_ = expected_render_process_host_id;
224 content::RenderProcessHost* rph =
225 content::RenderProcessHost::FromID(expected_render_process_host_id);
227 FontPrewarmerCoordinator::ForProfile(GetProfile()).SendFontsToPrewarm(rph);
231 void FontPrewarmerTabHelper::ReadyToCommitNavigation(
232 content::NavigationHandle* navigation_handle) {
233 if (!IsSearchResultsPageNavigation(navigation_handle))
236 content::RenderFrameHost* rfh = navigation_handle->GetRenderFrameHost();
238 FontPrewarmerCoordinator& coordinator =
239 FontPrewarmerCoordinator::ForProfile(GetProfile());
240 if (expected_render_process_host_id_ != rfh->GetProcess()->GetID())
241 coordinator.SendFontsToPrewarm(rfh->GetProcess());
242 coordinator.RequestFonts(rfh);
245 WEB_CONTENTS_USER_DATA_KEY_IMPL(FontPrewarmerTabHelper);