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 "HTMLFormControlElement.h"
29#include "HTMLFormElement.h"
30#include "HTMLNames.h"
31#include "HTMLObjectElement.h"
32#include "ValidityState.h"
33
34namespace WebCore {
35
36using namespace HTMLNames;
37
38FormAssociatedElement::FormAssociatedElement(HTMLFormElement* form)
39    : m_form(form)
40{
41}
42
43FormAssociatedElement::~FormAssociatedElement()
44{
45}
46
47ValidityState* FormAssociatedElement::validity()
48{
49    if (!m_validityState)
50        m_validityState = ValidityState::create(this);
51
52    return m_validityState.get();
53}
54
55void FormAssociatedElement::willMoveToNewOwnerDocument()
56{
57    HTMLElement* element = toHTMLElement(this);
58    if (element->fastHasAttribute(formAttr))
59        element->document()->unregisterFormElementWithFormAttribute(this);
60}
61
62void FormAssociatedElement::insertedIntoDocument()
63{
64    HTMLElement* element = toHTMLElement(this);
65    if (element->fastHasAttribute(formAttr))
66        element->document()->registerFormElementWithFormAttribute(this);
67}
68
69void FormAssociatedElement::removedFromDocument()
70{
71    HTMLElement* element = toHTMLElement(this);
72    if (element->fastHasAttribute(formAttr))
73        element->document()->unregisterFormElementWithFormAttribute(this);
74}
75
76void FormAssociatedElement::insertedIntoTree()
77{
78    HTMLElement* element = toHTMLElement(this);
79    if (element->fastHasAttribute(formAttr)) {
80        Element* formElement = element->document()->getElementById(element->fastGetAttribute(formAttr));
81        if (formElement && formElement->hasTagName(formTag)) {
82            if (m_form)
83                m_form->removeFormElement(this);
84            m_form = static_cast<HTMLFormElement*>(formElement);
85            m_form->registerFormElement(this);
86        }
87    }
88    if (!m_form) {
89        // This handles the case of a new form element being created by
90        // JavaScript and inserted inside a form.  In the case of the parser
91        // setting a form, we will already have a non-null value for m_form,
92        // and so we don't need to do anything.
93        m_form = element->findFormAncestor();
94        if (m_form)
95            m_form->registerFormElement(this);
96    }
97}
98
99static inline Node* findRoot(Node* n)
100{
101    Node* root = n;
102    for (; n; n = n->parentNode())
103        root = n;
104    return root;
105}
106
107void FormAssociatedElement::removedFromTree()
108{
109    HTMLElement* element = toHTMLElement(this);
110
111    // If the form and element are both in the same tree, preserve the connection to the form.
112    // Otherwise, null out our form and remove ourselves from the form's list of elements.
113    if (m_form && findRoot(element) != findRoot(m_form))
114        removeFromForm();
115}
116
117void FormAssociatedElement::removeFromForm()
118{
119    if (!m_form)
120        return;
121    m_form->removeFormElement(this);
122    m_form = 0;
123}
124
125void FormAssociatedElement::resetFormOwner(HTMLFormElement* form)
126{
127    HTMLElement* element = toHTMLElement(this);
128    const AtomicString& formId(element->fastGetAttribute(formAttr));
129    if (m_form) {
130        if (formId.isNull())
131            return;
132        m_form->removeFormElement(this);
133    }
134    m_form = 0;
135    if (!formId.isNull() && element->inDocument()) {
136        // The HTML5 spec says that the element should be associated with
137        // the first element in the document to have an ID that equal to
138        // the value of form attribute, so we put the result of
139        // document()->getElementById() over the given element.
140        Element* firstElement = element->document()->getElementById(formId);
141        if (firstElement && firstElement->hasTagName(formTag))
142            m_form = static_cast<HTMLFormElement*>(firstElement);
143        else
144            m_form = form;
145    } else
146        m_form = element->findFormAncestor();
147    if (m_form)
148        m_form->registerFormElement(this);
149}
150
151void FormAssociatedElement::formAttributeChanged()
152{
153    HTMLElement* element = toHTMLElement(this);
154    if (!element->fastHasAttribute(formAttr)) {
155        // The form attribute removed. We need to reset form owner here.
156        if (m_form)
157            m_form->removeFormElement(this);
158        m_form = element->findFormAncestor();
159        if (m_form)
160            form()->registerFormElement(this);
161        element->document()->unregisterFormElementWithFormAttribute(this);
162    } else
163        resetFormOwner(0);
164}
165
166const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement)
167{
168    if (associatedElement->isFormControlElement())
169        return static_cast<const HTMLFormControlElement*>(associatedElement);
170    // Assumes the element is an HTMLObjectElement
171    const HTMLElement* element = static_cast<const HTMLObjectElement*>(associatedElement);
172    ASSERT(element->hasTagName(objectTag));
173    return element;
174}
175
176HTMLElement* toHTMLElement(FormAssociatedElement* associatedElement)
177{
178    return const_cast<HTMLElement*>(toHTMLElement(static_cast<const FormAssociatedElement*>(associatedElement)));
179}
180
181} // namespace Webcore
182