From 1d32a0ad9eeb3d651e229d7a6301368b777d0406 Mon Sep 17 00:00:00 2001 From: Matthieu Dubet Date: Mon, 1 Jul 2024 17:36:52 -0700 Subject: [PATCH] [css-nesting] Implement new cascade behavior for mixed declarations and rules https://bugs.webkit.org/show_bug.cgi?id=275365 rdar://130094168 Reviewed by NOBODY (OOPS!). Previously, any bare declaration inside a style rule, whatever its position relative to other rules, would be shifted up at the top of the rule to be able to represent a style rule with a single continous leading block of declarations. This behavior has been fixed so the order of the declarations is respected during cascade. https://github.com/w3c/csswg-drafts/issues/10234 This introduces a new CSSNestedDeclarations object (a new kind of CSS style rule) to be able to store those block of declarations in-between rules and fit with the already existing RuleData/RuleSet mechanism. It is purposely not exposed as a style rule in the CSSOM. The CSSOM insertRule() functions (on CSSStyleRule and on CSSGroupingRule) is modified to allow inserting block of declarations. https://github.com/w3c/csswg-drafts/issues/10520 https://drafts.csswg.org/css-nesting/#the-cssnestrule * LayoutTests/TestExpectations: * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html: * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html: Added. * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html: Added. * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html: Manual sync from WPT * Source/WebCore/CMakeLists.txt: * Source/WebCore/DerivedSources-input.xcfilelist: * Source/WebCore/DerivedSources-output.xcfilelist: * Source/WebCore/DerivedSources.make: * Source/WebCore/Sources.txt: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/bindings/js/JSCSSRuleCustom.cpp: (WebCore::toJSNewlyCreated): * Source/WebCore/css/CSSGradientValue.h: * Source/WebCore/css/CSSNestedDeclarations.cpp: Added. (WebCore::CSSNestedDeclarations::CSSNestedDeclarations): (WebCore::CSSNestedDeclarations::style): (WebCore::CSSNestedDeclarations::cssText const): (WebCore::CSSNestedDeclarations::reattach): * Source/WebCore/css/CSSNestedDeclarations.h: Added. * Source/WebCore/css/CSSNestedDeclarations.idl: Added. * Source/WebCore/css/CSSShapeSegmentValue.cpp: * Source/WebCore/css/CSSStyleRule.cpp: (WebCore::CSSStyleRule::insertRule): * Source/WebCore/css/CSSStyleRule.h: * Source/WebCore/css/StyleRule.cpp: (WebCore::StyleRuleBase::visitDerived): (WebCore::StyleRuleBase::createCSSOMWrapper const): (WebCore::StyleRuleNestedDeclarations::StyleRuleNestedDeclarations): (WebCore::StyleRuleNestedDeclarations::debugDescription const): * Source/WebCore/css/StyleRule.h: (WebCore::StyleRuleBase::isStyleRuleNestedDeclarations const): (isType): * Source/WebCore/css/StyleRuleType.h: * Source/WebCore/css/StyleSheetContents.cpp: (WebCore::StyleSheetContents::traverseRules const): (WebCore::StyleSheetContents::traverseSubresources const): (WebCore::StyleSheetContents::mayDependOnBaseURL const): * Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp: * Source/WebCore/css/calc/CSSCalcTree+Simplification.h: * Source/WebCore/css/parser/CSSParser.h: * Source/WebCore/css/parser/CSSParserImpl.cpp: (WebCore::CSSParserImpl::parseNestedDeclarations): (WebCore::CSSParserImpl::createNestedDeclarationsRule): (WebCore::CSSParserImpl::consumeNestedGroupRules): (WebCore::CSSParserImpl::consumeBlockContent): (WebCore::CSSParserImpl::createNestingParentRule): Deleted. * Source/WebCore/css/parser/CSSParserImpl.h: * Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp: (WebCore::CSSPropertyParserHelpers::consumeImageSetResolutionOrTypeFunction): * Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h: * Source/WebCore/inspector/InspectorStyleSheet.cpp: (WebCore::flatteningStrategyForStyleRuleType): * Source/WebCore/style/RuleSetBuilder.cpp: (WebCore::Style::RuleSetBuilder::addChildRule): (WebCore::Style::RuleSetBuilder::addStyleRule): * Source/WebCore/style/RuleSetBuilder.h: --- LayoutTests/TestExpectations | 7 +- .../css/css-nesting/cssom-expected.txt | 1 + .../css/css-nesting/cssom.html | 37 +++++++++ .../mixed-declarations-rules-expected.html | 18 +++++ .../css-nesting/mixed-declarations-rules.html | 20 +++++ .../nested-declarations-cssom-expected.txt | 12 +-- .../nested-declarations-matching-expected.txt | 12 +-- ...ialize-group-rules-with-decls-expected.txt | 8 +- .../custom-property-rule-ambiguity.html | 7 +- Source/WebCore/CMakeLists.txt | 1 + .../WebCore/DerivedSources-input.xcfilelist | 1 + .../WebCore/DerivedSources-output.xcfilelist | 2 + Source/WebCore/DerivedSources.make | 1 + Source/WebCore/Sources.txt | 2 + .../WebCore/WebCore.xcodeproj/project.pbxproj | 8 ++ .../WebCore/bindings/js/JSCSSRuleCustom.cpp | 4 + Source/WebCore/css/CSSGradientValue.h | 3 +- Source/WebCore/css/CSSGroupingRule.cpp | 14 ++-- Source/WebCore/css/CSSNestedDeclarations.cpp | 61 ++++++++++++++ Source/WebCore/css/CSSNestedDeclarations.h | 58 ++++++++++++++ Source/WebCore/css/CSSNestedDeclarations.idl | 31 +++++++ Source/WebCore/css/CSSShapeSegmentValue.cpp | 1 + Source/WebCore/css/CSSStyleRule.cpp | 12 ++- Source/WebCore/css/CSSStyleRule.h | 1 + Source/WebCore/css/StyleRule.cpp | 19 +++++ Source/WebCore/css/StyleRule.h | 17 ++++ Source/WebCore/css/StyleRuleType.h | 1 + Source/WebCore/css/StyleSheetContents.cpp | 6 +- .../css/calc/CSSCalcTree+Simplification.cpp | 4 + .../css/calc/CSSCalcTree+Simplification.h | 1 + Source/WebCore/css/parser/CSSParser.h | 1 + Source/WebCore/css/parser/CSSParserImpl.cpp | 80 ++++++++++++++----- Source/WebCore/css/parser/CSSParserImpl.h | 6 +- .../CSSPropertyParserConsumer+Image.cpp | 4 +- .../parser/CSSPropertyParserConsumer+Image.h | 1 + .../WebCore/inspector/InspectorStyleSheet.cpp | 1 + Source/WebCore/style/RuleSetBuilder.cpp | 13 +++ Source/WebCore/style/RuleSetBuilder.h | 1 + 38 files changed, 419 insertions(+), 58 deletions(-) create mode 100644 LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html create mode 100644 LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html create mode 100644 Source/WebCore/css/CSSNestedDeclarations.cpp create mode 100644 Source/WebCore/css/CSSNestedDeclarations.h create mode 100644 Source/WebCore/css/CSSNestedDeclarations.idl diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations index 88fa34e2a925..ee03be54c530 100644 --- a/LayoutTests/TestExpectations +++ b/LayoutTests/TestExpectations @@ -8062,10 +8062,6 @@ imported/w3c/web-platform-tests/css/css-anchor-position/anchor-scroll-js-expose. fast/canvas/image-buffer-resource-limits.html [ Slow ] -# CSS Nesting interleaved declarations and rules -# https://bugs.webkit.org/show_bug.cgi?id=275365 -imported/w3c/web-platform-tests/css/css-nesting/nesting-basic.html [ ImageOnlyFailure ] - # Standardized CSS zoom tests. imported/w3c/web-platform-tests/css/css-viewport/width.html [ ImageOnlyFailure ] imported/w3c/web-platform-tests/css/css-viewport/zoom/border-spacing.html [ ImageOnlyFailure ] @@ -8085,6 +8081,9 @@ imported/w3c/web-platform-tests/css/css-viewport/zoom/word-spacing.html [ ImageO # WebRTC Encoded Transform - Test Expectation - Crashes on 'mac-wk2' debug and gtk-wk2 / wpe-wk2 webkit.org/b/275663 imported/w3c/web-platform-tests/webrtc-encoded-transform/script-transform-generateKeyFrame-simulcast.https.html [ Skip ] +# https://bugs.webkit.org/show_bug.cgi?id=276663 +inspector/css/getMatchedStylesForNodeNestingStyleGrouping.html [ ImageOnlyFailure ] + # https://bugs.webkit.org/show_bug.cgi?id=266843 flakey tests with racey promise console.logs imported/w3c/web-platform-tests/dom/observable/tentative/observable-from.any.html [ Skip ] diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt index 3069cf1be422..e0744c388ffd 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom-expected.txt @@ -16,4 +16,5 @@ PASS Simple CSSOM manipulation of subrules 8 PASS Simple CSSOM manipulation of subrules 9 PASS Simple CSSOM manipulation of subrules 10 PASS Mutating the selectorText of outer rule invalidates inner rules +PASS Manipulation of nested declarations through CSSOM diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html index 226fb791b572..c1c620f6bec2 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/cssom.html @@ -185,4 +185,41 @@ assert_equals(getComputedStyle(inner1).zIndex, '1'); assert_equals(getComputedStyle(inner2).zIndex, '1'); }, 'Mutating the selectorText of outer rule invalidates inner rules'); + + // CSSNestedDeclarations + test((t) => { + const main = document.createElement('main'); + main.innerHTML = ` + +
+
+ `; + document.documentElement.append(main); + t.add_cleanup(() => main.remove()); + assert_equals(getComputedStyle(outer).zIndex, '1'); + const main_ss = document.getElementById("main_ss").sheet; + const rule = main_ss.cssRules[0]; + assert_equals(rule.cssRules.length, 1); + rule.insertRule('z-index: 3;'); + assert_equals(rule.cssRules.length, 2); + assert_equals(getComputedStyle(outer).zIndex, '3'); + + // Throw only when no valid declaration https://github.com/w3c/csswg-drafts/issues/10520 + assert_throws_dom('SyntaxError', () => { rule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); }); + assert_equals(rule.cssRules.length, 2); + + // Test the insertion of nested declarations inside grouping rule + rule.insertRule('@media screen { a { color: blue; }}',2); + assert_equals(rule.cssRules.length, 3); + const mediaRule = rule.cssRules[2]; + mediaRule.insertRule('z-index: 3;'); + assert_equals(mediaRule.cssRules.length, 2); + assert_throws_dom('SyntaxError', () => { mediaRule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); }); + }, 'Manipulation of nested declarations through CSSOM'); + diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html new file mode 100644 index 000000000000..27fb9aa089b9 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules-expected.html @@ -0,0 +1,18 @@ + +Conditional rules with nesting + + + +

Tests pass if block is green

+
+ diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html new file mode 100644 index 000000000000..fbc9b54717c9 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/mixed-declarations-rules.html @@ -0,0 +1,20 @@ + +Mixed declarations and rules + + + + + +

Tests pass if block is green

+
+ diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt index 6c3edea49354..402bf6be325b 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-cssom-expected.txt @@ -1,8 +1,8 @@ -FAIL Trailing declarations assert_equals: expected 2 but got 1 -FAIL Mixed declarations assert_equals: expected 6 but got 3 -FAIL CSSNestedDeclarations.style assert_equals: expected 2 but got 1 -FAIL Nested group rule assert_equals: expected 2 but got 1 -FAIL Nested @scope rule assert_equals: expected 2 but got 1 -FAIL Inner rule starting with an ident assert_equals: expected 4 but got 2 +PASS Trailing declarations +PASS Mixed declarations +PASS CSSNestedDeclarations.style +PASS Nested group rule +PASS Nested @scope rule +PASS Inner rule starting with an ident diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt index 9173f6ddb7ef..899ddcaba4ef 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/nested-declarations-matching-expected.txt @@ -1,14 +1,14 @@ A1 A2 -FAIL Trailing declarations apply after any preceding rules assert_equals: expected "PASS" but got "FAIL" -FAIL Trailing declarations apply after any preceding rules (no leading) assert_equals: expected "PASS" but got "FAIL" -FAIL Trailing declarations apply after any preceding rules (multiple) assert_equals: expected "PASS" but got "FAIL" +PASS Trailing declarations apply after any preceding rules +PASS Trailing declarations apply after any preceding rules (no leading) +PASS Trailing declarations apply after any preceding rules (multiple) PASS Nested declarations rule has same specificity as outer selector PASS Nested declarations rule has top-level specificity behavior -FAIL Nested declarations rule has top-level specificity behavior (max matching) assert_equals: expected "PASS" but got "FAIL" -FAIL Bare declartaion in nested grouping rule can match pseudo-element assert_equals: expected "PASS" but got "FAIL" -FAIL Nested group rules have top-level specificity behavior assert_equals: expected "PASS" but got "FAIL" +PASS Nested declarations rule has top-level specificity behavior (max matching) +PASS Bare declartaion in nested grouping rule can match pseudo-element +PASS Nested group rules have top-level specificity behavior FAIL Nested @scope rules behave like :where(:scope) assert_equals: expected "PASS" but got "FAIL" FAIL Nested @scope rules behave like :where(:scope) (trailing) assert_equals: expected "PASS" but got "FAIL" FAIL Nested declarations rule responds to parent selector text change assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)" diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt index d95b21a00e48..b064e125839a 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-nesting/serialize-group-rules-with-decls-expected.txt @@ -1,13 +1,13 @@ PASS Declarations are serialized on one line, rules on two. -FAIL Mixed declarations/rules are on two lines. assert_equals: Mixed declarations/rules are on two lines. expected "div {\n @media screen {\n color: red; background-color: green;\n}\n}" but got "div {\n @media screen {\n & { color: red; background-color: green; }\n}\n}" -FAIL Implicit rule is serialized assert_equals: Implicit rule is serialized expected "div {\n @supports selector(&) {\n color: red; background-color: green;\n}\n &:hover { color: navy; }\n}" but got "div {\n @supports selector(&) {\n & { color: red; background-color: green; }\n}\n &:hover { color: navy; }\n}" +PASS Mixed declarations/rules are on two lines. +PASS Implicit rule is serialized PASS Implicit rule not removed PASS Implicit + empty hover rule PASS Implicit like rule not in first position PASS Two implicit-like rules -FAIL Implicit like rule after decls assert_equals: Implicit like rule after decls expected "div {\n @media screen {\n color: red;\n & { color: red; }\n}\n}" but got "div {\n @media screen {\n & { color: red; }\n & { color: red; }\n}\n}" -FAIL Implicit like rule after decls, missing closing braces assert_equals: Implicit like rule after decls, missing closing braces expected "div {\n @media screen {\n color: red;\n & { color: blue; }\n}\n}" but got "div {\n @media screen {\n & { color: red; }\n & { color: blue; }\n}\n}" +PASS Implicit like rule after decls +PASS Implicit like rule after decls, missing closing braces PASS Implicit like rule with other selectors PASS Implicit-like rule in style rule PASS Empty conditional rule diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html index 04f908acde2e..a8ac1de2c556 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html +++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-syntax/custom-property-rule-ambiguity.html @@ -46,11 +46,12 @@ assert_equals(rules.length, 1); assert_equals(rules[0].selectorText, 'div'); let div = rules[0]; - let x = div.style.getPropertyValue('--x'); - assert_equals(x.trim(), 'hover { }\n .b { }'); let childRules = div.cssRules; - assert_equals(childRules.length, 1); + assert_equals(childRules.length, 2); assert_equals(childRules[0].selectorText, '& .a'); + assert_true(childRules[1] instanceof CSSNestedDeclarations) + let x = childRules[1].style.getPropertyValue('--x'); + assert_equals(x.trim(), 'hover { }\n .b { }'); }, 'Nested rule that looks like a custom property declaration'); diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt index beca51b714ec..08ed736cae40 100644 --- a/Source/WebCore/CMakeLists.txt +++ b/Source/WebCore/CMakeLists.txt @@ -929,6 +929,7 @@ set(WebCore_NON_SVG_IDL_FILES css/CSSLayerStatementRule.idl css/CSSMediaRule.idl css/CSSNamespaceRule.idl + css/CSSNestedDeclarations.idl css/CSSPaintCallback.idl css/CSSPaintSize.idl css/CSSPageRule.idl diff --git a/Source/WebCore/DerivedSources-input.xcfilelist b/Source/WebCore/DerivedSources-input.xcfilelist index 9c47da74ef62..1e7fda430a8a 100644 --- a/Source/WebCore/DerivedSources-input.xcfilelist +++ b/Source/WebCore/DerivedSources-input.xcfilelist @@ -1243,6 +1243,7 @@ $(PROJECT_DIR)/css/CSSLayerBlockRule.idl $(PROJECT_DIR)/css/CSSLayerStatementRule.idl $(PROJECT_DIR)/css/CSSMediaRule.idl $(PROJECT_DIR)/css/CSSNamespaceRule.idl +$(PROJECT_DIR)/css/CSSNestedDeclarations.idl $(PROJECT_DIR)/css/CSSPageRule.idl $(PROJECT_DIR)/css/CSSPaintCallback.idl $(PROJECT_DIR)/css/CSSPaintSize.idl diff --git a/Source/WebCore/DerivedSources-output.xcfilelist b/Source/WebCore/DerivedSources-output.xcfilelist index 609ca8fafacd..a34c5a958f54 100644 --- a/Source/WebCore/DerivedSources-output.xcfilelist +++ b/Source/WebCore/DerivedSources-output.xcfilelist @@ -433,6 +433,8 @@ $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSMediaRule.cpp $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSMediaRule.h $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNamespaceRule.cpp $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNamespaceRule.h +$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNestedDeclarations.cpp +$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNestedDeclarations.h $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNumericArray.cpp $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNumericArray.h $(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSCSSNumericBaseType.cpp diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make index a3f0021fcd52..cb7bb7bdae54 100644 --- a/Source/WebCore/DerivedSources.make +++ b/Source/WebCore/DerivedSources.make @@ -964,6 +964,7 @@ JS_BINDING_IDLS := \ $(WebCore)/css/CSSKeyframesRule.idl \ $(WebCore)/css/CSSMediaRule.idl \ $(WebCore)/css/CSSNamespaceRule.idl \ + $(WebCore)/css/CSSNestedDeclarations.idl \ $(WebCore)/css/CSSPageRule.idl \ $(WebCore)/css/CSSPaintCallback.idl \ $(WebCore)/css/CSSPaintSize.idl \ diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt index 41ec5d21cc07..451f706d022f 100644 --- a/Source/WebCore/Sources.txt +++ b/Source/WebCore/Sources.txt @@ -896,6 +896,7 @@ css/CSSMarkup.cpp css/CSSMediaRule.cpp css/CSSNamedImageValue.cpp css/CSSNamespaceRule.cpp +css/CSSNestedDeclarations.cpp css/CSSOffsetRotateValue.cpp css/CSSPageRule.cpp css/CSSPaintImageValue.cpp @@ -3412,6 +3413,7 @@ JSCSSLayerBlockRule.cpp JSCSSLayerStatementRule.cpp JSCSSMediaRule.cpp JSCSSNamespaceRule.cpp +JSCSSNestedDeclarations.cpp JSCSSPageRule.cpp JSCSSPaintCallback.cpp JSCSSPaintSize.cpp diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj index 21fd4ee5dadb..c4c1739ffb12 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj @@ -4976,6 +4976,7 @@ BE913D80181EF92400DCB09E /* TrackPrivateBase.h in Headers */ = {isa = PBXBuildFile; fileRef = BE913D7F181EF8E500DCB09E /* TrackPrivateBase.h */; settings = {ATTRIBUTES = (Private, ); }; }; BE961C5518AD338C00D07DC5 /* InbandDataTextTrack.h in Headers */ = {isa = PBXBuildFile; fileRef = BE961C5318AD337C00D07DC5 /* InbandDataTextTrack.h */; }; BEA807C90F714A0300524199 /* SelectionGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = BEA807C70F714A0300524199 /* SelectionGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; }; + BEAA24022C34A9E600C37BBE /* CSSNestedDeclarations.h in Headers */ = {isa = PBXBuildFile; fileRef = BEAA23FF2C34A63900C37BBE /* CSSNestedDeclarations.h */; settings = {ATTRIBUTES = (Private, ); }; }; BEF29EEB1715DD0900C4B4C9 /* AudioTrackPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF29EE91715DD0900C4B4C9 /* AudioTrackPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; BEF29EEC1715DD0900C4B4C9 /* VideoTrackPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = BEF29EEA1715DD0900C4B4C9 /* VideoTrackPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; C0C054CB1118C8E400CE2636 /* CodeGenerator.pm in Headers */ = {isa = PBXBuildFile; fileRef = 93F8B3050A300FE100F61AB8 /* CodeGenerator.pm */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -18120,6 +18121,9 @@ BE983D95052A2E0A00892D85 /* WebCoreKeyboardUIMode.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreKeyboardUIMode.h; sourceTree = ""; tabWidth = 8; usesTabs = 0; }; BEA807C60F714A0300524199 /* SelectionGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectionGeometry.cpp; sourceTree = ""; }; BEA807C70F714A0300524199 /* SelectionGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectionGeometry.h; sourceTree = ""; }; + BEAA23FF2C34A63900C37BBE /* CSSNestedDeclarations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSNestedDeclarations.h; sourceTree = ""; }; + BEAA24002C34A64400C37BBE /* CSSNestedDeclarations.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = CSSNestedDeclarations.idl; sourceTree = ""; }; + BEAA24012C34A94300C37BBE /* CSSNestedDeclarations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSNestedDeclarations.cpp; sourceTree = ""; }; BED8BAF528EB355F003C7D65 /* CSSFontFeatureValuesRule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSFontFeatureValuesRule.cpp; sourceTree = ""; }; BED8BAF628EB3560003C7D65 /* CSSFontFeatureValuesRule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSFontFeatureValuesRule.h; sourceTree = ""; }; BEF29EE91715DD0900C4B4C9 /* AudioTrackPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTrackPrivate.h; sourceTree = ""; }; @@ -36336,6 +36340,9 @@ 94E8394E1DFB2700007BC6A7 /* CSSNamespaceRule.cpp */, 94E8394F1DFB2700007BC6A7 /* CSSNamespaceRule.h */, 94E839501DFB29A4007BC6A7 /* CSSNamespaceRule.idl */, + BEAA24012C34A94300C37BBE /* CSSNestedDeclarations.cpp */, + BEAA23FF2C34A63900C37BBE /* CSSNestedDeclarations.h */, + BEAA24002C34A64400C37BBE /* CSSNestedDeclarations.idl */, 668500B8273EEB7800FCCAD6 /* CSSOffsetRotateValue.cpp */, 668500B9273EEB7800FCCAD6 /* CSSOffsetRotateValue.h */, A80E6CCB0A1989CA007FB8C5 /* CSSPageRule.cpp */, @@ -39064,6 +39071,7 @@ A80E6D030A1989CA007FB8C5 /* CSSMediaRule.h in Headers */, 314BE3A11B30F6B700141982 /* CSSNamedImageValue.h in Headers */, 94E839511DFB2A0E007BC6A7 /* CSSNamespaceRule.h in Headers */, + BEAA24022C34A9E600C37BBE /* CSSNestedDeclarations.h in Headers */, 2AEF6FDB26E7ECC700326D02 /* CSSNumericArray.h in Headers */, 2AEF6FDC26E7ECCC00326D02 /* CSSNumericBaseType.h in Headers */, 2ACB4D2B26DC4ACE00BEB753 /* CSSNumericFactory.h in Headers */, diff --git a/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp b/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp index 3713055b3077..61122fd457bd 100644 --- a/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp +++ b/Source/WebCore/bindings/js/JSCSSRuleCustom.cpp @@ -38,6 +38,7 @@ #include "CSSLayerStatementRule.h" #include "CSSMediaRule.h" #include "CSSNamespaceRule.h" +#include "CSSNestedDeclarations.h" #include "CSSPageRule.h" #include "CSSPropertyRule.h" #include "CSSScopeRule.h" @@ -57,6 +58,7 @@ #include "JSCSSLayerStatementRule.h" #include "JSCSSMediaRule.h" #include "JSCSSNamespaceRule.h" +#include "JSCSSNestedDeclarations.h" #include "JSCSSPageRule.h" #include "JSCSSPropertyRule.h" #include "JSCSSScopeRule.h" @@ -87,6 +89,8 @@ JSValue toJSNewlyCreated(JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref(globalObject, WTFMove(rule)); case StyleRuleType::StyleWithNesting: return createWrapper(globalObject, WTFMove(rule)); + case StyleRuleType::NestedDeclarations: + return createWrapper(globalObject, WTFMove(rule)); case StyleRuleType::Media: return createWrapper(globalObject, WTFMove(rule)); case StyleRuleType::FontFace: diff --git a/Source/WebCore/css/CSSGradientValue.h b/Source/WebCore/css/CSSGradientValue.h index 1f0e88b0e173..8164fa21e375 100644 --- a/Source/WebCore/css/CSSGradientValue.h +++ b/Source/WebCore/css/CSSGradientValue.h @@ -31,11 +31,10 @@ #include "CSSPropertyParserConsumer+UnevaluatedCalc.h" #include "ColorInterpolationMethod.h" #include "Gradient.h" +#include "StyleImage.h" namespace WebCore { -class StyleImage; - namespace Style { class BuilderState; } diff --git a/Source/WebCore/css/CSSGroupingRule.cpp b/Source/WebCore/css/CSSGroupingRule.cpp index 7724366145e5..82887e346b16 100644 --- a/Source/WebCore/css/CSSGroupingRule.cpp +++ b/Source/WebCore/css/CSSGroupingRule.cpp @@ -32,6 +32,7 @@ #include "CSSGroupingRule.h" #include "CSSParser.h" +#include "CSSParserImpl.h" #include "CSSRuleList.h" #include "CSSStyleSheet.h" #include "StylePropertiesInlines.h" @@ -69,13 +70,15 @@ ExceptionOr CSSGroupingRule::insertRule(const String& ruleString, unsi auto isNestedContext = hasStyleRuleAncestor() ? CSSParserEnum::IsNestedContext::Yes : CSSParserEnum::IsNestedContext::No; RefPtr newRule = CSSParser::parseRule(parserContext(), styleSheet ? &styleSheet->contents() : nullptr, ruleString, isNestedContext); if (!newRule) { - // SyntaxError: Raised if the specified rule has a syntax error and is unparsable. - return Exception { ExceptionCode::SyntaxError }; + newRule = CSSParserImpl::parseNestedDeclarations(parserContext(), ruleString); + // SyntaxError raised if there is nothing valid to insert (rule or nested declarations). + if (!newRule) + return Exception { ExceptionCode::SyntaxError }; } if (newRule->isImportRule() || newRule->isNamespaceRule()) { - // FIXME: an HierarchyRequestError should also be thrown for a @charset or a nested - // @media rule. They are currently not getting parsed, resulting in a SyntaxError + // FIXME: an HierarchyRequestError should also be thrown for a @charset. + // They are currently not getting parsed, resulting in a SyntaxError // to get raised above. // HierarchyRequestError: Raised if the rule cannot be inserted at the specified @@ -84,8 +87,7 @@ ExceptionOr CSSGroupingRule::insertRule(const String& ruleString, unsi return Exception { ExceptionCode::HierarchyRequestError }; } - // Nesting inside style rule only accepts style rule or group rule - if (hasStyleRuleAncestor() && !newRule->isStyleRule() && !newRule->isGroupRule()) + if (hasStyleRuleAncestor() && !newRule->isStyleRule() && !newRule->isGroupRule() && !newRule->isNestedDeclarationsRule()) return Exception { ExceptionCode::HierarchyRequestError }; CSSStyleSheet::RuleMutationScope mutationScope(this); diff --git a/Source/WebCore/css/CSSNestedDeclarations.cpp b/Source/WebCore/css/CSSNestedDeclarations.cpp new file mode 100644 index 000000000000..a6422dbfb37d --- /dev/null +++ b/Source/WebCore/css/CSSNestedDeclarations.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "CSSNestedDeclarations.h" + +#include "DeclaredStylePropertyMap.h" +#include "MutableStyleProperties.h" +#include "PropertySetCSSStyleDeclaration.h" +#include "StyleProperties.h" +#include "StyleRule.h" + +#include + +namespace WebCore { + +CSSNestedDeclarations::CSSNestedDeclarations(StyleRuleNestedDeclarations& rule, CSSStyleSheet* parent) + : CSSRule(parent) + , m_styleRule(rule) +{ +} + +CSSNestedDeclarations::~CSSNestedDeclarations() = default; + +CSSStyleDeclaration& CSSNestedDeclarations::style() +{ + if (!m_propertiesCSSOMWrapper) + m_propertiesCSSOMWrapper = StyleRuleCSSStyleDeclaration::create(m_styleRule->mutableProperties(), *this); + return *m_propertiesCSSOMWrapper; +} + +String CSSNestedDeclarations::cssText() const +{ + return m_styleRule->properties().asText(); +} + +void CSSNestedDeclarations::reattach(StyleRuleBase& rule) +{ + m_styleRule = downcast(rule); + + if (m_propertiesCSSOMWrapper) + m_propertiesCSSOMWrapper->reattach(m_styleRule->mutableProperties()); +} + +} // namespace WebCore diff --git a/Source/WebCore/css/CSSNestedDeclarations.h b/Source/WebCore/css/CSSNestedDeclarations.h new file mode 100644 index 000000000000..2d0ea0fdfeb5 --- /dev/null +++ b/Source/WebCore/css/CSSNestedDeclarations.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "CSSRule.h" + +namespace WebCore { + +class CSSRuleList; +class CSSStyleDeclaration; +class DeclaredStylePropertyMap; +class StylePropertyMap; +class StyleRuleCSSStyleDeclaration; +class StyleRuleNestedDeclarations; + +class CSSNestedDeclarations final : public CSSRule, public CanMakeWeakPtr { +public: + static Ref create(StyleRuleNestedDeclarations& rule, CSSStyleSheet* sheet) { return adoptRef(* new CSSNestedDeclarations(rule, sheet)); }; + + virtual ~CSSNestedDeclarations(); + + WEBCORE_EXPORT CSSStyleDeclaration& style(); + // StylePropertyMap& styleMap() { return m_styleMap.get(); } + +private: + CSSNestedDeclarations(StyleRuleNestedDeclarations&, CSSStyleSheet*); + + String cssText() const final; + String cssTextInternal(StringBuilder& declarations, StringBuilder& rules) const; + + void reattach(StyleRuleBase&) final; + StyleRuleType styleRuleType() const final { return StyleRuleType::NestedDeclarations; } + + Ref m_styleRule; + // Ref m_styleMap; + RefPtr m_propertiesCSSOMWrapper; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_CSS_RULE(CSSNestedDeclarations, StyleRuleType::NestedDeclarations) diff --git a/Source/WebCore/css/CSSNestedDeclarations.idl b/Source/WebCore/css/CSSNestedDeclarations.idl new file mode 100644 index 000000000000..0046cbcafe2e --- /dev/null +++ b/Source/WebCore/css/CSSNestedDeclarations.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +// https://drafts.csswg.org/css-nesting/#the-cssnestrule + +typedef USVString CSSOMString; + +[ + Exposed=Window +] interface CSSNestedDeclarations: CSSRule { + [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + + // https://drafts.css-houdini.org/css-typed-om/#declared-stylepropertymap-objects + // [SameObject] readonly attribute StylePropertyMap styleMap; +}; diff --git a/Source/WebCore/css/CSSShapeSegmentValue.cpp b/Source/WebCore/css/CSSShapeSegmentValue.cpp index a39c375a0aac..48b8605cba36 100644 --- a/Source/WebCore/css/CSSShapeSegmentValue.cpp +++ b/Source/WebCore/css/CSSShapeSegmentValue.cpp @@ -29,6 +29,7 @@ #include "BasicShapes.h" #include "CSSPrimitiveValue.h" +#include "CSSPrimitiveValueMappings.h" #include "CSSValuePair.h" #include "CalculationValue.h" #include "StyleBuilderState.h" diff --git a/Source/WebCore/css/CSSStyleRule.cpp b/Source/WebCore/css/CSSStyleRule.cpp index 9bc6dbef1b3c..bb941763bb78 100644 --- a/Source/WebCore/css/CSSStyleRule.cpp +++ b/Source/WebCore/css/CSSStyleRule.cpp @@ -24,6 +24,7 @@ #include "CSSGroupingRule.h" #include "CSSParser.h" +#include "CSSParserImpl.h" #include "CSSRuleList.h" #include "CSSStyleSheet.h" #include "DeclaredStylePropertyMap.h" @@ -236,10 +237,13 @@ ExceptionOr CSSStyleRule::insertRule(const String& ruleString, unsigne RefPtr styleSheet = parentStyleSheet(); RefPtr newRule = CSSParser::parseRule(parserContext(), styleSheet ? &styleSheet->contents() : nullptr, ruleString, CSSParserEnum::IsNestedContext::Yes); - if (!newRule) - return Exception { ExceptionCode::SyntaxError }; - // We only accepts style rule or group rule (@media,...) inside style rules. - if (!newRule->isStyleRule() && !newRule->isGroupRule()) + if (!newRule) { + newRule = CSSParserImpl::parseNestedDeclarations(parserContext(), ruleString); + if (!newRule) + return Exception { ExceptionCode::SyntaxError }; + } + // We only accepts style rule, nested group rule (@media,...) or nested declarations inside style rules. + if (!newRule->isStyleRule() && !newRule->isGroupRule() && !newRule->isNestedDeclarationsRule()) return Exception { ExceptionCode::HierarchyRequestError }; CSSStyleSheet::RuleMutationScope mutationScope(this); diff --git a/Source/WebCore/css/CSSStyleRule.h b/Source/WebCore/css/CSSStyleRule.h index 07353bb1fcb5..6ddd53e63e65 100644 --- a/Source/WebCore/css/CSSStyleRule.h +++ b/Source/WebCore/css/CSSStyleRule.h @@ -33,6 +33,7 @@ class StylePropertyMap; class StyleRuleCSSStyleDeclaration; class StyleRule; class StyleRuleWithNesting; +class StyleRuleCSSStyleDeclaration; class CSSStyleRule final : public CSSRule, public CanMakeWeakPtr { public: diff --git a/Source/WebCore/css/StyleRule.cpp b/Source/WebCore/css/StyleRule.cpp index 8b3a4b4bd61a..47133698757f 100644 --- a/Source/WebCore/css/StyleRule.cpp +++ b/Source/WebCore/css/StyleRule.cpp @@ -35,6 +35,7 @@ #include "CSSLayerStatementRule.h" #include "CSSMediaRule.h" #include "CSSNamespaceRule.h" +#include "CSSNestedDeclarations.h" #include "CSSPageRule.h" #include "CSSPropertyRule.h" #include "CSSScopeRule.h" @@ -88,6 +89,8 @@ template constexpr decltype(auto) StyleRuleBase::visitDerived( return std::invoke(std::forward(visitor), uncheckedDowncast(*this)); case StyleRuleType::StyleWithNesting: return std::invoke(std::forward(visitor), uncheckedDowncast(*this)); + case StyleRuleType::NestedDeclarations: + return std::invoke(std::forward(visitor), uncheckedDowncast(*this)); case StyleRuleType::Page: return std::invoke(std::forward(visitor), uncheckedDowncast(*this)); case StyleRuleType::FontFace: @@ -170,6 +173,9 @@ Ref StyleRuleBase::createCSSOMWrapper(CSSStyleSheet* parentSheet, CSSRu [&](StyleRuleWithNesting& rule) -> Ref { return CSSStyleRule::create(rule, parentSheet); }, + [&](StyleRuleNestedDeclarations& rule) -> Ref { + return CSSNestedDeclarations::create(rule, parentSheet); + }, [&](StyleRulePage& rule) -> Ref { return CSSPageRule::create(rule, parentSheet); }, @@ -400,6 +406,19 @@ StyleRulePage::StyleRulePage(const StyleRulePage& o) { } +StyleRuleNestedDeclarations::StyleRuleNestedDeclarations(Ref&& properties) + : StyleRule(WTFMove(properties), false, { }) +{ + setType(StyleRuleType::NestedDeclarations); +} + +String StyleRuleNestedDeclarations::debugDescription() const +{ + StringBuilder builder; + builder.append("StyleRuleNestedDeclarations ["_s, properties().asText(), ']'); + return builder.toString(); +} + StyleRulePage::~StyleRulePage() = default; Ref StyleRulePage::create(Ref&& properties, CSSSelectorList&& selectors) diff --git a/Source/WebCore/css/StyleRule.h b/Source/WebCore/css/StyleRule.h index 9f1cca99935b..e640fc9dbd51 100644 --- a/Source/WebCore/css/StyleRule.h +++ b/Source/WebCore/css/StyleRule.h @@ -70,6 +70,7 @@ class StyleRuleBase : public RefCounted { bool isPageRule() const { return type() == StyleRuleType::Page; } bool isStyleRule() const { return type() == StyleRuleType::Style || type() == StyleRuleType::StyleWithNesting; } bool isStyleRuleWithNesting() const { return type() == StyleRuleType::StyleWithNesting; } + bool isNestedDeclarationsRule() const { return type() == StyleRuleType::NestedDeclarations; } bool isGroupRule() const { return type() == StyleRuleType::Media || type() == StyleRuleType::Supports || type() == StyleRuleType::LayerBlock || type() == StyleRuleType::Container || type() == StyleRuleType::Scope || type() == StyleRuleType::StartingStyle; } bool isSupportsRule() const { return type() == StyleRuleType::Supports; } bool isImportRule() const { return type() == StyleRuleType::Import; } @@ -188,6 +189,18 @@ class StyleRuleWithNesting final : public StyleRule { CSSSelectorList m_originalSelectorList; }; +class StyleRuleNestedDeclarations final : public StyleRule { +public: + static Ref create(Ref&& properties) { return adoptRef(*new StyleRuleNestedDeclarations(WTFMove(properties))); } + ~StyleRuleNestedDeclarations() = default; + Ref copy() const { return adoptRef(*new StyleRuleNestedDeclarations(*this)); } + + String debugDescription() const; +private: + explicit StyleRuleNestedDeclarations(Ref&&); + StyleRuleNestedDeclarations(const StyleRuleNestedDeclarations&) = default; +}; + class StyleRuleFontFace final : public StyleRuleBase { public: static Ref create(Ref&& properties) { return adoptRef(*new StyleRuleFontFace(WTFMove(properties))); } @@ -516,6 +529,10 @@ SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::StyleRuleWithNesting) static bool isType(const WebCore::StyleRuleBase& rule) { return rule.isStyleRuleWithNesting(); } SPECIALIZE_TYPE_TRAITS_END() +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::StyleRuleNestedDeclarations) + static bool isType(const WebCore::StyleRuleBase& rule) { return rule.isNestedDeclarationsRule(); } +SPECIALIZE_TYPE_TRAITS_END() + SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::StyleRuleGroup) static bool isType(const WebCore::StyleRuleBase& rule) { return rule.isGroupRule(); } SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/css/StyleRuleType.h b/Source/WebCore/css/StyleRuleType.h index 2dff763076a5..0dbd10c8d311 100644 --- a/Source/WebCore/css/StyleRuleType.h +++ b/Source/WebCore/css/StyleRuleType.h @@ -55,6 +55,7 @@ enum class StyleRuleType : uint8_t { StyleWithNesting, Scope, StartingStyle, + NestedDeclarations }; static constexpr auto firstUnexposedStyleRuleType = StyleRuleType::ViewTransition; diff --git a/Source/WebCore/css/StyleSheetContents.cpp b/Source/WebCore/css/StyleSheetContents.cpp index 459d53807b23..6de72d67058f 100644 --- a/Source/WebCore/css/StyleSheetContents.cpp +++ b/Source/WebCore/css/StyleSheetContents.cpp @@ -526,7 +526,7 @@ bool StyleSheetContents::traverseRules(const FunctionstyleSheet(); + RefPtr importedStyleSheet = importRule->styleSheet(); if (importedStyleSheet && importedStyleSheet->traverseRules(handler)) return true; } @@ -555,6 +555,8 @@ bool StyleSheetContents::traverseSubresources(const Function(rule).properties().traverseSubresources(handler); case StyleRuleType::StyleWithNesting: return uncheckedDowncast(rule).properties().traverseSubresources(handler); + case StyleRuleType::NestedDeclarations: + return uncheckedDowncast(rule).properties().traverseSubresources(handler); case StyleRuleType::FontFace: return uncheckedDowncast(rule).properties().traverseSubresources(handler); case StyleRuleType::Import: @@ -632,6 +634,8 @@ bool StyleSheetContents::mayDependOnBaseURL() const return uncheckedDowncast(rule).properties().mayDependOnBaseURL(); case StyleRuleType::StyleWithNesting: return uncheckedDowncast(rule).properties().mayDependOnBaseURL(); + case StyleRuleType::NestedDeclarations: + return uncheckedDowncast(rule).properties().mayDependOnBaseURL(); case StyleRuleType::FontFace: return uncheckedDowncast(rule).properties().mayDependOnBaseURL(); case StyleRuleType::Import: diff --git a/Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp b/Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp index 996f4a25bf74..631c958aa232 100644 --- a/Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp +++ b/Source/WebCore/css/calc/CSSCalcTree+Simplification.cpp @@ -29,7 +29,11 @@ #include "CSSCalcTree+NumericIdentity.h" #include "CSSCalcTree+Traversal.h" #include "CSSCalcTree.h" +#include "CSSPrimitiveValue.h" +#include "CalculationCategory.h" #include "CalculationExecutor.h" +#include "RenderStyle.h" +#include "RenderStyleInlines.h" #include namespace WebCore { diff --git a/Source/WebCore/css/calc/CSSCalcTree+Simplification.h b/Source/WebCore/css/calc/CSSCalcTree+Simplification.h index 67bdfb0f9096..ac1b24ac0e14 100644 --- a/Source/WebCore/css/calc/CSSCalcTree+Simplification.h +++ b/Source/WebCore/css/calc/CSSCalcTree+Simplification.h @@ -24,6 +24,7 @@ #pragma once +#include "CSSCalcSymbolTable.h" #include "CSSCalcTree.h" #include "CSSToLengthConversionData.h" #include diff --git a/Source/WebCore/css/parser/CSSParser.h b/Source/WebCore/css/parser/CSSParser.h index 7f8519d911b7..2531c0fd9827 100644 --- a/Source/WebCore/css/parser/CSSParser.h +++ b/Source/WebCore/css/parser/CSSParser.h @@ -40,6 +40,7 @@ class Element; class ImmutableStyleProperties; class MutableStyleProperties; class StyleRuleBase; +class StyleRuleNestedDeclarations; class StyleRuleKeyframe; class StyleSheetContents; diff --git a/Source/WebCore/css/parser/CSSParserImpl.cpp b/Source/WebCore/css/parser/CSSParserImpl.cpp index 5510dd3deece..fd61d2bedcdd 100644 --- a/Source/WebCore/css/parser/CSSParserImpl.cpp +++ b/Source/WebCore/css/parser/CSSParserImpl.cpp @@ -1,5 +1,5 @@ // Copyright 2014 The Chromium Authors. All rights reserved. -// Copyright (C) 2016-2022 Apple Inc. All rights reserved. +// Copyright (C) 2016-2024 Apple Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -70,7 +70,6 @@ #include "StyleRule.h" #include "StyleRuleImport.h" #include "StyleSheetContents.h" -#include "css/CSSProperty.h" #include #include #include @@ -241,6 +240,15 @@ RefPtr CSSParserImpl::parseRule(const String& string, const CSSPa return rule; } +RefPtr CSSParserImpl::parseNestedDeclarations(const CSSParserContext&context , const String& string) +{ + auto properties = MutableStyleProperties::createEmpty(); + if (!parseDeclarationList(properties.ptr(), string , context)) + return { }; + + return StyleRuleNestedDeclarations::create(WTFMove(properties)); +} + void CSSParserImpl::parseStyleSheet(const String& string, const CSSParserContext& context, StyleSheetContents& styleSheet) { CSSParserImpl parser(context, string, &styleSheet, nullptr); @@ -666,14 +674,10 @@ void CSSParserImpl::runInNewNestingContext(auto&& run) m_nestingContextStack.removeLast(); } -Ref CSSParserImpl::createNestingParentRule() +Ref CSSParserImpl::createNestedDeclarationsRule() { - auto nestingParentSelector = makeUnique(); - nestingParentSelector->setMatch(CSSSelector::Match::NestingParent); - MutableCSSSelectorList selectorList; - selectorList.append(WTFMove(nestingParentSelector)); auto properties = createStyleProperties(topContext().m_parsedProperties, m_context.mode); - return StyleRuleWithNesting::create(WTFMove(properties), m_context.hasDocumentSecurityOrigin, CSSSelectorList { WTFMove(selectorList) }, { }); + return StyleRuleNestedDeclarations::create(WTFMove(properties)); } RefPtr CSSParserImpl::protectedStyleSheet() const @@ -695,10 +699,10 @@ Vector> CSSParserImpl::consumeNestedGroupRules(CSSParserToken consumeStyleBlock(block, StyleRuleType::Style, ParsingStyleDeclarationsInRuleList::Yes); if (!topContext().m_parsedProperties.isEmpty()) { - // This at-rule contains orphan declarations, we attach them to an implicit parent nesting rule. Web + // This at-rule contains orphan declarations, we attach them to a nested declaration rule. Web // Inspector expects this rule to occur first in the children rules, and to contain all orphaned // property declarations. - rules.append(createNestingParentRule()); + rules.append(createNestedDeclarationsRule()); if (m_observerWrapper) m_observerWrapper->observer().markRuleBodyContainsImplicitlyNestedProperties(); @@ -1416,14 +1420,34 @@ void CSSParserImpl::consumeBlockContent(CSSParserTokenRange range, StyleRuleType range.consumeComponentValue(); }; - auto consumeNestedQualifiedRule = [&] { + auto consumeNestedQualifiedRule = [&] () -> RefPtr { RefPtr rule = consumeQualifiedRule(range, AllowedRules::RegularRules); if (!rule) - return false; + return { }; if (!rule->isStyleRule()) - return false; - topContext().m_parsedRules.append(rule.releaseNonNull()); - return true; + return { }; + return rule; + }; + + ParsedPropertyVector initialDeclarationBlock; + bool initialDeclarationBlockFinished = false; + auto storeDeclarations = [&] { + // We don't wrap the first declaration block, we store it until the end of the style rule. + if (!initialDeclarationBlockFinished) { + initialDeclarationBlockFinished = true; + std::swap(initialDeclarationBlock, topContext().m_parsedProperties); + return; + } + + // Nothing to wrap + if (topContext().m_parsedProperties.isEmpty()) + return; + + ParsedPropertyVector properties; + std::swap(properties, topContext().m_parsedProperties); + + auto rule = StyleRuleNestedDeclarations::create(createStyleProperties(properties, m_context.mode)); + topContext().m_parsedRules.append(WTFMove(rule)); }; while (!range.atEnd()) { @@ -1455,8 +1479,13 @@ void CSSParserImpl::consumeBlockContent(CSSParserTokenRange range, StyleRuleType if (!isValidDeclaration) { // If it's not a valid declaration, we try to parse it as a nested style rule. range = initialRange; - if (nestedRulesAllowed() && consumeNestedQualifiedRule()) - break; + if (nestedRulesAllowed()) { + if (auto rule = consumeNestedQualifiedRule()) { + storeDeclarations(); + topContext().m_parsedRules.append(rule.releaseNonNull()); + break; + } + } errorRecovery(); } break; @@ -1469,6 +1498,7 @@ void CSSParserImpl::consumeBlockContent(CSSParserTokenRange range, StyleRuleType if (!rule->isGroupRule()) break; topContext().m_parsedRules.append(rule.releaseNonNull()); + storeDeclarations(); } else { RefPtr rule = consumeAtRule(range, AllowedRules::NoRules); ASSERT_UNUSED(rule, !rule); @@ -1476,12 +1506,24 @@ void CSSParserImpl::consumeBlockContent(CSSParserTokenRange range, StyleRuleType break; } default: - if (nestedRulesAllowed() && consumeNestedQualifiedRule()) - break; + if (nestedRulesAllowed()) { + if (auto rule = consumeNestedQualifiedRule()) { + storeDeclarations(); + topContext().m_parsedRules.append(rule.releaseNonNull()); + break; + } + } errorRecovery(); } } + // Store trailing declarations if any + storeDeclarations(); + + // Restore the initial declaration block + if (!initialDeclarationBlock.isEmpty()) + std::swap(initialDeclarationBlock, topContext().m_parsedProperties); + // Yield remaining comments if (useObserver) { m_observerWrapper->yieldCommentsBefore(range); diff --git a/Source/WebCore/css/parser/CSSParserImpl.h b/Source/WebCore/css/parser/CSSParserImpl.h index a259843cdd5b..91b7cb20ac38 100644 --- a/Source/WebCore/css/parser/CSSParserImpl.h +++ b/Source/WebCore/css/parser/CSSParserImpl.h @@ -1,5 +1,5 @@ // Copyright 2014 The Chromium Authors. All rights reserved. -// Copyright (C) 2016-2022 Apple Inc. All rights reserved. +// Copyright (C) 2016-2024 Apple Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -114,6 +114,8 @@ class CSSParserImpl { static IsImportant consumeTrailingImportantAndWhitespace(CSSParserTokenRange&); + static RefPtr parseNestedDeclarations(const CSSParserContext&, const String&); + CSSTokenizer* tokenizer() const { return m_tokenizer.get(); } private: @@ -184,7 +186,7 @@ class CSSParserImpl { RefPtr protectedStyleSheet() const; - Ref createNestingParentRule(); + Ref createNestedDeclarationsRule(); void runInNewNestingContext(auto&& run); NestingContext& topContext() { diff --git a/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp b/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp index 89584c2612d7..676a1ab922ed 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp +++ b/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.cpp @@ -47,6 +47,7 @@ #include "CSSPropertyParserConsumer+Length.h" #include "CSSPropertyParserConsumer+LengthDefinitions.h" #include "CSSPropertyParserConsumer+MetaConsumer.h" +#include "CSSPropertyParserConsumer+MetaResolver.h" #include "CSSPropertyParserConsumer+MetaTransformer.h" #include "CSSPropertyParserConsumer+Number.h" #include "CSSPropertyParserConsumer+NumberDefinitions.h" @@ -59,6 +60,7 @@ #include "CSSPropertyParserConsumer+String.h" #include "CSSPropertyParserConsumer+URL.h" #include "CSSPropertyParserConsumer+UnevaluatedCalc.h" +#include "CSSValue.h" #include "CSSValueList.h" #include "StyleImage.h" #include @@ -1022,7 +1024,7 @@ static RefPtr consumeImageSetResolutionOrTypeFunction(CSSPars return CSSPrimitiveValue::create(typeFunction.value); }, [&](const auto& resolution) -> RefPtr { - return CSSPrimitiveValueResolverBase::resolve(resolution, { }, options); + return CSSPrimitiveValueResolverBase::resolve(resolution, CSSCalcSymbolTable { }, options); } ); } diff --git a/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h b/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h index 1467a36e6df5..a596e623731d 100644 --- a/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h +++ b/Source/WebCore/css/parser/CSSPropertyParserConsumer+Image.h @@ -32,6 +32,7 @@ namespace WebCore { class CSSParserTokenRange; class CSSValue; struct CSSParserContext; +class CSSValue; namespace CSSPropertyParserHelpers { diff --git a/Source/WebCore/inspector/InspectorStyleSheet.cpp b/Source/WebCore/inspector/InspectorStyleSheet.cpp index b3b7d173aa4b..6174c64091b4 100644 --- a/Source/WebCore/inspector/InspectorStyleSheet.cpp +++ b/Source/WebCore/inspector/InspectorStyleSheet.cpp @@ -120,6 +120,7 @@ static RuleFlatteningStrategy flatteningStrategyForStyleRuleType(StyleRuleType s case StyleRuleType::FontPaletteValues: case StyleRuleType::Property: case StyleRuleType::ViewTransition: + case StyleRuleType::NestedDeclarations: // These rule types do not contain rules that apply directly to an element (i.e. these rules should not appear // in the Styles details sidebar of the Elements tab in Web Inspector). return RuleFlatteningStrategy::Ignore; diff --git a/Source/WebCore/style/RuleSetBuilder.cpp b/Source/WebCore/style/RuleSetBuilder.cpp index c79c098c38cd..4312bbf4dd54 100644 --- a/Source/WebCore/style/RuleSetBuilder.cpp +++ b/Source/WebCore/style/RuleSetBuilder.cpp @@ -118,6 +118,11 @@ void RuleSetBuilder::addChildRule(Ref rule) addStyleRule(uncheckedDowncast(rule)); return; + case StyleRuleType::NestedDeclarations: + if (m_ruleSet) + addStyleRule(uncheckedDowncast(rule)); + return; + case StyleRuleType::Scope: { auto scopeRule = uncheckedDowncast(WTFMove(rule)); auto previousScopeIdentifier = m_currentScopeIdentifier; @@ -312,6 +317,14 @@ void RuleSetBuilder::addStyleRule(const StyleRule& rule) addStyleRuleWithSelectorList(rule.selectorList(), rule); } +void RuleSetBuilder::addStyleRule(StyleRuleNestedDeclarations& rule) +{ + ASSERT(m_selectorListStack.size()); + auto selectorList = *m_selectorListStack.last(); + rule.wrapperAdoptSelectorList(WTFMove(selectorList)); + addStyleRuleWithSelectorList(rule.selectorList(), rule); +} + void RuleSetBuilder::disallowDynamicMediaQueryEvaluationIfNeeded() { bool isScanningForDynamicEvaluation = !m_ruleSet; diff --git a/Source/WebCore/style/RuleSetBuilder.h b/Source/WebCore/style/RuleSetBuilder.h index b0970feab058..feae72633c85 100644 --- a/Source/WebCore/style/RuleSetBuilder.h +++ b/Source/WebCore/style/RuleSetBuilder.h @@ -41,6 +41,7 @@ class RuleSetBuilder { RuleSetBuilder(const MQ::MediaQueryEvaluator&); void addStyleRule(StyleRuleWithNesting&); + void addStyleRule(StyleRuleNestedDeclarations&); void addRulesFromSheetContents(const StyleSheetContents&); void addChildRules(const Vector>&); void addChildRule(Ref);