haikuwebkit/LayoutTests/fast/css/pseudo-invalid-fieldset-inv...

97 lines
3.2 KiB
HTML
Raw Permalink Normal View History

Implement :valid and :invalid matching for the fieldset element https://bugs.webkit.org/show_bug.cgi?id=138769 Reviewed by Darin Adler. Source/WebCore: In the latest HTML spec, the pseudo classes :valid and :invalid match a fieldset element based on its descendants: https://html.spec.whatwg.org/#selector-valid https://html.spec.whatwg.org/#selector-invalid This patch adds that behavior. There are two key problems to solve with these pseudo classes on fieldset: -Efficient matching. -Style invalidation when any of the descendant changes. To implement the style invalidation, I have modified HTMLFormControlElement to notify its ancestor when its state changes. The first change is making the state fully internal to HTMLFormControlElement, we do not want subclass to be able to change the behavior and forget to update the ancestors. To achieve that encapsulation, the interface was changed a bit: -Neither willValidate() nor isValidFormControlElement() inherit from Element. Instead, willValidate() is the implementation of FormAssociatedElement's interface and it is final. The method isValidFormControlElement() becomes completely internal to HTMLFormControlElement. -Since willValidate() should no longer be re-implemented by subclass, the elements that were depending on it have been migrated to recalcWillValidate() to set the initial state as needed. With the validity state fully encapsulated in HTMLFormControlElement, all I need is a way to communicate that information to HTMLFieldSetElement ancestors. This is done in two cases: -The validity state changes. -The tree changes in a way that would make the input element not a descendant of a HTMLFieldSetElement. The invalidation is simply done by walking up the ancestors and adding the current element to a "validity dependency list" on each HTMLFieldSetElement. Tests: fast/css/pseudo-invalid-fieldset-invalidation-optimization.html fast/css/pseudo-invalid-fieldset-style-sharing.html fast/css/pseudo-invalid-fieldset.html fast/css/pseudo-valid-fieldset-invalidation-optimization.html fast/css/pseudo-valid-fieldset-style-sharing.html fast/css/pseudo-valid-fieldset.html fast/selectors/invalid-fieldset-style-update-1.html fast/selectors/invalid-fieldset-style-update-2.html fast/selectors/invalid-fieldset-style-update-3.html fast/selectors/invalid-fieldset-style-update-4.html fast/selectors/invalid-fieldset-style-update-5.html fast/selectors/valid-fieldset-style-update-1.html fast/selectors/valid-fieldset-style-update-2.html fast/selectors/valid-fieldset-style-update-3.html fast/selectors/valid-fieldset-style-update-4.html fast/selectors/valid-fieldset-style-update-5.html * css/SelectorCheckerTestFunctions.h: (WebCore::isInRange): (WebCore::isOutOfRange): (WebCore::isInvalid): (WebCore::isValid): The hack "ContainsValidityStyleRules" is in the way of correct styling of FieldSet and Form. It is not the right way to get stylesheet properties anyway. * css/StyleResolver.cpp: (WebCore::StyleResolver::canShareStyleWithControl): Make sure style sharing does not incorrectly share style for fieldset elements. * dom/Document.cpp: (WebCore::Document::Document): * dom/Document.h: (WebCore::Document::containsValidityStyleRules): Deleted. (WebCore::Document::setContainsValidityStyleRules): Deleted. * dom/Element.h: (WebCore::Element::matchesValidPseudoClass): (WebCore::Element::matchesInvalidPseudoClass): (WebCore::Element::willValidate): Deleted. (WebCore::Element::isValidFormControlElement): Deleted. * html/FormAssociatedElement.cpp: (WebCore::FormAssociatedElement::customError): * html/FormAssociatedElement.h: * html/HTMLFieldSetElement.cpp: (WebCore::HTMLFieldSetElement::matchesValidPseudoClass): (WebCore::HTMLFieldSetElement::matchesInvalidPseudoClass): (WebCore::HTMLFieldSetElement::addInvalidDescendant): (WebCore::HTMLFieldSetElement::removeInvalidDescendant): Each HTMLFormControlElement that has constraint validation adds or removes itself from its HTMLFieldSetElement ancestors. It should be possible to just keep track of a count instead of a HashSet. I decided to got with the HashSet to make the code more robust and easier to debug. A few assertions ensure that the HashSet is actually used as a counter. * html/HTMLFieldSetElement.h: * html/HTMLFormControlElement.cpp: (WebCore::addInvalidElementToAncestorFromInsertionPoint): (WebCore::removeInvalidElementToAncestorFromInsertionPoint): (WebCore::HTMLFormControlElement::insertedInto): (WebCore::HTMLFormControlElement::removedFrom): One tricky part of those two functions is that we cannot use matchesValidPseudoClass() or matchesInvalidPseudoClass(). The reason is that HTMLFieldSetElement is a subclass of HTMLFormControlElement and it has its own definition of what Valid and Invalid mean when matching selectors. In HTMLFormControlElement, we must use the internal state, willValidate() and isValidFormControlElement() must be used directly. (WebCore::HTMLFormControlElement::matchesValidPseudoClass): (WebCore::HTMLFormControlElement::matchesInvalidPseudoClass): (WebCore::HTMLFormControlElement::willValidate): (WebCore::addInvalidElementToAncestors): (WebCore::removeInvalidElementFromAncestors): (WebCore::HTMLFormControlElement::setNeedsWillValidateCheck): (WebCore::HTMLFormControlElement::setNeedsValidityCheck): (WebCore::HTMLFormControlElement::isValidFormControlElement): Deleted. * html/HTMLFormControlElement.h: (WebCore::HTMLFormControlElement::isValidFormControlElement): * html/HTMLKeygenElement.h: * html/HTMLObjectElement.h: * html/HTMLOutputElement.h: LayoutTests: There are many ways to change the validation state of a submittable element. I included a series of test trying to exercises as many combination as possible. * fast/css/pseudo-valid-unapplied-expected.txt: * fast/css/pseudo-valid-unapplied.html: This test was checking that :valid and :invalid are not applied to fieldset. Such results are incorrect with the latest specification. * fast/css/pseudo-invalid-fieldset-expected.html: Added. * fast/css/pseudo-invalid-fieldset-invalidation-optimization-expected.txt: Added. * fast/css/pseudo-invalid-fieldset-invalidation-optimization.html: Added. * fast/css/pseudo-invalid-fieldset-style-sharing-expected.html: Added. * fast/css/pseudo-invalid-fieldset-style-sharing.html: Added. * fast/css/pseudo-invalid-fieldset.html: Added. * fast/css/pseudo-valid-fieldset-expected.html: Added. * fast/css/pseudo-valid-fieldset-invalidation-optimization-expected.txt: Added. * fast/css/pseudo-valid-fieldset-invalidation-optimization.html: Added. * fast/css/pseudo-valid-fieldset-style-sharing-expected.html: Added. * fast/css/pseudo-valid-fieldset-style-sharing.html: Added. * fast/css/pseudo-valid-fieldset.html: Added. * fast/selectors/invalid-fieldset-style-update-1-expected.txt: Added. * fast/selectors/invalid-fieldset-style-update-1.html: Added. * fast/selectors/invalid-fieldset-style-update-2-expected.txt: Added. * fast/selectors/invalid-fieldset-style-update-2.html: Added. * fast/selectors/invalid-fieldset-style-update-3-expected.txt: Added. * fast/selectors/invalid-fieldset-style-update-3.html: Added. * fast/selectors/invalid-fieldset-style-update-4-expected.txt: Added. * fast/selectors/invalid-fieldset-style-update-4.html: Added. * fast/selectors/invalid-fieldset-style-update-5-expected.txt: Added. * fast/selectors/invalid-fieldset-style-update-5.html: Added. * fast/selectors/valid-fieldset-style-update-1-expected.txt: Added. * fast/selectors/valid-fieldset-style-update-1.html: Added. * fast/selectors/valid-fieldset-style-update-2-expected.txt: Added. * fast/selectors/valid-fieldset-style-update-2.html: Added. * fast/selectors/valid-fieldset-style-update-3-expected.txt: Added. * fast/selectors/valid-fieldset-style-update-3.html: Added. * fast/selectors/valid-fieldset-style-update-4-expected.txt: Added. * fast/selectors/valid-fieldset-style-update-4.html: Added. * fast/selectors/valid-fieldset-style-update-5-expected.txt: Added. * fast/selectors/valid-fieldset-style-update-5.html: Added. Canonical link: https://commits.webkit.org/156632@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@176174 268f45cc-cd09-0410-ab3c-d52691b4dbfc
2014-11-17 07:01:21 +00:00
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
<style>
fieldset {
color: black;
}
fieldset:invalid {
color: rgb(0, 1, 2);
}
</style>
</head>
<body>
<div>
<!-- With renderer -->
<fieldset id="with-renderer">
<input class="input1" required value="Valid">
<input class="input2" required value="Valid">
<input class="input3" required value="Valid">
<input class="input4" required value="Valid">
</fieldset>
</div>
<div style="display:none;">
<!-- Without renderer -->
<fieldset id="without-renderer">
<input class="input1" required value="Valid">
<input class="input2" required value="Valid">
<input class="input3" required value="Valid">
<input class="input4" required value="Valid">
</fieldset>
</div>
</body>
<script>
description('Test that we do not invalidate the style of &lt;fieldset&gt; excessively when matching :invalid based on the descendants.');
function shouldNeedStyleRecalc(expected) {
var testFunction = expected ? shouldBeTrue : shouldBeFalse;
testFunction('window.internals.nodeNeedsStyleRecalc(document.getElementById("with-renderer"))');
testFunction('window.internals.nodeNeedsStyleRecalc(document.getElementById("without-renderer"))');
}
function checkStyle(expectedColor) {
shouldBeEqualToString('getComputedStyle(document.getElementById("with-renderer")).color', expectedColor);
shouldBeEqualToString('getComputedStyle(document.getElementById("without-renderer")).color', expectedColor);
}
// Force a layout to ensure we don't have dirty styles.
var offsetTop = document.documentElement.offsetTop;
// Initial state.
shouldNeedStyleRecalc(false);
checkStyle('rgb(0, 0, 0)');
// Make input3 invalid, the fieldset should also become invalid.
var inputs3 = document.querySelectorAll('.input3');
inputs3[0].value = '';
inputs3[1].value = '';
shouldNeedStyleRecalc(true);
checkStyle('rgb(0, 1, 2)');
// Making more fields invalid should not invalidate the fieldset's style.
var inputs = document.querySelectorAll(':matches(.input2, .input4)');
for (var i = 0; i < inputs.length; ++i)
inputs[i].value = '';
shouldNeedStyleRecalc(false);
checkStyle('rgb(0, 1, 2)');
// Removing valid fields should not invalidate the style.
var inputs1 = document.querySelectorAll(':matches(.input1)');
for (var i = 0; i < inputs1.length; ++i)
inputs1[i].parentNode.removeChild(inputs1[i]);
// Making all fields valid but one, fieldset still does not need to be invalidated.
var inputs = document.querySelectorAll(':matches(.input2, .input3)');
for (var i = 0; i < inputs.length; ++i)
inputs[i].removeAttribute('required');
shouldNeedStyleRecalc(false);
checkStyle('rgb(0, 1, 2)');
// Making the last input valid. The style must update, fieldset must be invalidated.
var inputs = document.querySelectorAll(':matches(.input4)');
for (var i = 0; i < inputs.length; ++i)
inputs[i].removeAttribute('required');
shouldNeedStyleRecalc(true);
checkStyle('rgb(0, 0, 0)');
document.getElementById("with-renderer").style.display = 'none';
</script>
<script src="../../resources/js-test-post.js"></script>
</html>