Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / forms / RadioButtonGroupScope.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20
21 #include "config.h"
22 #include "core/html/forms/RadioButtonGroupScope.h"
23
24 #include "core/html/HTMLInputElement.h"
25 #include "wtf/HashSet.h"
26
27 namespace WebCore {
28
29 class RadioButtonGroup {
30     WTF_MAKE_FAST_ALLOCATED;
31 public:
32     static PassOwnPtr<RadioButtonGroup> create();
33     bool isEmpty() const { return m_members.isEmpty(); }
34     bool isRequired() const { return m_requiredCount; }
35     HTMLInputElement* checkedButton() const { return m_checkedButton; }
36     void add(HTMLInputElement*);
37     void updateCheckedState(HTMLInputElement*);
38     void requiredAttributeChanged(HTMLInputElement*);
39     void remove(HTMLInputElement*);
40     bool contains(HTMLInputElement*) const;
41
42 private:
43     RadioButtonGroup();
44     void setNeedsValidityCheckForAllButtons();
45     bool isValid() const;
46     void setCheckedButton(HTMLInputElement*);
47
48     HashSet<HTMLInputElement*> m_members;
49     HTMLInputElement* m_checkedButton;
50     size_t m_requiredCount;
51 };
52
53 RadioButtonGroup::RadioButtonGroup()
54     : m_checkedButton(0)
55     , m_requiredCount(0)
56 {
57 }
58
59 PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create()
60 {
61     return adoptPtr(new RadioButtonGroup);
62 }
63
64 inline bool RadioButtonGroup::isValid() const
65 {
66     return !isRequired() || m_checkedButton;
67 }
68
69 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button)
70 {
71     HTMLInputElement* oldCheckedButton = m_checkedButton;
72     if (oldCheckedButton == button)
73         return;
74     m_checkedButton = button;
75     if (oldCheckedButton)
76         oldCheckedButton->setChecked(false);
77 }
78
79 void RadioButtonGroup::add(HTMLInputElement* button)
80 {
81     ASSERT(button->isRadioButton());
82     if (!m_members.add(button).isNewEntry)
83         return;
84     bool groupWasValid = isValid();
85     if (button->isRequired())
86         ++m_requiredCount;
87     if (button->checked())
88         setCheckedButton(button);
89
90     bool groupIsValid = isValid();
91     if (groupWasValid != groupIsValid) {
92         setNeedsValidityCheckForAllButtons();
93     } else if (!groupIsValid) {
94         // A radio button not in a group is always valid. We need to make it
95         // invalid only if the group is invalid.
96         button->setNeedsValidityCheck();
97     }
98 }
99
100 void RadioButtonGroup::updateCheckedState(HTMLInputElement* button)
101 {
102     ASSERT(button->isRadioButton());
103     ASSERT(m_members.contains(button));
104     bool wasValid = isValid();
105     if (button->checked()) {
106         setCheckedButton(button);
107     } else {
108         if (m_checkedButton == button)
109             m_checkedButton = 0;
110     }
111     if (wasValid != isValid())
112         setNeedsValidityCheckForAllButtons();
113 }
114
115 void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button)
116 {
117     ASSERT(button->isRadioButton());
118     ASSERT(m_members.contains(button));
119     bool wasValid = isValid();
120     if (button->isRequired()) {
121         ++m_requiredCount;
122     } else {
123         ASSERT(m_requiredCount);
124         --m_requiredCount;
125     }
126     if (wasValid != isValid())
127         setNeedsValidityCheckForAllButtons();
128 }
129
130 void RadioButtonGroup::remove(HTMLInputElement* button)
131 {
132     ASSERT(button->isRadioButton());
133     HashSet<HTMLInputElement*>::iterator it = m_members.find(button);
134     if (it == m_members.end())
135         return;
136     bool wasValid = isValid();
137     m_members.remove(it);
138     if (button->isRequired()) {
139         ASSERT(m_requiredCount);
140         --m_requiredCount;
141     }
142     if (m_checkedButton == button)
143         m_checkedButton = 0;
144
145     if (m_members.isEmpty()) {
146         ASSERT(!m_requiredCount);
147         ASSERT(!m_checkedButton);
148     } else if (wasValid != isValid()) {
149         setNeedsValidityCheckForAllButtons();
150     }
151     if (!wasValid) {
152         // A radio button not in a group is always valid. We need to make it
153         // valid only if the group was invalid.
154         button->setNeedsValidityCheck();
155     }
156 }
157
158 void RadioButtonGroup::setNeedsValidityCheckForAllButtons()
159 {
160     typedef HashSet<HTMLInputElement*>::const_iterator Iterator;
161     Iterator end = m_members.end();
162     for (Iterator it = m_members.begin(); it != end; ++it) {
163         HTMLInputElement* button = *it;
164         ASSERT(button->isRadioButton());
165         button->setNeedsValidityCheck();
166     }
167 }
168
169 bool RadioButtonGroup::contains(HTMLInputElement* button) const
170 {
171     return m_members.contains(button);
172 }
173
174 // ----------------------------------------------------------------
175
176 // Explicity define empty constructor and destructor in order to prevent the
177 // compiler from generating them as inlines. So we don't need to to define
178 // RadioButtonGroup in the header.
179 RadioButtonGroupScope::RadioButtonGroupScope()
180 {
181 }
182
183 RadioButtonGroupScope::~RadioButtonGroupScope()
184 {
185 }
186
187 void RadioButtonGroupScope::addButton(HTMLInputElement* element)
188 {
189     ASSERT(element->isRadioButton());
190     if (element->name().isEmpty())
191         return;
192
193     if (!m_nameToGroupMap)
194         m_nameToGroupMap = adoptPtr(new NameToGroupMap);
195
196     OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name(), PassOwnPtr<RadioButtonGroup>()).storedValue->value;
197     if (!group)
198         group = RadioButtonGroup::create();
199     group->add(element);
200 }
201
202 void RadioButtonGroupScope::updateCheckedState(HTMLInputElement* element)
203 {
204     ASSERT(element->isRadioButton());
205     if (element->name().isEmpty())
206         return;
207     ASSERT(m_nameToGroupMap);
208     if (!m_nameToGroupMap)
209         return;
210     RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
211     ASSERT(group);
212     group->updateCheckedState(element);
213 }
214
215 void RadioButtonGroupScope::requiredAttributeChanged(HTMLInputElement* element)
216 {
217     ASSERT(element->isRadioButton());
218     if (element->name().isEmpty())
219         return;
220     ASSERT(m_nameToGroupMap);
221     if (!m_nameToGroupMap)
222         return;
223     RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
224     ASSERT(group);
225     group->requiredAttributeChanged(element);
226 }
227
228 HTMLInputElement* RadioButtonGroupScope::checkedButtonForGroup(const AtomicString& name) const
229 {
230     if (!m_nameToGroupMap)
231         return 0;
232     RadioButtonGroup* group = m_nameToGroupMap->get(name);
233     return group ? group->checkedButton() : 0;
234 }
235
236 bool RadioButtonGroupScope::isInRequiredGroup(HTMLInputElement* element) const
237 {
238     ASSERT(element->isRadioButton());
239     if (element->name().isEmpty())
240         return false;
241     if (!m_nameToGroupMap)
242         return false;
243     RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
244     return group && group->isRequired() && group->contains(element);
245 }
246
247 void RadioButtonGroupScope::removeButton(HTMLInputElement* element)
248 {
249     ASSERT(element->isRadioButton());
250     if (element->name().isEmpty())
251         return;
252     if (!m_nameToGroupMap)
253         return;
254
255     RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
256     if (!group)
257         return;
258     group->remove(element);
259     if (group->isEmpty()) {
260         // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for
261         // better performance.
262         ASSERT(!group->isRequired());
263         ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton());
264     }
265 }
266
267 } // namespace