Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(100)

Side by Side Diff: Source/core/rendering/RenderListBox.cpp

Issue 347773002: Implement select listbox using shadow DOM (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: gixed slow select-max-length.html Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/) 3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo bile.com/)
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 #include "platform/fonts/FontCache.h" 55 #include "platform/fonts/FontCache.h"
56 #include "platform/graphics/GraphicsContext.h" 56 #include "platform/graphics/GraphicsContext.h"
57 #include "platform/scroll/Scrollbar.h" 57 #include "platform/scroll/Scrollbar.h"
58 #include "platform/text/BidiTextRun.h" 58 #include "platform/text/BidiTextRun.h"
59 #include <math.h> 59 #include <math.h>
60 60
61 namespace WebCore { 61 namespace WebCore {
62 62
63 using namespace HTMLNames; 63 using namespace HTMLNames;
64 64
65 const int rowSpacing = 1;
66
67 const int optionsSpacingHorizontal = 2;
68
69 // The minSize constant was originally defined to render scrollbars correctly. 65 // The minSize constant was originally defined to render scrollbars correctly.
70 // This might vary for different platforms. 66 // This might vary for different platforms.
71 const int minSize = 4; 67 const int minSize = 4;
72 68
73 // Default size when the multiple attribute is present but size attribute is abs ent. 69 // Default size when the multiple attribute is present but size attribute is abs ent.
74 const int defaultSize = 4; 70 const int defaultSize = 4;
75 71
76 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old 72 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
77 // widget, but I'm not sure this is right for the new control. 73 // widget, but I'm not sure this is right for the new control.
78 const int baselineAdjustment = 7; 74 const int baselineAdjustment = 7;
79 75
80 RenderListBox::RenderListBox(Element* element) 76 RenderListBox::RenderListBox(Element* element)
81 : RenderBlockFlow(element) 77 : RenderBlockFlow(element)
82 , m_optionsChanged(true)
83 , m_scrollToRevealSelectionAfterLayout(true)
84 , m_inAutoscroll(false)
85 , m_optionsWidth(0)
86 , m_indexOffset(0)
87 , m_listItemCount(0)
88 { 78 {
89 ASSERT(element); 79 ASSERT(element);
90 ASSERT(element->isHTMLElement()); 80 ASSERT(element->isHTMLElement());
91 ASSERT(isHTMLSelectElement(element)); 81 ASSERT(isHTMLSelectElement(element));
92
93 if (FrameView* frameView = frame()->view())
94 frameView->addScrollableArea(this);
95 } 82 }
96 83
97 RenderListBox::~RenderListBox() 84 RenderListBox::~RenderListBox()
98 { 85 {
99 setHasVerticalScrollbar(false);
100
101 if (FrameView* frameView = frame()->view())
102 frameView->removeScrollableArea(this);
103 }
104
105 // FIXME: Instead of this hack we should add a ShadowRoot to <select> with no in sertion point
106 // to prevent children from rendering.
107 bool RenderListBox::isChildAllowed(RenderObject* object, RenderStyle*) const
108 {
109 return object->isAnonymous() && !object->isRenderFullScreen();
110 } 86 }
111 87
112 inline HTMLSelectElement* RenderListBox::selectElement() const 88 inline HTMLSelectElement* RenderListBox::selectElement() const
113 { 89 {
114 return toHTMLSelectElement(node()); 90 return toHTMLSelectElement(node());
115 } 91 }
116 92
117 void RenderListBox::updateFromElement()
118 {
119 FontCachePurgePreventer fontCachePurgePreventer;
120 if (m_optionsChanged) {
121 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = se lectElement()->listItems();
122 int size = static_cast<int>(listItems.size());
123
124 float width = 0;
125 m_listItemCount = 0;
126 for (int i = 0; i < size; ++i) {
127 const HTMLElement& element = *listItems[i];
128
129 String text;
130 Font itemFont = style()->font();
131 if (isHTMLOptionElement(element)) {
132 const HTMLOptionElement& optionElement = toHTMLOptionElement(ele ment);
133 if (optionElement.isDisplayNone())
134 continue;
135 text = optionElement.textIndentedToRespectGroupLabel();
136 ++m_listItemCount;
137 } else if (isHTMLOptGroupElement(element)) {
138 if (toHTMLOptGroupElement(element).isDisplayNone())
139 continue;
140 text = toHTMLOptGroupElement(element).groupLabelText();
141 FontDescription d = itemFont.fontDescription();
142 d.setWeight(d.bolderWeight());
143 itemFont = Font(d);
144 itemFont.update(document().styleEngine()->fontSelector());
145 ++m_listItemCount;
146 } else if (isHTMLHRElement(element)) {
147 // HTMLSelect adds it to its list, so we will also add it to mat ch the count.
148 ++m_listItemCount;
149 continue;
150 }
151
152 if (!text.isEmpty()) {
153 applyTextTransform(style(), text, ' ');
154
155 bool hasStrongDirectionality;
156 TextDirection direction = determineDirectionality(text, hasStron gDirectionality);
157 TextRun textRun = constructTextRun(this, itemFont, text, style() , TextRun::AllowTrailingExpansion);
158 if (hasStrongDirectionality)
159 textRun.setDirection(direction);
160 textRun.disableRoundingHacks();
161 float textWidth = itemFont.width(textRun);
162 width = std::max(width, textWidth);
163 }
164 }
165 m_optionsWidth = static_cast<int>(ceilf(width));
166 m_optionsChanged = false;
167
168 setHasVerticalScrollbar(true);
169
170 setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
171 }
172 }
173
174 void RenderListBox::selectionChanged()
175 {
176 paintInvalidationForWholeRenderer();
177 if (!m_inAutoscroll) {
178 if (m_optionsChanged || needsLayout())
179 m_scrollToRevealSelectionAfterLayout = true;
180 else
181 scrollToRevealSelection();
182 }
183
184 if (AXObjectCache* cache = document().existingAXObjectCache())
185 cache->selectedChildrenChanged(this);
186 }
187
188 void RenderListBox::layout()
189 {
190 RenderBlockFlow::layout();
191
192 if (m_vBar) {
193 bool enabled = numVisibleItems() < numItems();
194 m_vBar->setEnabled(enabled);
195 m_vBar->setProportion(numVisibleItems(), numItems());
196 if (!enabled) {
197 scrollToOffsetWithoutAnimation(VerticalScrollbar, 0);
198 m_indexOffset = 0;
199 }
200 }
201
202 if (m_scrollToRevealSelectionAfterLayout) {
203 ForceHorriblySlowRectMapping slowRectMapping(*this);
204 scrollToRevealSelection();
205 }
206 }
207
208 void RenderListBox::invalidateTreeAfterLayout(const RenderLayerModelObject& inva lidationContainer)
209 {
210 repaintScrollbarIfNeeded();
211 RenderBox::invalidateTreeAfterLayout(invalidationContainer);
212 }
213
214 void RenderListBox::scrollToRevealSelection()
215 {
216 HTMLSelectElement* select = selectElement();
217
218 m_scrollToRevealSelectionAfterLayout = false;
219
220 int firstIndex = listIndexToRenderListBoxIndex(select->activeSelectionStartL istIndex());
221 int lastIndex = listIndexToRenderListBoxIndex(select->activeSelectionEndList Index());
222 if (firstIndex >= 0 && !listIndexIsVisible(lastIndex))
223 scrollToRevealElementAtListIndexInternal(firstIndex);
224 }
225
226 void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, L ayoutUnit& maxLogicalWidth) const
227 {
228 maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal + verticalSc rollbarWidth();
229 if (!style()->width().isPercent())
230 minLogicalWidth = maxLogicalWidth;
231 }
232
233 void RenderListBox::computePreferredLogicalWidths()
234 {
235 ASSERT(!m_optionsChanged);
236
237 m_minPreferredLogicalWidth = 0;
238 m_maxPreferredLogicalWidth = 0;
239 RenderStyle* styleToUse = style();
240
241 if (styleToUse->width().isFixed() && styleToUse->width().value() > 0)
242 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentB oxLogicalWidthForBoxSizing(styleToUse->width().value());
243 else
244 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferred LogicalWidth);
245
246 if (styleToUse->minWidth().isFixed() && styleToUse->minWidth().value() > 0) {
247 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value()));
248 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value()));
249 }
250
251 if (styleToUse->maxWidth().isFixed()) {
252 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value()));
253 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjust ContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value()));
254 }
255
256 LayoutUnit toAdd = borderAndPaddingWidth();
257 m_minPreferredLogicalWidth += toAdd;
258 m_maxPreferredLogicalWidth += toAdd;
259
260 clearPreferredLogicalWidthsDirty();
261 }
262
263 int RenderListBox::size() const 93 int RenderListBox::size() const
264 { 94 {
265 int specifiedSize = selectElement()->size(); 95 int specifiedSize = selectElement()->size();
266 if (specifiedSize > 1) 96 if (specifiedSize > 1)
267 return std::max(minSize, specifiedSize); 97 return max(minSize, specifiedSize);
268 98
269 return defaultSize; 99 return defaultSize;
270 } 100 }
271 101
272 int RenderListBox::numVisibleItems() const 102 LayoutUnit RenderListBox::defaultItemHeight() const
273 { 103 {
274 // Only count fully visible rows. But don't return 0 even if only part of a row shows. 104 return style()->fontMetrics().height();
275 return std::max<int>(1, (contentHeight() + rowSpacing) / itemHeight());
276 } 105 }
277 106
278 int RenderListBox::numItems() const 107 LayoutUnit RenderListBox::itemHeight() const
279 { 108 {
280 return m_listItemCount; 109 HTMLSelectElement* select = selectElement();
281 } 110 if (!select)
282 111 return 0;
283 LayoutUnit RenderListBox::listHeight() const 112 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& items = selectElem ent()->listItems();
284 { 113 if (items.size() == 0)
285 return itemHeight() * numItems() - rowSpacing; 114 return defaultItemHeight();
115 const Member<HTMLElement>& firstItem = items.first();
116 if (isHTMLOptionElement(firstItem)) {
117 RenderObject* optionRenderer = firstItem->renderer();
118 if (!optionRenderer)
119 return defaultItemHeight();
120 return optionRenderer->absoluteBoundingBoxRect().height();
esprehn 2014/06/23 21:59:57 I don't think this is the correct way to figure ou
keishi 2014/07/01 04:53:33 Tried RenderObject::height
121 }
122 if (isHTMLOptGroupElement(firstItem))
123 return toHTMLOptGroupElement(firstItem)->groupLabelHeight();
esprehn 2014/06/23 21:59:57 You shouldn't need to ask the DOM for this, you're
keishi 2014/07/01 04:53:33 I try to stay in the render tree in this next patc
124 return defaultItemHeight();
286 } 125 }
287 126
288 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const 127 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, Logi calExtentComputedValues& computedValues) const
289 { 128 {
290 LayoutUnit height = itemHeight() * size() - rowSpacing; 129 LayoutUnit height = itemHeight() * size();
291 // FIXME: The item height should have been added before updateLogicalHeight was called to avoid this hack. 130 // FIXME: The item height should have been added before updateLogicalHeight was called to avoid this hack.
292 updateIntrinsicContentLogicalHeight(height); 131 updateIntrinsicContentLogicalHeight(height);
293 132
294 height += borderAndPaddingHeight(); 133 height += borderAndPaddingHeight();
295 134
296 RenderBox::computeLogicalHeight(height, logicalTop, computedValues); 135 RenderBox::computeLogicalHeight(height, logicalTop, computedValues);
297 } 136 }
298 137
299 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L ineDirectionMode lineDirection, LinePositionMode linePositionMode) const 138 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, L ineDirectionMode lineDirection, LinePositionMode linePositionMode) const
300 { 139 {
301 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, l inePositionMode) - baselineAdjustment; 140 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, l inePositionMode) - baselineAdjustment;
302 } 141 }
303 142
304 LayoutRect RenderListBox::itemBoundingBoxRectInternal(const LayoutPoint& additio nalOffset, int index) const
305 {
306 // For RTL, items start after the left-side vertical scrollbar.
307 int scrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogicalLe ft() ? verticalScrollbarWidth() : 0;
308 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft() + scro llbarOffset,
309 additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (inde x - m_indexOffset),
310 contentWidth(), itemHeight());
311 }
312
313 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOf fset)
314 {
315 if (style()->visibility() != VISIBLE)
316 return;
317
318 int listItemsSize = numItems();
319
320 if (paintInfo.phase == PaintPhaseForeground) {
321 int index = m_indexOffset;
322 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) {
323 paintItemForeground(paintInfo, paintOffset, index);
324 index++;
325 }
326 }
327
328 // Paint the children.
329 RenderBlockFlow::paintObject(paintInfo, paintOffset);
330
331 switch (paintInfo.phase) {
332 // Depending on whether we have overlay scrollbars they
333 // get rendered in the foreground or background phases
334 case PaintPhaseForeground:
335 if (m_vBar->isOverlayScrollbar())
336 paintScrollbar(paintInfo, paintOffset);
337 break;
338 case PaintPhaseBlockBackground:
339 if (!m_vBar->isOverlayScrollbar())
340 paintScrollbar(paintInfo, paintOffset);
341 break;
342 case PaintPhaseChildBlockBackground:
343 case PaintPhaseChildBlockBackgrounds: {
344 int index = m_indexOffset;
345 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems ()) {
346 paintItemBackground(paintInfo, paintOffset, index);
347 index++;
348 }
349 break;
350 }
351 default:
352 break;
353 }
354 }
355
356 void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
357 {
358 if (!isSpatialNavigationEnabled(frame()))
359 return RenderBlockFlow::addFocusRingRects(rects, additionalOffset, paint Container);
360
361 HTMLSelectElement* select = selectElement();
362
363 // Focus the last selected item.
364 int selectedItem = select->activeSelectionEndListIndex();
365 if (selectedItem >= 0) {
366 rects.append(pixelSnappedIntRect(itemBoundingBoxRectInternal(additionalO ffset, selectedItem)));
367 return;
368 }
369
370 // No selected items, find the first non-disabled item.
371 int size = numItems();
372 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select ->listItems();
373 for (int i = 0; i < size; ++i) {
374 HTMLElement* element = listItems[renderListBoxIndexToListIndex(i)];
375 if (isHTMLOptionElement(*element) && !element->isDisabledFormControl()) {
376 rects.append(pixelSnappedIntRect(itemBoundingBoxRectInternal(additio nalOffset, i)));
377 return;
378 }
379 }
380 }
381
382 int RenderListBox::scrollbarLeft() const
383 {
384 int scrollbarLeft = 0;
385 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
386 scrollbarLeft = borderLeft();
387 else
388 scrollbarLeft = width() - borderRight() - (m_vBar ? m_vBar->width() : 0) ;
389 return scrollbarLeft;
390 }
391
392 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& pain tOffset)
393 {
394 if (m_vBar) {
395 IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + scrollbarLeft (),
396 paintOffset.y() + borderTop(),
397 m_vBar->width(),
398 height() - (borderTop() + borderBottom()));
399 m_vBar->setFrameRect(scrollRect);
400 m_vBar->paint(paintInfo.context, paintInfo.rect);
401 }
402 }
403
404 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle , Font itemFont, LayoutRect itemBoudingBox)
405 {
406 ETextAlign actualAlignment = itemStyle->textAlign();
407 // FIXME: Firefox doesn't respect JUSTIFY. Should we?
408 // FIXME: Handle TAEND here
409 if (actualAlignment == TASTART || actualAlignment == JUSTIFY)
410 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT;
411
412 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent());
413 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) {
414 float textWidth = itemFont.width(textRun);
415 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHoriz ontal);
416 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) {
417 float textWidth = itemFont.width(textRun);
418 offset.setWidth((itemBoudingBox.width() - textWidth) / 2);
419 } else
420 offset.setWidth(optionsSpacingHorizontal);
421 return offset;
422 }
423
424 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
425 {
426 FontCachePurgePreventer fontCachePurgePreventer;
427
428 HTMLSelectElement* select = selectElement();
429
430 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select ->listItems();
431 HTMLElement* element = listItems[renderListBoxIndexToListIndex(listIndex)];
432
433 RenderStyle* itemStyle = element->renderStyle();
434 if (!itemStyle)
435 itemStyle = style();
436
437 if (itemStyle->visibility() == HIDDEN)
438 return;
439
440 String itemText;
441 bool isOptionElement = isHTMLOptionElement(*element);
442 if (isOptionElement)
443 itemText = toHTMLOptionElement(*element).textIndentedToRespectGroupLabel ();
444 else if (isHTMLOptGroupElement(*element))
445 itemText = toHTMLOptGroupElement(*element).groupLabelText();
446 applyTextTransform(style(), itemText, ' ');
447
448 Color textColor = element->renderStyle() ? resolveColor(element->renderStyle (), CSSPropertyColor) : resolveColor(CSSPropertyColor);
449 if (isOptionElement && ((toHTMLOptionElement(*element).selected() && select- >suggestedIndex() < 0) || listIndex == select->suggestedIndex())) {
450 if (frame()->selection().isFocusedAndActive() && document().focusedEleme nt() == node())
451 textColor = RenderTheme::theme().activeListBoxSelectionForegroundCol or();
452 // Honor the foreground color for disabled items
453 else if (!element->isDisabledFormControl() && !select->isDisabledFormCon trol())
454 textColor = RenderTheme::theme().inactiveListBoxSelectionForegroundC olor();
455 }
456
457 paintInfo.context->setFillColor(textColor);
458
459 TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle-> direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding);
460 Font itemFont = style()->font();
461 LayoutRect r = itemBoundingBoxRectInternal(paintOffset, listIndex);
462 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r));
463
464 if (isHTMLOptGroupElement(*element)) {
465 FontDescription d = itemFont.fontDescription();
466 d.setWeight(d.bolderWeight());
467 itemFont = Font(d);
468 itemFont.update(document().styleEngine()->fontSelector());
469 }
470
471 // Draw the item text
472 TextRunPaintInfo textRunPaintInfo(textRun);
473 textRunPaintInfo.bounds = r;
474 paintInfo.context->drawBidiText(itemFont, textRunPaintInfo, roundedIntPoint( r.location()));
475 }
476
477 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
478 {
479 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems();
480 HTMLElement* element = listItems[renderListBoxIndexToListIndex(listIndex)];
481
482 Color backColor;
483 if (isHTMLOptionElement(*element) && ((toHTMLOptionElement(*element).selecte d() && selectElement()->suggestedIndex() < 0) || listIndex == selectElement()->s uggestedIndex())) {
484 if (frame()->selection().isFocusedAndActive() && document().focusedEleme nt() == node())
485 backColor = RenderTheme::theme().activeListBoxSelectionBackgroundCol or();
486 else
487 backColor = RenderTheme::theme().inactiveListBoxSelectionBackgroundC olor();
488 } else {
489 backColor = element->renderStyle() ? resolveColor(element->renderStyle() , CSSPropertyBackgroundColor) : resolveColor(CSSPropertyBackgroundColor);
490 }
491
492 // Draw the background for this list box item
493 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDE N) {
494 LayoutRect itemRect = itemBoundingBoxRectInternal(paintOffset, listIndex );
495 itemRect.intersect(controlClipRect(paintOffset));
496 paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor);
497 }
498 }
499
500 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const Layout Point& locationInContainer, const LayoutPoint& accumulatedOffset)
501 {
502 if (!m_vBar || !m_vBar->shouldParticipateInHitTesting())
503 return false;
504
505 LayoutRect vertRect(accumulatedOffset.x() + scrollbarLeft(),
506 accumulatedOffset.y() + borderTop(),
507 verticalScrollbarWidth(),
508 height() - borderTop() - borderBottom());
509
510 if (vertRect.contains(locationInContainer)) {
511 result.setScrollbar(m_vBar.get());
512 return true;
513 }
514 return false;
515 }
516
517 int RenderListBox::listIndexAtOffset(const LayoutSize& offset) const
518 {
519 if (!numItems())
520 return -1;
521
522 if (offset.height() < borderTop() + paddingTop() || offset.height() > height () - paddingBottom() - borderBottom())
523 return -1;
524
525 int scrollbarWidth = verticalScrollbarWidth();
526 int rightScrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogi calLeft() ? scrollbarWidth : 0;
527 int leftScrollbarOffset = style()->shouldPlaceBlockDirectionScrollbarOnLogic alLeft() ? 0 : scrollbarWidth;
528 if (offset.width() < borderLeft() + paddingLeft() + rightScrollbarOffset
529 || offset.width() > width() - borderRight() - paddingRight() - leftScrol lbarOffset)
530 return -1;
531
532 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight( ) + m_indexOffset;
533 return newOffset < numItems() ? renderListBoxIndexToListIndex(newOffset) : - 1;
534 }
535
536 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
537 {
538 const int maxSpeed = 20;
539 const int iconRadius = 7;
540 const int speedReducer = 4;
541
542 // FIXME: This doesn't work correctly with transforms.
543 FloatPoint absOffset = localToAbsolute();
544
545 IntPoint lastKnownMousePosition = frame()->eventHandler().lastKnownMousePosi tion();
546 // We need to check if the last known mouse position is out of the window. W hen the mouse is out of the window, the position is incoherent
547 static IntPoint previousMousePosition;
548 if (lastKnownMousePosition.y() < 0)
549 lastKnownMousePosition = previousMousePosition;
550 else
551 previousMousePosition = lastKnownMousePosition;
552
553 int yDelta = lastKnownMousePosition.y() - panStartMousePosition.y();
554
555 // If the point is too far from the center we limit the speed
556 yDelta = std::max<int>(std::min<int>(yDelta, maxSpeed), -maxSpeed);
557
558 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
559 return;
560
561 if (yDelta > 0)
562 absOffset.move(0, listHeight().toFloat());
563 else if (yDelta < 0)
564 yDelta--;
565
566 // Let's attenuate the speed
567 yDelta /= speedReducer;
568
569 IntPoint scrollPoint(0, 0);
570 scrollPoint.setY(absOffset.y() + yDelta);
571 int newOffset = scrollToward(scrollPoint);
572 if (newOffset < 0)
573 return;
574
575 m_inAutoscroll = true;
576 HTMLSelectElement* select = selectElement();
577 select->updateListBoxSelection(!select->multiple());
578 m_inAutoscroll = false;
579 }
580
581 int RenderListBox::scrollToward(const IntPoint& destination)
582 {
583 // FIXME: This doesn't work correctly with transforms.
584 FloatPoint absPos = localToAbsolute();
585 IntSize positionOffset = roundedIntSize(destination - absPos);
586
587 int rows = numVisibleItems();
588 int offset = m_indexOffset;
589
590 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealEl ementAtListIndexInternal(offset - 1))
591 return offset - 1;
592
593 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndexInternal(offset + rows))
594 return offset + rows - 1;
595
596 return listIndexAtOffset(positionOffset);
597 }
598
599 void RenderListBox::autoscroll(const IntPoint&)
600 {
601 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler().las tKnownMousePosition());
602
603 int endIndex = scrollToward(pos);
604 if (selectElement()->isDisabledFormControl())
605 return;
606
607 if (endIndex >= 0) {
608 HTMLSelectElement* select = selectElement();
609 m_inAutoscroll = true;
610
611 if (!select->multiple())
612 select->setActiveSelectionAnchorIndex(renderListBoxIndexToListIndex( endIndex));
613
614 select->setActiveSelectionEndIndex(renderListBoxIndexToListIndex(endInde x));
615 select->updateListBoxSelection(!select->multiple());
616 m_inAutoscroll = false;
617 }
618 }
619
620 void RenderListBox::stopAutoscroll()
621 {
622 if (selectElement()->isDisabledFormControl())
623 return;
624
625 selectElement()->listBoxOnChange();
626 }
627
628 bool RenderListBox::scrollToRevealElementAtListIndexInternal(int index)
629 {
630 if (index < 0 || index >= numItems() || listIndexIsVisible(index))
631 return false;
632
633 int newOffset;
634 if (index < m_indexOffset)
635 newOffset = index;
636 else
637 newOffset = index - numVisibleItems() + 1;
638
639 scrollToOffsetWithoutAnimation(VerticalScrollbar, newOffset);
640
641 return true;
642 }
643
644 bool RenderListBox::listIndexIsVisible(int index) const
645 {
646 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
647 }
648
649 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granular ity, float multiplier)
650 {
651 return ScrollableArea::scroll(direction, granularity, multiplier);
652 }
653
654 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
655 {
656 return orientation == VerticalScrollbar ? (numItems() - numVisibleItems()) : 0;
657 }
658
659 IntPoint RenderListBox::scrollPosition() const
660 {
661 return IntPoint(0, m_indexOffset);
662 }
663
664 void RenderListBox::setScrollOffset(const IntPoint& offset)
665 {
666 scrollTo(offset.y());
667 }
668
669 void RenderListBox::scrollTo(int newOffset)
670 {
671 if (newOffset == m_indexOffset)
672 return;
673
674 m_indexOffset = newOffset;
675
676 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isIn PerformLayout())
677 setShouldDoFullPaintInvalidationAfterLayout(true);
678 else
679 paintInvalidationForWholeRenderer();
680
681 node()->document().enqueueScrollEventForNode(node());
682 }
683
684 LayoutUnit RenderListBox::itemHeight() const
685 {
686 return style()->fontMetrics().height() + rowSpacing;
687 }
688
689 int RenderListBox::verticalScrollbarWidth() const
690 {
691 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
692 }
693
694 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
695 // how the control currently paints.
696 LayoutUnit RenderListBox::scrollWidth() const
697 {
698 // There is no horizontal scrolling allowed.
699 return clientWidth();
700 }
701
702 LayoutUnit RenderListBox::scrollHeight() const
703 {
704 return std::max(clientHeight(), listHeight());
705 }
706
707 LayoutUnit RenderListBox::scrollLeft() const
708 {
709 return 0;
710 }
711
712 void RenderListBox::setScrollLeft(LayoutUnit)
713 {
714 }
715
716 LayoutUnit RenderListBox::scrollTop() const
717 {
718 return m_indexOffset * itemHeight();
719 }
720
721 void RenderListBox::setScrollTop(LayoutUnit newTop)
722 {
723 // Determine an index and scroll to it.
724 int index = newTop / itemHeight();
725 if (index < 0 || index >= numItems() || index == m_indexOffset)
726 return;
727
728 scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
729 }
730
731 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re sult, const HitTestLocation& locationInContainer, const LayoutPoint& accumulated Offset, HitTestAction hitTestAction)
732 {
733 if (!RenderBlockFlow::nodeAtPoint(request, result, locationInContainer, accu mulatedOffset, hitTestAction))
734 return false;
735 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems();
736 int size = numItems();
737 LayoutPoint adjustedLocation = accumulatedOffset + location();
738
739 for (int i = 0; i < size; ++i) {
740 if (itemBoundingBoxRectInternal(adjustedLocation, i).contains(locationIn Container.point())) {
741 if (Element* node = listItems[renderListBoxIndexToListIndex(i)]) {
742 result.setInnerNode(node);
743 if (!result.innerNonSharedNode())
744 result.setInnerNonSharedNode(node);
745 result.setLocalPoint(locationInContainer.point() - toLayoutSize( adjustedLocation));
746 break;
747 }
748 }
749 }
750
751 return true;
752 }
753
754 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) c onst
755 {
756 LayoutRect clipRect = contentBoxRect();
757 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
758 clipRect.moveBy(additionalOffset + LayoutPoint(verticalScrollbarWidth(), 0));
759 else
760 clipRect.moveBy(additionalOffset);
761 return clipRect;
762 }
763
764 bool RenderListBox::isActive() const
765 {
766 Page* page = frame()->page();
767 return page && page->focusController().isActive();
768 }
769
770 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
771 {
772 IntRect scrollRect = rect;
773 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
774 scrollRect.move(borderLeft(), borderTop());
775 else
776 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop( ));
777
778 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isIn PerformLayout()) {
779 m_verticalBarDamage = scrollRect;
780 m_hasVerticalBarDamage = true;
781 } else {
782 invalidatePaintRectangle(scrollRect);
783 }
784 }
785
786 void RenderListBox::repaintScrollbarIfNeeded()
787 {
788 if (!hasVerticalBarDamage())
789 return;
790 invalidatePaintRectangle(verticalBarDamage());
791
792 resetScrollbarDamage();
793 }
794
795 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scr ollbar, const IntRect& scrollbarRect) const
796 {
797 RenderView* view = this->view();
798 if (!view)
799 return scrollbarRect;
800
801 IntRect rect = scrollbarRect;
802
803 int scrollbarTop = borderTop();
804 rect.move(scrollbarLeft(), scrollbarTop);
805
806 return view->frameView()->convertFromRenderer(*this, rect);
807 }
808
809 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scr ollbar, const IntRect& parentRect) const
810 {
811 RenderView* view = this->view();
812 if (!view)
813 return parentRect;
814
815 IntRect rect = view->frameView()->convertToRenderer(*this, parentRect);
816
817 int scrollbarTop = borderTop();
818 rect.move(-scrollbarLeft(), -scrollbarTop);
819 return rect;
820 }
821
822 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* sc rollbar, const IntPoint& scrollbarPoint) const
823 {
824 RenderView* view = this->view();
825 if (!view)
826 return scrollbarPoint;
827
828 IntPoint point = scrollbarPoint;
829
830 int scrollbarTop = borderTop();
831 point.move(scrollbarLeft(), scrollbarTop);
832
833 return view->frameView()->convertFromRenderer(*this, point);
834 }
835
836 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* sc rollbar, const IntPoint& parentPoint) const
837 {
838 RenderView* view = this->view();
839 if (!view)
840 return parentPoint;
841
842 IntPoint point = view->frameView()->convertToRenderer(*this, parentPoint);
843
844 int scrollbarTop = borderTop();
845 point.move(-scrollbarLeft(), -scrollbarTop);
846 return point;
847 }
848
849 IntSize RenderListBox::contentsSize() const
850 {
851 return IntSize(scrollWidth(), scrollHeight());
852 }
853
854 int RenderListBox::visibleHeight() const
855 {
856 return height();
857 }
858
859 int RenderListBox::visibleWidth() const
860 {
861 return width();
862 }
863
864 IntPoint RenderListBox::lastKnownMousePosition() const
865 {
866 RenderView* view = this->view();
867 if (!view)
868 return IntPoint();
869 return view->frameView()->lastKnownMousePosition();
870 }
871
872 bool RenderListBox::shouldSuspendScrollAnimations() const
873 {
874 RenderView* view = this->view();
875 if (!view)
876 return true;
877 return view->frameView()->shouldSuspendScrollAnimations();
878 }
879
880 bool RenderListBox::scrollbarsCanBeActive() const
881 {
882 RenderView* view = this->view();
883 if (!view)
884 return false;
885 return view->frameView()->scrollbarsCanBeActive();
886 }
887
888 IntPoint RenderListBox::minimumScrollPosition() const
889 {
890 return IntPoint();
891 }
892
893 IntPoint RenderListBox::maximumScrollPosition() const
894 {
895 return IntPoint(0, std::max(numItems() - numVisibleItems(), 0));
896 }
897
898 bool RenderListBox::userInputScrollable(ScrollbarOrientation orientation) const
899 {
900 return orientation == VerticalScrollbar;
901 }
902
903 bool RenderListBox::shouldPlaceVerticalScrollbarOnLeft() const
904 {
905 return false;
906 }
907
908 int RenderListBox::lineStep(ScrollbarOrientation) const
909 {
910 return 1;
911 }
912
913 int RenderListBox::pageStep(ScrollbarOrientation orientation) const
914 {
915 return std::max(1, numVisibleItems() - 1);
916 }
917
918 float RenderListBox::pixelStep(ScrollbarOrientation) const
919 {
920 return 1.0f / itemHeight();
921 }
922
923 IntRect RenderListBox::scrollableAreaBoundingBox() const
924 {
925 return absoluteBoundingBoxRect();
926 }
927
928 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
929 {
930 RefPtr<Scrollbar> widget;
931 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
932 if (hasCustomScrollbarStyle)
933 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node());
934 else {
935 widget = Scrollbar::create(this, VerticalScrollbar, RenderTheme::theme() .scrollbarControlSizeForPart(ListboxPart));
936 didAddScrollbar(widget.get(), VerticalScrollbar);
937 }
938 document().view()->addChild(widget.get());
939 return widget.release();
940 }
941
942 void RenderListBox::destroyScrollbar()
943 {
944 if (!m_vBar)
945 return;
946
947 if (!m_vBar->isCustomScrollbar())
948 ScrollableArea::willRemoveScrollbar(m_vBar.get(), VerticalScrollbar);
949 m_vBar->removeFromParent();
950 m_vBar->disconnectFromScrollableArea();
951 m_vBar = nullptr;
952 }
953
954 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
955 {
956 if (hasScrollbar == (m_vBar != 0))
957 return;
958
959 if (hasScrollbar)
960 m_vBar = createScrollbar();
961 else
962 destroyScrollbar();
963
964 if (m_vBar)
965 m_vBar->styleChanged();
966
967 // Force an update since we know the scrollbars have changed things.
968 if (document().hasAnnotatedRegions())
969 document().setAnnotatedRegionsDirty(true);
970 }
971
972 int RenderListBox::renderListBoxIndexToListIndex(int index) const
973 {
974 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems();
975 const int size = static_cast<int>(listItems.size());
976
977 if (size == numItems())
978 return index;
979
980 int listBoxIndex = 0;
981 int listIndex = 0;
982 for (; listIndex < size; ++listIndex) {
983 const HTMLElement& element = *listItems[listIndex];
984 if (isHTMLOptionElement(element) && toHTMLOptionElement(element).isDispl ayNone())
985 continue;
986 if (isHTMLOptGroupElement(element) && toHTMLOptGroupElement(element).isD isplayNone())
987 continue;
988 if (index == listBoxIndex)
989 break;
990 ++listBoxIndex;
991 }
992 return listIndex;
993 }
994
995 int RenderListBox::listIndexToRenderListBoxIndex(int index) const
996 {
997 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement> >& listItems = select Element()->listItems();
998 const int size = static_cast<int>(listItems.size());
999
1000 if (size == numItems())
1001 return index;
1002
1003 int listBoxIndex = 0;
1004 for (int listIndex = 0; listIndex < size; ++listIndex) {
1005 const HTMLElement& element = *listItems[listIndex];
1006 if (isHTMLOptionElement(element) && toHTMLOptionElement(element).isDispl ayNone())
1007 continue;
1008 if (isHTMLOptGroupElement(element) && toHTMLOptGroupElement(element).isD isplayNone())
1009 continue;
1010 if (index == listIndex)
1011 break;
1012 ++listBoxIndex;
1013 }
1014 return listBoxIndex;
1015 }
1016
1017 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& point, int inde x) const
1018 {
1019 return itemBoundingBoxRectInternal(point, listIndexToRenderListBoxIndex(inde x));
1020 }
1021
1022 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
1023 {
1024 return scrollToRevealElementAtListIndexInternal(listIndexToRenderListBoxInde x(index));
1025 }
1026
1027 } // namespace WebCore 143 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698