Skip to content

Commit

Permalink
Implement render-blocking fonts
Browse files Browse the repository at this point in the history
This patch implements render-blocking fonts following the doc [1]:
1. Sets a timer that starts on the first font preload, and fires after
   a fixed amount of time since navigation
2. Sets a timer that starts when rendering can be unblocked if fonts
   are not render-blocking, and fires at a fixed delay after this
   event (i.e., limiting font-caused FCP delay)
3. Make all fonts non-render-blocking when either timer fires

Note that the first part functionally conflicts with an existing
logic that shortly blocks rendering for 50ms since the first font preload, which was introduced in the previous project [2]. Hence,
they are implemented to share the same timer.

[1] https://docs.google.com/document/d/11xC1yC735bx2b_ybnLAUa4DeAPClt_v7EZktJw7KnFo/edit?resourcekey=0-DCQxDfZyJgNvmBnBHt247w
[2] https://crbug.com/1040632

Bug: 1412861
Change-Id: I0a9aa42e22012930fa712bd58d806a91e09a54a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4237941
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1105813}
  • Loading branch information
xiaochengh authored and Chromium LUCI CQ committed Feb 15, 2023
1 parent 68c207f commit bcdc49b
Show file tree
Hide file tree
Showing 9 changed files with 365 additions and 26 deletions.
14 changes: 14 additions & 0 deletions third_party/blink/common/features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1732,5 +1732,19 @@ BASE_FEATURE(kExtendScriptResourceLifetime,
"ExtendScriptResourceLifetime",
base::FEATURE_DISABLED_BY_DEFAULT);

BASE_FEATURE(kRenderBlockingFonts,
"RenderBlockingFonts",
base::FEATURE_DISABLED_BY_DEFAULT);

const base::FeatureParam<int> kMaxBlockingTimeMsForRenderBlockingFonts(
&features::kRenderBlockingFonts,
"max-blocking-time",
1500);

const base::FeatureParam<int> kMaxFCPDelayMsForRenderBlockingFonts(
&features::kRenderBlockingFonts,
"max-fcp-delay",
100);

} // namespace features
} // namespace blink
12 changes: 12 additions & 0 deletions third_party/blink/public/common/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,18 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
// See https://crbug.com/1393246.
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kExtendScriptResourceLifetime);

// Makes preloaded fonts render-blocking up to the limits below.
// See https://crbug.com/1412861
BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kRenderBlockingFonts);

// Max milliseconds from navigation start that fonts can block rendering.
BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
kMaxBlockingTimeMsForRenderBlockingFonts;

// Max milliseconds that font are allowed to delay of FCP.
BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
kMaxFCPDelayMsForRenderBlockingFonts;

} // namespace features
} // namespace blink

Expand Down
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3552,6 +3552,10 @@ void Document::WillInsertBody() {
supplement->WillInsertBody();
}

if (render_blocking_resource_manager_) {
render_blocking_resource_manager_->WillInsertDocumentBody();
}

// If we get to the <body> try to resume commits since we should have content
// to paint now.
// TODO(esprehn): Is this really optimal? We might start producing frames
Expand Down
16 changes: 16 additions & 0 deletions third_party/blink/renderer/core/loader/document_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3022,6 +3022,22 @@ base::TimeDelta DocumentLoader::RemainingTimeToLCPLimit() const {
return base::TimeDelta();
}

base::TimeDelta
DocumentLoader::RemainingTimeToRenderBlockingFontMaxBlockingTime() const {
DCHECK(base::FeatureList::IsEnabled(features::kRenderBlockingFonts));
// We shouldn't call this function before navigation start
DCHECK(!document_load_timing_.NavigationStart().is_null());
base::TimeTicks max_blocking_time =
document_load_timing_.NavigationStart() +
base::Milliseconds(
features::kMaxBlockingTimeMsForRenderBlockingFonts.Get());
base::TimeTicks now = clock_->NowTicks();
if (now < max_blocking_time) {
return max_blocking_time - now;
}
return base::TimeDelta();
}

mojom::blink::ContentSecurityNotifier&
DocumentLoader::GetContentSecurityNotifier() {
if (!content_security_notifier_.is_bound()) {
Expand Down
5 changes: 5 additions & 0 deletions third_party/blink/renderer/core/loader/document_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
// the LCP limit. See crbug.com/1065508 for details.
base::TimeDelta RemainingTimeToLCPLimit() const;

// We are experimenting the idea of making preloaded fonts render-blocking up
// to a certain amount of time after navigation starts. This returns the
// remaining time to that time limit. See crbug.com/1412861 for details.
base::TimeDelta RemainingTimeToRenderBlockingFontMaxBlockingTime() const;

mojom::blink::ContentSecurityNotifier& GetContentSecurityNotifier();

// Returns the value of the text fragment token and then resets it to false
Expand Down
2 changes: 1 addition & 1 deletion third_party/blink/renderer/core/loader/preload_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ Resource* PreloadHelper::StartPreload(ResourceType type,
resource = FontResource::Fetch(params, resource_fetcher, nullptr);
if (document.GetRenderBlockingResourceManager()) {
document.GetRenderBlockingResourceManager()
->EnsureStartFontPreloadTimer();
->EnsureStartFontPreloadMaxBlockingTimer();
}
break;
case ResourceType::kAudio:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

#include "third_party/blink/renderer/core/loader/render_blocking_resource_manager.h"

#include "base/feature_list.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/core/css/font_face.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/pending_link_preload.h"
#include "third_party/blink/renderer/core/script/script_element_base.h"

Expand Down Expand Up @@ -51,23 +54,24 @@ class ImperativeFontLoadFinishedCallback final

RenderBlockingResourceManager::RenderBlockingResourceManager(Document& document)
: document_(document),
font_preload_timer_(
font_preload_max_blocking_timer_(
document.GetTaskRunner(TaskType::kInternalFrameLifecycleControl),
this,
&RenderBlockingResourceManager::FontPreloadingTimerFired),
font_preload_max_fcp_delay_timer_(
document.GetTaskRunner(TaskType::kInternalFrameLifecycleControl),
this,
&RenderBlockingResourceManager::FontPreloadingTimerFired),
font_preload_timeout_(kMaxRenderingDelayForFontPreloads) {}

void RenderBlockingResourceManager::AddPendingFontPreload(
const PendingLinkPreload& link) {
if (font_preload_timer_has_fired_) {
if (font_preload_timer_has_fired_ || document_->body()) {
return;
}

if (document_->body())
return;

pending_font_preloads_.insert(&link);
EnsureStartFontPreloadTimer();
EnsureStartFontPreloadMaxBlockingTimer();
}

void RenderBlockingResourceManager::AddImperativeFontLoading(
Expand All @@ -82,7 +86,7 @@ void RenderBlockingResourceManager::AddImperativeFontLoading(
MakeGarbageCollected<ImperativeFontLoadFinishedCallback>(*document_);
font_face->AddCallback(callback);
++imperative_font_loading_count_;
EnsureStartFontPreloadTimer();
EnsureStartFontPreloadMaxBlockingTimer();
}

void RenderBlockingResourceManager::RemovePendingFontPreload(
Expand All @@ -92,23 +96,34 @@ void RenderBlockingResourceManager::RemovePendingFontPreload(
return;
}
pending_font_preloads_.erase(iter);
document_->RenderBlockingResourceUnblocked();
RenderBlockingResourceUnblocked();
}

void RenderBlockingResourceManager::RemoveImperativeFontLoading() {
if (font_preload_timer_has_fired_)
return;
DCHECK(imperative_font_loading_count_);
--imperative_font_loading_count_;
document_->RenderBlockingResourceUnblocked();
RenderBlockingResourceUnblocked();
}

void RenderBlockingResourceManager::EnsureStartFontPreloadTimer() {
if (!font_preload_timer_.IsActive())
font_preload_timer_.StartOneShot(font_preload_timeout_, FROM_HERE);
void RenderBlockingResourceManager::EnsureStartFontPreloadMaxBlockingTimer() {
if (font_preload_timer_has_fired_ ||
font_preload_max_blocking_timer_.IsActive()) {
return;
}
base::TimeDelta timeout =
base::FeatureList::IsEnabled(features::kRenderBlockingFonts)
? document_->Loader()
->RemainingTimeToRenderBlockingFontMaxBlockingTime()
: font_preload_timeout_;
font_preload_max_blocking_timer_.StartOneShot(timeout, FROM_HERE);
}

void RenderBlockingResourceManager::FontPreloadingTimerFired(TimerBase*) {
if (font_preload_timer_has_fired_) {
return;
}
font_preload_timer_has_fired_ = true;
pending_font_preloads_.clear();
imperative_font_loading_count_ = 0;
Expand All @@ -117,20 +132,21 @@ void RenderBlockingResourceManager::FontPreloadingTimerFired(TimerBase*) {

void RenderBlockingResourceManager::SetFontPreloadTimeoutForTest(
base::TimeDelta timeout) {
if (font_preload_timer_.IsActive()) {
font_preload_timer_.Stop();
font_preload_timer_.StartOneShot(timeout, FROM_HERE);
if (font_preload_max_blocking_timer_.IsActive()) {
font_preload_max_blocking_timer_.Stop();
font_preload_max_blocking_timer_.StartOneShot(timeout, FROM_HERE);
}
font_preload_timeout_ = timeout;
}

void RenderBlockingResourceManager::DisableFontPreloadTimeoutForTest() {
if (font_preload_timer_.IsActive())
font_preload_timer_.Stop();
if (font_preload_max_blocking_timer_.IsActive()) {
font_preload_max_blocking_timer_.Stop();
}
}

bool RenderBlockingResourceManager::FontPreloadTimerIsActiveForTest() const {
return font_preload_timer_.IsActive();
return font_preload_max_blocking_timer_.IsActive();
}

bool RenderBlockingResourceManager::AddPendingStylesheet(
Expand All @@ -148,7 +164,7 @@ bool RenderBlockingResourceManager::RemovePendingStylesheet(
if (iter == pending_stylesheet_owner_nodes_.end())
return false;
pending_stylesheet_owner_nodes_.erase(iter);
document_->RenderBlockingResourceUnblocked();
RenderBlockingResourceUnblocked();
return true;
}

Expand All @@ -165,15 +181,42 @@ void RenderBlockingResourceManager::RemovePendingScript(
if (iter == pending_scripts_.end())
return;
pending_scripts_.erase(iter);
RenderBlockingResourceUnblocked();
}

void RenderBlockingResourceManager::WillInsertDocumentBody() {
if (base::FeatureList::IsEnabled(features::kRenderBlockingFonts) &&
!HasNonFontRenderBlockingResources() && HasRenderBlockingFonts()) {
EnsureStartFontPreloadMaxFCPDelayTimer();
}
}

void RenderBlockingResourceManager::RenderBlockingResourceUnblocked() {
document_->RenderBlockingResourceUnblocked();
if (base::FeatureList::IsEnabled(features::kRenderBlockingFonts) &&
!HasNonFontRenderBlockingResources() && HasRenderBlockingFonts() &&
document_->body()) {
EnsureStartFontPreloadMaxFCPDelayTimer();
}
}

void RenderBlockingResourceManager::EnsureStartFontPreloadMaxFCPDelayTimer() {
if (font_preload_timer_has_fired_ ||
font_preload_max_fcp_delay_timer_.IsActive()) {
return;
}
base::TimeDelta max_fcp_delay =
base::Milliseconds(features::kMaxFCPDelayMsForRenderBlockingFonts.Get());
font_preload_max_fcp_delay_timer_.StartOneShot(max_fcp_delay, FROM_HERE);
}

void RenderBlockingResourceManager::Trace(Visitor* visitor) const {
visitor->Trace(document_);
visitor->Trace(pending_stylesheet_owner_nodes_);
visitor->Trace(pending_scripts_);
visitor->Trace(pending_font_preloads_);
visitor->Trace(font_preload_timer_);
visitor->Trace(font_preload_max_blocking_timer_);
visitor->Trace(font_preload_max_fcp_delay_timer_);
}

} // namespace blink
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,20 @@ class CORE_EXPORT RenderBlockingResourceManager final
const RenderBlockingResourceManager&) = delete;

bool HasRenderBlockingResources() const {
return pending_stylesheet_owner_nodes_.size() || pending_scripts_.size() ||
pending_font_preloads_.size() || imperative_font_loading_count_;
return HasNonFontRenderBlockingResources() || HasRenderBlockingFonts();
}
bool HasNonFontRenderBlockingResources() const {
return pending_stylesheet_owner_nodes_.size() || pending_scripts_.size();
}
bool HasRenderBlockingFonts() const {
return pending_font_preloads_.size() || imperative_font_loading_count_;
}

bool HasPendingStylesheets() const {
return pending_stylesheet_owner_nodes_.size();
}

void WillInsertDocumentBody();

// Returns true if the sheet is successfully added as a render-blocking
// resource.
bool AddPendingStylesheet(const Node& owner_node);
Expand All @@ -58,14 +65,17 @@ class CORE_EXPORT RenderBlockingResourceManager final

void AddImperativeFontLoading(FontFace*);
void RemoveImperativeFontLoading();
void EnsureStartFontPreloadTimer();
void EnsureStartFontPreloadMaxBlockingTimer();
void EnsureStartFontPreloadMaxFCPDelayTimer();
void FontPreloadingTimerFired(TimerBase*);

void Trace(Visitor* visitor) const;

private:
friend class RenderBlockingResourceManagerTest;

void RenderBlockingResourceUnblocked();

// Exposed to unit tests only.
void SetFontPreloadTimeoutForTest(base::TimeDelta timeout);
void DisableFontPreloadTimeoutForTest();
Expand All @@ -88,7 +98,10 @@ class CORE_EXPORT RenderBlockingResourceManager final

unsigned imperative_font_loading_count_ = 0;

HeapTaskRunnerTimer<RenderBlockingResourceManager> font_preload_timer_;
HeapTaskRunnerTimer<RenderBlockingResourceManager>
font_preload_max_blocking_timer_;
HeapTaskRunnerTimer<RenderBlockingResourceManager>
font_preload_max_fcp_delay_timer_;
base::TimeDelta font_preload_timeout_;
bool font_preload_timer_has_fired_ = false;
};
Expand Down

0 comments on commit bcdc49b

Please sign in to comment.