Skip to content
Permalink
Browse files
:has(~:dir(~)) should work
https://bugs.webkit.org/show_bug.cgi?id=243093

Reviewed by Antti Koivisto.

Implement :dir pseudo class invalidation. This patch introduces the notion of effective text direction
which is stored in Node's rareDataBitfields. To signify whether this is used for text direction or not,
another boolean value usesEffectiveTextDirection has been added to rareDataBitfields.

Like https://commits.webkit.org/253764@main for :lang, this patch has an optimization to avoid using
rareDataBitfields when the text direction matches that of the document element.

* LayoutTests/TestExpectations: Removed image only failures from tests that are now passing.
* LayoutTests/fast/css/dir-pseudo-container-query-expected.html: Added.
* LayoutTests/fast/css/dir-pseudo-container-query-invalidation-expected.html: Added.
* LayoutTests/fast/css/dir-pseudo-container-query-invalidation.html: Added.
* LayoutTests/fast/css/dir-pseudo-container-query.html: Added.
* LayoutTests/platform/win/TestExpectations: Skip the newly added tests since :dir isn't enabled yet.

* Source/WebCore/css/SelectorCheckerTestFunctions.h
(WebCore::matchesDirPseudoClass): Now uses effectiveTextDirection instead of computeDirectionality.

* Source/WebCore/dom/Document.cpp:
(WebCore::Document::childrenChanged): Calls setDocumentElementTextDirection. Note we need to explicitly
check usesEffectiveTextDirection since effectiveTextDirection falls back to documentElementTextDirection.

* Source/WebCore/dom/Document.h:
(WebCore::Document::documentElementTextDirection const): Added.
(WebCore::Document::setDocumentElementTextDirection): Added.

* Source/WebCore/dom/Node.cpp:
(WebCore::Node::incrementConnectedSubframeCount):
(WebCore::Node::effectiveTextDirection const): Added.
(WebCore::Node::setEffectiveTextDirection): Added.
(WebCore::Node::setUsesEffectiveTextDirection): Added.

* Source/WebCore/dom/Node.h:
(WebCore::Node::usesEffectiveTextDirection const): Added.

* Source/WebCore/html/HTMLElement.cpp:
(WebCore::TextDirectionDirective): Added.
(WebCore::parseTextDirection): Added.
(WebCore::isValidDirValue): Now uses parseTextDirection.
(WebCore::elementAffectsDirectionality): Moved up to be used in insertedIntoAncestor.
(WebCore::HTMLElement::insertedIntoAncestor): Inherit the effective text direction from parent unless
this is an input type=telephone.
(WebCore::HTMLElement::removedFromAncestor): Added. Clear the inherited effective text direction.
(WebCore::HTMLElement::computeDirectionality const): Removed.
(WebCore::HTMLElement::dirAttributeChanged): Added the invalidation logic for each value type. Notably,
when the new value is invalid, we inherit from the parent node unless this is an input type=telephone.
(WebCore::HTMLElement::updateEffectiveDirectionality): Added. Implements the recursive invalidation
for :dir pseudo class.
(WebCore::HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged): Update the effective
text direction instead of simply invalidating the style.
(WebCore::HTMLElement::updateEffectiveDirectionalityOfDirAuto): Ditto.
(WebCore::HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged):

* Source/WebCore/html/HTMLElement.h:

* Source/WebCore/html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::subtreeHasChanged): Don't call updateEffectiveDirectionalityOfDirAuto
if this element hasn't been marked with selfOrPrecedingNodesAffectDirAuto. Otherwise, we'd set
selfOrPrecedingNodesAffectDirAuto on input element without dir=auto. This was caught by new assertions.
(WebCore::HTMLInputElement::setValue): Added a call to updateEffectiveDirectionalityOfDirAuto when
selfOrPrecedingNodesAffectDirAuto is set. This is needed now that the effective text direction is cached
as opposed to re-computed each time in computeDirectionality.

* Source/WebCore/html/HTMLTextAreaElement.cpp:
(WebCore::HTMLTextAreaElement::subtreeHasChanged): Same as HTMLInputElement.
(WebCore::HTMLTextAreaElement::setValueCommon): Ditto.

Canonical link: https://commits.webkit.org/254017@main
  • Loading branch information
rniwa committed Sep 1, 2022
1 parent ece5576 commit ded49fab22337a0f9d321bc1b13e5ddeb49c917d
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 58 deletions.
@@ -1594,9 +1594,6 @@ webkit.org/b/185859 imported/w3c/web-platform-tests/css/selectors/focus-visible-
webkit.org/b/234139 imported/w3c/web-platform-tests/css/selectors/focus-visible-024.html [ Skip ]
webkit.org/b/234139 imported/w3c/web-platform-tests/css/selectors/focus-visible-025.html [ Skip ]
webkit.org/b/217904 imported/w3c/web-platform-tests/css/selectors/is-where-visited.html [ ImageOnlyFailure ]
webkit.org/b/64861 imported/w3c/web-platform-tests/css/selectors/dir-selector-auto-direction-change-001.html [ ImageOnlyFailure ]
webkit.org/b/64861 imported/w3c/web-platform-tests/css/selectors/dir-selector-change-001.html [ ImageOnlyFailure ]
webkit.org/b/64861 imported/w3c/web-platform-tests/css/selectors/dir-selector-change-004.html [ ImageOnlyFailure ]
webkit.org/b/64861 imported/w3c/web-platform-tests/css/selectors/dir-style-01a.html [ ImageOnlyFailure ]
webkit.org/b/64861 imported/w3c/web-platform-tests/css/selectors/dir-style-01b.html [ ImageOnlyFailure ]
webkit.org/b/223497 imported/w3c/web-platform-tests/css/selectors/nesting.html [ ImageOnlyFailure ]
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 100px; height: 100px; background: green;"></div>
</body>
</html>
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div style="width: 100px; height: 100px; background: green;"></div>
</body>
</html>
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html class="reftest-wait">
<body>
<section><div id="ltr1" class="ltr"></div></section>
<section id="ltr2" dir="rtl"><div class="ltr"><span></span></div></section>
<section dir="ltr"><div class="ltr"><span></span></div></section>
<section><div id="rtl1" class="rtl"><span></span></div></section>
<section id="rtl2"><div class="rtl"><span></span></div></section>
<style>
div, section { width: 100px; height: 20px; }
section { background: red; }
.ltr:has(*:dir(ltr)) { background: green; }
.ltr:has(*:dir(rtl)) { background: red; }
.rtl:has(*:dir(rtl)) { background: green; }
.rtl:has(*:dir(ltr)) { background: red; }
</style>
<script>
requestAnimationFrame(() => {
setTimeout(() => {
ltr1.appendChild(document.createElement('span'));
ltr2.dir = 'ltr';
rtl1.dir = 'rtl';
rtl2.dir = 'rtl';
document.documentElement.className = '';
}, 0);
});
</script>
</body>
</html>
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<section><div class="ltr"><span></span></div></section>
<section><div dir="ltr" class="ltr"><span></span></div></section>
<section dir="ltr"><div class="ltr"><span></span></div></section>
<section><div dir="rtl" class="rtl"><span></span></div></section>
<section dir="rtl"><div class="rtl"><span></span></div></section>
<style>
div, section { width: 100px; height: 20px; }
section { background: red; }
.ltr:has(*:dir(ltr)) { background: green; }
.ltr:has(*:dir(rtl)) { background: red; }
.rtl:has(*:dir(rtl)) { background: green; }
.rtl:has(*:dir(ltr)) { background: red; }
</style>
</body>
</html>
@@ -2840,6 +2840,8 @@ webkit.org/b/162668 fast/text/woff2-totalsfntsize.html [ Skip ]

# :dir pseudo class isn't enabled yet.
imported/w3c/web-platform-tests/css/css-pseudo/dir-pseudo-on-bdi-element.html [ Failure ]
fast/css/dir-pseudo-container-query-invalidation.html [ Failure ]
fast/css/dir-pseudo-container-query.html [ Failure ]
fast/css/dir-pseudo-on-input-element.html [ Failure ]

################################################################################
@@ -251,14 +251,14 @@ ALWAYS_INLINE bool matchesLangPseudoClass(const Element& element, const Vector<A

ALWAYS_INLINE bool matchesDirPseudoClass(const Element& element, const AtomString& argument)
{
// FIXME: Add support for non-HTML elements.
if (!is<HTMLElement>(element))
return false;

if (!element.document().settings().dirPseudoEnabled())
return false;

// FIXME: Add support for non-HTML elements.
switch (downcast<HTMLElement>(element).computeDirectionality()) {
switch (element.effectiveTextDirection()) {
case TextDirection::LTR:
return equalIgnoringASCIICase(argument, "ltr"_s);
case TextDirection::RTL:
@@ -983,6 +983,8 @@ void Document::childrenChanged(const ChildChange& change)
return;
m_documentElement = newDocumentElement;
setDocumentElementLanguage(m_documentElement ? m_documentElement->langFromAttribute() : nullAtom());
setDocumentElementTextDirection(m_documentElement && is<HTMLElement>(m_documentElement) && m_documentElement->usesEffectiveTextDirection()
? downcast<HTMLElement>(*m_documentElement).effectiveTextDirection() : TextDirection::LTR);
// The root style used for media query matching depends on the document element.
styleScope().clearResolver();
}
@@ -478,6 +478,8 @@ class Document

const AtomString& effectiveDocumentElementLanguage() const;
void setDocumentElementLanguage(const AtomString&);
TextDirection documentElementTextDirection() const { return m_documentElementTextDirection; }
void setDocumentElementTextDirection(TextDirection textDirection) { m_documentElementTextDirection = textDirection; }

String xmlEncoding() const { return m_xmlEncoding; }
String xmlVersion() const { return m_xmlVersion; }
@@ -1940,6 +1942,7 @@ class Document

AtomString m_contentLanguage;
AtomString m_documentElementLanguage;
TextDirection m_documentElementTextDirection;

RefPtr<TextResourceDecoder> m_decoder;

@@ -2630,7 +2630,7 @@ void Node::removedLastRef()

void Node::incrementConnectedSubframeCount(unsigned amount)
{
static_assert(RareDataBitFields { Page::maxNumberOfFrames, 0, 0 }.connectedSubframeCount == Page::maxNumberOfFrames, "connectedSubframeCount must fit Page::maxNumberOfFrames");
static_assert(RareDataBitFields { Page::maxNumberOfFrames, 0, 0, 0, 0 }.connectedSubframeCount == Page::maxNumberOfFrames, "connectedSubframeCount must fit Page::maxNumberOfFrames");

ASSERT(isContainerNode());
auto bitfields = rareDataBitfields();
@@ -2670,6 +2670,27 @@ void Node::updateAncestorConnectedSubframeCountForInsertion() const
node->incrementConnectedSubframeCount(count);
}

TextDirection Node::effectiveTextDirection() const
{
if (rareDataBitfields().usesEffectiveTextDirection)
return static_cast<TextDirection>(rareDataBitfields().effectiveTextDirection);
return document().documentElementTextDirection();
}

void Node::setEffectiveTextDirection(TextDirection direction)
{
auto bitfields = rareDataBitfields();
bitfields.effectiveTextDirection = static_cast<uint16_t>(direction);
setRareDataBitfields(bitfields);
}

void Node::setUsesEffectiveTextDirection(bool value)
{
auto bitfields = rareDataBitfields();
bitfields.usesEffectiveTextDirection = value;
setRareDataBitfields(bitfields);
}

bool Node::inRenderedDocument() const
{
return isConnected() && document().hasLivingRenderTree();
@@ -289,6 +289,12 @@ class Node : public EventTarget {
bool selfOrPrecedingNodesAffectDirAuto() const { return hasNodeFlag(NodeFlag::SelfOrPrecedingNodesAffectDirAuto); }
void setSelfOrPrecedingNodesAffectDirAuto(bool flag) { setNodeFlag(NodeFlag::SelfOrPrecedingNodesAffectDirAuto, flag); }

TextDirection effectiveTextDirection() const;
void setEffectiveTextDirection(TextDirection);

bool usesEffectiveTextDirection() const { return rareDataBitfields().usesEffectiveTextDirection; }
void setUsesEffectiveTextDirection(bool);

// Returns the enclosing event parent Element (or self) that, when clicked, would trigger a navigation.
WEBCORE_EXPORT Element* enclosingLinkEventParentOrSelf();

@@ -613,6 +619,8 @@ class Node : public EventTarget {
uint16_t connectedSubframeCount : 10;
uint16_t tabIndexState : 2;
uint16_t customElementState : 2;
uint16_t usesEffectiveTextDirection : 1;
uint16_t effectiveTextDirection : 1;
};

bool hasNodeFlag(NodeFlag flag) const { return m_nodeFlags.contains(flag); }

0 comments on commit ded49fa

Please sign in to comment.