Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has-expected.txt (292465 => 292466)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has-expected.txt 2022-04-06 15:02:01 UTC (rev 292465)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has-expected.txt 2022-04-06 15:07:06 UTC (rev 292466)
@@ -1,4 +1,11 @@
Check me!
-FAIL CSS Selectors Invalidation: input pseudo classes in :has() argument assert_equals: ancestor should be yellowgreen expected "rgb(154, 205, 50)" but got "rgb(0, 128, 0)"
+PASS :checked & :indeterminate invalidation
+PASS :disabled invalidation
+PASS :read-only invalidation
+PASS :valid invalidation
+FAIL :default invalidation with input[type=radio] assert_equals: ancestor should be lightblue expected "rgb(173, 216, 230)" but got "rgb(0, 0, 0)"
+PASS :required invalidation
+FAIL :out-of-range invalidation assert_equals: ancestor should be darkgreen expected "rgb(0, 100, 0)" but got "rgb(0, 0, 0)"
+FAIL :placeholder-shown invalidation assert_equals: ancestor should be navy expected "rgb(0, 0, 128)" but got "rgb(0, 0, 0)"
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has.html (292465 => 292466)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has.html 2022-04-06 15:02:01 UTC (rev 292465)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/input-pseudo-classes-in-has.html 2022-04-06 15:07:06 UTC (rev 292466)
@@ -5,9 +5,6 @@
<link rel="help" href=""
<script src=""
<script src=""
-<script src=""
-<script src=""
-<script src=""
<style>
.ancestor:has(#checkme:checked) { color: green }
.ancestor:has(#checkme:indeterminate) { color: yellowgreen }
@@ -27,7 +24,10 @@
<input id="numberinput" type="number" min="1" max="10" value="5">
</div>
<script>
- test(() => {
+ test(function() {
+ this.add_cleanup(() => {
+ checkme.checked = false;
+ });
checkme.checked = false;
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
@@ -37,51 +37,92 @@
checkme.indeterminate = true;
assert_equals(getComputedStyle(subject).color, "rgb(154, 205, 50)",
"ancestor should be yellowgreen");
+ const input = checkme;
checkme.remove();
+ input.indeterminate = false;
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
- {
- const input = document.createElement('input');
- input.id = 'checkme';
- input.setAttribute('type', 'checkbox');
- input.setAttribute('name', 'my-checkbox');
- input.checked = true;
- assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
- "ancestor should be black");
- subject.prepend(input);
- assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
- "ancestor should be green");
- }
+ subject.prepend(input);
+ checkme.checked = true;
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
+ "ancestor should be green");
+ }, ":checked & :indeterminate invalidation");
+ test(function() {
+ this.add_cleanup(() => {
+ checkme.disabled = false;
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
checkme.disabled = true;
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 255)",
"ancestor should be blue");
+ }, ":disabled invalidation");
+ test(function() {
+ this.add_cleanup(() => {
+ textinput.readOnly = false;
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
textinput.readOnly = true;
assert_equals(getComputedStyle(subject).color, "rgb(135, 206, 235)",
"ancestor should be skyblue");
- textinput.readOnly = false;
+ }, ":read-only invalidation");
- textinput.placeholder = 'placeholder text';
- assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 128)",
- "ancestor should be navy");
+ test(function() {
+ this.add_cleanup(() => {
+ textinput.value = "";
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
+ textinput.value = "text input";
+ assert_equals(getComputedStyle(subject).color, "rgb(144, 238, 144)",
+ "ancestor should be lightgreen");
+ }, ":valid invalidation");
+ test(function() {
+ this.add_cleanup(() => {
+ radioinput.removeAttribute("type");
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
radioinput.type = 'radio';
assert_equals(getComputedStyle(subject).color, "rgb(173, 216, 230)",
"ancestor should be lightblue");
+ }, ":default invalidation with input[type=radio]");
- textinput.value = "text input";
- assert_equals(getComputedStyle(subject).color, "rgb(144, 238, 144)",
- "ancestor should be lightgreen");
+ test(function() {
+ this.add_cleanup(() => {
+ numberinput.required = false;
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
+ numberinput.required = true;
+ assert_equals(getComputedStyle(subject).color, "rgb(255, 192, 203)",
+ "ancestor should be pink");
+ }, ":required invalidation");
+ test(function() {
+ this.add_cleanup(() => {
+ numberinput.value = 5;
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
numberinput.value = 12;
assert_equals(getComputedStyle(subject).color, "rgb(0, 100, 0)",
"ancestor should be darkgreen");
+ }, ":out-of-range invalidation");
- numberinput.required = true;
- assert_equals(getComputedStyle(subject).color, "rgb(255, 192, 203)",
- "ancestor should be pink");
-
- });
-</script>
\ No newline at end of file
+ test(function() {
+ this.add_cleanup(() => {
+ textinput.removeAttribute("placeholder");
+ });
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
+ "ancestor should be black");
+ textinput.placeholder = 'placeholder text';
+ assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 128)",
+ "ancestor should be navy");
+ }, ":placeholder-shown invalidation");
+</script>
Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (292465 => 292466)
--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2022-04-06 15:02:01 UTC (rev 292465)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp 2022-04-06 15:07:06 UTC (rev 292466)
@@ -167,15 +167,19 @@
}
}
} else if (name == readonlyAttr) {
- bool wasReadOnly = m_isReadOnly;
- m_isReadOnly = !value.isNull();
- if (wasReadOnly != m_isReadOnly)
+ bool newReadOnly = !value.isNull();
+ if (m_isReadOnly != newReadOnly) {
+ Style::PseudoClassChangeInvalidation readOnlyInvalidation(*this, { { CSSSelector::PseudoClassReadOnly, newReadOnly }, { CSSSelector::PseudoClassReadWrite, !newReadOnly } });
+ m_isReadOnly = newReadOnly;
readOnlyStateChanged();
+ }
} else if (name == requiredAttr) {
- bool wasRequired = m_isRequired;
- m_isRequired = !value.isNull();
- if (wasRequired != m_isRequired)
+ bool newRequired = !value.isNull();
+ if (m_isRequired != newRequired) {
+ Style::PseudoClassChangeInvalidation requiredInvalidation(*this, { { CSSSelector::PseudoClassRequired, newRequired }, { CSSSelector::PseudoClassOptional, !newRequired } });
+ m_isRequired = newRequired;
requiredStateChanged();
+ }
} else
HTMLElement::parseAttribute(name, value);
}
@@ -195,6 +199,9 @@
void HTMLFormControlElement::readOnlyStateChanged()
{
updateWillValidateAndValidity();
+
+ // Some input pseudo classes like :in-range/out-of-range change based on the readonly state.
+ // FIXME: Use PseudoClassChangeInvalidation instead for :has() support and more efficiency.
invalidateStyleForSubtree();
}
@@ -201,9 +208,6 @@
void HTMLFormControlElement::requiredStateChanged()
{
updateValidity();
- // Style recalculation is needed because style selectors may include
- // :required and :optional pseudo-classes.
- invalidateStyleForSubtree();
}
void HTMLFormControlElement::didAttachRenderers()