2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
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.
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.
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.
22 #include "core/html/forms/CheckedRadioButtons.h"
24 #include "core/html/HTMLInputElement.h"
25 #include "wtf/HashSet.h"
29 class RadioButtonGroup {
30 WTF_MAKE_FAST_ALLOCATED;
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;
44 void setNeedsValidityCheckForAllButtons();
46 void setCheckedButton(HTMLInputElement*);
48 HashSet<HTMLInputElement*> m_members;
49 HTMLInputElement* m_checkedButton;
50 size_t m_requiredCount;
53 RadioButtonGroup::RadioButtonGroup()
59 PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create()
61 return adoptPtr(new RadioButtonGroup);
64 inline bool RadioButtonGroup::isValid() const
66 return !isRequired() || m_checkedButton;
69 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button)
71 HTMLInputElement* oldCheckedButton = m_checkedButton;
72 if (oldCheckedButton == button)
74 m_checkedButton = button;
76 oldCheckedButton->setChecked(false);
79 void RadioButtonGroup::add(HTMLInputElement* button)
81 ASSERT(button->isRadioButton());
82 if (!m_members.add(button).isNewEntry)
84 bool groupWasValid = isValid();
85 if (button->isRequired())
87 if (button->checked())
88 setCheckedButton(button);
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();
100 void RadioButtonGroup::updateCheckedState(HTMLInputElement* button)
102 ASSERT(button->isRadioButton());
103 ASSERT(m_members.contains(button));
104 bool wasValid = isValid();
105 if (button->checked()) {
106 setCheckedButton(button);
108 if (m_checkedButton == button)
111 if (wasValid != isValid())
112 setNeedsValidityCheckForAllButtons();
115 void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button)
117 ASSERT(button->isRadioButton());
118 ASSERT(m_members.contains(button));
119 bool wasValid = isValid();
120 if (button->isRequired()) {
123 ASSERT(m_requiredCount);
126 if (wasValid != isValid())
127 setNeedsValidityCheckForAllButtons();
130 void RadioButtonGroup::remove(HTMLInputElement* button)
132 ASSERT(button->isRadioButton());
133 HashSet<HTMLInputElement*>::iterator it = m_members.find(button);
134 if (it == m_members.end())
136 bool wasValid = isValid();
137 m_members.remove(it);
138 if (button->isRequired()) {
139 ASSERT(m_requiredCount);
142 if (m_checkedButton == button)
145 if (m_members.isEmpty()) {
146 ASSERT(!m_requiredCount);
147 ASSERT(!m_checkedButton);
148 } else if (wasValid != isValid()) {
149 setNeedsValidityCheckForAllButtons();
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();
158 void RadioButtonGroup::setNeedsValidityCheckForAllButtons()
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();
169 bool RadioButtonGroup::contains(HTMLInputElement* button) const
171 return m_members.contains(button);
174 // ----------------------------------------------------------------
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 CheckedRadioButtons::CheckedRadioButtons()
183 CheckedRadioButtons::~CheckedRadioButtons()
187 void CheckedRadioButtons::addButton(HTMLInputElement* element)
189 ASSERT(element->isRadioButton());
190 if (element->name().isEmpty())
193 if (!m_nameToGroupMap)
194 m_nameToGroupMap = adoptPtr(new NameToGroupMap);
196 OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name(), PassOwnPtr<RadioButtonGroup>()).iterator->value;
198 group = RadioButtonGroup::create();
202 void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element)
204 ASSERT(element->isRadioButton());
205 if (element->name().isEmpty())
207 ASSERT(m_nameToGroupMap);
208 if (!m_nameToGroupMap)
210 RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
212 group->updateCheckedState(element);
215 void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element)
217 ASSERT(element->isRadioButton());
218 if (element->name().isEmpty())
220 ASSERT(m_nameToGroupMap);
221 if (!m_nameToGroupMap)
223 RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
225 group->requiredAttributeChanged(element);
228 HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
230 if (!m_nameToGroupMap)
232 RadioButtonGroup* group = m_nameToGroupMap->get(name);
233 return group ? group->checkedButton() : 0;
236 bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const
238 ASSERT(element->isRadioButton());
239 if (element->name().isEmpty())
241 if (!m_nameToGroupMap)
243 RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
244 return group && group->isRequired() && group->contains(element);
247 void CheckedRadioButtons::removeButton(HTMLInputElement* element)
249 ASSERT(element->isRadioButton());
250 if (element->name().isEmpty())
252 if (!m_nameToGroupMap)
255 RadioButtonGroup* group = m_nameToGroupMap->get(element->name());
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());