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
38namespace WebCore {
39
40using namespace HTMLNames;
41
42class FormAttributeTargetObserver final : private IdTargetObserver {
43 WTF_MAKE_FAST_ALLOCATED;
44public:
45 FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement&);
46
47private:
48 void idTargetChanged() override;
49
50 FormAssociatedElement& m_element;
51};
52
53FormAssociatedElement::FormAssociatedElement(HTMLFormElement* form)
54 : m_form(nullptr)
55 , m_formSetByParser(makeWeakPtr(form))
56{
57}
58
59FormAssociatedElement::~FormAssociatedElement()
60{
61 setForm(nullptr);
62}
63
64void FormAssociatedElement::didMoveToNewDocument(Document&)
65{
66 HTMLElement& element = asHTMLElement();
67 if (element.hasAttributeWithoutSynchronization(formAttr) && element.isConnected())
68 resetFormAttributeTargetObserver();
69}
70
71void 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
91void 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
102HTMLFormElement* 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
122void FormAssociatedElement::formOwnerRemovedFromTree(const Node& formRoot)
123{
124 ASSERT(m_form);
125 // Can't use RefPtr here beacuse this function might be called inside ~ShadowRoot via addChildNodesToDeletionQueue. See webkit.org/b/189493.
126 Node* rootNode = &asHTMLElement();
127 for (auto* ancestor = asHTMLElement().parentNode(); ancestor; ancestor = ancestor->parentNode()) {
128 if (ancestor == m_form) {
129 // Form is our ancestor so we don't need to reset our owner, we also no longer
130 // need an id observer since we are no longer connected.
131 m_formAttributeTargetObserver = nullptr;
132 return;
133 }
134 rootNode = ancestor;
135 }
136
137 // We are no longer in the same tree as our form owner so clear our owner.
138 if (rootNode != &formRoot)
139 setForm(nullptr);
140}
141
142void FormAssociatedElement::setForm(HTMLFormElement* newForm)
143{
144 if (m_form == newForm)
145 return;
146 willChangeForm();
147 if (m_form)
148 m_form->removeFormElement(this);
149 m_form = makeWeakPtr(newForm);
150 if (newForm)
151 newForm->registerFormElement(this);
152 didChangeForm();
153}
154
155void FormAssociatedElement::willChangeForm()
156{
157}
158
159void FormAssociatedElement::didChangeForm()
160{
161}
162
163void FormAssociatedElement::formWillBeDestroyed()
164{
165 ASSERT(m_form);
166 if (!m_form)
167 return;
168 willChangeForm();
169 m_form = nullptr;
170 didChangeForm();
171}
172
173void FormAssociatedElement::resetFormOwner()
174{
175 RefPtr<HTMLFormElement> originalForm = m_form.get();
176 setForm(findAssociatedForm(&asHTMLElement(), originalForm.get()));
177 HTMLElement& element = asHTMLElement();
178 auto* newForm = m_form.get();
179 if (newForm && newForm != originalForm && newForm->isConnected())
180 element.document().didAssociateFormControl(element);
181}
182
183void FormAssociatedElement::formAttributeChanged()
184{
185 HTMLElement& element = asHTMLElement();
186 if (!element.hasAttributeWithoutSynchronization(formAttr)) {
187 // The form attribute removed. We need to reset form owner here.
188 RefPtr<HTMLFormElement> originalForm = m_form.get();
189 // FIXME: Why does this not pass originalForm to findClosestFormAncestor?
190 setForm(HTMLFormElement::findClosestFormAncestor(element));
191 auto* newForm = m_form.get();
192 if (newForm && newForm != originalForm && newForm->isConnected())
193 element.document().didAssociateFormControl(element);
194 m_formAttributeTargetObserver = nullptr;
195 } else {
196 resetFormOwner();
197 if (element.isConnected())
198 resetFormAttributeTargetObserver();
199 }
200}
201
202bool FormAssociatedElement::customError() const
203{
204 return willValidate() && !m_customValidationMessage.isEmpty();
205}
206
207bool FormAssociatedElement::hasBadInput() const
208{
209 return false;
210}
211
212bool FormAssociatedElement::patternMismatch() const
213{
214 return false;
215}
216
217bool FormAssociatedElement::rangeOverflow() const
218{
219 return false;
220}
221
222bool FormAssociatedElement::rangeUnderflow() const
223{
224 return false;
225}
226
227bool FormAssociatedElement::stepMismatch() const
228{
229 return false;
230}
231
232bool FormAssociatedElement::tooShort() const
233{
234 return false;
235}
236
237bool FormAssociatedElement::tooLong() const
238{
239 return false;
240}
241
242bool FormAssociatedElement::typeMismatch() const
243{
244 return false;
245}
246
247bool FormAssociatedElement::isValid() const
248{
249 bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow()
250 || tooShort() || tooLong() || patternMismatch() || valueMissing() || hasBadInput() || customError();
251 return !someError;
252}
253
254bool FormAssociatedElement::valueMissing() const
255{
256 return false;
257}
258
259String FormAssociatedElement::customValidationMessage() const
260{
261 return m_customValidationMessage;
262}
263
264String FormAssociatedElement::validationMessage() const
265{
266 return customError() ? m_customValidationMessage : String();
267}
268
269void FormAssociatedElement::setCustomValidity(const String& error)
270{
271 m_customValidationMessage = error;
272}
273
274void FormAssociatedElement::resetFormAttributeTargetObserver()
275{
276 ASSERT_WITH_SECURITY_IMPLICATION(asHTMLElement().isConnected());
277 m_formAttributeTargetObserver = std::make_unique<FormAttributeTargetObserver>(asHTMLElement().attributeWithoutSynchronization(formAttr), *this);
278}
279
280void FormAssociatedElement::formAttributeTargetChanged()
281{
282 resetFormOwner();
283}
284
285const AtomicString& FormAssociatedElement::name() const
286{
287 const AtomicString& name = asHTMLElement().getNameAttribute();
288 return name.isNull() ? emptyAtom() : name;
289}
290
291bool FormAssociatedElement::isFormControlElementWithState() const
292{
293 return false;
294}
295
296FormAttributeTargetObserver::FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement& element)
297 : IdTargetObserver(element.asHTMLElement().treeScope().idTargetObserverRegistry(), id)
298 , m_element(element)
299{
300}
301
302void FormAttributeTargetObserver::idTargetChanged()
303{
304 m_element.formAttributeTargetChanged();
305}
306
307} // namespace Webcore
308