Skip to content

[css-scoping] Implement :has-slotted pseudo-class #34599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

keithamus
Copy link
Member

@keithamus keithamus commented Oct 2, 2024

91135a2

[css-scoping] Implement `:has-slotted` pseudo-class
https://bugs.webkit.org/show_bug.cgi?id=280608

Reviewed by NOBODY (OOPS!).

This implements the :has-slotted pseudo selector as specified in CSSWG
https://drafts.csswg.org/css-scoping/#the-has-slotted-pseudo.

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-changing-001-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-changing-002-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-functional-changing-001.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-functional-changing-003.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-manual-assignment-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-query-selector-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-slotted.tentative-expected.txt:
* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebCore/css/CSSPseudoSelectors.json:
* Source/WebCore/css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):
* Source/WebCore/css/SelectorCheckerTestFunctions.h:
(WebCore::matchesHasSlottedPseudoClass):
* Source/WebCore/css/parser/CSSParserContext.cpp:
(WebCore::add):
* Source/WebCore/css/parser/CSSParserContext.h:
* Source/WebCore/css/parser/CSSSelectorParserContext.cpp:
(WebCore::CSSSelectorParserContext::CSSSelectorParserContext):
(WebCore::add):
* Source/WebCore/css/parser/CSSSelectorParserContext.h:
* Source/WebCore/cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::JSC_DEFINE_NOEXCEPT_JIT_OPERATION):
(WebCore::SelectorCompiler::addPseudoClassType):
* Source/WebCore/dom/Node.cpp:
(WebCore::Node::setManuallyAssignedSlot):
* Source/WebCore/dom/SlotAssignment.cpp:
(WebCore::NamedSlotAssignment::didChangeSlot):
(WebCore::NamedSlotAssignment::willRemoveAssignedNode):
(WebCore::NamedSlotAssignment::assignToSlot):
* Source/WebCore/html/HTMLSlotElement.cpp:
(WebCore::containsAssignedNodeIgnoringSlots):
(WebCore::HTMLSlotElement::hasFlattenedSlottedContent const):
* Source/WebCore/html/HTMLSlotElement.h:

91135a2

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win
✅ 🧪 bindings ✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ⏳ 🧪 win-tests
✅ 🧪 webkitperl ❌ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe
✅ 🧪 ios-wk2-wpt ✅ 🧪 mac-wk1 ✅ 🛠 wpe-cairo
✅ 🛠 🧪 jsc ✅ 🧪 api-ios ✅ 🧪 mac-wk2 ✅ 🛠 gtk
✅ 🛠 🧪 jsc-arm64 ✅ 🛠 vision ❌ 🧪 mac-AS-debug-wk2 ✅ 🧪 gtk-wk2
✅ 🛠 vision-sim ✅ 🧪 mac-wk2-stress ✅ 🧪 api-gtk
✅ 🧪 vision-wk2 ✅ 🧪 mac-intel-wk2 ✅ 🛠 playstation
✅ 🛠 tv ✅ 🛠 mac-safer-cpp ✅ 🛠 jsc-armv7
✅ 🛠 tv-sim ❌ 🧪 jsc-armv7-tests
✅ 🛠 watch
✅ 🛠 watch-sim

@keithamus keithamus requested review from rniwa and cdumez as code owners October 2, 2024 22:41
@keithamus keithamus self-assigned this Oct 2, 2024
@keithamus keithamus added the CSS Cascading Style Sheets implementation label Oct 2, 2024
@keithamus keithamus marked this pull request as draft October 2, 2024 22:41
yisibl added a commit to cssdream/css3test that referenced this pull request Oct 10, 2024
@keithamus keithamus force-pushed the implement-has-slotted branch from d4507cc to 69f2c29 Compare January 9, 2025 09:51
@keithamus keithamus marked this pull request as ready for review January 9, 2025 09:53
@keithamus keithamus force-pushed the implement-has-slotted branch from 69f2c29 to 3a5e5b1 Compare January 9, 2025 09:53
@keithamus keithamus force-pushed the implement-has-slotted branch from 3a5e5b1 to 8918c55 Compare January 9, 2025 09:54
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Jan 9, 2025
@keithamus keithamus removed the merging-blocked Applied to prevent a change from being merged label Jan 9, 2025
@keithamus keithamus force-pushed the implement-has-slotted branch from 8918c55 to f93071a Compare January 9, 2025 12:33
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Jan 9, 2025
@keithamus keithamus removed the merging-blocked Applied to prevent a change from being merged label Jan 10, 2025
@keithamus keithamus force-pushed the implement-has-slotted branch from f93071a to 83cf06c Compare January 10, 2025 10:08
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Jan 10, 2025
@keithamus keithamus removed the merging-blocked Applied to prevent a change from being merged label Jan 18, 2025
@keithamus keithamus force-pushed the implement-has-slotted branch from 211de82 to 2ce8230 Compare February 23, 2025 21:25
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Feb 23, 2025
@keithamus keithamus force-pushed the implement-has-slotted branch from 2ce8230 to 201a2cb Compare February 25, 2025 15:56
@keithamus keithamus marked this pull request as ready for review February 25, 2025 21:40
@keithamus keithamus removed the merging-blocked Applied to prevent a change from being merged label Mar 7, 2025
@keithamus keithamus force-pushed the implement-has-slotted branch from 201a2cb to 642b383 Compare March 7, 2025 17:35
Copy link
Member

@darinadler darinadler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is OK as is.

Comment on lines 392 to 406
std::optional<Style::PseudoClassChangeInvalidation> styleInvalidation;
if (auto* slotElement = slot->element.get())
styleInvalidation.emplace(*slotElement, CSSSelector::PseudoClass::HasSlotted, slotElement->hasFlattenedSlottedContent());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to share more of this code? This sequence of three lines of code is pretty repetitive, shows up 5 times almost identically, and I think a helper function could improve clarity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style::PseudoClassChangeInvalidation is scope-dependent. It would be possible to define another scope that wraps this scope, but not worth it IMO

Comment on lines 150 to 154
Vector<Ref<Node>> nodes;
flattenAssignedNodes(nodes, *this);
return !nodes.isEmpty();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems inefficient to build the list of nodes just to check if it’s empty. Can we do it more efficiently?

Copy link
Member

@rniwa rniwa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more than 1 bits for context.mode.

@nt1m nt1m changed the title Implement :has-slotted pseudo selector [css-scoping] Implement :has-slotted pseudo-class Jun 30, 2025
@nt1m nt1m force-pushed the implement-has-slotted branch from 642b383 to 2621c0b Compare June 30, 2025 20:58
@nt1m nt1m self-assigned this Jun 30, 2025
@nt1m nt1m requested a review from rniwa June 30, 2025 21:00
@nt1m nt1m force-pushed the implement-has-slotted branch from 2621c0b to 62fd2c2 Compare June 30, 2025 21:09
https://bugs.webkit.org/show_bug.cgi?id=280608

Reviewed by NOBODY (OOPS!).

This implements the :has-slotted pseudo selector as specified in CSSWG
https://drafts.csswg.org/css-scoping/#the-has-slotted-pseudo.

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-changing-001-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-changing-002-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-functional-changing-001.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-functional-changing-003.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-manual-assignment-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-scoping/has-slotted-query-selector-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/parsing/parse-has-slotted.tentative-expected.txt:
* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebCore/css/CSSPseudoSelectors.json:
* Source/WebCore/css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):
* Source/WebCore/css/SelectorCheckerTestFunctions.h:
(WebCore::matchesHasSlottedPseudoClass):
* Source/WebCore/css/parser/CSSParserContext.cpp:
(WebCore::add):
* Source/WebCore/css/parser/CSSParserContext.h:
* Source/WebCore/css/parser/CSSSelectorParserContext.cpp:
(WebCore::CSSSelectorParserContext::CSSSelectorParserContext):
(WebCore::add):
* Source/WebCore/css/parser/CSSSelectorParserContext.h:
* Source/WebCore/cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::JSC_DEFINE_NOEXCEPT_JIT_OPERATION):
(WebCore::SelectorCompiler::addPseudoClassType):
* Source/WebCore/dom/Node.cpp:
(WebCore::Node::setManuallyAssignedSlot):
* Source/WebCore/dom/SlotAssignment.cpp:
(WebCore::NamedSlotAssignment::didChangeSlot):
(WebCore::NamedSlotAssignment::willRemoveAssignedNode):
(WebCore::NamedSlotAssignment::assignToSlot):
* Source/WebCore/html/HTMLSlotElement.cpp:
(WebCore::containsAssignedNodeIgnoringSlots):
(WebCore::HTMLSlotElement::hasFlattenedSlottedContent const):
* Source/WebCore/html/HTMLSlotElement.h:
@nt1m nt1m force-pushed the implement-has-slotted branch from 62fd2c2 to 91135a2 Compare June 30, 2025 23:42
@@ -112,6 +112,7 @@ CSSParserContext::CSSParserContext(const Document& document, const URL& sheetBas
, cssAxisRelativePositionKeywordsEnabled { document.settings().cssAxisRelativePositionKeywordsEnabled() }
, cssDynamicRangeLimitMixEnabled { document.settings().cssDynamicRangeLimitMixEnabled() }
, cssConstrainedDynamicRangeLimitEnabled { document.settings().cssConstrainedDynamicRangeLimitEnabled() }
, cssHasSlottedEnabled { document.settings().cssHasSlottedEnabled() }
, webkitMediaTextTrackDisplayQuirkEnabled { document.quirks().needsWebKitMediaTextTrackDisplayQuirk() }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this value not being included in the hash computation?

@@ -1375,8 +1376,17 @@ void Node::setManuallyAssignedSlot(HTMLSlotElement* slotElement)
else if (RefPtr text = dynamicDowncast<Text>(*this))
RenderTreeUpdater::tearDownRenderer(*text);

auto* oldSlotElement = manuallyAssignedSlot();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use RefPtr instead.

auto assignedNodes = std::exchange(slot->assignedNodes, { });
m_slotAssignmentsIsValid = false;

if (auto* slotElement = slot->element.get())
Copy link
Member

@rniwa rniwa Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use findFirstSlotElement and RefPtr instead.

slot->assignedNodes.removeFirstMatching([&node](const auto& item) {
return item.get() == &node;
});

if (auto* slotElement = slot->element.get())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

defaultSlotEntry->value->assignedNodes.append(child);
if (auto* slotElement = defaultSlotEntry->value->element.get())
styleInvalidation.emplace(*slotElement, CSSSelector::PseudoClass::HasSlotted, slotElement->hasFlattenedSlottedContent());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too late. NamedSlotAssignment::assignSlots is only called lazily.
We need to detect when :has-slotted is used and eagerly resolve the assignment.
See how shadowRoot.shouldFireSlotchangeEvent() && hasAssignedNodes(shadowRoot, slot) is checked to enqueue slotchange event.

auto* assignedNodes = slot.assignedNodes();
if (!assignedNodes) {
for (RefPtr<Node> child = slot.firstChild(); child; child = child->nextSibling()) {
if (auto* slot = dynamicDowncast<HTMLSlotElement>(*child)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use RefPtr.

ASSERT_NOT_REACHED();
continue;
}
if (auto* slot = dynamicDowncast<HTMLSlotElement>(*weakNode)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

@@ -56,6 +56,9 @@ class HTMLSlotElement final : public HTMLElement {
bool isInInsertedIntoAncestor() const { return m_isInInsertedIntoAncestor; }

void updateAccessibilityOnSlotChange() const;

bool hasFlattenedSlottedContent() const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add this code right beneath assignedElements instead.

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Jul 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CSS Cascading Style Sheets implementation merging-blocked Applied to prevent a change from being merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants