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, 2010 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 "HTMLFieldSetElement.h"
27
28#include "HTMLCollection.h"
29#include "HTMLLegendElement.h"
30#include "HTMLNames.h"
31#include "HTMLObjectElement.h"
32#include "NodeTraversal.h"
33#include "RenderFieldset.h"
34#include <wtf/StdLibExtras.h>
35
36namespace WebCore {
37
38using namespace HTMLNames;
39
40inline HTMLFieldSetElement::HTMLFieldSetElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
41    : HTMLFormControlElement(tagName, document, form)
42    , m_documentVersion(0)
43{
44    ASSERT(hasTagName(fieldsetTag));
45}
46
47PassRefPtr<HTMLFieldSetElement> HTMLFieldSetElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
48{
49    return adoptRef(new HTMLFieldSetElement(tagName, document, form));
50}
51
52void HTMLFieldSetElement::invalidateDisabledStateUnder(Element* base)
53{
54    for (Element* element = ElementTraversal::firstWithin(base); element; element = ElementTraversal::next(element, base)) {
55        if (element->isFormControlElement())
56            static_cast<HTMLFormControlElement*>(element)->ancestorDisabledStateWasChanged();
57    }
58}
59
60void HTMLFieldSetElement::disabledAttributeChanged()
61{
62    // This element must be updated before the style of nodes in its subtree gets recalculated.
63    HTMLFormControlElement::disabledAttributeChanged();
64    invalidateDisabledStateUnder(this);
65}
66
67void HTMLFieldSetElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
68{
69    HTMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
70    for (Element* element = ElementTraversal::firstWithin(this); element; element = ElementTraversal::nextSkippingChildren(element, this)) {
71        if (element->hasTagName(legendTag))
72            invalidateDisabledStateUnder(element);
73    }
74}
75
76bool HTMLFieldSetElement::supportsFocus() const
77{
78    return HTMLElement::supportsFocus();
79}
80
81const AtomicString& HTMLFieldSetElement::formControlType() const
82{
83    DEFINE_STATIC_LOCAL(const AtomicString, fieldset, ("fieldset", AtomicString::ConstructFromLiteral));
84    return fieldset;
85}
86
87RenderObject* HTMLFieldSetElement::createRenderer(RenderArena* arena, RenderStyle*)
88{
89    return new (arena) RenderFieldset(this);
90}
91
92HTMLLegendElement* HTMLFieldSetElement::legend() const
93{
94    for (Element* child = ElementTraversal::firstWithin(this); child; child = ElementTraversal::nextSkippingChildren(child, this)) {
95        if (child->hasTagName(legendTag))
96            return static_cast<HTMLLegendElement*>(child);
97    }
98    return 0;
99}
100
101PassRefPtr<HTMLCollection> HTMLFieldSetElement::elements()
102{
103    return ensureCachedHTMLCollection(FormControls);
104}
105
106void HTMLFieldSetElement::refreshElementsIfNeeded() const
107{
108    uint64_t docVersion = document()->domTreeVersion();
109    if (m_documentVersion == docVersion)
110        return;
111
112    m_documentVersion = docVersion;
113
114    m_associatedElements.clear();
115
116    for (Element* element = ElementTraversal::firstWithin(this); element; element = ElementTraversal::next(element, this)) {
117        if (element->hasTagName(objectTag)) {
118            m_associatedElements.append(static_cast<HTMLObjectElement*>(element));
119            continue;
120        }
121
122        if (!element->isFormControlElement())
123            continue;
124
125        m_associatedElements.append(static_cast<HTMLFormControlElement*>(element));
126    }
127}
128
129const Vector<FormAssociatedElement*>& HTMLFieldSetElement::associatedElements() const
130{
131    refreshElementsIfNeeded();
132    return m_associatedElements;
133}
134
135unsigned HTMLFieldSetElement::length() const
136{
137    refreshElementsIfNeeded();
138    unsigned len = 0;
139    for (unsigned i = 0; i < m_associatedElements.size(); ++i)
140        if (m_associatedElements[i]->isEnumeratable())
141            ++len;
142    return len;
143}
144
145} // namespace
146