Bug 202794 - Make CSS font shorthands parsable within a worker (i.e. without CSSValuePool)
Summary: Make CSS font shorthands parsable within a worker (i.e. without CSSValuePool)
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Canvas (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Lord
URL:
Keywords: InRadar
Depends on:
Blocks: 183720 186759 202793
  Show dependency treegraph
 
Reported: 2019-10-10 06:16 PDT by Chris Lord
Modified: 2020-11-20 13:44 PST (History)
19 users (show)

See Also:


Attachments
Patch (56.10 KB, patch)
2019-10-10 08:19 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (48.39 KB, patch)
2020-09-24 04:28 PDT, Chris Lord
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (48.40 KB, patch)
2020-09-24 04:36 PDT, Chris Lord
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (48.49 KB, patch)
2020-09-24 04:53 PDT, Chris Lord
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (49.19 KB, patch)
2020-09-24 06:37 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (48.13 KB, patch)
2020-09-28 03:56 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (48.32 KB, patch)
2020-09-28 08:56 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (61.41 KB, patch)
2020-09-29 02:45 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (61.58 KB, patch)
2020-10-05 06:56 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (61.58 KB, patch)
2020-10-15 08:20 PDT, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (61.59 KB, patch)
2020-11-18 05:00 PST, Chris Lord
no flags Details | Formatted Diff | Diff
Patch (63.15 KB, patch)
2020-11-18 07:08 PST, Chris Lord
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Lord 2019-10-10 06:16:14 PDT
This bug will track making fonts stylable within a Worker. Currently styling requires a Document, but the spec says font styling on OffscreenCanvas should be possible within a worker using WorkerGlobalScope as the font provider, essentially.
Comment 1 Chris Lord 2019-10-10 08:19:33 PDT
Created attachment 380642 [details]
Patch
Comment 2 Ryosuke Niwa 2019-10-10 13:11:19 PDT
As far as I know, none of objects in StyleResolver are thread safe. We need to very carefully separate those parts that are thread safe & make things needed for this work thread safe.
Comment 3 Chris Lord 2020-09-24 04:28:13 PDT
Created attachment 409552 [details]
Patch
Comment 4 Chris Lord 2020-09-24 04:32:49 PDT
A work-in-progress that I'm submitting to see failures on EWS. This patch adds CSSParser::parseFontWorkerSafe (perhaps should be called parseFontShortHandWorkerSafe...) which does the equivalent of parseColorWorkerSafe, but for font short-hands - that is, it does the CSS parsing using consume*Raw functions so as to bypass use of the CSSValuePool (which isn't thread-safe and isn't necessary as we really just want the end result, not any of the CSS gubbins).

To test this new path, this also adds a bunch of code that ultimately should be shared somehow with StyleBuilder* to apply font styling from raw font style data, as opposed to CSSValue via a StyleBuilder. It'd be good to have a static function somewhere that can do this.

Not handled in this WIP - Removing the need for Document, doing anything with FontResolver, anything to do with OffscreenCanvas.
Comment 5 Chris Lord 2020-09-24 04:36:43 PDT
Created attachment 409553 [details]
Patch
Comment 6 Chris Lord 2020-09-24 04:53:27 PDT
Created attachment 409557 [details]
Patch
Comment 7 Chris Lord 2020-09-24 06:37:33 PDT
Created attachment 409567 [details]
Patch
Comment 8 Chris Lord 2020-09-28 03:56:42 PDT
Created attachment 409882 [details]
Patch
Comment 9 Chris Lord 2020-09-28 08:56:45 PDT
Created attachment 409889 [details]
Patch
Comment 10 Chris Lord 2020-09-29 02:20:33 PDT
I'm hijacking this bug to track what this has turned into - further work will be needed for fonts to be fully stylable in a worker, but the first step is to make CSS font shorthands safely parsable.
Comment 11 Chris Lord 2020-09-29 02:45:19 PDT
Created attachment 409978 [details]
Patch
Comment 12 Chris Lord 2020-09-29 02:54:37 PDT
Some notes about this patch;

This is kind of the font equivalent of bug 204575, which made full CSS colour parsing available to OffscreenCanvas. This makes font shorthand parsing available to non-main-thread users by adding 'Raw' equivalents of the necessary consume* CSS parsing functions and removing the use of CSSValue. parseFontWorkerSafe returns a FontRaw object that contains the raw values of the font longhands.

To make this still useful, it also adds Style::ResolveForFontRaw, which takes a FontRaw and other necessary values and returns a RenderStyle. This removes the use of StyleBuilder, which also dependeded on using CSSValue. I've added a new file for this, somewhat in the vein of Style::ResolveForDocument.

To add test coverage and reduce the difference between HTMLCanvas and OffscreenCanvas, I've used this function in CanvasRenderingContext2D::setFont, which is also, pleasantly, a code reduction in that area.

There is some duplication between ResolveForFontRaw and StyleBuilderCustom/StyleBuilderConverter - not a huge amount, but I'd gladly take advice on the best way to get rid of it, as well of course as any feedback on this entire patch and design.

This is most of the work necessary for OffscreenCanvas on the main thread to provide font functions, but there are still lots of not-worker-safe/enabled bits of code left to work through for font support in a Worker.
Comment 13 Antti Koivisto 2020-09-29 05:58:02 PDT
Comment on attachment 409978 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=409978&action=review

> Source/WebCore/style/StyleResolveForFontRaw.cpp:49
> +Optional<RenderStyle> resolveForFontRaw(const FontRaw& fontRaw, FontCascadeDescription&& fontDescription, Document& document)
> +{
> +    auto fontStyle = RenderStyle::create();

RenderStyle is not thread safe. It consists of non-atomically refcounted substructures (in DataRef<> fields). For example RenderStyle::create simply clones the static default style, sharing all substructures.
Comment 14 Chris Lord 2020-09-29 06:25:49 PDT
(In reply to Antti Koivisto from comment #13)
> Comment on attachment 409978 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=409978&action=review
> 
> > Source/WebCore/style/StyleResolveForFontRaw.cpp:49
> > +Optional<RenderStyle> resolveForFontRaw(const FontRaw& fontRaw, FontCascadeDescription&& fontDescription, Document& document)
> > +{
> > +    auto fontStyle = RenderStyle::create();
> 
> RenderStyle is not thread safe. It consists of non-atomically refcounted
> substructures (in DataRef<> fields). For example RenderStyle::create simply
> clones the static default style, sharing all substructures.

ok, that's good to know - in a future bug, we would need a function to either create a worker-safe RenderStyle, or to replace RenderStyle with something that could hold the same relevant data and be worker-safe. For this patch, it's not really an issue, I don't think.
Comment 15 Chris Lord 2020-10-05 06:56:48 PDT
Created attachment 410511 [details]
Patch
Comment 16 Darin Adler 2020-10-10 13:12:11 PDT
Comment on attachment 410511 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=410511&action=review

So much new code here. Wisher there was a way to save a bit more and make the code a bit less repetitive.

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:140
> +        if (category != CalculationCategory::Percent) {
> +            if (!allowPercentOf || (category != CalculationCategory::PercentLength
> +                && category != CalculationCategory::PercentNumber))
> +                return WTF::nullopt;
> +        }

This logic is confusing enough that I think it might require a helper function. I also find the bool argument here both subtle and confusing.

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:217
> +        if (const CSSCalcValue* calcValue = calcParser.value()) {

auto

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:423
> +                if (auto result = calcParser.consumePercentRaw(true))

This mysterious "true" is something we try to avoid in WebKit code.

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:1970
> +    String familyName = concatenateFamilyName(range);
> +    if (familyName.isNull())
> +        return WTF::nullopt;
> +    return familyName;

This shows that Optional<String> is a strange type. Since String already has a null value built in, it’s overkill to use Optional with it, despite the fact that it seems logical to do that. Another possibilities to change concatenateFamilyName to return Optional<String>.

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:2090
> +AtomString genericFontFamilyFromValueID(CSSValueID ident)

This could return const AtomString&.

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:2108
> +        return { };

This would return nullAtom.

> Source/WebCore/style/StyleBuilderCustom.h:995
>                  isGenericFamily = true;

The old code used to say "default break". The new code instead sets isGenericFamily to true and calls CSSPropertyParserHelpers::genericFontFamilyFromValueID. Is that going to do the right thing?

> Source/WebCore/style/StyleResolveForFontRaw.cpp:199
> +            return static_cast<float>(CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, length.type, length.value));

Here we are casting to a float.

> Source/WebCore/style/StyleResolveForFontRaw.cpp:201
> +            return (parentSize * percentage) / 100.f;

Here we are writing an expression that will return a double without casting to a float. Unclear why the difference.
Comment 17 Chris Lord 2020-10-15 02:53:20 PDT
(In reply to Darin Adler from comment #16)
> Comment on attachment 410511 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=410511&action=review
> 
> So much new code here. Wisher there was a way to save a bit more and make
> the code a bit less repetitive.
> 
> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:140
> > +        if (category != CalculationCategory::Percent) {
> > +            if (!allowPercentOf || (category != CalculationCategory::PercentLength
> > +                && category != CalculationCategory::PercentNumber))
> > +                return WTF::nullopt;
> > +        }
> 
> This logic is confusing enough that I think it might require a helper
> function. I also find the bool argument here both subtle and confusing.

I agree, I didn't really like this when I wrote it but nothing immediately came to mind... I'll try again.

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:217
> > +        if (const CSSCalcValue* calcValue = calcParser.value()) {
> 
> auto

Will change

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:423
> > +                if (auto result = calcParser.consumePercentRaw(true))
> 
> This mysterious "true" is something we try to avoid in WebKit code.

I'll try to find something better for this (and failing that, at least an enum or something).

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:1970
> > +    String familyName = concatenateFamilyName(range);
> > +    if (familyName.isNull())
> > +        return WTF::nullopt;
> > +    return familyName;
> 
> This shows that Optional<String> is a strange type. Since String already has
> a null value built in, it’s overkill to use Optional with it, despite the
> fact that it seems logical to do that. Another possibilities to change
> concatenateFamilyName to return Optional<String>.

You're right, I'll change to just using String.

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:2090
> > +AtomString genericFontFamilyFromValueID(CSSValueID ident)
> 
> This could return const AtomString&.

Thanks, well spotted.

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:2108
> > +        return { };
> 
> This would return nullAtom.

I'll change this to emptyAtom() (which I assumed { } was, obviously incorrectly)

> > Source/WebCore/style/StyleBuilderCustom.h:995
> >                  isGenericFamily = true;
> 
> The old code used to say "default break". The new code instead sets
> isGenericFamily to true and calls
> CSSPropertyParserHelpers::genericFontFamilyFromValueID. Is that going to do
> the right thing?

The test for family.isEmpty() should mean that the default case still does the same thing with the above change.

> > Source/WebCore/style/StyleResolveForFontRaw.cpp:199
> > +            return static_cast<float>(CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, length.type, length.value));
> 
> Here we are casting to a float.
> 
> > Source/WebCore/style/StyleResolveForFontRaw.cpp:201
> > +            return (parentSize * percentage) / 100.f;
> 
> Here we are writing an expression that will return a double without casting
> to a float. Unclear why the difference.

This is just a mistake, I'll add the cast to the latter.
Comment 18 Chris Lord 2020-10-15 03:25:07 PDT
Something else that needs fixing up, genericFontFamilyFromValueID isn't worker-safe at all. Need to figure out what to do with that... Ideally, some kind of thread-safe never destroyed AtomString would be great...
Comment 19 Chris Lord 2020-10-15 05:15:45 PDT
(In reply to Chris Lord from comment #18)
> Something else that needs fixing up, genericFontFamilyFromValueID isn't
> worker-safe at all. Need to figure out what to do with that... Ideally, some
> kind of thread-safe never destroyed AtomString would be great...

Just to note, I misread and this isn't required for parsing, so can be dealt with in another bug.
Comment 20 Chris Lord 2020-10-15 08:20:20 PDT
Created attachment 411444 [details]
Patch
Comment 21 Chris Lord 2020-10-15 08:39:13 PDT
(In reply to Darin Adler from comment #16)
> Comment on attachment 410511 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=410511&action=review
> 
> So much new code here. Wisher there was a way to save a bit more and make
> the code a bit less repetitive.

The biggest cause of repetition is having to single out calc values, but I'm not sure how best to address that without some more major refactoring...

The rest of these issues I've solved in what I would consider the most obvious way, and this specifically;

> > Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:140
> > +        if (category != CalculationCategory::Percent) {
> > +            if (!allowPercentOf || (category != CalculationCategory::PercentLength
> > +                && category != CalculationCategory::PercentNumber))
> > +                return WTF::nullopt;
> > +        }
> 
> This logic is confusing enough that I think it might require a helper
> function. I also find the bool argument here both subtle and confusing.

I removed this weird logic and bool parameter and just added consumeLengthOrPercentRaw to CSSCalcParser, which makes more sense I think.
Comment 22 Chris Lord 2020-11-18 04:58:10 PST
Just to note, I plan on landing this soon - I've been manually checking OffscreenCanvas test results with main-thread font functions on a subsequent patch to make sure that behaviour doesn't deviate from canvas and from expectations. The results look good, which gives me a bit of extra confidence about landing this. Worst case scenario, we can back it out anyway.
Comment 23 Chris Lord 2020-11-18 05:00:45 PST
Created attachment 414439 [details]
Patch
Comment 24 Chris Lord 2020-11-18 07:08:12 PST
Created attachment 414446 [details]
Patch
Comment 25 EWS 2020-11-18 08:04:14 PST
Committed r269957: <https://trac.webkit.org/changeset/269957>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 414446 [details].
Comment 26 Radar WebKit Bug Importer 2020-11-18 08:05:18 PST
<rdar://problem/71539772>
Comment 27 Don Olmstead 2020-11-20 12:02:35 PST
Comment on attachment 414446 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=414446&action=review

> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:1957
> +            if (fontStyleIsWithinRange(angleInDegrees))

This is defined statically within CSSPropertyParser.cpp so this file is not buildable outside a unified build.
Comment 28 Darin Adler 2020-11-20 13:06:51 PST
Comment on attachment 414446 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=414446&action=review

>> Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:1957
>> +            if (fontStyleIsWithinRange(angleInDegrees))
> 
> This is defined statically within CSSPropertyParser.cpp so this file is not buildable outside a unified build.

Are you going to fix that, should I, or do you want Chris to fix it?
Comment 29 Darin Adler 2020-11-20 13:18:44 PST
(In reply to Darin Adler from comment #28)
> Are you going to fix that, should I, or do you want Chris to fix it?

I have a few minutes so I am working on a fix, but I might not get it done fast enough.
Comment 30 Darin Adler 2020-11-20 13:44:42 PST
Fixing in bug 219222.