| Index: Source/WebCore/dom/CheckedRadioButtons.cpp
|
| ===================================================================
|
| --- Source/WebCore/dom/CheckedRadioButtons.cpp (revision 111481)
|
| +++ Source/WebCore/dom/CheckedRadioButtons.cpp (working copy)
|
| @@ -22,244 +22,65 @@
|
| #include "CheckedRadioButtons.h"
|
|
|
| #include "HTMLInputElement.h"
|
| -#include <wtf/HashSet.h>
|
|
|
| namespace WebCore {
|
|
|
| -class RadioButtonGroup {
|
| -public:
|
| - static PassOwnPtr<RadioButtonGroup> create();
|
| - bool isEmpty() const { return m_members.isEmpty(); }
|
| - bool isRequired() const { return m_requiredCount; }
|
| - HTMLInputElement* checkedButton() const { return m_checkedButton; }
|
| - void add(HTMLInputElement*);
|
| - void updateCheckedState(HTMLInputElement*);
|
| - void requiredAttributeChanged(HTMLInputElement*);
|
| - void remove(HTMLInputElement*);
|
| -
|
| -private:
|
| - RadioButtonGroup();
|
| - void setNeedsValidityCheckForAllButtons();
|
| - bool isValid() const;
|
| - void setCheckedButton(HTMLInputElement*);
|
| -
|
| - HashSet<HTMLInputElement*> m_members;
|
| - HTMLInputElement* m_checkedButton;
|
| - size_t m_requiredCount;
|
| -};
|
| -
|
| -RadioButtonGroup::RadioButtonGroup()
|
| - : m_checkedButton(0)
|
| - , m_requiredCount(0)
|
| -{
|
| -}
|
| -
|
| -PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create()
|
| -{
|
| - return adoptPtr(new RadioButtonGroup);
|
| -}
|
| -
|
| -inline bool RadioButtonGroup::isValid() const
|
| -{
|
| - return !isRequired() || m_checkedButton;
|
| -}
|
| -
|
| -void RadioButtonGroup::setCheckedButton(HTMLInputElement* button)
|
| -{
|
| - HTMLInputElement* oldCheckedButton = m_checkedButton;
|
| - if (oldCheckedButton == button)
|
| - return;
|
| - m_checkedButton = button;
|
| - if (oldCheckedButton)
|
| - oldCheckedButton->setChecked(false);
|
| -}
|
| -
|
| -void RadioButtonGroup::add(HTMLInputElement* button)
|
| -{
|
| - ASSERT(button->isRadioButton());
|
| - if (!m_members.add(button).second)
|
| - return;
|
| - bool groupWasValid = isValid();
|
| - if (button->required())
|
| - ++m_requiredCount;
|
| - if (button->checked())
|
| - setCheckedButton(button);
|
| -
|
| - bool groupIsValid = isValid();
|
| - if (groupWasValid != groupIsValid)
|
| - setNeedsValidityCheckForAllButtons();
|
| - else if (!groupIsValid) {
|
| - // A radio button not in a group is always valid. We need to make it
|
| - // invalid only if the group is invalid.
|
| - button->setNeedsValidityCheck();
|
| - }
|
| -}
|
| -
|
| -void RadioButtonGroup::updateCheckedState(HTMLInputElement* button)
|
| -{
|
| - ASSERT(button->isRadioButton());
|
| - ASSERT(m_members.contains(button));
|
| - bool wasValid = isValid();
|
| - if (button->checked())
|
| - setCheckedButton(button);
|
| - else {
|
| - if (m_checkedButton == button)
|
| - m_checkedButton = 0;
|
| - }
|
| - if (wasValid != isValid())
|
| - setNeedsValidityCheckForAllButtons();
|
| -}
|
| -
|
| -void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button)
|
| -{
|
| - ASSERT(button->isRadioButton());
|
| - ASSERT(m_members.contains(button));
|
| - bool wasValid = isValid();
|
| - if (button->required())
|
| - ++m_requiredCount;
|
| - else {
|
| - ASSERT(m_requiredCount);
|
| - --m_requiredCount;
|
| - }
|
| - if (wasValid != isValid())
|
| - setNeedsValidityCheckForAllButtons();
|
| -}
|
| -
|
| -void RadioButtonGroup::remove(HTMLInputElement* button)
|
| -{
|
| - ASSERT(button->isRadioButton());
|
| - HashSet<HTMLInputElement*>::iterator it = m_members.find(button);
|
| - if (it == m_members.end())
|
| - return;
|
| - bool wasValid = isValid();
|
| - m_members.remove(it);
|
| - if (button->required()) {
|
| - ASSERT(m_requiredCount);
|
| - --m_requiredCount;
|
| - }
|
| - if (m_checkedButton == button)
|
| - m_checkedButton = 0;
|
| -
|
| - if (m_members.isEmpty()) {
|
| - ASSERT(!m_requiredCount);
|
| - ASSERT(!m_checkedButton);
|
| - } else if (wasValid != isValid())
|
| - setNeedsValidityCheckForAllButtons();
|
| - if (!wasValid) {
|
| - // A radio button not in a group is always valid. We need to make it
|
| - // valid only if the group was invalid.
|
| - button->setNeedsValidityCheck();
|
| - }
|
| -}
|
| -
|
| -void RadioButtonGroup::setNeedsValidityCheckForAllButtons()
|
| -{
|
| - typedef HashSet<HTMLInputElement*>::const_iterator Iterator;
|
| - Iterator end = m_members.end();
|
| - for (Iterator it = m_members.begin(); it != end; ++it) {
|
| - HTMLInputElement* button = *it;
|
| - ASSERT(button->isRadioButton());
|
| - button->setNeedsValidityCheck();
|
| - }
|
| -}
|
| -
|
| -// ----------------------------------------------------------------
|
| -
|
| static inline bool shouldMakeRadioGroup(HTMLInputElement* element)
|
| {
|
| return element->isRadioButton() && !element->name().isEmpty() && element->inDocument();
|
| }
|
|
|
| -// Explicity define empty constructor and destructor in order to prevent the
|
| -// compiler from generating them as inlines. So we don't need to to define
|
| -// RadioButtonGroup in the header.
|
| -CheckedRadioButtons::CheckedRadioButtons()
|
| -{
|
| -}
|
| -
|
| -CheckedRadioButtons::~CheckedRadioButtons()
|
| -{
|
| -}
|
| -
|
| void CheckedRadioButtons::addButton(HTMLInputElement* element)
|
| {
|
| if (!shouldMakeRadioGroup(element))
|
| return;
|
|
|
| - if (!m_nameToGroupMap)
|
| - m_nameToGroupMap = adoptPtr(new NameToGroupMap);
|
| + // We only track checked buttons.
|
| + if (!element->checked())
|
| + return;
|
|
|
| - OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name().impl(), PassOwnPtr<RadioButtonGroup>()).first->second;
|
| - if (!group)
|
| - group = RadioButtonGroup::create();
|
| - group->add(element);
|
| -}
|
| + if (!m_nameToCheckedRadioButtonMap)
|
| + m_nameToCheckedRadioButtonMap = adoptPtr(new NameToInputMap);
|
|
|
| -void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element)
|
| -{
|
| - if (!shouldMakeRadioGroup(element))
|
| + pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), element);
|
| + if (result.second)
|
| return;
|
| - ASSERT(m_nameToGroupMap);
|
| - if (!m_nameToGroupMap)
|
| +
|
| + HTMLInputElement* oldCheckedButton = result.first->second;
|
| + if (oldCheckedButton == element)
|
| return;
|
| - RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl());
|
| - ASSERT(group);
|
| - group->updateCheckedState(element);
|
| -}
|
|
|
| -void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element)
|
| -{
|
| - if (!shouldMakeRadioGroup(element))
|
| - return;
|
| - ASSERT(m_nameToGroupMap);
|
| - if (!m_nameToGroupMap)
|
| - return;
|
| - RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl());
|
| - ASSERT(group);
|
| - group->requiredAttributeChanged(element);
|
| + result.first->second = element;
|
| + oldCheckedButton->setChecked(false);
|
| }
|
|
|
| HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
|
| {
|
| - if (!m_nameToGroupMap)
|
| + if (!m_nameToCheckedRadioButtonMap)
|
| return 0;
|
| - m_nameToGroupMap->checkConsistency();
|
| - RadioButtonGroup* group = m_nameToGroupMap->get(name.impl());
|
| - return group ? group->checkedButton() : 0;
|
| -}
|
|
|
| -bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const
|
| -{
|
| - ASSERT(element->isRadioButton());
|
| - if (!element->inDocument())
|
| - return false;
|
| - if (!m_nameToGroupMap)
|
| - return false;
|
| -
|
| - RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl());
|
| - return group && group->isRequired();
|
| + m_nameToCheckedRadioButtonMap->checkConsistency();
|
| +
|
| + return m_nameToCheckedRadioButtonMap->get(name.impl());
|
| }
|
|
|
| void CheckedRadioButtons::removeButton(HTMLInputElement* element)
|
| {
|
| - if (!shouldMakeRadioGroup(element))
|
| + if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
|
| return;
|
| - if (!m_nameToGroupMap)
|
| +
|
| + m_nameToCheckedRadioButtonMap->checkConsistency();
|
| +
|
| + NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl());
|
| + if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
|
| return;
|
| +
|
| + ASSERT(element->shouldAppearChecked());
|
| + ASSERT(element->isRadioButton());
|
|
|
| - m_nameToGroupMap->checkConsistency();
|
| - NameToGroupMap::iterator it = m_nameToGroupMap->find(element->name().impl());
|
| - if (it == m_nameToGroupMap->end())
|
| - return;
|
| - it->second->remove(element);
|
| - if (it->second->isEmpty()) {
|
| - // FIXME: We may skip deallocating the empty RadioButtonGroup for
|
| - // performance improvement. If we do so, we need to change the key type
|
| - // of m_nameToGroupMap from AtomicStringImpl* to RefPtr<AtomicStringImpl>.
|
| - m_nameToGroupMap->remove(it);
|
| - if (m_nameToGroupMap->isEmpty())
|
| - m_nameToGroupMap.clear();
|
| - }
|
| + m_nameToCheckedRadioButtonMap->remove(it);
|
| + if (m_nameToCheckedRadioButtonMap->isEmpty())
|
| + m_nameToCheckedRadioButtonMap.clear();
|
| }
|
|
|
| } // namespace
|
|
|