Skip to content

Commit

Permalink
Add documentation and validation for CSSPseudoSelectors.json
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=267031
rdar://120407844

Reviewed by Simon Fraser.

Add validation for:
- field types
- unknown fields
- "argument" field values
- ensuring no-one uses "argument" along with "user-agent-part"

Also add some documentation inline in CSSPseudoSelectors.json.
Documentation for each field is also enforced through the script.

* Source/WebCore/css/CSSPseudoSelectors.json:
* Source/WebCore/css/process-css-pseudo-selectors.py:
(InputValidator):
(InputValidator.__init__):
(InputValidator.validate_fields):

Canonical link: https://commits.webkit.org/272651@main
  • Loading branch information
nt1m committed Jan 4, 2024
1 parent 82a3b2e commit 204b4f9
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 8 deletions.
25 changes: 25 additions & 0 deletions Source/WebCore/css/CSSPseudoSelectors.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
{
"documentation": {
"description": "This file is used to generate code for pseudo-classes and pseudo-elements.",
"fields": {
"aliases": "Legacy name aliases for the pseudo. They will serialize to their modern counterpart.",
"argument": [
"Specifies if the pseudo takes an argument, and whether that argument is required or optional.",
"The 'optional' value is for pseudos like `:host` which support both `:host` and `:host(argument)`.",
"The 'required' value is for pseudos which don't support the argument-free syntax like: `::highlight(name)`.",
"Omit this field if the pseudo never takes an argument."
],
"comment": "Add a description of how the pseudo is used. Especially useful for non-standard or internal ones.",
"condition": "Compile-time `#if ENABLE()` condition for the feature.",
"settings-flag": "Settings flag queried from CSSSelectorParserContext. Note that the flag needs to be manually added there.",
"status": "Specifies the standardization state of the pseudo.",
"supports-single-colon-for-compatibility": [
"For pseudo-elements only. Whether the pseudo-element supports the single colon form (e.g. `:after` instead of `::after`).",
"You should not have to add more of these. They exist only for compatibility with CSS 2.1."
],
"user-agent-part": [
"For pseudo-elements only. Whether the pseudo-element represents an element in an user agent shadow tree.",
"They internally map to `PseudoElement::UserAgentPart` or `PseudoElement::UserAgentPartLegacyAlias` for aliases."
]
},
"notes": "Pseudos that start with `-internal-` will automatically be restricted to user agent stylesheets."
},
"pseudo-classes": {
"-internal-html-document": {},
"-webkit-animating-full-screen-transition": {
Expand Down
71 changes: 63 additions & 8 deletions Source/WebCore/css/process-css-pseudo-selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,60 @@
import subprocess
import textwrap

# - MARK: Input file validation.

COMMON_KNOWN_KEY_TYPES = {
'aliases': [list],
'argument': [str],
'comment': [str],
'condition': [str],
'settings-flag': [str],
'status': [str],
}

KNOWN_KEY_TYPES = {
'pseudo-classes': COMMON_KNOWN_KEY_TYPES,
'pseudo-elements': dict(COMMON_KNOWN_KEY_TYPES, **{
'supports-single-colon-for-compatibility': [bool],
'user-agent-part': [bool],
})
}


class InputValidator:
def __init__(self, input_data):
self.check_field_documentation(input_data)
self.validate_fields(input_data, 'pseudo-classes')
self.validate_fields(input_data, 'pseudo-elements')

def check_field_documentation(self, input_data):
documentation = input_data['documentation']['fields']
for pseudo_type in KNOWN_KEY_TYPES:
for field in KNOWN_KEY_TYPES[pseudo_type]:
if field not in documentation:
raise Exception('Missing documentation for field "{}" found in "{}".'.format(field, pseudo_type))

def validate_fields(self, input_data, pseudo_type):
for pseudo_name, pseudo_data in input_data[pseudo_type].items():
for key, value in pseudo_data.items():
# Check for unknown fields.
if key not in KNOWN_KEY_TYPES[pseudo_type]:
raise Exception('Unknown field "{}" for {} found in "{}".'.format(key, pseudo_type, pseudo_name))

# Check if fields match expected types.
expected_types = KNOWN_KEY_TYPES[pseudo_type][key]
if type(value) not in expected_types:
raise Exception('Invalid value type {} for "{}" in "{}". Expected type in set "{}".'.format(type(value), key, pseudo_name, expected_types))

# Validate "argument" field.
if key == 'argument':
if value not in ['required', 'optional']:
raise Exception('Invalid "argument" field in {}. The field should either be absent, "required" or "optional".'.format(pseudo_name))

if key_is_true(pseudo_data, 'user-agent-part') and pseudo_type == 'pseudo-elements':
raise Exception('Setting "argument" along with "user-agent-part": true is not supported.')


# - MARK: Helpers.

class Writer:
Expand Down Expand Up @@ -127,12 +181,18 @@ def key_is_true(object, key):
return key in object and object[key]


# - MARK: Formatters.

def expand_ifdef_condition(condition):
return condition.replace('(', '_').replace(')', '')


def is_pseudo_selector_enabled(selector, webcore_defines):
if 'condition' not in selector:
return True
return expand_ifdef_condition(selector['condition']) in webcore_defines


# - MARK: Formatters.

def format_name_for_enum_class(stringPseudoType):
def format(substring):
if substring == 'webkit':
Expand All @@ -145,12 +205,6 @@ def format(substring):
return ''.join(output)


def is_pseudo_selector_enabled(selector, webcore_defines):
if 'condition' not in selector:
return True
return expand_ifdef_condition(selector['condition']) in webcore_defines


# - MARK: Code generators.

GeneratorTypes = {
Expand Down Expand Up @@ -741,6 +795,7 @@ def main():
input_data = json.load(input_file)
webcore_defines = [i.strip() for i in sys.argv[-1].split(' ')]

InputValidator(input_data)
GPerfGenerator(input_data, webcore_defines)
CSSSelectorEnumGenerator(input_data)
CSSSelectorInlinesGenerator(input_data)
Expand Down

0 comments on commit 204b4f9

Please sign in to comment.