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