Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / html / HTMLFieldSetElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLFieldSetElement.h"
27
28 #include "ElementIterator.h"
29 #include "HTMLFormControlsCollection.h"
30 #include "HTMLLegendElement.h"
31 #include "HTMLNames.h"
32 #include "HTMLObjectElement.h"
33 #include "NodeRareData.h"
34 #include "RenderFieldset.h"
35 #include <wtf/StdLibExtras.h>
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
41 inline HTMLFieldSetElement::HTMLFieldSetElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
42     : HTMLFormControlElement(tagName, document, form)
43     , m_documentVersion(0)
44 {
45     ASSERT(hasTagName(fieldsetTag));
46 }
47
48 HTMLFieldSetElement::~HTMLFieldSetElement()
49 {
50     if (fastHasAttribute(disabledAttr))
51         document().removeDisabledFieldsetElement();
52 }
53
54 Ref<HTMLFieldSetElement> HTMLFieldSetElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
55 {
56     return adoptRef(*new HTMLFieldSetElement(tagName, document, form));
57 }
58
59 static void updateFromControlElementsAncestorDisabledStateUnder(HTMLElement& startNode, bool isDisabled)
60 {
61     HTMLFormControlElement* control;
62     if (is<HTMLFormControlElement>(startNode))
63         control = &downcast<HTMLFormControlElement>(startNode);
64     else
65         control = Traversal<HTMLFormControlElement>::firstWithin(startNode);
66     while (control) {
67         control->setAncestorDisabled(isDisabled);
68         // Don't call setAncestorDisabled(false) on form contorls inside disabled fieldsets.
69         if (is<HTMLFieldSetElement>(*control) && control->fastHasAttribute(disabledAttr))
70             control = Traversal<HTMLFormControlElement>::nextSkippingChildren(*control, &startNode);
71         else
72             control = Traversal<HTMLFormControlElement>::next(*control, &startNode);
73     }
74 }
75
76 void HTMLFieldSetElement::disabledAttributeChanged()
77 {
78     if (fastHasAttribute(disabledAttr))
79         document().addDisabledFieldsetElement();
80     else
81         document().removeDisabledFieldsetElement();
82
83     HTMLFormControlElement::disabledAttributeChanged();
84 }
85
86 void HTMLFieldSetElement::disabledStateChanged()
87 {
88     // This element must be updated before the style of nodes in its subtree gets recalculated.
89     HTMLFormControlElement::disabledStateChanged();
90
91     if (disabledByAncestorFieldset())
92         return;
93
94     bool thisFieldsetIsDisabled = fastHasAttribute(disabledAttr);
95     bool hasSeenFirstLegendElement = false;
96     for (HTMLElement* control = Traversal<HTMLElement>::firstChild(*this); control; control = Traversal<HTMLElement>::nextSibling(*control)) {
97         if (!hasSeenFirstLegendElement && is<HTMLLegendElement>(*control)) {
98             hasSeenFirstLegendElement = true;
99             updateFromControlElementsAncestorDisabledStateUnder(*control, false /* isDisabled */);
100             continue;
101         }
102         updateFromControlElementsAncestorDisabledStateUnder(*control, thisFieldsetIsDisabled);
103     }
104 }
105
106 void HTMLFieldSetElement::childrenChanged(const ChildChange& change)
107 {
108     HTMLFormControlElement::childrenChanged(change);
109     if (!fastHasAttribute(disabledAttr))
110         return;
111
112     HTMLLegendElement* legend = Traversal<HTMLLegendElement>::firstChild(*this);
113     if (!legend)
114         return;
115
116     // We only care about the first legend element (in which form contorls are not disabled by this element) changing here.
117     updateFromControlElementsAncestorDisabledStateUnder(*legend, false /* isDisabled */);
118     while ((legend = Traversal<HTMLLegendElement>::nextSibling(*legend)))
119         updateFromControlElementsAncestorDisabledStateUnder(*legend, true);
120 }
121
122 void HTMLFieldSetElement::didMoveToNewDocument(Document* oldDocument)
123 {
124     HTMLFormControlElement::didMoveToNewDocument(oldDocument);
125     if (fastHasAttribute(disabledAttr)) {
126         if (oldDocument)
127             oldDocument->removeDisabledFieldsetElement();
128         document().addDisabledFieldsetElement();
129     }
130 }
131
132 bool HTMLFieldSetElement::matchesValidPseudoClass() const
133 {
134     return m_invalidDescendants.isEmpty();
135 }
136
137 bool HTMLFieldSetElement::matchesInvalidPseudoClass() const
138 {
139     return !m_invalidDescendants.isEmpty();
140 }
141
142 bool HTMLFieldSetElement::supportsFocus() const
143 {
144     return HTMLElement::supportsFocus();
145 }
146
147 const AtomicString& HTMLFieldSetElement::formControlType() const
148 {
149     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, fieldset, ("fieldset", AtomicString::ConstructFromLiteral));
150     return fieldset;
151 }
152
153 RenderPtr<RenderElement> HTMLFieldSetElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
154 {
155     return createRenderer<RenderFieldset>(*this, WTFMove(style));
156 }
157
158 HTMLLegendElement* HTMLFieldSetElement::legend() const
159 {
160     return const_cast<HTMLLegendElement*>(childrenOfType<HTMLLegendElement>(*this).first());
161 }
162
163 Ref<HTMLFormControlsCollection> HTMLFieldSetElement::elements()
164 {
165     return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, FormControls);
166 }
167
168 Ref<HTMLCollection> HTMLFieldSetElement::elementsForNativeBindings()
169 {
170     return elements();
171 }
172
173 void HTMLFieldSetElement::refreshElementsIfNeeded() const
174 {
175     uint64_t documentVersion = document().domTreeVersion();
176     if (m_documentVersion == documentVersion)
177         return;
178
179     m_documentVersion = documentVersion;
180
181     m_associatedElements.clear();
182
183     for (auto& element : descendantsOfType<Element>(const_cast<HTMLFieldSetElement&>(*this))) {
184         if (element.hasTagName(objectTag))
185             m_associatedElements.append(&downcast<HTMLObjectElement>(element));
186         else if (is<HTMLFormControlElement>(element))
187             m_associatedElements.append(&downcast<HTMLFormControlElement>(element));
188     }
189 }
190
191 const Vector<FormAssociatedElement*>& HTMLFieldSetElement::associatedElements() const
192 {
193     refreshElementsIfNeeded();
194     return m_associatedElements;
195 }
196
197 unsigned HTMLFieldSetElement::length() const
198 {
199     refreshElementsIfNeeded();
200     unsigned length = 0;
201     for (auto& element : m_associatedElements) {
202         if (element->isEnumeratable())
203             ++length;
204     }
205     return length;
206 }
207
208 void HTMLFieldSetElement::addInvalidDescendant(const HTMLFormControlElement& invalidFormControlElement)
209 {
210     ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(invalidFormControlElement), "FieldSet are never candidates for constraint validation.");
211     ASSERT(static_cast<const Element&>(invalidFormControlElement).matchesInvalidPseudoClass());
212     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.");
213
214     if (m_invalidDescendants.isEmpty())
215         setNeedsStyleRecalc();
216     m_invalidDescendants.add(&invalidFormControlElement);
217 }
218
219 void HTMLFieldSetElement::removeInvalidDescendant(const HTMLFormControlElement& formControlElement)
220 {
221     ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation.");
222     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.");
223
224     m_invalidDescendants.remove(&formControlElement);
225     if (m_invalidDescendants.isEmpty())
226         setNeedsStyleRecalc();
227 }
228
229 } // namespace