4d8b0bb79095d710ad90ff70843d4d15b4c53865
[WebKit-https.git] / WebCore / rendering / RenderListBox.cpp
1 /*
2  * This file is part of the select element renderer in WebCore.
3  *
4  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
5  *               2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer. 
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution. 
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission. 
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "RenderListBox.h"
34
35 #include "AXObjectCache.h"
36 #include "CSSStyleSelector.h"
37 #include "Document.h"
38 #include "EventHandler.h"
39 #include "EventNames.h"
40 #include "FocusController.h"
41 #include "Frame.h"
42 #include "FrameView.h"
43 #include "GraphicsContext.h"
44 #include "HTMLNames.h"
45 #include "HTMLSelectElement.h"
46 #include "HitTestResult.h"
47 #include "OptionGroupElement.h"
48 #include "OptionElement.h"
49 #include "Page.h"
50 #include "RenderScrollbar.h"
51 #include "RenderTheme.h"
52 #include "RenderView.h"
53 #include "Scrollbar.h"
54 #include "SelectionController.h"
55 #include "NodeRenderStyle.h"
56 #include <math.h>
57
58 using namespace std;
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63  
64 const int rowSpacing = 1;
65
66 const int optionsSpacingHorizontal = 2;
67
68 const int minSize = 4;
69 const int maxDefaultSize = 10;
70
71 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
72 // widget, but I'm not sure this is right for the new control.
73 const int baselineAdjustment = 7;
74
75 RenderListBox::RenderListBox(HTMLSelectElement* element)
76     : RenderBlock(element)
77     , m_optionsChanged(true)
78     , m_scrollToRevealSelectionAfterLayout(false)
79     , m_inAutoscroll(false)
80     , m_optionsWidth(0)
81     , m_indexOffset(0)
82 {
83 }
84
85 RenderListBox::~RenderListBox()
86 {
87     setHasVerticalScrollbar(false);
88 }
89
90 void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
91 {
92     RenderBlock::styleDidChange(diff, oldStyle);
93     setReplaced(isInline());
94 }
95
96 void RenderListBox::updateFromElement()
97 {
98     if (m_optionsChanged) {
99         const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems();
100         int size = numItems();
101         
102         float width = 0;
103         for (int i = 0; i < size; ++i) {
104             HTMLElement* element = listItems[i];
105             String text;
106             Font itemFont = style()->font();
107             if (OptionElement* optionElement = toOptionElement(element))
108                 text = optionElement->textIndentedToRespectGroupLabel();
109             else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) {
110                 text = optionGroupElement->groupLabelText();
111                 FontDescription d = itemFont.fontDescription();
112                 d.setWeight(d.bolderWeight());
113                 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
114                 itemFont.update(document()->styleSelector()->fontSelector());
115             }
116                 
117             if (!text.isEmpty()) {
118                 float textWidth = itemFont.floatWidth(TextRun(text.impl(), 0, 0, 0, false, false, false, false));
119                 width = max(width, textWidth);
120             }
121         }
122         m_optionsWidth = static_cast<int>(ceilf(width));
123         m_optionsChanged = false;
124         
125         setHasVerticalScrollbar(true);
126
127         setNeedsLayoutAndPrefWidthsRecalc();
128     }
129 }
130
131 void RenderListBox::selectionChanged()
132 {
133     repaint();
134     if (!m_inAutoscroll) {
135         if (m_optionsChanged || needsLayout())
136             m_scrollToRevealSelectionAfterLayout = true;
137         else
138             scrollToRevealSelection();
139     }
140     
141     if (AXObjectCache::accessibilityEnabled())
142         document()->axObjectCache()->selectedChildrenChanged(this);
143 }
144
145 void RenderListBox::layout()
146 {
147     RenderBlock::layout();
148     if (m_scrollToRevealSelectionAfterLayout)
149         scrollToRevealSelection();
150 }
151
152 void RenderListBox::scrollToRevealSelection()
153 {    
154     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
155
156     m_scrollToRevealSelectionAfterLayout = false;
157
158     int firstIndex = select->activeSelectionStartListIndex();
159     if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex()))
160         scrollToRevealElementAtListIndex(firstIndex);
161 }
162
163 void RenderListBox::calcPrefWidths()
164 {
165     ASSERT(!m_optionsChanged);
166
167     m_minPrefWidth = 0;
168     m_maxPrefWidth = 0;
169
170     if (style()->width().isFixed() && style()->width().value() > 0)
171         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
172     else {
173         m_maxPrefWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
174         if (m_vBar)
175             m_maxPrefWidth += m_vBar->width();
176     }
177
178     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
179         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
180         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
181     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
182         m_minPrefWidth = 0;
183     else
184         m_minPrefWidth = m_maxPrefWidth;
185
186     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
187         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
188         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
189     }
190
191     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
192     m_minPrefWidth += toAdd;
193     m_maxPrefWidth += toAdd;
194                                 
195     setPrefWidthsDirty(false);
196 }
197
198 int RenderListBox::size() const
199 {
200     int specifiedSize = static_cast<HTMLSelectElement*>(node())->size();
201     if (specifiedSize > 1)
202         return max(minSize, specifiedSize);
203     return min(max(minSize, numItems()), maxDefaultSize);
204 }
205
206 int RenderListBox::numVisibleItems() const
207 {
208     // Only count fully visible rows. But don't return 0 even if only part of a row shows.
209     return max(1, (contentHeight() + rowSpacing) / itemHeight());
210 }
211
212 int RenderListBox::numItems() const
213 {
214     return static_cast<HTMLSelectElement*>(node())->listItems().size();
215 }
216
217 int RenderListBox::listHeight() const
218 {
219     return itemHeight() * numItems() - rowSpacing;
220 }
221
222 void RenderListBox::calcHeight()
223 {
224     int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom();
225  
226     int itemHeight = RenderListBox::itemHeight();
227     setHeight(itemHeight * size() - rowSpacing + toAdd);
228     
229     RenderBlock::calcHeight();
230     
231     if (m_vBar) {
232         bool enabled = numVisibleItems() < numItems();
233         m_vBar->setEnabled(enabled);
234         m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight);
235         m_vBar->setProportion(numVisibleItems(), numItems());
236         if (!enabled)
237             m_indexOffset = 0;
238     }
239 }
240
241 int RenderListBox::baselinePosition(bool, bool) const
242 {
243     return height() + marginTop() + marginBottom() - baselineAdjustment;
244 }
245
246 IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index)
247 {
248     return IntRect(tx + borderLeft() + paddingLeft(),
249                    ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset),
250                    contentWidth(), itemHeight());
251 }
252     
253 void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty)
254 {
255     if (style()->visibility() != VISIBLE)
256         return;
257     
258     int listItemsSize = numItems();
259
260     if (paintInfo.phase == PaintPhaseForeground) {
261         int index = m_indexOffset;
262         while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
263             paintItemForeground(paintInfo, tx, ty, index);
264             index++;
265         }
266     }
267
268     // Paint the children.
269     RenderBlock::paintObject(paintInfo, tx, ty);
270
271     if (paintInfo.phase == PaintPhaseBlockBackground)
272         paintScrollbar(paintInfo, tx, ty);
273     else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
274         int index = m_indexOffset;
275         while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
276             paintItemBackground(paintInfo, tx, ty, index);
277             index++;
278         }
279     }
280 }
281
282 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty)
283 {
284     if (m_vBar) {
285         IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(),
286                            ty + borderTop(),
287                            m_vBar->width(),
288                            height() - (borderTop() + borderBottom()));
289         m_vBar->setFrameRect(scrollRect);
290         m_vBar->paint(paintInfo.context, paintInfo.rect);
291     }
292 }
293
294 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex)
295 {
296     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
297     const Vector<HTMLElement*>& listItems = select->listItems();
298     HTMLElement* element = listItems[listIndex];
299     OptionElement* optionElement = toOptionElement(element);
300
301     String itemText;
302     if (optionElement)
303         itemText = optionElement->textIndentedToRespectGroupLabel();
304     else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element))
305         itemText = optionGroupElement->groupLabelText();      
306
307     // Determine where the item text should be placed
308     IntRect r = itemBoundingBoxRect(tx, ty, listIndex);
309     r.move(optionsSpacingHorizontal, style()->font().ascent());
310
311     RenderStyle* itemStyle = element->renderStyle();
312     if (!itemStyle)
313         itemStyle = style();
314     
315     Color textColor = element->renderStyle() ? element->renderStyle()->color() : style()->color();
316     if (optionElement && optionElement->selected()) {
317         if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
318             textColor = theme()->activeListBoxSelectionForegroundColor();
319         // Honor the foreground color for disabled items
320         else if (!element->disabled())
321             textColor = theme()->inactiveListBoxSelectionForegroundColor();
322     }
323
324     paintInfo.context->setFillColor(textColor);
325
326     Font itemFont = style()->font();
327     if (element->hasTagName(optgroupTag)) {
328         FontDescription d = itemFont.fontDescription();
329         d.setWeight(d.bolderWeight());
330         itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
331         itemFont.update(document()->styleSelector()->fontSelector());
332     }
333
334     unsigned length = itemText.length();
335     const UChar* string = itemText.characters();
336     TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false);
337
338     // Draw the item text
339     if (itemStyle->visibility() != HIDDEN)
340         paintInfo.context->drawBidiText(itemFont, textRun, r.location());
341 }
342
343 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex)
344 {
345     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
346     const Vector<HTMLElement*>& listItems = select->listItems();
347     HTMLElement* element = listItems[listIndex];
348     OptionElement* optionElement = toOptionElement(element);
349
350     Color backColor;
351     if (optionElement && optionElement->selected()) {
352         if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
353             backColor = theme()->activeListBoxSelectionBackgroundColor();
354         else
355             backColor = theme()->inactiveListBoxSelectionBackgroundColor();
356     } else
357         backColor = element->renderStyle() ? element->renderStyle()->backgroundColor() : style()->backgroundColor();
358
359     // Draw the background for this list box item
360     if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
361         IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex);
362         itemRect.intersect(controlClipRect(tx, ty));
363         paintInfo.context->fillRect(itemRect, backColor);
364     }
365 }
366
367 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty)
368 {
369     if (!m_vBar)
370         return false;
371
372     IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(),
373                      _ty,
374                      m_vBar->width(),
375                      height() - borderTop() - borderBottom());
376
377     if (vertRect.contains(_x, _y)) {
378         result.setScrollbar(m_vBar.get());
379         return true;
380     }
381     return false;
382 }
383
384 int RenderListBox::listIndexAtOffset(int offsetX, int offsetY)
385 {
386     if (!numItems())
387         return -1;
388
389     if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom())
390         return -1;
391
392     int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
393     if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth)
394         return -1;
395
396     int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
397     return newOffset < numItems() ? newOffset : -1;
398 }
399
400 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
401 {
402     const int maxSpeed = 20;
403     const int iconRadius = 7;
404     const int speedReducer = 4;
405
406     // FIXME: This doesn't work correctly with transforms.
407     FloatPoint absOffset = localToAbsolute();
408
409     IntPoint currentMousePosition = document()->frame()->eventHandler()->currentMousePosition();
410     // 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
411     static IntPoint previousMousePosition;
412     if (currentMousePosition.y() < 0)
413         currentMousePosition = previousMousePosition;
414     else
415         previousMousePosition = currentMousePosition;
416
417     int yDelta = currentMousePosition.y() - panStartMousePosition.y();
418
419    // If the point is too far from the center we limit the speed
420     yDelta = max(min(yDelta, maxSpeed), -maxSpeed);
421     
422     if(abs(yDelta) < iconRadius) // at the center we let the space for the icon
423         return;
424
425     if (yDelta > 0)
426         //offsetY = view()->viewHeight();
427         absOffset.move(0, listHeight());
428    else if (yDelta < 0)
429        yDelta--;
430
431     // Let's attenuate the speed
432     yDelta /= speedReducer;
433
434     IntPoint scrollPoint(0,0);
435     scrollPoint.setY(absOffset.y() + yDelta);
436     int newOffset = scrollToward(scrollPoint);
437     if (newOffset < 0) 
438         return;
439
440     m_inAutoscroll = true;
441     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
442     select->updateListBoxSelection(!select->multiple());
443     m_inAutoscroll = false;
444 }
445
446 int RenderListBox::scrollToward(const IntPoint& destination)
447 {
448     // FIXME: This doesn't work correctly with transforms.
449     FloatPoint absPos = localToAbsolute();
450     int offsetX = destination.x() - absPos.x();
451     int offsetY = destination.y() - absPos.y();
452
453     int rows = numVisibleItems();
454     int offset = m_indexOffset;
455     
456     if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
457         return offset - 1;
458     
459     if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
460         return offset + rows - 1;
461     
462     return listIndexAtOffset(offsetX, offsetY);
463 }
464
465 void RenderListBox::autoscroll()
466 {
467     IntPoint pos = document()->frame()->view()->windowToContents(document()->frame()->eventHandler()->currentMousePosition());
468
469     int endIndex = scrollToward(pos);
470     if (endIndex >= 0) {
471         HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
472         m_inAutoscroll = true;
473
474         if (!select->multiple())
475             select->setActiveSelectionAnchorIndex(endIndex);
476
477         select->setActiveSelectionEndIndex(endIndex);
478         select->updateListBoxSelection(!select->multiple());
479         m_inAutoscroll = false;
480     }
481 }
482
483 void RenderListBox::stopAutoscroll()
484 {
485     static_cast<HTMLSelectElement*>(node())->listBoxOnChange();
486 }
487
488 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
489 {
490     if (index < 0 || index >= numItems() || listIndexIsVisible(index))
491         return false;
492
493     int newOffset;
494     if (index < m_indexOffset)
495         newOffset = index;
496     else
497         newOffset = index - numVisibleItems() + 1;
498
499     m_indexOffset = newOffset;
500     if (m_vBar)
501         m_vBar->setValue(m_indexOffset);
502
503     return true;
504 }
505
506 bool RenderListBox::listIndexIsVisible(int index)
507 {    
508     return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
509 }
510
511 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
512 {
513     return m_vBar && m_vBar->scroll(direction, granularity, multiplier);
514 }
515
516 void RenderListBox::valueChanged(unsigned listIndex)
517 {
518     HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node());
519     select->setSelectedIndex(select->listToOptionIndex(listIndex));
520     select->onChange();
521 }
522
523 void RenderListBox::valueChanged(Scrollbar*)
524 {
525     int newOffset = m_vBar->value();
526     if (newOffset != m_indexOffset) {
527         m_indexOffset = newOffset;
528         repaint();
529         // Fire the scroll DOM event.
530         node()->dispatchEventForType(eventNames().scrollEvent, false, false);
531     }
532 }
533
534 int RenderListBox::itemHeight() const
535 {
536     return style()->font().height() + rowSpacing;
537 }
538
539 int RenderListBox::verticalScrollbarWidth() const
540 {
541     return m_vBar ? m_vBar->width() : 0;
542 }
543
544 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
545 // how the control currently paints.
546 int RenderListBox::scrollWidth() const
547 {
548     // There is no horizontal scrolling allowed.
549     return clientWidth();
550 }
551
552 int RenderListBox::scrollHeight() const
553 {
554     return max(clientHeight(), listHeight());
555 }
556
557 int RenderListBox::scrollLeft() const
558 {
559     return 0;
560 }
561
562 void RenderListBox::setScrollLeft(int)
563 {
564 }
565
566 int RenderListBox::scrollTop() const
567 {
568     return m_indexOffset * itemHeight();
569 }
570
571 void RenderListBox::setScrollTop(int newTop)
572 {
573     // Determine an index and scroll to it.    
574     int index = newTop / itemHeight();
575     if (index < 0 || index >= numItems() || index == m_indexOffset)
576         return;
577     m_indexOffset = index;
578     if (m_vBar)
579         m_vBar->setValue(index);
580 }
581
582 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
583 {
584     if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction))
585         return false;
586     const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems();
587     int size = numItems();
588     tx += this->x();
589     ty += this->y();
590     for (int i = 0; i < size; ++i) {
591         if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) {
592             if (HTMLElement* node = listItems[i]) {
593                 result.setInnerNode(node);
594                 if (!result.innerNonSharedNode())
595                     result.setInnerNonSharedNode(node);
596                 result.setLocalPoint(IntPoint(x - tx, y - ty));
597                 break;
598             }
599         }
600     }
601
602     return true;
603 }
604
605 IntRect RenderListBox::controlClipRect(int tx, int ty) const
606 {
607     IntRect clipRect = contentBoxRect();
608     clipRect.move(tx, ty);
609     return clipRect;
610 }
611
612 bool RenderListBox::isActive() const
613 {
614     Page* page = document()->frame()->page();
615     return page && page->focusController()->isActive();
616 }
617
618 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
619 {
620     IntRect scrollRect = rect;
621     scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
622     repaintRectangle(scrollRect);
623 }
624
625 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
626 {
627     RefPtr<Scrollbar> widget;
628     bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
629     if (hasCustomScrollbarStyle)
630         widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this);
631     else
632         widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar);
633     document()->view()->addChild(widget.get());        
634     return widget.release();
635 }
636
637 void RenderListBox::destroyScrollbar()
638 {
639     if (!m_vBar)
640         return;
641     
642     m_vBar->removeFromParent();
643     m_vBar->setClient(0);
644     m_vBar = 0;
645 }
646
647 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
648 {
649     if (hasScrollbar == (m_vBar != 0))
650         return;
651
652     if (hasScrollbar)
653         m_vBar = createScrollbar();
654     else
655         destroyScrollbar();
656
657     if (m_vBar)
658         m_vBar->styleChanged();
659
660 #if ENABLE(DASHBOARD_SUPPORT)
661     // Force an update since we know the scrollbars have changed things.
662     if (document()->hasDashboardRegions())
663         document()->setDashboardRegionsDirty(true);
664 #endif
665 }
666
667 } // namespace WebCore