| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
| 5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
| 6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
| 7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
| 8 * | 8 * |
| 9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 #include <wtf/HashSet.h> | 25 #include <wtf/HashSet.h> |
| 26 | 26 |
| 27 namespace WebCore { | 27 namespace WebCore { |
| 28 | 28 |
| 29 class RadioButtonGroup { | 29 class RadioButtonGroup { |
| 30 WTF_MAKE_FAST_ALLOCATED; | 30 WTF_MAKE_FAST_ALLOCATED; |
| 31 public: | 31 public: |
| 32 static PassOwnPtr<RadioButtonGroup> create(); | 32 static PassOwnPtr<RadioButtonGroup> create(); |
| 33 bool isEmpty() const { return m_members.isEmpty(); } | 33 bool isEmpty() const { return m_members.isEmpty(); } |
| 34 bool isRequired() const { return m_requiredCount; } | 34 bool isRequired() const { return m_requiredCount; } |
| 35 HTMLInputElement* checkedButton() const { return m_checkedButton; } | 35 Result<HTMLInputElement> checkedButton() const { return Handle<HTMLInputElem
ent>(m_checkedButton); } // FIXME(oilpan): Remove Handle<>(). |
| 36 void add(HTMLInputElement*); | 36 void add(Handle<HTMLInputElement>); |
| 37 void updateCheckedState(HTMLInputElement*); | 37 void updateCheckedState(Handle<HTMLInputElement>); |
| 38 void requiredAttributeChanged(HTMLInputElement*); | 38 void requiredAttributeChanged(Handle<HTMLInputElement>); |
| 39 void remove(HTMLInputElement*); | 39 void remove(Handle<HTMLInputElement>); |
| 40 bool contains(HTMLInputElement*) const; | 40 bool contains(Handle<HTMLInputElement>) const; |
| 41 | 41 |
| 42 private: | 42 private: |
| 43 RadioButtonGroup(); | 43 RadioButtonGroup(); |
| 44 void setNeedsValidityCheckForAllButtons(); | 44 void setNeedsValidityCheckForAllButtons(); |
| 45 bool isValid() const; | 45 bool isValid() const; |
| 46 void setCheckedButton(HTMLInputElement*); | 46 void setCheckedButton(Handle<HTMLInputElement>); |
| 47 | 47 |
| 48 // FIXME(oilpan): Move RadioButtonGroup to the heap and use Members. |
| 48 HashSet<HTMLInputElement*> m_members; | 49 HashSet<HTMLInputElement*> m_members; |
| 49 HTMLInputElement* m_checkedButton; | 50 HTMLInputElement* m_checkedButton; |
| 50 size_t m_requiredCount; | 51 size_t m_requiredCount; |
| 51 }; | 52 }; |
| 52 | 53 |
| 53 RadioButtonGroup::RadioButtonGroup() | 54 RadioButtonGroup::RadioButtonGroup() |
| 54 : m_checkedButton(0) | 55 : m_checkedButton(0) |
| 55 , m_requiredCount(0) | 56 , m_requiredCount(0) |
| 56 { | 57 { |
| 57 } | 58 } |
| 58 | 59 |
| 59 PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create() | 60 PassOwnPtr<RadioButtonGroup> RadioButtonGroup::create() |
| 60 { | 61 { |
| 61 return adoptPtr(new RadioButtonGroup); | 62 return adoptPtr(new RadioButtonGroup); |
| 62 } | 63 } |
| 63 | 64 |
| 64 inline bool RadioButtonGroup::isValid() const | 65 inline bool RadioButtonGroup::isValid() const |
| 65 { | 66 { |
| 66 return !isRequired() || m_checkedButton; | 67 return !isRequired() || m_checkedButton; |
| 67 } | 68 } |
| 68 | 69 |
| 69 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) | 70 void RadioButtonGroup::setCheckedButton(Handle<HTMLInputElement> button) |
| 70 { | 71 { |
| 71 HTMLInputElement* oldCheckedButton = m_checkedButton; | 72 Handle<HTMLInputElement> oldCheckedButton = Handle<HTMLInputElement>(m_check
edButton); // FIXME(oilpan): Remove Handle<>(). |
| 72 if (oldCheckedButton == button) | 73 if (oldCheckedButton == button) |
| 73 return; | 74 return; |
| 74 m_checkedButton = button; | 75 m_checkedButton = button.raw(); |
| 75 if (oldCheckedButton) | 76 if (oldCheckedButton) |
| 76 oldCheckedButton->setChecked(false); | 77 oldCheckedButton->setChecked(false); |
| 77 } | 78 } |
| 78 | 79 |
| 79 void RadioButtonGroup::add(HTMLInputElement* button) | 80 void RadioButtonGroup::add(Handle<HTMLInputElement> button) |
| 80 { | 81 { |
| 81 ASSERT(button->isRadioButton()); | 82 ASSERT(button->isRadioButton()); |
| 82 if (!m_members.add(button).isNewEntry) | 83 if (!m_members.add(button.raw()).isNewEntry) |
| 83 return; | 84 return; |
| 84 bool groupWasValid = isValid(); | 85 bool groupWasValid = isValid(); |
| 85 if (button->isRequired()) | 86 if (button->isRequired()) |
| 86 ++m_requiredCount; | 87 ++m_requiredCount; |
| 87 if (button->checked()) | 88 if (button->checked()) |
| 88 setCheckedButton(button); | 89 setCheckedButton(button); |
| 89 | 90 |
| 90 bool groupIsValid = isValid(); | 91 bool groupIsValid = isValid(); |
| 91 if (groupWasValid != groupIsValid) | 92 if (groupWasValid != groupIsValid) |
| 92 setNeedsValidityCheckForAllButtons(); | 93 setNeedsValidityCheckForAllButtons(); |
| 93 else if (!groupIsValid) { | 94 else if (!groupIsValid) { |
| 94 // A radio button not in a group is always valid. We need to make it | 95 // A radio button not in a group is always valid. We need to make it |
| 95 // invalid only if the group is invalid. | 96 // invalid only if the group is invalid. |
| 96 button->setNeedsValidityCheck(); | 97 button->setNeedsValidityCheck(); |
| 97 } | 98 } |
| 98 } | 99 } |
| 99 | 100 |
| 100 void RadioButtonGroup::updateCheckedState(HTMLInputElement* button) | 101 void RadioButtonGroup::updateCheckedState(Handle<HTMLInputElement> button) |
| 101 { | 102 { |
| 102 ASSERT(button->isRadioButton()); | 103 ASSERT(button->isRadioButton()); |
| 103 ASSERT(m_members.contains(button)); | 104 ASSERT(m_members.contains(button.raw())); |
| 104 bool wasValid = isValid(); | 105 bool wasValid = isValid(); |
| 105 if (button->checked()) | 106 if (button->checked()) |
| 106 setCheckedButton(button); | 107 setCheckedButton(button); |
| 107 else { | 108 else { |
| 108 if (m_checkedButton == button) | 109 if (m_checkedButton == button.raw()) |
| 109 m_checkedButton = 0; | 110 m_checkedButton = 0; |
| 110 } | 111 } |
| 111 if (wasValid != isValid()) | 112 if (wasValid != isValid()) |
| 112 setNeedsValidityCheckForAllButtons(); | 113 setNeedsValidityCheckForAllButtons(); |
| 113 } | 114 } |
| 114 | 115 |
| 115 void RadioButtonGroup::requiredAttributeChanged(HTMLInputElement* button) | 116 void RadioButtonGroup::requiredAttributeChanged(Handle<HTMLInputElement> button) |
| 116 { | 117 { |
| 117 ASSERT(button->isRadioButton()); | 118 ASSERT(button->isRadioButton()); |
| 118 ASSERT(m_members.contains(button)); | 119 ASSERT(m_members.contains(button.raw())); |
| 119 bool wasValid = isValid(); | 120 bool wasValid = isValid(); |
| 120 if (button->isRequired()) | 121 if (button->isRequired()) |
| 121 ++m_requiredCount; | 122 ++m_requiredCount; |
| 122 else { | 123 else { |
| 123 ASSERT(m_requiredCount); | 124 ASSERT(m_requiredCount); |
| 124 --m_requiredCount; | 125 --m_requiredCount; |
| 125 } | 126 } |
| 126 if (wasValid != isValid()) | 127 if (wasValid != isValid()) |
| 127 setNeedsValidityCheckForAllButtons(); | 128 setNeedsValidityCheckForAllButtons(); |
| 128 } | 129 } |
| 129 | 130 |
| 130 void RadioButtonGroup::remove(HTMLInputElement* button) | 131 void RadioButtonGroup::remove(Handle<HTMLInputElement> button) |
| 131 { | 132 { |
| 132 ASSERT(button->isRadioButton()); | 133 ASSERT(button->isRadioButton()); |
| 133 HashSet<HTMLInputElement*>::iterator it = m_members.find(button); | 134 HashSet<HTMLInputElement*>::iterator it = m_members.find(button.raw()); |
| 134 if (it == m_members.end()) | 135 if (it == m_members.end()) |
| 135 return; | 136 return; |
| 136 bool wasValid = isValid(); | 137 bool wasValid = isValid(); |
| 137 m_members.remove(it); | 138 m_members.remove(it); |
| 138 if (button->isRequired()) { | 139 if (button->isRequired()) { |
| 139 ASSERT(m_requiredCount); | 140 ASSERT(m_requiredCount); |
| 140 --m_requiredCount; | 141 --m_requiredCount; |
| 141 } | 142 } |
| 142 if (m_checkedButton == button) | 143 if (m_checkedButton == button.raw()) |
| 143 m_checkedButton = 0; | 144 m_checkedButton = 0; |
| 144 | 145 |
| 145 if (m_members.isEmpty()) { | 146 if (m_members.isEmpty()) { |
| 146 ASSERT(!m_requiredCount); | 147 ASSERT(!m_requiredCount); |
| 147 ASSERT(!m_checkedButton); | 148 ASSERT(!m_checkedButton); |
| 148 } else if (wasValid != isValid()) | 149 } else if (wasValid != isValid()) |
| 149 setNeedsValidityCheckForAllButtons(); | 150 setNeedsValidityCheckForAllButtons(); |
| 150 if (!wasValid) { | 151 if (!wasValid) { |
| 151 // A radio button not in a group is always valid. We need to make it | 152 // A radio button not in a group is always valid. We need to make it |
| 152 // valid only if the group was invalid. | 153 // valid only if the group was invalid. |
| 153 button->setNeedsValidityCheck(); | 154 button->setNeedsValidityCheck(); |
| 154 } | 155 } |
| 155 } | 156 } |
| 156 | 157 |
| 157 void RadioButtonGroup::setNeedsValidityCheckForAllButtons() | 158 void RadioButtonGroup::setNeedsValidityCheckForAllButtons() |
| 158 { | 159 { |
| 159 typedef HashSet<HTMLInputElement*>::const_iterator Iterator; | 160 typedef HashSet<HTMLInputElement*>::const_iterator Iterator; |
| 160 Iterator end = m_members.end(); | 161 Iterator end = m_members.end(); |
| 161 for (Iterator it = m_members.begin(); it != end; ++it) { | 162 for (Iterator it = m_members.begin(); it != end; ++it) { |
| 162 HTMLInputElement* button = *it; | 163 Handle<HTMLInputElement> button = Handle<HTMLInputElement>(*it); // FIXM
E(oilpan): Remove Handle<>(). |
| 163 ASSERT(button->isRadioButton()); | 164 ASSERT(button->isRadioButton()); |
| 164 button->setNeedsValidityCheck(); | 165 button->setNeedsValidityCheck(); |
| 165 } | 166 } |
| 166 } | 167 } |
| 167 | 168 |
| 168 bool RadioButtonGroup::contains(HTMLInputElement* button) const | 169 bool RadioButtonGroup::contains(Handle<HTMLInputElement> button) const |
| 169 { | 170 { |
| 170 return m_members.contains(button); | 171 return m_members.contains(button.raw()); |
| 171 } | 172 } |
| 172 | 173 |
| 173 // ---------------------------------------------------------------- | 174 // ---------------------------------------------------------------- |
| 174 | 175 |
| 175 // Explicity define empty constructor and destructor in order to prevent the | 176 // Explicity define empty constructor and destructor in order to prevent the |
| 176 // compiler from generating them as inlines. So we don't need to to define | 177 // compiler from generating them as inlines. So we don't need to to define |
| 177 // RadioButtonGroup in the header. | 178 // RadioButtonGroup in the header. |
| 178 CheckedRadioButtons::CheckedRadioButtons() | 179 CheckedRadioButtons::CheckedRadioButtons() |
| 179 { | 180 { |
| 180 } | 181 } |
| 181 | 182 |
| 182 CheckedRadioButtons::~CheckedRadioButtons() | 183 CheckedRadioButtons::~CheckedRadioButtons() |
| 183 { | 184 { |
| 184 } | 185 } |
| 185 | 186 |
| 186 void CheckedRadioButtons::addButton(HTMLInputElement* element) | 187 void CheckedRadioButtons::addButton(Handle<HTMLInputElement> element) |
| 187 { | 188 { |
| 188 ASSERT(element->isRadioButton()); | 189 ASSERT(element->isRadioButton()); |
| 189 if (element->name().isEmpty()) | 190 if (element->name().isEmpty()) |
| 190 return; | 191 return; |
| 191 | 192 |
| 192 if (!m_nameToGroupMap) | 193 if (!m_nameToGroupMap) |
| 193 m_nameToGroupMap = adoptPtr(new NameToGroupMap); | 194 m_nameToGroupMap = adoptPtr(new NameToGroupMap); |
| 194 | 195 |
| 195 OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name().impl
(), PassOwnPtr<RadioButtonGroup>()).iterator->value; | 196 OwnPtr<RadioButtonGroup>& group = m_nameToGroupMap->add(element->name().impl
(), PassOwnPtr<RadioButtonGroup>()).iterator->value; |
| 196 if (!group) | 197 if (!group) |
| 197 group = RadioButtonGroup::create(); | 198 group = RadioButtonGroup::create(); |
| 198 group->add(element); | 199 group->add(element); |
| 199 } | 200 } |
| 200 | 201 |
| 201 void CheckedRadioButtons::updateCheckedState(HTMLInputElement* element) | 202 void CheckedRadioButtons::updateCheckedState(Handle<HTMLInputElement> element) |
| 202 { | 203 { |
| 203 ASSERT(element->isRadioButton()); | 204 ASSERT(element->isRadioButton()); |
| 204 if (element->name().isEmpty()) | 205 if (element->name().isEmpty()) |
| 205 return; | 206 return; |
| 206 ASSERT(m_nameToGroupMap); | 207 ASSERT(m_nameToGroupMap); |
| 207 if (!m_nameToGroupMap) | 208 if (!m_nameToGroupMap) |
| 208 return; | 209 return; |
| 209 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | 210 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); |
| 210 ASSERT(group); | 211 ASSERT(group); |
| 211 group->updateCheckedState(element); | 212 group->updateCheckedState(element); |
| 212 } | 213 } |
| 213 | 214 |
| 214 void CheckedRadioButtons::requiredAttributeChanged(HTMLInputElement* element) | 215 void CheckedRadioButtons::requiredAttributeChanged(Handle<HTMLInputElement> elem
ent) |
| 215 { | 216 { |
| 216 ASSERT(element->isRadioButton()); | 217 ASSERT(element->isRadioButton()); |
| 217 if (element->name().isEmpty()) | 218 if (element->name().isEmpty()) |
| 218 return; | 219 return; |
| 219 ASSERT(m_nameToGroupMap); | 220 ASSERT(m_nameToGroupMap); |
| 220 if (!m_nameToGroupMap) | 221 if (!m_nameToGroupMap) |
| 221 return; | 222 return; |
| 222 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | 223 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); |
| 223 ASSERT(group); | 224 ASSERT(group); |
| 224 group->requiredAttributeChanged(element); | 225 group->requiredAttributeChanged(element); |
| 225 } | 226 } |
| 226 | 227 |
| 227 HTMLInputElement* CheckedRadioButtons::checkedButtonForGroup(const AtomicString&
name) const | 228 Result<HTMLInputElement> CheckedRadioButtons::checkedButtonForGroup(const Atomic
String& name) const |
| 228 { | 229 { |
| 229 if (!m_nameToGroupMap) | 230 if (!m_nameToGroupMap) |
| 230 return 0; | 231 return nullptr; |
| 231 m_nameToGroupMap->checkConsistency(); | 232 m_nameToGroupMap->checkConsistency(); |
| 232 RadioButtonGroup* group = m_nameToGroupMap->get(name.impl()); | 233 RadioButtonGroup* group = m_nameToGroupMap->get(name.impl()); |
| 233 return group ? group->checkedButton() : 0; | 234 return group ? group->checkedButton() : nullptr; |
| 234 } | 235 } |
| 235 | 236 |
| 236 bool CheckedRadioButtons::isInRequiredGroup(HTMLInputElement* element) const | 237 bool CheckedRadioButtons::isInRequiredGroup(Handle<HTMLInputElement> element) co
nst |
| 237 { | 238 { |
| 238 ASSERT(element->isRadioButton()); | 239 ASSERT(element->isRadioButton()); |
| 239 if (element->name().isEmpty()) | 240 if (element->name().isEmpty()) |
| 240 return false; | 241 return false; |
| 241 if (!m_nameToGroupMap) | 242 if (!m_nameToGroupMap) |
| 242 return false; | 243 return false; |
| 243 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); | 244 RadioButtonGroup* group = m_nameToGroupMap->get(element->name().impl()); |
| 244 return group && group->isRequired() && group->contains(element); | 245 return group && group->isRequired() && group->contains(element); |
| 245 } | 246 } |
| 246 | 247 |
| 247 void CheckedRadioButtons::removeButton(HTMLInputElement* element) | 248 void CheckedRadioButtons::removeButton(Handle<HTMLInputElement> element) |
| 248 { | 249 { |
| 249 ASSERT(element->isRadioButton()); | 250 ASSERT(element->isRadioButton()); |
| 250 if (element->name().isEmpty()) | 251 if (element->name().isEmpty()) |
| 251 return; | 252 return; |
| 252 if (!m_nameToGroupMap) | 253 if (!m_nameToGroupMap) |
| 253 return; | 254 return; |
| 254 | 255 |
| 255 m_nameToGroupMap->checkConsistency(); | 256 m_nameToGroupMap->checkConsistency(); |
| 256 NameToGroupMap::iterator it = m_nameToGroupMap->find(element->name().impl())
; | 257 NameToGroupMap::iterator it = m_nameToGroupMap->find(element->name().impl())
; |
| 257 if (it == m_nameToGroupMap->end()) | 258 if (it == m_nameToGroupMap->end()) |
| 258 return; | 259 return; |
| 259 it->value->remove(element); | 260 it->value->remove(element); |
| 260 if (it->value->isEmpty()) { | 261 if (it->value->isEmpty()) { |
| 261 // FIXME: We may skip deallocating the empty RadioButtonGroup for | 262 // FIXME: We may skip deallocating the empty RadioButtonGroup for |
| 262 // performance improvement. If we do so, we need to change the key type | 263 // performance improvement. If we do so, we need to change the key type |
| 263 // of m_nameToGroupMap from AtomicStringImpl* to RefPtr<AtomicStringImpl
>. | 264 // of m_nameToGroupMap from AtomicStringImpl* to RefPtr<AtomicStringImpl
>. |
| 264 m_nameToGroupMap->remove(it); | 265 m_nameToGroupMap->remove(it); |
| 265 if (m_nameToGroupMap->isEmpty()) | 266 if (m_nameToGroupMap->isEmpty()) |
| 266 m_nameToGroupMap.clear(); | 267 m_nameToGroupMap.clear(); |
| 267 } | 268 } |
| 268 } | 269 } |
| 269 | 270 |
| 270 } // namespace | 271 } // namespace |
| OLD | NEW |