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/html/HTMLInputElement.h" | 24 #include "core/html/HTMLInputElement.h" |
25 #include "core/layout/LayoutObject.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() const; | |
41 | 44 |
42 DECLARE_TRACE(); | 45 DECLARE_TRACE(); |
43 | 46 |
44 private: | 47 private: |
45 RadioButtonGroup(); | 48 RadioButtonGroup(); |
46 void setNeedsValidityCheckForAllButtons(); | 49 void setNeedsValidityCheckForAllButtons(); |
47 bool isValid() const; | 50 bool isValid() const; |
48 void setCheckedButton(HTMLInputElement*); | 51 void setCheckedButton(HTMLInputElement*); |
52 bool isNext(HTMLInputElement* list, HTMLInputElement* current) const; | |
keishi
2016/02/05 03:05:26
nit: Could you make the method name more specific
je_julie(Not used)
2016/02/13 03:19:00
I updated it with isNextPosition().
| |
49 | 53 |
50 // The map records the 'required' state of each (button) element. | 54 // The map records the 'required' state of each (button) element. |
51 using Members = WillBeHeapHashMap<RawPtrWillBeMember<HTMLInputElement>, bool >; | 55 using Members = WillBeHeapHashMap<RawPtrWillBeMember<HTMLInputElement>, bool >; |
52 | 56 |
53 #if ENABLE(OILPAN) | 57 #if ENABLE(OILPAN) |
54 using MemberKeyValue = WTF::KeyValuePair<Member<HTMLInputElement>, bool>; | 58 using MemberKeyValue = WTF::KeyValuePair<Member<HTMLInputElement>, bool>; |
55 #else | 59 #else |
56 using MemberKeyValue = WTF::KeyValuePair<HTMLInputElement*, bool>; | 60 using MemberKeyValue = WTF::KeyValuePair<HTMLInputElement*, bool>; |
57 #endif | 61 #endif |
58 | 62 |
59 void updateRequiredButton(MemberKeyValue&, bool isRequired); | 63 void updateRequiredButton(MemberKeyValue&, bool isRequired); |
60 | 64 |
61 Members m_members; | 65 Members m_members; |
62 RawPtrWillBeMember<HTMLInputElement> m_checkedButton; | 66 RawPtrWillBeMember<HTMLInputElement> m_checkedButton; |
63 size_t m_requiredCount; | 67 size_t m_requiredCount; |
68 bool m_needToUpdateIndex; | |
64 }; | 69 }; |
65 | 70 |
66 RadioButtonGroup::RadioButtonGroup() | 71 RadioButtonGroup::RadioButtonGroup() |
67 : m_checkedButton(nullptr) | 72 : m_checkedButton(nullptr) |
68 , m_requiredCount(0) | 73 , m_requiredCount(0) |
74 , m_needToUpdateIndex(false) | |
69 { | 75 { |
70 } | 76 } |
71 | 77 |
72 PassOwnPtrWillBeRawPtr<RadioButtonGroup> RadioButtonGroup::create() | 78 PassOwnPtrWillBeRawPtr<RadioButtonGroup> RadioButtonGroup::create() |
73 { | 79 { |
74 return adoptPtrWillBeNoop(new RadioButtonGroup); | 80 return adoptPtrWillBeNoop(new RadioButtonGroup); |
75 } | 81 } |
76 | 82 |
77 inline bool RadioButtonGroup::isValid() const | 83 inline bool RadioButtonGroup::isValid() const |
78 { | 84 { |
(...skipping 23 matching lines...) Expand all Loading... | |
102 m_requiredCount--; | 108 m_requiredCount--; |
103 } | 109 } |
104 } | 110 } |
105 | 111 |
106 void RadioButtonGroup::add(HTMLInputElement* button) | 112 void RadioButtonGroup::add(HTMLInputElement* button) |
107 { | 113 { |
108 ASSERT(button->type() == InputTypeNames::radio); | 114 ASSERT(button->type() == InputTypeNames::radio); |
109 auto addResult = m_members.add(button, false); | 115 auto addResult = m_members.add(button, false); |
110 if (!addResult.isNewEntry) | 116 if (!addResult.isNewEntry) |
111 return; | 117 return; |
118 | |
119 m_needToUpdateIndex = true; | |
112 bool groupWasValid = isValid(); | 120 bool groupWasValid = isValid(); |
113 updateRequiredButton(*addResult.storedValue, button->isRequired()); | 121 updateRequiredButton(*addResult.storedValue, button->isRequired()); |
114 if (button->checked()) | 122 if (button->checked()) |
115 setCheckedButton(button); | 123 setCheckedButton(button); |
116 | 124 |
117 bool groupIsValid = isValid(); | 125 bool groupIsValid = isValid(); |
118 if (groupWasValid != groupIsValid) { | 126 if (groupWasValid != groupIsValid) { |
119 setNeedsValidityCheckForAllButtons(); | 127 setNeedsValidityCheckForAllButtons(); |
120 } else if (!groupIsValid) { | 128 } else if (!groupIsValid) { |
121 // A radio button not in a group is always valid. We need to make it | 129 // 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()) | 163 if (wasValid != isValid()) |
156 setNeedsValidityCheckForAllButtons(); | 164 setNeedsValidityCheckForAllButtons(); |
157 } | 165 } |
158 | 166 |
159 void RadioButtonGroup::remove(HTMLInputElement* button) | 167 void RadioButtonGroup::remove(HTMLInputElement* button) |
160 { | 168 { |
161 ASSERT(button->type() == InputTypeNames::radio); | 169 ASSERT(button->type() == InputTypeNames::radio); |
162 auto it = m_members.find(button); | 170 auto it = m_members.find(button); |
163 if (it == m_members.end()) | 171 if (it == m_members.end()) |
164 return; | 172 return; |
173 | |
174 m_needToUpdateIndex = true; | |
165 bool wasValid = isValid(); | 175 bool wasValid = isValid(); |
166 ASSERT(it->value == button->isRequired()); | 176 ASSERT(it->value == button->isRequired()); |
167 updateRequiredButton(*it, false); | 177 updateRequiredButton(*it, false); |
168 m_members.remove(it); | 178 m_members.remove(it); |
169 if (m_checkedButton == button) | 179 if (m_checkedButton == button) |
170 m_checkedButton = nullptr; | 180 m_checkedButton = nullptr; |
171 | 181 |
172 if (m_members.isEmpty()) { | 182 if (m_members.isEmpty()) { |
173 ASSERT(!m_requiredCount); | 183 ASSERT(!m_requiredCount); |
174 ASSERT(!m_checkedButton); | 184 ASSERT(!m_checkedButton); |
(...skipping 14 matching lines...) Expand all Loading... | |
189 ASSERT(button->type() == InputTypeNames::radio); | 199 ASSERT(button->type() == InputTypeNames::radio); |
190 button->setNeedsValidityCheck(); | 200 button->setNeedsValidityCheck(); |
191 } | 201 } |
192 } | 202 } |
193 | 203 |
194 bool RadioButtonGroup::contains(HTMLInputElement* button) const | 204 bool RadioButtonGroup::contains(HTMLInputElement* button) const |
195 { | 205 { |
196 return m_members.contains(button); | 206 return m_members.contains(button); |
197 } | 207 } |
198 | 208 |
209 void RadioButtonGroup::updatePosition() const | |
210 { | |
211 if (!m_needToUpdateIndex) | |
keishi
2016/02/05 03:05:26
List order seems to rely on layout.
Does m_needToU
je_julie(Not used)
2016/02/13 03:19:00
I also set this flag in LayoutBlockFlow::layoutBlo
| |
212 return; | |
213 | |
214 WillBeHeapVector<RawPtrWillBeMember<HTMLInputElement>> orderd; | |
215 for (auto& element : m_members) { | |
216 HTMLInputElement* const current = element.key; | |
217 ASSERT(current->type() == InputTypeNames::radio); | |
218 if (orderd.isEmpty()) { | |
219 orderd.append(current); | |
220 } else { | |
221 unsigned position = 0; | |
222 for (auto& orderedList : orderd) { | |
223 if (!isNext(orderedList, current)) | |
224 break; | |
225 position++; | |
226 } | |
227 orderd.insert(position, current); | |
228 } | |
229 } | |
230 | |
231 unsigned index = 0; | |
232 for (auto& orderedList : orderd) { | |
233 index++; | |
234 orderedList->setPositionInRadioGroup(index); | |
235 } | |
236 } | |
237 | |
238 bool RadioButtonGroup::isNext(HTMLInputElement* list, HTMLInputElement* current) const | |
239 { | |
240 if (!current->layoutObject() || !list->layoutObject()) | |
241 return true; | |
242 | |
243 IntRect listRect = list->layoutObject()->absoluteBoundingBoxRect(); | |
244 IntRect currentRect = current->layoutObject()->absoluteBoundingBoxRect(); | |
245 if (listRect.y() < currentRect.y()) | |
246 return true; | |
247 if (listRect.y() == currentRect.y() && listRect.x() <= currentRect.x()) | |
keishi
2016/02/05 03:05:26
Just curious. Is this ok for rtl?
je_julie(Not used)
2016/02/13 03:19:00
Right. We have to consider RTL case as well. I upd
| |
248 return true; | |
249 return false; | |
250 } | |
251 | |
252 unsigned RadioButtonGroup::sizeOfMembers() const | |
253 { | |
254 return m_members.size(); | |
255 } | |
256 | |
199 DEFINE_TRACE(RadioButtonGroup) | 257 DEFINE_TRACE(RadioButtonGroup) |
200 { | 258 { |
201 #if ENABLE(OILPAN) | 259 #if ENABLE(OILPAN) |
202 visitor->trace(m_members); | 260 visitor->trace(m_members); |
203 visitor->trace(m_checkedButton); | 261 visitor->trace(m_checkedButton); |
204 #endif | 262 #endif |
205 } | 263 } |
206 | 264 |
207 // ---------------------------------------------------------------- | 265 // ---------------------------------------------------------------- |
208 | 266 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 { | 328 { |
271 ASSERT(element->type() == InputTypeNames::radio); | 329 ASSERT(element->type() == InputTypeNames::radio); |
272 if (element->name().isEmpty()) | 330 if (element->name().isEmpty()) |
273 return false; | 331 return false; |
274 if (!m_nameToGroupMap) | 332 if (!m_nameToGroupMap) |
275 return false; | 333 return false; |
276 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 334 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
277 return group && group->isRequired() && group->contains(element); | 335 return group && group->isRequired() && group->contains(element); |
278 } | 336 } |
279 | 337 |
338 unsigned RadioButtonGroupScope::sizeOfGroup(HTMLInputElement* element) const | |
339 { | |
340 if (!m_nameToGroupMap) | |
341 return 0; | |
342 | |
343 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | |
344 if (!group) | |
345 return 0; | |
346 return group->sizeOfMembers(); | |
347 } | |
348 | |
349 void RadioButtonGroupScope::updateGroupPosition(HTMLInputElement* element) const | |
350 { | |
351 if (!m_nameToGroupMap) | |
352 return; | |
353 | |
354 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | |
355 if (!group) | |
356 return; | |
357 group->updatePosition(); | |
358 } | |
359 | |
280 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) | 360 void RadioButtonGroupScope::removeButton(HTMLInputElement* element) |
281 { | 361 { |
282 ASSERT(element->type() == InputTypeNames::radio); | 362 ASSERT(element->type() == InputTypeNames::radio); |
283 if (element->name().isEmpty()) | 363 if (element->name().isEmpty()) |
284 return; | 364 return; |
285 if (!m_nameToGroupMap) | 365 if (!m_nameToGroupMap) |
286 return; | 366 return; |
287 | 367 |
288 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); | 368 RadioButtonGroup* group = m_nameToGroupMap->get(element->name()); |
289 if (!group) | 369 if (!group) |
290 return; | 370 return; |
291 group->remove(element); | 371 group->remove(element); |
292 if (group->isEmpty()) { | 372 if (group->isEmpty()) { |
293 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for | 373 // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for |
294 // better performance. | 374 // better performance. |
295 ASSERT(!group->isRequired()); | 375 ASSERT(!group->isRequired()); |
296 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); | 376 ASSERT_WITH_SECURITY_IMPLICATION(!group->checkedButton()); |
297 } | 377 } |
298 } | 378 } |
299 | 379 |
300 DEFINE_TRACE(RadioButtonGroupScope) | 380 DEFINE_TRACE(RadioButtonGroupScope) |
301 { | 381 { |
302 #if ENABLE(OILPAN) | 382 #if ENABLE(OILPAN) |
303 visitor->trace(m_nameToGroupMap); | 383 visitor->trace(m_nameToGroupMap); |
304 #endif | 384 #endif |
305 } | 385 } |
306 | 386 |
307 } // namespace blink | 387 } // namespace blink |
OLD | NEW |