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/NodeTraversal.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 void setNeedToUpdate(bool); | |
43 void updateAXPosition(); | |
44 unsigned sizeOfMembers() const; | |
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_needToUpdateAXPosition; | |
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_needToUpdateAXPosition(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 { |
79 return !isRequired() || m_checkedButton; | 86 return !isRequired() || m_checkedButton; |
80 } | 87 } |
81 | 88 |
82 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) | 89 void RadioButtonGroup::setCheckedButton(HTMLInputElement* button) |
83 { | 90 { |
84 HTMLInputElement* oldCheckedButton = m_checkedButton; | 91 HTMLInputElement* oldCheckedButton = m_checkedButton; |
85 if (oldCheckedButton == button) | 92 if (oldCheckedButton == button) |
86 return; | 93 return; |
87 m_checkedButton = button; | 94 m_checkedButton = button; |
88 if (oldCheckedButton) | 95 if (oldCheckedButton) |
89 oldCheckedButton->setChecked(false); | 96 oldCheckedButton->setChecked(false); |
90 } | 97 } |
91 | 98 |
99 bool RadioButtonGroup::isNextPosition(HTMLInputElement* listItem, HTMLInputEleme nt* current) const | |
100 { | |
101 Node* commonAncestor = NodeTraversal::commonAncestor(*listItem, *current); | |
102 for (Node* node = commonAncestor; node; node = NodeTraversal::next(*node)) { | |
103 if (!node->isHTMLElement() || !isHTMLInputElement(node) || !toHTMLInputE lement(node)->isRadioButton()) | |
104 continue; | |
105 if (node == listItem) | |
106 return true; | |
107 if (node == current) | |
108 return false; | |
109 } | |
110 return true; | |
111 } | |
112 | |
92 void RadioButtonGroup::updateRequiredButton(MemberKeyValue& it, bool isRequired) | 113 void RadioButtonGroup::updateRequiredButton(MemberKeyValue& it, bool isRequired) |
93 { | 114 { |
94 if (it.value == isRequired) | 115 if (it.value == isRequired) |
95 return; | 116 return; |
96 | 117 |
97 it.value = isRequired; | 118 it.value = isRequired; |
98 if (isRequired) { | 119 if (isRequired) { |
99 m_requiredCount++; | 120 m_requiredCount++; |
100 } else { | 121 } else { |
101 ASSERT(m_requiredCount); | 122 ASSERT(m_requiredCount); |
102 m_requiredCount--; | 123 m_requiredCount--; |
103 } | 124 } |
104 } | 125 } |
105 | 126 |
106 void RadioButtonGroup::add(HTMLInputElement* button) | 127 void RadioButtonGroup::add(HTMLInputElement* button) |
107 { | 128 { |
108 ASSERT(button->type() == InputTypeNames::radio); | 129 ASSERT(button->type() == InputTypeNames::radio); |
109 auto addResult = m_members.add(button, false); | 130 auto addResult = m_members.add(button, false); |
110 if (!addResult.isNewEntry) | 131 if (!addResult.isNewEntry) |
111 return; | 132 return; |
133 | |
134 setNeedToUpdate(true); | |
112 bool groupWasValid = isValid(); | 135 bool groupWasValid = isValid(); |
113 updateRequiredButton(*addResult.storedValue, button->isRequired()); | 136 updateRequiredButton(*addResult.storedValue, button->isRequired()); |
114 if (button->checked()) | 137 if (button->checked()) |
115 setCheckedButton(button); | 138 setCheckedButton(button); |
116 | 139 |
117 bool groupIsValid = isValid(); | 140 bool groupIsValid = isValid(); |
118 if (groupWasValid != groupIsValid) { | 141 if (groupWasValid != groupIsValid) { |
119 setNeedsValidityCheckForAllButtons(); | 142 setNeedsValidityCheckForAllButtons(); |
120 } else if (!groupIsValid) { | 143 } else if (!groupIsValid) { |
121 // A radio button not in a group is always valid. We need to make it | 144 // 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()) | 178 if (wasValid != isValid()) |
156 setNeedsValidityCheckForAllButtons(); | 179 setNeedsValidityCheckForAllButtons(); |
157 } | 180 } |
158 | 181 |
159 void RadioButtonGroup::remove(HTMLInputElement* button) | 182 void RadioButtonGroup::remove(HTMLInputElement* button) |
160 { | 183 { |
161 ASSERT(button->type() == InputTypeNames::radio); | 184 ASSERT(button->type() == InputTypeNames::radio); |
162 auto it = m_members.find(button); | 185 auto it = m_members.find(button); |
163 if (it == m_members.end()) | 186 if (it == m_members.end()) |
164 return; | 187 return; |
188 | |
189 setNeedToUpdate(true); | |
165 bool wasValid = isValid(); | 190 bool wasValid = isValid(); |
166 ASSERT(it->value == button->isRequired()); | 191 ASSERT(it->value == button->isRequired()); |
167 updateRequiredButton(*it, false); | 192 updateRequiredButton(*it, false); |
168 m_members.remove(it); | 193 m_members.remove(it); |
169 if (m_checkedButton == button) | 194 if (m_checkedButton == button) |
170 m_checkedButton = nullptr; | 195 m_checkedButton = nullptr; |
171 | 196 |
172 if (m_members.isEmpty()) { | 197 if (m_members.isEmpty()) { |
173 ASSERT(!m_requiredCount); | 198 ASSERT(!m_requiredCount); |
174 ASSERT(!m_checkedButton); | 199 ASSERT(!m_checkedButton); |
(...skipping 14 matching lines...) Expand all Loading... | |
189 ASSERT(button->type() == InputTypeNames::radio); | 214 ASSERT(button->type() == InputTypeNames::radio); |
190 button->setNeedsValidityCheck(); | 215 button->setNeedsValidityCheck(); |
191 } | 216 } |
192 } | 217 } |
193 | 218 |
194 bool RadioButtonGroup::contains(HTMLInputElement* button) const | 219 bool RadioButtonGroup::contains(HTMLInputElement* button) const |
195 { | 220 { |
196 return m_members.contains(button); | 221 return m_members.contains(button); |
197 } | 222 } |
198 | 223 |
224 void RadioButtonGroup::setNeedToUpdate(bool set) | |
225 { | |
226 m_needToUpdateAXPosition = set; | |
227 } | |
228 | |
229 void RadioButtonGroup::updateAXPosition() | |
230 { | |
231 if (!m_needToUpdateAXPosition) | |
232 return; | |
233 | |
234 WillBeHeapVector<RawPtrWillBeMember<HTMLInputElement>> orderdList; | |
235 for (auto& element : m_members) { | |
236 HTMLInputElement* const current = element.key; | |
237 if (orderdList.isEmpty()) { | |
238 orderdList.append(current); | |
239 } else { | |
240 unsigned position = 0; | |
241 for (auto& item : orderdList) { | |
242 if (!isNextPosition(item, current)) | |
keishi
2016/02/23 09:43:00
This reordering is like O(N^3). Could we use Radio
je_julie(Not used)
2016/02/27 14:01:08
I moved handling position for AX to AXRadioInput a
| |
243 break; | |
244 position++; | |
245 } | |
246 orderdList.insert(position, current); | |
247 } | |
248 } | |
249 | |
250 unsigned index = 0; | |
251 for (auto& orderedNode : orderdList) { | |
252 index++; | |
253 if (AXObjectCache* cache = orderedNode->document().existingAXObjectCache ()) | |
254 cache->radiobuttonPositionChanged(orderedNode, index); | |
255 } | |
256 setNeedToUpdate(false); | |
257 } | |
258 | |
259 unsigned RadioButtonGroup::sizeOfMembers() const | |
260 { | |
261 return m_members.size(); | |
262 } | |
263 | |
199 DEFINE_TRACE(RadioButtonGroup) | 264 DEFINE_TRACE(RadioButtonGroup) |
200 { | 265 { |
201 #if ENABLE(OILPAN) | 266 #if ENABLE(OILPAN) |
202 visitor->trace(m_members); | 267 visitor->trace(m_members); |
203 visitor->trace(m_checkedButton); | 268 visitor->trace(m_checkedButton); |
204 #endif | 269 #endif |
205 } | 270 } |
206 | 271 |
207 // ---------------------------------------------------------------- | 272 // ---------------------------------------------------------------- |
208 | 273 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 { | 335 { |
271 ASSERT(element->type() == InputTypeNames::radio); | 336 ASSERT(element->type() == InputTypeNames::radio); |
272 if (element->name().isEmpty()) | 337 if (element->name().isEmpty()) |
273 return false; | 338 return false; |
274 if (!m_nameToGroupMap) | 339 if (!m_nameToGroupMap) |
275 return false; | 340 return false; |
276 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 341 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
277 return group && group->isRequired() && group->contains(element); | 342 return group && group->isRequired() && group->contains(element); |
278 } | 343 } |
279 | 344 |
345 unsigned RadioButtonGroupScope::sizeOfGroup(const HTMLInputElement* element) con st | |
346 { | |
347 if (!m_nameToGroupMap) | |
348 return 0; | |
349 | |
350 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | |
351 if (!group) | |
352 return 0; | |
353 return group->sizeOfMembers(); | |
354 } | |
355 | |
356 void RadioButtonGroupScope::updateAXPositionInGroup(const HTMLInputElement* elem ent) const | |
357 { | |
358 if (!m_nameToGroupMap) | |
359 return; | |
360 | |
361 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | |
362 if (!group) | |
363 return; | |
364 group->updateAXPosition(); | |
365 } | |
366 | |
367 void RadioButtonGroupScope::setNeedToUpdateAXPositionInGroup(const HTMLInputElem ent* element, bool set) const | |
368 { | |
369 if (!m_nameToGroupMap) | |
370 return; | |
371 | |
372 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | |
373 if (!group) | |
374 return; | |
375 group->setNeedToUpdate(set); | |
keishi
2016/02/23 09:43:00
If we do something like RadioInputType::findNextFo
je_julie(Not used)
2016/02/27 14:01:08
I removed almost all handling to AXRadioInput as y
| |
376 } | |
377 | |
280 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) | 378 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) |
281 { | 379 { |
282 ASSERT(element->type() == InputTypeNames::radio); | 380 ASSERT(element->type() == InputTypeNames::radio); |
283 if (element->name().isEmpty()) | 381 if (element->name().isEmpty()) |
284 return; | 382 return; |
285 if (!m_nameToGroupMap) | 383 if (!m_nameToGroupMap) |
286 return; | 384 return; |
287 | 385 |
288 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 386 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
289 if (!group) | 387 if (!group) |
290 return; | 388 return; |
291 group->remove(element); | 389 group->remove(element); |
292 if (group->isEmpty()) { | 390 if (group->isEmpty()) { |
293 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for | 391 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for |
294 // better performance. | 392 // better performance. |
295 ASSERT(!group->isRequired()); | 393 ASSERT(!group->isRequired()); |
296 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); | 394 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); |
297 } | 395 } |
298 } | 396 } |
299 | 397 |
300 DEFINE_TRACE(RadioButtonGroupScope) | 398 DEFINE_TRACE(RadioButtonGroupScope) |
301 { | 399 { |
302 #if ENABLE(OILPAN) | 400 #if ENABLE(OILPAN) |
303 visitor->trace(m_nameToGroupMap); | 401 visitor->trace(m_nameToGroupMap); |
304 #endif | 402 #endif |
305 } | 403 } |
306 | 404 |
307 } // namespace blink | 405 } // namespace blink |
OLD | NEW |