RenderListBox.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
1/*
2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 *               2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
17 *
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.
28 */
29
30#include "config.h"
31#include "RenderListBox.h"
32
33#include "AXObjectCache.h"
34#include "CSSStyleSelector.h"
35#include "Document.h"
36#include "EventHandler.h"
37#include "EventQueue.h"
38#include "FocusController.h"
39#include "Frame.h"
40#include "FrameView.h"
41#include "GraphicsContext.h"
42#include "HTMLNames.h"
43#include "HitTestResult.h"
44#include "NodeRenderStyle.h"
45#include "OptionGroupElement.h"
46#include "OptionElement.h"
47#include "Page.h"
48#include "PaintInfo.h"
49#include "RenderLayer.h"
50#include "RenderScrollbar.h"
51#include "RenderTheme.h"
52#include "RenderView.h"
53#include "Scrollbar.h"
54#include "ScrollbarTheme.h"
55#include "SelectElement.h"
56#include "SelectionController.h"
57#include "TextRun.h"
58#include <math.h>
59
60using namespace std;
61
62namespace WebCore {
63
64using namespace HTMLNames;
65
66const int rowSpacing = 1;
67
68const int optionsSpacingHorizontal = 2;
69
70const int minSize = 4;
71const int maxDefaultSize = 10;
72
73// FIXME: This hardcoded baselineAdjustment is what we used to do for the old
74// widget, but I'm not sure this is right for the new control.
75const int baselineAdjustment = 7;
76
77RenderListBox::RenderListBox(Element* element)
78    : RenderBlock(element)
79    , m_optionsChanged(true)
80    , m_scrollToRevealSelectionAfterLayout(false)
81    , m_inAutoscroll(false)
82    , m_optionsWidth(0)
83    , m_indexOffset(0)
84{
85}
86
87RenderListBox::~RenderListBox()
88{
89    setHasVerticalScrollbar(false);
90}
91
92void RenderListBox::updateFromElement()
93{
94    if (m_optionsChanged) {
95        const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
96        int size = numItems();
97
98        float width = 0;
99        for (int i = 0; i < size; ++i) {
100            Element* element = listItems[i];
101            String text;
102            Font itemFont = style()->font();
103            if (OptionElement* optionElement = toOptionElement(element))
104                text = optionElement->textIndentedToRespectGroupLabel();
105            else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) {
106                text = optionGroupElement->groupLabelText();
107                FontDescription d = itemFont.fontDescription();
108                d.setWeight(d.bolderWeight());
109                itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
110                itemFont.update(document()->styleSelector()->fontSelector());
111            }
112
113            if (!text.isEmpty()) {
114                float textWidth = itemFont.width(TextRun(text.impl(), false, 0, 0, TextRun::AllowTrailingExpansion, false, false));
115                width = max(width, textWidth);
116            }
117        }
118        m_optionsWidth = static_cast<int>(ceilf(width));
119        m_optionsChanged = false;
120
121        setHasVerticalScrollbar(true);
122
123        setNeedsLayoutAndPrefWidthsRecalc();
124    }
125}
126
127void RenderListBox::selectionChanged()
128{
129    repaint();
130    if (!m_inAutoscroll) {
131        if (m_optionsChanged || needsLayout())
132            m_scrollToRevealSelectionAfterLayout = true;
133        else
134            scrollToRevealSelection();
135    }
136
137    if (AXObjectCache::accessibilityEnabled())
138        document()->axObjectCache()->selectedChildrenChanged(this);
139}
140
141void RenderListBox::layout()
142{
143    RenderBlock::layout();
144    if (m_scrollToRevealSelectionAfterLayout) {
145        view()->disableLayoutState();
146        scrollToRevealSelection();
147        view()->enableLayoutState();
148    }
149}
150
151void RenderListBox::scrollToRevealSelection()
152{
153    SelectElement* select = toSelectElement(static_cast<Element*>(node()));
154
155    m_scrollToRevealSelectionAfterLayout = false;
156
157    int firstIndex = select->activeSelectionStartListIndex();
158    if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex()))
159        scrollToRevealElementAtListIndex(firstIndex);
160}
161
162void RenderListBox::computePreferredLogicalWidths()
163{
164    ASSERT(!m_optionsChanged);
165
166    m_minPreferredLogicalWidth = 0;
167    m_maxPreferredLogicalWidth = 0;
168
169    if (style()->width().isFixed() && style()->width().value() > 0)
170        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
171    else {
172        m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
173        if (m_vBar)
174            m_maxPreferredLogicalWidth += m_vBar->width();
175    }
176
177    if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
178        m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
179        m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
180    } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
181        m_minPreferredLogicalWidth = 0;
182    else
183        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
184
185    if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
186        m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
187        m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
188    }
189
190    int toAdd = borderAndPaddingWidth();
191    m_minPreferredLogicalWidth += toAdd;
192    m_maxPreferredLogicalWidth += toAdd;
193
194    setPreferredLogicalWidthsDirty(false);
195}
196
197int RenderListBox::size() const
198{
199    int specifiedSize = toSelectElement(static_cast<Element*>(node()))->size();
200    if (specifiedSize > 1)
201        return max(minSize, specifiedSize);
202    return min(max(minSize, numItems()), maxDefaultSize);
203}
204
205int RenderListBox::numVisibleItems() const
206{
207    // Only count fully visible rows. But don't return 0 even if only part of a row shows.
208    return max(1, (contentHeight() + rowSpacing) / itemHeight());
209}
210
211int RenderListBox::numItems() const
212{
213    return toSelectElement(static_cast<Element*>(node()))->listItems().size();
214}
215
216int RenderListBox::listHeight() const
217{
218    return itemHeight() * numItems() - rowSpacing;
219}
220
221void RenderListBox::computeLogicalHeight()
222{
223    int toAdd = borderAndPaddingHeight();
224
225    int itemHeight = RenderListBox::itemHeight();
226    setHeight(itemHeight * size() - rowSpacing + toAdd);
227
228    RenderBlock::computeLogicalHeight();
229
230    if (m_vBar) {
231        bool enabled = numVisibleItems() < numItems();
232        m_vBar->setEnabled(enabled);
233        m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight);
234        m_vBar->setProportion(numVisibleItems(), numItems());
235        if (!enabled)
236            m_indexOffset = 0;
237    }
238}
239
240int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const
241{
242    return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment;
243}
244
245IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index)
246{
247    return IntRect(tx + borderLeft() + paddingLeft(),
248                   ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset),
249                   contentWidth(), itemHeight());
250}
251
252void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty)
253{
254    if (style()->visibility() != VISIBLE)
255        return;
256
257    int listItemsSize = numItems();
258
259    if (paintInfo.phase == PaintPhaseForeground) {
260        int index = m_indexOffset;
261        while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
262            paintItemForeground(paintInfo, tx, ty, index);
263            index++;
264        }
265    }
266
267    // Paint the children.
268    RenderBlock::paintObject(paintInfo, tx, ty);
269
270    if (paintInfo.phase == PaintPhaseBlockBackground)
271        paintScrollbar(paintInfo, tx, ty);
272    else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
273        int index = m_indexOffset;
274        while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
275            paintItemBackground(paintInfo, tx, ty, index);
276            index++;
277        }
278    }
279}
280
281void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
282{
283    if (!isSpatialNavigationEnabled(frame()))
284        return RenderBlock::addFocusRingRects(rects, tx, ty);
285
286    SelectElement* select = toSelectElement(static_cast<Element*>(node()));
287
288    // Focus the last selected item.
289    int selectedItem = select->activeSelectionEndListIndex();
290    if (selectedItem >= 0) {
291        rects.append(itemBoundingBoxRect(tx, ty, selectedItem));
292        return;
293    }
294
295    // No selected items, find the first non-disabled item.
296    int size = numItems();
297    const Vector<Element*>& listItems = select->listItems();
298    for (int i = 0; i < size; ++i) {
299        OptionElement* optionElement = toOptionElement(listItems[i]);
300        if (optionElement && !optionElement->disabled()) {
301            rects.append(itemBoundingBoxRect(tx, ty, i));
302            return;
303        }
304    }
305}
306
307void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty)
308{
309    if (m_vBar) {
310        IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(),
311                           ty + borderTop(),
312                           m_vBar->width(),
313                           height() - (borderTop() + borderBottom()));
314        m_vBar->setFrameRect(scrollRect);
315        m_vBar->paint(paintInfo.context, paintInfo.rect);
316    }
317}
318
319void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex)
320{
321    SelectElement* select = toSelectElement(static_cast<Element*>(node()));
322    const Vector<Element*>& listItems = select->listItems();
323    Element* element = listItems[listIndex];
324    OptionElement* optionElement = toOptionElement(element);
325
326    String itemText;
327    if (optionElement)
328        itemText = optionElement->textIndentedToRespectGroupLabel();
329    else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element))
330        itemText = optionGroupElement->groupLabelText();
331
332    // Determine where the item text should be placed
333    IntRect r = itemBoundingBoxRect(tx, ty, listIndex);
334    r.move(optionsSpacingHorizontal, style()->fontMetrics().ascent());
335
336    RenderStyle* itemStyle = element->renderStyle();
337    if (!itemStyle)
338        itemStyle = style();
339
340    Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor);
341    if (optionElement && optionElement->selected()) {
342        if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
343            textColor = theme()->activeListBoxSelectionForegroundColor();
344        // Honor the foreground color for disabled items
345        else if (!element->disabled())
346            textColor = theme()->inactiveListBoxSelectionForegroundColor();
347    }
348
349    ColorSpace colorSpace = itemStyle->colorSpace();
350    paintInfo.context->setFillColor(textColor, colorSpace);
351
352    Font itemFont = style()->font();
353    if (isOptionGroupElement(element)) {
354        FontDescription d = itemFont.fontDescription();
355        d.setWeight(d.bolderWeight());
356        itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
357        itemFont.update(document()->styleSelector()->fontSelector());
358    }
359
360    unsigned length = itemText.length();
361    const UChar* string = itemText.characters();
362    TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override);
363
364    // Draw the item text
365    if (itemStyle->visibility() != HIDDEN)
366        paintInfo.context->drawBidiText(itemFont, textRun, r.location());
367}
368
369void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex)
370{
371    SelectElement* select = toSelectElement(static_cast<Element*>(node()));
372    const Vector<Element*>& listItems = select->listItems();
373    Element* element = listItems[listIndex];
374    OptionElement* optionElement = toOptionElement(element);
375
376    Color backColor;
377    if (optionElement && optionElement->selected()) {
378        if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
379            backColor = theme()->activeListBoxSelectionBackgroundColor();
380        else
381            backColor = theme()->inactiveListBoxSelectionBackgroundColor();
382    } else
383        backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor);
384
385    // Draw the background for this list box item
386    if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
387        ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace();
388        IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex);
389        itemRect.intersect(controlClipRect(tx, ty));
390        paintInfo.context->fillRect(itemRect, backColor, colorSpace);
391    }
392}
393
394bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty)
395{
396    if (!m_vBar)
397        return false;
398
399    IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(),
400                     _ty + borderTop(),
401                     m_vBar->width(),
402                     height() - borderTop() - borderBottom());
403
404    if (vertRect.contains(_x, _y)) {
405        result.setScrollbar(m_vBar.get());
406        return true;
407    }
408    return false;
409}
410
411int RenderListBox::listIndexAtOffset(int offsetX, int offsetY)
412{
413    if (!numItems())
414        return -1;
415
416    if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom())
417        return -1;
418
419    int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
420    if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth)
421        return -1;
422
423    int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
424    return newOffset < numItems() ? newOffset : -1;
425}
426
427void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
428{
429    const int maxSpeed = 20;
430    const int iconRadius = 7;
431    const int speedReducer = 4;
432
433    // FIXME: This doesn't work correctly with transforms.
434    FloatPoint absOffset = localToAbsolute();
435
436    IntPoint currentMousePosition = frame()->eventHandler()->currentMousePosition();
437    // 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
438    static IntPoint previousMousePosition;
439    if (currentMousePosition.y() < 0)
440        currentMousePosition = previousMousePosition;
441    else
442        previousMousePosition = currentMousePosition;
443
444    int yDelta = currentMousePosition.y() - panStartMousePosition.y();
445
446    // If the point is too far from the center we limit the speed
447    yDelta = max(min(yDelta, maxSpeed), -maxSpeed);
448
449    if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
450        return;
451
452    if (yDelta > 0)
453        //offsetY = view()->viewHeight();
454        absOffset.move(0, listHeight());
455    else if (yDelta < 0)
456        yDelta--;
457
458    // Let's attenuate the speed
459    yDelta /= speedReducer;
460
461    IntPoint scrollPoint(0, 0);
462    scrollPoint.setY(absOffset.y() + yDelta);
463    int newOffset = scrollToward(scrollPoint);
464    if (newOffset < 0)
465        return;
466
467    m_inAutoscroll = true;
468    SelectElement* select = toSelectElement(static_cast<Element*>(node()));
469    select->updateListBoxSelection(!select->multiple());
470    m_inAutoscroll = false;
471}
472
473int RenderListBox::scrollToward(const IntPoint& destination)
474{
475    // FIXME: This doesn't work correctly with transforms.
476    FloatPoint absPos = localToAbsolute();
477    int offsetX = destination.x() - absPos.x();
478    int offsetY = destination.y() - absPos.y();
479
480    int rows = numVisibleItems();
481    int offset = m_indexOffset;
482
483    if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
484        return offset - 1;
485
486    if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
487        return offset + rows - 1;
488
489    return listIndexAtOffset(offsetX, offsetY);
490}
491
492void RenderListBox::autoscroll()
493{
494    IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition());
495
496    int endIndex = scrollToward(pos);
497    if (endIndex >= 0) {
498        SelectElement* select = toSelectElement(static_cast<Element*>(node()));
499        m_inAutoscroll = true;
500
501        if (!select->multiple())
502            select->setActiveSelectionAnchorIndex(endIndex);
503
504        select->setActiveSelectionEndIndex(endIndex);
505        select->updateListBoxSelection(!select->multiple());
506        m_inAutoscroll = false;
507    }
508}
509
510void RenderListBox::stopAutoscroll()
511{
512    toSelectElement(static_cast<Element*>(node()))->listBoxOnChange();
513}
514
515bool RenderListBox::scrollToRevealElementAtListIndex(int index)
516{
517    if (index < 0 || index >= numItems() || listIndexIsVisible(index))
518        return false;
519
520    int newOffset;
521    if (index < m_indexOffset)
522        newOffset = index;
523    else
524        newOffset = index - numVisibleItems() + 1;
525
526    ScrollableArea::scrollToYOffsetWithoutAnimation(newOffset);
527
528    return true;
529}
530
531bool RenderListBox::listIndexIsVisible(int index)
532{
533    return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
534}
535
536bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**)
537{
538    return ScrollableArea::scroll(direction, granularity, multiplier);
539}
540
541bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**)
542{
543    return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier);
544}
545
546void RenderListBox::valueChanged(unsigned listIndex)
547{
548    Element* element = static_cast<Element*>(node());
549    SelectElement* select = toSelectElement(element);
550    select->setSelectedIndex(select->listToOptionIndex(listIndex));
551    element->dispatchFormControlChangeEvent();
552}
553
554int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
555{
556    return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
557}
558
559int RenderListBox::scrollPosition(Scrollbar*) const
560{
561    return m_indexOffset;
562}
563
564void RenderListBox::setScrollOffset(const IntPoint& offset)
565{
566    scrollTo(offset.y());
567}
568
569void RenderListBox::scrollTo(int newOffset)
570{
571    if (newOffset == m_indexOffset)
572        return;
573
574    m_indexOffset = newOffset;
575    repaint();
576    node()->document()->eventQueue()->enqueueScrollEvent(node(), EventQueue::ScrollEventElementTarget);
577}
578
579int RenderListBox::itemHeight() const
580{
581    return style()->fontMetrics().height() + rowSpacing;
582}
583
584int RenderListBox::verticalScrollbarWidth() const
585{
586    return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
587}
588
589// FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
590// how the control currently paints.
591int RenderListBox::scrollWidth() const
592{
593    // There is no horizontal scrolling allowed.
594    return clientWidth();
595}
596
597int RenderListBox::scrollHeight() const
598{
599    return max(clientHeight(), listHeight());
600}
601
602int RenderListBox::scrollLeft() const
603{
604    return 0;
605}
606
607void RenderListBox::setScrollLeft(int)
608{
609}
610
611int RenderListBox::scrollTop() const
612{
613    return m_indexOffset * itemHeight();
614}
615
616void RenderListBox::setScrollTop(int newTop)
617{
618    // Determine an index and scroll to it.
619    int index = newTop / itemHeight();
620    if (index < 0 || index >= numItems() || index == m_indexOffset)
621        return;
622
623    ScrollableArea::scrollToYOffsetWithoutAnimation(index);
624}
625
626bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
627{
628    if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction))
629        return false;
630    const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
631    int size = numItems();
632    tx += this->x();
633    ty += this->y();
634    for (int i = 0; i < size; ++i) {
635        if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) {
636            if (Element* node = listItems[i]) {
637                result.setInnerNode(node);
638                if (!result.innerNonSharedNode())
639                    result.setInnerNonSharedNode(node);
640                result.setLocalPoint(IntPoint(x - tx, y - ty));
641                break;
642            }
643        }
644    }
645
646    return true;
647}
648
649IntRect RenderListBox::controlClipRect(int tx, int ty) const
650{
651    IntRect clipRect = contentBoxRect();
652    clipRect.move(tx, ty);
653    return clipRect;
654}
655
656bool RenderListBox::isActive() const
657{
658    Page* page = frame()->page();
659    return page && page->focusController()->isActive();
660}
661
662void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
663{
664    IntRect scrollRect = rect;
665    scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
666    repaintRectangle(scrollRect);
667}
668
669IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
670{
671    RenderView* view = this->view();
672    if (!view)
673        return scrollbarRect;
674
675    IntRect rect = scrollbarRect;
676
677    int scrollbarLeft = width() - borderRight() - scrollbar->width();
678    int scrollbarTop = borderTop();
679    rect.move(scrollbarLeft, scrollbarTop);
680
681    return view->frameView()->convertFromRenderer(this, rect);
682}
683
684IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
685{
686    RenderView* view = this->view();
687    if (!view)
688        return parentRect;
689
690    IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
691
692    int scrollbarLeft = width() - borderRight() - scrollbar->width();
693    int scrollbarTop = borderTop();
694    rect.move(-scrollbarLeft, -scrollbarTop);
695    return rect;
696}
697
698IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
699{
700    RenderView* view = this->view();
701    if (!view)
702        return scrollbarPoint;
703
704    IntPoint point = scrollbarPoint;
705
706    int scrollbarLeft = width() - borderRight() - scrollbar->width();
707    int scrollbarTop = borderTop();
708    point.move(scrollbarLeft, scrollbarTop);
709
710    return view->frameView()->convertFromRenderer(this, point);
711}
712
713IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
714{
715    RenderView* view = this->view();
716    if (!view)
717        return parentPoint;
718
719    IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
720
721    int scrollbarLeft = width() - borderRight() - scrollbar->width();
722    int scrollbarTop = borderTop();
723    point.move(-scrollbarLeft, -scrollbarTop);
724    return point;
725}
726
727IntSize RenderListBox::contentsSize() const
728{
729    return IntSize(scrollWidth(), scrollHeight());
730}
731
732int RenderListBox::visibleHeight() const
733{
734    return height();
735}
736
737int RenderListBox::visibleWidth() const
738{
739    return width();
740}
741
742IntPoint RenderListBox::currentMousePosition() const
743{
744    RenderView* view = this->view();
745    if (!view)
746        return IntPoint();
747    return view->frameView()->currentMousePosition();
748}
749
750PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
751{
752    RefPtr<Scrollbar> widget;
753    bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
754    if (hasCustomScrollbarStyle)
755        widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this);
756    else {
757        widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
758        didAddVerticalScrollbar(widget.get());
759    }
760    document()->view()->addChild(widget.get());
761    return widget.release();
762}
763
764void RenderListBox::destroyScrollbar()
765{
766    if (!m_vBar)
767        return;
768
769    if (!m_vBar->isCustomScrollbar())
770        ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get());
771    m_vBar->removeFromParent();
772    m_vBar->disconnectFromScrollableArea();
773    m_vBar = 0;
774}
775
776void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
777{
778    if (hasScrollbar == (m_vBar != 0))
779        return;
780
781    if (hasScrollbar)
782        m_vBar = createScrollbar();
783    else
784        destroyScrollbar();
785
786    if (m_vBar)
787        m_vBar->styleChanged();
788
789#if ENABLE(DASHBOARD_SUPPORT)
790    // Force an update since we know the scrollbars have changed things.
791    if (document()->hasDashboardRegions())
792        document()->setDashboardRegionsDirty(true);
793#endif
794}
795
796} // namespace WebCore
797