Home | History | Annotate | Download | only in platform
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 COMPUTER, 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 "ScrollView.h"
     28 
     29 #include "GraphicsContext.h"
     30 #include "HostWindow.h"
     31 #include "PlatformMouseEvent.h"
     32 #include "PlatformWheelEvent.h"
     33 #include "Scrollbar.h"
     34 #include "ScrollbarTheme.h"
     35 #include <wtf/StdLibExtras.h>
     36 
     37 using std::max;
     38 
     39 namespace WebCore {
     40 
     41 ScrollView::ScrollView()
     42     : m_horizontalScrollbarMode(ScrollbarAuto)
     43     , m_verticalScrollbarMode(ScrollbarAuto)
     44     , m_prohibitsScrolling(false)
     45     , m_canBlitOnScroll(true)
     46     , m_scrollbarsAvoidingResizer(0)
     47     , m_scrollbarsSuppressed(false)
     48     , m_inUpdateScrollbars(false)
     49     , m_updateScrollbarsPass(0)
     50     , m_drawPanScrollIcon(false)
     51     , m_useFixedLayout(false)
     52     , m_paintsEntireContents(false)
     53 {
     54     platformInit();
     55 }
     56 
     57 ScrollView::~ScrollView()
     58 {
     59     platformDestroy();
     60 }
     61 
     62 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
     63 {
     64     Widget* child = prpChild.get();
     65     ASSERT(child != this && !child->parent());
     66     child->setParent(this);
     67     m_children.add(prpChild);
     68     if (child->platformWidget())
     69         platformAddChild(child);
     70 }
     71 
     72 void ScrollView::removeChild(Widget* child)
     73 {
     74     ASSERT(child->parent() == this);
     75     child->setParent(0);
     76     m_children.remove(child);
     77     if (child->platformWidget())
     78         platformRemoveChild(child);
     79 }
     80 
     81 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
     82 {
     83     if (hasBar && !m_horizontalScrollbar) {
     84         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
     85         addChild(m_horizontalScrollbar.get());
     86         m_horizontalScrollbar->styleChanged();
     87     } else if (!hasBar && m_horizontalScrollbar) {
     88         removeChild(m_horizontalScrollbar.get());
     89         m_horizontalScrollbar = 0;
     90     }
     91 }
     92 
     93 void ScrollView::setHasVerticalScrollbar(bool hasBar)
     94 {
     95     if (hasBar && !m_verticalScrollbar) {
     96         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
     97         addChild(m_verticalScrollbar.get());
     98         m_verticalScrollbar->styleChanged();
     99     } else if (!hasBar && m_verticalScrollbar) {
    100         removeChild(m_verticalScrollbar.get());
    101         m_verticalScrollbar = 0;
    102     }
    103 }
    104 
    105 #if !PLATFORM(GTK)
    106 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
    107 {
    108     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
    109 }
    110 
    111 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
    112 {
    113     if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode())
    114         return;
    115     m_horizontalScrollbarMode = horizontalMode;
    116     m_verticalScrollbarMode = verticalMode;
    117     if (platformWidget())
    118         platformSetScrollbarModes();
    119     else
    120         updateScrollbars(scrollOffset());
    121 }
    122 #endif
    123 
    124 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
    125 {
    126     if (platformWidget()) {
    127         platformScrollbarModes(horizontalMode, verticalMode);
    128         return;
    129     }
    130     horizontalMode = m_horizontalScrollbarMode;
    131     verticalMode = m_verticalScrollbarMode;
    132 }
    133 
    134 void ScrollView::setCanHaveScrollbars(bool canScroll)
    135 {
    136     ScrollbarMode newHorizontalMode;
    137     ScrollbarMode newVerticalMode;
    138 
    139     scrollbarModes(newHorizontalMode, newVerticalMode);
    140 
    141     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
    142         newVerticalMode = ScrollbarAuto;
    143     else if (!canScroll)
    144         newVerticalMode = ScrollbarAlwaysOff;
    145 
    146     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
    147         newHorizontalMode = ScrollbarAuto;
    148     else if (!canScroll)
    149         newHorizontalMode = ScrollbarAlwaysOff;
    150 
    151     setScrollbarModes(newHorizontalMode, newVerticalMode);
    152 }
    153 
    154 void ScrollView::setCanBlitOnScroll(bool b)
    155 {
    156     if (platformWidget()) {
    157         platformSetCanBlitOnScroll(b);
    158         return;
    159     }
    160 
    161     m_canBlitOnScroll = b;
    162 }
    163 
    164 bool ScrollView::canBlitOnScroll() const
    165 {
    166     if (platformWidget())
    167         return platformCanBlitOnScroll();
    168 
    169     return m_canBlitOnScroll;
    170 }
    171 
    172 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
    173 {
    174     m_paintsEntireContents = paintsEntireContents;
    175 }
    176 
    177 #if !PLATFORM(GTK)
    178 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
    179 {
    180     if (platformWidget())
    181         return platformVisibleContentRect(includeScrollbars);
    182     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
    183                    IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)),
    184                            max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
    185 }
    186 #endif
    187 
    188 int ScrollView::layoutWidth() const
    189 {
    190     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
    191 }
    192 
    193 int ScrollView::layoutHeight() const
    194 {
    195     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
    196 }
    197 
    198 IntSize ScrollView::fixedLayoutSize() const
    199 {
    200     return m_fixedLayoutSize;
    201 }
    202 
    203 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
    204 {
    205     if (fixedLayoutSize() == newSize)
    206         return;
    207     m_fixedLayoutSize = newSize;
    208     updateScrollbars(scrollOffset());
    209 }
    210 
    211 bool ScrollView::useFixedLayout() const
    212 {
    213     return m_useFixedLayout;
    214 }
    215 
    216 void ScrollView::setUseFixedLayout(bool enable)
    217 {
    218     if (useFixedLayout() == enable)
    219         return;
    220     m_useFixedLayout = enable;
    221     updateScrollbars(scrollOffset());
    222 }
    223 
    224 IntSize ScrollView::contentsSize() const
    225 {
    226     if (platformWidget())
    227         return platformContentsSize();
    228     return m_contentsSize;
    229 }
    230 
    231 void ScrollView::setContentsSize(const IntSize& newSize)
    232 {
    233     if (contentsSize() == newSize)
    234         return;
    235     m_contentsSize = newSize;
    236     if (platformWidget())
    237         platformSetContentsSize();
    238     else
    239         updateScrollbars(scrollOffset());
    240 }
    241 
    242 IntPoint ScrollView::maximumScrollPosition() const
    243 {
    244     IntSize maximumOffset = contentsSize() - visibleContentRect().size();
    245     maximumOffset.clampNegativeToZero();
    246     return IntPoint(maximumOffset.width(), maximumOffset.height());
    247 }
    248 
    249 void ScrollView::valueChanged(Scrollbar* scrollbar)
    250 {
    251     // Figure out if we really moved.
    252     IntSize newOffset = m_scrollOffset;
    253     if (scrollbar) {
    254         if (scrollbar->orientation() == HorizontalScrollbar)
    255             newOffset.setWidth(scrollbar->value());
    256         else if (scrollbar->orientation() == VerticalScrollbar)
    257             newOffset.setHeight(scrollbar->value());
    258     }
    259 
    260     IntSize scrollDelta = newOffset - m_scrollOffset;
    261     if (scrollDelta == IntSize())
    262         return;
    263     m_scrollOffset = newOffset;
    264 
    265     if (scrollbarsSuppressed())
    266         return;
    267 
    268     scrollContents(scrollDelta);
    269 }
    270 
    271 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
    272 {
    273 #if PLATFORM(ANDROID)
    274     if (platformProhibitsScrolling())
    275         return;
    276 #endif
    277     // FIXME: This method is not transform-aware.  It should just be moved to FrameView so that an accurate
    278     // position for the child view can be determined.
    279     IntRect rect = r;
    280     ScrollView* view = this;
    281     while (view) {
    282         if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling.
    283             return;
    284         view->setScrollPosition(rect.location());
    285         rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
    286         if (view->parent())
    287             rect.intersect(view->frameRect());
    288         view = view->parent();
    289     }
    290 
    291     // We may be embedded inside some containing platform scroll view that we don't manage.  This is the case
    292     // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView
    293     // that contains both it and message headers.  Let the HostWindow know about this scroll so that it can pass the message
    294     // on up the view chain.
    295     // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to
    296     // expose the headers.
    297     if (hostWindow())
    298         hostWindow()->scrollRectIntoView(rect, this);
    299 }
    300 
    301 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
    302 {
    303     if (prohibitsScrolling())
    304         return;
    305 
    306     if (platformWidget()) {
    307         platformSetScrollPosition(scrollPoint);
    308         return;
    309     }
    310 
    311     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
    312     newScrollPosition.clampNegativeToZero();
    313 
    314     if (newScrollPosition == scrollPosition())
    315         return;
    316 
    317     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
    318 }
    319 
    320 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
    321 {
    322     if (platformWidget())
    323         return platformScroll(direction, granularity);
    324 
    325     if (direction == ScrollUp || direction == ScrollDown) {
    326         if (m_verticalScrollbar)
    327             return m_verticalScrollbar->scroll(direction, granularity);
    328     } else {
    329         if (m_horizontalScrollbar)
    330             return m_horizontalScrollbar->scroll(direction, granularity);
    331     }
    332     return false;
    333 }
    334 
    335 static const unsigned cMaxUpdateScrollbarsPass = 2;
    336 
    337 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
    338 {
    339     if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
    340         return;
    341 
    342     // If we came in here with the view already needing a layout, then go ahead and do that
    343     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
    344     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
    345     if (!m_scrollbarsSuppressed) {
    346         m_inUpdateScrollbars = true;
    347         visibleContentsResized();
    348         m_inUpdateScrollbars = false;
    349     }
    350 
    351     bool hasHorizontalScrollbar = m_horizontalScrollbar;
    352     bool hasVerticalScrollbar = m_verticalScrollbar;
    353 
    354     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
    355     bool newHasVerticalScrollbar = hasVerticalScrollbar;
    356 
    357     ScrollbarMode hScroll = m_horizontalScrollbarMode;
    358     ScrollbarMode vScroll = m_verticalScrollbarMode;
    359 
    360     if (hScroll != ScrollbarAuto)
    361         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
    362     if (vScroll != ScrollbarAuto)
    363         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
    364 
    365     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
    366         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
    367             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
    368         if (hasVerticalScrollbar != newHasVerticalScrollbar)
    369             setHasVerticalScrollbar(newHasVerticalScrollbar);
    370     } else {
    371         bool sendContentResizedNotification = false;
    372 
    373         IntSize docSize = contentsSize();
    374         IntSize frameSize = frameRect().size();
    375 
    376         if (hScroll == ScrollbarAuto) {
    377             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
    378             if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
    379                 newHasHorizontalScrollbar = false;
    380         }
    381         if (vScroll == ScrollbarAuto) {
    382             newHasVerticalScrollbar = docSize.height() > visibleHeight();
    383             if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
    384                 newHasVerticalScrollbar = false;
    385         }
    386 
    387         // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
    388         // try to both gain/lose a scrollbar in the same pass.
    389         if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
    390             newHasVerticalScrollbar = false;
    391         if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
    392             newHasHorizontalScrollbar = false;
    393 
    394         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
    395             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
    396             sendContentResizedNotification = true;
    397         }
    398 
    399         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
    400             setHasVerticalScrollbar(newHasVerticalScrollbar);
    401             sendContentResizedNotification = true;
    402         }
    403 
    404         if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
    405             m_updateScrollbarsPass++;
    406             contentsResized();
    407             visibleContentsResized();
    408             IntSize newDocSize = contentsSize();
    409             if (newDocSize == docSize) {
    410                 // The layout with the new scroll state had no impact on
    411                 // the document's overall size, so updateScrollbars didn't get called.
    412                 // Recur manually.
    413                 updateScrollbars(desiredOffset);
    414             }
    415             m_updateScrollbarsPass--;
    416         }
    417     }
    418 
    419     // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
    420     // doing it multiple times).
    421     if (m_updateScrollbarsPass)
    422         return;
    423 
    424     m_inUpdateScrollbars = true;
    425     IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
    426     IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
    427     scroll.clampNegativeToZero();
    428 
    429     if (m_horizontalScrollbar) {
    430         int clientWidth = visibleWidth();
    431         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
    432         int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
    433         IntRect oldRect(m_horizontalScrollbar->frameRect());
    434         IntRect hBarRect = IntRect(0,
    435                                    height() - m_horizontalScrollbar->height(),
    436                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
    437                                    m_horizontalScrollbar->height());
    438         m_horizontalScrollbar->setFrameRect(hBarRect);
    439         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
    440             m_horizontalScrollbar->invalidate();
    441 
    442         if (m_scrollbarsSuppressed)
    443             m_horizontalScrollbar->setSuppressInvalidation(true);
    444         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
    445         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
    446         m_horizontalScrollbar->setValue(scroll.width());
    447         if (m_scrollbarsSuppressed)
    448             m_horizontalScrollbar->setSuppressInvalidation(false);
    449     }
    450 
    451     if (m_verticalScrollbar) {
    452         int clientHeight = visibleHeight();
    453         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
    454         int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
    455         if (pageStep < 0)
    456             pageStep = clientHeight;
    457         IntRect oldRect(m_verticalScrollbar->frameRect());
    458         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(),
    459                                    0,
    460                                    m_verticalScrollbar->width(),
    461                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
    462         m_verticalScrollbar->setFrameRect(vBarRect);
    463         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
    464             m_verticalScrollbar->invalidate();
    465 
    466         if (m_scrollbarsSuppressed)
    467             m_verticalScrollbar->setSuppressInvalidation(true);
    468         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
    469         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
    470         m_verticalScrollbar->setValue(scroll.height());
    471         if (m_scrollbarsSuppressed)
    472             m_verticalScrollbar->setSuppressInvalidation(false);
    473     }
    474 
    475     if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
    476         frameRectsChanged();
    477         updateScrollCorner();
    478     }
    479 
    480     // See if our offset has changed in a situation where we might not have scrollbars.
    481     // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
    482     // It can also happen when maximizing a window that has scrollbars (but the new maximized result
    483     // does not).
    484     IntSize scrollDelta = scroll - m_scrollOffset;
    485     if (scrollDelta != IntSize()) {
    486        m_scrollOffset = scroll;
    487        scrollContents(scrollDelta);
    488     }
    489 
    490     m_inUpdateScrollbars = false;
    491 }
    492 
    493 const int panIconSizeLength = 16;
    494 
    495 void ScrollView::scrollContents(const IntSize& scrollDelta)
    496 {
    497     if (!hostWindow())
    498         return;
    499 
    500     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
    501     // with the clip rect every time to keep it smooth.
    502     IntRect clipRect = windowClipRect();
    503     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
    504     IntRect updateRect = clipRect;
    505     updateRect.intersect(scrollViewRect);
    506 
    507     // Invalidate the window (not the backing store).
    508     hostWindow()->repaint(updateRect, false);
    509 
    510     if (m_drawPanScrollIcon) {
    511         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
    512         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
    513         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
    514         panScrollIconDirtyRect.intersect(clipRect);
    515         hostWindow()->repaint(panScrollIconDirtyRect, true);
    516     }
    517 
    518     if (canBlitOnScroll()) { // The main frame can just blit the WebView window
    519        // FIXME: Find a way to blit subframes without blitting overlapping content
    520        hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect);
    521     } else {
    522        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
    523        // windowed plugins.
    524        hostWindow()->repaint(updateRect, true, false, true); // Invalidate the backing store and repaint it synchronously
    525     }
    526 
    527     // This call will move children with native widgets (plugins) and invalidate them as well.
    528     frameRectsChanged();
    529 
    530     // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should
    531     // be very fast).
    532     hostWindow()->paint();
    533 }
    534 
    535 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
    536 {
    537     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
    538     return viewPoint + scrollOffset();
    539 }
    540 
    541 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
    542 {
    543     IntPoint viewPoint = contentsPoint - scrollOffset();
    544     return convertToContainingWindow(viewPoint);
    545 }
    546 
    547 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
    548 {
    549     IntRect viewRect = convertFromContainingWindow(windowRect);
    550     viewRect.move(scrollOffset());
    551     return viewRect;
    552 }
    553 
    554 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
    555 {
    556     IntRect viewRect = contentsRect;
    557     viewRect.move(-scrollOffset());
    558     return convertToContainingWindow(viewRect);
    559 }
    560 
    561 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
    562 {
    563     if (platformWidget())
    564         return platformContentsToScreen(rect);
    565     if (!hostWindow())
    566         return IntRect();
    567     return hostWindow()->windowToScreen(contentsToWindow(rect));
    568 }
    569 
    570 IntPoint ScrollView::screenToContents(const IntPoint& point) const
    571 {
    572     if (platformWidget())
    573         return platformScreenToContents(point);
    574     if (!hostWindow())
    575         return IntPoint();
    576     return windowToContents(hostWindow()->screenToWindow(point));
    577 }
    578 
    579 bool ScrollView::containsScrollbarsAvoidingResizer() const
    580 {
    581     return !m_scrollbarsAvoidingResizer;
    582 }
    583 
    584 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
    585 {
    586     int oldCount = m_scrollbarsAvoidingResizer;
    587     m_scrollbarsAvoidingResizer += overlapDelta;
    588     if (parent())
    589         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
    590     else if (!scrollbarsSuppressed()) {
    591         // If we went from n to 0 or from 0 to n and we're the outermost view,
    592         // we need to invalidate the windowResizerRect(), since it will now need to paint
    593         // differently.
    594         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
    595             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
    596             invalidateRect(windowResizerRect());
    597     }
    598 }
    599 
    600 void ScrollView::setParent(ScrollView* parentView)
    601 {
    602     if (parentView == parent())
    603         return;
    604 
    605     if (m_scrollbarsAvoidingResizer && parent())
    606         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
    607 
    608     Widget::setParent(parentView);
    609 
    610     if (m_scrollbarsAvoidingResizer && parent())
    611         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
    612 }
    613 
    614 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
    615 {
    616     if (suppressed == m_scrollbarsSuppressed)
    617         return;
    618 
    619     m_scrollbarsSuppressed = suppressed;
    620 
    621     if (platformWidget())
    622         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
    623     else if (repaintOnUnsuppress && !suppressed) {
    624         if (m_horizontalScrollbar)
    625             m_horizontalScrollbar->invalidate();
    626         if (m_verticalScrollbar)
    627             m_verticalScrollbar->invalidate();
    628 
    629         // Invalidate the scroll corner too on unsuppress.
    630         invalidateRect(scrollCornerRect());
    631     }
    632 }
    633 
    634 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
    635 {
    636     if (platformWidget())
    637         return 0;
    638 
    639     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
    640     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
    641         return m_horizontalScrollbar.get();
    642     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
    643         return m_verticalScrollbar.get();
    644     return 0;
    645 }
    646 
    647 void ScrollView::wheelEvent(PlatformWheelEvent& e)
    648 {
    649     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
    650 #if PLATFORM(WX)
    651     if (!canHaveScrollbars()) {
    652 #else
    653     if (!canHaveScrollbars() || platformWidget()) {
    654 #endif
    655         return;
    656     }
    657 
    658     // Determine how much we want to scroll.  If we can move at all, we will accept the event.
    659     IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
    660     if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
    661         (e.deltaX() > 0 && scrollOffset().width() > 0) ||
    662         (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
    663         (e.deltaY() > 0 && scrollOffset().height() > 0)) {
    664         e.accept();
    665         float deltaX = e.deltaX();
    666         float deltaY = e.deltaY();
    667         if (e.granularity() == ScrollByPageWheelEvent) {
    668             ASSERT(deltaX == 0);
    669             bool negative = deltaY < 0;
    670             deltaY = max(max<int>(visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), visibleHeight() - Scrollbar::maxOverlapBetweenPages()), 1);
    671             if (negative)
    672                 deltaY = -deltaY;
    673         }
    674         scrollBy(IntSize(-deltaX, -deltaY));
    675     }
    676 }
    677 
    678 void ScrollView::setFrameRect(const IntRect& newRect)
    679 {
    680     IntRect oldRect = frameRect();
    681 
    682     if (newRect == oldRect)
    683         return;
    684 
    685     Widget::setFrameRect(newRect);
    686 
    687     if (platformWidget())
    688         return;
    689 
    690     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
    691         updateScrollbars(m_scrollOffset);
    692         contentsResized();
    693     }
    694 
    695     frameRectsChanged();
    696 }
    697 
    698 void ScrollView::frameRectsChanged()
    699 {
    700     if (platformWidget())
    701         return;
    702 
    703     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
    704     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
    705         (*current)->frameRectsChanged();
    706 }
    707 
    708 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
    709 {
    710     IntRect paintRect = rect;
    711     if (!paintsEntireContents())
    712         paintRect.intersect(visibleContentRect());
    713 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
    714     if (rect != paintRect)
    715         platformOffscreenContentRectangle(visibleContentRect(), rect);
    716 #endif
    717     if (paintRect.isEmpty())
    718         return;
    719     if (platformWidget()) {
    720         platformRepaintContentRectangle(paintRect, now);
    721         return;
    722     }
    723 
    724     if (hostWindow())
    725         hostWindow()->repaint(contentsToWindow(paintRect), true, now);
    726 }
    727 
    728 IntRect ScrollView::scrollCornerRect() const
    729 {
    730     IntRect cornerRect;
    731 
    732     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
    733         cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
    734                                  height() - m_horizontalScrollbar->height(),
    735                                  width() - m_horizontalScrollbar->width(),
    736                                  m_horizontalScrollbar->height()));
    737     }
    738 
    739     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
    740         cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
    741                                  m_verticalScrollbar->height(),
    742                                  m_verticalScrollbar->width(),
    743                                  height() - m_verticalScrollbar->height()));
    744     }
    745 
    746     return cornerRect;
    747 }
    748 
    749 void ScrollView::updateScrollCorner()
    750 {
    751 }
    752 
    753 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
    754 {
    755     ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
    756 }
    757 
    758 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
    759 {
    760     if (m_horizontalScrollbar)
    761         m_horizontalScrollbar->paint(context, rect);
    762     if (m_verticalScrollbar)
    763         m_verticalScrollbar->paint(context, rect);
    764 
    765     paintScrollCorner(context, scrollCornerRect());
    766 }
    767 
    768 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
    769 {
    770     DEFINE_STATIC_LOCAL(Image*, panScrollIcon, (Image::loadPlatformResource("panIcon").releaseRef()));
    771     context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint);
    772 }
    773 
    774 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
    775 {
    776     if (platformWidget()) {
    777         Widget::paint(context, rect);
    778         return;
    779     }
    780 
    781     if (context->paintingDisabled() && !context->updatingControlTints())
    782         return;
    783 
    784     IntRect documentDirtyRect = rect;
    785     documentDirtyRect.intersect(frameRect());
    786 
    787     context->save();
    788 
    789     context->translate(x(), y());
    790     documentDirtyRect.move(-x(), -y());
    791 
    792     context->translate(-scrollX(), -scrollY());
    793     documentDirtyRect.move(scrollX(), scrollY());
    794 
    795     context->clip(visibleContentRect());
    796 
    797     paintContents(context, documentDirtyRect);
    798 
    799     context->restore();
    800 
    801     // Now paint the scrollbars.
    802     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
    803         context->save();
    804         IntRect scrollViewDirtyRect = rect;
    805         scrollViewDirtyRect.intersect(frameRect());
    806         context->translate(x(), y());
    807         scrollViewDirtyRect.move(-x(), -y());
    808 
    809         paintScrollbars(context, scrollViewDirtyRect);
    810 
    811         context->restore();
    812     }
    813 
    814     // Paint the panScroll Icon
    815     if (m_drawPanScrollIcon)
    816         paintPanScrollIcon(context);
    817 }
    818 
    819 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
    820 {
    821     if (!scrollbarCornerPresent())
    822         return false;
    823 
    824     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
    825 
    826     if (m_horizontalScrollbar) {
    827         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
    828         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
    829         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
    830 
    831         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
    832     }
    833 
    834     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
    835     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
    836     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
    837 
    838     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
    839 }
    840 
    841 bool ScrollView::scrollbarCornerPresent() const
    842 {
    843     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
    844            (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
    845 }
    846 
    847 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
    848 {
    849     // Scrollbars won't be transformed within us
    850     IntRect newRect = localRect;
    851     newRect.move(scrollbar->x(), scrollbar->y());
    852     return newRect;
    853 }
    854 
    855 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
    856 {
    857     IntRect newRect = parentRect;
    858     // Scrollbars won't be transformed within us
    859     newRect.move(-scrollbar->x(), -scrollbar->y());
    860     return newRect;
    861 }
    862 
    863 // FIXME: test these on windows
    864 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
    865 {
    866     // Scrollbars won't be transformed within us
    867     IntPoint newPoint = localPoint;
    868     newPoint.move(scrollbar->x(), scrollbar->y());
    869     return newPoint;
    870 }
    871 
    872 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
    873 {
    874     IntPoint newPoint = parentPoint;
    875     // Scrollbars won't be transformed within us
    876     newPoint.move(-scrollbar->x(), -scrollbar->y());
    877     return newPoint;
    878 }
    879 
    880 void ScrollView::setParentVisible(bool visible)
    881 {
    882     if (isParentVisible() == visible)
    883         return;
    884 
    885     Widget::setParentVisible(visible);
    886 
    887     if (!isSelfVisible())
    888         return;
    889 
    890     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
    891     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
    892         (*it)->setParentVisible(visible);
    893 }
    894 
    895 void ScrollView::show()
    896 {
    897     if (!isSelfVisible()) {
    898         setSelfVisible(true);
    899         if (isParentVisible()) {
    900             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
    901             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
    902                 (*it)->setParentVisible(true);
    903         }
    904     }
    905 
    906     Widget::show();
    907 }
    908 
    909 void ScrollView::hide()
    910 {
    911     if (isSelfVisible()) {
    912         if (isParentVisible()) {
    913             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
    914             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
    915                 (*it)->setParentVisible(false);
    916         }
    917         setSelfVisible(false);
    918     }
    919 
    920     Widget::hide();
    921 }
    922 
    923 bool ScrollView::isOffscreen() const
    924 {
    925     if (platformWidget())
    926         return platformIsOffscreen();
    927 
    928     if (!isVisible())
    929         return true;
    930 
    931     // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
    932     // currently, we can add the method when the other platforms decide to implement this concept.
    933     return false;
    934 }
    935 
    936 
    937 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
    938 {
    939     if (!hostWindow())
    940         return;
    941     m_drawPanScrollIcon = true;
    942     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
    943     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength,panIconSizeLength)), true, true);
    944 }
    945 
    946 void ScrollView::removePanScrollIcon()
    947 {
    948     if (!hostWindow())
    949         return;
    950     m_drawPanScrollIcon = false;
    951     hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true, true);
    952 }
    953 
    954 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT)
    955 
    956 void ScrollView::platformInit()
    957 {
    958 }
    959 
    960 void ScrollView::platformDestroy()
    961 {
    962 }
    963 
    964 #endif
    965 
    966 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
    967 
    968 void ScrollView::platformAddChild(Widget*)
    969 {
    970 }
    971 
    972 void ScrollView::platformRemoveChild(Widget*)
    973 {
    974 }
    975 
    976 #endif
    977 
    978 #if !PLATFORM(MAC)
    979 
    980 void ScrollView::platformSetScrollbarsSuppressed(bool)
    981 {
    982 }
    983 
    984 #endif
    985 
    986 #if !PLATFORM(MAC) && !PLATFORM(WX)
    987 
    988 #if !PLATFORM(ANDROID)
    989 void ScrollView::platformSetScrollbarModes()
    990 {
    991 }
    992 
    993 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
    994 {
    995     horizontal = ScrollbarAuto;
    996     vertical = ScrollbarAuto;
    997 }
    998 #endif
    999 
   1000 void ScrollView::platformSetCanBlitOnScroll(bool)
   1001 {
   1002 }
   1003 
   1004 bool ScrollView::platformCanBlitOnScroll() const
   1005 {
   1006     return false;
   1007 }
   1008 
   1009 #if !PLATFORM(ANDROID)
   1010 IntRect ScrollView::platformVisibleContentRect(bool) const
   1011 {
   1012     return IntRect();
   1013 }
   1014 #endif
   1015 
   1016 #if !PLATFORM(ANDROID)
   1017 IntSize ScrollView::platformContentsSize() const
   1018 {
   1019     return IntSize();
   1020 }
   1021 #endif
   1022 
   1023 void ScrollView::platformSetContentsSize()
   1024 {
   1025 }
   1026 
   1027 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
   1028 {
   1029     return rect;
   1030 }
   1031 
   1032 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
   1033 {
   1034     return point;
   1035 }
   1036 
   1037 #if !PLATFORM(ANDROID)
   1038 void ScrollView::platformSetScrollPosition(const IntPoint&)
   1039 {
   1040 }
   1041 #endif
   1042 
   1043 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
   1044 {
   1045     return true;
   1046 }
   1047 
   1048 #if !PLATFORM(ANDROID)
   1049 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
   1050 {
   1051 }
   1052 
   1053 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
   1054 void ScrollView::platformOffscreenContentRectangle(const IntRect& )
   1055 {
   1056 }
   1057 #endif
   1058 #endif
   1059 
   1060 bool ScrollView::platformIsOffscreen() const
   1061 {
   1062     return false;
   1063 }
   1064 
   1065 #endif
   1066 
   1067 }
   1068 
   1069