bd42622014db247cbca55b27acfab1f510f9c0ea
[WebKit-https.git] / Source / WebCore / html / FormAssociatedElement.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-2016 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 "FormAssociatedElement.h"
27
28 #include "EditorClient.h"
29 #include "ElementAncestorIterator.h"
30 #include "FormController.h"
31 #include "Frame.h"
32 #include "HTMLFormControlElement.h"
33 #include "HTMLFormElement.h"
34 #include "HTMLNames.h"
35 #include "HTMLObjectElement.h"
36 #include "IdTargetObserver.h"
37
38 namespace WebCore {
39
40 using namespace HTMLNames;
41
42 class FormAttributeTargetObserver final : private IdTargetObserver {
43     WTF_MAKE_FAST_ALLOCATED;
44 public:
45     FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement&);
46
47 private:
48     void idTargetChanged() override;
49
50     FormAssociatedElement& m_element;
51 };
52
53 FormAssociatedElement::FormAssociatedElement(HTMLFormElement* form)
54     : m_form(nullptr)
55     , m_formSetByParser(makeWeakPtr(form))
56 {
57 }
58
59 FormAssociatedElement::~FormAssociatedElement()
60 {
61     setForm(nullptr);
62 }
63
64 void FormAssociatedElement::didMoveToNewDocument(Document&)
65 {
66     HTMLElement& element = asHTMLElement();
67     if (element.hasAttributeWithoutSynchronization(formAttr) && element.isConnected())
68         resetFormAttributeTargetObserver();
69 }
70
71 void FormAssociatedElement::insertedIntoAncestor(Node::InsertionType insertionType, ContainerNode&)
72 {
73     HTMLElement& element = asHTMLElement();
74     if (m_formSetByParser) {
75         // The form could have been removed by a script during parsing.
76         if (m_formSetByParser->isConnected())
77             setForm(m_formSetByParser.get());
78         m_formSetByParser = nullptr;
79     }
80
81     if (m_form && element.rootElement() != m_form->rootElement())
82         setForm(nullptr);
83
84     if (!insertionType.connectedToDocument)
85         return;
86
87     if (element.hasAttributeWithoutSynchronization(formAttr))
88         resetFormAttributeTargetObserver();
89 }
90
91 void FormAssociatedElement::removedFromAncestor(Node::RemovalType, ContainerNode&)
92 {
93     m_formAttributeTargetObserver = nullptr;
94
95     // If the form and element are both in the same tree, preserve the connection to the form.
96     // Otherwise, null out our form and remove ourselves from the form's list of elements.
97     // Do not rely on rootNode() because our IsInTreeScope is outdated.
98     if (m_form && &asHTMLElement().traverseToRootNode() != &m_form->traverseToRootNode())
99         setForm(nullptr);
100 }
101
102 HTMLFormElement* FormAssociatedElement::findAssociatedForm(const HTMLElement* element, HTMLFormElement* currentAssociatedForm)
103 {
104     const AtomicString& formId(element->attributeWithoutSynchronization(formAttr));
105     if (!formId.isNull() && element->isConnected()) {
106         // The HTML5 spec says that the element should be associated with
107         // the first element in the document to have an ID that equal to
108         // the value of form attribute, so we put the result of
109         // treeScope().getElementById() over the given element.
110         RefPtr<Element> newFormCandidate = element->treeScope().getElementById(formId);
111         if (is<HTMLFormElement>(newFormCandidate))
112             return downcast<HTMLFormElement>(newFormCandidate.get());
113         return nullptr;
114     }
115
116     if (!currentAssociatedForm)
117         return HTMLFormElement::findClosestFormAncestor(*element);
118
119     return currentAssociatedForm;
120 }
121
122 HTMLFormElement* FormAssociatedElement::form() const
123 {
124     return m_form.get();
125 }
126
127 void FormAssociatedElement::formOwnerRemovedFromTree(const Node& formRoot)
128 {
129     ASSERT(m_form);
130     // Can't use RefPtr here beacuse this function might be called inside ~ShadowRoot via addChildNodesToDeletionQueue. See webkit.org/b/189493.
131     Node* rootNode = &asHTMLElement();
132     for (auto* ancestor = asHTMLElement().parentNode(); ancestor; ancestor = ancestor->parentNode()) {
133         if (ancestor == m_form) {
134             // Form is our ancestor so we don't need to reset our owner, we also no longer
135             // need an id observer since we are no longer connected.
136             m_formAttributeTargetObserver = nullptr;
137             return;
138         }
139         rootNode = ancestor;
140     }
141
142     // We are no longer in the same tree as our form owner so clear our owner.
143     if (rootNode != &formRoot)
144         setForm(nullptr);
145 }
146
147 void FormAssociatedElement::setForm(HTMLFormElement* newForm)
148 {
149     if (m_form == newForm)
150         return;
151     willChangeForm();
152     if (m_form)
153         m_form->removeFormElement(this);
154     m_form = makeWeakPtr(newForm);
155     if (newForm)
156         newForm->registerFormElement(this);
157     didChangeForm();
158 }
159
160 void FormAssociatedElement::willChangeForm()
161 {
162 }
163
164 void FormAssociatedElement::didChangeForm()
165 {
166 }
167
168 void FormAssociatedElement::formWillBeDestroyed()
169 {
170     ASSERT(m_form);
171     if (!m_form)
172         return;
173     willChangeForm();
174     m_form = nullptr;
175     didChangeForm();
176 }
177
178 void FormAssociatedElement::resetFormOwner()
179 {
180     RefPtr<HTMLFormElement> originalForm = m_form.get();
181     setForm(findAssociatedForm(&asHTMLElement(), originalForm.get()));
182     HTMLElement& element = asHTMLElement();
183     auto* newForm = m_form.get();
184     if (newForm && newForm != originalForm && newForm->isConnected())
185         element.document().didAssociateFormControl(element);
186 }
187
188 void FormAssociatedElement::formAttributeChanged()
189 {
190     HTMLElement& element = asHTMLElement();
191     if (!element.hasAttributeWithoutSynchronization(formAttr)) {
192         // The form attribute removed. We need to reset form owner here.
193         RefPtr<HTMLFormElement> originalForm = m_form.get();
194         // FIXME: Why does this not pass originalForm to findClosestFormAncestor?
195         setForm(HTMLFormElement::findClosestFormAncestor(element));
196         auto* newForm = m_form.get();
197         if (newForm && newForm != originalForm && newForm->isConnected())
198             element.document().didAssociateFormControl(element);
199         m_formAttributeTargetObserver = nullptr;
200     } else {
201         resetFormOwner();
202         if (element.isConnected())
203             resetFormAttributeTargetObserver();
204     }
205 }
206
207 bool FormAssociatedElement::customError() const
208 {
209     return willValidate() && !m_customValidationMessage.isEmpty();
210 }
211
212 bool FormAssociatedElement::hasBadInput() const
213 {
214     return false;
215 }
216
217 bool FormAssociatedElement::patternMismatch() const
218 {
219     return false;
220 }
221
222 bool FormAssociatedElement::rangeOverflow() const
223 {
224     return false;
225 }
226
227 bool FormAssociatedElement::rangeUnderflow() const
228 {
229     return false;
230 }
231
232 bool FormAssociatedElement::stepMismatch() const
233 {
234     return false;
235 }
236
237 bool FormAssociatedElement::tooShort() const
238 {
239     return false;
240 }
241
242 bool FormAssociatedElement::tooLong() const
243 {
244     return false;
245 }
246
247 bool FormAssociatedElement::typeMismatch() const
248 {
249     return false;
250 }
251
252 bool FormAssociatedElement::isValid() const
253 {
254     bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow()
255         || tooShort() || tooLong() || patternMismatch() || valueMissing() || hasBadInput() || customError();
256     return !someError;
257 }
258
259 bool FormAssociatedElement::valueMissing() const
260 {
261     return false;
262 }
263
264 String FormAssociatedElement::customValidationMessage() const
265 {
266     return m_customValidationMessage;
267 }
268
269 String FormAssociatedElement::validationMessage() const
270 {
271     return customError() ? m_customValidationMessage : String();
272 }
273
274 void FormAssociatedElement::setCustomValidity(const String& error)
275 {
276     m_customValidationMessage = error;
277 }
278
279 void FormAssociatedElement::resetFormAttributeTargetObserver()
280 {
281     ASSERT_WITH_SECURITY_IMPLICATION(asHTMLElement().isConnected());
282     m_formAttributeTargetObserver = std::make_unique<FormAttributeTargetObserver>(asHTMLElement().attributeWithoutSynchronization(formAttr), *this);
283 }
284
285 void FormAssociatedElement::formAttributeTargetChanged()
286 {
287     resetFormOwner();
288 }
289
290 const AtomicString& FormAssociatedElement::name() const
291 {
292     const AtomicString& name = asHTMLElement().getNameAttribute();
293     return name.isNull() ? emptyAtom() : name;
294 }
295
296 bool FormAssociatedElement::isFormControlElementWithState() const
297 {
298     return false;
299 }
300
301 FormAttributeTargetObserver::FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement& element)
302     : IdTargetObserver(element.asHTMLElement().treeScope().idTargetObserverRegistry(), id)
303     , m_element(element)
304 {
305 }
306
307 void FormAttributeTargetObserver::idTargetChanged()
308 {
309     m_element.formAttributeTargetChanged();
310 }
311
312 } // namespace Webcore