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 "core/rendering/RenderScrollbar.h"
     28 
     29 #include "core/css/PseudoStyleRequest.h"
     30 #include "core/frame/Frame.h"
     31 #include "core/frame/FrameView.h"
     32 #include "core/rendering/RenderPart.h"
     33 #include "core/rendering/RenderScrollbarPart.h"
     34 #include "core/rendering/RenderScrollbarTheme.h"
     35 
     36 namespace WebCore {
     37 
     38 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, Frame* owningFrame)
     39 {
     40     return adoptRef(new RenderScrollbar(scrollableArea, orientation, ownerNode, owningFrame));
     41 }
     42 
     43 RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, Node* ownerNode, Frame* owningFrame)
     44     : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
     45     , m_owner(ownerNode)
     46     , m_owningFrame(owningFrame)
     47 {
     48     ASSERT(ownerNode || owningFrame);
     49 
     50     // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
     51 
     52     // Update the scrollbar size.
     53     int width = 0;
     54     int height = 0;
     55     updateScrollbarPart(ScrollbarBGPart);
     56     if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
     57         part->layout();
     58         width = part->width();
     59         height = part->height();
     60     } else if (this->orientation() == HorizontalScrollbar)
     61         width = this->width();
     62     else
     63         height = this->height();
     64 
     65     setFrameRect(IntRect(0, 0, width, height));
     66 }
     67 
     68 RenderScrollbar::~RenderScrollbar()
     69 {
     70     if (!m_parts.isEmpty()) {
     71         // When a scrollbar is detached from its parent (causing all parts removal) and
     72         // ready to be destroyed, its destruction can be delayed because of RefPtr
     73         // maintained in other classes such as EventHandler (m_lastScrollbarUnderMouse).
     74         // Meanwhile, we can have a call to updateScrollbarPart which recreates the
     75         // scrollbar part. So, we need to destroy these parts since we don't want them
     76         // to call on a destroyed scrollbar. See webkit bug 68009.
     77         updateScrollbarParts(true);
     78     }
     79 }
     80 
     81 RenderBox* RenderScrollbar::owningRenderer() const
     82 {
     83     if (m_owningFrame) {
     84         RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
     85         return currentRenderer;
     86     }
     87     return m_owner && m_owner->renderer() ? m_owner->renderer()->enclosingBox() : 0;
     88 }
     89 
     90 void RenderScrollbar::setParent(Widget* parent)
     91 {
     92     Scrollbar::setParent(parent);
     93     if (!parent) {
     94         // Destroy all of the scrollbar's RenderBoxes.
     95         updateScrollbarParts(true);
     96     }
     97 }
     98 
     99 void RenderScrollbar::setEnabled(bool e)
    100 {
    101     bool wasEnabled = enabled();
    102     Scrollbar::setEnabled(e);
    103     if (wasEnabled != e)
    104         updateScrollbarParts();
    105 }
    106 
    107 void RenderScrollbar::styleChanged()
    108 {
    109     updateScrollbarParts();
    110 }
    111 
    112 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
    113 {
    114     if (context->updatingControlTints()) {
    115         updateScrollbarParts();
    116         return;
    117     }
    118     Scrollbar::paint(context, damageRect);
    119 }
    120 
    121 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
    122 {
    123     if (part == m_hoveredPart)
    124         return;
    125 
    126     ScrollbarPart oldPart = m_hoveredPart;
    127     m_hoveredPart = part;
    128 
    129     updateScrollbarPart(oldPart);
    130     updateScrollbarPart(m_hoveredPart);
    131 
    132     updateScrollbarPart(ScrollbarBGPart);
    133     updateScrollbarPart(TrackBGPart);
    134 }
    135 
    136 void RenderScrollbar::setPressedPart(ScrollbarPart part)
    137 {
    138     ScrollbarPart oldPart = m_pressedPart;
    139     Scrollbar::setPressedPart(part);
    140 
    141     updateScrollbarPart(oldPart);
    142     updateScrollbarPart(part);
    143 
    144     updateScrollbarPart(ScrollbarBGPart);
    145     updateScrollbarPart(TrackBGPart);
    146 }
    147 
    148 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
    149 {
    150     if (!owningRenderer())
    151         return 0;
    152 
    153     RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), owningRenderer()->style());
    154     // Scrollbars for root frames should always have background color
    155     // unless explicitly specified as transparent. So we force it.
    156     // This is because WebKit assumes scrollbar to be always painted and missing background
    157     // causes visual artifact like non-repainted dirty region.
    158     if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
    159         result->setBackgroundColor(Color::white);
    160 
    161     return result;
    162 }
    163 
    164 void RenderScrollbar::updateScrollbarParts(bool destroy)
    165 {
    166     updateScrollbarPart(ScrollbarBGPart, destroy);
    167     updateScrollbarPart(BackButtonStartPart, destroy);
    168     updateScrollbarPart(ForwardButtonStartPart, destroy);
    169     updateScrollbarPart(BackTrackPart, destroy);
    170     updateScrollbarPart(ThumbPart, destroy);
    171     updateScrollbarPart(ForwardTrackPart, destroy);
    172     updateScrollbarPart(BackButtonEndPart, destroy);
    173     updateScrollbarPart(ForwardButtonEndPart, destroy);
    174     updateScrollbarPart(TrackBGPart, destroy);
    175 
    176     if (destroy)
    177         return;
    178 
    179     // See if the scrollbar's thickness changed.  If so, we need to mark our owning object as needing a layout.
    180     bool isHorizontal = orientation() == HorizontalScrollbar;
    181     int oldThickness = isHorizontal ? height() : width();
    182     int newThickness = 0;
    183     RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
    184     if (part) {
    185         part->layout();
    186         newThickness = isHorizontal ? part->height() : part->width();
    187     }
    188 
    189     if (newThickness != oldThickness) {
    190         setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())));
    191         if (RenderBox* box = owningRenderer())
    192             box->setChildNeedsLayout();
    193     }
    194 }
    195 
    196 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
    197 {
    198     switch (part) {
    199         case BackButtonStartPart:
    200         case ForwardButtonStartPart:
    201         case BackButtonEndPart:
    202         case ForwardButtonEndPart:
    203             return SCROLLBAR_BUTTON;
    204         case BackTrackPart:
    205         case ForwardTrackPart:
    206             return SCROLLBAR_TRACK_PIECE;
    207         case ThumbPart:
    208             return SCROLLBAR_THUMB;
    209         case TrackBGPart:
    210             return SCROLLBAR_TRACK;
    211         case ScrollbarBGPart:
    212             return SCROLLBAR;
    213         case NoPart:
    214         case AllParts:
    215             break;
    216     }
    217     ASSERT_NOT_REACHED();
    218     return SCROLLBAR;
    219 }
    220 
    221 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
    222 {
    223     if (partType == NoPart)
    224         return;
    225 
    226     RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType,  pseudoForScrollbarPart(partType)) : PassRefPtr<RenderStyle>(0);
    227 
    228     bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
    229 
    230     if (needRenderer && partStyle->display() != BLOCK) {
    231         // See if we are a button that should not be visible according to OS settings.
    232         ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
    233         switch (partType) {
    234             case BackButtonStartPart:
    235                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
    236                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    237                 break;
    238             case ForwardButtonStartPart:
    239                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    240                 break;
    241             case BackButtonEndPart:
    242                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    243                 break;
    244             case ForwardButtonEndPart:
    245                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
    246                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    247                 break;
    248             default:
    249                 break;
    250         }
    251     }
    252 
    253     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    254     if (!partRenderer && needRenderer) {
    255         partRenderer = RenderScrollbarPart::createAnonymous(&owningRenderer()->document(), this, partType);
    256         m_parts.set(partType, partRenderer);
    257     } else if (partRenderer && !needRenderer) {
    258         m_parts.remove(partType);
    259         partRenderer->destroy();
    260         partRenderer = 0;
    261     }
    262 
    263     if (partRenderer)
    264         partRenderer->setStyle(partStyle.release());
    265 }
    266 
    267 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
    268 {
    269     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    270     if (!partRenderer)
    271         return;
    272     partRenderer->paintIntoRect(graphicsContext, location(), rect);
    273 }
    274 
    275 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
    276 {
    277     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    278     if (!partRenderer)
    279         return IntRect();
    280 
    281     partRenderer->layout();
    282 
    283     bool isHorizontal = orientation() == HorizontalScrollbar;
    284     if (partType == BackButtonStartPart)
    285         return IntRect(location(), IntSize(isHorizontal ? partRenderer->pixelSnappedWidth() : width(), isHorizontal ? height() : partRenderer->pixelSnappedHeight()));
    286     if (partType == ForwardButtonEndPart)
    287         return IntRect(isHorizontal ? x() + width() - partRenderer->pixelSnappedWidth() : x(),
    288                        isHorizontal ? y() : y() + height() - partRenderer->pixelSnappedHeight(),
    289                        isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    290                        isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    291 
    292     if (partType == ForwardButtonStartPart) {
    293         IntRect previousButton = buttonRect(BackButtonStartPart);
    294         return IntRect(isHorizontal ? x() + previousButton.width() : x(),
    295                        isHorizontal ? y() : y() + previousButton.height(),
    296                        isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    297                        isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    298     }
    299 
    300     IntRect followingButton = buttonRect(ForwardButtonEndPart);
    301     return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->pixelSnappedWidth() : x(),
    302                    isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->pixelSnappedHeight(),
    303                    isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    304                    isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    305 }
    306 
    307 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
    308 {
    309     RenderScrollbarPart* part = m_parts.get(TrackBGPart);
    310     if (part)
    311         part->layout();
    312 
    313     if (orientation() == HorizontalScrollbar) {
    314         int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0;
    315         int marginRight = part ? static_cast<int>(part->marginRight()) : 0;
    316         startLength += marginLeft;
    317         endLength += marginRight;
    318         int totalLength = startLength + endLength;
    319         return IntRect(x() + startLength, y(), width() - totalLength, height());
    320     }
    321 
    322     int marginTop = part ? static_cast<int>(part->marginTop()) : 0;
    323     int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0;
    324     startLength += marginTop;
    325     endLength += marginBottom;
    326     int totalLength = startLength + endLength;
    327 
    328     return IntRect(x(), y() + startLength, width(), height() - totalLength);
    329 }
    330 
    331 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
    332 {
    333     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    334     if (!partRenderer)
    335         return oldRect;
    336 
    337     partRenderer->layout();
    338 
    339     IntRect rect = oldRect;
    340     if (orientation() == HorizontalScrollbar) {
    341         rect.setX(rect.x() + partRenderer->marginLeft());
    342         rect.setWidth(rect.width() - partRenderer->marginWidth());
    343     } else {
    344         rect.setY(rect.y() + partRenderer->marginTop());
    345         rect.setHeight(rect.height() - partRenderer->marginHeight());
    346     }
    347     return rect;
    348 }
    349 
    350 int RenderScrollbar::minimumThumbLength()
    351 {
    352     RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
    353     if (!partRenderer)
    354         return 0;
    355     partRenderer->layout();
    356     return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
    357 }
    358 
    359 }
    360