OLD | NEW |
1 /* | 1 /* |
2 * This file is part of the select element layoutObject in WebCore. | 2 * This file is part of the select element layoutObject in WebCore. |
3 * | 3 * |
4 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). | 4 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
5 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. | 5 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. |
6 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.com/) | 6 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.com/) |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 | 49 |
50 namespace blink { | 50 namespace blink { |
51 | 51 |
52 using namespace HTMLNames; | 52 using namespace HTMLNames; |
53 | 53 |
54 LayoutMenuList::LayoutMenuList(Element* element) | 54 LayoutMenuList::LayoutMenuList(Element* element) |
55 : LayoutFlexibleBox(element) | 55 : LayoutFlexibleBox(element) |
56 , m_buttonText(nullptr) | 56 , m_buttonText(nullptr) |
57 , m_innerBlock(nullptr) | 57 , m_innerBlock(nullptr) |
58 , m_optionsChanged(true) | 58 , m_optionsChanged(true) |
59 , m_isEmpty(false) | |
60 , m_hasUpdatedActiveOption(false) | |
61 , m_popupIsVisible(false) | |
62 , m_optionsWidth(0) | 59 , m_optionsWidth(0) |
63 , m_lastActiveIndex(-1) | 60 , m_lastActiveIndex(-1) |
| 61 , m_popupIsVisible(false) |
64 , m_indexToSelectOnCancel(-1) | 62 , m_indexToSelectOnCancel(-1) |
65 { | 63 { |
66 ASSERT(isHTMLSelectElement(element)); | 64 ASSERT(isHTMLSelectElement(element)); |
67 } | 65 } |
68 | 66 |
69 LayoutMenuList::~LayoutMenuList() | 67 LayoutMenuList::~LayoutMenuList() |
70 { | 68 { |
71 ASSERT(!m_popup); | 69 ASSERT(!m_popup); |
72 } | 70 } |
73 | 71 |
(...skipping 16 matching lines...) Expand all Loading... |
90 { | 88 { |
91 if (m_innerBlock) { | 89 if (m_innerBlock) { |
92 ASSERT(firstChild() == m_innerBlock); | 90 ASSERT(firstChild() == m_innerBlock); |
93 ASSERT(!m_innerBlock->nextSibling()); | 91 ASSERT(!m_innerBlock->nextSibling()); |
94 return; | 92 return; |
95 } | 93 } |
96 | 94 |
97 // Create an anonymous block. | 95 // Create an anonymous block. |
98 ASSERT(!firstChild()); | 96 ASSERT(!firstChild()); |
99 m_innerBlock = createAnonymousBlock(); | 97 m_innerBlock = createAnonymousBlock(); |
100 | |
101 m_buttonText = new LayoutText(&document(), StringImpl::empty()); | |
102 // We need to set the text explicitly though it was specified in the | |
103 // constructor because LayoutText doesn't refer to the text | |
104 // specified in the constructor in a case of re-transforming. | |
105 m_buttonText->setStyle(mutableStyle()); | |
106 m_innerBlock->addChild(m_buttonText); | |
107 | |
108 adjustInnerStyle(); | 98 adjustInnerStyle(); |
109 LayoutFlexibleBox::addChild(m_innerBlock); | 99 LayoutFlexibleBox::addChild(m_innerBlock); |
110 } | 100 } |
111 | 101 |
112 void LayoutMenuList::adjustInnerStyle() | 102 void LayoutMenuList::adjustInnerStyle() |
113 { | 103 { |
114 ComputedStyle& innerStyle = m_innerBlock->mutableStyleRef(); | 104 ComputedStyle& innerStyle = m_innerBlock->mutableStyleRef(); |
115 innerStyle.setFlexGrow(1); | 105 innerStyle.setFlexGrow(1); |
116 innerStyle.setFlexShrink(1); | 106 innerStyle.setFlexShrink(1); |
117 // min-width: 0; is needed for correct shrinking. | 107 // min-width: 0; is needed for correct shrinking. |
(...skipping 21 matching lines...) Expand all Loading... |
139 } | 129 } |
140 } | 130 } |
141 | 131 |
142 inline HTMLSelectElement* LayoutMenuList::selectElement() const | 132 inline HTMLSelectElement* LayoutMenuList::selectElement() const |
143 { | 133 { |
144 return toHTMLSelectElement(node()); | 134 return toHTMLSelectElement(node()); |
145 } | 135 } |
146 | 136 |
147 void LayoutMenuList::addChild(LayoutObject* newChild, LayoutObject* beforeChild) | 137 void LayoutMenuList::addChild(LayoutObject* newChild, LayoutObject* beforeChild) |
148 { | 138 { |
| 139 createInnerBlock(); |
149 m_innerBlock->addChild(newChild, beforeChild); | 140 m_innerBlock->addChild(newChild, beforeChild); |
150 ASSERT(m_innerBlock == firstChild()); | 141 ASSERT(m_innerBlock == firstChild()); |
151 | 142 |
152 if (AXObjectCache* cache = document().existingAXObjectCache()) | 143 if (AXObjectCache* cache = document().existingAXObjectCache()) |
153 cache->childrenChanged(this); | 144 cache->childrenChanged(this); |
154 } | 145 } |
155 | 146 |
156 void LayoutMenuList::removeChild(LayoutObject* oldChild) | 147 void LayoutMenuList::removeChild(LayoutObject* oldChild) |
157 { | 148 { |
158 if (oldChild == m_innerBlock || !m_innerBlock) { | 149 if (oldChild == m_innerBlock || !m_innerBlock) { |
159 LayoutFlexibleBox::removeChild(oldChild); | 150 LayoutFlexibleBox::removeChild(oldChild); |
160 m_innerBlock = nullptr; | 151 m_innerBlock = nullptr; |
161 } else { | 152 } else { |
162 m_innerBlock->removeChild(oldChild); | 153 m_innerBlock->removeChild(oldChild); |
163 } | 154 } |
164 } | 155 } |
165 | 156 |
166 void LayoutMenuList::styleDidChange(StyleDifference diff, const ComputedStyle* o
ldStyle) | 157 void LayoutMenuList::styleDidChange(StyleDifference diff, const ComputedStyle* o
ldStyle) |
167 { | 158 { |
168 LayoutBlock::styleDidChange(diff, oldStyle); | 159 LayoutBlock::styleDidChange(diff, oldStyle); |
169 | 160 |
170 if (!m_innerBlock) | 161 if (m_buttonText) |
171 createInnerBlock(); | 162 m_buttonText->setStyle(mutableStyle()); |
172 | 163 if (m_innerBlock) // LayoutBlock handled updating the anonymous block's styl
e. |
173 m_buttonText->setStyle(mutableStyle()); | 164 adjustInnerStyle(); |
174 adjustInnerStyle(); | |
175 | 165 |
176 bool fontChanged = !oldStyle || oldStyle->font() != style()->font(); | 166 bool fontChanged = !oldStyle || oldStyle->font() != style()->font(); |
177 if (fontChanged) | 167 if (fontChanged) |
178 updateOptionsWidth(); | 168 updateOptionsWidth(); |
179 } | 169 } |
180 | 170 |
181 void LayoutMenuList::updateOptionsWidth() | 171 void LayoutMenuList::updateOptionsWidth() |
182 { | 172 { |
183 float maxOptionWidth = 0; | 173 float maxOptionWidth = 0; |
184 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = selectE
lement()->listItems(); | 174 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = selectE
lement()->listItems(); |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 } | 280 } |
291 | 281 |
292 setText(text.stripWhiteSpace()); | 282 setText(text.stripWhiteSpace()); |
293 | 283 |
294 didUpdateActiveOption(optionIndex); | 284 didUpdateActiveOption(optionIndex); |
295 } | 285 } |
296 | 286 |
297 void LayoutMenuList::setText(const String& s) | 287 void LayoutMenuList::setText(const String& s) |
298 { | 288 { |
299 if (s.isEmpty()) { | 289 if (s.isEmpty()) { |
300 // FIXME: This is a hack. We need the select to have the same baseline p
ositioning as | 290 if (!m_buttonText || !m_buttonText->isBR()) { |
301 // any surrounding text. Wihtout any content, we align the bottom of the
select to the bottom | 291 // FIXME: We should not modify the structure of the layout tree |
302 // of the text. With content (In this case the faked " ") we correctly a
lign the middle of | 292 // during layout. crbug.com/370462 |
303 // the select to the middle of the text. It should be possible to remove
this, just set | 293 DeprecatedDisableModifyLayoutTreeStructureAsserts disabler; |
304 // s.impl() into the text and have things align correctly ... crbug.com
/485982 | 294 if (m_buttonText) |
305 m_isEmpty = true; | 295 m_buttonText->destroy(); |
306 m_buttonText->setText(StringImpl::create(" ", 1), true); | 296 m_buttonText = new LayoutBR(&document()); |
| 297 m_buttonText->setStyle(mutableStyle()); |
| 298 addChild(m_buttonText); |
| 299 } |
307 } else { | 300 } else { |
308 m_isEmpty = false; | 301 if (m_buttonText && !m_buttonText->isBR()) { |
309 m_buttonText->setText(s.impl(), true); | 302 m_buttonText->setText(s.impl(), true); |
| 303 } else { |
| 304 // FIXME: We should not modify the structure of the layout tree |
| 305 // during layout. crbug.com/370462 |
| 306 DeprecatedDisableModifyLayoutTreeStructureAsserts disabler; |
| 307 if (m_buttonText) |
| 308 m_buttonText->destroy(); |
| 309 m_buttonText = new LayoutText(&document(), s.impl()); |
| 310 m_buttonText->setStyle(mutableStyle()); |
| 311 // We need to set the text explicitly though it was specified in the |
| 312 // constructor because LayoutText doesn't refer to the text |
| 313 // specified in the constructor in a case of re-transforming. |
| 314 m_buttonText->setText(s.impl(), true); |
| 315 addChild(m_buttonText); |
| 316 } |
| 317 adjustInnerStyle(); |
310 } | 318 } |
311 adjustInnerStyle(); | |
312 } | 319 } |
313 | 320 |
314 String LayoutMenuList::text() const | 321 String LayoutMenuList::text() const |
315 { | 322 { |
316 return m_buttonText && !m_isEmpty ? m_buttonText->text() : String(); | 323 return m_buttonText ? m_buttonText->text() : String(); |
317 } | 324 } |
318 | 325 |
319 LayoutRect LayoutMenuList::controlClipRect(const LayoutPoint& additionalOffset)
const | 326 LayoutRect LayoutMenuList::controlClipRect(const LayoutPoint& additionalOffset)
const |
320 { | 327 { |
321 // Clip to the intersection of the content box and the content box for the i
nner box | 328 // Clip to the intersection of the content box and the content box for the i
nner box |
322 // This will leave room for the arrows which sit in the inner box padding, | 329 // This will leave room for the arrows which sit in the inner box padding, |
323 // and if the inner box ever spills out of the outer box, that will get clip
ped too. | 330 // and if the inner box ever spills out of the outer box, that will get clip
ped too. |
324 LayoutRect outerBox = contentBoxRect(); | 331 LayoutRect outerBox = contentBoxRect(); |
325 outerBox.moveBy(additionalOffset); | 332 outerBox.moveBy(additionalOffset); |
326 | 333 |
(...skipping 12 matching lines...) Expand all Loading... |
339 } | 346 } |
340 | 347 |
341 void LayoutMenuList::showPopup() | 348 void LayoutMenuList::showPopup() |
342 { | 349 { |
343 if (m_popupIsVisible) | 350 if (m_popupIsVisible) |
344 return; | 351 return; |
345 | 352 |
346 if (document().frameHost()->chrome().hasOpenedPopup()) | 353 if (document().frameHost()->chrome().hasOpenedPopup()) |
347 return; | 354 return; |
348 | 355 |
| 356 // Create m_innerBlock here so it ends up as the first child. |
| 357 // This is important because otherwise we might try to create m_innerBlock |
| 358 // inside the showPopup call and it would fail. |
| 359 createInnerBlock(); |
349 if (!m_popup) | 360 if (!m_popup) |
350 m_popup = document().frameHost()->chrome().createPopupMenu(*document().f
rame(), this); | 361 m_popup = document().frameHost()->chrome().createPopupMenu(*document().f
rame(), this); |
351 m_popupIsVisible = true; | 362 m_popupIsVisible = true; |
352 | 363 |
353 FloatQuad quad(localToAbsoluteQuad(FloatQuad(borderBoundingBox()))); | 364 FloatQuad quad(localToAbsoluteQuad(FloatQuad(borderBoundingBox()))); |
354 IntSize size = pixelSnappedIntRect(frameRect()).size(); | 365 IntSize size = pixelSnappedIntRect(frameRect()).size(); |
355 HTMLSelectElement* select = selectElement(); | 366 HTMLSelectElement* select = selectElement(); |
356 m_popup->show(quad, size, select->optionToListIndex(select->selectedIndex())
); | 367 m_popup->show(quad, size, select->optionToListIndex(select->selectedIndex())
); |
357 if (AXObjectCache* cache = document().existingAXObjectCache()) | 368 if (AXObjectCache* cache = document().existingAXObjectCache()) |
358 cache->didShowMenuListPopup(this); | 369 cache->didShowMenuListPopup(this); |
| 370 |
359 } | 371 } |
360 | 372 |
361 void LayoutMenuList::hidePopup() | 373 void LayoutMenuList::hidePopup() |
362 { | 374 { |
363 if (m_popup) | 375 if (m_popup) |
364 m_popup->hide(); | 376 m_popup->hide(); |
365 } | 377 } |
366 | 378 |
367 void LayoutMenuList::valueChanged(unsigned listIndex, bool fireOnChange) | 379 void LayoutMenuList::valueChanged(unsigned listIndex, bool fireOnChange) |
368 { | 380 { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 return; | 428 return; |
417 | 429 |
418 if (m_lastActiveIndex == optionIndex) | 430 if (m_lastActiveIndex == optionIndex) |
419 return; | 431 return; |
420 m_lastActiveIndex = optionIndex; | 432 m_lastActiveIndex = optionIndex; |
421 | 433 |
422 HTMLSelectElement* select = selectElement(); | 434 HTMLSelectElement* select = selectElement(); |
423 int listIndex = select->optionToListIndex(optionIndex); | 435 int listIndex = select->optionToListIndex(optionIndex); |
424 if (listIndex < 0 || listIndex >= static_cast<int>(select->listItems().size(
))) | 436 if (listIndex < 0 || listIndex >= static_cast<int>(select->listItems().size(
))) |
425 return; | 437 return; |
426 | |
427 // We skip sending accessiblity notifications for the very first option, oth
erwise | |
428 // we get extra focus and select events that are undesired. | |
429 if (!m_hasUpdatedActiveOption) { | |
430 m_hasUpdatedActiveOption = true; | |
431 return; | |
432 } | |
433 | |
434 document().existingAXObjectCache()->handleUpdateActiveMenuOption(this, optio
nIndex); | 438 document().existingAXObjectCache()->handleUpdateActiveMenuOption(this, optio
nIndex); |
435 } | 439 } |
436 | 440 |
437 String LayoutMenuList::itemText(unsigned listIndex) const | 441 String LayoutMenuList::itemText(unsigned listIndex) const |
438 { | 442 { |
439 HTMLSelectElement* select = selectElement(); | 443 HTMLSelectElement* select = selectElement(); |
440 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = select-
>listItems(); | 444 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = select-
>listItems(); |
441 if (listIndex >= listItems.size()) | 445 if (listIndex >= listItems.size()) |
442 return String(); | 446 return String(); |
443 | 447 |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
619 HTMLElement* element = listItems[listIndex]; | 623 HTMLElement* element = listItems[listIndex]; |
620 return isHTMLOptionElement(*element) && toHTMLOptionElement(*element).select
ed(); | 624 return isHTMLOptionElement(*element) && toHTMLOptionElement(*element).select
ed(); |
621 } | 625 } |
622 | 626 |
623 void LayoutMenuList::provisionalSelectionChanged(unsigned listIndex) | 627 void LayoutMenuList::provisionalSelectionChanged(unsigned listIndex) |
624 { | 628 { |
625 setIndexToSelectOnCancel(listIndex); | 629 setIndexToSelectOnCancel(listIndex); |
626 } | 630 } |
627 | 631 |
628 } // namespace blink | 632 } // namespace blink |
OLD | NEW |