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"
36 #include "DocumentEventQueue.h"
37 #include "EventHandler.h"
38 #include "FocusController.h"
39 #include "FontCache.h"
41 #include "FrameSelection.h"
42 #include "FrameView.h"
43 #include "GraphicsContext.h"
44 #include "HTMLNames.h"
45 #include "HTMLOptionElement.h"
46 #include "HTMLOptGroupElement.h"
47 #include "HTMLSelectElement.h"
48 #include "HitTestResult.h"
49 #include "NodeRenderStyle.h"
51 #include "PaintInfo.h"
52 #include "RenderLayer.h"
53 #include "RenderScrollbar.h"
54 #include "RenderText.h"
55 #include "RenderTheme.h"
56 #include "RenderView.h"
57 #include "Scrollbar.h"
58 #include "ScrollbarTheme.h"
59 #include "SpatialNavigation.h"
60 #include "StyleResolver.h"
67 using namespace HTMLNames;
69 const int rowSpacing = 1;
71 const int optionsSpacingHorizontal = 2;
73 // The minSize constant was originally defined to render scrollbars correctly.
74 // This might vary for different platforms.
75 const int minSize = 4;
77 // Default size when the multiple attribute is present but size attribute is absent.
78 const int defaultSize = 4;
80 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
81 // widget, but I'm not sure this is right for the new control.
82 const int baselineAdjustment = 7;
84 RenderListBox::RenderListBox(Element* element)
85 : RenderBlock(element)
86 , m_optionsChanged(true)
87 , m_scrollToRevealSelectionAfterLayout(false)
88 , m_inAutoscroll(false)
93 ASSERT(element->isHTMLElement());
94 ASSERT(element->hasTagName(HTMLNames::selectTag));
96 if (FrameView* frameView = frame()->view())
97 frameView->addScrollableArea(this);
100 RenderListBox::~RenderListBox()
102 setHasVerticalScrollbar(false);
104 if (FrameView* frameView = frame()->view())
105 frameView->removeScrollableArea(this);
108 void RenderListBox::updateFromElement()
110 FontCachePurgePreventer fontCachePurgePreventer;
112 if (m_optionsChanged) {
113 const Vector<HTMLElement*>& listItems = toHTMLSelectElement(node())->listItems();
114 int size = numItems();
117 for (int i = 0; i < size; ++i) {
118 HTMLElement* element = listItems[i];
120 Font itemFont = style()->font();
121 if (element->hasTagName(optionTag))
122 text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
123 else if (element->hasTagName(optgroupTag)) {
124 text = static_cast<const HTMLOptGroupElement*>(element)->groupLabelText();
125 FontDescription d = itemFont.fontDescription();
126 d.setWeight(d.bolderWeight());
127 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
128 itemFont.update(document()->styleResolver()->fontSelector());
131 if (!text.isEmpty()) {
132 applyTextTransform(style(), text, ' ');
133 // FIXME: Why is this always LTR? Can't text direction affect the width?
134 TextRun textRun = constructTextRun(this, itemFont, text, style(), TextRun::AllowTrailingExpansion);
135 textRun.disableRoundingHacks();
136 float textWidth = itemFont.width(textRun);
137 width = max(width, textWidth);
140 m_optionsWidth = static_cast<int>(ceilf(width));
141 m_optionsChanged = false;
143 setHasVerticalScrollbar(true);
145 setNeedsLayoutAndPrefWidthsRecalc();
149 void RenderListBox::selectionChanged()
152 if (!m_inAutoscroll) {
153 if (m_optionsChanged || needsLayout())
154 m_scrollToRevealSelectionAfterLayout = true;
156 scrollToRevealSelection();
159 if (AXObjectCache::accessibilityEnabled())
160 document()->axObjectCache()->selectedChildrenChanged(this);
163 void RenderListBox::layout()
165 RenderBlock::layout();
166 if (m_scrollToRevealSelectionAfterLayout) {
167 LayoutStateDisabler layoutStateDisabler(view());
168 scrollToRevealSelection();
172 void RenderListBox::scrollToRevealSelection()
174 HTMLSelectElement* select = toHTMLSelectElement(node());
176 m_scrollToRevealSelectionAfterLayout = false;
178 int firstIndex = select->activeSelectionStartListIndex();
179 if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex()))
180 scrollToRevealElementAtListIndex(firstIndex);
183 void RenderListBox::computePreferredLogicalWidths()
185 ASSERT(!m_optionsChanged);
187 m_minPreferredLogicalWidth = 0;
188 m_maxPreferredLogicalWidth = 0;
190 if (style()->width().isFixed() && style()->width().value() > 0)
191 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
193 m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
195 m_maxPreferredLogicalWidth += m_vBar->width();
198 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
199 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
200 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
201 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
202 m_minPreferredLogicalWidth = 0;
204 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
206 if (style()->maxWidth().isFixed()) {
207 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
208 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
211 LayoutUnit toAdd = borderAndPaddingWidth();
212 m_minPreferredLogicalWidth += toAdd;
213 m_maxPreferredLogicalWidth += toAdd;
215 setPreferredLogicalWidthsDirty(false);
218 int RenderListBox::size() const
220 int specifiedSize = toHTMLSelectElement(node())->size();
221 if (specifiedSize > 1)
222 return max(minSize, specifiedSize);
227 int RenderListBox::numVisibleItems() const
229 // Only count fully visible rows. But don't return 0 even if only part of a row shows.
230 return max<int>(1, (contentHeight() + rowSpacing) / itemHeight());
233 int RenderListBox::numItems() const
235 return toHTMLSelectElement(node())->listItems().size();
238 LayoutUnit RenderListBox::listHeight() const
240 return itemHeight() * numItems() - rowSpacing;
243 void RenderListBox::computeLogicalHeight()
245 int toAdd = borderAndPaddingHeight();
247 int itemHeight = RenderListBox::itemHeight();
248 setHeight(itemHeight * size() - rowSpacing + toAdd);
250 RenderBlock::computeLogicalHeight();
253 bool enabled = numVisibleItems() < numItems();
254 m_vBar->setEnabled(enabled);
255 m_vBar->setSteps(1, max(1, numVisibleItems() - 1), itemHeight);
256 m_vBar->setProportion(numVisibleItems(), numItems());
258 scrollToOffsetWithoutAnimation(VerticalScrollbar, 0);
264 LayoutUnit RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const
266 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment;
269 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& additionalOffset, int index)
271 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft(),
272 additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset),
273 contentWidth(), itemHeight());
276 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
278 if (style()->visibility() != VISIBLE)
281 int listItemsSize = numItems();
283 if (paintInfo.phase == PaintPhaseForeground) {
284 int index = m_indexOffset;
285 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
286 paintItemForeground(paintInfo, paintOffset, index);
291 // Paint the children.
292 RenderBlock::paintObject(paintInfo, paintOffset);
294 switch (paintInfo.phase) {
295 // Depending on whether we have overlay scrollbars they
296 // get rendered in the foreground or background phases
297 case PaintPhaseForeground:
298 if (m_vBar->isOverlayScrollbar())
299 paintScrollbar(paintInfo, paintOffset);
301 case PaintPhaseBlockBackground:
302 if (!m_vBar->isOverlayScrollbar())
303 paintScrollbar(paintInfo, paintOffset);
305 case PaintPhaseChildBlockBackground:
306 case PaintPhaseChildBlockBackgrounds: {
307 int index = m_indexOffset;
308 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
309 paintItemBackground(paintInfo, paintOffset, index);
319 void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset)
321 if (!isSpatialNavigationEnabled(frame()))
322 return RenderBlock::addFocusRingRects(rects, additionalOffset);
324 HTMLSelectElement* select = toHTMLSelectElement(node());
326 // Focus the last selected item.
327 int selectedItem = select->activeSelectionEndListIndex();
328 if (selectedItem >= 0) {
329 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, selectedItem)));
333 // No selected items, find the first non-disabled item.
334 int size = numItems();
335 const Vector<HTMLElement*>& listItems = select->listItems();
336 for (int i = 0; i < size; ++i) {
337 HTMLElement* element = listItems[i];
338 if (element->hasTagName(optionTag) && !toHTMLOptionElement(element)->disabled()) {
339 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, i)));
345 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
348 IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + width() - borderRight() - m_vBar->width(),
349 paintOffset.y() + borderTop(),
351 height() - (borderTop() + borderBottom()));
352 m_vBar->setFrameRect(scrollRect);
353 m_vBar->paint(paintInfo.context, paintInfo.rect);
357 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle, Font itemFont, LayoutRect itemBoudingBox)
359 ETextAlign actualAlignment = itemStyle->textAlign();
360 // FIXME: Firefox doesn't respect JUSTIFY. Should we?
361 // FIXME: Handle TAEND here
362 if (actualAlignment == TASTART || actualAlignment == JUSTIFY)
363 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT;
365 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent());
366 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) {
367 float textWidth = itemFont.width(textRun);
368 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHorizontal);
369 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) {
370 float textWidth = itemFont.width(textRun);
371 offset.setWidth((itemBoudingBox.width() - textWidth) / 2);
373 offset.setWidth(optionsSpacingHorizontal);
377 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
379 FontCachePurgePreventer fontCachePurgePreventer;
381 const Vector<HTMLElement*>& listItems = toHTMLSelectElement(node())->listItems();
382 HTMLElement* element = listItems[listIndex];
384 RenderStyle* itemStyle = element->renderStyle();
388 if (itemStyle->visibility() == HIDDEN)
392 bool isOptionElement = element->hasTagName(optionTag);
394 itemText = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
395 else if (element->hasTagName(optgroupTag))
396 itemText = static_cast<const HTMLOptGroupElement*>(element)->groupLabelText();
397 applyTextTransform(style(), itemText, ' ');
399 Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor);
400 if (isOptionElement && toHTMLOptionElement(element)->selected()) {
401 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
402 textColor = theme()->activeListBoxSelectionForegroundColor();
403 // Honor the foreground color for disabled items
404 else if (!element->disabled())
405 textColor = theme()->inactiveListBoxSelectionForegroundColor();
408 ColorSpace colorSpace = itemStyle->colorSpace();
409 paintInfo.context->setFillColor(textColor, colorSpace);
411 TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle->direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding);
412 Font itemFont = style()->font();
413 LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex);
414 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r));
416 if (element->hasTagName(optgroupTag)) {
417 FontDescription d = itemFont.fontDescription();
418 d.setWeight(d.bolderWeight());
419 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
420 itemFont.update(document()->styleResolver()->fontSelector());
423 // Draw the item text
424 paintInfo.context->drawBidiText(itemFont, textRun, roundedIntPoint(r.location()));
427 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
429 const Vector<HTMLElement*>& listItems = toHTMLSelectElement(node())->listItems();
430 HTMLElement* element = listItems[listIndex];
433 if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()) {
434 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
435 backColor = theme()->activeListBoxSelectionBackgroundColor();
437 backColor = theme()->inactiveListBoxSelectionBackgroundColor();
439 backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor);
441 // Draw the background for this list box item
442 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
443 ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace();
444 LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex);
445 itemRect.intersect(controlClipRect(paintOffset));
446 paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor, colorSpace);
450 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset)
452 if (!m_vBar || !m_vBar->shouldParticipateInHitTesting())
455 LayoutRect vertRect(accumulatedOffset.x() + width() - borderRight() - m_vBar->width(),
456 accumulatedOffset.y() + borderTop(),
458 height() - borderTop() - borderBottom());
460 if (vertRect.contains(pointInContainer)) {
461 result.setScrollbar(m_vBar.get());
467 int RenderListBox::listIndexAtOffset(const LayoutSize& offset)
472 if (offset.height() < borderTop() + paddingTop() || offset.height() > height() - paddingBottom() - borderBottom())
475 int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
476 if (offset.width() < borderLeft() + paddingLeft() || offset.width() > width() - borderRight() - paddingRight() - scrollbarWidth)
479 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
480 return newOffset < numItems() ? newOffset : -1;
483 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
485 const int maxSpeed = 20;
486 const int iconRadius = 7;
487 const int speedReducer = 4;
489 // FIXME: This doesn't work correctly with transforms.
490 FloatPoint absOffset = localToAbsolute();
492 IntPoint currentMousePosition = frame()->eventHandler()->currentMousePosition();
493 // 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
494 static IntPoint previousMousePosition;
495 if (currentMousePosition.y() < 0)
496 currentMousePosition = previousMousePosition;
498 previousMousePosition = currentMousePosition;
500 int yDelta = currentMousePosition.y() - panStartMousePosition.y();
502 // If the point is too far from the center we limit the speed
503 yDelta = max<int>(min<int>(yDelta, maxSpeed), -maxSpeed);
505 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
509 //offsetY = view()->viewHeight();
510 absOffset.move(0, listHeight());
514 // Let's attenuate the speed
515 yDelta /= speedReducer;
517 IntPoint scrollPoint(0, 0);
518 scrollPoint.setY(absOffset.y() + yDelta);
519 int newOffset = scrollToward(scrollPoint);
523 m_inAutoscroll = true;
524 HTMLSelectElement* select = toHTMLSelectElement(node());
525 select->updateListBoxSelection(!select->multiple());
526 m_inAutoscroll = false;
529 int RenderListBox::scrollToward(const IntPoint& destination)
531 // FIXME: This doesn't work correctly with transforms.
532 FloatPoint absPos = localToAbsolute();
533 IntSize positionOffset = roundedIntSize(destination - absPos);
535 int rows = numVisibleItems();
536 int offset = m_indexOffset;
538 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
541 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
542 return offset + rows - 1;
544 return listIndexAtOffset(positionOffset);
547 void RenderListBox::autoscroll()
549 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition());
551 int endIndex = scrollToward(pos);
553 HTMLSelectElement* select = toHTMLSelectElement(node());
554 m_inAutoscroll = true;
556 if (!select->multiple())
557 select->setActiveSelectionAnchorIndex(endIndex);
559 select->setActiveSelectionEndIndex(endIndex);
560 select->updateListBoxSelection(!select->multiple());
561 m_inAutoscroll = false;
565 void RenderListBox::stopAutoscroll()
567 toHTMLSelectElement(node())->listBoxOnChange();
570 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
572 if (index < 0 || index >= numItems() || listIndexIsVisible(index))
576 if (index < m_indexOffset)
579 newOffset = index - numVisibleItems() + 1;
581 scrollToOffsetWithoutAnimation(VerticalScrollbar, newOffset);
586 bool RenderListBox::listIndexIsVisible(int index)
588 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
591 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**)
593 return ScrollableArea::scroll(direction, granularity, multiplier);
596 bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**)
598 return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier);
601 void RenderListBox::valueChanged(unsigned listIndex)
603 HTMLSelectElement* element = toHTMLSelectElement(node());
604 element->setSelectedIndex(element->listToOptionIndex(listIndex));
605 element->dispatchFormControlChangeEvent();
608 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
610 return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
613 int RenderListBox::scrollPosition(Scrollbar*) const
615 return m_indexOffset;
618 void RenderListBox::setScrollOffset(const IntPoint& offset)
620 scrollTo(offset.y());
623 void RenderListBox::scrollTo(int newOffset)
625 if (newOffset == m_indexOffset)
628 m_indexOffset = newOffset;
630 node()->document()->eventQueue()->enqueueOrDispatchScrollEvent(node(), DocumentEventQueue::ScrollEventElementTarget);
633 LayoutUnit RenderListBox::itemHeight() const
635 return style()->fontMetrics().height() + rowSpacing;
638 int RenderListBox::verticalScrollbarWidth() const
640 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
643 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
644 // how the control currently paints.
645 int RenderListBox::scrollWidth() const
647 // There is no horizontal scrolling allowed.
648 return pixelSnappedClientWidth();
651 int RenderListBox::scrollHeight() const
653 return max(pixelSnappedClientHeight(), roundToInt(listHeight()));
656 int RenderListBox::scrollLeft() const
661 void RenderListBox::setScrollLeft(int)
665 int RenderListBox::scrollTop() const
667 return m_indexOffset * itemHeight();
670 void RenderListBox::setScrollTop(int newTop)
672 // Determine an index and scroll to it.
673 int index = newTop / itemHeight();
674 if (index < 0 || index >= numItems() || index == m_indexOffset)
677 scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
680 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
682 if (!RenderBlock::nodeAtPoint(request, result, pointInContainer, accumulatedOffset, hitTestAction))
684 const Vector<HTMLElement*>& listItems = toHTMLSelectElement(node())->listItems();
685 int size = numItems();
686 LayoutPoint adjustedLocation = accumulatedOffset + location();
688 for (int i = 0; i < size; ++i) {
689 if (itemBoundingBoxRect(adjustedLocation, i).contains(pointInContainer.point())) {
690 if (Element* node = listItems[i]) {
691 result.setInnerNode(node);
692 if (!result.innerNonSharedNode())
693 result.setInnerNonSharedNode(node);
694 result.setLocalPoint(pointInContainer.point() - toLayoutSize(adjustedLocation));
703 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) const
705 LayoutRect clipRect = contentBoxRect();
706 clipRect.moveBy(additionalOffset);
710 bool RenderListBox::isActive() const
712 Page* page = frame()->page();
713 return page && page->focusController()->isActive();
716 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
718 IntRect scrollRect = rect;
719 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
720 repaintRectangle(scrollRect);
723 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
725 RenderView* view = this->view();
727 return scrollbarRect;
729 IntRect rect = scrollbarRect;
731 int scrollbarLeft = width() - borderRight() - scrollbar->width();
732 int scrollbarTop = borderTop();
733 rect.move(scrollbarLeft, scrollbarTop);
735 return view->frameView()->convertFromRenderer(this, rect);
738 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
740 RenderView* view = this->view();
744 IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
746 int scrollbarLeft = width() - borderRight() - scrollbar->width();
747 int scrollbarTop = borderTop();
748 rect.move(-scrollbarLeft, -scrollbarTop);
752 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
754 RenderView* view = this->view();
756 return scrollbarPoint;
758 IntPoint point = scrollbarPoint;
760 int scrollbarLeft = width() - borderRight() - scrollbar->width();
761 int scrollbarTop = borderTop();
762 point.move(scrollbarLeft, scrollbarTop);
764 return view->frameView()->convertFromRenderer(this, point);
767 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
769 RenderView* view = this->view();
773 IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
775 int scrollbarLeft = width() - borderRight() - scrollbar->width();
776 int scrollbarTop = borderTop();
777 point.move(-scrollbarLeft, -scrollbarTop);
781 IntSize RenderListBox::contentsSize() const
783 return IntSize(scrollWidth(), scrollHeight());
786 int RenderListBox::visibleHeight() const
791 int RenderListBox::visibleWidth() const
796 IntPoint RenderListBox::currentMousePosition() const
798 RenderView* view = this->view();
801 return view->frameView()->currentMousePosition();
804 bool RenderListBox::shouldSuspendScrollAnimations() const
806 RenderView* view = this->view();
809 return view->frameView()->shouldSuspendScrollAnimations();
812 bool RenderListBox::isOnActivePage() const
814 return !document()->inPageCache();
817 ScrollableArea* RenderListBox::enclosingScrollableArea() const
819 // FIXME: Return a RenderLayer that's scrollable.
823 IntRect RenderListBox::scrollableAreaBoundingBox() const
825 return absoluteBoundingBoxRect();
828 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
830 RefPtr<Scrollbar> widget;
831 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
832 if (hasCustomScrollbarStyle)
833 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node());
835 widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
836 didAddVerticalScrollbar(widget.get());
838 document()->view()->addChild(widget.get());
839 return widget.release();
842 void RenderListBox::destroyScrollbar()
847 if (!m_vBar->isCustomScrollbar())
848 ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get());
849 m_vBar->removeFromParent();
850 m_vBar->disconnectFromScrollableArea();
854 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
856 if (hasScrollbar == (m_vBar != 0))
860 m_vBar = createScrollbar();
865 m_vBar->styleChanged();
867 #if ENABLE(DASHBOARD_SUPPORT)
868 // Force an update since we know the scrollbars have changed things.
869 if (document()->hasDashboardRegions())
870 document()->setDashboardRegionsDirty(true);
874 } // namespace WebCore