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 |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 * | 18 * |
19 */ | 19 */ |
20 | 20 |
21 #include "core/html/forms/RadioButtonGroupScope.h" | 21 #include "core/html/forms/RadioButtonGroupScope.h" |
22 | 22 |
23 #include "core/InputTypeNames.h" | 23 #include "core/InputTypeNames.h" |
| 24 #include "core/dom/NodeComputedStyle.h" |
24 #include "core/html/HTMLInputElement.h" | 25 #include "core/html/HTMLInputElement.h" |
25 #include "wtf/HashMap.h" | 26 #include "wtf/HashMap.h" |
26 | 27 |
27 namespace blink { | 28 namespace blink { |
28 | 29 |
29 class RadioButtonGroup : public NoBaseWillBeGarbageCollected<RadioButtonGroup> { | 30 class RadioButtonGroup : public NoBaseWillBeGarbageCollected<RadioButtonGroup> { |
30 USING_FAST_MALLOC_WILL_BE_REMOVED(RadioButtonGroup); | 31 USING_FAST_MALLOC_WILL_BE_REMOVED(RadioButtonGroup); |
31 public: | 32 public: |
32 static PassOwnPtrWillBeRawPtr<RadioButtonGroup> create(); | 33 static PassOwnPtrWillBeRawPtr<RadioButtonGroup> create(); |
33 bool isEmpty() const { return m_members.isEmpty(); } | 34 bool isEmpty() const { return m_members.isEmpty(); } |
34 bool isRequired() const { return m_requiredCount; } | 35 bool isRequired() const { return m_requiredCount; } |
35 HTMLInputElement* checkedButton() const { return m_checkedButton; } | 36 HTMLInputElement* checkedButton() const { return m_checkedButton; } |
36 void add(HTMLInputElement*); | 37 void add(HTMLInputElement*); |
37 void updateCheckedState(HTMLInputElement*); | 38 void updateCheckedState(HTMLInputElement*); |
38 void requiredAttributeChanged(HTMLInputElement*); | 39 void requiredAttributeChanged(HTMLInputElement*); |
39 void remove(HTMLInputElement*); | 40 void remove(HTMLInputElement*); |
40 bool contains(HTMLInputElement*) const; | 41 bool contains(HTMLInputElement*) const; |
| 42 unsigned sizeOfMembers() const; |
| 43 void updatePosition(); |
| 44 void needUpdatePosition() { m_needToUpdateIndex = true; } |
41 | 45 |
42 DECLARE_TRACE(); | 46 DECLARE_TRACE(); |
43 | 47 |
44 private: | 48 private: |
45 RadioButtonGroup(); | 49 RadioButtonGroup(); |
46 void setNeedsValidityCheckForAllButtons(); | 50 void setNeedsValidityCheckForAllButtons(); |
47 bool isValid() const; | 51 bool isValid() const; |
48 void setCheckedButton(HTMLInputElement*); | 52 void setCheckedButton(HTMLInputElement*); |
| 53 bool isNextPosition(HTMLInputElement* list, HTMLInputElement* current) const
; |
49 | 54 |
50 // The map records the 'required' state of each (button) element. | 55 // The map records the 'required' state of each (button) element. |
51 using Members = WillBeHeapHashMap<RawPtrWillBeMember<HTMLInputElement>, bool
>; | 56 using Members = WillBeHeapHashMap<RawPtrWillBeMember<HTMLInputElement>, bool
>; |
52 | 57 |
53 #if ENABLE(OILPAN) | 58 #if ENABLE(OILPAN) |
54 using MemberKeyValue = WTF::KeyValuePair<Member<HTMLInputElement>, bool>; | 59 using MemberKeyValue = WTF::KeyValuePair<Member<HTMLInputElement>, bool>; |
55 #else | 60 #else |
56 using MemberKeyValue = WTF::KeyValuePair<HTMLInputElement*, bool>; | 61 using MemberKeyValue = WTF::KeyValuePair<HTMLInputElement*, bool>; |
57 #endif | 62 #endif |
58 | 63 |
59 void updateRequiredButton(MemberKeyValue&, bool isRequired); | 64 void updateRequiredButton(MemberKeyValue&, bool isRequired); |
60 | 65 |
61 Members m_members; | 66 Members m_members; |
62 RawPtrWillBeMember<HTMLInputElement> m_checkedButton; | 67 RawPtrWillBeMember<HTMLInputElement> m_checkedButton; |
63 size_t m_requiredCount; | 68 size_t m_requiredCount; |
| 69 bool m_needToUpdateIndex; |
64 }; | 70 }; |
65 | 71 |
66 RadioButtonGroup::RadioButtonGroup() | 72 RadioButtonGroup::RadioButtonGroup() |
67 : m_checkedButton(nullptr) | 73 : m_checkedButton(nullptr) |
68 , m_requiredCount(0) | 74 , m_requiredCount(0) |
| 75 , m_needToUpdateIndex(false) |
69 { | 76 { |
70 } | 77 } |
71 | 78 |
72 PassOwnPtrWillBeRawPtr<RadioButtonGroup> RadioButtonGroup::create() | 79 PassOwnPtrWillBeRawPtr<RadioButtonGroup> RadioButtonGroup::create() |
73 { | 80 { |
74 return adoptPtrWillBeNoop(new RadioButtonGroup); | 81 return adoptPtrWillBeNoop(new RadioButtonGroup); |
75 } | 82 } |
76 | 83 |
77 inline bool RadioButtonGroup::isValid() const | 84 inline bool RadioButtonGroup::isValid() const |
78 { | 85 { |
(...skipping 23 matching lines...) Expand all Loading... |
102 m_requiredCount--; | 109 m_requiredCount--; |
103 } | 110 } |
104 } | 111 } |
105 | 112 |
106 void RadioButtonGroup::add(HTMLInputElement* button) | 113 void RadioButtonGroup::add(HTMLInputElement* button) |
107 { | 114 { |
108 ASSERT(button->type() == InputTypeNames::radio); | 115 ASSERT(button->type() == InputTypeNames::radio); |
109 auto addResult = m_members.add(button, false); | 116 auto addResult = m_members.add(button, false); |
110 if (!addResult.isNewEntry) | 117 if (!addResult.isNewEntry) |
111 return; | 118 return; |
| 119 |
| 120 needUpdatePosition(); |
112 bool groupWasValid = isValid(); | 121 bool groupWasValid = isValid(); |
113 updateRequiredButton(*addResult.storedValue, button->isRequired()); | 122 updateRequiredButton(*addResult.storedValue, button->isRequired()); |
114 if (button->checked()) | 123 if (button->checked()) |
115 setCheckedButton(button); | 124 setCheckedButton(button); |
116 | 125 |
117 bool groupIsValid = isValid(); | 126 bool groupIsValid = isValid(); |
118 if (groupWasValid != groupIsValid) { | 127 if (groupWasValid != groupIsValid) { |
119 setNeedsValidityCheckForAllButtons(); | 128 setNeedsValidityCheckForAllButtons(); |
120 } else if (!groupIsValid) { | 129 } else if (!groupIsValid) { |
121 // A radio button not in a group is always valid. We need to make it | 130 // A radio button not in a group is always valid. We need to make it |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 if (wasValid != isValid()) | 164 if (wasValid != isValid()) |
156 setNeedsValidityCheckForAllButtons(); | 165 setNeedsValidityCheckForAllButtons(); |
157 } | 166 } |
158 | 167 |
159 void RadioButtonGroup::remove(HTMLInputElement* button) | 168 void RadioButtonGroup::remove(HTMLInputElement* button) |
160 { | 169 { |
161 ASSERT(button->type() == InputTypeNames::radio); | 170 ASSERT(button->type() == InputTypeNames::radio); |
162 auto it = m_members.find(button); | 171 auto it = m_members.find(button); |
163 if (it == m_members.end()) | 172 if (it == m_members.end()) |
164 return; | 173 return; |
| 174 |
| 175 needUpdatePosition(); |
165 bool wasValid = isValid(); | 176 bool wasValid = isValid(); |
166 ASSERT(it->value == button->isRequired()); | 177 ASSERT(it->value == button->isRequired()); |
167 updateRequiredButton(*it, false); | 178 updateRequiredButton(*it, false); |
168 m_members.remove(it); | 179 m_members.remove(it); |
169 if (m_checkedButton == button) | 180 if (m_checkedButton == button) |
170 m_checkedButton = nullptr; | 181 m_checkedButton = nullptr; |
171 | 182 |
172 if (m_members.isEmpty()) { | 183 if (m_members.isEmpty()) { |
173 ASSERT(!m_requiredCount); | 184 ASSERT(!m_requiredCount); |
174 ASSERT(!m_checkedButton); | 185 ASSERT(!m_checkedButton); |
(...skipping 14 matching lines...) Expand all Loading... |
189 ASSERT(button->type() == InputTypeNames::radio); | 200 ASSERT(button->type() == InputTypeNames::radio); |
190 button->setNeedsValidityCheck(); | 201 button->setNeedsValidityCheck(); |
191 } | 202 } |
192 } | 203 } |
193 | 204 |
194 bool RadioButtonGroup::contains(HTMLInputElement* button) const | 205 bool RadioButtonGroup::contains(HTMLInputElement* button) const |
195 { | 206 { |
196 return m_members.contains(button); | 207 return m_members.contains(button); |
197 } | 208 } |
198 | 209 |
| 210 void RadioButtonGroup::updatePosition() |
| 211 { |
| 212 if (!m_needToUpdateIndex) |
| 213 return; |
| 214 |
| 215 WillBeHeapVector<RawPtrWillBeMember<HTMLInputElement>> orderd; |
| 216 for (auto& element : m_members) { |
| 217 HTMLInputElement* const current = element.key; |
| 218 ASSERT(current->type() == InputTypeNames::radio); |
| 219 if (orderd.isEmpty()) { |
| 220 orderd.append(current); |
| 221 } else { |
| 222 unsigned position = 0; |
| 223 for (auto& item : orderd) { |
| 224 if (!isNextPosition(item, current)) |
| 225 break; |
| 226 position++; |
| 227 } |
| 228 orderd.insert(position, current); |
| 229 } |
| 230 } |
| 231 |
| 232 unsigned index = 0; |
| 233 for (auto& orderedList : orderd) { |
| 234 index++; |
| 235 orderedList->setPositionInRadioGroup(index); |
| 236 } |
| 237 m_needToUpdateIndex = false; |
| 238 } |
| 239 |
| 240 bool RadioButtonGroup::isNextPosition(HTMLInputElement* listItem, HTMLInputEleme
nt* current) const |
| 241 { |
| 242 if (!current->layoutObject() || !listItem->layoutObject()) |
| 243 return true; |
| 244 |
| 245 IntRect itemRect = listItem->layoutObject()->absoluteBoundingBoxRect(); |
| 246 IntRect currentRect = current->layoutObject()->absoluteBoundingBoxRect(); |
| 247 if (itemRect.y() < currentRect.y()) |
| 248 return true; |
| 249 if (itemRect.y() == currentRect.y()) { |
| 250 Node* commonAncestor = NodeTraversal::commonAncestor(*listItem, *current
); |
| 251 if (const ComputedStyle* style = commonAncestor->computedStyle()) { |
| 252 if ((style->isLeftToRightDirection() && itemRect.x() <= currentRect.
x()) |
| 253 || (!style->isLeftToRightDirection() && itemRect.x() >= currentR
ect.x())) |
| 254 return true; |
| 255 } |
| 256 } |
| 257 return false; |
| 258 } |
| 259 |
| 260 unsigned RadioButtonGroup::sizeOfMembers() const |
| 261 { |
| 262 return m_members.size(); |
| 263 } |
| 264 |
199 DEFINE_TRACE(RadioButtonGroup) | 265 DEFINE_TRACE(RadioButtonGroup) |
200 { | 266 { |
201 #if ENABLE(OILPAN) | 267 #if ENABLE(OILPAN) |
202 visitor->trace(m_members); | 268 visitor->trace(m_members); |
203 visitor->trace(m_checkedButton); | 269 visitor->trace(m_checkedButton); |
204 #endif | 270 #endif |
205 } | 271 } |
206 | 272 |
207 // ---------------------------------------------------------------- | 273 // ---------------------------------------------------------------- |
208 | 274 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
270 { | 336 { |
271 ASSERT(element->type() == InputTypeNames::radio); | 337 ASSERT(element->type() == InputTypeNames::radio); |
272 if (element->name().isEmpty()) | 338 if (element->name().isEmpty()) |
273 return false; | 339 return false; |
274 if (!m_nameToGroupMap) | 340 if (!m_nameToGroupMap) |
275 return false; | 341 return false; |
276 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 342 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
277 return group && group->isRequired() && group->contains(element); | 343 return group && group->isRequired() && group->contains(element); |
278 } | 344 } |
279 | 345 |
| 346 unsigned RadioButtonGroupScope::sizeOfGroup(const HTMLInputElement* element) con
st |
| 347 { |
| 348 if (!m_nameToGroupMap) |
| 349 return 0; |
| 350 |
| 351 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
| 352 if (!group) |
| 353 return 0; |
| 354 return group->sizeOfMembers(); |
| 355 } |
| 356 |
| 357 void RadioButtonGroupScope::updateGroupPosition(const HTMLInputElement* element)
const |
| 358 { |
| 359 if (!m_nameToGroupMap) |
| 360 return; |
| 361 |
| 362 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
| 363 if (!group) |
| 364 return; |
| 365 group->updatePosition(); |
| 366 } |
| 367 |
| 368 void RadioButtonGroupScope::needUpdatePositionGroup(const HTMLInputElement* elem
ent) const |
| 369 { |
| 370 if (!m_nameToGroupMap) |
| 371 return; |
| 372 |
| 373 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
| 374 if (!group) |
| 375 return; |
| 376 group->needUpdatePosition(); |
| 377 } |
| 378 |
280 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) | 379 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) |
281 { | 380 { |
282 ASSERT(element->type() == InputTypeNames::radio); | 381 ASSERT(element->type() == InputTypeNames::radio); |
283 if (element->name().isEmpty()) | 382 if (element->name().isEmpty()) |
284 return; | 383 return; |
285 if (!m_nameToGroupMap) | 384 if (!m_nameToGroupMap) |
286 return; | 385 return; |
287 | 386 |
288 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 387 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
289 if (!group) | 388 if (!group) |
290 return; | 389 return; |
291 group->remove(element); | 390 group->remove(element); |
292 if (group->isEmpty()) { | 391 if (group->isEmpty()) { |
293 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for | 392 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for |
294 // better performance. | 393 // better performance. |
295 ASSERT(!group->isRequired()); | 394 ASSERT(!group->isRequired()); |
296 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); | 395 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); |
297 } | 396 } |
298 } | 397 } |
299 | 398 |
300 DEFINE_TRACE(RadioButtonGroupScope) | 399 DEFINE_TRACE(RadioButtonGroupScope) |
301 { | 400 { |
302 #if ENABLE(OILPAN) | 401 #if ENABLE(OILPAN) |
303 visitor->trace(m_nameToGroupMap); | 402 visitor->trace(m_nameToGroupMap); |
304 #endif | 403 #endif |
305 } | 404 } |
306 | 405 |
307 } // namespace blink | 406 } // namespace blink |
OLD | NEW |