diff options
Diffstat (limited to 'Source/WebCore/html/HTMLFieldSetElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLFieldSetElement.cpp | 150 |
1 files changed, 123 insertions, 27 deletions
diff --git a/Source/WebCore/html/HTMLFieldSetElement.cpp b/Source/WebCore/html/HTMLFieldSetElement.cpp index 0dab60d55..a79ad15ba 100644 --- a/Source/WebCore/html/HTMLFieldSetElement.cpp +++ b/Source/WebCore/html/HTMLFieldSetElement.cpp @@ -26,10 +26,11 @@ #include "HTMLFieldSetElement.h" #include "ElementIterator.h" -#include "HTMLCollection.h" +#include "HTMLFormControlsCollection.h" #include "HTMLLegendElement.h" #include "HTMLNames.h" #include "HTMLObjectElement.h" +#include "NodeRareData.h" #include "RenderFieldset.h" #include <wtf/StdLibExtras.h> @@ -39,35 +40,105 @@ using namespace HTMLNames; inline HTMLFieldSetElement::HTMLFieldSetElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form) : HTMLFormControlElement(tagName, document, form) - , m_documentVersion(0) { ASSERT(hasTagName(fieldsetTag)); } -PassRefPtr<HTMLFieldSetElement> HTMLFieldSetElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form) +HTMLFieldSetElement::~HTMLFieldSetElement() { - return adoptRef(new HTMLFieldSetElement(tagName, document, form)); + if (m_hasDisabledAttribute) + document().removeDisabledFieldsetElement(); } -void HTMLFieldSetElement::invalidateDisabledStateUnder(Element* base) +Ref<HTMLFieldSetElement> HTMLFieldSetElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form) { - for (auto& control : descendantsOfType<HTMLFormControlElement>(*base)) - control.ancestorDisabledStateWasChanged(); + return adoptRef(*new HTMLFieldSetElement(tagName, document, form)); +} + +static void updateFromControlElementsAncestorDisabledStateUnder(HTMLElement& startNode, bool isDisabled) +{ + HTMLFormControlElement* control; + if (is<HTMLFormControlElement>(startNode)) + control = &downcast<HTMLFormControlElement>(startNode); + else + control = Traversal<HTMLFormControlElement>::firstWithin(startNode); + while (control) { + control->setAncestorDisabled(isDisabled); + // Don't call setAncestorDisabled(false) on form controls inside disabled fieldsets. + if (is<HTMLFieldSetElement>(*control) && control->hasAttributeWithoutSynchronization(disabledAttr)) + control = Traversal<HTMLFormControlElement>::nextSkippingChildren(*control, &startNode); + else + control = Traversal<HTMLFormControlElement>::next(*control, &startNode); + } } void HTMLFieldSetElement::disabledAttributeChanged() { - // This element must be updated before the style of nodes in its subtree gets recalculated. + bool hasDisabledAttribute = hasAttributeWithoutSynchronization(disabledAttr); + if (m_hasDisabledAttribute != hasDisabledAttribute) { + m_hasDisabledAttribute = hasDisabledAttribute; + if (hasDisabledAttribute) + document().addDisabledFieldsetElement(); + else + document().removeDisabledFieldsetElement(); + } + HTMLFormControlElement::disabledAttributeChanged(); - invalidateDisabledStateUnder(this); +} + +void HTMLFieldSetElement::disabledStateChanged() +{ + // This element must be updated before the style of nodes in its subtree gets recalculated. + HTMLFormControlElement::disabledStateChanged(); + + if (disabledByAncestorFieldset()) + return; + + bool thisFieldsetIsDisabled = hasAttributeWithoutSynchronization(disabledAttr); + bool hasSeenFirstLegendElement = false; + for (HTMLElement* control = Traversal<HTMLElement>::firstChild(*this); control; control = Traversal<HTMLElement>::nextSibling(*control)) { + if (!hasSeenFirstLegendElement && is<HTMLLegendElement>(*control)) { + hasSeenFirstLegendElement = true; + updateFromControlElementsAncestorDisabledStateUnder(*control, false /* isDisabled */); + continue; + } + updateFromControlElementsAncestorDisabledStateUnder(*control, thisFieldsetIsDisabled); + } } void HTMLFieldSetElement::childrenChanged(const ChildChange& change) { HTMLFormControlElement::childrenChanged(change); + if (!hasAttributeWithoutSynchronization(disabledAttr)) + return; + + HTMLLegendElement* legend = Traversal<HTMLLegendElement>::firstChild(*this); + if (!legend) + return; - for (auto& legend : childrenOfType<HTMLLegendElement>(*this)) - invalidateDisabledStateUnder(&legend); + // We only care about the first legend element (in which form controls are not disabled by this element) changing here. + updateFromControlElementsAncestorDisabledStateUnder(*legend, false /* isDisabled */); + while ((legend = Traversal<HTMLLegendElement>::nextSibling(*legend))) + updateFromControlElementsAncestorDisabledStateUnder(*legend, true); +} + +void HTMLFieldSetElement::didMoveToNewDocument(Document& oldDocument) +{ + HTMLFormControlElement::didMoveToNewDocument(oldDocument); + if (m_hasDisabledAttribute) { + oldDocument.removeDisabledFieldsetElement(); + document().addDisabledFieldsetElement(); + } +} + +bool HTMLFieldSetElement::matchesValidPseudoClass() const +{ + return m_invalidDescendants.isEmpty(); +} + +bool HTMLFieldSetElement::matchesInvalidPseudoClass() const +{ + return !m_invalidDescendants.isEmpty(); } bool HTMLFieldSetElement::supportsFocus() const @@ -77,26 +148,31 @@ bool HTMLFieldSetElement::supportsFocus() const const AtomicString& HTMLFieldSetElement::formControlType() const { - DEFINE_STATIC_LOCAL(const AtomicString, fieldset, ("fieldset", AtomicString::ConstructFromLiteral)); + static NeverDestroyed<const AtomicString> fieldset("fieldset", AtomicString::ConstructFromLiteral); return fieldset; } -RenderPtr<RenderElement> HTMLFieldSetElement::createElementRenderer(PassRef<RenderStyle> style) +RenderPtr<RenderElement> HTMLFieldSetElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { - return createRenderer<RenderFieldset>(*this, std::move(style)); + return createRenderer<RenderFieldset>(*this, WTFMove(style)); } HTMLLegendElement* HTMLFieldSetElement::legend() const { - return const_cast<HTMLLegendElement*>(descendantsOfType<HTMLLegendElement>(*this).first()); + return const_cast<HTMLLegendElement*>(childrenOfType<HTMLLegendElement>(*this).first()); } -PassRefPtr<HTMLCollection> HTMLFieldSetElement::elements() +Ref<HTMLFormControlsCollection> HTMLFieldSetElement::elements() { - return ensureCachedHTMLCollection(FormControls); + return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, FormControls); } -void HTMLFieldSetElement::refreshElementsIfNeeded() const +Ref<HTMLCollection> HTMLFieldSetElement::elementsForNativeBindings() +{ + return elements(); +} + +void HTMLFieldSetElement::updateAssociatedElements() const { uint64_t documentVersion = document().domTreeVersion(); if (m_documentVersion == documentVersion) @@ -106,29 +182,49 @@ void HTMLFieldSetElement::refreshElementsIfNeeded() const m_associatedElements.clear(); - for (auto& element : descendantsOfType<Element>(const_cast<HTMLFieldSetElement&>(*this))) { - if (element.hasTagName(objectTag)) - m_associatedElements.append(&toHTMLObjectElement(element)); - else if (element.isFormControlElement()) - m_associatedElements.append(&toHTMLFormControlElement(element)); + for (auto& element : descendantsOfType<HTMLElement>(const_cast<HTMLFieldSetElement&>(*this))) { + if (is<HTMLObjectElement>(element)) + m_associatedElements.append(&downcast<HTMLObjectElement>(element)); + else if (is<HTMLFormControlElement>(element)) + m_associatedElements.append(&downcast<HTMLFormControlElement>(element)); } } const Vector<FormAssociatedElement*>& HTMLFieldSetElement::associatedElements() const { - refreshElementsIfNeeded(); + updateAssociatedElements(); return m_associatedElements; } unsigned HTMLFieldSetElement::length() const { - refreshElementsIfNeeded(); unsigned length = 0; - for (unsigned i = 0; i < m_associatedElements.size(); ++i) { - if (m_associatedElements[i]->isEnumeratable()) + for (auto* element : associatedElements()) { + if (element->isEnumeratable()) ++length; } return length; } +void HTMLFieldSetElement::addInvalidDescendant(const HTMLFormControlElement& invalidFormControlElement) +{ + ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(invalidFormControlElement), "FieldSet are never candidates for constraint validation."); + ASSERT(static_cast<const Element&>(invalidFormControlElement).matchesInvalidPseudoClass()); + ASSERT_WITH_MESSAGE(!m_invalidDescendants.contains(&invalidFormControlElement), "Updating the fieldset on validity change is not an efficient operation, it should only be done when necessary."); + + if (m_invalidDescendants.isEmpty()) + invalidateStyleForSubtree(); + m_invalidDescendants.add(&invalidFormControlElement); +} + +void HTMLFieldSetElement::removeInvalidDescendant(const HTMLFormControlElement& formControlElement) +{ + ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation."); + ASSERT_WITH_MESSAGE(m_invalidDescendants.contains(&formControlElement), "Updating the fieldset on validity change is not an efficient operation, it should only be done when necessary."); + + m_invalidDescendants.remove(&formControlElement); + if (m_invalidDescendants.isEmpty()) + invalidateStyleForSubtree(); +} + } // namespace |