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 blink {
     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::setHoveredPart(ScrollbarPart part)
    114 {
    115     if (part == m_hoveredPart)
    116         return;
    117 
    118     ScrollbarPart oldPart = m_hoveredPart;
    119     m_hoveredPart = part;
    120 
    121     updateScrollbarPart(oldPart);
    122     updateScrollbarPart(m_hoveredPart);
    123 
    124     updateScrollbarPart(ScrollbarBGPart);
    125     updateScrollbarPart(TrackBGPart);
    126 }
    127 
    128 void RenderScrollbar::setPressedPart(ScrollbarPart part)
    129 {
    130     ScrollbarPart oldPart = m_pressedPart;
    131     Scrollbar::setPressedPart(part);
    132 
    133     updateScrollbarPart(oldPart);
    134     updateScrollbarPart(part);
    135 
    136     updateScrollbarPart(ScrollbarBGPart);
    137     updateScrollbarPart(TrackBGPart);
    138 }
    139 
    140 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
    141 {
    142     if (!owningRenderer())
    143         return nullptr;
    144 
    145     RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(PseudoStyleRequest(pseudoId, this, partType), owningRenderer()->style());
    146     // Scrollbars for root frames should always have background color
    147     // unless explicitly specified as transparent. So we force it.
    148     // This is because WebKit assumes scrollbar to be always painted and missing background
    149     // causes visual artifact like non-paint invalidated dirty region.
    150     if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
    151         result->setBackgroundColor(StyleColor(Color::white));
    152 
    153     return result;
    154 }
    155 
    156 void RenderScrollbar::updateScrollbarParts(bool destroy)
    157 {
    158     updateScrollbarPart(ScrollbarBGPart, destroy);
    159     updateScrollbarPart(BackButtonStartPart, destroy);
    160     updateScrollbarPart(ForwardButtonStartPart, destroy);
    161     updateScrollbarPart(BackTrackPart, destroy);
    162     updateScrollbarPart(ThumbPart, destroy);
    163     updateScrollbarPart(ForwardTrackPart, destroy);
    164     updateScrollbarPart(BackButtonEndPart, destroy);
    165     updateScrollbarPart(ForwardButtonEndPart, destroy);
    166     updateScrollbarPart(TrackBGPart, destroy);
    167 
    168     if (destroy)
    169         return;
    170 
    171     // See if the scrollbar's thickness changed.  If so, we need to mark our owning object as needing a layout.
    172     bool isHorizontal = orientation() == HorizontalScrollbar;
    173     int oldThickness = isHorizontal ? height() : width();
    174     int newThickness = 0;
    175     RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
    176     if (part) {
    177         part->layout();
    178         newThickness = isHorizontal ? part->height() : part->width();
    179     }
    180 
    181     if (newThickness != oldThickness) {
    182         setFrameRect(IntRect(location(), IntSize(isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())));
    183         if (RenderBox* box = owningRenderer())
    184             box->setChildNeedsLayout();
    185     }
    186 }
    187 
    188 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
    189 {
    190     switch (part) {
    191         case BackButtonStartPart:
    192         case ForwardButtonStartPart:
    193         case BackButtonEndPart:
    194         case ForwardButtonEndPart:
    195             return SCROLLBAR_BUTTON;
    196         case BackTrackPart:
    197         case ForwardTrackPart:
    198             return SCROLLBAR_TRACK_PIECE;
    199         case ThumbPart:
    200             return SCROLLBAR_THUMB;
    201         case TrackBGPart:
    202             return SCROLLBAR_TRACK;
    203         case ScrollbarBGPart:
    204             return SCROLLBAR;
    205         case NoPart:
    206         case AllParts:
    207             break;
    208     }
    209     ASSERT_NOT_REACHED();
    210     return SCROLLBAR;
    211 }
    212 
    213 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
    214 {
    215     if (partType == NoPart)
    216         return;
    217 
    218     RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType,  pseudoForScrollbarPart(partType)) : PassRefPtr<RenderStyle>(nullptr);
    219 
    220     bool needRenderer = !destroy && partStyle && partStyle->display() != NONE;
    221 
    222     if (needRenderer && partStyle->display() != BLOCK) {
    223         // See if we are a button that should not be visible according to OS settings.
    224         ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
    225         switch (partType) {
    226             case BackButtonStartPart:
    227                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
    228                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    229                 break;
    230             case ForwardButtonStartPart:
    231                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    232                 break;
    233             case BackButtonEndPart:
    234                 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
    235                 break;
    236             case ForwardButtonEndPart:
    237                 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
    238                                 buttonsPlacement == ScrollbarButtonsDoubleBoth);
    239                 break;
    240             default:
    241                 break;
    242         }
    243     }
    244 
    245     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    246     if (!partRenderer && needRenderer) {
    247         partRenderer = RenderScrollbarPart::createAnonymous(&owningRenderer()->document(), this, partType);
    248         m_parts.set(partType, partRenderer);
    249     } else if (partRenderer && !needRenderer) {
    250         m_parts.remove(partType);
    251         partRenderer->destroy();
    252         partRenderer = 0;
    253     }
    254 
    255     if (partRenderer)
    256         partRenderer->setStyle(partStyle.release());
    257 }
    258 
    259 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
    260 {
    261     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    262     if (!partRenderer)
    263         return;
    264     partRenderer->paintIntoRect(graphicsContext, location(), rect);
    265 }
    266 
    267 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
    268 {
    269     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    270     if (!partRenderer)
    271         return IntRect();
    272 
    273     partRenderer->layout();
    274 
    275     bool isHorizontal = orientation() == HorizontalScrollbar;
    276     if (partType == BackButtonStartPart)
    277         return IntRect(location(), IntSize(isHorizontal ? partRenderer->pixelSnappedWidth() : width(), isHorizontal ? height() : partRenderer->pixelSnappedHeight()));
    278     if (partType == ForwardButtonEndPart)
    279         return IntRect(isHorizontal ? x() + width() - partRenderer->pixelSnappedWidth() : x(),
    280                        isHorizontal ? y() : y() + height() - partRenderer->pixelSnappedHeight(),
    281                        isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    282                        isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    283 
    284     if (partType == ForwardButtonStartPart) {
    285         IntRect previousButton = buttonRect(BackButtonStartPart);
    286         return IntRect(isHorizontal ? x() + previousButton.width() : x(),
    287                        isHorizontal ? y() : y() + previousButton.height(),
    288                        isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    289                        isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    290     }
    291 
    292     IntRect followingButton = buttonRect(ForwardButtonEndPart);
    293     return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->pixelSnappedWidth() : x(),
    294                    isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->pixelSnappedHeight(),
    295                    isHorizontal ? partRenderer->pixelSnappedWidth() : width(),
    296                    isHorizontal ? height() : partRenderer->pixelSnappedHeight());
    297 }
    298 
    299 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
    300 {
    301     RenderScrollbarPart* part = m_parts.get(TrackBGPart);
    302     if (part)
    303         part->layout();
    304 
    305     if (orientation() == HorizontalScrollbar) {
    306         int marginLeft = part ? static_cast<int>(part->marginLeft()) : 0;
    307         int marginRight = part ? static_cast<int>(part->marginRight()) : 0;
    308         startLength += marginLeft;
    309         endLength += marginRight;
    310         int totalLength = startLength + endLength;
    311         return IntRect(x() + startLength, y(), width() - totalLength, height());
    312     }
    313 
    314     int marginTop = part ? static_cast<int>(part->marginTop()) : 0;
    315     int marginBottom = part ? static_cast<int>(part->marginBottom()) : 0;
    316     startLength += marginTop;
    317     endLength += marginBottom;
    318     int totalLength = startLength + endLength;
    319 
    320     return IntRect(x(), y() + startLength, width(), height() - totalLength);
    321 }
    322 
    323 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
    324 {
    325     RenderScrollbarPart* partRenderer = m_parts.get(partType);
    326     if (!partRenderer)
    327         return oldRect;
    328 
    329     partRenderer->layout();
    330 
    331     IntRect rect = oldRect;
    332     if (orientation() == HorizontalScrollbar) {
    333         rect.setX(rect.x() + partRenderer->marginLeft());
    334         rect.setWidth(rect.width() - partRenderer->marginWidth());
    335     } else {
    336         rect.setY(rect.y() + partRenderer->marginTop());
    337         rect.setHeight(rect.height() - partRenderer->marginHeight());
    338     }
    339     return rect;
    340 }
    341 
    342 int RenderScrollbar::minimumThumbLength()
    343 {
    344     RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
    345     if (!partRenderer)
    346         return 0;
    347     partRenderer->layout();
    348     return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
    349 }
    350 
    351 }
    352