Skip to content

Commit 197ddc8

Browse files
committed
Limit the scope of invalidation traversal for :has(> .changed) .subject
https://bugs.webkit.org/show_bug.cgi?id=297893 rdar://159173631 Reviewed by Simon Fraser. It is sufficient to traverse the subtree of the parent to invalidate in this case, rather than the entire DOM. This helps with GitHub performance as it uses selectors of this type. * Source/WebCore/style/ChildChangeInvalidation.cpp: (WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement): * Source/WebCore/style/ClassChangeInvalidation.cpp: (WebCore::Style::ClassChangeInvalidation::computeInvalidation): * Source/WebCore/style/RuleFeature.cpp: (WebCore::Style::isSiblingOrSubject): (WebCore::Style::isScopeBreaking): (WebCore::Style::computeHasPseudoClassMatchElement): (WebCore::Style::computeSubSelectorMatchElement): Add two new MatchElement types for these cases: `:has(> .changed) .subject `:has(> .changed) > .subject * Source/WebCore/style/RuleFeature.h: * Source/WebCore/style/StyleInvalidator.cpp: (WebCore::Style::Invalidator::invalidateStyleWithMatchElement): Traverse only the required subtrees for the new types. Also document each invalidation traversal case. Canonical link: https://commits.webkit.org/299162@main
1 parent ac56c26 commit 197ddc8

File tree

5 files changed

+57
-2
lines changed

5 files changed

+57
-2
lines changed

Source/WebCore/style/ChildChangeInvalidation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ void ChildChangeInvalidation::invalidateForChangedElement(Element& changedElemen
4949
auto canAffectElementsWithStyle = [&](MatchElement matchElement) {
5050
switch (matchElement) {
5151
case MatchElement::HasSibling:
52+
case MatchElement::HasAnySibling:
5253
case MatchElement::HasChild:
54+
case MatchElement::HasChildAncestor:
55+
case MatchElement::HasChildParent:
5356
return isChild;
5457
case MatchElement::HasDescendant:
5558
case MatchElement::HasSiblingDescendant:

Source/WebCore/style/ClassChangeInvalidation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ void ClassChangeInvalidation::computeInvalidation(const SpaceSplitString& oldCla
129129
case MatchElement::ParentSibling:
130130
case MatchElement::AncestorSibling:
131131
case MatchElement::HasChild:
132+
case MatchElement::HasChildParent:
133+
case MatchElement::HasChildAncestor:
132134
case MatchElement::HasDescendant:
133135
case MatchElement::HasSibling:
134136
case MatchElement::HasSiblingDescendant:

Source/WebCore/style/RuleFeature.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ static bool isSiblingOrSubject(MatchElement matchElement)
5858
case MatchElement::HasChild:
5959
case MatchElement::HasDescendant:
6060
case MatchElement::HasSiblingDescendant:
61+
case MatchElement::HasChildParent:
62+
case MatchElement::HasChildAncestor:
6163
case MatchElement::HasNonSubject:
6264
case MatchElement::HasScopeBreaking:
6365
return false;
@@ -104,6 +106,8 @@ static bool isScopeBreaking(MatchElement matchElement)
104106
case MatchElement::HasChild:
105107
case MatchElement::HasDescendant:
106108
case MatchElement::HasSiblingDescendant:
109+
case MatchElement::HasChildParent:
110+
case MatchElement::HasChildAncestor:
107111
case MatchElement::HasNonSubject:
108112
return false;
109113
}
@@ -230,6 +234,8 @@ MatchElement computeHasPseudoClassMatchElement(const CSSSelector& hasSelector)
230234
case MatchElement::HasSibling:
231235
case MatchElement::HasSiblingDescendant:
232236
case MatchElement::HasAnySibling:
237+
case MatchElement::HasChildParent:
238+
case MatchElement::HasChildAncestor:
233239
case MatchElement::HasNonSubject:
234240
case MatchElement::HasScopeBreaking:
235241
case MatchElement::Host:
@@ -258,9 +264,21 @@ static MatchElement computeSubSelectorMatchElement(MatchElement matchElement, co
258264
return MatchElement::Host;
259265

260266
if (type == CSSSelector::PseudoClass::Has) {
267+
auto hasSelectorMatchElement = computeHasPseudoClassMatchElement(childSelector);
268+
269+
if (hasSelectorMatchElement == MatchElement::HasChild) {
270+
// :has(> .changed) > .subject
271+
if (matchElement == MatchElement::Parent)
272+
return MatchElement::HasChildParent;
273+
// :has(> .changed) .subject
274+
if (matchElement == MatchElement::Ancestor)
275+
return MatchElement::HasChildAncestor;
276+
}
277+
261278
if (matchElement != MatchElement::Subject)
262279
return MatchElement::HasNonSubject;
263-
return computeHasPseudoClassMatchElement(childSelector);
280+
281+
return hasSelectorMatchElement;
264282
}
265283

266284
}

Source/WebCore/style/RuleFeature.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ enum class MatchElement : uint8_t {
5555
HasSibling,
5656
HasSiblingDescendant,
5757
HasAnySibling,
58-
HasNonSubject, // FIXME: This is a catch-all for cases where :has() is in a non-subject position.
58+
HasChildParent,
59+
HasChildAncestor,
60+
HasNonSubject, // FIXME: This is a catch-all for the rest of cases where :has() is in a non-subject position.
5961
HasScopeBreaking, // FIXME: This is a catch-all for cases where :has() contains a scope breaking sub-selector like, like :has(:is(.x .y)).
6062
Host,
6163
HostChild

Source/WebCore/style/StyleInvalidator.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,42 +273,50 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
273273
{
274274
switch (matchElement) {
275275
case MatchElement::Subject: {
276+
// .changed
276277
invalidateIfNeeded(element, nullptr);
277278
break;
278279
}
279280
case MatchElement::Parent: {
281+
// .changed > .subject
280282
auto children = childrenOfType<Element>(element);
281283
for (auto& child : children)
282284
invalidateIfNeeded(child, nullptr);
283285
break;
284286
}
285287
case MatchElement::Ancestor: {
288+
// .changed .subject
286289
SelectorMatchingState selectorMatchingState;
287290
invalidateStyleForDescendants(element, &selectorMatchingState);
288291
break;
289292
}
290293
case MatchElement::DirectSibling:
294+
// .changed + .subject
291295
if (auto* sibling = element.nextElementSibling())
292296
invalidateIfNeeded(*sibling, nullptr);
293297
break;
294298
case MatchElement::IndirectSibling:
299+
// .changed ~ .subject
295300
for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling())
296301
invalidateIfNeeded(*sibling, nullptr);
297302
break;
298303
case MatchElement::AnySibling:
304+
// :nth-last-child(even of .changed)
299305
if (CheckedPtr parentNode = element.parentNode()) {
300306
for (auto& parentChild : childrenOfType<Element>(*element.parentNode()))
301307
invalidateIfNeeded(parentChild, nullptr);
302308
}
303309
break;
304310
case MatchElement::ParentSibling:
311+
// .changed ~ .a > .subject
305312
for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
306313
auto siblingChildren = childrenOfType<Element>(*sibling);
307314
for (auto& siblingChild : siblingChildren)
308315
invalidateIfNeeded(siblingChild, nullptr);
309316
}
310317
break;
311318
case MatchElement::AncestorSibling: {
319+
// .changed ~ .a .subject
312320
SelectorMatchingState selectorMatchingState;
313321
for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
314322
selectorMatchingState.selectorFilter.popParentsUntil(element.parentElement());
@@ -317,13 +325,15 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
317325
break;
318326
}
319327
case MatchElement::ParentAnySibling:
328+
// :nth-last-child(even of .changed) > .subject
320329
for (auto& sibling : childrenOfType<Element>(*element.parentNode())) {
321330
auto siblingChildren = childrenOfType<Element>(sibling);
322331
for (auto& siblingChild : siblingChildren)
323332
invalidateIfNeeded(siblingChild, nullptr);
324333
}
325334
break;
326335
case MatchElement::AncestorAnySibling: {
336+
// :nth-last-child(even of .changed) .subject
327337
SelectorMatchingState selectorMatchingState;
328338
for (auto& sibling : childrenOfType<Element>(*element.parentNode())) {
329339
selectorMatchingState.selectorFilter.popParentsUntil(element.parentElement());
@@ -332,11 +342,13 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
332342
break;
333343
}
334344
case MatchElement::HasChild: {
345+
// :has(> .changed)
335346
if (auto* parent = element.parentElement())
336347
invalidateIfNeeded(*parent, nullptr);
337348
break;
338349
}
339350
case MatchElement::HasDescendant: {
351+
// :has(.changed)
340352
Vector<Element*, 16> ancestors;
341353
for (auto* parent = element.parentElement(); parent; parent = parent->parentElement())
342354
ancestors.append(parent);
@@ -350,6 +362,7 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
350362
break;
351363
}
352364
case MatchElement::HasSibling:
365+
// :has(~ .changed)
353366
if (auto* sibling = element.previousElementSibling()) {
354367
SelectorMatchingState selectorMatchingState;
355368
if (RefPtr parent = element.parentElement())
@@ -359,7 +372,11 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
359372
invalidateIfNeeded(*sibling, &selectorMatchingState);
360373
}
361374
break;
375+
376+
case MatchElement::HasChildParent:
377+
// :has(> .changed) > .subject
362378
case MatchElement::HasAnySibling: {
379+
// :has(~ :is(.changed ~ .x))
363380
SelectorMatchingState selectorMatchingState;
364381
if (auto* parent = element.parentElement())
365382
selectorMatchingState.selectorFilter.pushParentInitializingIfNeeded(*parent);
@@ -368,6 +385,7 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
368385
break;
369386
}
370387
case MatchElement::HasSiblingDescendant: {
388+
// :has(~ .a .changed)
371389
Vector<Element*, 16> elementAndAncestors;
372390
elementAndAncestors.append(&element);
373391
for (auto* parent = element.parentElement(); parent; parent = parent->parentElement())
@@ -383,16 +401,28 @@ void Invalidator::invalidateStyleWithMatchElement(Element& element, MatchElement
383401
}
384402
break;
385403
}
404+
case MatchElement::HasChildAncestor: {
405+
// :has(> .changed) .subject
406+
if (CheckedPtr parent = element.parentElement()) {
407+
SelectorMatchingState selectorMatchingState;
408+
invalidateStyleForDescendants(*parent, &selectorMatchingState);
409+
}
410+
break;
411+
}
386412
case MatchElement::HasNonSubject:
413+
// :has(.changed) .subject
387414
case MatchElement::HasScopeBreaking: {
415+
// :has(:is(.changed .a))
388416
SelectorMatchingState selectorMatchingState;
389417
invalidateStyleForDescendants(*element.document().documentElement(), &selectorMatchingState);
390418
break;
391419
}
392420
case MatchElement::Host:
421+
// :host(.changed) .subject
393422
invalidateInShadowTreeIfNeeded(element);
394423
break;
395424
case MatchElement::HostChild:
425+
// ::slotted(.changed)
396426
if (auto* host = element.shadowHost()) {
397427
for (auto& hostChild : childrenOfType<Element>(*host))
398428
invalidateIfNeeded(hostChild, nullptr);

0 commit comments

Comments
 (0)