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 "FormAssociatedElement.h"
27
28#include "EditorClient.h"
29#include "FormController.h"
30#include "Frame.h"
31#include "HTMLFormControlElement.h"
32#include "HTMLFormElement.h"
33#include "HTMLNames.h"
34#include "HTMLObjectElement.h"
35#include "IdTargetObserver.h"
36#include "ValidityState.h"
37
38namespace WebCore {
39
40using namespace HTMLNames;
41
42class FormAttributeTargetObserver : IdTargetObserver {
43    WTF_MAKE_FAST_ALLOCATED;
44public:
45    static PassOwnPtr<FormAttributeTargetObserver> create(const AtomicString& id, FormAssociatedElement*);
46    virtual void idTargetChanged() OVERRIDE;
47
48private:
49    FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement*);
50
51    FormAssociatedElement* m_element;
52};
53
54FormAssociatedElement::FormAssociatedElement()
55    : m_form(0)
56{
57}
58
59FormAssociatedElement::~FormAssociatedElement()
60{
61    setForm(0);
62}
63
64ValidityState* FormAssociatedElement::validity()
65{
66    if (!m_validityState)
67        m_validityState = ValidityState::create(this);
68
69    return m_validityState.get();
70}
71
72void FormAssociatedElement::didMoveToNewDocument(Document* oldDocument)
73{
74    HTMLElement* element = toHTMLElement(this);
75    if (oldDocument && element->fastHasAttribute(formAttr))
76        resetFormAttributeTargetObserver();
77}
78
79void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint)
80{
81    resetFormOwner();
82    if (!insertionPoint->inDocument())
83        return;
84
85    HTMLElement* element = toHTMLElement(this);
86    if (element->fastHasAttribute(formAttr))
87        resetFormAttributeTargetObserver();
88}
89
90void FormAssociatedElement::removedFrom(ContainerNode* insertionPoint)
91{
92    HTMLElement* element = toHTMLElement(this);
93    if (insertionPoint->inDocument() && element->fastHasAttribute(formAttr))
94        m_formAttributeTargetObserver = nullptr;
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    if (m_form && element->highestAncestor() != m_form->highestAncestor())
98        setForm(0);
99}
100
101HTMLFormElement* FormAssociatedElement::findAssociatedForm(const HTMLElement* element, HTMLFormElement* currentAssociatedForm)
102{
103    const AtomicString& formId(element->fastGetAttribute(formAttr));
104    if (!formId.isNull() && element->inDocument()) {
105        // The HTML5 spec says that the element should be associated with
106        // the first element in the document to have an ID that equal to
107        // the value of form attribute, so we put the result of
108        // treeScope()->getElementById() over the given element.
109        HTMLFormElement* newForm = 0;
110        Element* newFormCandidate = element->treeScope()->getElementById(formId);
111        if (newFormCandidate && newFormCandidate->hasTagName(formTag))
112            newForm = static_cast<HTMLFormElement*>(newFormCandidate);
113        return newForm;
114    }
115
116    if (!currentAssociatedForm)
117        return element->findFormAncestor();
118
119    return currentAssociatedForm;
120}
121
122void FormAssociatedElement::formRemovedFromTree(const Node* formRoot)
123{
124    ASSERT(m_form);
125    if (toHTMLElement(this)->highestAncestor() != formRoot)
126        setForm(0);
127}
128
129void FormAssociatedElement::setForm(HTMLFormElement* newForm)
130{
131    if (m_form == newForm)
132        return;
133    willChangeForm();
134    if (m_form)
135        m_form->removeFormElement(this);
136    m_form = newForm;
137    if (m_form)
138        m_form->registerFormElement(this);
139    didChangeForm();
140}
141
142void FormAssociatedElement::willChangeForm()
143{
144}
145
146void FormAssociatedElement::didChangeForm()
147{
148}
149
150void FormAssociatedElement::formWillBeDestroyed()
151{
152    ASSERT(m_form);
153    if (!m_form)
154        return;
155    willChangeForm();
156    m_form = 0;
157    didChangeForm();
158}
159
160void FormAssociatedElement::resetFormOwner()
161{
162    HTMLFormElement* originalForm = m_form;
163    setForm(findAssociatedForm(toHTMLElement(this), m_form));
164    HTMLElement* element = toHTMLElement(this);
165    if (m_form && m_form != originalForm && m_form->inDocument())
166        element->document()->didAssociateFormControl(element);
167}
168
169void FormAssociatedElement::formAttributeChanged()
170{
171    HTMLElement* element = toHTMLElement(this);
172    if (!element->fastHasAttribute(formAttr)) {
173        // The form attribute removed. We need to reset form owner here.
174        HTMLFormElement* originalForm = m_form;
175        setForm(element->findFormAncestor());
176        HTMLElement* element = toHTMLElement(this);
177        if (m_form && m_form != originalForm && m_form->inDocument())
178            element->document()->didAssociateFormControl(element);
179        m_formAttributeTargetObserver = nullptr;
180    } else {
181        resetFormOwner();
182        if (element->inDocument())
183            resetFormAttributeTargetObserver();
184    }
185}
186
187bool FormAssociatedElement::customError() const
188{
189    const HTMLElement* element = toHTMLElement(this);
190    return element->willValidate() && !m_customValidationMessage.isEmpty();
191}
192
193bool FormAssociatedElement::hasBadInput() const
194{
195    return false;
196}
197
198bool FormAssociatedElement::patternMismatch() const
199{
200    return false;
201}
202
203bool FormAssociatedElement::rangeOverflow() const
204{
205    return false;
206}
207
208bool FormAssociatedElement::rangeUnderflow() const
209{
210    return false;
211}
212
213bool FormAssociatedElement::stepMismatch() const
214{
215    return false;
216}
217
218bool FormAssociatedElement::tooLong() const
219{
220    return false;
221}
222
223bool FormAssociatedElement::typeMismatch() const
224{
225    return false;
226}
227
228bool FormAssociatedElement::valid() const
229{
230    bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow()
231        || tooLong() || patternMismatch() || valueMissing() || hasBadInput() || customError();
232    return !someError;
233}
234
235bool FormAssociatedElement::valueMissing() const
236{
237    return false;
238}
239
240String FormAssociatedElement::customValidationMessage() const
241{
242    return m_customValidationMessage;
243}
244
245String FormAssociatedElement::validationMessage() const
246{
247    return customError() ? m_customValidationMessage : String();
248}
249
250void FormAssociatedElement::setCustomValidity(const String& error)
251{
252    m_customValidationMessage = error;
253}
254
255void FormAssociatedElement::resetFormAttributeTargetObserver()
256{
257    ASSERT(toHTMLElement(this)->inDocument());
258    m_formAttributeTargetObserver = FormAttributeTargetObserver::create(toHTMLElement(this)->fastGetAttribute(formAttr), this);
259}
260
261void FormAssociatedElement::formAttributeTargetChanged()
262{
263    resetFormOwner();
264}
265
266const AtomicString& FormAssociatedElement::name() const
267{
268    const AtomicString& name = toHTMLElement(this)->getNameAttribute();
269    return name.isNull() ? emptyAtom : name;
270}
271
272bool FormAssociatedElement::isFormControlElementWithState() const
273{
274    return false;
275}
276
277const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement)
278{
279    if (associatedElement->isFormControlElement())
280        return static_cast<const HTMLFormControlElement*>(associatedElement);
281    // Assumes the element is an HTMLObjectElement
282    const HTMLElement* element = static_cast<const HTMLObjectElement*>(associatedElement);
283    ASSERT(element->hasTagName(objectTag));
284    return element;
285}
286
287HTMLElement* toHTMLElement(FormAssociatedElement* associatedElement)
288{
289    return const_cast<HTMLElement*>(toHTMLElement(static_cast<const FormAssociatedElement*>(associatedElement)));
290}
291
292PassOwnPtr<FormAttributeTargetObserver> FormAttributeTargetObserver::create(const AtomicString& id, FormAssociatedElement* element)
293{
294    return adoptPtr(new FormAttributeTargetObserver(id, element));
295}
296
297FormAttributeTargetObserver::FormAttributeTargetObserver(const AtomicString& id, FormAssociatedElement* element)
298    : IdTargetObserver(toHTMLElement(element)->treeScope()->idTargetObserverRegistry(), id)
299    , m_element(element)
300{
301}
302
303void FormAttributeTargetObserver::idTargetChanged()
304{
305    m_element->formAttributeTargetChanged();
306}
307
308} // namespace Webcore
309