REGRESSION(r212513): LastResort is platform-dependent, so its semantics should not...
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 May 2017 20:26:39 +0000 (20:26 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 16 May 2017 20:26:39 +0000 (20:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=168487

Reviewed by Antti Koivisto.

Source/WebCore:

There are three ways a Web author can chain multiple font files together:
1. Multiple entries in the "src" descriptor in an @font-face rule
2. Multiple @font-face rules with the same "font-family" descriptor
3. Multiple entries in the "font-family" property on an element

Before r212513, the code which iterated across #2 and #3 above could have
triggered each item in the chain to download. r212513 tried to solve this
by using LastResort as the interstitial font used during downloads, because
LastResort supports every character and therefore solves #3 above. However,
this change had a few problems:

1. Previously, our code would try to avoid using the interstitial font for
layout or rendering whenever possible (because one of the chains above may
have named a local font which would be better to use). In order to use the
benefits of LastResort, I had to remove this avoidance logic and make
WebKit try to use the interstitial font as often as possible. However, due
to the large metrics of LastResort, this means that offsetWidth queries
during font loading would be wildly inaccurate, causing Google Docs to break.
2. It also means that canvas drawing during font loading would actually draw
LastResort, causing Bing maps to break.
3. LastResort is platform-specific, so only platforms which have it would
actually be able to load fonts correctly.

Instead, we should keep the older logic about avoiding using the
interstitial font so that loading has a better experience for the user.
We solve the unnecessary download problem by giving our loading code a
downloading policy enum, which has two values: allow downloads or forbid
downloads. Whenever our loading code returns the interstitial font, we
continue our search, but we change the policy to forbid downloads.

There is one piece of subtlety, though: It is more common for web authors
to put good fallbacks in the "font-family" property than in the "src"
descriptor inside @font-face. This means that we shouldn't exhaustively
search through the @font-face src list first. Instead, we should look
through the src list until we hit a non-local font, and then immediately
start looking through the other other chains.

Tests: fast/text/font-download-font-face-src-list.html
       fast/text/font-download-font-family-property.html
       fast/text/font-download-remote-fallback-all.html
       fast/text/font-interstitial-invisible-width-while-loading.html
       fast/text/font-weight-download-3.html
       fast/text/web-font-load-fallback-during-loading-2.html
       fast/text/web-font-load-invisible-during-loading.html

* css/CSSFontFace.cpp:
(WebCore::CSSFontFace::fontLoadEventOccurred): Implement support for
the font download policy.
(WebCore::CSSFontFace::setStatus): After 3 seconds of loading, we
will start drawing the fallback font. However, for testing, we have an
internal setting to make this switch happen immediately. This patch now
requires that this internal switch happen synchronously.
(WebCore::CSSFontFace::pump): Implement support for the font download
policy.
(WebCore::CSSFontFace::load): Ditto.
(WebCore::CSSFontFace::font): Ditto.
* css/CSSFontFace.h: Ditto.
* css/CSSFontSelector.cpp:
(WebCore::CSSFontSelector::beginLoadingFontSoon): Implement support for
synchronous font download timeouts.
* css/CSSSegmentedFontFace.cpp:
(WebCore::CSSSegmentedFontFace::fontRanges): Implement support for the
font download policy.
* platform/graphics/Font.cpp: Add new flag which represents if the
interstitial font was created after the 3 second timeout or before.
Previously, we would distinguish between these two cases by knowing
that one font was LastResort and the other font was a fallback. Now that
we're using fallback fonts on both sides of the 3 second timeout, we
now no longer know which one should be invisible. This new enum solves
this problem.
(WebCore::Font::Font):
(WebCore::Font::verticalRightOrientationFont):
(WebCore::Font::uprightOrientationFont):
* platform/graphics/Font.h: Ditto.
(WebCore::Font::create):
(WebCore::Font::origin):
(WebCore::Font::visibility):
* platform/graphics/FontCache.h:
* platform/graphics/FontCascade.cpp: We try to fall back to a local() font
during downloads, but there might not be one that we can use. Therefore, we
can't use the presence of the interstitial font to detect if we should paint
invisibly. Instead, we can move this logic into the font-specific part of
painting, and consult with the specific font to know if it was created from
a timed-out @font-face rule or not.
(WebCore::FontCascade::drawText):
(WebCore::shouldDrawIfLoading):
(WebCore::FontCascade::drawGlyphBuffer):
(WebCore::FontCascade::drawEmphasisMarks):
* platform/graphics/FontCascade.h:
* platform/graphics/FontCascadeFonts.cpp:
(WebCore::FontCascadeFonts::glyphDataForVariant): Implement the logic
described above where we switch the policy if we encounter the intestitial
font.
(WebCore::FontCascadeFonts::glyphDataForNormalVariant): Ditto.
(WebCore::glyphPageFromFontRanges): Ditto.
* platform/graphics/FontRanges.cpp: Implement support for the font download
policy.
(WebCore::FontRanges::Range::font):
(WebCore::FontRanges::glyphDataForCharacter):
(WebCore::FontRanges::fontForCharacter):
(WebCore::FontRanges::fontForFirstRange):
* platform/graphics/FontRanges.h:
* platform/graphics/FontSelector.h:
* platform/graphics/freetype/FontCacheFreeType.cpp:
(WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.
* platform/graphics/mac/FontCacheMac.mm:
(WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.
* platform/graphics/win/FontCacheWin.cpp:
(WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.

LayoutTests:

* fast/text/font-download-font-face-src-list-expected.txt: Added.
* fast/text/font-download-font-face-src-list.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
* fast/text/font-download-font-family-property-expected.txt: Added.
* fast/text/font-download-font-family-property.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
* fast/text/font-download-remote-fallback-all-expected.txt: Added.
* fast/text/font-download-remote-fallback-all.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
* fast/text/font-interstitial-invisible-width-while-loading-expected.txt: Added.
* fast/text/font-interstitial-invisible-width-while-loading.html: Added.
* fast/text/font-weight-download-2.html:
* fast/text/font-weight-download-3-expected.txt: Added.
* fast/text/font-weight-download-3.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
* fast/text/web-font-load-fallback-during-loading-2-expected.html: Added.
* fast/text/web-font-load-fallback-during-loading-2.html: Added.
* fast/text/web-font-load-fallback-during-loading-expected.html:
* fast/text/web-font-load-fallback-during-loading.html:
* fast/text/web-font-load-invisible-during-loading-expected.txt: Added.
* fast/text/web-font-load-invisible-during-loading.html: Added.
* http/tests/webfont/fallback-font-while-loading-expected.txt:
* http/tests/webfont/fallback-font-while-loading.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@216944 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/text/font-download-font-face-src-list-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-download-font-face-src-list.html [new file with mode: 0644]
LayoutTests/fast/text/font-download-font-family-property-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-download-font-family-property.html [new file with mode: 0644]
LayoutTests/fast/text/font-download-remote-fallback-all-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-download-remote-fallback-all.html [new file with mode: 0644]
LayoutTests/fast/text/font-interstitial-invisible-width-while-loading-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-interstitial-invisible-width-while-loading.html [new file with mode: 0644]
LayoutTests/fast/text/font-weight-download-2.html
LayoutTests/fast/text/font-weight-download-3-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/font-weight-download-3.html [new file with mode: 0644]
LayoutTests/fast/text/web-font-load-fallback-during-loading-2-expected.html [new file with mode: 0644]
LayoutTests/fast/text/web-font-load-fallback-during-loading-2.html [new file with mode: 0644]
LayoutTests/fast/text/web-font-load-fallback-during-loading-expected.html
LayoutTests/fast/text/web-font-load-fallback-during-loading.html
LayoutTests/fast/text/web-font-load-invisible-during-loading-expected.txt [new file with mode: 0644]
LayoutTests/fast/text/web-font-load-invisible-during-loading.html [new file with mode: 0644]
LayoutTests/http/tests/webfont/fallback-font-while-loading-expected.txt
LayoutTests/http/tests/webfont/fallback-font-while-loading.html
Source/WebCore/ChangeLog
Source/WebCore/css/CSSFontFace.cpp
Source/WebCore/css/CSSFontFace.h
Source/WebCore/css/CSSFontSelector.cpp
Source/WebCore/css/CSSSegmentedFontFace.cpp
Source/WebCore/platform/graphics/Font.cpp
Source/WebCore/platform/graphics/Font.h
Source/WebCore/platform/graphics/FontCache.h
Source/WebCore/platform/graphics/FontCascade.cpp
Source/WebCore/platform/graphics/FontCascade.h
Source/WebCore/platform/graphics/FontCascadeFonts.cpp
Source/WebCore/platform/graphics/FontRanges.cpp
Source/WebCore/platform/graphics/FontRanges.h
Source/WebCore/platform/graphics/FontSelector.h
Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp
Source/WebCore/platform/graphics/mac/FontCacheMac.mm
Source/WebCore/platform/graphics/win/FontCacheWin.cpp

index ca39a0f..bd5295a 100644 (file)
@@ -1,3 +1,30 @@
+2017-05-16  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        REGRESSION(r212513): LastResort is platform-dependent, so its semantics should not be required to perform font loading correctly.
+        https://bugs.webkit.org/show_bug.cgi?id=168487
+
+        Reviewed by Antti Koivisto.
+
+        * fast/text/font-download-font-face-src-list-expected.txt: Added.
+        * fast/text/font-download-font-face-src-list.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
+        * fast/text/font-download-font-family-property-expected.txt: Added.
+        * fast/text/font-download-font-family-property.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
+        * fast/text/font-download-remote-fallback-all-expected.txt: Added.
+        * fast/text/font-download-remote-fallback-all.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
+        * fast/text/font-interstitial-invisible-width-while-loading-expected.txt: Added.
+        * fast/text/font-interstitial-invisible-width-while-loading.html: Added.
+        * fast/text/font-weight-download-2.html:
+        * fast/text/font-weight-download-3-expected.txt: Added.
+        * fast/text/font-weight-download-3.html: Copied from LayoutTests/fast/text/font-weight-download-2.html.
+        * fast/text/web-font-load-fallback-during-loading-2-expected.html: Added.
+        * fast/text/web-font-load-fallback-during-loading-2.html: Added.
+        * fast/text/web-font-load-fallback-during-loading-expected.html:
+        * fast/text/web-font-load-fallback-during-loading.html:
+        * fast/text/web-font-load-invisible-during-loading-expected.txt: Added.
+        * fast/text/web-font-load-invisible-during-loading.html: Added.
+        * http/tests/webfont/fallback-font-while-loading-expected.txt:
+        * http/tests/webfont/fallback-font-while-loading.html:
+
 2017-05-16  Eric Carlson  <eric.carlson@apple.com>
 
         [MediaStream] Return default device list until user gives permission to capture
diff --git a/LayoutTests/fast/text/font-download-font-face-src-list-expected.txt b/LayoutTests/fast/text/font-download-font-face-src-list-expected.txt
new file mode 100644 (file)
index 0000000..c02e144
--- /dev/null
@@ -0,0 +1,6 @@
+font-download-font-face-src-list.html - didFinishLoading
+Ahem_CJK.ttf - willSendRequest <NSURLRequest URL Ahem_CJK.ttf, main document URL font-download-font-face-src-list.html, http method GET> redirectResponse (null)
+Ahem_CJK.ttf - didReceiveResponse <NSURLResponse Ahem_CJK.ttf, http status code 0>
+Ahem_CJK.ttf - didFinishLoading
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+横
diff --git a/LayoutTests/fast/text/font-download-font-face-src-list.html b/LayoutTests/fast/text/font-download-font-face-src-list.html
new file mode 100644 (file)
index 0000000..9be7467
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpResourceLoadCallbacks();
+    testRunner.dumpAsText();
+}
+if (window.internals) {
+    internals.invalidateFontCache();
+    internals.clearMemoryCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "WebFont";
+    src: url("../../resources/Ahem_CJK.ttf") format("truetype"), url("../../resources/Ahem.otf") format("opentype");
+}
+</style>
+</head>
+<body>
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+<div style="font-size: 100px; font-family: 'WebFont';">&#x6A2A;</div>
+</body>
+</html>
+
+
diff --git a/LayoutTests/fast/text/font-download-font-family-property-expected.txt b/LayoutTests/fast/text/font-download-font-family-property-expected.txt
new file mode 100644 (file)
index 0000000..d803554
--- /dev/null
@@ -0,0 +1,6 @@
+font-download-font-family-property.html - didFinishLoading
+Ahem_CJK.ttf - willSendRequest <NSURLRequest URL Ahem_CJK.ttf, main document URL font-download-font-family-property.html, http method GET> redirectResponse (null)
+Ahem_CJK.ttf - didReceiveResponse <NSURLResponse Ahem_CJK.ttf, http status code 0>
+Ahem_CJK.ttf - didFinishLoading
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+横
diff --git a/LayoutTests/fast/text/font-download-font-family-property.html b/LayoutTests/fast/text/font-download-font-family-property.html
new file mode 100644 (file)
index 0000000..07cf481
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpResourceLoadCallbacks();
+    testRunner.dumpAsText();
+}
+if (window.internals) {
+    internals.invalidateFontCache();
+    internals.clearMemoryCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "WebFont";
+    src: url("../../resources/Ahem_CJK.ttf") format("truetype");
+}
+@font-face {
+    font-family: "WebFont2";
+    src: url("../../resources/Ahem.otf") format("opentype");
+}
+</style>
+</head>
+<body>
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+<div style="font-size: 100px; font-family: 'WebFont', 'WebFont2';">&#x6A2A;</div>
+</body>
+</html>
+
diff --git a/LayoutTests/fast/text/font-download-remote-fallback-all-expected.txt b/LayoutTests/fast/text/font-download-remote-fallback-all-expected.txt
new file mode 100644 (file)
index 0000000..a05e637
--- /dev/null
@@ -0,0 +1,6 @@
+font-download-remote-fallback-all.html - didFinishLoading
+Ahem_CJK.ttf - willSendRequest <NSURLRequest URL Ahem_CJK.ttf, main document URL font-download-remote-fallback-all.html, http method GET> redirectResponse (null)
+Ahem_CJK.ttf - didReceiveResponse <NSURLResponse Ahem_CJK.ttf, http status code 0>
+Ahem_CJK.ttf - didFinishLoading
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if any of the "garbage" font names are requested.
+横
diff --git a/LayoutTests/fast/text/font-download-remote-fallback-all.html b/LayoutTests/fast/text/font-download-remote-fallback-all.html
new file mode 100644 (file)
index 0000000..2f829f8
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpResourceLoadCallbacks();
+    testRunner.dumpAsText();
+}
+if (window.internals) {
+    internals.invalidateFontCache();
+    internals.clearMemoryCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "WebFont";
+    font-weight: 100;
+    src: url("../../resources/Ahem_CJK.ttf"), url("garbage2");
+}
+@font-face {
+    font-family: "WebFont";
+    font-weight: 900;
+    src: url("garbage3"), url("garbage4");
+}
+@font-face {
+    font-family: "WebFont2";
+    src: url("garbage5"), url("garbage6");
+}
+</style>
+</head>
+<body>
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if any of the "garbage" font names are requested.
+<div style="font-size: 100px; font-weight: 100; font-family: 'WebFont', 'WebFont2';">&#x6A2A;</div>
+</body>
+</html>
diff --git a/LayoutTests/fast/text/font-interstitial-invisible-width-while-loading-expected.txt b/LayoutTests/fast/text/font-interstitial-invisible-width-while-loading-expected.txt
new file mode 100644 (file)
index 0000000..f51bb4c
--- /dev/null
@@ -0,0 +1,11 @@
+This test makes sure that the advances of the interstitial (invisible) font used during font loading is not very far from 'Times'. This is necessary for web compatibility.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS target.offsetWidth is within 5 of 223
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Hello
+Hello
diff --git a/LayoutTests/fast/text/font-interstitial-invisible-width-while-loading.html b/LayoutTests/fast/text/font-interstitial-invisible-width-while-loading.html
new file mode 100644 (file)
index 0000000..d6a0c6c
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+if (window.internals) {
+    internals.invalidateFontCache();
+    internals.clearMemoryCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "WebFont";
+    src: url("../../resources/Ahem.otf") format("opentype");
+}
+</style>
+</head>
+<body>
+<div><span id="target" style="font: 100px 'WebFont';">Hello</span></div>
+<div><span id="ref" style="font: 100px 'Times';">Hello</span></div>
+<script>
+description("This test makes sure that the advances of the interstitial (invisible) font used during font loading is not very far from 'Times'. This is necessary for web compatibility.");
+var target = document.getElementById("target");
+var ref = document.getElementById("ref");
+shouldBeCloseTo("target.offsetWidth", ref.offsetWidth, 5);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index f8aeedd..1b27896 100644 (file)
@@ -28,4 +28,4 @@ if (window.internals) {
 This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
 <div style="font: 100 100px 'WebFont';">&#x6A2A;</div>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/LayoutTests/fast/text/font-weight-download-3-expected.txt b/LayoutTests/fast/text/font-weight-download-3-expected.txt
new file mode 100644 (file)
index 0000000..e69cd5c
--- /dev/null
@@ -0,0 +1,6 @@
+font-weight-download-3.html - didFinishLoading
+Ahem_CJK.ttf - willSendRequest <NSURLRequest URL Ahem_CJK.ttf, main document URL font-weight-download-3.html, http method GET> redirectResponse (null)
+Ahem_CJK.ttf - didReceiveResponse <NSURLResponse Ahem_CJK.ttf, http status code 0>
+Ahem_CJK.ttf - didFinishLoading
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+横̀
diff --git a/LayoutTests/fast/text/font-weight-download-3.html b/LayoutTests/fast/text/font-weight-download-3.html
new file mode 100644 (file)
index 0000000..2d8ce1d
--- /dev/null
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpResourceLoadCallbacks();
+    testRunner.dumpAsText();
+}
+if (window.internals) {
+    internals.invalidateFontCache();
+    internals.clearMemoryCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "WebFont";
+    font-weight: 100;
+    src: url("../../resources/Ahem_CJK.ttf") format("truetype");
+}
+@font-face {
+    font-family: "WebFont";
+    font-weight: 900;
+    src: url("../../resources/Ahem.otf") format("opentype");
+}
+</style>
+</head>
+<body>
+This test makes sure that unnecessary fonts aren't downloaded. The test fails if Ahem.otf is downloaded.
+<div style="font: 100 100px 'WebFont';">&#x6A2A;&#x300;</div>
+</body>
+</html>
+
diff --git a/LayoutTests/fast/text/web-font-load-fallback-during-loading-2-expected.html b/LayoutTests/fast/text/web-font-load-fallback-during-loading-2-expected.html
new file mode 100644 (file)
index 0000000..f357242
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+This test makes sure that fallback fonts are used during the time when fonts are loading.
+<p><span id="test" style="font-family: 'Helvetica';">This is rendered with Helvetica.</span></p>
+</body>
+</html>
diff --git a/LayoutTests/fast/text/web-font-load-fallback-during-loading-2.html b/LayoutTests/fast/text/web-font-load-fallback-during-loading-2.html
new file mode 100644 (file)
index 0000000..b23c78c
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.internals) {
+    internals.settings.setWebFontsAlwaysFallBack(true);
+    internals.clearMemoryCache();
+    internals.invalidateFontCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: "Helvetica";
+    src: url("../../resources/Ahem.ttf") format("truetype");
+}
+</style>
+</head>
+<body>
+This test makes sure that fallback fonts are used during the time when fonts are loading.
+<p><span id="test" style="font-family: 'Helvetica';">This is rendered with Helvetica.</span></p>
+</body>
+</html>
index 92aa591..58b5da0 100644 (file)
@@ -3,10 +3,11 @@
 <head>
 </head>
 <body>
-This test makes sure that fallback fonts are used during the time when fonts are loading. The test passes if the next line below reads "Test complete" and all the subsequent statements below are legible and true.
-<p id="console">Test complete</p>
-<p><span class="test" style="font-family: Helvetica">This is rendered with Helvetica.</span></p>
-<p><span class="test" style="font-family: Helvetica">This is rendered with Helvetica.</span></p>
-<p><span class="test" style="font-family: Helvetica">This is rendered with Helvetica.</span></p>
+<div style="-webkit-text-size-adjust: none;">
+This test makes sure that fallback fonts are used during the time when fonts are loading.
+<p id="console">PASS: Element is not rendered with American Typewriter.</p>
+<p><span class="test" style="font-family: 'American Typewriter';">This is a test rendering.</span></p>
+<p><span class="test" style="font-family: 'American Typewriter';">PASS: This is rendered with American Typewriter.</span></p>
+</div>
 </body>
-</html>
\ No newline at end of file
+</html>
index 977645c..94b0575 100644 (file)
@@ -7,60 +7,45 @@ if (window.internals) {
     internals.clearMemoryCache();
     internals.invalidateFontCache();
 }
-if (window.testRunner)
-    testRunner.waitUntilDone();
-
-function computeCurrentWidths() {
-    var result = [];
-    var collection = document.getElementsByClassName("test");
-    for (var i = 0; i < collection.length; ++i)
-        result.push(collection.item(i).getBoundingClientRect().width);
-    return result;
-}
-
-function testCompleted(widths, goal) {
-    for (var i = 0; i < widths.length; ++i) {
-        if (widths[i] != goal)
-            return false;
-    }
-    return true;
-}
-
-var token = window.setInterval(function () {
-    if (testCompleted(computeCurrentWidths(), expectedWidth)) {
-        window.clearInterval(token);
-        document.getElementById("console").appendChild(document.createTextNode("Test complete"));
-        if (window.testRunner)
-            testRunner.notifyDone();
-    }
-}, 0);
-
 </script>
 <style>
 @font-face {
-    font-family: WebFont;
-    src: url("../../resources/Ahem.ttf") format("truetype"), local("Helvetica");
+    font-family: "WebFont";
+    src: url("../../resources/Ahem.ttf") format("truetype"), local("American Typewriter");
 }
 @font-face {
-    font-family: WebFont2;
-    src: local("Helvetica");
+    font-family: "WebFont2";
+    src: local("American Typewriter");
+}
+@font-face {
+    font-family: "WebFont2";
+    src: url("../../resources/Ahem.ttf") format("truetype");
 }
 @font-face {
-    font-family: WebFont2;
+    font-family: "WebFont3";
     src: url("../../resources/Ahem.ttf") format("truetype");
 }
 </style>
 </head>
 <body>
-This test makes sure that fallback fonts are used during the time when fonts are loading. The test passes if the next line below reads "Test complete" and all the subsequent statements below are legible and true.
-<!-- FIXME: This test is racey. The font may complete downloading before the 0-delay timer fires,
-in which case the "Test Complete" text will not be added. -->
-<p id="console"></p>
-<p><span id="reference" style="font-family: Helvetica">This is rendered with Helvetica.</span></p>
-<p><span class="test" style="font-family: WebFont">This is rendered with Helvetica.</span></p>
-<p><span class="test" style="font-family: WebFont2">This is rendered with Helvetica.</span></p>
+<div style="-webkit-text-size-adjust: none;">
+This test makes sure that fallback fonts are used during the time when fonts are loading.
+<p id="console">Test NOT complete.</p>
+<p><span id="americanTypewriter" style="font-family: 'American Typewriter';">This is a test rendering.</span></p>
+<p id="container"><span id="notAmericanTypewriter" style="font-family: 'WebFont';">This is a test rendering.</span></p>
+<p><span style="font-family: 'WebFont2';">PASS: This is rendered with American Typewriter.</span></p>
+<!-- Because the primary font is the platform-dependent interstitial font, the baseline position of the line is calculated with this platform-specific font, which means we can't test it with a ref test because we don't know exactly where the text will end up. -->
+<!--p><span style="font-family: 'WebFont3', 'American Typewriter';">PASS: This is rendered with American Typewriter.</span></p-->
+</div>
 <script>
-var expectedWidth = document.getElementById("reference").getBoundingClientRect().width;
+var console = document.getElementById("console");
+var americanTypewriterWidth = document.getElementById("americanTypewriter").getBoundingClientRect().width;
+var notAmericanTypewriterWidth = document.getElementById("notAmericanTypewriter").getBoundingClientRect().width;
+if (americanTypewriterWidth == notAmericanTypewriterWidth)
+    console.innerText = "FAIL: Element should not be rendered with American Typewriter.";
+else
+    console.innerText = "PASS: Element is not rendered with American Typewriter.";
+document.getElementById("container").style.display = "none";
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/LayoutTests/fast/text/web-font-load-invisible-during-loading-expected.txt b/LayoutTests/fast/text/web-font-load-invisible-during-loading-expected.txt
new file mode 100644 (file)
index 0000000..9e0cbc7
--- /dev/null
@@ -0,0 +1,21 @@
+This test makes sure that fallback fonts are used during the time when fonts are loading.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS test1 is not americanTypewriterWidth
+PASS test2 is americanTypewriterWidth
+PASS test3 is americanTypewriterWidth
+PASS test4 is americanTypewriterWidth
+PASS successfullyParsed is true
+
+TEST COMPLETE
+This is rendered with American Typewriter.
+
+This is rendered with American Typewriter.
+
+This is rendered with American Typewriter.
+
+This is rendered with American Typewriter.
+
+This is rendered with American Typewriter.
diff --git a/LayoutTests/fast/text/web-font-load-invisible-during-loading.html b/LayoutTests/fast/text/web-font-load-invisible-during-loading.html
new file mode 100644 (file)
index 0000000..9730251
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+if (window.internals) {
+    internals.clearMemoryCache();
+    internals.invalidateFontCache();
+}
+</script>
+<style>
+@font-face {
+    font-family: WebFont;
+    src: url("../../resources/Ahem.ttf") format("truetype"), local("American Typewriter");
+}
+@font-face {
+    font-family: WebFont2;
+    src: local("American Typewriter");
+}
+@font-face {
+    font-family: WebFont2;
+    src: url("../../resources/Ahem.ttf") format("truetype");
+}
+@font-face {
+    font-family: WebFont3;
+    src: url("../../resources/Ahem.ttf") format("truetype");
+}
+</style>
+</head>
+<body>
+<div style="width: 5000px; -webkit-text-size-adjust: none;">
+<p><span id="americanTypewriter" style="font-family: 'American Typewriter';">This is rendered with American Typewriter.</span></p>
+<p><span id="test1" style="font-family: 'WebFont';">This is rendered with American Typewriter.</span></p>
+<p><span id="test2" style="font-family: 'WebFont2';">This is rendered with American Typewriter.</span></p>
+<p><span id="test3" style="font-family: 'WebFont3', 'American Typewriter';">This is rendered with American Typewriter.</span></p>
+<p><span id="test4" style="font-family: 'American Typewriter';">This is rendered with American Typewriter.</span></p>
+</div>
+<script>
+description("This test makes sure that fallback fonts are used during the time when fonts are loading.");
+var americanTypewriterWidth = document.getElementById("americanTypewriter").getBoundingClientRect().width;
+var test1 = document.getElementById("test1").getBoundingClientRect().width;
+var test2 = document.getElementById("test2").getBoundingClientRect().width;
+var test3 = document.getElementById("test3").getBoundingClientRect().width;
+</script>
+<style>
+@font-face {
+    font-family: "American Typewriter";
+    src: url("../../resources/Ahem.ttf") format("truetype");
+}
+</style>
+<script>
+var test4 = document.getElementById("test4").getBoundingClientRect().width;
+shouldNotBe("test1", "americanTypewriterWidth");
+shouldBe("test2", "americanTypewriterWidth");
+shouldBe("test3", "americanTypewriterWidth");
+shouldBe("test4", "americanTypewriterWidth");
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 3ae57b4..969aa03 100644 (file)
@@ -1,7 +1,7 @@
-This test checks that the fallback font is not used for layout while a webfont is loading.
+This test checks that the fallback font is used for layout while a webfont is loading.
 
 Target:
 A text to be measured.
 Reference:
 A text to be measured.
-PASS: The width of target text and reference text are different.
+PASS: The width of target text and reference text is the same.
index b15a3bf..ec8ed0f 100644 (file)
@@ -5,7 +5,7 @@
 }
 </style>
 <p>
-This test checks that the fallback font is not used for layout while a webfont is loading.
+This test checks that the fallback font is used for layout while a webfont is loading.
 </p>
 Target:
 <div>
@@ -26,10 +26,10 @@ function checkSize() {
     var targetWidth = document.getElementById('target').offsetWidth;
     var referenceWidth = document.getElementById('reference').offsetWidth;
     var result = document.getElementById('result');
-    if (targetWidth != referenceWidth)
-        result.innerText = 'PASS: The width of target text and reference text are different.';
+    if (targetWidth == referenceWidth)
+        result.innerText = 'PASS: The width of target text and reference text is the same.';
     else
-        result.innerText = 'FAIL: The width of target text and reference text are the same.';
+        result.innerText = 'FAIL: The width of target text and reference text is different: ' + targetWidth + ' != ' + referenceWidth;
 }
 
 checkSize();
index fa0afc9..4f47794 100644 (file)
@@ -1,3 +1,120 @@
+2017-05-16  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        REGRESSION(r212513): LastResort is platform-dependent, so its semantics should not be required to perform font loading correctly.
+        https://bugs.webkit.org/show_bug.cgi?id=168487
+
+        Reviewed by Antti Koivisto.
+
+        There are three ways a Web author can chain multiple font files together:
+        1. Multiple entries in the "src" descriptor in an @font-face rule
+        2. Multiple @font-face rules with the same "font-family" descriptor
+        3. Multiple entries in the "font-family" property on an element
+
+        Before r212513, the code which iterated across #2 and #3 above could have
+        triggered each item in the chain to download. r212513 tried to solve this
+        by using LastResort as the interstitial font used during downloads, because
+        LastResort supports every character and therefore solves #3 above. However,
+        this change had a few problems:
+
+        1. Previously, our code would try to avoid using the interstitial font for
+        layout or rendering whenever possible (because one of the chains above may
+        have named a local font which would be better to use). In order to use the
+        benefits of LastResort, I had to remove this avoidance logic and make
+        WebKit try to use the interstitial font as often as possible. However, due
+        to the large metrics of LastResort, this means that offsetWidth queries
+        during font loading would be wildly inaccurate, causing Google Docs to break.
+        2. It also means that canvas drawing during font loading would actually draw
+        LastResort, causing Bing maps to break.
+        3. LastResort is platform-specific, so only platforms which have it would
+        actually be able to load fonts correctly.
+
+        Instead, we should keep the older logic about avoiding using the
+        interstitial font so that loading has a better experience for the user.
+        We solve the unnecessary download problem by giving our loading code a
+        downloading policy enum, which has two values: allow downloads or forbid
+        downloads. Whenever our loading code returns the interstitial font, we
+        continue our search, but we change the policy to forbid downloads.
+
+        There is one piece of subtlety, though: It is more common for web authors
+        to put good fallbacks in the "font-family" property than in the "src"
+        descriptor inside @font-face. This means that we shouldn't exhaustively
+        search through the @font-face src list first. Instead, we should look
+        through the src list until we hit a non-local font, and then immediately
+        start looking through the other other chains.
+
+        Tests: fast/text/font-download-font-face-src-list.html
+               fast/text/font-download-font-family-property.html
+               fast/text/font-download-remote-fallback-all.html
+               fast/text/font-interstitial-invisible-width-while-loading.html
+               fast/text/font-weight-download-3.html
+               fast/text/web-font-load-fallback-during-loading-2.html
+               fast/text/web-font-load-invisible-during-loading.html
+
+        * css/CSSFontFace.cpp:
+        (WebCore::CSSFontFace::fontLoadEventOccurred): Implement support for
+        the font download policy.
+        (WebCore::CSSFontFace::setStatus): After 3 seconds of loading, we
+        will start drawing the fallback font. However, for testing, we have an
+        internal setting to make this switch happen immediately. This patch now
+        requires that this internal switch happen synchronously.
+        (WebCore::CSSFontFace::pump): Implement support for the font download
+        policy.
+        (WebCore::CSSFontFace::load): Ditto.
+        (WebCore::CSSFontFace::font): Ditto.
+        * css/CSSFontFace.h: Ditto.
+        * css/CSSFontSelector.cpp:
+        (WebCore::CSSFontSelector::beginLoadingFontSoon): Implement support for
+        synchronous font download timeouts.
+        * css/CSSSegmentedFontFace.cpp:
+        (WebCore::CSSSegmentedFontFace::fontRanges): Implement support for the
+        font download policy.
+        * platform/graphics/Font.cpp: Add new flag which represents if the
+        interstitial font was created after the 3 second timeout or before.
+        Previously, we would distinguish between these two cases by knowing
+        that one font was LastResort and the other font was a fallback. Now that
+        we're using fallback fonts on both sides of the 3 second timeout, we
+        now no longer know which one should be invisible. This new enum solves
+        this problem.
+        (WebCore::Font::Font):
+        (WebCore::Font::verticalRightOrientationFont):
+        (WebCore::Font::uprightOrientationFont):
+        * platform/graphics/Font.h: Ditto.
+        (WebCore::Font::create):
+        (WebCore::Font::origin):
+        (WebCore::Font::visibility):
+        * platform/graphics/FontCache.h:
+        * platform/graphics/FontCascade.cpp: We try to fall back to a local() font
+        during downloads, but there might not be one that we can use. Therefore, we
+        can't use the presence of the interstitial font to detect if we should paint
+        invisibly. Instead, we can move this logic into the font-specific part of
+        painting, and consult with the specific font to know if it was created from
+        a timed-out @font-face rule or not.
+        (WebCore::FontCascade::drawText):
+        (WebCore::shouldDrawIfLoading):
+        (WebCore::FontCascade::drawGlyphBuffer):
+        (WebCore::FontCascade::drawEmphasisMarks):
+        * platform/graphics/FontCascade.h:
+        * platform/graphics/FontCascadeFonts.cpp:
+        (WebCore::FontCascadeFonts::glyphDataForVariant): Implement the logic
+        described above where we switch the policy if we encounter the intestitial
+        font.
+        (WebCore::FontCascadeFonts::glyphDataForNormalVariant): Ditto.
+        (WebCore::glyphPageFromFontRanges): Ditto.
+        * platform/graphics/FontRanges.cpp: Implement support for the font download
+        policy.
+        (WebCore::FontRanges::Range::font):
+        (WebCore::FontRanges::glyphDataForCharacter):
+        (WebCore::FontRanges::fontForCharacter):
+        (WebCore::FontRanges::fontForFirstRange):
+        * platform/graphics/FontRanges.h:
+        * platform/graphics/FontSelector.h:
+        * platform/graphics/freetype/FontCacheFreeType.cpp:
+        (WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.
+        * platform/graphics/mac/FontCacheMac.mm:
+        (WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.
+        * platform/graphics/win/FontCacheWin.cpp:
+        (WebCore::FontCache::lastResortFallbackFontForEveryCharacter): Deleted.
+
 2017-05-16  Zalan Bujtas  <zalan@apple.com>
 
         Simple line layout: Move setCollapedWhitespaceWidth call to updateLineConstrains.
index f185e90..2370479 100644 (file)
@@ -419,8 +419,8 @@ void CSSFontFace::fontLoadEventOccurred()
     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
     // when a source is failed or succeeded before we have asked it to load.
-    if (m_sourcesPopulated)
-        pump();
+    if (m_sourcesPopulated && !webFontsShouldAlwaysFallBack())
+        pump(ExternalResourceDownloadPolicy::Forbid);
 
     ASSERT(m_fontSelector);
     m_fontSelector->fontLoaded();
@@ -526,16 +526,23 @@ void CSSFontFace::setStatus(Status newStatus)
         break;
     }
 
-    if (newStatus == Status::Loading)
-        m_timeoutTimer.startOneShot(webFontsShouldAlwaysFallBack() ? 0_s : 3_s);
-    else if (newStatus == Status::Success || newStatus == Status::Failure)
-        m_timeoutTimer.stop();
+    bool webFontsShouldAlwaysFallBack = this->webFontsShouldAlwaysFallBack();
+    if (!webFontsShouldAlwaysFallBack) {
+        if (newStatus == Status::Loading) {
+            Seconds timeUntilInterstitialFontIsDrawnVisibly = 3_s;
+            m_timeoutTimer.startOneShot(timeUntilInterstitialFontIsDrawnVisibly);
+        } else if (newStatus == Status::Success || newStatus == Status::Failure)
+            m_timeoutTimer.stop();
+    }
 
     iterateClients(m_clients, [&](Client& client) {
         client.fontStateChanged(*this, m_status, newStatus);
     });
 
     m_status = newStatus;
+
+    if (newStatus == Status::Loading && webFontsShouldAlwaysFallBack)
+        timeoutFired();
 }
 
 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
@@ -550,7 +557,7 @@ bool CSSFontFace::webFontsShouldAlwaysFallBack() const
     return m_fontSelector && m_fontSelector->document() && m_fontSelector->document()->settings().webFontsAlwaysFallBack();
 }
 
-size_t CSSFontFace::pump()
+size_t CSSFontFace::pump(ExternalResourceDownloadPolicy policy)
 {
     size_t i;
     for (i = 0; i < m_sources.size(); ++i) {
@@ -558,15 +565,17 @@ size_t CSSFontFace::pump()
 
         if (source->status() == CSSFontFaceSource::Status::Pending) {
             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
-            if (m_status == Status::Pending)
-                setStatus(Status::Loading);
-            source->load(m_fontSelector.get());
+            if (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()) {
+                if (m_status == Status::Pending)
+                    setStatus(Status::Loading);
+                source->load(m_fontSelector.get());
+            }
         }
 
         switch (source->status()) {
         case CSSFontFaceSource::Status::Pending:
-            ASSERT_NOT_REACHED();
-            break;
+            ASSERT(policy == ExternalResourceDownloadPolicy::Forbid);
+            return i;
         case CSSFontFaceSource::Status::Loading:
             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
             if (m_status == Status::Pending)
@@ -594,10 +603,10 @@ size_t CSSFontFace::pump()
 
 void CSSFontFace::load()
 {
-    pump();
+    pump(ExternalResourceDownloadPolicy::Allow);
 }
 
-RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
+RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy policy)
 {
     if (allSourcesFailed())
         return nullptr;
@@ -606,26 +615,18 @@ RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool synt
     // return null from font(), which means we need to continue looping through the remainder
     // of the sources to try to find a font to use. These subsequent tries should not affect
     // our own state, though.
-    size_t startIndex = pump();
-    bool fontIsLoading = false;
+    size_t startIndex = pump(policy);
     for (size_t i = startIndex; i < m_sources.size(); ++i) {
         auto& source = m_sources[i];
-        if (source->status() == CSSFontFaceSource::Status::Pending) {
-            if (fontIsLoading && source->requiresExternalResource())
-                continue;
+        if (source->status() == CSSFontFaceSource::Status::Pending && (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()))
             source->load(m_fontSelector.get());
-        }
 
         switch (source->status()) {
         case CSSFontFaceSource::Status::Pending:
-            ASSERT_NOT_REACHED();
-            break;
-        case CSSFontFaceSource::Status::Loading:
-            ASSERT(!fontIsLoading);
-            fontIsLoading = true;
-            if (status() == Status::TimedOut)
-                continue;
-            return Font::create(FontCache::singleton().lastResortFallbackFontForEveryCharacter(fontDescription)->platformData(), Font::Origin::Remote, Font::Interstitial::Yes);
+        case CSSFontFaceSource::Status::Loading: {
+            Font::Visibility visibility = status() == Status::TimedOut ? Font::Visibility::Visible : Font::Visibility::Invisible;
+            return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), Font::Origin::Remote, Font::Interstitial::Yes, visibility);
+        }
         case CSSFontFaceSource::Status::Success:
             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings, m_fontSelectionCapabilities))
                 return result;
index 62d262a..c5a4a1d 100644 (file)
@@ -53,6 +53,7 @@ class Document;
 class FontDescription;
 class Font;
 class FontFace;
+enum class ExternalResourceDownloadPolicy;
 
 class CSSFontFace final : public RefCounted<CSSFontFace> {
 public:
@@ -108,7 +109,8 @@ public:
     void fontLoaded(CSSFontFaceSource&);
 
     void load();
-    RefPtr<Font> font(const FontDescription&, bool syntheticBold, bool syntheticItalic);
+
+    RefPtr<Font> font(const FontDescription&, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy);
 
     static void appendSources(CSSFontFace&, CSSValueList&, Document*, bool isInitiatingElementInUserAgentShadowTree);
 
@@ -158,7 +160,7 @@ public:
 private:
     CSSFontFace(CSSFontSelector*, StyleRuleFontFace*, FontFace*, bool isLocalFallback);
 
-    size_t pump();
+    size_t pump(ExternalResourceDownloadPolicy);
     void setStatus(Status);
     void notifyClientsOfFontPropertyChange();
 
index 95e32bd..471fca3 100644 (file)
@@ -332,11 +332,13 @@ void CSSFontSelector::beginLoadingFontSoon(CachedFont& font)
     if (!m_document)
         return;
 
-    m_fontsToBeginLoading.append(&font);
-    // Increment the request count now, in order to prevent didFinishLoad from being dispatched
-    // after this font has been requested but before it began loading. Balanced by
-    // decrementRequestCount() in beginLoadTimerFired() and in clearDocument().
-    m_document->cachedResourceLoader().incrementRequestCount(font);
+    if (!m_document->settings().webFontsAlwaysFallBack()) {
+        m_fontsToBeginLoading.append(&font);
+        // Increment the request count now, in order to prevent didFinishLoad from being dispatched
+        // after this font has been requested but before it began loading. Balanced by
+        // decrementRequestCount() in beginLoadTimerFired() and in clearDocument().
+        m_document->cachedResourceLoader().incrementRequestCount(font);
+    }
     m_beginLoadingTimer.startOneShot(0_s);
 }
 
index 6a3ddab..e36a77a 100644 (file)
@@ -66,10 +66,14 @@ public:
         return adoptRef(*new CSSFontAccessor(fontFace, fontDescription, syntheticBold, syntheticItalic));
     }
 
-    const Font* font() const final
+    const Font* font(ExternalResourceDownloadPolicy policy) const final
     {
-        if (!m_result)
-            m_result = m_fontFace->font(m_fontDescription, m_syntheticBold, m_syntheticItalic);
+        if (!m_result || (policy == ExternalResourceDownloadPolicy::Allow
+            && (m_fontFace->status() == CSSFontFace::Status::Pending || m_fontFace->status() == CSSFontFace::Status::Loading || m_fontFace->status() == CSSFontFace::Status::TimedOut))) {
+            const auto result = m_fontFace->font(m_fontDescription, m_syntheticBold, m_syntheticItalic, policy);
+            if (!m_result)
+                m_result = result;
+        }
         return m_result.value().get();
     }
 
@@ -121,11 +125,9 @@ FontRanges CSSSegmentedFontFace::fontRanges(const FontDescription& fontDescripti
             bool syntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && !isFontWeightBold(selectionCapabilities.weight.maximum) && isFontWeightBold(desiredRequest.weight);
             bool syntheticItalic = (fontDescription.fontSynthesis() & FontSynthesisStyle) && !isItalic(selectionCapabilities.slope.maximum) && isItalic(desiredRequest.slope);
 
-            // This doesn't trigger an unnecessary download because every element styled with this family will need font metrics in order to run layout.
             // Metrics used for layout come from FontRanges::fontForFirstRange(), which assumes that the first font is non-null.
-            // We're kicking off this necessary first download now.
             auto fontAccessor = CSSFontAccessor::create(face, fontDescription, syntheticBold, syntheticItalic);
-            if (result.isNull() && !fontAccessor->font())
+            if (result.isNull() && !fontAccessor->font(ExternalResourceDownloadPolicy::Forbid))
                 continue;
             appendFont(result, WTFMove(fontAccessor), face->ranges());
         }
index 293d07e..ebbc90e 100644 (file)
@@ -55,15 +55,16 @@ unsigned GlyphPage::s_count = 0;
 const float smallCapsFontSizeMultiplier = 0.7f;
 const float emphasisMarkFontSizeMultiplier = 0.5f;
 
-Font::Font(const FontPlatformData& platformData, Origin origin, Interstitial interstitial, OrientationFallback orientationFallback)
+Font::Font(const FontPlatformData& platformData, Origin origin, Interstitial interstitial, Visibility visibility, OrientationFallback orientationFallback)
     : m_maxCharWidth(-1)
     , m_avgCharWidth(-1)
     , m_platformData(platformData)
     , m_mathData(nullptr)
+    , m_origin(origin)
+    , m_visibility(visibility)
     , m_treatAsFixedPitch(false)
-    , m_origin(origin == Origin::Remote)
     , m_isInterstitial(interstitial == Interstitial::Yes)
-    , m_isTextOrientationFallback(orientationFallback == OrientationFallback::Fallback)
+    , m_isTextOrientationFallback(orientationFallback == OrientationFallback::Yes)
     , m_isBrokenIdeographFallback(false)
     , m_hasVerticalGlyphs(false)
     , m_isUsedInSystemFallbackCache(false)
@@ -75,7 +76,7 @@ Font::Font(const FontPlatformData& platformData, Origin origin, Interstitial int
     platformGlyphInit();
     platformCharWidthInit();
 #if ENABLE(OPENTYPE_VERTICAL)
-    if (platformData.orientation() == Vertical && orientationFallback == OrientationFallback::NoFallback) {
+    if (platformData.orientation() == Vertical && orientationFallback == OrientationFallback::No) {
         m_verticalData = FontCache::singleton().verticalData(platformData);
         m_hasVerticalGlyphs = m_verticalData.get() && m_verticalData->hasVerticalMetrics();
     }
@@ -266,7 +267,7 @@ const Font& Font::verticalRightOrientationFont() const
         m_derivedFontData = std::make_unique<DerivedFonts>();
     if (!m_derivedFontData->verticalRightOrientation) {
         auto verticalRightPlatformData = FontPlatformData::cloneWithOrientation(m_platformData, Horizontal);
-        m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, origin(), Interstitial::No, OrientationFallback::Fallback);
+        m_derivedFontData->verticalRightOrientation = create(verticalRightPlatformData, origin(), Interstitial::No, Visibility::Visible, OrientationFallback::Yes);
     }
     ASSERT(m_derivedFontData->verticalRightOrientation != this);
     return *m_derivedFontData->verticalRightOrientation;
@@ -277,7 +278,7 @@ const Font& Font::uprightOrientationFont() const
     if (!m_derivedFontData)
         m_derivedFontData = std::make_unique<DerivedFonts>();
     if (!m_derivedFontData->uprightOrientation)
-        m_derivedFontData->uprightOrientation = create(m_platformData, origin(), Interstitial::No, OrientationFallback::Fallback);
+        m_derivedFontData->uprightOrientation = create(m_platformData, origin(), Interstitial::No, Visibility::Visible, OrientationFallback::Yes);
     ASSERT(m_derivedFontData->uprightOrientation != this);
     return *m_derivedFontData->uprightOrientation;
 }
index def8c4f..20c9661 100644 (file)
@@ -84,13 +84,17 @@ public:
         Yes,
         No
     };
+    enum class Visibility {
+        Visible,
+        Invisible
+    };
     enum class OrientationFallback {
-        Fallback,
-        NoFallback
+        Yes,
+        No
     };
-    static Ref<Font> create(const FontPlatformData& platformData, Origin origin = Origin::Local, Interstitial interstitial = Interstitial::No, OrientationFallback orientationFallback = OrientationFallback::NoFallback)
+    static Ref<Font> create(const FontPlatformData& platformData, Origin origin = Origin::Local, Interstitial interstitial = Interstitial::No, Visibility visibility = Visibility::Visible, OrientationFallback orientationFallback = OrientationFallback::No)
     {
-        return adoptRef(*new Font(platformData, origin, interstitial, orientationFallback));
+        return adoptRef(*new Font(platformData, origin, interstitial, visibility, orientationFallback));
     }
 
     WEBCORE_EXPORT ~Font();
@@ -181,8 +185,9 @@ public:
     void determinePitch();
     Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; }
 
-    Origin origin() const { return m_origin ? Origin::Remote : Origin::Local; }
+    Origin origin() const { return m_origin; }
     bool isInterstitial() const { return m_isInterstitial; }
+    Visibility visibility() const { return m_visibility; }
 
 #ifndef NDEBUG
     String description() const;
@@ -220,7 +225,7 @@ public:
 #endif
 
 private:
-    Font(const FontPlatformData&, Origin, Interstitial, OrientationFallback);
+    Font(const FontPlatformData&, Origin, Interstitial, Visibility, OrientationFallback);
 
     void platformInit();
     void platformGlyphInit();
@@ -303,8 +308,10 @@ private:
     mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties;
 #endif
 
+    Origin m_origin; // Whether or not we are custom font loaded via @font-face
+    Visibility m_visibility; // @font-face's internal timer can cause us to show fonts even when a font is being downloaded.
+
     unsigned m_treatAsFixedPitch : 1;
-    unsigned m_origin : 1; // Whether or not we are custom font loaded via @font-face
     unsigned m_isInterstitial : 1; // Whether or not this custom font is the last resort placeholder for a loading font
 
     unsigned m_isTextOrientationFallback : 1;
index 9df2e5d..79c6ade 100644 (file)
@@ -202,7 +202,6 @@ public:
 
     WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, FontSelectionSpecifiedCapabilities fontFaceCapabilities = { }, bool checkingAlternateName = false);
     WEBCORE_EXPORT Ref<Font> lastResortFallbackFont(const FontDescription&);
-    Ref<Font> lastResortFallbackFontForEveryCharacter(const FontDescription&);
     WEBCORE_EXPORT Ref<Font> fontForPlatformData(const FontPlatformData&);
     RefPtr<Font> similarFont(const FontDescription&, const AtomicString& family);
 
index 6f1786d..f20ee38 100644 (file)
@@ -307,12 +307,6 @@ float FontCascade::glyphBufferForTextRun(CodePath codePathToUse, const TextRun&
 
 float FontCascade::drawText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, unsigned from, std::optional<unsigned> to, CustomFontNotReadyAction customFontNotReadyAction) const
 {
-    // Don't draw anything while we are using custom fonts that are in the process of loading,
-    // except if the 'force' argument is set to true (in which case it will use a fallback
-    // font).
-    if (isLoadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady)
-        return 0;
-
     unsigned destination = to.value_or(run.length());
 
     CodePath codePathToUse = codePath(run);
@@ -327,7 +321,7 @@ float FontCascade::drawText(GraphicsContext& context, const TextRun& run, const
         return 0;
     // Draw the glyph buffer now at the starting point returned in startX.
     FloatPoint startPoint(startX, point.y());
-    drawGlyphBuffer(context, glyphBuffer, startPoint);
+    drawGlyphBuffer(context, glyphBuffer, startPoint, customFontNotReadyAction);
     return startPoint.x() - startX;
 }
 
@@ -1400,7 +1394,15 @@ void FontCascade::drawEmphasisMarksForSimpleText(GraphicsContext& context, const
     drawEmphasisMarks(context, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
 }
 
-void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& glyphBuffer, FloatPoint& point) const
+inline bool shouldDrawIfLoading(const Font& font, FontCascade::CustomFontNotReadyAction customFontNotReadyAction)
+{
+    // Don't draw anything while we are using custom fonts that are in the process of loading,
+    // except if the 'customFontNotReadyAction' argument is set to UseFallbackIfFontNotReady
+    // (in which case "font" will be a fallback font).
+    return !font.isInterstitial() || font.visibility() == Font::Visibility::Visible || customFontNotReadyAction == FontCascade::CustomFontNotReadyAction::UseFallbackIfFontNotReady;
+}
+
+void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& glyphBuffer, FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const
 {
     // Draw each contiguous run of glyphs that use the same font data.
     const Font* fontData = glyphBuffer.fontAt(0);
@@ -1415,7 +1417,8 @@ void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& g
         FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph);
 
         if (nextFontData != fontData || nextOffset != offset) {
-            context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+            if (shouldDrawIfLoading(*fontData, customFontNotReadyAction))
+                context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
 
             lastFrom = nextGlyph;
             fontData = nextFontData;
@@ -1428,7 +1431,8 @@ void FontCascade::drawGlyphBuffer(GraphicsContext& context, const GlyphBuffer& g
         nextGlyph++;
     }
 
-    context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+    if (shouldDrawIfLoading(*fontData, customFontNotReadyAction))
+        context.drawGlyphs(*this, *fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
     point.setX(nextX);
 }
 
@@ -1473,7 +1477,7 @@ void FontCascade::drawEmphasisMarks(GraphicsContext& context, const GlyphBuffer&
     }
     markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0);
 
-    drawGlyphBuffer(context, markBuffer, startPoint);
+    drawGlyphBuffer(context, markBuffer, startPoint, CustomFontNotReadyAction::DoNotPaintIfFontNotReady);
 }
 
 float FontCascade::floatWidthForSimpleText(const TextRun& run, HashSet<const Font*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
index 9424995..771c6fb 100644 (file)
@@ -221,7 +221,7 @@ private:
     // Returns the initial in-stream advance.
     float getGlyphsAndAdvancesForSimpleText(const TextRun&, unsigned from, unsigned to, GlyphBuffer&, ForTextEmphasisOrNot = NotForTextEmphasis) const;
     void drawEmphasisMarksForSimpleText(GraphicsContext&, const TextRun&, const AtomicString& mark, const FloatPoint&, unsigned from, unsigned to) const;
-    void drawGlyphBuffer(GraphicsContext&, const GlyphBuffer&, FloatPoint&) const;
+    void drawGlyphBuffer(GraphicsContext&, const GlyphBuffer&, FloatPoint&, CustomFontNotReadyAction) const;
     void drawEmphasisMarks(GraphicsContext&, const GlyphBuffer&, const AtomicString&, const FloatPoint&) const;
     float floatWidthForSimpleText(const TextRun&, HashSet<const Font*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
     int offsetForPositionForSimpleText(const TextRun&, float position, bool includePartialGlyphs) const;
index ff8968c..286798e 100644 (file)
@@ -350,13 +350,21 @@ GlyphData FontCascadeFonts::glyphDataForSystemFallback(UChar32 c, const FontCasc
 
 GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 c, const FontCascadeDescription& description, FontVariant variant, unsigned fallbackIndex)
 {
+    ExternalResourceDownloadPolicy policy = ExternalResourceDownloadPolicy::Allow;
+    GlyphData loadingResult;
     while (true) {
         auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex++);
         if (fontRanges.isNull())
             break;
-        GlyphData data = fontRanges.glyphDataForCharacter(c);
+        GlyphData data = fontRanges.glyphDataForCharacter(c, policy);
         if (!data.font)
             continue;
+        if (data.font->isInterstitial()) {
+            policy = ExternalResourceDownloadPolicy::Forbid;
+            if (!loadingResult.font)
+                loadingResult = data;
+            continue;
+        }
         // The variantFont function should not normally return 0.
         // But if it does, we will just render the capital letter big.
         if (const Font* variantFont = data.font->variantFont(description, variant))
@@ -364,18 +372,28 @@ GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 c, const FontCascadeDesc
         return data;
     }
 
+    if (loadingResult.font)
+        return loadingResult;
     return glyphDataForSystemFallback(c, description, variant);
 }
 
 GlyphData FontCascadeFonts::glyphDataForNormalVariant(UChar32 c, const FontCascadeDescription& description)
 {
+    ExternalResourceDownloadPolicy policy = ExternalResourceDownloadPolicy::Allow;
+    GlyphData loadingResult;
     for (unsigned fallbackIndex = 0; ; ++fallbackIndex) {
         auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex);
         if (fontRanges.isNull())
             break;
-        GlyphData data = fontRanges.glyphDataForCharacter(c);
+        GlyphData data = fontRanges.glyphDataForCharacter(c, policy);
         if (!data.font)
             continue;
+        if (data.font->isInterstitial()) {
+            policy = ExternalResourceDownloadPolicy::Forbid;
+            if (!loadingResult.font)
+                loadingResult = data;
+            continue;
+        }
         if (data.font->platformData().orientation() == Vertical && !data.font->isTextOrientationFallback()) {
             if (!FontCascade::isCJKIdeographOrSymbol(c))
                 return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data);
@@ -389,6 +407,8 @@ GlyphData FontCascadeFonts::glyphDataForNormalVariant(UChar32 c, const FontCasca
         return data;
     }
 
+    if (loadingResult.font)
+        return loadingResult;
     return glyphDataForSystemFallback(c, description, NormalVariant);
 }
 
@@ -397,13 +417,20 @@ static RefPtr<GlyphPage> glyphPageFromFontRanges(unsigned pageNumber, const Font
     const Font* font = nullptr;
     UChar32 pageRangeFrom = pageNumber * GlyphPage::size;
     UChar32 pageRangeTo = pageRangeFrom + GlyphPage::size - 1;
+    auto policy = ExternalResourceDownloadPolicy::Allow;
     for (unsigned i = 0; i < fontRanges.size(); ++i) {
         auto& range = fontRanges.rangeAt(i);
-        if (range.to()) {
-            if (range.from() <= pageRangeFrom && pageRangeTo <= range.to())
-                font = range.font();
-            break;
+        if (range.from() <= pageRangeFrom && pageRangeTo <= range.to()) {
+            font = range.font(policy);
+            if (!font)
+                continue;
+            if (font->isInterstitial()) {
+                font = nullptr;
+                policy = ExternalResourceDownloadPolicy::Forbid;
+                continue;
+            }
         }
+        break;
     }
     if (!font || font->platformData().orientation() == Vertical)
         return nullptr;
index 048c263..21612c7 100644 (file)
@@ -33,9 +33,9 @@
 
 namespace WebCore {
 
-const Font* FontRanges::Range::font() const
+const Font* FontRanges::Range::font(ExternalResourceDownloadPolicy policy) const
 {
-    return m_fontAccessor->font();
+    return m_fontAccessor->font(policy);
 }
 
 FontRanges::FontRanges()
@@ -55,7 +55,7 @@ private:
     {
     }
 
-    const Font* font() const final
+    const Font* font(ExternalResourceDownloadPolicy) const final
     {
         return m_font.get();
     }
@@ -78,28 +78,44 @@ FontRanges::~FontRanges()
 {
 }
 
-GlyphData FontRanges::glyphDataForCharacter(UChar32 character) const
+GlyphData FontRanges::glyphDataForCharacter(UChar32 character, ExternalResourceDownloadPolicy policy) const
 {
+    const Font* resultFont = nullptr;
     for (auto& range : m_ranges) {
         if (range.from() <= character && character <= range.to()) {
-            if (auto* font = range.font()) {
-                auto glyphData = font->glyphDataForCharacter(character);
-                if (glyphData.glyph)
-                    return glyphData;
+            if (auto* font = range.font(policy)) {
+                if (font->isInterstitial()) {
+                    policy = ExternalResourceDownloadPolicy::Forbid;
+                    if (!resultFont)
+                        resultFont = font;
+                } else {
+                    auto glyphData = font->glyphDataForCharacter(character);
+                    if (glyphData.glyph)
+                        return glyphData;
+                }
             }
         }
     }
+    if (resultFont) {
+        // We want higher-level code to be able to differentiate between
+        // "The interstitial font doesn't have the character" and
+        // "The real downloaded font doesn't have the character".
+        GlyphData result = resultFont->glyphDataForCharacter(character);
+        if (!result.font)
+            result.font = resultFont;
+        return result;
+    }
     return GlyphData();
 }
 
 const Font* FontRanges::fontForCharacter(UChar32 character) const
 {
-    return glyphDataForCharacter(character).font;
+    return glyphDataForCharacter(character, ExternalResourceDownloadPolicy::Allow).font;
 }
 
 const Font& FontRanges::fontForFirstRange() const
 {
-    auto* font = m_ranges[0].font();
+    auto* font = m_ranges[0].font(ExternalResourceDownloadPolicy::Forbid);
     ASSERT(font);
     return *font;
 }
index 4fe0bec..3f72c71 100644 (file)
@@ -34,6 +34,11 @@ namespace WebCore {
 
 class FontAccessor;
 
+enum class ExternalResourceDownloadPolicy {
+    Forbid,
+    Allow
+};
+
 class FontRanges {
 public:
     struct Range {
@@ -57,7 +62,7 @@ public:
 
         UChar32 from() const { return m_from; }
         UChar32 to() const { return m_to; }
-        const Font* font() const;
+        const Font* font(ExternalResourceDownloadPolicy) const;
         const FontAccessor& fontAccessor() const { return m_fontAccessor; }
 
     private:
@@ -79,7 +84,7 @@ public:
     unsigned size() const { return m_ranges.size(); }
     const Range& rangeAt(unsigned i) const { return m_ranges[i]; }
 
-    GlyphData glyphDataForCharacter(UChar32) const;
+    GlyphData glyphDataForCharacter(UChar32, ExternalResourceDownloadPolicy) const;
     WEBCORE_EXPORT const Font* fontForCharacter(UChar32) const;
     WEBCORE_EXPORT const Font& fontForFirstRange() const;
     bool isLoading() const;
index 7f5f323..8f7cbb0 100644 (file)
@@ -39,7 +39,7 @@ class FontAccessor : public RefCounted<FontAccessor> {
 public:
     virtual ~FontAccessor() { }
 
-    virtual const Font* font() const = 0;
+    virtual const Font* font(ExternalResourceDownloadPolicy) const = 0;
     virtual bool isLoading() const = 0;
 };
 
index 754c4b1..756b399 100644 (file)
@@ -123,11 +123,6 @@ Vector<String> FontCache::systemFontFamilies()
     return fontFamilies;
 }
 
-Ref<Font> FontCache::lastResortFallbackFontForEveryCharacter(const FontDescription& fontDescription)
-{
-    return lastResortFallbackFont(fontDescription);
-}
-
 Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
 {
     // We want to return a fallback font here, otherwise the logic preventing FontConfig
index 80a0240..9d9146d 100644 (file)
@@ -149,11 +149,4 @@ Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescripti
 
 #endif // PLATFORM(MAC)
 
-Ref<Font> FontCache::lastResortFallbackFontForEveryCharacter(const FontDescription& fontDescription)
-{
-    auto result = fontForFamily(fontDescription, AtomicString("LastResort", AtomicString::ConstructFromLiteral));
-    ASSERT(result);
-    return *result;
-}
-
 } // namespace WebCore
index 2053bf6..8f78b9d 100644 (file)
@@ -338,11 +338,6 @@ RefPtr<Font> FontCache::fontFromDescriptionAndLogFont(const FontDescription& fon
     return fontData;
 }
 
-Ref<Font> FontCache::lastResortFallbackFontForEveryCharacter(const FontDescription& fontDescription)
-{
-    return lastResortFallbackFont(fontDescription);
-}
-
 Ref<Font> FontCache::lastResortFallbackFont(const FontDescription& fontDescription)
 {
     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, fallbackFontName, ());