RESOLVED FIXED 202548
FontFaceSet's ready promise is not always resolved
https://bugs.webkit.org/show_bug.cgi?id=202548
Summary FontFaceSet's ready promise is not always resolved
Myles C. Maxfield
Reported 2019-10-03 16:29:09 PDT
When we do layout on an element, FontRanges::glyphDataForCharacter() will cause the first available font to start loading, but will continue looking at subsequent fonts to see if there's a good one we can render while the load is happening. When looking for a fallback font, it calls FontRanges::Range::font() with a ExternalResourceDownloadPolicy set to Forbid. This is fine, except that a side effect of calling this function is that the CSSFontFace marks itself as Loading, which means document.fonts.ready is deferred. Then, the load finishes, and the subsequent CSSFontFace is never actually used, meaning it never exits the Loading state, which means document.fonts.ready never fires.
Attachments
Test (1.68 KB, patch)
2019-10-03 16:29 PDT, Myles C. Maxfield
no flags
Patch (5.86 KB, patch)
2019-10-03 17:00 PDT, Myles C. Maxfield
youennf: review+
Myles C. Maxfield
Comment 1 2019-10-03 16:29:39 PDT
Myles C. Maxfield
Comment 2 2019-10-03 16:30:32 PDT
This was discovered during Chris's investigation of https://bugs.webkit.org/show_bug.cgi?id=202476.
Myles C. Maxfield
Comment 3 2019-10-03 16:33:14 PDT
The fix for this is likely in CSSFontFace::pump() where it setStatus(Status::Loading) right before source->load().
Myles C. Maxfield
Comment 4 2019-10-03 17:00:13 PDT
Chris Dumez
Comment 5 2019-10-04 08:36:04 PDT
Comment on attachment 380169 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380169&action=review > Source/WebCore/css/CSSFontFace.cpp:740 > + if (policy == ExternalResourceDownloadPolicy::Allow && m_status == Status::Pending) I have one concern. Imagine the case where this is a local font and this local font ends up being successful and Policy is ExternalResourceDownloadPolicy::Forbid. Previously, we would do: 1. Status update: Pending -> Loading 2. Call load() 3. Then below, source->status() returns Success 4. Status update: Loading -> Success (See switch below). After your change: 1. Call load() without status update, so we are Pending 2. Then below, source->status() returns Success 3. Status update: Pending -> Loading Now we're stuck in Loading state even though our source successfully loaded, right?
Chris Dumez
Comment 6 2019-10-04 08:39:07 PDT
Comment on attachment 380169 [details] Patch cq- to make sure this does not inadvertently committed before answering my comment. If my comment is inaccurate, feel free to override and cq+.
Myles C. Maxfield
Comment 7 2019-10-10 12:38:52 PDT
Comment on attachment 380169 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380169&action=review >> Source/WebCore/css/CSSFontFace.cpp:740 >> + if (policy == ExternalResourceDownloadPolicy::Allow && m_status == Status::Pending) > > I have one concern. > > Imagine the case where this is a local font and this local font ends up being successful and Policy is ExternalResourceDownloadPolicy::Forbid. Previously, we would do: > 1. Status update: Pending -> Loading > 2. Call load() > 3. Then below, source->status() returns Success > 4. Status update: Loading -> Success (See switch below). > > After your change: > 1. Call load() without status update, so we are Pending > 2. Then below, source->status() returns Success > 3. Status update: Pending -> Loading > > Now we're stuck in Loading state even though our source successfully loaded, right? The next two lines after the Pending -> Loading transition is if (m_status == Status::Loading || m_status == Status::TimedOut) setStatus(Status::Success); We just set the status to Loading, so we take this "if" branch and set the status to success. I'll add a test.
Myles C. Maxfield
Comment 8 2019-10-10 12:42:25 PDT
Radar WebKit Bug Importer
Comment 9 2019-10-10 12:43:17 PDT
Chris Dumez
Comment 10 2019-10-10 12:53:02 PDT
Comment on attachment 380169 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=380169&action=review >>> Source/WebCore/css/CSSFontFace.cpp:740 >>> + if (policy == ExternalResourceDownloadPolicy::Allow && m_status == Status::Pending) >> >> I have one concern. >> >> Imagine the case where this is a local font and this local font ends up being successful and Policy is ExternalResourceDownloadPolicy::Forbid. Previously, we would do: >> 1. Status update: Pending -> Loading >> 2. Call load() >> 3. Then below, source->status() returns Success >> 4. Status update: Loading -> Success (See switch below). >> >> After your change: >> 1. Call load() without status update, so we are Pending >> 2. Then below, source->status() returns Success >> 3. Status update: Pending -> Loading >> >> Now we're stuck in Loading state even though our source successfully loaded, right? > > The next two lines after the Pending -> Loading transition is > > if (m_status == Status::Loading || m_status == Status::TimedOut) > setStatus(Status::Success); > > We just set the status to Loading, so we take this "if" branch and set the status to success. > > I'll add a test. Oh, duh. Sorry about the noise.
Note You need to log in before you can comment on or make changes to this bug.