Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: f5cac74bf03a3b32a3fc08adef4768745714cc55
      
https://github.com/WebKit/WebKit/commit/f5cac74bf03a3b32a3fc08adef4768745714cc55
  Author: Ahmad Saleem <[email protected]>
  Date:   2026-08-06 (Thu, 06 Aug 2026)

  Changed paths:
    M 
LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-evaluation-expected.txt
    M 
LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-evaluation.html
    M Source/WebCore/style/ElementRuleCollector.cpp

  Log Message:
  -----------
  Nested @scope with :has(:scope ...) in <scope-start> picks scoping roots 
outside the outer scope
https://bugs.webkit.org/show_bug.cgi?id=281976
rdar://138495467

Reviewed by Antti Koivisto.

Testcase:

      @scope (.outer:has(.bar)) {
        @scope (:has(:scope .baz)) {
          .foo { color: green; }
        }
      }

.foo is green, but ":has(:scope .baz)" requires its anchor to be an ancestor of
:scope, i.e. of .outer, and such an element is never in the outer scope, so the
inner @scope should produce no scoping root at all.

Per CSS Cascade 6, the prelude of a nested @scope is "scoped by the selectors of
the outer one" [1], and a scoped selector containing an explicit :scope "is
interpreted as a non-relative selector (but the subject must still be in scope
to match)" [2], in scope meaning "an inclusive descendant of the scoping root"
that is "not an inclusive descendant of a scoping limit" [3].

We only implement the relative half: a nested prelude normally gets an implicit
":scope " prepended, which incidentally keeps the candidate root inside the
outer scope. That prepend is skipped when the prelude already contains :scope or
&, including inside :has(), since CSSSelector::hasExplicitPseudoClassScope()
visits functional pseudo-classes. Skipping it is correct, but we then never
check the subject is in scope, so findScopingRoots() accepts any matching
ancestor -- here <body>, which .foo descends from.

Reject candidate scoping roots that are not inclusive descendants of the
enclosing scoping root. Both are on the ancestor chain of the element being
matched, so comparing distances is sufficient. Outer scoping limits need no
extra check: a limit between the outer root and the inner candidate also sits on
the path from the element, so the outer @scope has already rejected that root.

[1] https://www.w3.org/TR/css-cascade-6/#scope-scope
[2] https://www.w3.org/TR/css-cascade-6/#scoped-rules
[3] https://www.w3.org/TR/css-cascade-6/#in-scope

* 
LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-evaluation-expected.txt:
* 
LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/scope-evaluation.html:
* Source/WebCore/style/ElementRuleCollector.cpp:
(WebCore::Style::ElementRuleCollector::scopeRulesMatch):

Canonical link: https://commits.webkit.org/318711@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to