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