Ignore:
Timestamp:
Apr 21, 2021, 12:07:50 PM (4 years ago)
Author:
[email protected]
Message:

[css-counter-styles] Parse @counter-style descriptors
https://bugs.webkit.org/show_bug.cgi?id=224718

Patch by Tyler Wilcock <Tyler Wilcock> on 2021-04-21
Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Parsing for all @counter-style descriptors is implemented with this
patch, so mark more tests passing.

You'll notice that some @counter-style descriptors implemented in this
patch did not gain any passing tests (e.g. pad, negative). In all
of these cases, the expected results contain a <string> value, and we
fail only because we incorrectly don't serialize these <string> values
with quotes. I have manually confirmed in all cases that these values
are properly parsed, so it's just the serialization that's incorrect.

These <string> values serialize without quotes because WebKit's representation
of custom identifiers is not a separate type, but instead overloaded onto the
CSS_STRING type. This means that during serialization time, WebKit must guess
whether it is actually serializing a string (and include quotes if so), or if
it's serializing a custom ident (leaving off quotes if so).

Relevant code snippet:

https://github.com/WebKit/WebKit/blob/36caeec07975bd5f47db8ac6b749c2787230a461/Source/WebCore/css/CSSMarkup.cpp#L153#L161

Relevant changelog snippet from David Hyatt, 2016-12-07:

We also overload CSS_STRING primitive value type and have it act as both a string
and a custom identifier. This is lame, since the parser should have made two different
types of objects instead, but since our parser doesn't do that yet, I added a serializeAsStringOrCustomIdent
that preserves our old behavior of "quote the string only if needed." In this case what
that really meant was "Try to guess that we were originally a custom ident and leave off
quotes if so." This function will go away once we properly create CSSStringValues and
CSSCustomIdentValues instead of turning the latter into strings.

  • web-platform-tests/css/css-counter-styles/counter-style-fallback-expected.txt:
  • web-platform-tests/css/css-counter-styles/counter-style-prefix-suffix-syntax-expected.txt:
  • web-platform-tests/css/css-counter-styles/counter-style-range-syntax-expected.txt:
  • web-platform-tests/css/css-counter-styles/counter-style-speak-as-syntax-expected.txt:
  • web-platform-tests/css/css-counter-styles/counter-style-system-syntax-expected.txt:

Source/WebCore:

Implement parsing and CSSCounterStyleRule IDL interface for @counter-style descriptors.
See spec for full details on all descriptors:

https://drafts.csswg.org/css-counter-styles-3/#the-counter-style-rule

Test: webexposed/counter-style-image-symbols-not-exposed.html and WPTs

  • css/CSSComputedStyleDeclaration.cpp:

(WebCore::ComputedStyleExtractor::valueForPropertyInStyle):
Return nullptr for new @counter-style descriptor properties.

  • css/CSSCounterStyleRule.cpp:

(WebCore::toCounterStyleSystemEnum):
(WebCore::symbolsValidForSystem):
(WebCore::StyleRuleCounterStyle::newValueInvalidOrEqual const):
(WebCore::CSSCounterStyleRule::cssText const):
(WebCore::CSSCounterStyleRule::setName):
(WebCore::CSSCounterStyleRule::setterInternal):
(WebCore::CSSCounterStyleRule::setSystem):
(WebCore::CSSCounterStyleRule::setNegative):
(WebCore::CSSCounterStyleRule::setPrefix):
(WebCore::CSSCounterStyleRule::setSuffix):
(WebCore::CSSCounterStyleRule::setRange):
(WebCore::CSSCounterStyleRule::setPad):
(WebCore::CSSCounterStyleRule::setFallback):
(WebCore::CSSCounterStyleRule::setSymbols):
(WebCore::CSSCounterStyleRule::setAdditiveSymbols):
(WebCore::CSSCounterStyleRule::setSpeakAs):
Implement setters and tangential functionality required by setters.

  • css/CSSCounterStyleRule.h:

Replace FIXME with actual descriptor getter and setter
implementations.

  • css/CSSProperties.json:

Add @counter-style descriptor properties.

  • css/CSSValueKeywords.in:

Add new values required for system and speak-as
@counter-style descriptor properties.

  • css/parser/CSSPropertyParser.cpp:

(WebCore::consumeCounterStyleSystem):
(WebCore::consumeCounterStyleSymbol):
(WebCore::consumeCounterStyleNegative):
(WebCore::consumeCounterStyleRangeBound):
(WebCore::consumeCounterStyleRange):
(WebCore::consumeCounterStylePad):
(WebCore::consumeCounterStyleSymbols):
(WebCore::consumeCounterStyleAdditiveSymbols):
(WebCore::consumeCounterStyleSpeakAs):
(WebCore::CSSPropertyParser::parseCounterStyleDescriptor):
Parse @counter-style descriptors.

Tools:

  • DumpRenderTree/TestOptions.cpp:

(WTR::TestOptions::defaults):
Fix typo (missing 's'). CSSCounterStyleAtRulesEnabled, not
CSSCounterStyleAtRuleEnabled.

LayoutTests:

Add test ensuring <image> @counter-style symbol values cannot be
parsed when the counterStyleAtRuleImageSymbolsEnabled feature flag
is disabled.

---

This test is skipped on Windows because I haven't been able to get the
required feature flags (CSSCounterStyleAtRulesEnabled and
CSSCounterStyleAtRuleImageSymbolsEnabled) to work properly for that
port.

The code hidden behind these flags is all in the CSS parser, which is not
unique to Windows, so I think we can be confident that if the test passes
on all other platforms, that the behavior is correct on Windows too.

One attempt at implementing the necessary Windows-specific flag functionality is here:

https://bugs.webkit.org/attachment.cgi?id=426371&action=edit

Which failed to compile[1] with this error:

C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp(834,51): error C2039: 'setCSSCounterStyleAtRulesEnabled': is not a member of 'IWebPreferencesPrivate7' [C:\cygwin\home\buildbot\worker\Windows-EWS\build\WebKitBuild\Release\Tools\DumpRenderTree\DumpRenderTreeLib.vcxproj]
C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp(835,62): error C2039: 'setCSSCounterStyleAtRuleImageSymbolsEnabled': is not a member of 'IWebPreferencesPrivate7' [C:\cygwin\home\buildbot\worker\Windows-EWS\build\WebKitBuild\Release\Tools\DumpRenderTree\DumpRenderTreeLib.vcxproj]

Those methods are present in IWebPreferencesPrivate7.idl, and implemented similarly to other
flags in other places (e.g. win/WebPreferences.{h, cpp}, win/WebPreferenceKeysPrivate.h).
I can't reproduce this compilation error on my Windows machine.

I then tried removing the lines that caused the above compilation failure.
Those setters are called in DumpRenderTree::enableExperimentalFeatures, so in
lieu of enabling these flags there I could enable the flag I need via test header.

That patch is: https://bugs.webkit.org/attachment.cgi?id=426509&action=edit

This results in successful compilation, but causes lots (all?) of the
layout tests to fail[2] with a stacktrace that looks like:

00 00000065738fdf00 00007ffc3e9e3113 WebKit!WebPreferences::speechRecognitionEnabled(int * enabled = 0x00007ffc`3eae0f50)+0x29 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Source\WebKitLegacy\win\WebPreferences.cpp @ 2617]
01 00000065738fdf30 00007ffc3e9e3cc0 DumpRenderTreeLib!resetWebPreferencesToConsistentValues(struct IWebPreferences * preferences = 0x00000205`e2f204b0)+0x63 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp @ 847]
02 00000065738fdfa0 00007ffc3e9e4171 DumpRenderTreeLib!resetWebViewToConsistentStateBeforeTesting(class WTR::TestOptions * options = 0x00000065`738fea60)+0x2e0 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp @ 1054]
03 00000065738fe050 00007ffc3e9e67d3 DumpRenderTreeLib!runTest(class std::basic_string<char,std::char_traits<char>,std::allocator<char> > * inputLine = <Value unavailable error>)+0x2f1 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp @ 1239]
04 00000065738feca0 00007ff789952f30 DumpRenderTreeLib!main(int argc = <Value unavailable error>, char argv = <Value unavailable error>)+0x5d3 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\DumpRenderTree\win\DumpRenderTree.cpp @ 1676]
05 00000065738ff5b0 00007ff789953884 DumpRenderTree!main(int argc = 0n2, char
argv = 0x00000205`e2e74b80)+0x880 [C:\cygwin\home\buildbot\worker\Windows-EWS\build\Tools\win\DLLLauncher\DLLLauncherMain.cpp @ 232]

I haven't done much digging into why this happens, and cannot reproduce it on my Windows machine.

[1]: https://ews-build.webkit.org/#/builders/10/builds/86747
[2]: https://ews-build.webkit.org/#/builders/10/builds/86897

  • platform/win/TestExpectations: Skip newly added test on Windows.
  • webexposed/counter-style-image-symbols-not-exposed-expected.txt: Added.
  • webexposed/counter-style-image-symbols-not-exposed.html: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/css/CSSCounterStyleRule.cpp

    r276152 r276380  
    2727#include "CSSCounterStyleRule.h"
    2828
     29#include "CSSPropertyParser.h"
     30#include "CSSStyleSheet.h"
     31#include "CSSTokenizer.h"
     32#include "Pair.h"
     33#include <wtf/text/StringBuilder.h>
     34
    2935namespace WebCore {
    3036   
     
    4147}
    4248
     49static CounterStyleSystem toCounterStyleSystemEnum(RefPtr<CSSValue> system)
     50{
     51    if (!system || !system->isPrimitiveValue())
     52        return CounterStyleSystem::Symbolic;
     53
     54    auto& primitiveSystemValue = downcast<CSSPrimitiveValue>(*system);
     55    ASSERT(primitiveSystemValue.isValueID() || primitiveSystemValue.isPair());
     56    CSSValueID systemKeyword = CSSValueInvalid;
     57    if (primitiveSystemValue.isValueID())
     58        systemKeyword = primitiveSystemValue.valueID();
     59    else if (auto* pair = primitiveSystemValue.pairValue()) {
     60        // This value must be `fixed` or `extends`, both of which can or must have an additional component.
     61        auto firstValue = pair->first();
     62        ASSERT(firstValue && firstValue->isValueID());
     63        if (firstValue)
     64            systemKeyword = firstValue->valueID();
     65    }
     66
     67    switch (systemKeyword) {
     68    case CSSValueCyclic:
     69        return CounterStyleSystem::Cyclic;
     70    case CSSValueFixed:
     71        return CounterStyleSystem::Fixed;
     72    case CSSValueSymbolic:
     73        return CounterStyleSystem::Symbolic;
     74    case CSSValueAlphabetic:
     75        return CounterStyleSystem::Alphabetic;
     76    case CSSValueNumeric:
     77        return CounterStyleSystem::Numeric;
     78    case CSSValueAdditive:
     79        return CounterStyleSystem::Additive;
     80    case CSSValueExtends:
     81        return CounterStyleSystem::Extends;
     82    default:
     83        ASSERT_NOT_REACHED();
     84        return CounterStyleSystem::Symbolic;
     85    }
     86}
     87
     88static bool symbolsValidForSystem(CounterStyleSystem system, RefPtr<CSSValue> symbols, RefPtr<CSSValue> additiveSymbols)
     89{
     90    switch (system) {
     91    case CounterStyleSystem::Cyclic:
     92    case CounterStyleSystem::Fixed:
     93    case CounterStyleSystem::Symbolic:
     94        return symbols && symbols->isValueList() && downcast<CSSValueList>(*symbols).length();
     95    case CounterStyleSystem::Alphabetic:
     96    case CounterStyleSystem::Numeric:
     97        return symbols && symbols->isValueList() && downcast<CSSValueList>(*symbols).length() >= 2u;
     98    case CounterStyleSystem::Additive:
     99        return additiveSymbols && additiveSymbols->isValueList() && downcast<CSSValueList>(*additiveSymbols).length();
     100    case CounterStyleSystem::Extends:
     101        return !symbols && !additiveSymbols;
     102    default:
     103        ASSERT_NOT_REACHED();
     104        return false;
     105    }
     106}
     107
     108bool StyleRuleCounterStyle::newValueInvalidOrEqual(CSSPropertyID propertyID, const RefPtr<CSSValue> newValue) const
     109{
     110    auto currentValue = m_properties->getPropertyCSSValue(propertyID);
     111    if (compareCSSValuePtr(currentValue, newValue))
     112        return true;
     113
     114    RefPtr<CSSValue> symbols;
     115    RefPtr<CSSValue> additiveSymbols;
     116    switch (propertyID) {
     117    case CSSPropertySystem:
     118        // If the attribute being set is `system`, and the new value would change the algorithm used, do nothing
     119        // and abort these steps.
     120        // (It's okay to change an aspect of the algorithm, like the first symbol value of a `fixed` system.)
     121        return toCounterStyleSystemEnum(currentValue) != toCounterStyleSystemEnum(newValue);
     122    case CSSPropertySymbols:
     123        symbols = newValue;
     124        additiveSymbols = m_properties->getPropertyCSSValue(CSSPropertyAdditiveSymbols);
     125        break;
     126    case CSSPropertyAdditiveSymbols:
     127        symbols = m_properties->getPropertyCSSValue(CSSPropertySymbols);
     128        additiveSymbols = newValue;
     129        break;
     130    default:
     131        return false;
     132    }
     133    auto system = m_properties->getPropertyCSSValue(CSSPropertySystem);
     134    return symbolsValidForSystem(toCounterStyleSystemEnum(system), symbols, additiveSymbols);
     135}
     136
    43137StyleRuleCounterStyle::~StyleRuleCounterStyle() = default;
    44138
     
    65159String CSSCounterStyleRule::cssText() const
    66160{
    67     // FIXME: Implement this function when we parse @counter-style descriptors.
    68     return emptyString();
     161    String systemText = system();
     162    const char* systemPrefix = systemText.isEmpty() ? "" : " system: ";
     163    const char* systemSuffix = systemText.isEmpty() ? "" : ";";
     164
     165    String symbolsText = symbols();
     166    const char* symbolsPrefix = symbolsText.isEmpty() ? "" : " symbols: ";
     167    const char* symbolsSuffix = symbolsText.isEmpty() ? "" : ";";
     168
     169    String additiveSymbolsText = additiveSymbols();
     170    const char* additiveSymbolsPrefix = additiveSymbolsText.isEmpty() ? "" : " additive-symbols: ";
     171    const char* additiveSymbolsSuffix = additiveSymbolsText.isEmpty() ? "" : ";";
     172
     173    String negativeText = negative();
     174    const char* negativePrefix = negativeText.isEmpty() ? "" : " negative: ";
     175    const char* negativeSuffix = negativeText.isEmpty() ? "" : ";";
     176
     177    String prefixText = prefix();
     178    const char* prefixTextPrefix = prefixText.isEmpty() ? "" : " prefix: ";
     179    const char* prefixTextSuffix = prefixText.isEmpty() ? "" : ";";
     180
     181    String suffixText = suffix();
     182    const char* suffixTextPrefix = suffixText.isEmpty() ? "" : " suffix: ";
     183    const char* suffixTextSuffix = suffixText.isEmpty() ? "" : ";";
     184
     185    String padText = pad();
     186    const char* padPrefix = padText.isEmpty() ? "" : " pad: ";
     187    const char* padSuffix = padText.isEmpty() ? "" : ";";
     188
     189    String rangeText = range();
     190    const char* rangePrefix = rangeText.isEmpty() ? "" : " range: ";
     191    const char* rangeSuffix = rangeText.isEmpty() ? "" : ";";
     192
     193    String fallbackText = fallback();
     194    const char* fallbackPrefix = fallbackText.isEmpty() ? "" : " fallback: ";
     195    const char* fallbackSuffix = fallbackText.isEmpty() ? "" : ";";
     196
     197    String speakAsText = speakAs();
     198    const char* speakAsPrefix = speakAsText.isEmpty() ? "" : " speak-as: ";
     199    const char* speakAsSuffix = speakAsText.isEmpty() ? "" : ";";
     200
     201    return makeString("@counter-style ", name(), " {", systemPrefix, systemText, systemSuffix, symbolsPrefix, symbolsText, symbolsSuffix, additiveSymbolsPrefix, additiveSymbolsText, additiveSymbolsSuffix, negativePrefix, negativeText, negativeSuffix, prefixTextPrefix, prefixText, prefixTextSuffix, suffixTextPrefix, suffixText, suffixTextSuffix, padPrefix, padText, padSuffix, rangePrefix, rangeText, rangeSuffix, fallbackPrefix, fallbackText, fallbackSuffix, speakAsPrefix, speakAsText, speakAsSuffix, " }");
    69202}
    70203
     
    74207}
    75208
     209// https://drafts.csswg.org/css-counter-styles-3/#dom-csscounterstylerule-name
     210void CSSCounterStyleRule::setName(const String& text)
     211{
     212    auto tokens = CSSTokenizer(text).tokenRange();
     213    auto name = CSSPropertyParserHelpers::consumeCounterStyleNameInPrelude(tokens);
     214    if (name.isNull() || name == m_counterStyleRule->name())
     215        return;
     216
     217    CSSStyleSheet::RuleMutationScope mutationScope(this);
     218    m_counterStyleRule->setName(name);
     219}
     220
     221void CSSCounterStyleRule::setterInternal(CSSPropertyID propertyID, const String& valueText)
     222{
     223    auto tokens = CSSTokenizer(valueText).tokenRange();
     224    auto newValue = CSSPropertyParser::parseCounterStyleDescriptor(propertyID, tokens, parserContext());
     225    if (m_counterStyleRule->newValueInvalidOrEqual(propertyID, newValue))
     226        return;
     227
     228    CSSStyleSheet::RuleMutationScope mutationScope(this);
     229    m_counterStyleRule->mutableProperties().setProperty(propertyID, WTFMove(newValue));
     230}
     231
     232void CSSCounterStyleRule::setSystem(const String& text)
     233{
     234    setterInternal(CSSPropertySystem, text);
     235}
     236
     237void CSSCounterStyleRule::setNegative(const String& text)
     238{
     239    setterInternal(CSSPropertyNegative, text);
     240}
     241
     242void CSSCounterStyleRule::setPrefix(const String& text)
     243{
     244    setterInternal(CSSPropertyPrefix, text);
     245}
     246
     247void CSSCounterStyleRule::setSuffix(const String& text)
     248{
     249    setterInternal(CSSPropertySuffix, text);
     250}
     251
     252void CSSCounterStyleRule::setRange(const String& text)
     253{
     254    setterInternal(CSSPropertyRange, text);
     255}
     256
     257void CSSCounterStyleRule::setPad(const String& text)
     258{
     259    setterInternal(CSSPropertyPad, text);
     260}
     261
     262void CSSCounterStyleRule::setFallback(const String& text)
     263{
     264    setterInternal(CSSPropertyFallback, text);
     265}
     266
     267void CSSCounterStyleRule::setSymbols(const String& text)
     268{
     269    setterInternal(CSSPropertySymbols, text);
     270}
     271
     272void CSSCounterStyleRule::setAdditiveSymbols(const String& text)
     273{
     274    setterInternal(CSSPropertyAdditiveSymbols, text);
     275}
     276
     277void CSSCounterStyleRule::setSpeakAs(const String& text)
     278{
     279    setterInternal(CSSPropertySpeakAs, text);
     280}
     281
    76282} // namespace WebCore
Note: See TracChangeset for help on using the changeset viewer.