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, 2007 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 "core/html/FormAssociatedElement.h"
27
28#include "core/HTMLNames.h"
29#include "core/dom/IdTargetObserver.h"
30#include "core/dom/NodeTraversal.h"
31#include "core/html/HTMLFormControlElement.h"
32#include "core/html/HTMLFormElement.h"
33#include "core/html/HTMLLabelElement.h"
34#include "core/html/HTMLObjectElement.h"
35#include "core/html/ValidityState.h"
36
37namespace blink {
38
39using namespace HTMLNames;
40
41class FormAttributeTargetObserver : public IdTargetObserver {
42    WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
43public:
44    static PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> create(const AtomicString& id, FormAssociatedElement*);
45    virtual void trace(Visitor*) OVERRIDE;
46    virtual void idTargetChanged() OVERRIDE;
47
48private:
49    FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement*);
50
51    RawPtrWillBeMember<FormAssociatedElement> m_element;
52};
53
54FormAssociatedElement::FormAssociatedElement()
55    : m_formWasSetByParser(false)
56{
57}
58
59FormAssociatedElement::~FormAssociatedElement()
60{
61    // We can't call setForm here because it contains virtual calls.
62}
63
64void FormAssociatedElement::trace(Visitor* visitor)
65{
66    visitor->trace(m_formAttributeTargetObserver);
67    visitor->trace(m_form);
68    visitor->trace(m_validityState);
69}
70
71ValidityState* FormAssociatedElement::validity()
72{
73    if (!m_validityState)
74        m_validityState = ValidityState::create(this);
75
76    return m_validityState.get();
77}
78
79void FormAssociatedElement::didMoveToNewDocument(Document& oldDocument)
80{
81    HTMLElement* element = toHTMLElement(this);
82    if (element->fastHasAttribute(formAttr))
83        setFormAttributeTargetObserver(nullptr);
84}
85
86void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint)
87{
88    if (!m_formWasSetByParser || !m_form || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get()))
89        resetFormOwner();
90
91    if (!insertionPoint->inDocument())
92        return;
93
94    HTMLElement* element = toHTMLElement(this);
95    if (element->fastHasAttribute(formAttr))
96        resetFormAttributeTargetObserver();
97}
98
99void FormAssociatedElement::removedFrom(ContainerNode* insertionPoint)
100{
101    HTMLElement* element = toHTMLElement(this);
102    if (insertionPoint->inDocument() && element->fastHasAttribute(formAttr))
103        setFormAttributeTargetObserver(nullptr);
104    // If the form and element are both in the same tree, preserve the connection to the form.
105    // Otherwise, null out our form and remove ourselves from the form's list of elements.
106    if (m_form && NodeTraversal::highestAncestorOrSelf(*element) != NodeTraversal::highestAncestorOrSelf(*m_form.get()))
107        resetFormOwner();
108}
109
110HTMLFormElement* FormAssociatedElement::findAssociatedForm(const HTMLElement* element)
111{
112    const AtomicString& formId(element->fastGetAttribute(formAttr));
113    // 3. If the element is reassociateable, has a form content attribute, and
114    // is itself in a Document, then run these substeps:
115    if (!formId.isNull() && element->inDocument()) {
116        // 3.1. If the first element in the Document to have an ID that is
117        // case-sensitively equal to the element's form content attribute's
118        // value is a form element, then associate the form-associated element
119        // with that form element.
120        // 3.2. Abort the "reset the form owner" steps.
121        Element* newFormCandidate = element->treeScope().getElementById(formId);
122        return isHTMLFormElement(newFormCandidate) ? toHTMLFormElement(newFormCandidate) : 0;
123    }
124    // 4. Otherwise, if the form-associated element in question has an ancestor
125    // form element, then associate the form-associated element with the nearest
126    // such ancestor form element.
127    return element->findFormAncestor();
128}
129
130void FormAssociatedElement::formRemovedFromTree(const Node& formRoot)
131{
132    ASSERT(m_form);
133    if (NodeTraversal::highestAncestorOrSelf(toHTMLElement(*this)) == formRoot)
134        return;
135    resetFormOwner();
136}
137
138void FormAssociatedElement::associateByParser(HTMLFormElement* form)
139{
140    if (form && form->inDocument()) {
141        m_formWasSetByParser = true;
142        setForm(form);
143        form->didAssociateByParser();
144    }
145}
146
147void FormAssociatedElement::setForm(HTMLFormElement* newForm)
148{
149    if (m_form.get() == newForm)
150        return;
151    willChangeForm();
152    if (m_form)
153        m_form->disassociate(*this);
154    if (newForm) {
155#if ENABLE(OILPAN)
156        m_form = newForm;
157#else
158        m_form = newForm->createWeakPtr();
159#endif
160        m_form->associate(*this);
161    } else {
162#if ENABLE(OILPAN)
163        m_form = nullptr;
164#else
165        m_form = WeakPtr<HTMLFormElement>();
166#endif
167    }
168    didChangeForm();
169}
170
171void FormAssociatedElement::willChangeForm()
172{
173}
174
175void FormAssociatedElement::didChangeForm()
176{
177}
178
179void FormAssociatedElement::resetFormOwner()
180{
181    m_formWasSetByParser = false;
182    HTMLElement* element = toHTMLElement(this);
183    const AtomicString& formId(element->fastGetAttribute(formAttr));
184    HTMLFormElement* nearestForm = element->findFormAncestor();
185    // 1. If the element's form owner is not null, and either the element is not
186    // reassociateable or its form content attribute is not present, and the
187    // element's form owner is its nearest form element ancestor after the
188    // change to the ancestor chain, then do nothing, and abort these steps.
189    if (m_form && formId.isNull() && m_form.get() == nearestForm)
190        return;
191
192    HTMLFormElement* originalForm = m_form.get();
193    setForm(findAssociatedForm(element));
194    // FIXME: Move didAssociateFormControl call to didChangeForm or
195    // HTMLFormElement::associate.
196    if (m_form && m_form.get() != originalForm && m_form->inDocument())
197        element->document().didAssociateFormControl(element);
198}
199
200void FormAssociatedElement::formAttributeChanged()
201{
202    resetFormOwner();
203    resetFormAttributeTargetObserver();
204}
205
206bool FormAssociatedElement::customError() const
207{
208    const HTMLElement* element = toHTMLElement(this);
209    return element->willValidate() && !m_customValidationMessage.isEmpty();
210}
211
212bool FormAssociatedElement::hasBadInput() const
213{
214    return false;
215}
216
217bool FormAssociatedElement::patternMismatch() const
218{
219    return false;
220}
221
222bool FormAssociatedElement::rangeOverflow() const
223{
224    return false;
225}
226
227bool FormAssociatedElement::rangeUnderflow() const
228{
229    return false;
230}
231
232bool FormAssociatedElement::stepMismatch() 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::valid() const
248{
249    bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow()
250        || 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::setFormAttributeTargetObserver(PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> newObserver)
275{
276    if (m_formAttributeTargetObserver)
277        m_formAttributeTargetObserver->unregister();
278    m_formAttributeTargetObserver = newObserver;
279}
280
281void FormAssociatedElement::resetFormAttributeTargetObserver()
282{
283    HTMLElement* element = toHTMLElement(this);
284    const AtomicString& formId(element->fastGetAttribute(formAttr));
285    if (!formId.isNull() && element->inDocument())
286        setFormAttributeTargetObserver(FormAttributeTargetObserver::create(formId, this));
287    else
288        setFormAttributeTargetObserver(nullptr);
289}
290
291void FormAssociatedElement::formAttributeTargetChanged()
292{
293    resetFormOwner();
294}
295
296const AtomicString& FormAssociatedElement::name() const
297{
298    const AtomicString& name = toHTMLElement(this)->getNameAttribute();
299    return name.isNull() ? emptyAtom : name;
300}
301
302bool FormAssociatedElement::isFormControlElementWithState() const
303{
304    return false;
305}
306
307const HTMLElement& toHTMLElement(const FormAssociatedElement& associatedElement)
308{
309    if (associatedElement.isFormControlElement())
310        return toHTMLFormControlElement(associatedElement);
311    else if (associatedElement.isLabelElement())
312        return toHTMLLabelElement(associatedElement);
313    else
314        return toHTMLObjectElement(associatedElement);
315}
316
317const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement)
318{
319    ASSERT(associatedElement);
320    return &toHTMLElement(*associatedElement);
321}
322
323HTMLElement* toHTMLElement(FormAssociatedElement* associatedElement)
324{
325    return const_cast<HTMLElement*>(toHTMLElement(static_cast<const FormAssociatedElement*>(associatedElement)));
326}
327
328HTMLElement& toHTMLElement(FormAssociatedElement& associatedElement)
329{
330    return const_cast<HTMLElement&>(toHTMLElement(static_cast<const FormAssociatedElement&>(associatedElement)));
331}
332
333PassOwnPtrWillBeRawPtr<FormAttributeTargetObserver> FormAttributeTargetObserver::create(const AtomicString& id, FormAssociatedElement* element)
334{
335    return adoptPtrWillBeNoop(new FormAttributeTargetObserver(id, element));
336}
337
338FormAttributeTargetObserver::FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement* element)
339    : IdTargetObserver(toHTMLElement(element)->treeScope().idTargetObserverRegistry(), id)
340    , m_element(element)
341{
342}
343
344void FormAttributeTargetObserver::trace(Visitor* visitor)
345{
346    visitor->trace(m_element);
347    IdTargetObserver::trace(visitor);
348}
349
350void FormAttributeTargetObserver::idTargetChanged()
351{
352    m_element->formAttributeTargetChanged();
353}
354
355} // namespace blink
356