[css-counter-styles] Parse and add feature flag for @counter-style
https://bugs.webkit.org/show_bug.cgi?id=223150

Patch by Tyler Wilcock <twilco.o@protonmail.com> on 2021-04-16
Reviewed by Darin Adler.

LayoutTests/imported/w3c:

@counter-style and its descriptors are now exposed behind a feature
flag, so pass some tests and fail others for a different reason
because this patch doesn't actually implement descriptor parsing and
setting yet.

* web-platform-tests/css/css-counter-styles/counter-style-additive-symbols-syntax-expected.txt:
* web-platform-tests/css/css-counter-styles/counter-style-fallback-expected.txt:
* web-platform-tests/css/css-counter-styles/counter-style-name-syntax-expected.txt:
* web-platform-tests/css/css-counter-styles/counter-style-negative-syntax-expected.txt:
* web-platform-tests/css/css-counter-styles/counter-style-pad-syntax-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-symbols-syntax-expected.txt:
* web-platform-tests/css/css-counter-styles/counter-style-system-syntax-expected.txt:
* web-platform-tests/css/css-counter-styles/idlharness-expected.txt:
* web-platform-tests/css/cssom/CSSCounterStyleRule-expected.txt:

Source/WebCore:

Parse @counter-style (without implementing descriptor parsing) behind
a new feature flag, CSSCounterStyleAtRulesEnabled.  A separate feature
flag for @counter-style <image> symbol values has also been added, as
image symbols have extra complexities that we won't want to hold the
entire feature back on.
https://www.w3.org/TR/css-counter-styles-3

The CSSCounterStyleRule IDL interface is also added and implemented,
and similarly feature flagged.
https://www.w3.org/TR/css-counter-styles-3/#apis

Test: webexposed/counter-style-is-not-exposed.html and existing WPTs.

* CMakeLists.txt:
Add CSSCounterStyleRule.idl.

* DerivedSources-input.xcfilelist:
Add CSSCounterStyleRule.idl.

* DerivedSources-output.xcfilelist:
Add JSCSSCounterStyleRule.h and JSCSSCounterStyleRule.cpp.

* DerivedSources.make:
Add CSSCounterStyleRule.idl.

* Sources.txt:
Add CSSCounterStyleRule.cpp and JSCSSCounterStyleRule.cpp.

* WebCore.xcodeproj/project.pbxproj:
Add CounterStyle.h, CounterStyle.cpp, and CounterStyle.idl.

* bindings/js/JSCSSRuleCustom.cpp:
(WebCore::toJSNewlyCreated):
Support CSSCounterStyleRule.

* bindings/js/WebCoreBuiltinNames.h:
Add macro(CSSCounterStyleRule) to generate counter-style built-in
names.

* css/CSSCounterStyleRule.cpp: Added.
(WebCore::StyleRuleCounterStyle::StyleRuleCounterStyle):
(WebCore::StyleRuleCounterStyle::create):
(WebCore::StyleRuleCounterStyle::mutableProperties):
(WebCore::CSSCounterStyleRule::CSSCounterStyleRule):
(WebCore::CSSCounterStyleRule::reattach):
(WebCore::CSSCounterStyleRule::cssText const):

* css/CSSCounterStyleRule.h: Added.
(isType): Add specialized rule.isCounterStyleRule() implementation.

* css/CSSCounterStyleRule.idl: Added.
* css/CSSRule.cpp: Add new StyleRuleType::CounterStyle COMPILE_ASSERT.
* css/CSSRule.h:
Add COUNTER_STYLE_RULE constant.  Also add comment `// WebIDL enum` to
disable enum_casing lint, since these values are named to match IDL
attributes.
* css/CSSRule.idl: Add COUNTER_STYLE_RULE constant behind flag.

* css/StyleRule.cpp:
(WebCore::StyleRuleBase::destroy):
(WebCore::StyleRuleBase::copy const):
(WebCore::StyleRuleBase::createCSSOMWrapper const):
Handle newly added StyleRuleType::CounterStyle.

* css/StyleRule.h:
(WebCore::StyleRuleBase::isCounterStyleRule const): Added.

* css/StyleRuleType.h: Add StyleRuleType::CounterStyle

* css/StyleSheetContents.cpp:
(WebCore::traverseRulesInVector):
(WebCore::StyleSheetContents::traverseSubresources const):
If @counter-style image symbols flag is enabled, do traverse
subresources.

* css/parser/CSSAtRuleID.cpp:
If the @counter-style feature flag is enabled, return newly added
CSSAtRuleCounterStyle ID.

(WebCore::cssAtRuleID):
* css/parser/CSSAtRuleID.h:
Add CSSAtRuleCounterStyle ID.

* css/parser/CSSParserContext.cpp:
(WebCore::operator==):
* css/parser/CSSParserContext.h:
(WebCore::CSSParserContextHash::hash):
Add cssCounterStyleAtRulesEnabled and
cssCounterStyleAtRuleImageSymbolsEnabled flags.

* css/parser/CSSParserFastPaths.cpp:
(WebCore::CSSParserFastPaths::isValidKeywordPropertyAndValue):
Refactor to use new CSSPropertyParserHelpers::isPredefinedCounterStyle
method.

* css/parser/CSSParserImpl.cpp:
(WebCore::computeNewAllowedRules):
(WebCore::CSSParserImpl::consumeAtRule):
(WebCore::CSSParserImpl::consumeCounterStyleRule): Added.
(WebCore::CSSParserImpl::consumeDeclarationList):
(WebCore::CSSParserImpl::consumeDeclaration):
* css/parser/CSSParserImpl.h:
Handle new @counter-style rule.

* css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::parseValue): Handle
StyleRuleType::CounterStyle.
(WebCore::consumeCounterContent):
Refactor to use new CSSPropertyParserHelpers::isPredefinedCounterStyle
method.
(WebCore::CSSPropertyParser::parseCounterStyleDescriptor):
Add placeholder for future @counter-style descriptor parsing work.

* css/parser/CSSPropertyParser.h:
Add parseCounterStyleDescriptor method

* css/parser/CSSPropertyParserHelpers.h:
* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::consumeCustomIdent):
Add new flag that allows consumed custom ident values to be lowercased
on-parse.  This is necessary for some <counter-style-name> values.
(WebCore::CSSPropertyParserHelpers::isPredefinedCounterStyle): Added.
(WebCore::CSSPropertyParserHelpers::consumeCounterStyleName):
(WebCore::CSSPropertyParserHelpers::consumeCounterStyleNameInPrelude):
Parse @counter-style names, both in-prelude names and non-prelude
names (e.g. as part of the `extends` descriptor).

Source/WTF:

* Scripts/Preferences/WebPreferencesExperimental.yaml:
 Add CSSCounterStyleAtRulesEnabled and CSSCounterStyleAtRuleImageSymbolsEnabled flags.

Tools:

* DumpRenderTree/TestOptions.cpp:
(WTR::TestOptions::defaults):
Add `false` defaults for CSSCounterStyleAtRulesEnabled and
CSSCounterStyleAtRuleImageSymbolsEnabled flags.

LayoutTests:

Add tests ensuring @counter-style is not exposed when feature flag is
disabled.

* webexposed/counter-style-is-not-exposed-expected.txt: Added.
* webexposed/counter-style-is-not-exposed.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@276152 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt
index d36d5a5..816993d 100644
--- a/Source/WebCore/CMakeLists.txt
+++ b/Source/WebCore/CMakeLists.txt
@@ -736,6 +736,7 @@
     crypto/parameters/RsaPssParams.idl
 
     css/CSSConditionRule.idl
+    css/CSSCounterStyleRule.idl
     css/CSSFontFaceRule.idl
     css/CSSGroupingRule.idl
     css/CSSImportRule.idl
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index bf4a7d2..0256d48 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,137 @@
+2021-04-16  Tyler Wilcock  <twilco.o@protonmail.com>
+
+        [css-counter-styles] Parse and add feature flag for @counter-style
+        https://bugs.webkit.org/show_bug.cgi?id=223150
+
+        Reviewed by Darin Adler.
+
+        Parse @counter-style (without implementing descriptor parsing) behind
+        a new feature flag, CSSCounterStyleAtRulesEnabled.  A separate feature
+        flag for @counter-style <image> symbol values has also been added, as
+        image symbols have extra complexities that we won't want to hold the
+        entire feature back on.
+        https://www.w3.org/TR/css-counter-styles-3
+
+        The CSSCounterStyleRule IDL interface is also added and implemented,
+        and similarly feature flagged.
+        https://www.w3.org/TR/css-counter-styles-3/#apis
+
+        Test: webexposed/counter-style-is-not-exposed.html and existing WPTs.
+
+        * CMakeLists.txt:
+        Add CSSCounterStyleRule.idl.
+
+        * DerivedSources-input.xcfilelist:
+        Add CSSCounterStyleRule.idl.
+
+        * DerivedSources-output.xcfilelist:
+        Add JSCSSCounterStyleRule.h and JSCSSCounterStyleRule.cpp.
+
+        * DerivedSources.make:
+        Add CSSCounterStyleRule.idl.
+
+        * Sources.txt:
+        Add CSSCounterStyleRule.cpp and JSCSSCounterStyleRule.cpp.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        Add CounterStyle.h, CounterStyle.cpp, and CounterStyle.idl.
+
+        * bindings/js/JSCSSRuleCustom.cpp:
+        (WebCore::toJSNewlyCreated):
+        Support CSSCounterStyleRule.
+
+        * bindings/js/WebCoreBuiltinNames.h:
+        Add macro(CSSCounterStyleRule) to generate counter-style built-in
+        names.
+
+        * css/CSSCounterStyleRule.cpp: Added.
+        (WebCore::StyleRuleCounterStyle::StyleRuleCounterStyle):
+        (WebCore::StyleRuleCounterStyle::create):
+        (WebCore::StyleRuleCounterStyle::mutableProperties):
+        (WebCore::CSSCounterStyleRule::CSSCounterStyleRule):
+        (WebCore::CSSCounterStyleRule::reattach):
+        (WebCore::CSSCounterStyleRule::cssText const):
+
+        * css/CSSCounterStyleRule.h: Added.
+        (isType): Add specialized rule.isCounterStyleRule() implementation.
+
+        * css/CSSCounterStyleRule.idl: Added.
+        * css/CSSRule.cpp: Add new StyleRuleType::CounterStyle COMPILE_ASSERT.
+        * css/CSSRule.h:
+        Add COUNTER_STYLE_RULE constant.  Also add comment `// WebIDL enum` to
+        disable enum_casing lint, since these values are named to match IDL
+        attributes.
+        * css/CSSRule.idl: Add COUNTER_STYLE_RULE constant behind flag.
+
+        * css/StyleRule.cpp: 
+        (WebCore::StyleRuleBase::destroy):
+        (WebCore::StyleRuleBase::copy const):
+        (WebCore::StyleRuleBase::createCSSOMWrapper const):
+        Handle newly added StyleRuleType::CounterStyle.
+
+        * css/StyleRule.h:
+        (WebCore::StyleRuleBase::isCounterStyleRule const): Added.
+
+        * css/StyleRuleType.h: Add StyleRuleType::CounterStyle
+
+        * css/StyleSheetContents.cpp:
+        (WebCore::traverseRulesInVector):
+        (WebCore::StyleSheetContents::traverseSubresources const):
+        If @counter-style image symbols flag is enabled, do traverse
+        subresources.
+
+        * css/parser/CSSAtRuleID.cpp:
+        If the @counter-style feature flag is enabled, return newly added
+        CSSAtRuleCounterStyle ID.
+
+        (WebCore::cssAtRuleID):
+        * css/parser/CSSAtRuleID.h:
+        Add CSSAtRuleCounterStyle ID.
+
+        * css/parser/CSSParserContext.cpp:
+        (WebCore::operator==):
+        * css/parser/CSSParserContext.h:
+        (WebCore::CSSParserContextHash::hash):
+        Add cssCounterStyleAtRulesEnabled and
+        cssCounterStyleAtRuleImageSymbolsEnabled flags.
+
+        * css/parser/CSSParserFastPaths.cpp:
+        (WebCore::CSSParserFastPaths::isValidKeywordPropertyAndValue):
+        Refactor to use new CSSPropertyParserHelpers::isPredefinedCounterStyle
+        method.
+
+        * css/parser/CSSParserImpl.cpp:
+        (WebCore::computeNewAllowedRules):
+        (WebCore::CSSParserImpl::consumeAtRule):
+        (WebCore::CSSParserImpl::consumeCounterStyleRule): Added.
+        (WebCore::CSSParserImpl::consumeDeclarationList):
+        (WebCore::CSSParserImpl::consumeDeclaration):
+        * css/parser/CSSParserImpl.h:
+        Handle new @counter-style rule.
+
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::CSSPropertyParser::parseValue): Handle
+        StyleRuleType::CounterStyle.
+        (WebCore::consumeCounterContent):
+        Refactor to use new CSSPropertyParserHelpers::isPredefinedCounterStyle
+        method.
+        (WebCore::CSSPropertyParser::parseCounterStyleDescriptor):
+        Add placeholder for future @counter-style descriptor parsing work.
+
+        * css/parser/CSSPropertyParser.h:
+        Add parseCounterStyleDescriptor method
+
+        * css/parser/CSSPropertyParserHelpers.h:
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::consumeCustomIdent):
+        Add new flag that allows consumed custom ident values to be lowercased
+        on-parse.  This is necessary for some <counter-style-name> values.
+        (WebCore::CSSPropertyParserHelpers::isPredefinedCounterStyle): Added.
+        (WebCore::CSSPropertyParserHelpers::consumeCounterStyleName):
+        (WebCore::CSSPropertyParserHelpers::consumeCounterStyleNameInPrelude):
+        Parse @counter-style names, both in-prelude names and non-prelude
+        names (e.g. as part of the `extends` descriptor).
+
 2021-04-16  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed GTK / WinCairo build fix after Antoine's r276141.
diff --git a/Source/WebCore/DerivedSources-input.xcfilelist b/Source/WebCore/DerivedSources-input.xcfilelist
index db8d175..75b561e 100644
--- a/Source/WebCore/DerivedSources-input.xcfilelist
+++ b/Source/WebCore/DerivedSources-input.xcfilelist
@@ -720,6 +720,7 @@
 $(PROJECT_DIR)/crypto/parameters/RsaOaepParams.idl
 $(PROJECT_DIR)/crypto/parameters/RsaPssParams.idl
 $(PROJECT_DIR)/css/CSSConditionRule.idl
+$(PROJECT_DIR)/css/CSSCounterStyleRule.idl
 $(PROJECT_DIR)/css/CSSFontFaceRule.idl
 $(PROJECT_DIR)/css/CSSGroupingRule.idl
 $(PROJECT_DIR)/css/CSSImportRule.idl
diff --git a/Source/WebCore/DerivedSources-output.xcfilelist b/Source/WebCore/DerivedSources-output.xcfilelist
index 1a24495..2496a1c 100644
--- a/Source/WebCore/DerivedSources-output.xcfilelist
+++ b/Source/WebCore/DerivedSources-output.xcfilelist
@@ -283,6 +283,8 @@
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSAnimation.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSConditionRule.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSConditionRule.h
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSCounterStyleRule.cpp
+$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSCounterStyleRule.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSFontFaceRule.cpp
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSFontFaceRule.h
 $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSGroupingRule.cpp
diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make
index 2d85a7d..e0ec630 100644
--- a/Source/WebCore/DerivedSources.make
+++ b/Source/WebCore/DerivedSources.make
@@ -617,6 +617,7 @@
     $(WebCore)/crypto/parameters/RsaOaepParams.idl \
     $(WebCore)/crypto/parameters/RsaPssParams.idl \
     $(WebCore)/css/CSSConditionRule.idl \
+    $(WebCore)/css/CSSCounterStyleRule.idl \
     $(WebCore)/css/CSSFontFaceRule.idl \
     $(WebCore)/css/CSSGroupingRule.idl \
     $(WebCore)/css/CSSImportRule.idl \
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index ecacdeb..bdb2e55 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -723,6 +723,7 @@
 css/CSSComputedStyleDeclaration.cpp
 css/CSSConditionRule.cpp
 css/CSSContentDistributionValue.cpp
+css/CSSCounterStyleRule.cpp
 css/CSSCrossfadeValue.cpp
 css/CSSCursorImageValue.cpp
 css/CSSCustomIdentValue.cpp
@@ -2835,6 +2836,7 @@
 JSCDATASection.cpp
 JSCSSAnimation.cpp
 JSCSSConditionRule.cpp
+JSCSSCounterStyleRule.cpp
 JSCSSFontFaceRule.cpp
 JSCSSGroupingRule.cpp
 JSCSSImportRule.cpp
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 2d6a461..bcc8d24 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -3090,6 +3090,7 @@
 		98CBC8052618F68C001593C8 /* SelectionRestorationMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 98CBC8032618F68B001593C8 /* SelectionRestorationMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		98CE432A129E00E5005821DC /* LinkLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 98CE4329129E00E5005821DC /* LinkLoader.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		98EB1F951313FE0500D0E1EA /* NotImplemented.h in Headers */ = {isa = PBXBuildFile; fileRef = 98EB1F941313FE0500D0E1EA /* NotImplemented.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		98F9D66925FDE02200BD842D /* CSSCounterStyleRule.h in Headers */ = {isa = PBXBuildFile; fileRef = 98F9D66825FDDFE800BD842D /* CSSCounterStyleRule.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 9920398118B95BC600B39AF9 /* UserInputBridge.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		994C603A253A277300BDF060 /* InspectorFrontendAPIDispatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 994C6037253A277200BDF060 /* InspectorFrontendAPIDispatcher.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		996E59DF1DF0128D006612B9 /* NavigatorWebDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 996E59DC1DF00D90006612B9 /* NavigatorWebDriver.h */; };
@@ -12123,6 +12124,9 @@
 		98CE4325129E00BD005821DC /* LinkLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LinkLoader.cpp; sourceTree = "<group>"; };
 		98CE4329129E00E5005821DC /* LinkLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinkLoader.h; sourceTree = "<group>"; };
 		98EB1F941313FE0500D0E1EA /* NotImplemented.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotImplemented.h; sourceTree = "<group>"; };
+		98F9D66525FDDFE700BD842D /* CSSCounterStyleRule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSSCounterStyleRule.cpp; sourceTree = "<group>"; };
+		98F9D66725FDDFE800BD842D /* CSSCounterStyleRule.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CSSCounterStyleRule.idl; sourceTree = "<group>"; };
+		98F9D66825FDDFE800BD842D /* CSSCounterStyleRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSCounterStyleRule.h; sourceTree = "<group>"; };
 		9908B0ED1BCACF9100ED0F65 /* ByteLengthQueuingStrategy.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = ByteLengthQueuingStrategy.js; sourceTree = "<group>"; };
 		9908B0EE1BCACF9100ED0F65 /* CountQueuingStrategy.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = CountQueuingStrategy.js; sourceTree = "<group>"; };
 		9908B0EF1BCACF9100ED0F65 /* ReadableStream.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = ReadableStream.js; sourceTree = "<group>"; };
@@ -29600,6 +29604,9 @@
 				930AAC99250EB8180013DA9F /* CSSConditionRule.idl */,
 				9DAC7C521AF2CAA100437C44 /* CSSContentDistributionValue.cpp */,
 				9DAC7C531AF2CAA100437C44 /* CSSContentDistributionValue.h */,
+				98F9D66525FDDFE700BD842D /* CSSCounterStyleRule.cpp */,
+				98F9D66825FDDFE800BD842D /* CSSCounterStyleRule.h */,
+				98F9D66725FDDFE800BD842D /* CSSCounterStyleRule.idl */,
 				2D8FEBDA143E3EF70072502B /* CSSCrossfadeValue.cpp */,
 				2D8FEBDB143E3EF70072502B /* CSSCrossfadeValue.h */,
 				AA0978ED0ABAA6E100874480 /* CSSCursorImageValue.cpp */,
@@ -31958,6 +31965,7 @@
 				BCEA4790097CAAC80094C9E4 /* CSSComputedStyleDeclaration.h in Headers */,
 				930AAC9A250EB8490013DA9F /* CSSConditionRule.h in Headers */,
 				9DAC7C551AF2CAA200437C44 /* CSSContentDistributionValue.h in Headers */,
+				98F9D66925FDE02200BD842D /* CSSCounterStyleRule.h in Headers */,
 				2D8FEBDD143E3EF70072502B /* CSSCrossfadeValue.h in Headers */,
 				AA21ECCD0ABF0FC6002B834C /* CSSCursorImageValue.h in Headers */,
 				9444CBE41D8861990073A074 /* CSSCustomIdentValue.h in Headers */,
@@ -32115,6 +32123,7 @@
 				4162A451101145AE00DFF3ED /* DedicatedWorkerGlobalScope.h in Headers */,
 				41A3D58F101C152D00316D07 /* DedicatedWorkerThread.h in Headers */,
 				FD06DFA6134A4DEF006F5D7D /* DefaultAudioDestinationNode.h in Headers */,
+				E4F38D1B2626F13B007B1064 /* DefaultResourceLoadPriority.h in Headers */,
 				CD83D36221122A210076E11C /* DeferrableTask.h in Headers */,
 				FD31602C12B0267600C1A359 /* DelayDSPKernel.h in Headers */,
 				FD31602E12B0267600C1A359 /* DelayNode.h in Headers */,
@@ -34614,7 +34623,6 @@
 				B595FF471824CEE300FF51CD /* RenderIterator.h in Headers */,
 				BCEA487A097D93020094C9E4 /* RenderLayer.h in Headers */,
 				0F580CFF0F12DE9B0051D689 /* RenderLayerBacking.h in Headers */,
-				E4F38D1B2626F13B007B1064 /* DefaultResourceLoadPriority.h in Headers */,
 				0F580CFD0F12DE9B0051D689 /* RenderLayerCompositor.h in Headers */,
 				3C244FEAA375AC633F88BE6F /* RenderLayerModelObject.h in Headers */,
 				0F53FB84213B7A6400C40D34 /* RenderLayerScrollableArea.h in Headers */,
diff --git a/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp b/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp
index 4d56853..750e00c 100644
--- a/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp
+++ b/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSCSSRule.h"
 
+#include "CSSCounterStyleRule.h"
 #include "CSSFontFaceRule.h"
 #include "CSSImportRule.h"
 #include "CSSKeyframeRule.h"
@@ -35,6 +36,7 @@
 #include "CSSPageRule.h"
 #include "CSSStyleRule.h"
 #include "CSSSupportsRule.h"
+#include "JSCSSCounterStyleRule.h"
 #include "JSCSSFontFaceRule.h"
 #include "JSCSSImportRule.h"
 #include "JSCSSKeyframeRule.h"
@@ -80,6 +82,8 @@
         return createWrapper<CSSKeyframesRule>(globalObject, WTFMove(rule));
     case CSSRule::SUPPORTS_RULE:
         return createWrapper<CSSSupportsRule>(globalObject, WTFMove(rule));
+    case CSSRule::COUNTER_STYLE_RULE:
+        return createWrapper<CSSCounterStyleRule>(globalObject, WTFMove(rule));
     default:
         return createWrapper<CSSRule>(globalObject, WTFMove(rule));
     }
diff --git a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h
index 86eaaa0..bee1e6b 100644
--- a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h
+++ b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h
@@ -75,6 +75,7 @@
     macro(Credential) \
     macro(CredentialsContainer) \
     macro(CSSAnimation) \
+    macro(CSSCounterStyleRule) \
     macro(CSSImageValue) \
     macro(CSSNumericValue) \
     macro(CSSPaintSize) \
diff --git a/Source/WebCore/css/CSSCounterStyleRule.cpp b/Source/WebCore/css/CSSCounterStyleRule.cpp
new file mode 100644
index 0000000..229c452
--- /dev/null
+++ b/Source/WebCore/css/CSSCounterStyleRule.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 Tyler Wilcock <twilco.o@protonmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CSSCounterStyleRule.h"
+
+namespace WebCore {
+    
+StyleRuleCounterStyle::StyleRuleCounterStyle(const AtomString& name, Ref<StyleProperties>&& properties)
+    : StyleRuleBase(StyleRuleType::CounterStyle)
+    , m_name(name)
+    , m_properties(WTFMove(properties))
+{
+}
+
+Ref<StyleRuleCounterStyle> StyleRuleCounterStyle::create(const AtomString& name, Ref<StyleProperties>&& properties)
+{
+    return adoptRef(*new StyleRuleCounterStyle(name, WTFMove(properties)));
+}
+
+StyleRuleCounterStyle::~StyleRuleCounterStyle() = default;
+
+MutableStyleProperties& StyleRuleCounterStyle::mutableProperties()
+{
+    if (!is<MutableStyleProperties>(m_properties.get()))
+        m_properties = m_properties->mutableCopy();
+    return downcast<MutableStyleProperties>(m_properties.get());
+}
+
+Ref<CSSCounterStyleRule> CSSCounterStyleRule::create(StyleRuleCounterStyle& rule, CSSStyleSheet* sheet)
+{
+    return adoptRef(*new CSSCounterStyleRule(rule, sheet));
+}
+
+CSSCounterStyleRule::CSSCounterStyleRule(StyleRuleCounterStyle& counterStyleRule, CSSStyleSheet* parent)
+    : CSSRule(parent)
+    , m_counterStyleRule(counterStyleRule)
+{
+}
+
+CSSCounterStyleRule::~CSSCounterStyleRule() = default;
+
+String CSSCounterStyleRule::cssText() const
+{
+    // FIXME: Implement this function when we parse @counter-style descriptors.
+    return emptyString();
+}
+
+void CSSCounterStyleRule::reattach(StyleRuleBase& rule)
+{
+    m_counterStyleRule = static_cast<StyleRuleCounterStyle&>(rule);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/css/CSSCounterStyleRule.h b/Source/WebCore/css/CSSCounterStyleRule.h
new file mode 100644
index 0000000..b24fe1f
--- /dev/null
+++ b/Source/WebCore/css/CSSCounterStyleRule.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 Tyler Wilcock <twilco.o@protonmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CSSRule.h"
+#include "StyleProperties.h"
+#include "StyleRule.h"
+#include <wtf/text/AtomString.h>
+
+namespace WebCore {
+
+class StyleRuleCounterStyle final : public StyleRuleBase {
+public:
+    static Ref<StyleRuleCounterStyle> create(const AtomString& name, Ref<StyleProperties>&&);
+    ~StyleRuleCounterStyle();
+
+    const StyleProperties& properties() const { return m_properties; }
+    MutableStyleProperties& mutableProperties();
+
+    const AtomString& name() const { return m_name; }
+
+private:
+    explicit StyleRuleCounterStyle(const AtomString&, Ref<StyleProperties>&&);
+
+    AtomString m_name;
+    Ref<StyleProperties> m_properties;
+};
+
+class CSSCounterStyleRule final : public CSSRule {
+public:
+    static Ref<CSSCounterStyleRule> create(StyleRuleCounterStyle&, CSSStyleSheet*);
+    virtual ~CSSCounterStyleRule();
+
+    String cssText() const final;
+    void reattach(StyleRuleBase&) final;
+    CSSRule::Type type() const final { return COUNTER_STYLE_RULE; }
+
+    String name() const { return m_counterStyleRule->name(); }
+    // FIXME: Implement after we parse @counter-style descriptors.
+    String system() const { return emptyString(); }
+    String negative() const { return emptyString(); }
+    String prefix() const { return emptyString(); }
+    String suffix() const { return emptyString(); }
+    String range() const { return emptyString(); }
+    String pad() const { return emptyString(); }
+    String fallback() const { return emptyString(); }
+    String symbols() const { return emptyString(); }
+    String additiveSymbols() const { return emptyString(); }
+    String speakAs() const { return emptyString(); }
+
+    // FIXME: Implement after we parse @counter-style descriptors.
+    void setName(const String&) { }
+    void setSystem(const String&) { }
+    void setNegative(const String&) { }
+    void setPrefix(const String&) { }
+    void setSuffix(const String&) { }
+    void setRange(const String&) { }
+    void setPad(const String&) { }
+    void setFallback(const String&) { }
+    void setSymbols(const String&) { }
+    void setAdditiveSymbols(const String&) { }
+    void setSpeakAs(const String&) { }
+
+private:
+    CSSCounterStyleRule(StyleRuleCounterStyle&, CSSStyleSheet* parent);
+
+    Ref<StyleRuleCounterStyle> m_counterStyleRule;
+};
+
+} // namespace WebCore
+
+SPECIALIZE_TYPE_TRAITS_CSS_RULE(CSSCounterStyleRule, CSSRule::COUNTER_STYLE_RULE)
+
+SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::StyleRuleCounterStyle)
+static bool isType(const WebCore::StyleRuleBase& rule) { return rule.isCounterStyleRule(); }
+SPECIALIZE_TYPE_TRAITS_END()
+
diff --git a/Source/WebCore/css/CSSCounterStyleRule.idl b/Source/WebCore/css/CSSCounterStyleRule.idl
new file mode 100644
index 0000000..cfc8b06
--- /dev/null
+++ b/Source/WebCore/css/CSSCounterStyleRule.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 Tyler Wilcock <twilco.o@protonmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// https://www.w3.org/TR/css-counter-styles-3/#the-csscounterstylerule-interface
+[
+    EnabledBySetting=CSSCounterStyleAtRules,
+    Exposed=Window
+] interface CSSCounterStyleRule : CSSRule {
+   attribute DOMString name;
+   attribute DOMString system;
+   attribute DOMString symbols;
+   attribute DOMString additiveSymbols;
+   attribute DOMString negative;
+   attribute DOMString prefix;
+   attribute DOMString suffix;
+   attribute DOMString range;
+   attribute DOMString pad;
+   attribute DOMString speakAs;
+   attribute DOMString fallback;
+};
diff --git a/Source/WebCore/css/CSSRule.cpp b/Source/WebCore/css/CSSRule.cpp
index b886868..0f3c439 100644
--- a/Source/WebCore/css/CSSRule.cpp
+++ b/Source/WebCore/css/CSSRule.cpp
@@ -46,6 +46,7 @@
 COMPILE_ASSERT(StyleRuleType::Keyframes == static_cast<StyleRuleType>(CSSRule::Type::KEYFRAMES_RULE), enums_should_match);
 COMPILE_ASSERT(StyleRuleType::Keyframe == static_cast<StyleRuleType>(CSSRule::Type::KEYFRAME_RULE), enums_should_match);
 COMPILE_ASSERT(StyleRuleType::Namespace == static_cast<StyleRuleType>(CSSRule::Type::NAMESPACE_RULE), enums_should_match);
+COMPILE_ASSERT(StyleRuleType::CounterStyle == static_cast<StyleRuleType>(CSSRule::Type::COUNTER_STYLE_RULE), enums_should_match);
 COMPILE_ASSERT(StyleRuleType::Supports == static_cast<StyleRuleType>(CSSRule::Type::SUPPORTS_RULE), enums_should_match);
 
 ExceptionOr<void> CSSRule::setCssText(const String&)
diff --git a/Source/WebCore/css/CSSRule.h b/Source/WebCore/css/CSSRule.h
index c034626..7efb3a1 100644
--- a/Source/WebCore/css/CSSRule.h
+++ b/Source/WebCore/css/CSSRule.h
@@ -36,6 +36,7 @@
 public:
     virtual ~CSSRule() = default;
 
+    // WebIDL enum
     enum Type {
         UNKNOWN_RULE,
         STYLE_RULE,
@@ -48,7 +49,8 @@
         KEYFRAME_RULE,
         MARGIN_RULE,
         NAMESPACE_RULE,
-        SUPPORTS_RULE = 12,
+        COUNTER_STYLE_RULE,
+        SUPPORTS_RULE
     };
 
     enum DeprecatedType {
diff --git a/Source/WebCore/css/CSSRule.idl b/Source/WebCore/css/CSSRule.idl
index b50aa08..f3e6613 100644
--- a/Source/WebCore/css/CSSRule.idl
+++ b/Source/WebCore/css/CSSRule.idl
@@ -42,6 +42,7 @@
     const unsigned short KEYFRAME_RULE = 8;
     const unsigned short MARGIN_RULE = 9;
     const unsigned short NAMESPACE_RULE = 10;
+    [EnabledBySetting=CSSCounterStyleAtRules] const unsigned short COUNTER_STYLE_RULE = 11;
     const unsigned short SUPPORTS_RULE = 12;
 
     // Legacy synonyms for the above, kept to avoid breaking existing content.
diff --git a/Source/WebCore/css/StyleRule.cpp b/Source/WebCore/css/StyleRule.cpp
index c83ca82..65e5c50 100644
--- a/Source/WebCore/css/StyleRule.cpp
+++ b/Source/WebCore/css/StyleRule.cpp
@@ -22,6 +22,7 @@
 #include "config.h"
 #include "StyleRule.h"
 
+#include "CSSCounterStyleRule.h"
 #include "CSSDeferredParser.h"
 #include "CSSFontFaceRule.h"
 #include "CSSImportRule.h"
@@ -90,6 +91,9 @@
     case StyleRuleType::Charset:
         delete downcast<StyleRuleCharset>(this);
         return;
+    case StyleRuleType::CounterStyle:
+        delete downcast<StyleRuleCounterStyle>(this);
+        return;
     case StyleRuleType::Unknown:
         ASSERT_NOT_REACHED();
         return;
@@ -112,6 +116,8 @@
         return downcast<StyleRuleSupports>(*this).copy();
     case StyleRuleType::Keyframes:
         return downcast<StyleRuleKeyframes>(*this).copy();
+    case StyleRuleType::CounterStyle:
+        return downcast<StyleRuleCounterStyle>(*this).copy();
     case StyleRuleType::Import:
     case StyleRuleType::Namespace:
         // FIXME: Copy import and namespace rules.
@@ -153,6 +159,9 @@
     case StyleRuleType::Namespace:
         rule = CSSNamespaceRule::create(downcast<StyleRuleNamespace>(self), parentSheet);
         break;
+    case StyleRuleType::CounterStyle:
+        rule = CSSCounterStyleRule::create(downcast<StyleRuleCounterStyle>(self), parentSheet);
+        break;
     case StyleRuleType::Unknown:
     case StyleRuleType::Charset:
     case StyleRuleType::Keyframe:
diff --git a/Source/WebCore/css/StyleRule.h b/Source/WebCore/css/StyleRule.h
index 08a7875..e02c56a 100644
--- a/Source/WebCore/css/StyleRule.h
+++ b/Source/WebCore/css/StyleRule.h
@@ -47,6 +47,7 @@
     StyleRuleType type() const { return static_cast<StyleRuleType>(m_type); }
     
     bool isCharsetRule() const { return type() == StyleRuleType::Charset; }
+    bool isCounterStyleRule() const { return type() == StyleRuleType::CounterStyle; }
     bool isFontFaceRule() const { return type() == StyleRuleType::FontFace; }
     bool isKeyframesRule() const { return type() == StyleRuleType::Keyframes; }
     bool isKeyframeRule() const { return type() == StyleRuleType::Keyframe; }
diff --git a/Source/WebCore/css/StyleRuleType.h b/Source/WebCore/css/StyleRuleType.h
index cbb939b..c404746 100644
--- a/Source/WebCore/css/StyleRuleType.h
+++ b/Source/WebCore/css/StyleRuleType.h
@@ -38,6 +38,7 @@
     Keyframes,
     Keyframe, // Not used. These are internally non-rule StyleRuleKeyframe objects.
     Namespace = 10,
+    CounterStyle = 11,
     Supports = 12,
 };
 
diff --git a/Source/WebCore/css/StyleSheetContents.cpp b/Source/WebCore/css/StyleSheetContents.cpp
index 3cf3ec0..5d96446 100644
--- a/Source/WebCore/css/StyleSheetContents.cpp
+++ b/Source/WebCore/css/StyleSheetContents.cpp
@@ -444,6 +444,7 @@
         case StyleRuleType::Namespace:
         case StyleRuleType::Unknown:
         case StyleRuleType::Charset:
+        case StyleRuleType::CounterStyle:
         case StyleRuleType::Keyframe:
         case StyleRuleType::Supports:
             break;
@@ -478,6 +479,8 @@
             if (auto* cachedResource = downcast<StyleRuleImport>(rule).cachedCSSStyleSheet())
                 return handler(*cachedResource);
             return false;
+        case StyleRuleType::CounterStyle:
+            return m_parserContext.counterStyleAtRuleImageSymbolsEnabled;
         case StyleRuleType::Media:
         case StyleRuleType::Page:
         case StyleRuleType::Keyframes:
diff --git a/Source/WebCore/css/parser/CSSAtRuleID.cpp b/Source/WebCore/css/parser/CSSAtRuleID.cpp
index 3f6c6d1..e4b7d33 100644
--- a/Source/WebCore/css/parser/CSSAtRuleID.cpp
+++ b/Source/WebCore/css/parser/CSSAtRuleID.cpp
@@ -56,6 +56,8 @@
         return CSSAtRuleWebkitKeyframes;
     if (equalIgnoringASCIICase(name, "apply"))
         return CSSAtRuleApply;
+    if (equalIgnoringASCIICase(name, "counter-style"))
+        return CSSAtRuleCounterStyle;
     return CSSAtRuleInvalid;
 }
 
diff --git a/Source/WebCore/css/parser/CSSAtRuleID.h b/Source/WebCore/css/parser/CSSAtRuleID.h
index 9b83a29..9685268 100644
--- a/Source/WebCore/css/parser/CSSAtRuleID.h
+++ b/Source/WebCore/css/parser/CSSAtRuleID.h
@@ -47,6 +47,7 @@
 
     CSSAtRuleWebkitKeyframes = 10,
     CSSAtRuleApply = 11,
+    CSSAtRuleCounterStyle = 12,
 };
 
 CSSAtRuleID cssAtRuleID(StringView name);
diff --git a/Source/WebCore/css/parser/CSSParserContext.cpp b/Source/WebCore/css/parser/CSSParserContext.cpp
index 0981626..ac61409 100644
--- a/Source/WebCore/css/parser/CSSParserContext.cpp
+++ b/Source/WebCore/css/parser/CSSParserContext.cpp
@@ -73,6 +73,8 @@
     , colorMixEnabled { document.settings().cssColorMixEnabled() }
     , constantPropertiesEnabled { document.settings().constantPropertiesEnabled() }
     , containmentEnabled { document.settings().cssContainmentEnabled() }
+    , counterStyleAtRulesEnabled { document.settings().cssCounterStyleAtRulesEnabled() }
+    , counterStyleAtRuleImageSymbolsEnabled { document.settings().cssCounterStyleAtRuleImageSymbolsEnabled() }
     , cssColor4 { document.settings().cssColor4() }
     , deferredCSSParserEnabled { document.settings().deferredCSSParserEnabled() }
     , individualTransformPropertiesEnabled { document.settings().cssIndividualTransformPropertiesEnabled() }
@@ -113,6 +115,8 @@
         && a.colorMixEnabled == b.colorMixEnabled
         && a.constantPropertiesEnabled == b.constantPropertiesEnabled
         && a.containmentEnabled == b.containmentEnabled
+        && a.counterStyleAtRulesEnabled == b.counterStyleAtRulesEnabled
+        && a.counterStyleAtRuleImageSymbolsEnabled == b.counterStyleAtRuleImageSymbolsEnabled
         && a.cssColor4 == b.cssColor4
         && a.deferredCSSParserEnabled == b.deferredCSSParserEnabled
         && a.individualTransformPropertiesEnabled == b.individualTransformPropertiesEnabled
diff --git a/Source/WebCore/css/parser/CSSParserContext.h b/Source/WebCore/css/parser/CSSParserContext.h
index 49560d8..5342601 100644
--- a/Source/WebCore/css/parser/CSSParserContext.h
+++ b/Source/WebCore/css/parser/CSSParserContext.h
@@ -60,6 +60,8 @@
     bool colorMixEnabled { false };
     bool constantPropertiesEnabled { false };
     bool containmentEnabled { false };
+    bool counterStyleAtRulesEnabled { false };
+    bool counterStyleAtRuleImageSymbolsEnabled { false };
     bool cssColor4 { false };
     bool deferredCSSParserEnabled { false };
     bool individualTransformPropertiesEnabled { false };
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
index 0e2e07c..dafa60b 100644
--- a/Source/WebCore/css/parser/CSSParserFastPaths.cpp
+++ b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
@@ -35,6 +35,7 @@
 #include "CSSParserIdioms.h"
 #include "CSSPrimitiveValue.h"
 #include "CSSPropertyParser.h"
+#include "CSSPropertyParserHelpers.h"
 #include "CSSValueList.h"
 #include "CSSValuePool.h"
 #include "HTMLParserIdioms.h"
@@ -661,7 +662,7 @@
     case CSSPropertyListStyleType:
         // See section CSS_PROP_LIST_STYLE_TYPE of file CSSValueKeywords.in
         // for the list of supported list-style-types.
-        return (valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone;
+        return CSSPropertyParserHelpers::isPredefinedCounterStyle(valueID) || valueID == CSSValueNone;
     case CSSPropertyMaskType:
         return valueID == CSSValueLuminance || valueID == CSSValueAlpha;
     case CSSPropertyMathStyle:
diff --git a/Source/WebCore/css/parser/CSSParserImpl.cpp b/Source/WebCore/css/parser/CSSParserImpl.cpp
index 34b74c0..ec0c1a6 100644
--- a/Source/WebCore/css/parser/CSSParserImpl.cpp
+++ b/Source/WebCore/css/parser/CSSParserImpl.cpp
@@ -31,6 +31,7 @@
 #include "CSSParserImpl.h"
 
 #include "CSSAtRuleID.h"
+#include "CSSCounterStyleRule.h"
 #include "CSSCustomPropertyValue.h"
 #include "CSSDeferredParser.h"
 #include "CSSKeyframeRule.h"
@@ -337,7 +338,7 @@
 
 static CSSParserImpl::AllowedRulesType computeNewAllowedRules(CSSParserImpl::AllowedRulesType allowedRules, StyleRuleBase* rule)
 {
-    if (!rule || allowedRules == CSSParserImpl::KeyframeRules || allowedRules == CSSParserImpl::NoRules)
+    if (!rule || allowedRules == CSSParserImpl::KeyframeRules || allowedRules == CSSParserImpl::CounterStyleRules || allowedRules == CSSParserImpl::NoRules)
         return allowedRules;
     ASSERT(allowedRules <= CSSParserImpl::RegularRules);
     if (rule->isCharsetRule() || rule->isImportRule())
@@ -448,6 +449,8 @@
         return consumeKeyframesRule(false, prelude, block);
     case CSSAtRulePage:
         return consumePageRule(prelude, block);
+    case CSSAtRuleCounterStyle:
+        return consumeCounterStyleRule(prelude, block);
     default:
         return nullptr; // Parse error, unrecognised at-rule with block
     }
@@ -652,6 +655,27 @@
     return StyleRulePage::create(createStyleProperties(m_parsedProperties, m_context.mode), WTFMove(selectorList));
 }
 
+RefPtr<StyleRuleCounterStyle> CSSParserImpl::consumeCounterStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+    if (!m_context.counterStyleAtRulesEnabled)
+        return nullptr;
+
+    auto rangeCopy = prelude; // For inspector callbacks
+    auto name = CSSPropertyParserHelpers::consumeCounterStyleNameInPrelude(rangeCopy);
+    if (name.isNull())
+        return nullptr;
+
+    if (m_observerWrapper) {
+        m_observerWrapper->observer().startRuleHeader(StyleRuleType::CounterStyle, m_observerWrapper->startOffset(rangeCopy));
+        m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+        m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
+        m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
+    }
+
+    consumeDeclarationList(block, StyleRuleType::CounterStyle);
+    return StyleRuleCounterStyle::create(name, createStyleProperties(m_parsedProperties, m_context.mode));
+}
+
 // FIXME-NEWPARSER: Support "apply"
 /*void CSSParserImpl::consumeApplyRule(CSSParserTokenRange prelude)
 {
@@ -728,7 +752,7 @@
 {
     ASSERT(m_parsedProperties.isEmpty());
 
-    bool useObserver = m_observerWrapper && (ruleType == StyleRuleType::Style || ruleType == StyleRuleType::Keyframe);
+    bool useObserver = m_observerWrapper && (ruleType == StyleRuleType::Style || ruleType == StyleRuleType::Keyframe || ruleType == StyleRuleType::CounterStyle);
     if (useObserver) {
         m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(range));
         m_observerWrapper->skipCommentsBefore(range, true);
@@ -810,7 +834,7 @@
         consumeCustomPropertyValue(range.makeSubRange(&range.peek(), declarationValueEnd), variableName, important);
     }
 
-    if (important && (ruleType == StyleRuleType::FontFace || ruleType == StyleRuleType::Keyframe))
+    if (important && (ruleType == StyleRuleType::FontFace || ruleType == StyleRuleType::Keyframe || ruleType == StyleRuleType::CounterStyle))
         return;
 
     if (propertyID != CSSPropertyInvalid)
diff --git a/Source/WebCore/css/parser/CSSParserImpl.h b/Source/WebCore/css/parser/CSSParserImpl.h
index 613f85f..a3d5c54 100644
--- a/Source/WebCore/css/parser/CSSParserImpl.h
+++ b/Source/WebCore/css/parser/CSSParserImpl.h
@@ -45,6 +45,7 @@
 class CSSParserObserverWrapper;
 class CSSSelectorList;
 class CSSTokenizer;
+class StyleRuleCounterStyle;
 class StyleRuleKeyframe;
 class StyleRule;
 class StyleRuleBase;
@@ -79,6 +80,7 @@
         RegularRules,
         KeyframeRules,
         ApplyRules, // For @apply inside style rules
+        CounterStyleRules,
         NoRules, // For parsing at-rules inside declaration lists
     };
 
@@ -132,6 +134,7 @@
     RefPtr<StyleRuleFontFace> consumeFontFaceRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
     RefPtr<StyleRuleKeyframes> consumeKeyframesRule(bool webkitPrefixed, CSSParserTokenRange prelude, CSSParserTokenRange block);
     RefPtr<StyleRulePage> consumePageRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
+    RefPtr<StyleRuleCounterStyle> consumeCounterStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
     
     // FIXME-NEWPARSER: Support "apply"
     // void consumeApplyRule(CSSParserTokenRange prelude);
diff --git a/Source/WebCore/css/parser/CSSPropertyParser.cpp b/Source/WebCore/css/parser/CSSPropertyParser.cpp
index 5900518..7761f43 100644
--- a/Source/WebCore/css/parser/CSSPropertyParser.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParser.cpp
@@ -234,6 +234,8 @@
 
     if (ruleType == StyleRuleType::FontFace)
         parseSuccess = parser.parseFontFaceDescriptor(propertyID);
+    else if (ruleType == StyleRuleType::CounterStyle)
+        parseSuccess = parser.parseCounterStyleDescriptor(propertyID, context);
     else
         parseSuccess = parser.parseValueStart(propertyID, important);
 
@@ -2242,7 +2244,7 @@
     RefPtr<CSSPrimitiveValue> listStyle;
     if (consumeCommaIncludingWhitespace(args)) {
         CSSValueID id = args.peek().id();
-        if ((id != CSSValueNone && (id < CSSValueDisc || id > CSSValueKatakanaIroha)))
+        if ((id != CSSValueNone && !isPredefinedCounterStyle(id)))
             return nullptr;
         listStyle = consumeIdent(args);
     } else
@@ -4434,6 +4436,27 @@
     return nullptr;
 }
 
+RefPtr<CSSValue> CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID propId, CSSParserTokenRange& range, const CSSParserContext& context)
+{
+    if (!context.counterStyleAtRulesEnabled)
+        return nullptr;
+    // FIXME: Implement this function when we can parse @counter-style descriptors.
+    UNUSED_PARAM(propId);
+    UNUSED_PARAM(range);
+    UNUSED_PARAM(context);
+    return nullptr;
+}
+
+bool CSSPropertyParser::parseCounterStyleDescriptor(CSSPropertyID propId, const CSSParserContext& context)
+{
+    auto parsedValue = parseCounterStyleDescriptor(propId, m_range, context);
+    if (!parsedValue || !m_range.atEnd())
+        return false;
+
+    addProperty(propId, CSSPropertyInvalid, *parsedValue, false);
+    return true;
+}
+
 bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId)
 {
     RefPtr<CSSValue> parsedValue;
diff --git a/Source/WebCore/css/parser/CSSPropertyParser.h b/Source/WebCore/css/parser/CSSPropertyParser.h
index 71a1ad8..75d9437 100644
--- a/Source/WebCore/css/parser/CSSPropertyParser.h
+++ b/Source/WebCore/css/parser/CSSPropertyParser.h
@@ -56,6 +56,8 @@
     static RefPtr<CSSCustomPropertyValue> parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange&, const Style::BuilderState&, const CSSParserContext&);
     static void collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange&, const CSSParserContext&);
 
+    static RefPtr<CSSValue> parseCounterStyleDescriptor(CSSPropertyID, CSSParserTokenRange&, const CSSParserContext&);
+
 private:
     CSSPropertyParser(const CSSParserTokenRange&, const CSSParserContext&, Vector<CSSProperty, 256>*, bool consumeWhitespace = true);
 
@@ -71,6 +73,7 @@
 
     bool parseViewportDescriptor(CSSPropertyID propId, bool important);
     bool parseFontFaceDescriptor(CSSPropertyID);
+    bool parseCounterStyleDescriptor(CSSPropertyID, const CSSParserContext&);
 
     void addProperty(CSSPropertyID, CSSPropertyID, Ref<CSSValue>&&, bool important, bool implicit = false);
     void addExpandedPropertyForValue(CSSPropertyID propId, Ref<CSSValue>&&, bool);
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
index 0f7c670..506971c 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
@@ -670,11 +670,12 @@
 // FIXME-NEWPARSER: Eventually we'd like this to use CSSCustomIdentValue, but we need
 // to do other plumbing work first (like changing Pair to CSSValuePair and make it not
 // use only primitive values).
-RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range)
+RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range, bool shouldLowercase)
 {
     if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().id()))
         return nullptr;
-    return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSUnitType::CSS_STRING);
+    auto identifier = range.consumeIncludingWhitespace().value();
+    return CSSValuePool::singleton().createValue(shouldLowercase ? identifier.convertToASCIILowercase() : identifier.toString(), CSSUnitType::CSS_STRING);
 }
 
 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange& range)
@@ -2930,6 +2931,43 @@
     return nullptr;
 }
 
+// https://www.w3.org/TR/css-counter-styles-3/#predefined-counters
+bool isPredefinedCounterStyle(CSSValueID valueID)
+{
+    return valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha;
+}
+
+// https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style-name
+RefPtr<CSSPrimitiveValue> consumeCounterStyleName(CSSParserTokenRange& range)
+{
+    // <counter-style-name> is a <custom-ident> that is not an ASCII case-insensitive match for "none".
+    auto valueID = range.peek().id();
+    if (valueID == CSSValueNone)
+        return nullptr;
+    // If the value is an ASCII case-insensitive match for any of the predefined counter styles, lowercase it.
+    if (auto name = consumeCustomIdent(range, isPredefinedCounterStyle(valueID)))
+        return name;
+    return nullptr;
+}
+
+// https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style-name
+AtomString consumeCounterStyleNameInPrelude(CSSParserTokenRange& prelude)
+{
+    auto nameToken = prelude.consumeIncludingWhitespace();
+    if (!prelude.atEnd())
+        return AtomString();
+    // Ensure this token is a valid <custom-ident>.
+    if (nameToken.type() != IdentToken || isCSSWideKeyword(nameToken.id()))
+        return AtomString();
+    // In the context of the prelude of an @counter-style rule, a <counter-style-name> must not be an ASCII
+    // case-insensitive match for "decimal" or "disc". No <counter-style-name>, prelude or not, may be an ASCII
+    // case-insensitive match for "none".
+    if (identMatches<CSSValueDecimal, CSSValueDisc, CSSValueNone>(nameToken.id()))
+        return AtomString();
+    auto name = nameToken.value();
+    return isPredefinedCounterStyle(nameToken.id()) ? name.convertToASCIILowercase() : name.toString();
+}
+
 Optional<CSSValueID> consumeFontVariantCSS21Raw(CSSParserTokenRange& range)
 {
     return consumeIdentRaw<CSSValueNormal, CSSValueSmallCaps>(range);
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
index 473282a..5bc680b 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
@@ -106,7 +106,7 @@
 template<CSSValueID... allowedIdents> RefPtr<CSSPrimitiveValue> consumeIdent(CSSParserTokenRange&);
 template<CSSValueID... allowedIdents> RefPtr<CSSPrimitiveValue> consumeIdentWorkerSafe(CSSParserTokenRange&, CSSValuePool&);
 
-RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange&);
+RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange&, bool shouldLowercase = false);
 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange&);
 StringView consumeUrlAsStringView(CSSParserTokenRange&);
 RefPtr<CSSPrimitiveValue> consumeUrl(CSSParserTokenRange&);
@@ -165,6 +165,10 @@
     Vector<FontFamilyRaw> family;
 };
 
+bool isPredefinedCounterStyle(CSSValueID);
+RefPtr<CSSPrimitiveValue> consumeCounterStyleName(CSSParserTokenRange&);
+AtomString consumeCounterStyleNameInPrelude(CSSParserTokenRange&);
+
 Optional<CSSValueID> consumeFontVariantCSS21Raw(CSSParserTokenRange&);
 Optional<CSSValueID> consumeFontWeightKeywordValueRaw(CSSParserTokenRange&);
 Optional<FontWeightRaw> consumeFontWeightRaw(CSSParserTokenRange&);