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