diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/html/forms/RadioButtonGroupScope.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/html/forms/RadioButtonGroupScope.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/core/html/forms/RadioButtonGroupScope.cpp b/chromium/third_party/WebKit/Source/core/html/forms/RadioButtonGroupScope.cpp new file mode 100644 index 00000000000..92909e3a87a --- /dev/null +++ b/chromium/third_party/WebKit/Source/core/html/forms/RadioButtonGroupScope.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "core/html/forms/RadioButtonGroupScope.h" + +#include "core/html/HTMLInputElement.h" +#include "wtf/HashSet.h" + +namespace WebCore { + +class RadioButtonGroup : public NoBaseWillBeGarbageCollected<RadioButtonGroup> { + WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED; +public: + static PassOwnPtrWillBeRawPtr<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*); + bool contains(HTMLInputElement*) const; + + void trace(Visitor*); + +private: + RadioButtonGroup(); + void setNeedsValidityCheckForAllButtons(); + bool isValid() const; + void setCheckedButton(HTMLInputElement*); + + WillBeHeapHashSet<RawPtrWillBeMember<HTMLInputElement> > m_members; + RawPtrWillBeMember<HTMLInputElement> m_checkedButton; + size_t m_requiredCount; +}; + +RadioButtonGroup::RadioButtonGroup() + : m_checkedButton(nullptr) + , m_requiredCount(0) +{ +} + +PassOwnPtrWillBeRawPtr<RadioButtonGroup> RadioButtonGroup::create() +{ + return adoptPtrWillBeNoop(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).isNewEntry) + return; + bool groupWasValid = isValid(); + if (button->isRequired()) + ++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 = nullptr; + } + if (wasValid != isValid()) + setNeedsValidityCheckForAllButtons(); +} + +void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) +{ + ASSERT(button->isRadioButton()); + ASSERT(m_members.contains(button)); + bool wasValid = isValid(); + if (button->isRequired()) { + ++m_requiredCount; + } else { + ASSERT(m_requiredCount); + --m_requiredCount; + } + if (wasValid != isValid()) + setNeedsValidityCheckForAllButtons(); +} + +void RadioButtonGroup::remove(HTMLInputElement* button) +{ + ASSERT(button->isRadioButton()); + WillBeHeapHashSet<RawPtrWillBeMember<HTMLInputElement> >::iterator it = m_members.find(button); + if (it == m_members.end()) + return; + bool wasValid = isValid(); + m_members.remove(it); + if (button->isRequired()) { + ASSERT(m_requiredCount); + --m_requiredCount; + } + if (m_checkedButton == button) + m_checkedButton = nullptr; + + 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 WillBeHeapHashSet<RawPtrWillBeMember<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(); + } +} + +bool RadioButtonGroup::contains(HTMLInputElement* button) const +{ + return m_members.contains(button); +} + +void RadioButtonGroup::trace(Visitor* visitor) +{ + visitor->trace(m_members); + visitor->trace(m_checkedButton); +} + +// ---------------------------------------------------------------- + +// 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. +RadioButtonGroupScope::RadioButtonGroupScope() +{ +} + +RadioButtonGroupScope::~RadioButtonGroupScope() +{ +} + +void RadioButtonGroupScope::addButton(HTMLInputElement* element) +{ + ASSERT(element->isRadioButton()); + if (element->name().isEmpty()) + return; + + if (!m_nameToGroupMap) + m_nameToGroupMap = adoptPtrWillBeNoop(new NameToGroupMap); + + OwnPtrWillBeMember<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name(), nullptr).storedValue->value; + if (!group) + group = RadioButtonGroup::create(); + group->add(element); +} + +void RadioButtonGroupScope::updateCheckedState(HTMLInputElement* element) +{ + ASSERT(element->isRadioButton()); + if (element->name().isEmpty()) + return; + ASSERT(m_nameToGroupMap); + if (!m_nameToGroupMap) + return; + RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); + ASSERT(group); + group->updateCheckedState(element); +} + +void RadioButtonGroupScope::requiredAttributeChanged(HTMLInputElement* element) +{ + ASSERT(element->isRadioButton()); + if (element->name().isEmpty()) + return; + ASSERT(m_nameToGroupMap); + if (!m_nameToGroupMap) + return; + RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); + ASSERT(group); + group->requiredAttributeChanged(element); +} + +HTMLInputElement* RadioButtonGroupScope::checkedButtonForGroup(const AtomicString& name) const +{ + if (!m_nameToGroupMap) + return 0; + RadioButtonGroup* group = m_nameToGroupMap->get(name); + return group ? group->checkedButton() : 0; +} + +bool RadioButtonGroupScope::isInRequiredGroup(HTMLInputElement* element) const +{ + ASSERT(element->isRadioButton()); + if (element->name().isEmpty()) + return false; + if (!m_nameToGroupMap) + return false; + RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); + return group && group->isRequired() && group->contains(element); +} + +void RadioButtonGroupScope::removeButton(HTMLInputElement* element) +{ + ASSERT(element->isRadioButton()); + if (element->name().isEmpty()) + return; + if (!m_nameToGroupMap) + return; + + RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); + if (!group) + return; + group->remove(element); + if (group->isEmpty()) { + // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for + // better performance. + ASSERT(!group->isRequired()); + ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); + } +} + +void RadioButtonGroupScope::trace(Visitor* visitor) +{ +#if ENABLE(OILPAN) + visitor->trace(m_nameToGroupMap); +#endif +} + +} // namespace |