Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "RenderScrollbar.h"
     28 
     29 #include "Frame.h"
     30 #include "FrameView.h"
     31 #include "RenderPart.h"
     32 #include "RenderScrollbarPart.h"
     33 #include "RenderScrollbarTheme.h"
     34 
     35 namespace WebCore {
     36 
     37 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
     38 {
     39     return adoptRef(new RenderScrollbar(scrollableArea, orientation, renderer, owningFrame));
     40 }
     41 
     42 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
     43     : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
     44     , m_owner(renderer)
     45     , m_owningFrame(owningFrame)
     46 {
     47     // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
     48 
     49     // Update the scrollbar size.
     50     int width = 0;
     51     int height = 0;
     52     updateScrollbarPart(ScrollbarBGPart);
     53     if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
     54         part->layout();
     55         width = part->width();
     56         height = part->height();
     57     } else if (this->orientation() == HorizontalScrollbar)
     58         width = this->width();
     59     else
     60         height = this->height();
     61 
     62     setFrameRect(IntRect(0, 0, width, height));
     63 }
     64 
     65 RenderScrollbar::~RenderScrollbar()
     66 {
     67     ASSERT(m_parts.isEmpty());
     68 }
     69 
     70 RenderBox* RenderScrollbar::owningRenderer() const
     71 {
     72     if (m_owningFrame) {
     73         RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
     74         return currentRenderer;
     75     }
     76     return m_owner;
     77 }
     78 
     79 void RenderScrollbar::setParent(ScrollView* parent)
     80 {
     81     Scrollbar::setParent(parent);
     82     if (!parent) {
     83         // Destroy all of the scrollbar's RenderBoxes.
     84         updateScrollbarParts(true);
     85     }
     86 }
     87 
     88 void RenderScrollbar::setEnabled(bool e)
     89 {
     90     bool wasEnabled = enabled();
     91     Scrollbar::setEnabled(e);
     92     if (wasEnabled != e)
     93         updateScrollbarParts();
     94 }
     95 
     96 void RenderScrollbar::styleChanged()
     97 {
     98     updateScrollbarParts();
     99 }
    100 
    101 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
    102 {
    103     if (context->updatingControlTints()) {
    104         updateScrollbarParts();
    105         return;
    106     }
    107     Scrollbar::paint(context, damageRect);
    108 }
    109 
    110 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
    111 {
    112     if (part == m_hoveredPart)
    113         return;
    114 
    115     ScrollbarPart oldPart = m_hoveredPart;
    116     m_hoveredPart = part;
    117 
    118     updateScrollbarPart(oldPart);
    119     updateScrollbarPart(m_hoveredPart);
    120 
    121     updateScrollbarPart(ScrollbarBGPart);
    122     updateScrollbarPart(TrackBGPart);
    123 }
    124 
    125 void RenderScrollbar::setPressedPart(ScrollbarPart part)
    126 {
    127     ScrollbarPart oldPart = m_pressedPart;
    128     Scrollbar::setPressedPart(part);
    129 
    130     updateScrollbarPart(oldPart);
    131     updateScrollbarPart(part);
    132 
    133     updateScrollbarPart(ScrollbarBGPart);
    134     updateScrollbarPart(TrackBGPart);
    135 }
    136 
    137 static ScrollbarPart s_styleResolvePart;
    138 static RenderScrollbar* s_styleResolveScrollbar;
    139 
    140 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
    141 {
    142     return s_styleResolveScrollbar;
    143 }
    144 
    145 ScrollbarPart RenderScrollbar::partForStyleResolve()
    146 {
    147     return s_styleResolvePart;
    148 }
    149 
    150 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
    151 {
    152     if (!m_owner)
    153         return 0;
    154 
    155     s_styleResolvePart = partType;
    156     s_styleResolveScrollbar = this;
    157     RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style());
    158     s_styleResolvePart = NoPart;
    159     s_styleResolveScrollbar = 0;
    160 
    161     // Scrollbars for root frames should always have background color
    162     // unless explicitly specified as transparent. So we force it.
    163     // This is because WebKit assumes scrollbar to be always painted and missing background
    164     // causes visual artifact like non-repainted dirty region.
    165     if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
    166         result->setBackgroundColor(Color::white);
    167 
    168     return result;
    169 }
    170 
    171 void RenderScrollbar::updateScrollbarParts(bool destroy)
    172 {
    173     updateScrollbarPart(ScrollbarBGPart, destroy);
    174     updateScrollbarPart(BackButtonStartPart, destroy);
    175     updateScrollbarPart(ForwardButtonStartPart, destroy);
    176     updateScrollbarPart(BackTrackPart, destroy);
    177     updateScrollbarPart(ThumbPart, destroy);
    178     updateScrollbarPart(ForwardTrackPart, destroy);
    179     updateScrollbarPart(BackButtonEndPart, destroy);
    180     updateScrollbarPart(ForwardButtonEndPart, destroy);
    181     updateScrollbarPart(TrackBGPart, destroy);
    182 
    183     if (destroy)
    184         return;
    185 
    186     // See if the scrollbar's thickness changed.  If so, we need to mark our owning object as needing a layout.
    187     bool isHorizontal = orientation() == HorizontalScrollbar;
    188     int oldThickness = isHorizontal ? height() : width();
    189     int newThickness = 0;
    190     RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
    191     if (part) {
    192         part->layout();
    193         newThickness = isHorizontal ? part->height() : part->width();
    194     }
    195 
    196     if (newThickness != oldThickness) {
    197         setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()));
    198         owningRenderer()->setChildNeedsLayout(true);
    199     }
    200 }
    201 
    202 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
    203 {
    204     switch (part) {
    205         case BackButtonStartPart:
    206         case ForwardButtonStartPart:
    207         case BackButtonEndPart:
    208         case ForwardButtonEndPart:
    209             return SCROLLBAR_BUTTON;
    210         case BackTrackPart:
    211         case ForwardTrackPart:
    212             return SCROLLBAR_TRACK_PIECE;
    213         case ThumbPart:
    214             return SCROLLBAR_THUMB;
    215         case TrackBGPart:
    216             return SCROLLBAR_TRACK;
    217         case ScrollbarBGPart:
    218             return SCROLLBAR;
    219         case NoPart:
    220         case AllParts:
    221             break;
    222     }
    223     ASSERT_NOT_REACHED();
    224     return SCROLLBAR;
    225 }
    226 
    227 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
    228 {
    229     if (partType == NoPart)
    230         return;
    231 
    232     RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType,  pseudoForScrollbarPart(partType)) : 0;
    233 
    234     bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
    235 
    236     if (needRenderer && partStyle->display() != BLOCK) {
    237         // See if we are a button that should not be visible according to OS settings.
    238         ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
    239         switch (partType) {
    240             case BackButtonStartPart:
    241                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
    242                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    243                 break;
    244             case ForwardButtonStartPart:
    245                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    246                 break;
    247             case BackButtonEndPart:
    248                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    249                 break;
    250             case ForwardButtonEndPart:
    251                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
    252                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    253                 break;
    254             default:
    255                 break;
    256         }
    257     }
    258 
    259     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    260     if (!partRenderer && needRenderer) {
    261         partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType);
    262         m_parts.set(partType, partRenderer);
    263     } else if (partRenderer && !needRenderer) {
    264         m_parts.remove(partType);
    265         partRenderer->destroy();
    266         partRenderer = 0;
    267     }
    268 
    269     if (partRenderer)
    270         partRenderer->setStyle(partStyle.release());
    271 }
    272 
    273 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
    274 {
    275     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    276     if (!partRenderer)
    277         return;
    278     partRenderer->paintIntoRect(graphicsContext, x(), y(), rect);
    279 }
    280 
    281 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
    282 {
    283     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    284     if (!partRenderer)
    285         return IntRect();
    286 
    287     partRenderer->layout();
    288 
    289     bool isHorizontal = orientation() == HorizontalScrollbar;
    290     if (partType == BackButtonStartPart)
    291         return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height());
    292     if (partType == ForwardButtonEndPart)
    293         return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(),
    294 
    295                        isHorizontal ? y() : y() + height() - partRenderer->height(),
    296                        isHorizontal ? partRenderer->width() : width(),
    297                        isHorizontal ? height() : partRenderer->height());
    298 
    299     if (partType == ForwardButtonStartPart) {
    300         IntRect previousButton = buttonRect(BackButtonStartPart);
    301         return IntRect(isHorizontal ? x() + previousButton.width() : x(),
    302                        isHorizontal ? y() : y() + previousButton.height(),
    303                        isHorizontal ? partRenderer->width() : width(),
    304                        isHorizontal ? height() : partRenderer->height());
    305     }
    306 
    307     IntRect followingButton = buttonRect(ForwardButtonEndPart);
    308     return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(),
    309                    isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(),
    310                    isHorizontal ? partRenderer->width() : width(),
    311                    isHorizontal ? height() : partRenderer->height());
    312 }
    313 
    314 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
    315 {
    316     RenderScrollbarPart* part = m_parts.get(TrackBGPart);
    317     if (part)
    318         part->layout();
    319 
    320     if (orientation() == HorizontalScrollbar) {
    321         int marginLeft = part ? part->marginLeft() : 0;
    322         int marginRight = part ? part->marginRight() : 0;
    323         startLength += marginLeft;
    324         endLength += marginRight;
    325         int totalLength = startLength + endLength;
    326         return IntRect(x() + startLength, y(), width() - totalLength, height());
    327     }
    328 
    329     int marginTop = part ? part->marginTop() : 0;
    330     int marginBottom = part ? part->marginBottom() : 0;
    331     startLength += marginTop;
    332     endLength += marginBottom;
    333     int totalLength = startLength + endLength;
    334 
    335     return IntRect(x(), y() + startLength, width(), height() - totalLength);
    336 }
    337 
    338 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
    339 {
    340     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    341     if (!partRenderer)
    342         return oldRect;
    343 
    344     partRenderer->layout();
    345 
    346     IntRect rect = oldRect;
    347     if (orientation() == HorizontalScrollbar) {
    348         rect.setX(rect.x() + partRenderer->marginLeft());
    349         rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight()));
    350     } else {
    351         rect.setY(rect.y() + partRenderer->marginTop());
    352         rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom()));
    353     }
    354     return rect;
    355 }
    356 
    357 int RenderScrollbar::minimumThumbLength()
    358 {
    359     RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
    360     if (!partRenderer)
    361         return 0;
    362     partRenderer->layout();
    363     return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
    364 }
    365 
    366 }
    367