2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "RenderListBox.h"
33 #include "AXObjectCache.h"
34 #include "CSSFontSelector.h"
35 #include "CSSStyleSelector.h"
37 #include "EventHandler.h"
38 #include "EventQueue.h"
39 #include "FocusController.h"
40 #include "FontCache.h"
42 #include "FrameSelection.h"
43 #include "FrameView.h"
44 #include "GraphicsContext.h"
45 #include "HTMLNames.h"
46 #include "HitTestResult.h"
47 #include "NodeRenderStyle.h"
48 #include "OptionGroupElement.h"
49 #include "OptionElement.h"
51 #include "PaintInfo.h"
52 #include "RenderLayer.h"
53 #include "RenderScrollbar.h"
54 #include "RenderTheme.h"
55 #include "RenderView.h"
56 #include "Scrollbar.h"
57 #include "ScrollbarTheme.h"
58 #include "SelectElement.h"
59 #include "SpatialNavigation.h"
66 using namespace HTMLNames;
68 const int rowSpacing = 1;
70 const int optionsSpacingHorizontal = 2;
72 const int minSize = 4;
73 const int maxDefaultSize = 10;
75 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
76 // widget, but I'm not sure this is right for the new control.
77 const int baselineAdjustment = 7;
79 RenderListBox::RenderListBox(Element* element)
80 : RenderBlock(element)
81 , m_optionsChanged(true)
82 , m_scrollToRevealSelectionAfterLayout(false)
83 , m_inAutoscroll(false)
87 if (Page* page = frame()->page()) {
89 m_page->addScrollableArea(this);
93 RenderListBox::~RenderListBox()
95 setHasVerticalScrollbar(false);
97 m_page->removeScrollableArea(this);
100 void RenderListBox::updateFromElement()
102 FontCachePurgePreventer fontCachePurgePreventer;
104 if (m_optionsChanged) {
105 const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
106 int size = numItems();
109 for (int i = 0; i < size; ++i) {
110 Element* element = listItems[i];
112 Font itemFont = style()->font();
113 if (OptionElement* optionElement = toOptionElement(element))
114 text = optionElement->textIndentedToRespectGroupLabel();
115 else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) {
116 text = optionGroupElement->groupLabelText();
117 FontDescription d = itemFont.fontDescription();
118 d.setWeight(d.bolderWeight());
119 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
120 itemFont.update(document()->styleSelector()->fontSelector());
123 if (!text.isEmpty()) {
124 // FIXME: Why is this always LTR? Can't text direction affect the width?
125 TextRun textRun = constructTextRun(this, itemFont, text, style(), TextRun::AllowTrailingExpansion);
126 textRun.disableRoundingHacks();
127 float textWidth = itemFont.width(textRun);
128 width = max(width, textWidth);
131 m_optionsWidth = static_cast<int>(ceilf(width));
132 m_optionsChanged = false;
134 setHasVerticalScrollbar(true);
136 setNeedsLayoutAndPrefWidthsRecalc();
140 void RenderListBox::selectionChanged()
143 if (!m_inAutoscroll) {
144 if (m_optionsChanged || needsLayout())
145 m_scrollToRevealSelectionAfterLayout = true;
147 scrollToRevealSelection();
150 if (AXObjectCache::accessibilityEnabled())
151 document()->axObjectCache()->selectedChildrenChanged(this);
154 void RenderListBox::layout()
156 RenderBlock::layout();
157 if (m_scrollToRevealSelectionAfterLayout) {
158 LayoutStateDisabler layoutStateDisabler(view());
159 scrollToRevealSelection();
163 void RenderListBox::scrollToRevealSelection()
165 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
167 m_scrollToRevealSelectionAfterLayout = false;
169 int firstIndex = select->activeSelectionStartListIndex();
170 if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex()))
171 scrollToRevealElementAtListIndex(firstIndex);
174 void RenderListBox::computePreferredLogicalWidths()
176 ASSERT(!m_optionsChanged);
178 m_minPreferredLogicalWidth = 0;
179 m_maxPreferredLogicalWidth = 0;
181 if (style()->width().isFixed() && style()->width().value() > 0)
182 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
184 m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
186 m_maxPreferredLogicalWidth += m_vBar->width();
189 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
190 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
191 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
192 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
193 m_minPreferredLogicalWidth = 0;
195 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
197 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
198 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
199 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
202 LayoutUnit toAdd = borderAndPaddingWidth();
203 m_minPreferredLogicalWidth += toAdd;
204 m_maxPreferredLogicalWidth += toAdd;
206 setPreferredLogicalWidthsDirty(false);
209 int RenderListBox::size() const
211 int specifiedSize = toSelectElement(static_cast<Element*>(node()))->size();
212 if (specifiedSize > 1)
213 return max(minSize, specifiedSize);
214 return min(max(minSize, numItems()), maxDefaultSize);
217 int RenderListBox::numVisibleItems() const
219 // Only count fully visible rows. But don't return 0 even if only part of a row shows.
220 return max<int>(1, (contentHeight() + rowSpacing) / itemHeight());
223 int RenderListBox::numItems() const
225 return toSelectElement(static_cast<Element*>(node()))->listItems().size();
228 LayoutUnit RenderListBox::listHeight() const
230 return itemHeight() * numItems() - rowSpacing;
233 void RenderListBox::computeLogicalHeight()
235 int toAdd = borderAndPaddingHeight();
237 int itemHeight = RenderListBox::itemHeight();
238 setHeight(itemHeight * size() - rowSpacing + toAdd);
240 RenderBlock::computeLogicalHeight();
243 bool enabled = numVisibleItems() < numItems();
244 m_vBar->setEnabled(enabled);
245 m_vBar->setSteps(1, max(1, numVisibleItems() - 1), itemHeight);
246 m_vBar->setProportion(numVisibleItems(), numItems());
252 LayoutUnit RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const
254 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment;
257 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& additionalOffset, int index)
259 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft(),
260 additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset),
261 contentWidth(), itemHeight());
264 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
266 if (style()->visibility() != VISIBLE)
269 int listItemsSize = numItems();
271 if (paintInfo.phase == PaintPhaseForeground) {
272 int index = m_indexOffset;
273 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
274 paintItemForeground(paintInfo, paintOffset, index);
279 // Paint the children.
280 RenderBlock::paintObject(paintInfo, paintOffset);
282 switch (paintInfo.phase) {
283 // Depending on whether we have overlay scrollbars they
284 // get rendered in the foreground or background phases
285 case PaintPhaseForeground:
286 if (m_vBar->isOverlayScrollbar())
287 paintScrollbar(paintInfo, paintOffset);
289 case PaintPhaseBlockBackground:
290 if (!m_vBar->isOverlayScrollbar())
291 paintScrollbar(paintInfo, paintOffset);
293 case PaintPhaseChildBlockBackground:
294 case PaintPhaseChildBlockBackgrounds: {
295 int index = m_indexOffset;
296 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
297 paintItemBackground(paintInfo, paintOffset, index);
307 void RenderListBox::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset)
309 if (!isSpatialNavigationEnabled(frame()))
310 return RenderBlock::addFocusRingRects(rects, additionalOffset);
312 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
314 // Focus the last selected item.
315 int selectedItem = select->activeSelectionEndListIndex();
316 if (selectedItem >= 0) {
317 rects.append(itemBoundingBoxRect(additionalOffset, selectedItem));
321 // No selected items, find the first non-disabled item.
322 int size = numItems();
323 const Vector<Element*>& listItems = select->listItems();
324 for (int i = 0; i < size; ++i) {
325 OptionElement* optionElement = toOptionElement(listItems[i]);
326 if (optionElement && !optionElement->disabled()) {
327 rects.append(itemBoundingBoxRect(additionalOffset, i));
333 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
336 LayoutRect scrollRect(paintOffset.x() + width() - borderRight() - m_vBar->width(),
337 paintOffset.y() + borderTop(),
339 height() - (borderTop() + borderBottom()));
340 m_vBar->setFrameRect(scrollRect);
341 m_vBar->paint(paintInfo.context, paintInfo.rect);
345 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle, Font itemFont, LayoutRect itemBoudingBox)
347 ETextAlign actualAlignment = itemStyle->textAlign();
348 // FIXME: Firefox doesn't respect JUSTIFY. Should we?
349 if (actualAlignment == TAAUTO || actualAlignment == JUSTIFY)
350 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT;
352 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent());
353 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) {
354 float textWidth = itemFont.width(textRun);
355 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHorizontal);
356 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) {
357 float textWidth = itemFont.width(textRun);
358 offset.setWidth((itemBoudingBox.width() - textWidth) / 2);
360 offset.setWidth(optionsSpacingHorizontal);
364 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
366 FontCachePurgePreventer fontCachePurgePreventer;
368 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
369 const Vector<Element*>& listItems = select->listItems();
370 Element* element = listItems[listIndex];
371 OptionElement* optionElement = toOptionElement(element);
373 RenderStyle* itemStyle = element->renderStyle();
377 if (itemStyle->visibility() == HIDDEN)
382 itemText = optionElement->textIndentedToRespectGroupLabel();
383 else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element))
384 itemText = optionGroupElement->groupLabelText();
386 Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor);
387 if (optionElement && optionElement->selected()) {
388 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
389 textColor = theme()->activeListBoxSelectionForegroundColor();
390 // Honor the foreground color for disabled items
391 else if (!element->disabled())
392 textColor = theme()->inactiveListBoxSelectionForegroundColor();
395 ColorSpace colorSpace = itemStyle->colorSpace();
396 paintInfo.context->setFillColor(textColor, colorSpace);
398 unsigned length = itemText.length();
399 const UChar* string = itemText.characters();
400 TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, itemStyle->direction(), itemStyle->unicodeBidi() == Override, TextRun::NoRounding);
401 Font itemFont = style()->font();
402 LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex);
403 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r));
405 if (isOptionGroupElement(element)) {
406 FontDescription d = itemFont.fontDescription();
407 d.setWeight(d.bolderWeight());
408 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
409 itemFont.update(document()->styleSelector()->fontSelector());
412 // Draw the item text
413 if (itemStyle->visibility() != HIDDEN)
414 paintInfo.context->drawBidiText(itemFont, textRun, r.location());
417 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
419 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
420 const Vector<Element*>& listItems = select->listItems();
421 Element* element = listItems[listIndex];
422 OptionElement* optionElement = toOptionElement(element);
425 if (optionElement && optionElement->selected()) {
426 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
427 backColor = theme()->activeListBoxSelectionBackgroundColor();
429 backColor = theme()->inactiveListBoxSelectionBackgroundColor();
431 backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor);
433 // Draw the background for this list box item
434 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
435 ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace();
436 LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex);
437 itemRect.intersect(controlClipRect(paintOffset));
438 paintInfo.context->fillRect(itemRect, backColor, colorSpace);
442 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
447 LayoutRect vertRect(accumulatedOffset.x() + width() - borderRight() - m_vBar->width(),
448 accumulatedOffset.y() + borderTop(),
450 height() - borderTop() - borderBottom());
452 if (vertRect.contains(pointInContainer)) {
453 result.setScrollbar(m_vBar.get());
459 int RenderListBox::listIndexAtOffset(const LayoutSize& offset)
464 if (offset.height() < borderTop() + paddingTop() || offset.height() > height() - paddingBottom() - borderBottom())
467 LayoutUnit scrollbarWidth = m_vBar ? m_vBar->width() : 0;
468 if (offset.width() < borderLeft() + paddingLeft() || offset.width() > width() - borderRight() - paddingRight() - scrollbarWidth)
471 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
472 return newOffset < numItems() ? newOffset : -1;
475 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
477 const int maxSpeed = 20;
478 const int iconRadius = 7;
479 const int speedReducer = 4;
481 // FIXME: This doesn't work correctly with transforms.
482 FloatPoint absOffset = localToAbsolute();
484 IntPoint currentMousePosition = roundedIntPoint(frame()->eventHandler()->currentMousePosition());
485 // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent
486 static IntPoint previousMousePosition;
487 if (currentMousePosition.y() < 0)
488 currentMousePosition = previousMousePosition;
490 previousMousePosition = currentMousePosition;
492 LayoutUnit yDelta = currentMousePosition.y() - panStartMousePosition.y();
494 // If the point is too far from the center we limit the speed
495 yDelta = max<LayoutUnit>(min<LayoutUnit>(yDelta, maxSpeed), -maxSpeed);
497 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
501 //offsetY = view()->viewHeight();
502 absOffset.move(0, listHeight());
506 // Let's attenuate the speed
507 yDelta /= speedReducer;
509 LayoutPoint scrollPoint(0, 0);
510 scrollPoint.setY(absOffset.y() + yDelta);
511 LayoutUnit newOffset = scrollToward(scrollPoint);
515 m_inAutoscroll = true;
516 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
517 select->updateListBoxSelection(!select->multiple());
518 m_inAutoscroll = false;
521 int RenderListBox::scrollToward(const LayoutPoint& destination)
523 // FIXME: This doesn't work correctly with transforms.
524 FloatPoint absPos = localToAbsolute();
525 LayoutSize positionOffset = roundedLayoutSize(destination - absPos);
527 int rows = numVisibleItems();
528 int offset = m_indexOffset;
530 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
533 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
534 return offset + rows - 1;
536 return listIndexAtOffset(positionOffset);
539 void RenderListBox::autoscroll()
541 LayoutPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition());
543 int endIndex = scrollToward(pos);
545 SelectElement* select = toSelectElement(static_cast<Element*>(node()));
546 m_inAutoscroll = true;
548 if (!select->multiple())
549 select->setActiveSelectionAnchorIndex(endIndex);
551 select->setActiveSelectionEndIndex(endIndex);
552 select->updateListBoxSelection(!select->multiple());
553 m_inAutoscroll = false;
557 void RenderListBox::stopAutoscroll()
559 toSelectElement(static_cast<Element*>(node()))->listBoxOnChange();
562 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
564 if (index < 0 || index >= numItems() || listIndexIsVisible(index))
568 if (index < m_indexOffset)
571 newOffset = index - numVisibleItems() + 1;
573 ScrollableArea::scrollToYOffsetWithoutAnimation(newOffset);
578 bool RenderListBox::listIndexIsVisible(int index)
580 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
583 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**)
585 return ScrollableArea::scroll(direction, granularity, multiplier);
588 bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**)
590 return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier);
593 void RenderListBox::valueChanged(unsigned listIndex)
595 Element* element = static_cast<Element*>(node());
596 SelectElement* select = toSelectElement(element);
597 select->setSelectedIndex(select->listToOptionIndex(listIndex));
598 element->dispatchFormControlChangeEvent();
601 LayoutUnit RenderListBox::scrollSize(ScrollbarOrientation orientation) const
603 return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
606 LayoutUnit RenderListBox::scrollPosition(Scrollbar*) const
608 return m_indexOffset;
611 void RenderListBox::setScrollOffset(const LayoutPoint& offset)
613 scrollTo(offset.y());
616 void RenderListBox::scrollTo(int newOffset)
618 if (newOffset == m_indexOffset)
621 m_indexOffset = newOffset;
623 node()->document()->eventQueue()->enqueueOrDispatchScrollEvent(node(), EventQueue::ScrollEventElementTarget);
626 LayoutUnit RenderListBox::itemHeight() const
628 return style()->fontMetrics().height() + rowSpacing;
631 LayoutUnit RenderListBox::verticalScrollbarWidth() const
633 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
636 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
637 // how the control currently paints.
638 LayoutUnit RenderListBox::scrollWidth() const
640 // There is no horizontal scrolling allowed.
641 return clientWidth();
644 LayoutUnit RenderListBox::scrollHeight() const
646 return max(clientHeight(), listHeight());
649 LayoutUnit RenderListBox::scrollLeft() const
654 void RenderListBox::setScrollLeft(LayoutUnit)
658 LayoutUnit RenderListBox::scrollTop() const
660 return m_indexOffset * itemHeight();
663 void RenderListBox::setScrollTop(LayoutUnit newTop)
665 // Determine an index and scroll to it.
666 int index = newTop / itemHeight();
667 if (index < 0 || index >= numItems() || index == m_indexOffset)
670 ScrollableArea::scrollToYOffsetWithoutAnimation(index);
673 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
675 if (!RenderBlock::nodeAtPoint(request, result, pointInContainer, accumulatedOffset, hitTestAction))
677 const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
678 int size = numItems();
679 LayoutPoint adjustedLocation = accumulatedOffset + location();
681 for (int i = 0; i < size; ++i) {
682 if (itemBoundingBoxRect(adjustedLocation, i).contains(pointInContainer)) {
683 if (Element* node = listItems[i]) {
684 result.setInnerNode(node);
685 if (!result.innerNonSharedNode())
686 result.setInnerNonSharedNode(node);
687 result.setLocalPoint(pointInContainer - toLayoutSize(adjustedLocation));
696 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) const
698 LayoutRect clipRect = contentBoxRect();
699 clipRect.moveBy(additionalOffset);
703 bool RenderListBox::isActive() const
705 Page* page = frame()->page();
706 return page && page->focusController()->isActive();
709 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const LayoutRect& rect)
711 LayoutRect scrollRect = rect;
712 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
713 repaintRectangle(scrollRect);
716 LayoutRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const LayoutRect& scrollbarRect) const
718 RenderView* view = this->view();
720 return scrollbarRect;
722 LayoutRect rect = scrollbarRect;
724 LayoutUnit scrollbarLeft = width() - borderRight() - scrollbar->width();
725 LayoutUnit scrollbarTop = borderTop();
726 rect.move(scrollbarLeft, scrollbarTop);
728 return view->frameView()->convertFromRenderer(this, rect);
731 LayoutRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const LayoutRect& parentRect) const
733 RenderView* view = this->view();
737 LayoutRect rect = view->frameView()->convertToRenderer(this, parentRect);
739 LayoutUnit scrollbarLeft = width() - borderRight() - scrollbar->width();
740 LayoutUnit scrollbarTop = borderTop();
741 rect.move(-scrollbarLeft, -scrollbarTop);
745 LayoutPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const LayoutPoint& scrollbarPoint) const
747 RenderView* view = this->view();
749 return scrollbarPoint;
751 LayoutPoint point = scrollbarPoint;
753 LayoutUnit scrollbarLeft = width() - borderRight() - scrollbar->width();
754 LayoutUnit scrollbarTop = borderTop();
755 point.move(scrollbarLeft, scrollbarTop);
757 return view->frameView()->convertFromRenderer(this, point);
760 LayoutPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const LayoutPoint& parentPoint) const
762 RenderView* view = this->view();
766 LayoutPoint point = view->frameView()->convertToRenderer(this, parentPoint);
768 LayoutUnit scrollbarLeft = width() - borderRight() - scrollbar->width();
769 LayoutUnit scrollbarTop = borderTop();
770 point.move(-scrollbarLeft, -scrollbarTop);
774 LayoutSize RenderListBox::contentsSize() const
776 return LayoutSize(scrollWidth(), scrollHeight());
779 LayoutUnit RenderListBox::visibleHeight() const
784 LayoutUnit RenderListBox::visibleWidth() const
789 LayoutPoint RenderListBox::currentMousePosition() const
791 RenderView* view = this->view();
793 return LayoutPoint();
794 return view->frameView()->currentMousePosition();
797 bool RenderListBox::shouldSuspendScrollAnimations() const
799 RenderView* view = this->view();
802 return view->frameView()->shouldSuspendScrollAnimations();
805 bool RenderListBox::isOnActivePage() const
807 return !document()->inPageCache();
810 ScrollableArea* RenderListBox::enclosingScrollableArea() const
812 // FIXME: Return a RenderLayer that's scrollable.
816 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
818 RefPtr<Scrollbar> widget;
819 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
820 if (hasCustomScrollbarStyle)
821 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this);
823 widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
824 didAddVerticalScrollbar(widget.get());
826 document()->view()->addChild(widget.get());
827 return widget.release();
830 void RenderListBox::destroyScrollbar()
835 if (!m_vBar->isCustomScrollbar())
836 ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get());
837 m_vBar->removeFromParent();
838 m_vBar->disconnectFromScrollableArea();
842 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
844 if (hasScrollbar == (m_vBar != 0))
848 m_vBar = createScrollbar();
853 m_vBar->styleChanged();
855 #if ENABLE(DASHBOARD_SUPPORT)
856 // Force an update since we know the scrollbars have changed things.
857 if (document()->hasDashboardRegions())
858 document()->setDashboardRegionsDirty(true);
862 } // namespace WebCore