Bug 186344

Summary: font-synthesis inline/computed style in non-canonical
Product: WebKit Reporter: Eric Willigers <ericwilligers>
Component: CSSAssignee: Joonghun Park <jh718.park>
Status: RESOLVED FIXED    
Severity: Normal CC: darin, esprehn+autocc, ews-watchlist, glenn, gsnedders, gyuyoung.kim, jh718.park, koivisto, macpherson, menard, mmaxfield, simon.fraser, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: Safari 11   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Update layout test results for ios and common
none
Canonicalize CSSValueList at parsing stage in consumeFontSynthesis
none
Patch for landing none

Description Eric Willigers 2018-06-06 04:08:48 PDT
Test case:
https://jsfiddle.net/ericwilligers/2q9cmrha/

Spec:
https://drafts.csswg.org/css-fonts-3/#propdef-font-synthesis
none | [ weight || style ]

If the values  'weight style'  or  'style weight'  are assigned, the computed style should be 'weight style', but WebKit gives  'style weight'.
Comment 1 Myles C. Maxfield 2018-06-18 15:59:45 PDT
I thought the spec was in the process of changing the syntax fairly dramatically? I mean, we can flip the order super easily, but if we're going to be changing our implementation substantially, we may want to wait.
Comment 2 Sam Sneddon [:gsnedders] 2021-10-20 08:34:58 PDT
Looks like we're gaining interoperability on that ordering, so I guess we should:

https://wpt.fyi/results/css/css-fonts/parsing/font-synthesis-valid.html?label=master&label=experimental&product=chrome&product=firefox&product=webkitgtk&product=safari&aligned
Comment 3 Joonghun Park 2021-11-02 17:59:21 PDT
Created attachment 443153 [details]
Patch
Comment 4 Joonghun Park 2021-11-02 18:54:30 PDT
Comment on attachment 443153 [details]
Patch

Layout test results will be updated with the next patchset.
Comment 5 Myles C. Maxfield 2021-11-02 19:08:07 PDT
Comment on attachment 443153 [details]
Patch

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

r=mews

> Source/WebCore/css/StyleProperties.cpp:173
> +        case CSSPropertyFontSynthesis:
> +            return fontSynthesisPropertyValue(*value);

This is a surprising place for this code. Why is this here and not below (or maybe even in another function?)
Comment 6 Simon Fraser (smfr) 2021-11-02 20:04:30 PDT
Comment on attachment 443153 [details]
Patch

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

>> Source/WebCore/css/StyleProperties.cpp:173
>> +            return fontSynthesisPropertyValue(*value);
> 
> This is a surprising place for this code. Why is this here and not below (or maybe even in another function?)

Falling through from CSSPropertyStrokeOpacity and returning a fontSynthesisPropertyValue() doesn't seem right.
Comment 7 Joonghun Park 2021-11-02 21:05:05 PDT
Comment on attachment 443153 [details]
Patch

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

>>> Source/WebCore/css/StyleProperties.cpp:173
>>> +            return fontSynthesisPropertyValue(*value);
>> 
>> This is a surprising place for this code. Why is this here and not below (or maybe even in another function?)
> 
> Falling through from CSSPropertyStrokeOpacity and returning a fontSynthesisPropertyValue() doesn't seem right.

I moved this callsite to above. Originally I placed this here because it was right before default switch but I didn't see the FALLTHROUGH.
And it seems that below place is for shorthand properties, so I will place this call as below.

if (auto value = getPropertyCSSValue(propertyID)) {
        switch (propertyID) {
        case CSSPropertyFontSynthesis:
            return fontSynthesisPropertyValue(*value);
        case CSSPropertyFillOpacity:
        case CSSPropertyFloodOpacity:
...
Comment 8 Joonghun Park 2021-11-02 22:23:50 PDT
Created attachment 443174 [details]
Patch
Comment 9 Joonghun Park 2021-11-02 22:59:56 PDT
I updated layout test result on Ubuntu 21.10
and will update layout test result on macOS 12.0.1 too in the next patchset.
Comment 10 Joonghun Park 2021-11-03 06:07:11 PDT
Created attachment 443195 [details]
Patch
Comment 11 Joonghun Park 2021-11-03 06:19:48 PDT
Could you please review this patch again? I think this patch is ready for review now.
Comment 12 Joonghun Park 2021-11-04 00:39:26 PDT
Created attachment 443281 [details]
Update layout test results for ios and common
Comment 13 Joonghun Park 2021-11-04 13:55:14 PDT
Hi, please review this patch when you are available. It seems that ews has passed.
Comment 14 Myles C. Maxfield 2021-11-05 17:23:47 PDT
Comment on attachment 443281 [details]
Update layout test results for ios and common

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

> Source/WebCore/css/StyleProperties.cpp:505
> +String StyleProperties::fontSynthesisPropertyValue(CSSValue& value) const
> +{
> +    String valueStr = value.cssText();
> +    if (isCSSWideValueKeyword(valueStr) || is<CSSPrimitiveValue>(value))
> +        return valueStr;
> +
> +    if (is<CSSValueList>(value)) {
> +        bool hasWeight = false;
> +        bool hasStyle = false;
> +        if (downcast<CSSValueList>(value).hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueWeight).ptr()))
> +            hasWeight = true;
> +        if (downcast<CSSValueList>(value).hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueStyle).ptr()))
> +            hasStyle = true;
> +        return makeString(hasWeight ? "weight" : "", (hasWeight && hasStyle) ? " " : "", hasStyle ? "style" : "");
> +    }
> +
> +    return String();
> +}

I think, instead of writing this function, we should change consumeFontSynthesis() in CSSPropertyParser.cpp to just canonicalize the list at parsing time. Canonicalizing at parse time would be a simpler solution, and there would not have to be another special case here - StyleProperties::getPropertyValue() would be entirely unmodified.

Maybe something like this:

    bool foundWeight = false;
    bool foundStyle = false;
    bool foundSmallCaps = false;
    while (true) {
        auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
        if (!ident)
            break;
        switch (ident->valueID()) {
        case CSSValueWeight:
            if (foundWeight)
                return nullptr;
            foundWeight = true;
        case CSSValueStyle:
            if (foundStyle)
                return nullptr;
            foundStyle = true;
        case CSSValueSmallCaps:
            if (foundSmallCaps)
                return nullptr;
            foundSmallCaps = true;
        }
    }

    RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
    if (foundWeight)
        list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueWeight));
    if (foundStyle)
        list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueStyle));
    if (foundSmallCaps)
        list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSmallCaps));

    if (!list->length())
        return nullptr;
    return list;

It's probably even possible to make this more concise by using lambdas.
Comment 15 Joonghun Park 2021-11-05 20:39:37 PDT
Comment on attachment 443281 [details]
Update layout test results for ios and common

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

>> Source/WebCore/css/StyleProperties.cpp:505
>> +}
> 
> I think, instead of writing this function, we should change consumeFontSynthesis() in CSSPropertyParser.cpp to just canonicalize the list at parsing time. Canonicalizing at parse time would be a simpler solution, and there would not have to be another special case here - StyleProperties::getPropertyValue() would be entirely unmodified.
> 
> Maybe something like this:
> 
>     bool foundWeight = false;
>     bool foundStyle = false;
>     bool foundSmallCaps = false;
>     while (true) {
>         auto ident = consumeIdent<CSSValueWeight, CSSValueStyle, CSSValueSmallCaps>(range);
>         if (!ident)
>             break;
>         switch (ident->valueID()) {
>         case CSSValueWeight:
>             if (foundWeight)
>                 return nullptr;
>             foundWeight = true;
>         case CSSValueStyle:
>             if (foundStyle)
>                 return nullptr;
>             foundStyle = true;
>         case CSSValueSmallCaps:
>             if (foundSmallCaps)
>                 return nullptr;
>             foundSmallCaps = true;
>         }
>     }
> 
>     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
>     if (foundWeight)
>         list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueWeight));
>     if (foundStyle)
>         list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueStyle));
>     if (foundSmallCaps)
>         list->append(CSSValuePool::singleton().createIdentifierValue(CSSValueSmallCaps));
> 
>     if (!list->length())
>         return nullptr;
>     return list;
> 
> It's probably even possible to make this more concise by using lambdas.

Thank you for your review, Myles:) I applied your comment in the next patchset and will upload it soon.
Comment 16 Joonghun Park 2021-11-05 21:51:33 PDT
Created attachment 443477 [details]
Canonicalize CSSValueList at parsing stage in consumeFontSynthesis
Comment 17 Myles C. Maxfield 2021-11-06 00:45:04 PDT
Comment on attachment 443477 [details]
Canonicalize CSSValueList at parsing stage in consumeFontSynthesis

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

> Source/WebCore/css/parser/CSSPropertyParser.cpp:995
> +    auto makeCanonicalizedList = [](CSSParserTokenRange& range) -> RefPtr<CSSValueList> {

The fact that this code is in a lambda doesn't seem to be useful. When I mentioned "lambda" I was suggesting using it as a tool to reduce (nearly-)duplicated code.

If it's not possible to reduce (nearly-)duplicated code, I suggest we promote this code to no longer be within a lambda.
Comment 18 Joonghun Park 2021-11-06 04:57:58 PDT
Comment on attachment 443477 [details]
Canonicalize CSSValueList at parsing stage in consumeFontSynthesis

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

>> Source/WebCore/css/parser/CSSPropertyParser.cpp:995
>> +    auto makeCanonicalizedList = [](CSSParserTokenRange& range) -> RefPtr<CSSValueList> {
> 
> The fact that this code is in a lambda doesn't seem to be useful. When I mentioned "lambda" I was suggesting using it as a tool to reduce (nearly-)duplicated code.
> 
> If it's not possible to reduce (nearly-)duplicated code, I suggest we promote this code to no longer be within a lambda.

I added a small tiny lambda below, except that I promoted all the codes to no longer be within a lambda in the next patchset. Thank you for your review.

auto checkAndMarkExistence = [](bool* found) {
    if (*found)
        return false;
    return *found = true;
};
Comment 19 Joonghun Park 2021-11-06 05:00:30 PDT
Created attachment 443482 [details]
Patch for landing
Comment 20 EWS 2021-11-06 16:07:32 PDT
Committed r285383 (243940@main): <https://commits.webkit.org/243940@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 443482 [details].
Comment 21 Radar WebKit Bug Importer 2021-11-06 16:08:17 PDT
<rdar://problem/85105405>
Comment 22 Myles C. Maxfield 2021-11-06 16:18:00 PDT
Committed r285384 (243941@main): <https://commits.webkit.org/243941@main>