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