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