Home | History | Annotate | Download | only in platform
      1 /*
      2  * Copyright (c) 2010, Google Inc. All rights reserved.
      3  * Copyright (C) 2008, 2011 Apple Inc. All Rights Reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "core/platform/ScrollableArea.h"
     34 
     35 #include "core/platform/ScrollAnimator.h"
     36 #include "core/platform/ScrollbarTheme.h"
     37 #include "core/platform/graphics/FloatPoint.h"
     38 #include "core/platform/graphics/GraphicsLayer.h"
     39 #include "wtf/PassOwnPtr.h"
     40 
     41 #include "core/platform/chromium/TraceEvent.h"
     42 
     43 static const int kPixelsPerLineStep = 40;
     44 static const float kMinFractionToStepWhenPaging = 0.875f;
     45 
     46 namespace WebCore {
     47 
     48 struct SameSizeAsScrollableArea {
     49     virtual ~SameSizeAsScrollableArea();
     50     void* pointer;
     51     unsigned bitfields : 16;
     52     IntPoint origin;
     53 };
     54 
     55 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
     56 
     57 int ScrollableArea::pixelsPerLineStep()
     58 {
     59     return kPixelsPerLineStep;
     60 }
     61 
     62 float ScrollableArea::minFractionToStepWhenPaging()
     63 {
     64     return kMinFractionToStepWhenPaging;
     65 }
     66 
     67 int ScrollableArea::maxOverlapBetweenPages()
     68 {
     69     static int maxOverlapBetweenPages = ScrollbarTheme::theme()->maxOverlapBetweenPages();
     70     return maxOverlapBetweenPages;
     71 }
     72 
     73 ScrollableArea::ScrollableArea()
     74     : m_constrainsScrollingToContentEdge(true)
     75     , m_inLiveResize(false)
     76     , m_verticalScrollElasticity(ScrollElasticityNone)
     77     , m_horizontalScrollElasticity(ScrollElasticityNone)
     78     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
     79     , m_scrollOriginChanged(false)
     80 {
     81 }
     82 
     83 ScrollableArea::~ScrollableArea()
     84 {
     85 }
     86 
     87 ScrollAnimator* ScrollableArea::scrollAnimator() const
     88 {
     89     if (!m_scrollAnimator)
     90         m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
     91 
     92     return m_scrollAnimator.get();
     93 }
     94 
     95 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
     96 {
     97     if (m_scrollOrigin != origin) {
     98         m_scrollOrigin = origin;
     99         m_scrollOriginChanged = true;
    100     }
    101 }
    102 
    103 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
    104 {
    105     ScrollbarOrientation orientation;
    106 
    107     if (direction == ScrollUp || direction == ScrollDown)
    108         orientation = VerticalScrollbar;
    109     else
    110         orientation = HorizontalScrollbar;
    111 
    112     if (!userInputScrollable(orientation))
    113         return false;
    114 
    115     float step = 0;
    116     switch (granularity) {
    117     case ScrollByLine:
    118         step = lineStep(orientation);
    119         break;
    120     case ScrollByPage:
    121         step = pageStep(orientation);
    122         break;
    123     case ScrollByDocument:
    124         step = documentStep(orientation);
    125         break;
    126     case ScrollByPixel:
    127     case ScrollByPrecisePixel:
    128         step = pixelStep(orientation);
    129         break;
    130     }
    131 
    132     if (direction == ScrollUp || direction == ScrollLeft)
    133         multiplier = -multiplier;
    134 
    135     return scrollAnimator()->scroll(orientation, granularity, step, multiplier);
    136 }
    137 
    138 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
    139 {
    140     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
    141 }
    142 
    143 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
    144 {
    145     if (orientation == HorizontalScrollbar)
    146         scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
    147     else
    148         scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
    149 }
    150 
    151 void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
    152 {
    153     scrollPositionChanged(position);
    154     scrollAnimator()->setCurrentPosition(position);
    155 }
    156 
    157 void ScrollableArea::scrollPositionChanged(const IntPoint& position)
    158 {
    159     TRACE_EVENT0("webkit", "ScrollableArea::scrollPositionChanged");
    160 
    161     IntPoint oldPosition = scrollPosition();
    162     // Tell the derived class to scroll its contents.
    163     setScrollOffset(position);
    164 
    165     Scrollbar* verticalScrollbar = this->verticalScrollbar();
    166 
    167     // Tell the scrollbars to update their thumb postions.
    168     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
    169         horizontalScrollbar->offsetDidChange();
    170         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
    171             if (!verticalScrollbar)
    172                 horizontalScrollbar->invalidate();
    173             else {
    174                 // If there is both a horizontalScrollbar and a verticalScrollbar,
    175                 // then we must also invalidate the corner between them.
    176                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
    177                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
    178                 horizontalScrollbar->invalidateRect(boundsAndCorner);
    179             }
    180         }
    181     }
    182     if (verticalScrollbar) {
    183         verticalScrollbar->offsetDidChange();
    184         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
    185             verticalScrollbar->invalidate();
    186     }
    187 
    188     if (scrollPosition() != oldPosition)
    189         scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
    190 }
    191 
    192 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
    193 {
    194     return scrollAnimator()->handleWheelEvent(wheelEvent);
    195 }
    196 
    197 // NOTE: Only called from Internals for testing.
    198 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
    199 {
    200     setScrollOffsetFromAnimation(offset);
    201 }
    202 
    203 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
    204 {
    205     if (requestScrollPositionUpdate(offset))
    206         return;
    207 
    208     scrollPositionChanged(offset);
    209 }
    210 
    211 void ScrollableArea::willStartLiveResize()
    212 {
    213     if (m_inLiveResize)
    214         return;
    215     m_inLiveResize = true;
    216     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    217         scrollAnimator->willStartLiveResize();
    218 }
    219 
    220 void ScrollableArea::willEndLiveResize()
    221 {
    222     if (!m_inLiveResize)
    223         return;
    224     m_inLiveResize = false;
    225     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    226         scrollAnimator->willEndLiveResize();
    227 }
    228 
    229 void ScrollableArea::contentAreaWillPaint() const
    230 {
    231     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    232         scrollAnimator->contentAreaWillPaint();
    233 }
    234 
    235 void ScrollableArea::mouseEnteredContentArea() const
    236 {
    237     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    238         scrollAnimator->mouseEnteredContentArea();
    239 }
    240 
    241 void ScrollableArea::mouseExitedContentArea() const
    242 {
    243     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    244         scrollAnimator->mouseEnteredContentArea();
    245 }
    246 
    247 void ScrollableArea::mouseMovedInContentArea() const
    248 {
    249     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    250         scrollAnimator->mouseMovedInContentArea();
    251 }
    252 
    253 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
    254 {
    255     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
    256 }
    257 
    258 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
    259 {
    260     scrollAnimator()->mouseExitedScrollbar(scrollbar);
    261 }
    262 
    263 void ScrollableArea::contentAreaDidShow() const
    264 {
    265     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    266         scrollAnimator->contentAreaDidShow();
    267 }
    268 
    269 void ScrollableArea::contentAreaDidHide() const
    270 {
    271     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    272         scrollAnimator->contentAreaDidHide();
    273 }
    274 
    275 void ScrollableArea::finishCurrentScrollAnimations() const
    276 {
    277     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    278         scrollAnimator->finishCurrentScrollAnimations();
    279 }
    280 
    281 void ScrollableArea::didAddVerticalScrollbar(Scrollbar* scrollbar)
    282 {
    283     scrollAnimator()->didAddVerticalScrollbar(scrollbar);
    284 
    285     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
    286     setScrollbarOverlayStyle(scrollbarOverlayStyle());
    287 }
    288 
    289 void ScrollableArea::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
    290 {
    291     scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
    292 }
    293 
    294 void ScrollableArea::didAddHorizontalScrollbar(Scrollbar* scrollbar)
    295 {
    296     scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
    297 
    298     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
    299     setScrollbarOverlayStyle(scrollbarOverlayStyle());
    300 }
    301 
    302 void ScrollableArea::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
    303 {
    304     scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
    305 }
    306 
    307 void ScrollableArea::contentsResized()
    308 {
    309     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    310         scrollAnimator->contentsResized();
    311 }
    312 
    313 bool ScrollableArea::hasOverlayScrollbars() const
    314 {
    315     return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
    316         || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
    317 }
    318 
    319 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
    320 {
    321     m_scrollbarOverlayStyle = overlayStyle;
    322 
    323     if (horizontalScrollbar()) {
    324         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(horizontalScrollbar());
    325         horizontalScrollbar()->invalidate();
    326     }
    327 
    328     if (verticalScrollbar()) {
    329         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(verticalScrollbar());
    330         verticalScrollbar()->invalidate();
    331     }
    332 }
    333 
    334 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
    335 {
    336     if (scrollbar == horizontalScrollbar()) {
    337         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
    338             graphicsLayer->setNeedsDisplay();
    339             graphicsLayer->setContentsNeedsDisplay();
    340             return;
    341         }
    342     } else if (scrollbar == verticalScrollbar()) {
    343         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
    344             graphicsLayer->setNeedsDisplay();
    345             graphicsLayer->setContentsNeedsDisplay();
    346             return;
    347         }
    348     }
    349     invalidateScrollbarRect(scrollbar, rect);
    350 }
    351 
    352 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
    353 {
    354     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
    355         graphicsLayer->setNeedsDisplay();
    356         return;
    357     }
    358     invalidateScrollCornerRect(rect);
    359 }
    360 
    361 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
    362 {
    363     return layerForHorizontalScrollbar();
    364 }
    365 
    366 bool ScrollableArea::hasLayerForVerticalScrollbar() const
    367 {
    368     return layerForVerticalScrollbar();
    369 }
    370 
    371 bool ScrollableArea::hasLayerForScrollCorner() const
    372 {
    373     return layerForScrollCorner();
    374 }
    375 
    376 void ScrollableArea::serviceScrollAnimations()
    377 {
    378     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
    379         scrollAnimator->serviceScrollAnimations();
    380 }
    381 
    382 IntRect ScrollableArea::visibleContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
    383 {
    384     int verticalScrollbarWidth = 0;
    385     int horizontalScrollbarHeight = 0;
    386 
    387     if (scrollbarInclusion == IncludeScrollbars) {
    388         if (Scrollbar* verticalBar = verticalScrollbar())
    389             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
    390         if (Scrollbar* horizontalBar = horizontalScrollbar())
    391             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
    392     }
    393 
    394     return IntRect(scrollPosition().x(),
    395                    scrollPosition().y(),
    396                    std::max(0, visibleWidth() + verticalScrollbarWidth),
    397                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
    398 }
    399 
    400 IntPoint ScrollableArea::clampScrollPosition(const IntPoint& scrollPosition) const
    401 {
    402     return scrollPosition.shrunkTo(maximumScrollPosition()).expandedTo(minimumScrollPosition());
    403 }
    404 
    405 int ScrollableArea::lineStep(ScrollbarOrientation) const
    406 {
    407     return pixelsPerLineStep();
    408 }
    409 
    410 int ScrollableArea::documentStep(ScrollbarOrientation orientation) const
    411 {
    412     return scrollSize(orientation);
    413 }
    414 
    415 float ScrollableArea::pixelStep(ScrollbarOrientation) const
    416 {
    417     return 1;
    418 }
    419 
    420 } // namespace WebCore
    421