Commit 1289bbe7 authored by toyoshim's avatar toyoshim Committed by Commit bot

Reland: WebFonts cache-aware timeout adaptation

Suspicious root cause of the last revert was fixed. See crbug.com/670638.
Description from https://crrev.com/dc8303234f47ccfc841185d33c45e4852776b65e:

This CL introduces adaptive webfont display behavior, reducing unnecessary Flash
of Unstyled Text (FOUT) if webfont is already available in disk cache.  This is
available as a experimental Web Platform feature.

In webfont display, fallback font will be used if webfont loading time exceeds
certain timeout values in slow network. However it's observed that disk cache
RTT may also hit the timeout regardless of network speed. In such cases we would
like to block display until disk cache misses and actual network request is
being sent.

BUG=570205
TBR=isherman@chromium.org

Review-Url: https://codereview.chromium.org/2556683003
Cr-Commit-Position: refs/heads/master@{#437522}
parent 3a5f1623
......@@ -1149,6 +1149,7 @@ source_set("unit_tests") {
"fetch/MemoryCacheTest.cpp",
"fetch/MockFetchContext.h",
"fetch/MockResourceClients.cpp",
"fetch/MockResourceClients.h",
"fetch/MultipartImageResourceParserTest.cpp",
"fetch/RawResourceTest.cpp",
"fetch/ResourceFetcherTest.cpp",
......@@ -1266,6 +1267,8 @@ source_set("unit_tests") {
"loader/ThreadableLoaderTest.cpp",
"loader/resource/CSSStyleSheetResourceTest.cpp",
"loader/resource/FontResourceTest.cpp",
"loader/resource/MockFontResourceClient.cpp",
"loader/resource/MockFontResourceClient.h",
"origin_trials/OriginTrialContextTest.cpp",
"page/ChromeClientTest.cpp",
"page/ContextMenuControllerTest.cpp",
......
......@@ -34,6 +34,7 @@
#include "core/fetch/ResourceFetcher.h"
#include "core/loader/resource/FontResource.h"
#include "platform/CrossOriginAttributeValue.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/fonts/FontCache.h"
#include "platform/fonts/FontCustomPlatformData.h"
#include "platform/weborigin/SecurityPolicy.h"
......@@ -90,6 +91,8 @@ FontResource* CSSFontFaceSrcValue::fetch(Document* document) const {
if (!m_fetched) {
FetchRequest request(ResourceRequest(m_absoluteResource),
FetchInitiatorTypeNames::css);
if (RuntimeEnabledFeatures::webFontsCacheAwareTimeoutAdaptationEnabled())
request.setCacheAwareLoadingEnabled(IsCacheAwareLoadingEnabled);
request.setContentSecurityCheck(m_shouldCheckContentSecurityPolicy);
SecurityOrigin* securityOrigin = document->getSecurityOrigin();
setCrossOriginAccessControl(request, securityOrigin);
......
......@@ -29,6 +29,7 @@
#include "core/fetch/FetchRequest.h"
#include "core/fetch/ResourceClientWalker.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/fetch/ResourceLoader.h"
#include "platform/Histogram.h"
#include "platform/SharedBuffer.h"
#include "platform/fonts/FontCustomPlatformData.h"
......@@ -39,6 +40,8 @@ namespace blink {
// Durations of font-display periods.
// https://tabatkins.github.io/specs/css-font-display/#font-display-desc
// TODO(toyoshim): Revisit short limit value once cache-aware font display is
// launched. crbug.com/570205
static const double fontLoadWaitShortLimitSec = 0.1;
static const double fontLoadWaitLongLimitSec = 3.0;
......@@ -95,6 +98,12 @@ FontResource::~FontResource() {}
void FontResource::didAddClient(ResourceClient* c) {
DCHECK(FontResourceClient::isExpectedType(c));
Resource::didAddClient(c);
// Block client callbacks if currently loading from cache.
if (isLoading() && loader()->isCacheAwareLoadingActivated())
return;
ProhibitAddRemoveClientInScope prohibitAddRemoveClient(this);
if (m_loadLimitState == ShortLimitExceeded ||
m_loadLimitState == LongLimitExceeded)
static_cast<FontResourceClient*>(c)->fontLoadShortLimitExceeded(this);
......@@ -146,19 +155,53 @@ FontPlatformData FontResource::platformDataFromCustomData(
return m_fontData->fontPlatformData(size, bold, italic, orientation);
}
void FontResource::willReloadAfterDiskCacheMiss() {
DCHECK(isLoading());
DCHECK(loader()->isCacheAwareLoadingActivated());
if (m_loadLimitState == ShortLimitExceeded ||
m_loadLimitState == LongLimitExceeded) {
notifyClientsShortLimitExceeded();
}
if (m_loadLimitState == LongLimitExceeded)
notifyClientsLongLimitExceeded();
DEFINE_STATIC_LOCAL(
EnumerationHistogram, loadLimitHistogram,
("WebFont.LoadLimitOnDiskCacheMiss", LoadLimitStateEnumMax));
loadLimitHistogram.count(m_loadLimitState);
}
void FontResource::fontLoadShortLimitCallback(TimerBase*) {
DCHECK(isLoading());
DCHECK_EQ(m_loadLimitState, UnderLimit);
m_loadLimitState = ShortLimitExceeded;
ResourceClientWalker<FontResourceClient> walker(clients());
while (FontResourceClient* client = walker.next())
client->fontLoadShortLimitExceeded(this);
// Block client callbacks if currently loading from cache.
if (loader()->isCacheAwareLoadingActivated())
return;
notifyClientsShortLimitExceeded();
}
void FontResource::fontLoadLongLimitCallback(TimerBase*) {
DCHECK(isLoading());
DCHECK_EQ(m_loadLimitState, ShortLimitExceeded);
m_loadLimitState = LongLimitExceeded;
// Block client callbacks if currently loading from cache.
if (loader()->isCacheAwareLoadingActivated())
return;
notifyClientsLongLimitExceeded();
}
void FontResource::notifyClientsShortLimitExceeded() {
ProhibitAddRemoveClientInScope prohibitAddRemoveClient(this);
ResourceClientWalker<FontResourceClient> walker(clients());
while (FontResourceClient* client = walker.next())
client->fontLoadShortLimitExceeded(this);
}
void FontResource::notifyClientsLongLimitExceeded() {
ProhibitAddRemoveClientInScope prohibitAddRemoveClient(this);
ResourceClientWalker<FontResourceClient> walker(clients());
while (FontResourceClient* client = walker.next())
client->fontLoadLongLimitExceeded(this);
......
......@@ -26,6 +26,7 @@
#ifndef FontResource_h
#define FontResource_h
#include "base/gtest_prod_util.h"
#include "core/CoreExport.h"
#include "core/fetch/Resource.h"
#include "core/fetch/ResourceClient.h"
......@@ -72,6 +73,8 @@ class CORE_EXPORT FontResource final : public Resource {
// font is not needed for painting the text.
bool isLowPriorityLoadingAllowedForRemoteFont() const;
void willReloadAfterDiskCacheMiss() override;
void onMemoryDump(WebMemoryDumpLevelOfDetail,
WebProcessMemoryDump*) const override;
......@@ -91,12 +94,16 @@ class CORE_EXPORT FontResource final : public Resource {
void checkNotify() override;
void fontLoadShortLimitCallback(TimerBase*);
void fontLoadLongLimitCallback(TimerBase*);
void notifyClientsShortLimitExceeded();
void notifyClientsLongLimitExceeded();
// This is used in UMA histograms, should not change order.
enum LoadLimitState {
LoadNotStarted,
UnderLimit,
ShortLimitExceeded,
LongLimitExceeded
LongLimitExceeded,
LoadLimitStateEnumMax
};
std::unique_ptr<FontCustomPlatformData> m_fontData;
......@@ -107,6 +114,7 @@ class CORE_EXPORT FontResource final : public Resource {
Timer<FontResource> m_fontLoadLongLimitTimer;
friend class MemoryCache;
FRIEND_TEST_ALL_PREFIXES(FontResourceTest, CacheAwareFontLoading);
};
DEFINE_RESOURCE_TYPE_CASTS(Font);
......@@ -118,6 +126,10 @@ class FontResourceClient : public ResourceClient {
return client->getResourceClientType() == FontType;
}
ResourceClientType getResourceClientType() const final { return FontType; }
// If cache-aware loading is activated, both callbacks will be blocked until
// disk cache miss. Calls to addClient() and removeClient() in both callbacks
// are prohibited to prevent race issues regarding current loading state.
virtual void fontLoadShortLimitExceeded(FontResource*) {}
virtual void fontLoadLongLimitExceeded(FontResource*) {}
......
......@@ -8,11 +8,13 @@
#include "core/fetch/FetchRequest.h"
#include "core/fetch/MemoryCache.h"
#include "core/fetch/MockFetchContext.h"
#include "core/fetch/MockResourceClients.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/fetch/ResourceLoader.h"
#include "core/loader/resource/MockFontResourceClient.h"
#include "platform/exported/WrappedResourceResponse.h"
#include "platform/network/ResourceError.h"
#include "platform/network/ResourceRequest.h"
#include "platform/network/ResourceResponse.h"
#include "platform/weborigin/KURL.h"
#include "public/platform/Platform.h"
#include "public/platform/WebURLLoaderMockFactory.h"
......@@ -82,4 +84,61 @@ TEST_F(FontResourceTest,
memoryCache()->remove(resource1);
}
// Tests if cache-aware font loading works correctly.
TEST_F(FontResourceTest, CacheAwareFontLoading) {
KURL url(ParsedURLString, "http://127.0.0.1:8000/font.woff");
ResourceResponse response;
response.setURL(url);
response.setHTTPStatusCode(200);
Platform::current()->getURLLoaderMockFactory()->registerURL(
url, WrappedResourceResponse(response), "");
ResourceFetcher* fetcher = ResourceFetcher::create(
MockFetchContext::create(MockFetchContext::kShouldLoadNewResource));
FetchRequest fetchRequest = FetchRequest(url, FetchInitiatorInfo());
fetchRequest.setCacheAwareLoadingEnabled(IsCacheAwareLoadingEnabled);
FontResource* resource = FontResource::fetch(fetchRequest, fetcher);
ASSERT_TRUE(resource);
Persistent<MockFontResourceClient> client =
new MockFontResourceClient(resource);
fetcher->startLoad(resource);
EXPECT_TRUE(resource->loader()->isCacheAwareLoadingActivated());
resource->m_loadLimitState = FontResource::UnderLimit;
// FontResource callbacks should be blocked during cache-aware loading.
resource->fontLoadShortLimitCallback(nullptr);
EXPECT_FALSE(client->fontLoadShortLimitExceededCalled());
// Fail first request as disk cache miss.
resource->loader()->didFail(ResourceError::cacheMissError(url));
// Once cache miss error returns, previously blocked callbacks should be
// called immediately.
EXPECT_FALSE(resource->loader()->isCacheAwareLoadingActivated());
EXPECT_TRUE(client->fontLoadShortLimitExceededCalled());
EXPECT_FALSE(client->fontLoadLongLimitExceededCalled());
// Add client now, fontLoadShortLimitExceeded() should be called.
Persistent<MockFontResourceClient> client2 =
new MockFontResourceClient(resource);
EXPECT_TRUE(client2->fontLoadShortLimitExceededCalled());
EXPECT_FALSE(client2->fontLoadLongLimitExceededCalled());
// FontResource callbacks are not blocked now.
resource->fontLoadLongLimitCallback(nullptr);
EXPECT_TRUE(client->fontLoadLongLimitExceededCalled());
// Add client now, both callbacks should be called.
Persistent<MockFontResourceClient> client3 =
new MockFontResourceClient(resource);
EXPECT_TRUE(client3->fontLoadShortLimitExceededCalled());
EXPECT_TRUE(client3->fontLoadLongLimitExceededCalled());
Platform::current()->getURLLoaderMockFactory()->serveAsynchronousRequests();
Platform::current()->getURLLoaderMockFactory()->unregisterURL(url);
memoryCache()->remove(resource);
}
} // namespace blink
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/loader/resource/MockFontResourceClient.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
MockFontResourceClient::MockFontResourceClient(Resource* resource)
: m_resource(resource),
m_fontLoadShortLimitExceededCalled(false),
m_fontLoadLongLimitExceededCalled(false) {
ThreadState::current()->registerPreFinalizer(this);
m_resource->addClient(this);
}
MockFontResourceClient::~MockFontResourceClient() {}
void MockFontResourceClient::fontLoadShortLimitExceeded(FontResource*) {
ASSERT_FALSE(m_fontLoadShortLimitExceededCalled);
ASSERT_FALSE(m_fontLoadLongLimitExceededCalled);
m_fontLoadShortLimitExceededCalled = true;
}
void MockFontResourceClient::fontLoadLongLimitExceeded(FontResource*) {
ASSERT_TRUE(m_fontLoadShortLimitExceededCalled);
ASSERT_FALSE(m_fontLoadLongLimitExceededCalled);
m_fontLoadLongLimitExceededCalled = true;
}
void MockFontResourceClient::dispose() {
if (m_resource) {
m_resource->removeClient(this);
m_resource = nullptr;
}
}
} // namespace blink
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MockFontResourceClient_h
#define MockFontResourceClient_h
#include "core/fetch/Resource.h"
#include "core/fetch/ResourceClient.h"
#include "core/loader/resource/FontResource.h"
#include "platform/heap/Handle.h"
namespace blink {
class MockFontResourceClient final
: public GarbageCollectedFinalized<MockFontResourceClient>,
public FontResourceClient {
USING_PRE_FINALIZER(MockFontResourceClient, dispose);
USING_GARBAGE_COLLECTED_MIXIN(MockFontResourceClient);
public:
explicit MockFontResourceClient(Resource*);
~MockFontResourceClient() override;
void fontLoadShortLimitExceeded(FontResource*) override;
void fontLoadLongLimitExceeded(FontResource*) override;
bool fontLoadShortLimitExceededCalled() const {
return m_fontLoadShortLimitExceededCalled;
}
bool fontLoadLongLimitExceededCalled() const {
return m_fontLoadLongLimitExceededCalled;
}
DEFINE_INLINE_TRACE() {
visitor->trace(m_resource);
FontResourceClient::trace(visitor);
}
String debugName() const override { return "MockFontResourceClient"; }
private:
void dispose();
Member<Resource> m_resource;
bool m_fontLoadShortLimitExceededCalled;
bool m_fontLoadLongLimitExceededCalled;
};
} // namespace blink
#endif // MockFontResourceClient_h
......@@ -264,6 +264,7 @@ ScrollCustomization
AutoplayMutedVideos settable_from_internals=True
VisualViewportAPI status=experimental
WakeLock status=experimental
WebFontsCacheAwareTimeoutAdaptation status=experimental
WebFontsInterventionV2With2G
WebFontsInterventionV2With3G
WebFontsInterventionV2WithSlow2G
......
......@@ -72437,6 +72437,16 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
<histogram name="WebFont.LoadLimitOnDiskCacheMiss" enum="WebFontLoadLimitState">
<owner>ksakamoto@chromium.org</owner>
<owner>shaochuan@chromium.org</owner>
<owner>toyoshim@chromium.org</owner>
<summary>
The load limit state when the first disk-cache-only request fails by cache
miss, if WebFonts cache-aware timeout adaptation is enabled and applicable.
</summary>
</histogram>
<histogram name="WebFont.LoadTime.0.Under10KB" units="ms">
<obsolete>
Deprecated as of 8/2013, replaced by WebFont.DownloadTime.0.Under10KB.
......@@ -106011,6 +106021,13 @@ value.
<int value="3" label="Was triggered, and would time out"/>
</enum>
<enum name="WebFontLoadLimitState" type="int">
<int value="0" label="Load not started"/>
<int value="1" label="Under limit"/>
<int value="2" label="Short limit exceeded"/>
<int value="3" label="Long limit exceeded"/>
</enum>
<enum name="WebFontPackageFormat" type="int">
<int value="0" label="Unknown / Decode error"/>
<int value="1" label="SFNT"/>
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