Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (287444 => 287445)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-26 18:45:32 UTC (rev 287445)
@@ -1,3 +1,12 @@
+2021-12-26 Antti Koivisto <an...@apple.com>
+
+ [:has() pseudo-class] Support :disabled and :enabled pseudo-class invalidation
+ https://bugs.webkit.org/show_bug.cgi?id=234636
+
+ Reviewed by Simon Fraser.
+
+ * web-platform-tests/css/selectors/invalidation/has-pseudo-class.html:
+
2021-12-24 Tim Nguyen <n...@apple.com>
Internally unprefix -webkit-font-kerning CSS property
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class-expected.txt (287444 => 287445)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class-expected.txt 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class-expected.txt 2021-12-26 18:45:32 UTC (rev 287445)
@@ -1,8 +1,32 @@
-PASS Initial color
-PASS Set checked on checkbox
-PASS Unset checked on checkbox
-PASS Set selected on option
-PASS Unset selected on option
+PASS Before set checked on checkbox, testing subject
+PASS Set checked on checkbox, testing subject
+PASS Unset checked on checkbox, testing subject
+PASS Set select on option
+PASS Reset select
+PASS Before set disabled on checkbox, testing subject
+PASS Set disabled on checkbox, testing subject
+PASS Unset disabled on checkbox, testing subject
+PASS Before set disabled on checkbox, testing subject3
+PASS Set disabled on checkbox, testing subject3
+PASS Unset disabled on checkbox, testing subject3
+PASS Before set disabled on option, testing subject
+PASS Set disabled on option, testing subject
+PASS Unset disabled on option, testing subject
+PASS Before set disabled on option, testing subject3
+PASS Set disabled on option, testing subject3
+PASS Unset disabled on option, testing subject3
+PASS Before set disabled on optgroup, testing subject
+PASS Set disabled on optgroup, testing subject
+PASS Unset disabled on optgroup, testing subject
+PASS Before set disabled on optgroup, testing subject2
+PASS Set disabled on optgroup, testing subject2
+PASS Unset disabled on optgroup, testing subject2
+PASS Before set disabled on optgroup, testing subject3
+PASS Set disabled on optgroup, testing subject3
+PASS Unset disabled on optgroup, testing subject3
+PASS Before set disabled on optgroup, testing subject4
+PASS Set disabled on optgroup, testing subject4
+PASS Unset disabled on optgroup, testing subject4
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class.html (287444 => 287445)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class.html 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-pseudo-class.html 2021-12-26 18:45:32 UTC (rev 287445)
@@ -6,15 +6,24 @@
<script src=""
<link rel="help" href=""
<style>
-div, main { color: grey }
+main:has(input) div { color: grey }
main:has(#checkbox:checked) > #subject { color: red }
-main:has(#option:checked) > #subject { color: green }
+main:has(#option:checked) > #subject { color: red }
+main:has(#checkbox:disabled) > #subject { color: green }
+main:has(#option:disabled) > :is(#subject, #subject2) { color: green }
+main:has(#optgroup:disabled) > #subject { color: blue }
+main:not(:has(#checkbox:enabled)) > #subject3 { color: green }
+main:not(:has(#option:enabled)) :is(#subject3, #subject4) { color: green }
+main:not(:has(#optgroup:enabled)) > #subject3 { color: blue }
</style>
<main id=main>
<input type=checkbox id=checkbox></input>
- <select><option>a</option><option id=option>b</option></select>
+ <select id=select><optgroup id=optgroup><option>a</option><option id=option>b</option></optgroup></select>
<div id=subject></div>
+ <div id=subject2></div>
+ <div id=subject3></div>
+ <div id=subject4></div>
</main>
<script>
@@ -26,22 +35,43 @@
const purple = 'rgb(128, 0, 128)';
const pink = 'rgb(255, 192, 203)';
-function testColor(test_name, color) {
+function testColor(test_name, subject_element, color) {
test(function() {
- assert_equals(getComputedStyle(subject).color, color);
+ assert_equals(getComputedStyle(subject_element).color, color);
}, test_name);
}
-function testPseudoClassChange(element, property, expectedColor)
+function testPseudoClassChange(element, property, subject_element, expectedColor)
{
+ testColor(`Before set ${property} on ${element.id}, testing ${subject_element.id}`, subject_element, grey);
+
element[property] = true;
- testColor(`Set ${property} on ${element.id}`, expectedColor);
+ testColor(`Set ${property} on ${element.id}, testing ${subject_element.id}`, subject_element, expectedColor);
+
element[property] = false;
- testColor(`Unset ${property} on ${element.id}`, grey);
+ testColor(`Unset ${property} on ${element.id}, testing ${subject_element.id}`, subject_element, grey);
}
-testColor('Initial color', grey);
+function testSelectedChange(option, subject_element, expectedColor)
+{
+ const oldOption = select.selectedOptions[0];
+ option.selected = true;
+ testColor(`Set select on ${option.id}`, subject_element, expectedColor);
+ oldOption.selected = true;
+ testColor(`Reset select`, subject, grey);
+}
-testPseudoClassChange(checkbox, "checked", red);
-testPseudoClassChange(option, "selected", green);
+testPseudoClassChange(checkbox, "checked", subject, red);
+testSelectedChange(option, subject, red);
+
+testPseudoClassChange(checkbox, "disabled", subject, green);
+testPseudoClassChange(checkbox, "disabled", subject3, green);
+testPseudoClassChange(option, "disabled", subject, green);
+testPseudoClassChange(option, "disabled", subject3, green);
+
+testPseudoClassChange(optgroup, "disabled", subject, blue);
+testPseudoClassChange(optgroup, "disabled", subject2, green);
+testPseudoClassChange(optgroup, "disabled", subject3, blue);
+testPseudoClassChange(optgroup, "disabled", subject4, green);
+
</script>
Modified: trunk/Source/WebCore/ChangeLog (287444 => 287445)
--- trunk/Source/WebCore/ChangeLog 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/Source/WebCore/ChangeLog 2021-12-26 18:45:32 UTC (rev 287445)
@@ -1,3 +1,29 @@
+2021-12-26 Antti Koivisto <an...@apple.com>
+
+ [:has() pseudo-class] Support :disabled and :enabled pseudo-class invalidation
+ https://bugs.webkit.org/show_bug.cgi?id=234636
+
+ Reviewed by Simon Fraser.
+
+ Use Style::PseudoClassChangeInvalidation to support invalidation with :has(:disabled).
+
+ * html/HTMLFormControlElement.cpp:
+ (WebCore::HTMLFormControlElement::setAncestorDisabled):
+ (WebCore::HTMLFormControlElement::parseAttribute):
+ (WebCore::HTMLFormControlElement::disabledStateChanged):
+ * html/HTMLOptGroupElement.cpp:
+ (WebCore::HTMLOptGroupElement::isDisabledFormControl const):
+
+ Use a member bit instead of checking the attribute directly. This allows invalidation to be scoped over the state change.
+
+ (WebCore::HTMLOptGroupElement::parseAttribute):
+
+ Optgroup can flip the disabled status of the associated option elements too so handle that specifically.
+
+ * html/HTMLOptGroupElement.h:
+ * html/HTMLOptionElement.cpp:
+ (WebCore::HTMLOptionElement::parseAttribute):
+
2021-12-26 Alan Bujtas <za...@apple.com>
makeTextLogicalOrderCacheIfNeeded is only interested in whether the content needs visual reordering
Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (287444 => 287445)
--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2021-12-26 18:45:32 UTC (rev 287445)
@@ -40,6 +40,7 @@
#include "HTMLLegendElement.h"
#include "HTMLParserIdioms.h"
#include "HTMLTextAreaElement.h"
+#include "PseudoClassChangeInvalidation.h"
#include "Quirks.h"
#include "RenderBox.h"
#include "RenderTheme.h"
@@ -143,10 +144,14 @@
void HTMLFormControlElement::setAncestorDisabled(bool isDisabled)
{
ASSERT(computeIsDisabledByFieldsetAncestor() == isDisabled);
- bool oldValue = m_disabledByAncestorFieldset;
+ if (m_disabledByAncestorFieldset == isDisabled)
+ return;
+
+ Style::PseudoClassChangeInvalidation disabledInvalidation(*this, CSSSelector::PseudoClassDisabled);
+ Style::PseudoClassChangeInvalidation enabledInvalidation(*this, CSSSelector::PseudoClassEnabled);
+
m_disabledByAncestorFieldset = isDisabled;
- if (oldValue != m_disabledByAncestorFieldset)
- disabledStateChanged();
+ disabledStateChanged();
}
void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const AtomString& value)
@@ -155,10 +160,13 @@
formAttributeChanged();
else if (name == disabledAttr) {
if (canBeActuallyDisabled()) {
- bool oldDisabled = m_disabled;
- m_disabled = !value.isNull();
- if (oldDisabled != m_disabled)
+ bool newDisabled = !value.isNull();
+ if (m_disabled != newDisabled) {
+ Style::PseudoClassChangeInvalidation disabledInvalidation(*this, CSSSelector::PseudoClassDisabled);
+ Style::PseudoClassChangeInvalidation enabledInvalidation(*this, CSSSelector::PseudoClassEnabled);
+ m_disabled = newDisabled;
disabledAttributeChanged();
+ }
}
} else if (name == readonlyAttr) {
bool wasReadOnly = m_isReadOnly;
@@ -182,7 +190,6 @@
void HTMLFormControlElement::disabledStateChanged()
{
updateWillValidateAndValidity();
- invalidateStyleForSubtree();
if (renderer() && renderer()->style().hasEffectiveAppearance())
renderer()->theme().stateChanged(*renderer(), ControlStates::States::Enabled);
}
Modified: trunk/Source/WebCore/html/HTMLOptGroupElement.cpp (287444 => 287445)
--- trunk/Source/WebCore/html/HTMLOptGroupElement.cpp 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/Source/WebCore/html/HTMLOptGroupElement.cpp 2021-12-26 18:45:32 UTC (rev 287445)
@@ -27,8 +27,11 @@
#include "Document.h"
#include "ElementAncestorIterator.h"
+#include "ElementIterator.h"
#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
+#include "PseudoClassChangeInvalidation.h"
#include "RenderMenuList.h"
#include "NodeRenderStyle.h"
#include "StyleResolver.h"
@@ -54,7 +57,7 @@
bool HTMLOptGroupElement::isDisabledFormControl() const
{
- return hasAttributeWithoutSynchronization(disabledAttr);
+ return m_isDisabled;
}
bool HTMLOptGroupElement::isFocusable() const
@@ -83,8 +86,21 @@
HTMLElement::parseAttribute(name, value);
recalcSelectOptions();
- if (name == disabledAttr)
- invalidateStyleForSubtree();
+ if (name == disabledAttr) {
+ bool newDisabled = !value.isNull();
+ if (m_isDisabled != newDisabled) {
+ Style::PseudoClassChangeInvalidation disabledInvalidation(*this, CSSSelector::PseudoClassDisabled);
+ Style::PseudoClassChangeInvalidation enabledInvalidation(*this, CSSSelector::PseudoClassEnabled);
+
+ Vector<Style::PseudoClassChangeInvalidation> optionInvalidation;
+ for (auto& descendant : descendantsOfType<HTMLOptionElement>(*this)) {
+ optionInvalidation.append({ descendant, CSSSelector::PseudoClassDisabled });
+ optionInvalidation.append({ descendant, CSSSelector::PseudoClassEnabled });
+ }
+
+ m_isDisabled = newDisabled;
+ }
+ }
}
void HTMLOptGroupElement::recalcSelectOptions()
Modified: trunk/Source/WebCore/html/HTMLOptGroupElement.h (287444 => 287445)
--- trunk/Source/WebCore/html/HTMLOptGroupElement.h 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/Source/WebCore/html/HTMLOptGroupElement.h 2021-12-26 18:45:32 UTC (rev 287445)
@@ -52,6 +52,8 @@
bool accessKeyAction(bool sendMouseEvents) final;
void recalcSelectOptions();
+
+ bool m_isDisabled { false };
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/HTMLOptionElement.cpp (287444 => 287445)
--- trunk/Source/WebCore/html/HTMLOptionElement.cpp 2021-12-26 17:21:44 UTC (rev 287444)
+++ trunk/Source/WebCore/html/HTMLOptionElement.cpp 2021-12-26 18:45:32 UTC (rev 287445)
@@ -176,10 +176,11 @@
} else
#endif
if (name == disabledAttr) {
- bool oldDisabled = m_disabled;
- m_disabled = !value.isNull();
- if (oldDisabled != m_disabled) {
- invalidateStyleForSubtree();
+ bool newDisabled = !value.isNull();
+ if (m_disabled != newDisabled) {
+ Style::PseudoClassChangeInvalidation disabledInvalidation(*this, CSSSelector::PseudoClassDisabled);
+ Style::PseudoClassChangeInvalidation enabledInvalidation(*this, CSSSelector::PseudoClassEnabled);
+ m_disabled = newDisabled;
if (renderer() && renderer()->style().hasEffectiveAppearance())
renderer()->theme().stateChanged(*renderer(), ControlStates::States::Enabled);
}