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 "platform/scroll/ScrollView.h"
28
29#include "platform/graphics/GraphicsContextStateSaver.h"
30#include "platform/graphics/GraphicsLayer.h"
31#include "platform/HostWindow.h"
32#include "platform/scroll/ScrollbarTheme.h"
33#include "wtf/StdLibExtras.h"
34
35using namespace std;
36
37namespace WebCore {
38
39ScrollView::ScrollView()
40    : m_horizontalScrollbarMode(ScrollbarAuto)
41    , m_verticalScrollbarMode(ScrollbarAuto)
42    , m_horizontalScrollbarLock(false)
43    , m_verticalScrollbarLock(false)
44    , m_scrollbarsAvoidingResizer(0)
45    , m_scrollbarsSuppressed(false)
46    , m_inUpdateScrollbars(false)
47    , m_drawPanScrollIcon(false)
48    , m_paintsEntireContents(false)
49    , m_clipsRepaints(true)
50{
51}
52
53ScrollView::~ScrollView()
54{
55}
56
57void ScrollView::addChild(PassRefPtr<Widget> prpChild)
58{
59    Widget* child = prpChild.get();
60    ASSERT(child != this && !child->parent());
61    child->setParent(this);
62    m_children.add(prpChild);
63}
64
65void ScrollView::removeChild(Widget* child)
66{
67    ASSERT(child->parent() == this);
68    child->setParent(0);
69    m_children.remove(child);
70}
71
72void ScrollView::setHasHorizontalScrollbar(bool hasBar)
73{
74    if (hasBar && !m_horizontalScrollbar) {
75        m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
76        addChild(m_horizontalScrollbar.get());
77        didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
78        m_horizontalScrollbar->styleChanged();
79    } else if (!hasBar && m_horizontalScrollbar) {
80        willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
81        removeChild(m_horizontalScrollbar.get());
82        m_horizontalScrollbar = nullptr;
83    }
84}
85
86void ScrollView::setHasVerticalScrollbar(bool hasBar)
87{
88    if (hasBar && !m_verticalScrollbar) {
89        m_verticalScrollbar = createScrollbar(VerticalScrollbar);
90        addChild(m_verticalScrollbar.get());
91        didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
92        m_verticalScrollbar->styleChanged();
93    } else if (!hasBar && m_verticalScrollbar) {
94        willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
95        removeChild(m_verticalScrollbar.get());
96        m_verticalScrollbar = nullptr;
97    }
98}
99
100PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
101{
102    return Scrollbar::create(this, orientation, RegularScrollbar);
103}
104
105void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
106                                   bool horizontalLock, bool verticalLock)
107{
108    bool needsUpdate = false;
109
110    if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
111        m_horizontalScrollbarMode = horizontalMode;
112        needsUpdate = true;
113    }
114
115    if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
116        m_verticalScrollbarMode = verticalMode;
117        needsUpdate = true;
118    }
119
120    if (horizontalLock)
121        setHorizontalScrollbarLock();
122
123    if (verticalLock)
124        setVerticalScrollbarLock();
125
126    if (!needsUpdate)
127        return;
128
129    updateScrollbars(scrollOffset());
130
131    if (!layerForScrolling())
132        return;
133    blink::WebLayer* layer = layerForScrolling()->platformLayer();
134    if (!layer)
135        return;
136    layer->setUserScrollable(userInputScrollable(HorizontalScrollbar), userInputScrollable(VerticalScrollbar));
137}
138
139void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
140{
141    horizontalMode = m_horizontalScrollbarMode;
142    verticalMode = m_verticalScrollbarMode;
143}
144
145void ScrollView::setCanHaveScrollbars(bool canScroll)
146{
147    ScrollbarMode newHorizontalMode;
148    ScrollbarMode newVerticalMode;
149
150    scrollbarModes(newHorizontalMode, newVerticalMode);
151
152    if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
153        newVerticalMode = ScrollbarAuto;
154    else if (!canScroll)
155        newVerticalMode = ScrollbarAlwaysOff;
156
157    if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
158        newHorizontalMode = ScrollbarAuto;
159    else if (!canScroll)
160        newHorizontalMode = ScrollbarAlwaysOff;
161
162    setScrollbarModes(newHorizontalMode, newVerticalMode);
163}
164
165bool ScrollView::shouldAttemptToScrollUsingFastPath() const
166{
167    return true;
168}
169
170void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
171{
172    m_paintsEntireContents = paintsEntireContents;
173}
174
175void ScrollView::setClipsRepaints(bool clipsRepaints)
176{
177    m_clipsRepaints = clipsRepaints;
178}
179
180IntSize ScrollView::unscaledVisibleContentSize(IncludeScrollbarsInRect scrollbarInclusion) const
181{
182    return scrollbarInclusion == ExcludeScrollbars ? excludeScrollbars(frameRect().size()) : frameRect().size();
183}
184
185IntSize ScrollView::excludeScrollbars(const IntSize& size) const
186{
187    int verticalScrollbarWidth = 0;
188    int horizontalScrollbarHeight = 0;
189
190    if (Scrollbar* verticalBar = verticalScrollbar())
191        verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
192    if (Scrollbar* horizontalBar = horizontalScrollbar())
193        horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
194
195    return IntSize(max(0, size.width() - verticalScrollbarWidth),
196        max(0, size.height() - horizontalScrollbarHeight));
197
198}
199
200IntRect ScrollView::visibleContentRect(IncludeScrollbarsInRect scollbarInclusion) const
201{
202    FloatSize visibleContentSize = unscaledVisibleContentSize(scollbarInclusion);
203    visibleContentSize.scale(1 / visibleContentScaleFactor());
204    return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize));
205}
206
207IntSize ScrollView::contentsSize() const
208{
209    return m_contentsSize;
210}
211
212void ScrollView::setContentsSize(const IntSize& newSize)
213{
214    if (contentsSize() == newSize)
215        return;
216    m_contentsSize = newSize;
217    updateScrollbars(scrollOffset());
218    updateOverhangAreas();
219}
220
221IntPoint ScrollView::maximumScrollPosition() const
222{
223    IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), contentsHeight() - visibleHeight() - scrollOrigin().y());
224    maximumOffset.clampNegativeToZero();
225    return maximumOffset;
226}
227
228IntPoint ScrollView::minimumScrollPosition() const
229{
230    return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
231}
232
233IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
234{
235    if (!constrainsScrollingToContentEdge())
236        return scrollPoint;
237
238    IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
239    newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
240    return newScrollPosition;
241}
242
243int ScrollView::scrollSize(ScrollbarOrientation orientation) const
244{
245    Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
246
247    // If no scrollbars are present, the content may still be scrollable.
248    if (!scrollbar) {
249        IntSize scrollSize = m_contentsSize - visibleContentRect().size();
250        scrollSize.clampNegativeToZero();
251        return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height();
252    }
253
254    return scrollbar->totalSize() - scrollbar->visibleSize();
255}
256
257void ScrollView::notifyPageThatContentAreaWillPaint() const
258{
259}
260
261void ScrollView::setScrollOffset(const IntPoint& offset)
262{
263    scrollTo(toIntSize(adjustScrollPositionWithinRange(offset)));
264}
265
266void ScrollView::scrollTo(const IntSize& newOffset)
267{
268    IntSize scrollDelta = newOffset - m_scrollOffset;
269    if (scrollDelta == IntSize())
270        return;
271    m_scrollOffset = newOffset;
272
273    if (scrollbarsSuppressed())
274        return;
275
276    if (isFrameView())
277        m_pendingScrollDelta += scrollDelta;
278    else
279        scrollContents(scrollDelta);
280}
281
282void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
283{
284    IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
285
286    if (newScrollPosition == scrollPosition())
287        return;
288
289    updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
290}
291
292bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
293{
294    ScrollDirection physicalDirection =
295        toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument());
296
297    return ScrollableArea::scroll(physicalDirection, granularity);
298}
299
300IntSize ScrollView::overhangAmount() const
301{
302    IntSize stretch;
303
304    IntPoint currentScrollPosition = scrollPosition();
305    IntPoint minScrollPosition = minimumScrollPosition();
306    IntPoint maxScrollPosition = maximumScrollPosition();
307
308    if (currentScrollPosition.x() < minScrollPosition.x())
309        stretch.setWidth(currentScrollPosition.x() - minScrollPosition.x());
310    if (currentScrollPosition.x() > maxScrollPosition.x())
311        stretch.setWidth(currentScrollPosition.x() - maxScrollPosition.x());
312
313    if (currentScrollPosition.y() < minScrollPosition.y())
314        stretch.setHeight(currentScrollPosition.y() - minScrollPosition.y());
315    if (currentScrollPosition.y() > maxScrollPosition.y())
316        stretch.setHeight(currentScrollPosition.y() - maxScrollPosition.y());
317
318    return stretch;
319}
320
321void ScrollView::windowResizerRectChanged()
322{
323    updateScrollbars(scrollOffset());
324}
325
326static bool useOverlayScrollbars()
327{
328    // FIXME: Need to detect the presence of CSS custom scrollbars, which are non-overlay regardless the ScrollbarTheme.
329    return ScrollbarTheme::theme()->usesOverlayScrollbars();
330}
331
332void ScrollView::computeScrollbarExistence(bool& newHasHorizontalScrollbar, bool& newHasVerticalScrollbar, ComputeScrollbarExistenceOption option) const
333{
334    bool hasHorizontalScrollbar = m_horizontalScrollbar;
335    bool hasVerticalScrollbar = m_verticalScrollbar;
336
337    newHasHorizontalScrollbar = hasHorizontalScrollbar;
338    newHasVerticalScrollbar = hasVerticalScrollbar;
339
340    ScrollbarMode hScroll = m_horizontalScrollbarMode;
341    ScrollbarMode vScroll = m_verticalScrollbarMode;
342
343    if (hScroll != ScrollbarAuto)
344        newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
345    if (vScroll != ScrollbarAuto)
346        newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
347
348    if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto))
349        return;
350
351    IntSize docSize = contentsSize();
352
353    if (hScroll == ScrollbarAuto)
354        newHasHorizontalScrollbar = docSize.width() > visibleWidth();
355    if (vScroll == ScrollbarAuto)
356        newHasVerticalScrollbar = docSize.height() > visibleHeight();
357
358    if (useOverlayScrollbars())
359        return;
360
361    IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size();
362
363    bool attemptToRemoveScrollbars = (option == FirstPass
364        && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height());
365    if (attemptToRemoveScrollbars) {
366        if (hScroll == ScrollbarAuto)
367            newHasHorizontalScrollbar = false;
368        if (vScroll == ScrollbarAuto)
369            newHasVerticalScrollbar = false;
370    }
371
372    // If we ever turn one scrollbar off, always turn the other one off too.
373    // Never ever try to both gain/lose a scrollbar in the same pass.
374    if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
375        newHasVerticalScrollbar = false;
376    if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
377        newHasHorizontalScrollbar = false;
378}
379
380void ScrollView::updateScrollbarGeometry()
381{
382    if (m_horizontalScrollbar) {
383        int clientWidth = visibleWidth();
384        IntRect oldRect(m_horizontalScrollbar->frameRect());
385        IntRect hBarRect((shouldPlaceVerticalScrollbarOnLeft() && m_verticalScrollbar) ? m_verticalScrollbar->width() : 0,
386                        height() - m_horizontalScrollbar->height(),
387                        width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
388                        m_horizontalScrollbar->height());
389        m_horizontalScrollbar->setFrameRect(hBarRect);
390        if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
391            m_horizontalScrollbar->invalidate();
392
393        if (m_scrollbarsSuppressed)
394            m_horizontalScrollbar->setSuppressInvalidation(true);
395        m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
396        m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
397        m_horizontalScrollbar->offsetDidChange();
398        if (m_scrollbarsSuppressed)
399            m_horizontalScrollbar->setSuppressInvalidation(false);
400    }
401
402    if (m_verticalScrollbar) {
403        int clientHeight = visibleHeight();
404        IntRect oldRect(m_verticalScrollbar->frameRect());
405        IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
406                         0,
407                         m_verticalScrollbar->width(),
408                         height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
409        m_verticalScrollbar->setFrameRect(vBarRect);
410        if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
411            m_verticalScrollbar->invalidate();
412
413        if (m_scrollbarsSuppressed)
414            m_verticalScrollbar->setSuppressInvalidation(true);
415        m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
416        m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
417        m_verticalScrollbar->offsetDidChange();
418        if (m_scrollbarsSuppressed)
419            m_verticalScrollbar->setSuppressInvalidation(false);
420    }
421}
422
423bool ScrollView::adjustScrollbarExistence(ComputeScrollbarExistenceOption option)
424{
425    ASSERT(m_inUpdateScrollbars);
426
427    // If we came in here with the view already needing a layout, then go ahead and do that
428    // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
429    // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
430    if (!m_scrollbarsSuppressed)
431        scrollbarExistenceDidChange();
432
433    bool hasHorizontalScrollbar = m_horizontalScrollbar;
434    bool hasVerticalScrollbar = m_verticalScrollbar;
435
436    bool newHasHorizontalScrollbar = false;
437    bool newHasVerticalScrollbar = false;
438    computeScrollbarExistence(newHasHorizontalScrollbar, newHasVerticalScrollbar, option);
439
440    bool scrollbarExistenceChanged = hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar;
441    if (!scrollbarExistenceChanged)
442        return false;
443
444    setHasHorizontalScrollbar(newHasHorizontalScrollbar);
445    setHasVerticalScrollbar(newHasVerticalScrollbar);
446
447    if (m_scrollbarsSuppressed)
448        return true;
449
450    if (!useOverlayScrollbars())
451        contentsResized();
452    scrollbarExistenceDidChange();
453    return true;
454}
455
456void ScrollView::updateScrollbars(const IntSize& desiredOffset)
457{
458    if (m_inUpdateScrollbars)
459        return;
460    InUpdateScrollbarsScope inUpdateScrollbarsScope(this);
461
462    IntSize oldVisibleSize = visibleSize();
463
464    bool scrollbarExistenceChanged = false;
465    int maxUpdateScrollbarsPass = useOverlayScrollbars() || m_scrollbarsSuppressed ? 1 : 3;
466    for (int updateScrollbarsPass = 0; updateScrollbarsPass < maxUpdateScrollbarsPass; updateScrollbarsPass++) {
467        if (!adjustScrollbarExistence(updateScrollbarsPass ? Incremental : FirstPass))
468            break;
469        scrollbarExistenceChanged = true;
470    }
471
472    updateScrollbarGeometry();
473
474    if (scrollbarExistenceChanged) {
475        // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
476        frameRectsChanged();
477        positionScrollbarLayers();
478        updateScrollCorner();
479    }
480
481    // FIXME: We don't need to do this if we are composited.
482    IntSize newVisibleSize = visibleSize();
483    if (newVisibleSize.width() > oldVisibleSize.width()) {
484        if (shouldPlaceVerticalScrollbarOnLeft())
485            invalidateRect(IntRect(0, 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height()));
486        else
487            invalidateRect(IntRect(oldVisibleSize.width(), 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height()));
488    }
489    if (newVisibleSize.height() > oldVisibleSize.height())
490        invalidateRect(IntRect(0, oldVisibleSize.height(), newVisibleSize.width(), newVisibleSize.height() - oldVisibleSize.height()));
491
492    IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
493    if (!isRubberBandInProgress())
494        adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
495    if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
496        ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition);
497        resetScrollOriginChanged();
498    }
499}
500
501const int panIconSizeLength = 16;
502
503IntRect ScrollView::rectToCopyOnScroll() const
504{
505    IntRect scrollViewRect = convertToRootView(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight()));
506    if (hasOverlayScrollbars()) {
507        int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
508        int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
509
510        scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
511        scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
512    }
513    return scrollViewRect;
514}
515
516void ScrollView::scrollContentsIfNeeded()
517{
518    if (m_pendingScrollDelta.isZero())
519        return;
520    IntSize scrollDelta = m_pendingScrollDelta;
521    m_pendingScrollDelta = IntSize();
522    scrollContents(scrollDelta);
523}
524
525void ScrollView::scrollContents(const IntSize& scrollDelta)
526{
527    HostWindow* window = hostWindow();
528    if (!window)
529        return;
530
531    // Since scrolling is double buffered, we will be blitting the scroll view's intersection
532    // with the clip rect every time to keep it smooth.
533    IntRect clipRect = windowClipRect();
534    IntRect scrollViewRect = rectToCopyOnScroll();
535    IntRect updateRect = clipRect;
536    updateRect.intersect(scrollViewRect);
537
538    if (m_drawPanScrollIcon) {
539        // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
540        // https://bugs.webkit.org/show_bug.cgi?id=47837
541        int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
542        IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
543        IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
544        panScrollIconDirtyRect.intersect(clipRect);
545        window->invalidateContentsAndRootView(panScrollIconDirtyRect);
546    }
547
548    if (!shouldAttemptToScrollUsingFastPath() || !scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
549        scrollContentsSlowPath(updateRect);
550
551    // Invalidate the overhang areas if they are visible.
552    updateOverhangAreas();
553
554    // This call will move children with native widgets (plugins) and invalidate them as well.
555    frameRectsChanged();
556}
557
558bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
559{
560    hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
561    return true;
562}
563
564void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
565{
566    hostWindow()->invalidateContentsForSlowScroll(updateRect);
567}
568
569IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
570{
571    IntPoint viewPoint = convertFromRootView(rootViewPoint);
572    return viewPoint + scrollOffset();
573}
574
575IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
576{
577    IntPoint viewPoint = contentsPoint - scrollOffset();
578    return convertToRootView(viewPoint);
579}
580
581IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
582{
583    IntRect viewRect = convertFromRootView(rootViewRect);
584    viewRect.move(scrollOffset());
585    return viewRect;
586}
587
588IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
589{
590    IntRect viewRect = contentsRect;
591    viewRect.move(-scrollOffset());
592    return convertToRootView(viewRect);
593}
594
595IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
596{
597    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
598    return viewPoint + scrollOffset();
599}
600
601FloatPoint ScrollView::windowToContents(const FloatPoint& windowPoint) const
602{
603    FloatPoint viewPoint = convertFromContainingWindow(windowPoint);
604    return viewPoint + scrollOffset();
605}
606
607IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
608{
609    IntPoint viewPoint = contentsPoint - scrollOffset();
610    return convertToContainingWindow(viewPoint);
611}
612
613IntRect ScrollView::windowToContents(const IntRect& windowRect) const
614{
615    IntRect viewRect = convertFromContainingWindow(windowRect);
616    viewRect.move(scrollOffset());
617    return viewRect;
618}
619
620IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
621{
622    IntRect viewRect = contentsRect;
623    viewRect.move(-scrollOffset());
624    return convertToContainingWindow(viewRect);
625}
626
627IntRect ScrollView::contentsToScreen(const IntRect& rect) const
628{
629    HostWindow* window = hostWindow();
630    if (!window)
631        return IntRect();
632    return window->rootViewToScreen(contentsToRootView(rect));
633}
634
635bool ScrollView::containsScrollbarsAvoidingResizer() const
636{
637    return !m_scrollbarsAvoidingResizer;
638}
639
640void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
641{
642    int oldCount = m_scrollbarsAvoidingResizer;
643    m_scrollbarsAvoidingResizer += overlapDelta;
644    if (parent())
645        toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(overlapDelta);
646    else if (!scrollbarsSuppressed()) {
647        // If we went from n to 0 or from 0 to n and we're the outermost view,
648        // we need to invalidate the windowResizerRect(), since it will now need to paint
649        // differently.
650        if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
651            (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
652            invalidateRect(windowResizerRect());
653    }
654}
655
656void ScrollView::setParent(Widget* parentView)
657{
658    if (parentView == parent())
659        return;
660
661    if (m_scrollbarsAvoidingResizer && parent())
662        toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
663
664    Widget::setParent(parentView);
665
666    if (m_scrollbarsAvoidingResizer && parent())
667        toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
668}
669
670void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
671{
672    if (suppressed == m_scrollbarsSuppressed)
673        return;
674
675    m_scrollbarsSuppressed = suppressed;
676
677    if (repaintOnUnsuppress && !suppressed) {
678        if (m_horizontalScrollbar)
679            m_horizontalScrollbar->invalidate();
680        if (m_verticalScrollbar)
681            m_verticalScrollbar->invalidate();
682
683        // Invalidate the scroll corner too on unsuppress.
684        invalidateRect(scrollCornerRect());
685    }
686}
687
688Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
689{
690    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
691    if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
692        return m_horizontalScrollbar.get();
693    if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
694        return m_verticalScrollbar.get();
695    return 0;
696}
697
698void ScrollView::setFrameRect(const IntRect& newRect)
699{
700    IntRect oldRect = frameRect();
701
702    if (newRect == oldRect)
703        return;
704
705    Widget::setFrameRect(newRect);
706
707    updateScrollbars(scrollOffset());
708
709    frameRectsChanged();
710}
711
712void ScrollView::frameRectsChanged()
713{
714    HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
715    for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
716        (*current)->frameRectsChanged();
717}
718
719static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
720{
721    if (!graphicsLayer || !scrollbar)
722        return;
723
724    IntRect scrollbarRect = scrollbar->frameRect();
725    graphicsLayer->setPosition(scrollbarRect.location());
726
727    if (scrollbarRect.size() == graphicsLayer->size())
728        return;
729
730    graphicsLayer->setSize(scrollbarRect.size());
731
732    if (graphicsLayer->hasContentsLayer()) {
733        graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
734        return;
735    }
736
737    graphicsLayer->setDrawsContent(true);
738    graphicsLayer->setNeedsDisplay();
739}
740
741static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
742{
743    if (!graphicsLayer)
744        return;
745    graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
746    graphicsLayer->setPosition(cornerRect.location());
747    if (cornerRect.size() != graphicsLayer->size())
748        graphicsLayer->setNeedsDisplay();
749    graphicsLayer->setSize(cornerRect.size());
750}
751
752void ScrollView::positionScrollbarLayers()
753{
754    positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
755    positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
756    positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
757}
758
759bool ScrollView::userInputScrollable(ScrollbarOrientation orientation) const
760{
761    ScrollbarMode mode = (orientation == HorizontalScrollbar) ?
762        m_horizontalScrollbarMode : m_verticalScrollbarMode;
763
764    return mode == ScrollbarAuto || mode == ScrollbarAlwaysOn;
765}
766
767bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const
768{
769    return false;
770}
771
772void ScrollView::contentRectangleForPaintInvalidation(const IntRect& rect)
773{
774    IntRect paintRect = rect;
775    if (clipsPaintInvalidations() && !paintsEntireContents())
776        paintRect.intersect(visibleContentRect());
777    if (paintRect.isEmpty())
778        return;
779
780    if (HostWindow* window = hostWindow())
781        window->invalidateContentsAndRootView(contentsToWindow(paintRect));
782}
783
784IntRect ScrollView::scrollCornerRect() const
785{
786    IntRect cornerRect;
787
788    if (hasOverlayScrollbars())
789        return cornerRect;
790
791    if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
792        cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
793                                 height() - m_horizontalScrollbar->height(),
794                                 width() - m_horizontalScrollbar->width(),
795                                 m_horizontalScrollbar->height()));
796    }
797
798    if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
799        cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
800                                 m_verticalScrollbar->height(),
801                                 m_verticalScrollbar->width(),
802                                 height() - m_verticalScrollbar->height()));
803    }
804
805    return cornerRect;
806}
807
808bool ScrollView::isScrollCornerVisible() const
809{
810    return !scrollCornerRect().isEmpty();
811}
812
813void ScrollView::scrollbarStyleChanged()
814{
815    contentsResized();
816    updateScrollbars(scrollOffset());
817    positionScrollbarLayers();
818}
819
820void ScrollView::updateScrollCorner()
821{
822}
823
824void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
825{
826    ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect);
827}
828
829void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
830{
831    bar->paint(context, rect);
832}
833
834void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
835{
836    invalidateRect(rect);
837}
838
839void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
840{
841    if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
842        paintScrollbar(context, m_horizontalScrollbar.get(), rect);
843    if (m_verticalScrollbar && !layerForVerticalScrollbar())
844        paintScrollbar(context, m_verticalScrollbar.get(), rect);
845
846    if (layerForScrollCorner())
847        return;
848    paintScrollCorner(context, scrollCornerRect());
849}
850
851void ScrollView::paintPanScrollIcon(GraphicsContext* context)
852{
853    DEFINE_STATIC_REF(Image, panScrollIcon, (Image::loadPlatformResource("panIcon")));
854    IntPoint iconGCPoint = m_panScrollIconPoint;
855    if (parent())
856        iconGCPoint = toScrollView(parent())->windowToContents(iconGCPoint);
857    context->drawImage(panScrollIcon, iconGCPoint);
858}
859
860void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
861{
862    if (context->paintingDisabled() && !context->updatingControlTints())
863        return;
864
865    notifyPageThatContentAreaWillPaint();
866
867    IntRect documentDirtyRect = rect;
868    if (!paintsEntireContents()) {
869        IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect().size());
870        documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
871    }
872
873    if (!documentDirtyRect.isEmpty()) {
874        GraphicsContextStateSaver stateSaver(*context);
875
876        context->translate(x(), y());
877        documentDirtyRect.moveBy(-location());
878
879        if (!paintsEntireContents()) {
880            context->translate(-scrollX(), -scrollY());
881            documentDirtyRect.moveBy(scrollPosition());
882
883            context->clip(visibleContentRect());
884        }
885
886        paintContents(context, documentDirtyRect);
887    }
888
889    calculateAndPaintOverhangAreas(context, rect);
890
891    // Now paint the scrollbars.
892    if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
893        GraphicsContextStateSaver stateSaver(*context);
894        IntRect scrollViewDirtyRect = rect;
895        IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size());
896        scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
897        context->translate(x(), y());
898        scrollViewDirtyRect.moveBy(-location());
899        context->clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
900
901        paintScrollbars(context, scrollViewDirtyRect);
902    }
903
904    // Paint the panScroll Icon
905    if (m_drawPanScrollIcon)
906        paintPanScrollIcon(context);
907}
908
909void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
910{
911    int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
912        ? verticalScrollbar()->width() : 0;
913    int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
914        ? horizontalScrollbar()->height() : 0;
915
916    int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
917    if (physicalScrollY < 0) {
918        horizontalOverhangRect = frameRect();
919        horizontalOverhangRect.setHeight(-physicalScrollY);
920        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
921    } else if (contentsHeight() && physicalScrollY > contentsHeight() - visibleHeight()) {
922        int height = physicalScrollY - (contentsHeight() - visibleHeight());
923        horizontalOverhangRect = frameRect();
924        horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
925        horizontalOverhangRect.setHeight(height);
926        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
927    }
928
929    int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
930    if (physicalScrollX < 0) {
931        verticalOverhangRect.setWidth(-physicalScrollX);
932        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
933        verticalOverhangRect.setX(frameRect().x());
934        if (horizontalOverhangRect.y() == frameRect().y())
935            verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
936        else
937            verticalOverhangRect.setY(frameRect().y());
938    } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
939        int width = physicalScrollX - (contentsWidth() - visibleWidth());
940        verticalOverhangRect.setWidth(width);
941        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
942        verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
943        if (horizontalOverhangRect.y() == frameRect().y())
944            verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
945        else
946            verticalOverhangRect.setY(frameRect().y());
947    }
948}
949
950void ScrollView::updateOverhangAreas()
951{
952    HostWindow* window = hostWindow();
953    if (!window)
954        return;
955
956    IntRect horizontalOverhangRect;
957    IntRect verticalOverhangRect;
958    calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
959    if (!horizontalOverhangRect.isEmpty())
960        window->invalidateContentsAndRootView(horizontalOverhangRect);
961    if (!verticalOverhangRect.isEmpty())
962        window->invalidateContentsAndRootView(verticalOverhangRect);
963}
964
965void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
966{
967    ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
968    ScrollbarTheme::theme()->paintOverhangShadows(context, scrollOffset(), horizontalOverhangRect, verticalOverhangRect, dirtyRect);
969}
970
971void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
972{
973    IntRect horizontalOverhangRect;
974    IntRect verticalOverhangRect;
975    calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
976
977    if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
978        paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
979}
980
981void ScrollView::calculateAndPaintOverhangBackground(GraphicsContext* context, const IntRect& dirtyRect)
982{
983    IntRect horizontalOverhangRect;
984    IntRect verticalOverhangRect;
985    calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
986
987    if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
988        ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
989}
990
991bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
992{
993    if (!scrollbarCornerPresent())
994        return false;
995
996    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
997
998    if (m_horizontalScrollbar) {
999        int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
1000        int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
1001        int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
1002
1003        return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1004    }
1005
1006    int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1007    int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1008    int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1009
1010    return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1011}
1012
1013bool ScrollView::scrollbarCornerPresent() const
1014{
1015    return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1016        || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1017}
1018
1019IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1020{
1021    // Scrollbars won't be transformed within us
1022    IntRect newRect = localRect;
1023    newRect.moveBy(scrollbar->location());
1024    return newRect;
1025}
1026
1027IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1028{
1029    IntRect newRect = parentRect;
1030    // Scrollbars won't be transformed within us
1031    newRect.moveBy(-scrollbar->location());
1032    return newRect;
1033}
1034
1035// FIXME: test these on windows
1036IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1037{
1038    // Scrollbars won't be transformed within us
1039    IntPoint newPoint = localPoint;
1040    newPoint.moveBy(scrollbar->location());
1041    return newPoint;
1042}
1043
1044IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1045{
1046    IntPoint newPoint = parentPoint;
1047    // Scrollbars won't be transformed within us
1048    newPoint.moveBy(-scrollbar->location());
1049    return newPoint;
1050}
1051
1052void ScrollView::setParentVisible(bool visible)
1053{
1054    if (isParentVisible() == visible)
1055        return;
1056
1057    Widget::setParentVisible(visible);
1058
1059    if (!isSelfVisible())
1060        return;
1061
1062    HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1063    for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1064        (*it)->setParentVisible(visible);
1065}
1066
1067void ScrollView::show()
1068{
1069    if (!isSelfVisible()) {
1070        setSelfVisible(true);
1071        if (isParentVisible()) {
1072            HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1073            for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1074                (*it)->setParentVisible(true);
1075        }
1076    }
1077
1078    Widget::show();
1079}
1080
1081void ScrollView::hide()
1082{
1083    if (isSelfVisible()) {
1084        if (isParentVisible()) {
1085            HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1086            for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1087                (*it)->setParentVisible(false);
1088        }
1089        setSelfVisible(false);
1090    }
1091
1092    Widget::hide();
1093}
1094
1095void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1096{
1097    HostWindow* window = hostWindow();
1098    if (!window)
1099        return;
1100    m_drawPanScrollIcon = true;
1101    m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1102    window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1103}
1104
1105void ScrollView::removePanScrollIcon()
1106{
1107    HostWindow* window = hostWindow();
1108    if (!window)
1109        return;
1110    m_drawPanScrollIcon = false;
1111    window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1112}
1113
1114void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1115{
1116    if (scrollOrigin() == origin)
1117        return;
1118
1119    ScrollableArea::setScrollOrigin(origin);
1120
1121    // Update if the scroll origin changes, since our position will be different if the content size did not change.
1122    if (updatePositionAtAll && updatePositionSynchronously)
1123        updateScrollbars(scrollOffset());
1124}
1125
1126} // namespace WebCore
1127