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 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 INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ScrollView.h"
28
29#include "GraphicsContext.h"
30#include "GraphicsLayer.h"
31#include "HostWindow.h"
32#include "PlatformMouseEvent.h"
33#include "PlatformWheelEvent.h"
34#include "ScrollAnimator.h"
35#include "Scrollbar.h"
36#include "ScrollbarTheme.h"
37#include <wtf/StdLibExtras.h>
38
39namespace WebCore {
40
41ScrollView::ScrollView()
42    : m_horizontalScrollbarMode(ScrollbarAuto)
43    , m_verticalScrollbarMode(ScrollbarAuto)
44    , m_horizontalScrollbarLock(false)
45    , m_verticalScrollbarLock(false)
46    , m_prohibitsScrolling(false)
47    , m_canBlitOnScroll(true)
48    , m_scrollbarsAvoidingResizer(0)
49    , m_scrollbarsSuppressed(false)
50    , m_inUpdateScrollbars(false)
51    , m_updateScrollbarsPass(0)
52    , m_drawPanScrollIcon(false)
53    , m_useFixedLayout(false)
54    , m_paintsEntireContents(false)
55    , m_clipsRepaints(true)
56    , m_delegatesScrolling(false)
57{
58}
59
60ScrollView::~ScrollView()
61{
62}
63
64void ScrollView::addChild(PassRefPtr<Widget> prpChild)
65{
66    Widget* child = prpChild.get();
67    ASSERT(child != this && !child->parent());
68    child->setParent(this);
69    m_children.add(prpChild);
70    if (child->platformWidget())
71        platformAddChild(child);
72}
73
74void ScrollView::removeChild(Widget* child)
75{
76    ASSERT(child->parent() == this);
77    child->setParent(0);
78    m_children.remove(child);
79    if (child->platformWidget())
80        platformRemoveChild(child);
81}
82
83bool ScrollView::setHasHorizontalScrollbar(bool hasBar, bool* contentSizeAffected)
84{
85    ASSERT(!hasBar || !avoidScrollbarCreation());
86    if (hasBar && !m_horizontalScrollbar) {
87        m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
88        addChild(m_horizontalScrollbar.get());
89        didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
90        m_horizontalScrollbar->styleChanged();
91        if (contentSizeAffected)
92            *contentSizeAffected = !m_horizontalScrollbar->isOverlayScrollbar();
93        return true;
94    }
95
96    if (!hasBar && m_horizontalScrollbar) {
97        bool wasOverlayScrollbar = m_horizontalScrollbar->isOverlayScrollbar();
98        willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
99        removeChild(m_horizontalScrollbar.get());
100        m_horizontalScrollbar = 0;
101        if (contentSizeAffected)
102            *contentSizeAffected = !wasOverlayScrollbar;
103        return true;
104    }
105
106    return false;
107}
108
109bool ScrollView::setHasVerticalScrollbar(bool hasBar, bool* contentSizeAffected)
110{
111    ASSERT(!hasBar || !avoidScrollbarCreation());
112    if (hasBar && !m_verticalScrollbar) {
113        m_verticalScrollbar = createScrollbar(VerticalScrollbar);
114        addChild(m_verticalScrollbar.get());
115        didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
116        m_verticalScrollbar->styleChanged();
117        if (contentSizeAffected)
118            *contentSizeAffected = !m_verticalScrollbar->isOverlayScrollbar();
119        return true;
120    }
121
122    if (!hasBar && m_verticalScrollbar) {
123        bool wasOverlayScrollbar = m_verticalScrollbar->isOverlayScrollbar();
124        willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
125        removeChild(m_verticalScrollbar.get());
126        m_verticalScrollbar = 0;
127        if (contentSizeAffected)
128            *contentSizeAffected = !wasOverlayScrollbar;
129        return true;
130    }
131
132    return false;
133}
134
135PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
136{
137    return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
138}
139
140void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
141                                   bool horizontalLock, bool verticalLock)
142{
143    bool needsUpdate = false;
144
145    if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
146        m_horizontalScrollbarMode = horizontalMode;
147        needsUpdate = true;
148    }
149
150    if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
151        m_verticalScrollbarMode = verticalMode;
152        needsUpdate = true;
153    }
154
155    if (horizontalLock)
156        setHorizontalScrollbarLock();
157
158    if (verticalLock)
159        setVerticalScrollbarLock();
160
161    if (!needsUpdate)
162        return;
163
164    if (platformWidget())
165        platformSetScrollbarModes();
166    else
167        updateScrollbars(scrollOffset());
168}
169
170void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
171{
172    if (platformWidget()) {
173        platformScrollbarModes(horizontalMode, verticalMode);
174        return;
175    }
176    horizontalMode = m_horizontalScrollbarMode;
177    verticalMode = m_verticalScrollbarMode;
178}
179
180void ScrollView::setCanHaveScrollbars(bool canScroll)
181{
182    ScrollbarMode newHorizontalMode;
183    ScrollbarMode newVerticalMode;
184
185    scrollbarModes(newHorizontalMode, newVerticalMode);
186
187    if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
188        newVerticalMode = ScrollbarAuto;
189    else if (!canScroll)
190        newVerticalMode = ScrollbarAlwaysOff;
191
192    if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
193        newHorizontalMode = ScrollbarAuto;
194    else if (!canScroll)
195        newHorizontalMode = ScrollbarAlwaysOff;
196
197    setScrollbarModes(newHorizontalMode, newVerticalMode);
198}
199
200void ScrollView::setCanBlitOnScroll(bool b)
201{
202    if (platformWidget()) {
203        platformSetCanBlitOnScroll(b);
204        return;
205    }
206
207    m_canBlitOnScroll = b;
208}
209
210bool ScrollView::canBlitOnScroll() const
211{
212    if (platformWidget())
213        return platformCanBlitOnScroll();
214
215    return m_canBlitOnScroll;
216}
217
218void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
219{
220    m_paintsEntireContents = paintsEntireContents;
221}
222
223void ScrollView::setClipsRepaints(bool clipsRepaints)
224{
225    m_clipsRepaints = clipsRepaints;
226}
227
228void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
229{
230    if (m_delegatesScrolling == delegatesScrolling)
231        return;
232
233    m_delegatesScrolling = delegatesScrolling;
234    delegatesScrollingDidChange();
235}
236
237IntPoint ScrollView::contentsScrollPosition() const
238{
239#if PLATFORM(IOS)
240    if (platformWidget())
241        return actualScrollPosition();
242#endif
243    return scrollPosition();
244}
245
246void ScrollView::setContentsScrollPosition(const IntPoint& position)
247{
248#if PLATFORM(IOS)
249    if (platformWidget())
250        setActualScrollPosition(position);
251#endif
252    setScrollPosition(position);
253}
254
255#if !PLATFORM(IOS)
256IntRect ScrollView::unobscuredContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
257{
258    return unobscuredContentRectInternal(scrollbarInclusion);
259}
260#endif
261
262IntRect ScrollView::unobscuredContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
263{
264    FloatSize visibleContentSize = unscaledUnobscuredVisibleContentSize(scrollbarInclusion);
265    visibleContentSize.scale(1 / visibleContentScaleFactor());
266    return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize));
267}
268
269IntSize ScrollView::unscaledVisibleContentSizeIncludingObscuredArea(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
270{
271    if (platformWidget())
272        return platformVisibleContentSizeIncludingObscuredArea(scrollbarInclusion == IncludeScrollbars);
273
274#if USE(TILED_BACKING_STORE)
275    if (!m_fixedVisibleContentRect.isEmpty())
276        return m_fixedVisibleContentRect.size();
277#endif
278
279    int verticalScrollbarWidth = 0;
280    int horizontalScrollbarHeight = 0;
281
282    if (scrollbarInclusion == ExcludeScrollbars) {
283        if (Scrollbar* verticalBar = verticalScrollbar())
284            verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
285        if (Scrollbar* horizontalBar = horizontalScrollbar())
286            horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
287    }
288
289    return IntSize(width() - verticalScrollbarWidth, height() - horizontalScrollbarHeight).expandedTo(IntSize());
290}
291
292IntSize ScrollView::unscaledUnobscuredVisibleContentSize(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
293{
294    IntSize visibleContentSize = unscaledVisibleContentSizeIncludingObscuredArea(scrollbarInclusion);
295
296    if (platformWidget())
297        return platformVisibleContentSize(scrollbarInclusion == IncludeScrollbars);
298
299#if USE(TILED_BACKING_STORE)
300    if (!m_fixedVisibleContentRect.isEmpty())
301        return visibleContentSize;
302#endif
303
304    visibleContentSize.setHeight(visibleContentSize.height() - topContentInset());
305    return visibleContentSize;
306}
307
308IntRect ScrollView::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior visibleContentRectBehavior) const
309{
310#if PLATFORM(IOS)
311    if (visibleContentRectBehavior == LegacyIOSDocumentViewRect) {
312        if (platformWidget())
313            return platformVisibleContentRect(true /* include scrollbars */);
314    }
315
316    if (platformWidget())
317        return unobscuredContentRect(scrollbarInclusion);
318#else
319    UNUSED_PARAM(visibleContentRectBehavior);
320#endif
321
322    if (platformWidget())
323        return platformVisibleContentRect(scrollbarInclusion == IncludeScrollbars);
324
325#if USE(TILED_BACKING_STORE)
326    if (!m_fixedVisibleContentRect.isEmpty())
327        return m_fixedVisibleContentRect;
328#endif
329
330    return unobscuredContentRect(scrollbarInclusion);
331}
332
333IntSize ScrollView::layoutSize() const
334{
335    return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? unscaledUnobscuredVisibleContentSize(ExcludeScrollbars) : m_fixedLayoutSize;
336}
337
338IntSize ScrollView::fixedLayoutSize() const
339{
340    return m_fixedLayoutSize;
341}
342
343void ScrollView::setFixedLayoutSize(const IntSize& newSize)
344{
345    if (fixedLayoutSize() == newSize)
346        return;
347    m_fixedLayoutSize = newSize;
348    if (m_useFixedLayout)
349        fixedLayoutSizeChanged();
350}
351
352bool ScrollView::useFixedLayout() const
353{
354    return m_useFixedLayout;
355}
356
357void ScrollView::setUseFixedLayout(bool enable)
358{
359    if (useFixedLayout() == enable)
360        return;
361    m_useFixedLayout = enable;
362    if (!m_fixedLayoutSize.isEmpty())
363        fixedLayoutSizeChanged();
364}
365
366void ScrollView::fixedLayoutSizeChanged()
367{
368    updateScrollbars(scrollOffset());
369    contentsResized();
370}
371
372IntSize ScrollView::contentsSize() const
373{
374    return m_contentsSize;
375}
376
377void ScrollView::setContentsSize(const IntSize& newSize)
378{
379    if (contentsSize() == newSize)
380        return;
381    m_contentsSize = newSize;
382    if (platformWidget())
383        platformSetContentsSize();
384    else
385        updateScrollbars(scrollOffset());
386    updateOverhangAreas();
387}
388
389IntPoint ScrollView::maximumScrollPosition() const
390{
391    IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), totalContentsSize().height() - visibleHeight() - scrollOrigin().y());
392    maximumOffset.clampNegativeToZero();
393    return maximumOffset;
394}
395
396IntPoint ScrollView::minimumScrollPosition() const
397{
398    return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
399}
400
401IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
402{
403    if (!constrainsScrollingToContentEdge())
404        return scrollPoint;
405
406    IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
407    newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
408    return newScrollPosition;
409}
410
411IntSize ScrollView::documentScrollOffsetRelativeToViewOrigin() const
412{
413    return scrollOffset() - IntSize(0, headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
414}
415
416IntPoint ScrollView::documentScrollPositionRelativeToViewOrigin() const
417{
418    IntPoint scrollPosition = this->scrollPosition();
419    return IntPoint(scrollPosition.x(), scrollPosition.y() - headerHeight() - topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
420}
421
422IntSize ScrollView::documentScrollOffsetRelativeToScrollableAreaOrigin() const
423{
424    return scrollOffset() - IntSize(0, headerHeight());
425}
426
427int ScrollView::scrollSize(ScrollbarOrientation orientation) const
428{
429    // If no scrollbars are present, it does not indicate content is not be scrollable.
430    if (!m_horizontalScrollbar && !m_verticalScrollbar && !prohibitsScrolling()) {
431        IntSize scrollSize = m_contentsSize - visibleContentRect(LegacyIOSDocumentVisibleRect).size();
432        scrollSize.clampNegativeToZero();
433        return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height();
434    }
435
436    Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
437    return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
438}
439
440void ScrollView::notifyPageThatContentAreaWillPaint() const
441{
442}
443
444void ScrollView::setScrollOffset(const IntPoint& offset)
445{
446    int horizontalOffset = offset.x();
447    int verticalOffset = offset.y();
448    if (constrainsScrollingToContentEdge()) {
449        horizontalOffset = std::max(std::min(horizontalOffset, contentsWidth() - visibleWidth()), 0);
450        verticalOffset = std::max(std::min(verticalOffset, totalContentsSize().height() - visibleHeight()), 0);
451    }
452
453    IntSize newOffset = m_scrollOffset;
454    newOffset.setWidth(horizontalOffset - scrollOrigin().x());
455    newOffset.setHeight(verticalOffset - scrollOrigin().y());
456
457    scrollTo(newOffset);
458}
459
460void ScrollView::scrollTo(const IntSize& newOffset)
461{
462    IntSize scrollDelta = newOffset - m_scrollOffset;
463    if (scrollDelta == IntSize())
464        return;
465    m_scrollOffset = newOffset;
466
467    if (scrollbarsSuppressed())
468        return;
469
470#if USE(TILED_BACKING_STORE)
471    if (delegatesScrolling()) {
472        hostWindow()->delegatedScrollRequested(IntPoint(newOffset));
473        return;
474    }
475#endif
476    updateLayerPositionsAfterScrolling();
477    scrollContents(scrollDelta);
478    updateCompositingLayersAfterScrolling();
479}
480
481int ScrollView::scrollPosition(Scrollbar* scrollbar) const
482{
483    if (scrollbar->orientation() == HorizontalScrollbar)
484        return scrollPosition().x() + scrollOrigin().x();
485    if (scrollbar->orientation() == VerticalScrollbar)
486        return scrollPosition().y() + scrollOrigin().y();
487    return 0;
488}
489
490void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
491{
492    if (prohibitsScrolling())
493        return;
494
495    if (platformWidget()) {
496        platformSetScrollPosition(scrollPoint);
497        return;
498    }
499
500    IntPoint newScrollPosition = !delegatesScrolling() ? adjustScrollPositionWithinRange(scrollPoint) : scrollPoint;
501
502    if ((!delegatesScrolling() || !inProgrammaticScroll()) && newScrollPosition == scrollPosition())
503        return;
504
505    if (requestScrollPositionUpdate(newScrollPosition))
506        return;
507
508    updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
509}
510
511bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
512{
513    if (platformWidget())
514        return platformScroll(direction, granularity);
515
516    return ScrollableArea::scroll(direction, granularity);
517}
518
519bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity)
520{
521    return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity);
522}
523
524IntSize ScrollView::overhangAmount() const
525{
526    IntSize stretch;
527
528    int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
529    if (physicalScrollY < 0)
530        stretch.setHeight(physicalScrollY);
531    else if (totalContentsSize().height() && physicalScrollY > totalContentsSize().height() - visibleHeight())
532        stretch.setHeight(physicalScrollY - (totalContentsSize().height() - visibleHeight()));
533
534    int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
535    if (physicalScrollX < 0)
536        stretch.setWidth(physicalScrollX);
537    else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth())
538        stretch.setWidth(physicalScrollX - (contentsWidth() - visibleWidth()));
539
540    return stretch;
541}
542
543void ScrollView::windowResizerRectChanged()
544{
545    if (platformWidget())
546        return;
547
548    updateScrollbars(scrollOffset());
549}
550
551void ScrollView::updateScrollbars(const IntSize& desiredOffset)
552{
553    if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget() || delegatesScrolling())
554        return;
555
556    bool hasOverlayScrollbars = (!m_horizontalScrollbar || m_horizontalScrollbar->isOverlayScrollbar()) && (!m_verticalScrollbar || m_verticalScrollbar->isOverlayScrollbar());
557
558    // If we came in here with the view already needing a layout, then go ahead and do that
559    // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
560    // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
561    if (!m_scrollbarsSuppressed && !hasOverlayScrollbars) {
562        m_inUpdateScrollbars = true;
563        visibleContentsResized();
564        m_inUpdateScrollbars = false;
565    }
566
567    IntRect oldScrollCornerRect = scrollCornerRect();
568
569    bool hasHorizontalScrollbar = m_horizontalScrollbar;
570    bool hasVerticalScrollbar = m_verticalScrollbar;
571
572    bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
573    bool newHasVerticalScrollbar = hasVerticalScrollbar;
574
575    ScrollbarMode hScroll = m_horizontalScrollbarMode;
576    ScrollbarMode vScroll = m_verticalScrollbarMode;
577
578    if (hScroll != ScrollbarAuto)
579        newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
580    if (vScroll != ScrollbarAuto)
581        newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
582
583    bool scrollbarAddedOrRemoved = false;
584
585    if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
586        if (hasHorizontalScrollbar != newHasHorizontalScrollbar && (hasHorizontalScrollbar || !avoidScrollbarCreation())) {
587            if (setHasHorizontalScrollbar(newHasHorizontalScrollbar))
588                scrollbarAddedOrRemoved = true;
589        }
590
591        if (hasVerticalScrollbar != newHasVerticalScrollbar && (hasVerticalScrollbar || !avoidScrollbarCreation())) {
592            if (setHasVerticalScrollbar(newHasVerticalScrollbar))
593                scrollbarAddedOrRemoved = true;
594        }
595    } else {
596        bool sendContentResizedNotification = false;
597
598        IntSize docSize = totalContentsSize();
599        IntSize fullVisibleSize = unobscuredContentRectIncludingScrollbars().size();
600
601        if (hScroll == ScrollbarAuto)
602            newHasHorizontalScrollbar = docSize.width() > visibleWidth();
603        if (vScroll == ScrollbarAuto)
604            newHasVerticalScrollbar = docSize.height() > visibleHeight();
605
606        bool needAnotherPass = false;
607        if (!hasOverlayScrollbars) {
608            // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
609            // try to both gain/lose a scrollbar in the same pass.
610            if (!m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height()) {
611                if (hScroll == ScrollbarAuto)
612                    newHasHorizontalScrollbar = false;
613                if (vScroll == ScrollbarAuto)
614                    newHasVerticalScrollbar = false;
615            }
616            if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn) {
617                newHasVerticalScrollbar = false;
618                needAnotherPass = true;
619            }
620            if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn) {
621                newHasHorizontalScrollbar = false;
622                needAnotherPass = true;
623            }
624        }
625
626        if (hasHorizontalScrollbar != newHasHorizontalScrollbar && (hasHorizontalScrollbar || !avoidScrollbarCreation())) {
627            if (scrollOrigin().y() && !newHasHorizontalScrollbar)
628                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->height()));
629            if (m_horizontalScrollbar)
630                m_horizontalScrollbar->invalidate();
631
632            bool changeAffectsContentSize = false;
633            if (setHasHorizontalScrollbar(newHasHorizontalScrollbar, &changeAffectsContentSize)) {
634                scrollbarAddedOrRemoved = true;
635                sendContentResizedNotification |= changeAffectsContentSize;
636            }
637        }
638
639        if (hasVerticalScrollbar != newHasVerticalScrollbar && (hasVerticalScrollbar || !avoidScrollbarCreation())) {
640            if (scrollOrigin().x() && !newHasVerticalScrollbar)
641                ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->width(), scrollOrigin().y()));
642            if (m_verticalScrollbar)
643                m_verticalScrollbar->invalidate();
644
645            bool changeAffectsContentSize = false;
646            if (setHasVerticalScrollbar(newHasVerticalScrollbar, &changeAffectsContentSize)) {
647                scrollbarAddedOrRemoved = true;
648                sendContentResizedNotification |= changeAffectsContentSize;
649            }
650        }
651
652        const unsigned cMaxUpdateScrollbarsPass = 2;
653        if ((sendContentResizedNotification || needAnotherPass) && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
654            m_updateScrollbarsPass++;
655            contentsResized();
656            visibleContentsResized();
657            IntSize newDocSize = totalContentsSize();
658            if (newDocSize == docSize) {
659                // The layout with the new scroll state had no impact on
660                // the document's overall size, so updateScrollbars didn't get called.
661                // Recur manually.
662                updateScrollbars(desiredOffset);
663            }
664            m_updateScrollbarsPass--;
665        }
666    }
667
668    if (scrollbarAddedOrRemoved)
669        addedOrRemovedScrollbar();
670
671    // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
672    // doing it multiple times).
673    if (m_updateScrollbarsPass)
674        return;
675
676    m_inUpdateScrollbars = true;
677
678    if (m_horizontalScrollbar) {
679        int clientWidth = visibleWidth();
680        int pageStep = std::max(std::max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
681        IntRect oldRect(m_horizontalScrollbar->frameRect());
682        IntRect hBarRect(0,
683            height() - m_horizontalScrollbar->height(),
684            width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
685            m_horizontalScrollbar->height());
686        m_horizontalScrollbar->setFrameRect(hBarRect);
687        if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
688            m_horizontalScrollbar->invalidate();
689
690        if (m_scrollbarsSuppressed)
691            m_horizontalScrollbar->setSuppressInvalidation(true);
692        m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
693        m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
694        m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
695        if (m_scrollbarsSuppressed)
696            m_horizontalScrollbar->setSuppressInvalidation(false);
697    }
698
699    if (m_verticalScrollbar) {
700        int clientHeight = visibleHeight();
701        int pageStep = std::max(std::max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
702        IntRect oldRect(m_verticalScrollbar->frameRect());
703        IntRect vBarRect(width() - m_verticalScrollbar->width(),
704            topContentInset(),
705            m_verticalScrollbar->width(),
706            height() - topContentInset() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
707        m_verticalScrollbar->setFrameRect(vBarRect);
708        if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
709            m_verticalScrollbar->invalidate();
710
711        if (m_scrollbarsSuppressed)
712            m_verticalScrollbar->setSuppressInvalidation(true);
713        m_verticalScrollbar->setEnabled(totalContentsSize().height() > clientHeight);
714        m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
715        m_verticalScrollbar->setProportion(clientHeight, totalContentsSize().height());
716        if (m_scrollbarsSuppressed)
717            m_verticalScrollbar->setSuppressInvalidation(false);
718    }
719
720    if (hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar) {
721        // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
722        frameRectsChanged();
723        positionScrollbarLayers();
724        updateScrollCorner();
725        if (!m_horizontalScrollbar && !m_verticalScrollbar)
726            invalidateScrollCornerRect(oldScrollCornerRect);
727    }
728
729    IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
730    if (!isRubberBandInProgress())
731        adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
732
733    if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
734        ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition + toIntSize(scrollOrigin()));
735        resetScrollOriginChanged();
736    }
737
738    // Make sure the scrollbar offsets are up to date.
739    if (m_horizontalScrollbar)
740        m_horizontalScrollbar->offsetDidChange();
741    if (m_verticalScrollbar)
742        m_verticalScrollbar->offsetDidChange();
743
744    m_inUpdateScrollbars = false;
745}
746
747const int panIconSizeLength = 16;
748
749IntRect ScrollView::rectToCopyOnScroll() const
750{
751    IntRect scrollViewRect = convertToRootView(IntRect(0, 0, visibleWidth(), visibleHeight()));
752    if (hasOverlayScrollbars()) {
753        int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
754        int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
755
756        scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
757        scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
758    }
759    return scrollViewRect;
760}
761
762void ScrollView::scrollContents(const IntSize& scrollDelta)
763{
764    HostWindow* window = hostWindow();
765    if (!window)
766        return;
767
768    // Since scrolling is double buffered, we will be blitting the scroll view's intersection
769    // with the clip rect every time to keep it smooth.
770    IntRect clipRect = windowClipRect();
771    IntRect scrollViewRect = rectToCopyOnScroll();
772    IntRect updateRect = clipRect;
773    updateRect.intersect(scrollViewRect);
774
775    // Invalidate the root view (not the backing store).
776    window->invalidateRootView(updateRect);
777
778    if (m_drawPanScrollIcon) {
779        // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
780        // https://bugs.webkit.org/show_bug.cgi?id=47837
781        int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + std::max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
782        IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
783        IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
784        panScrollIconDirtyRect.intersect(clipRect);
785        window->invalidateContentsAndRootView(panScrollIconDirtyRect);
786    }
787
788    if (canBlitOnScroll()) { // The main frame can just blit the WebView window
789        // FIXME: Find a way to scroll subframes with this faster path
790        if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
791            scrollContentsSlowPath(updateRect);
792    } else {
793       // We need to go ahead and repaint the entire backing store.  Do it now before moving the
794       // windowed plugins.
795       scrollContentsSlowPath(updateRect);
796    }
797
798    // Invalidate the overhang areas if they are visible.
799    updateOverhangAreas();
800
801    // This call will move children with native widgets (plugins) and invalidate them as well.
802    frameRectsChanged();
803
804    // Now blit the backingstore into the window which should be very fast.
805    window->invalidateRootView(IntRect());
806}
807
808bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
809{
810    hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
811    return true;
812}
813
814void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
815{
816    hostWindow()->invalidateContentsForSlowScroll(updateRect);
817}
818
819IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
820{
821    if (delegatesScrolling())
822        return convertFromRootView(rootViewPoint);
823
824    IntPoint viewPoint = convertFromRootView(rootViewPoint);
825    return viewPoint + documentScrollOffsetRelativeToViewOrigin();
826}
827
828IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
829{
830    if (delegatesScrolling())
831        return convertToRootView(contentsPoint);
832
833    IntPoint viewPoint = contentsPoint + IntSize(0, headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset)) - scrollOffset();
834    return convertToRootView(viewPoint);
835}
836
837IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
838{
839    if (delegatesScrolling())
840        return convertFromRootView(rootViewRect);
841
842    IntRect viewRect = convertFromRootView(rootViewRect);
843    viewRect.move(documentScrollOffsetRelativeToViewOrigin());
844    return viewRect;
845}
846
847IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
848{
849    if (delegatesScrolling())
850        return convertToRootView(contentsRect);
851
852    IntRect viewRect = contentsRect;
853    viewRect.move(-scrollOffset() + IntSize(0, headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset)));
854    return convertToRootView(viewRect);
855}
856
857IntPoint ScrollView::rootViewToTotalContents(const IntPoint& rootViewPoint) const
858{
859    if (delegatesScrolling())
860        return convertFromRootView(rootViewPoint);
861
862    IntPoint viewPoint = convertFromRootView(rootViewPoint);
863    return viewPoint + scrollOffset() - IntSize(0, topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
864}
865
866IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
867{
868    if (delegatesScrolling())
869        return convertFromContainingWindow(windowPoint);
870
871    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
872    return viewPoint + documentScrollOffsetRelativeToViewOrigin();
873}
874
875IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
876{
877    if (delegatesScrolling())
878        return convertToContainingWindow(contentsPoint);
879
880    IntPoint viewPoint = contentsPoint + IntSize(0, headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset)) - scrollOffset();
881    return convertToContainingWindow(viewPoint);
882}
883
884IntRect ScrollView::windowToContents(const IntRect& windowRect) const
885{
886    if (delegatesScrolling())
887        return convertFromContainingWindow(windowRect);
888
889    IntRect viewRect = convertFromContainingWindow(windowRect);
890    viewRect.move(documentScrollOffsetRelativeToViewOrigin());
891    return viewRect;
892}
893
894IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
895{
896    if (delegatesScrolling())
897        return convertToContainingWindow(contentsRect);
898
899    IntRect viewRect = contentsRect;
900    viewRect.move(-scrollOffset() + IntSize(0, headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset)));
901    return convertToContainingWindow(viewRect);
902}
903
904IntRect ScrollView::contentsToScreen(const IntRect& rect) const
905{
906    HostWindow* window = hostWindow();
907    if (platformWidget())
908        return platformContentsToScreen(rect);
909    if (!window)
910        return IntRect();
911    return window->rootViewToScreen(contentsToRootView(rect));
912}
913
914IntPoint ScrollView::screenToContents(const IntPoint& point) const
915{
916    HostWindow* window = hostWindow();
917    if (platformWidget())
918        return platformScreenToContents(point);
919    if (!window)
920        return IntPoint();
921    return rootViewToContents(window->screenToRootView(point));
922}
923
924bool ScrollView::containsScrollbarsAvoidingResizer() const
925{
926    return !m_scrollbarsAvoidingResizer;
927}
928
929void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
930{
931    int oldCount = m_scrollbarsAvoidingResizer;
932    m_scrollbarsAvoidingResizer += overlapDelta;
933    if (parent())
934        parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
935    else if (!scrollbarsSuppressed()) {
936        // If we went from n to 0 or from 0 to n and we're the outermost view,
937        // we need to invalidate the windowResizerRect(), since it will now need to paint
938        // differently.
939        if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
940            (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
941            invalidateRect(windowResizerRect());
942    }
943}
944
945void ScrollView::setParent(ScrollView* parentView)
946{
947    if (parentView == parent())
948        return;
949
950    if (m_scrollbarsAvoidingResizer && parent())
951        parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
952
953    Widget::setParent(parentView);
954
955    if (m_scrollbarsAvoidingResizer && parent())
956        parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
957}
958
959void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
960{
961    if (suppressed == m_scrollbarsSuppressed)
962        return;
963
964    m_scrollbarsSuppressed = suppressed;
965
966    if (platformWidget())
967        platformSetScrollbarsSuppressed(repaintOnUnsuppress);
968    else if (repaintOnUnsuppress && !suppressed) {
969        if (m_horizontalScrollbar)
970            m_horizontalScrollbar->invalidate();
971        if (m_verticalScrollbar)
972            m_verticalScrollbar->invalidate();
973
974        // Invalidate the scroll corner too on unsuppress.
975        invalidateRect(scrollCornerRect());
976    }
977}
978
979Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
980{
981    if (platformWidget())
982        return 0;
983
984    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
985    if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
986        return m_horizontalScrollbar.get();
987    if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
988        return m_verticalScrollbar.get();
989    return 0;
990}
991
992void ScrollView::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
993{
994    ScrollableArea::setScrollbarOverlayStyle(overlayStyle);
995    platformSetScrollbarOverlayStyle(overlayStyle);
996}
997
998void ScrollView::setFrameRect(const IntRect& newRect)
999{
1000    IntRect oldRect = frameRect();
1001
1002    if (newRect == oldRect)
1003        return;
1004
1005    Widget::setFrameRect(newRect);
1006
1007    frameRectsChanged();
1008
1009    updateScrollbars(scrollOffset());
1010
1011    if (!m_useFixedLayout && oldRect.size() != newRect.size())
1012        contentsResized();
1013}
1014
1015void ScrollView::frameRectsChanged()
1016{
1017    if (platformWidget())
1018        return;
1019
1020    HashSet<RefPtr<Widget>>::const_iterator end = m_children.end();
1021    for (HashSet<RefPtr<Widget>>::const_iterator current = m_children.begin(); current != end; ++current)
1022        (*current)->frameRectsChanged();
1023}
1024
1025void ScrollView::clipRectChanged()
1026{
1027    HashSet<RefPtr<Widget>>::const_iterator end = m_children.end();
1028    for (HashSet<RefPtr<Widget>>::const_iterator current = m_children.begin(); current != end; ++current)
1029        (*current)->clipRectChanged();
1030}
1031
1032static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
1033{
1034    if (!graphicsLayer || !scrollbar)
1035        return;
1036
1037    IntRect scrollbarRect = scrollbar->frameRect();
1038    graphicsLayer->setPosition(scrollbarRect.location());
1039
1040    if (scrollbarRect.size() == graphicsLayer->size())
1041        return;
1042
1043    graphicsLayer->setSize(scrollbarRect.size());
1044
1045    if (graphicsLayer->usesContentsLayer()) {
1046        graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
1047        return;
1048    }
1049
1050    graphicsLayer->setDrawsContent(true);
1051    graphicsLayer->setNeedsDisplay();
1052}
1053
1054static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
1055{
1056    if (!graphicsLayer)
1057        return;
1058    graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
1059    graphicsLayer->setPosition(cornerRect.location());
1060    if (cornerRect.size() != graphicsLayer->size())
1061        graphicsLayer->setNeedsDisplay();
1062    graphicsLayer->setSize(cornerRect.size());
1063}
1064
1065void ScrollView::positionScrollbarLayers()
1066{
1067    positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
1068    positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
1069    positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
1070}
1071
1072void ScrollView::repaintContentRectangle(const IntRect& rect)
1073{
1074    IntRect paintRect = rect;
1075    if (clipsRepaints() && !paintsEntireContents())
1076        paintRect.intersect(visibleContentRect(LegacyIOSDocumentVisibleRect));
1077    if (paintRect.isEmpty())
1078        return;
1079
1080    if (platformWidget()) {
1081        notifyPageThatContentAreaWillPaint();
1082        platformRepaintContentRectangle(paintRect);
1083        return;
1084    }
1085
1086    if (HostWindow* window = hostWindow())
1087        window->invalidateContentsAndRootView(contentsToWindow(paintRect));
1088}
1089
1090IntRect ScrollView::scrollCornerRect() const
1091{
1092    IntRect cornerRect;
1093
1094    if (hasOverlayScrollbars())
1095        return cornerRect;
1096
1097    int heightTrackedByScrollbar = height() - topContentInset();
1098
1099    if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
1100        cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
1101            height() - m_horizontalScrollbar->height(),
1102            width() - m_horizontalScrollbar->width(),
1103            m_horizontalScrollbar->height()));
1104    }
1105
1106    if (m_verticalScrollbar && heightTrackedByScrollbar - m_verticalScrollbar->height() > 0) {
1107        cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
1108            m_verticalScrollbar->height() + topContentInset(),
1109            m_verticalScrollbar->width(),
1110            heightTrackedByScrollbar - m_verticalScrollbar->height()));
1111    }
1112
1113    return cornerRect;
1114}
1115
1116bool ScrollView::isScrollCornerVisible() const
1117{
1118    return !scrollCornerRect().isEmpty();
1119}
1120
1121void ScrollView::scrollbarStyleChanged(int, bool forceUpdate)
1122{
1123    if (!forceUpdate)
1124        return;
1125
1126    contentsResized();
1127    updateScrollbars(scrollOffset());
1128    positionScrollbarLayers();
1129}
1130
1131void ScrollView::updateScrollCorner()
1132{
1133}
1134
1135void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
1136{
1137    ScrollbarTheme::theme()->paintScrollCorner(this, context, cornerRect);
1138}
1139
1140void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
1141{
1142    bar->paint(context, rect);
1143}
1144
1145void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
1146{
1147    invalidateRect(rect);
1148}
1149
1150void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
1151{
1152    if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
1153        paintScrollbar(context, m_horizontalScrollbar.get(), rect);
1154    if (m_verticalScrollbar && !layerForVerticalScrollbar())
1155        paintScrollbar(context, m_verticalScrollbar.get(), rect);
1156
1157    if (layerForScrollCorner())
1158        return;
1159
1160    paintScrollCorner(context, scrollCornerRect());
1161}
1162
1163void ScrollView::paintPanScrollIcon(GraphicsContext* context)
1164{
1165    static Image* panScrollIcon = Image::loadPlatformResource("panIcon").leakRef();
1166    IntPoint iconGCPoint = m_panScrollIconPoint;
1167    if (parent())
1168        iconGCPoint = parent()->windowToContents(iconGCPoint);
1169    context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, iconGCPoint);
1170}
1171
1172void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
1173{
1174    if (platformWidget()) {
1175        Widget::paint(context, rect);
1176        return;
1177    }
1178
1179    if (context->paintingDisabled() && !context->updatingControlTints())
1180        return;
1181
1182    notifyPageThatContentAreaWillPaint();
1183
1184    IntRect documentDirtyRect = rect;
1185    if (!paintsEntireContents()) {
1186        IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect(LegacyIOSDocumentVisibleRect).size());
1187        documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
1188    }
1189
1190    if (!documentDirtyRect.isEmpty()) {
1191        GraphicsContextStateSaver stateSaver(*context);
1192
1193        context->translate(x(), y());
1194        documentDirtyRect.moveBy(-location());
1195
1196        if (!paintsEntireContents()) {
1197            context->translate(-scrollX(), -scrollY());
1198            documentDirtyRect.moveBy(scrollPosition());
1199
1200            context->clip(visibleContentRect(LegacyIOSDocumentVisibleRect));
1201        }
1202
1203        paintContents(context, documentDirtyRect);
1204    }
1205
1206#if ENABLE(RUBBER_BANDING)
1207    if (!layerForOverhangAreas())
1208        calculateAndPaintOverhangAreas(context, rect);
1209#else
1210    calculateAndPaintOverhangAreas(context, rect);
1211#endif
1212
1213    // Now paint the scrollbars.
1214    if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
1215        GraphicsContextStateSaver stateSaver(*context);
1216        IntRect scrollViewDirtyRect = rect;
1217        IntRect visibleAreaWithScrollbars(location(), unobscuredContentRectIncludingScrollbars().size());
1218        scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
1219        context->translate(x(), y());
1220        scrollViewDirtyRect.moveBy(-location());
1221
1222        paintScrollbars(context, scrollViewDirtyRect);
1223    }
1224
1225    // Paint the panScroll Icon
1226    if (m_drawPanScrollIcon)
1227        paintPanScrollIcon(context);
1228}
1229
1230void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
1231{
1232    int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
1233        ? verticalScrollbar()->width() : 0;
1234    int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
1235        ? horizontalScrollbar()->height() : 0;
1236
1237    int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
1238    if (physicalScrollY < 0) {
1239        horizontalOverhangRect = frameRect();
1240        horizontalOverhangRect.setHeight(-physicalScrollY);
1241        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
1242    } else if (totalContentsSize().height() && physicalScrollY > totalContentsSize().height() - visibleHeight()) {
1243        int height = physicalScrollY - (totalContentsSize().height() - visibleHeight());
1244        horizontalOverhangRect = frameRect();
1245        horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
1246        horizontalOverhangRect.setHeight(height);
1247        horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
1248    }
1249
1250    int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
1251    if (physicalScrollX < 0) {
1252        verticalOverhangRect.setWidth(-physicalScrollX);
1253        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
1254        verticalOverhangRect.setX(frameRect().x());
1255        if (horizontalOverhangRect.y() == frameRect().y())
1256            verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
1257        else
1258            verticalOverhangRect.setY(frameRect().y());
1259    } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
1260        int width = physicalScrollX - (contentsWidth() - visibleWidth());
1261        verticalOverhangRect.setWidth(width);
1262        verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
1263        verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
1264        if (horizontalOverhangRect.y() == frameRect().y())
1265            verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
1266        else
1267            verticalOverhangRect.setY(frameRect().y());
1268    }
1269}
1270
1271void ScrollView::updateOverhangAreas()
1272{
1273    HostWindow* window = hostWindow();
1274    if (!window)
1275        return;
1276
1277    IntRect horizontalOverhangRect;
1278    IntRect verticalOverhangRect;
1279    calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
1280    if (!horizontalOverhangRect.isEmpty())
1281        window->invalidateContentsAndRootView(horizontalOverhangRect);
1282    if (!verticalOverhangRect.isEmpty())
1283        window->invalidateContentsAndRootView(verticalOverhangRect);
1284}
1285
1286void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
1287{
1288    ScrollbarTheme::theme()->paintOverhangAreas(this, context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
1289}
1290
1291void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
1292{
1293    IntRect horizontalOverhangRect;
1294    IntRect verticalOverhangRect;
1295    calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
1296
1297    if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
1298        paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
1299}
1300
1301bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
1302{
1303    if (!scrollbarCornerPresent())
1304        return false;
1305
1306    IntPoint viewPoint = convertFromContainingWindow(windowPoint);
1307
1308    if (m_horizontalScrollbar) {
1309        int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
1310        int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
1311        int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
1312
1313        return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1314    }
1315
1316    int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1317    int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1318    int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1319
1320    return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1321}
1322
1323bool ScrollView::scrollbarCornerPresent() const
1324{
1325    return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1326        || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1327}
1328
1329IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1330{
1331    // Scrollbars won't be transformed within us
1332    IntRect newRect = localRect;
1333    newRect.moveBy(scrollbar->location());
1334    return newRect;
1335}
1336
1337IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1338{
1339    IntRect newRect = parentRect;
1340    // Scrollbars won't be transformed within us
1341    newRect.moveBy(-scrollbar->location());
1342    return newRect;
1343}
1344
1345// FIXME: test these on windows
1346IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1347{
1348    // Scrollbars won't be transformed within us
1349    IntPoint newPoint = localPoint;
1350    newPoint.moveBy(scrollbar->location());
1351    return newPoint;
1352}
1353
1354IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1355{
1356    IntPoint newPoint = parentPoint;
1357    // Scrollbars won't be transformed within us
1358    newPoint.moveBy(-scrollbar->location());
1359    return newPoint;
1360}
1361
1362void ScrollView::setParentVisible(bool visible)
1363{
1364    if (isParentVisible() == visible)
1365        return;
1366
1367    Widget::setParentVisible(visible);
1368
1369    if (!isSelfVisible())
1370        return;
1371
1372    HashSet<RefPtr<Widget>>::iterator end = m_children.end();
1373    for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
1374        (*it)->setParentVisible(visible);
1375}
1376
1377void ScrollView::show()
1378{
1379    if (!isSelfVisible()) {
1380        setSelfVisible(true);
1381        if (isParentVisible()) {
1382            HashSet<RefPtr<Widget>>::iterator end = m_children.end();
1383            for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
1384                (*it)->setParentVisible(true);
1385        }
1386    }
1387
1388    Widget::show();
1389}
1390
1391void ScrollView::hide()
1392{
1393    if (isSelfVisible()) {
1394        if (isParentVisible()) {
1395            HashSet<RefPtr<Widget>>::iterator end = m_children.end();
1396            for (HashSet<RefPtr<Widget>>::iterator it = m_children.begin(); it != end; ++it)
1397                (*it)->setParentVisible(false);
1398        }
1399        setSelfVisible(false);
1400    }
1401
1402    Widget::hide();
1403}
1404
1405bool ScrollView::isOffscreen() const
1406{
1407    if (platformWidget())
1408        return platformIsOffscreen();
1409
1410    if (!isVisible())
1411        return true;
1412
1413    // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
1414    // currently, we can add the method when the other platforms decide to implement this concept.
1415    return false;
1416}
1417
1418
1419void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1420{
1421    HostWindow* window = hostWindow();
1422    if (!window)
1423        return;
1424    m_drawPanScrollIcon = true;
1425    m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1426    window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1427}
1428
1429void ScrollView::removePanScrollIcon()
1430{
1431    HostWindow* window = hostWindow();
1432    if (!window)
1433        return;
1434    m_drawPanScrollIcon = false;
1435    window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1436}
1437
1438void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1439{
1440    if (scrollOrigin() == origin)
1441        return;
1442
1443    ScrollableArea::setScrollOrigin(origin);
1444
1445    if (platformWidget()) {
1446        platformSetScrollOrigin(origin, updatePositionAtAll, updatePositionSynchronously);
1447        return;
1448    }
1449
1450    // Update if the scroll origin changes, since our position will be different if the content size did not change.
1451    if (updatePositionAtAll && updatePositionSynchronously)
1452        updateScrollbars(scrollOffset());
1453}
1454
1455void ScrollView::styleDidChange()
1456{
1457    if (m_horizontalScrollbar)
1458        m_horizontalScrollbar->styleChanged();
1459
1460    if (m_verticalScrollbar)
1461        m_verticalScrollbar->styleChanged();
1462}
1463
1464#if !PLATFORM(COCOA)
1465
1466void ScrollView::platformAddChild(Widget*)
1467{
1468}
1469
1470void ScrollView::platformRemoveChild(Widget*)
1471{
1472}
1473
1474#endif
1475
1476#if !PLATFORM(COCOA)
1477
1478void ScrollView::platformSetScrollbarsSuppressed(bool)
1479{
1480}
1481
1482void ScrollView::platformSetScrollOrigin(const IntPoint&, bool, bool)
1483{
1484}
1485
1486void ScrollView::platformSetScrollbarOverlayStyle(ScrollbarOverlayStyle)
1487{
1488}
1489
1490#endif
1491
1492#if !PLATFORM(COCOA)
1493
1494void ScrollView::platformSetScrollbarModes()
1495{
1496}
1497
1498void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
1499{
1500    horizontal = ScrollbarAuto;
1501    vertical = ScrollbarAuto;
1502}
1503
1504void ScrollView::platformSetCanBlitOnScroll(bool)
1505{
1506}
1507
1508bool ScrollView::platformCanBlitOnScroll() const
1509{
1510    return false;
1511}
1512
1513IntRect ScrollView::platformVisibleContentRect(bool) const
1514{
1515    return IntRect();
1516}
1517
1518float ScrollView::platformTopContentInset() const
1519{
1520    return 0;
1521}
1522
1523void ScrollView::platformSetTopContentInset(float)
1524{
1525}
1526
1527IntSize ScrollView::platformVisibleContentSize(bool) const
1528{
1529    return IntSize();
1530}
1531
1532IntRect ScrollView::platformVisibleContentRectIncludingObscuredArea(bool) const
1533{
1534    return IntRect();
1535}
1536
1537IntSize ScrollView::platformVisibleContentSizeIncludingObscuredArea(bool) const
1538{
1539    return IntSize();
1540}
1541
1542void ScrollView::platformSetContentsSize()
1543{
1544}
1545
1546IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1547{
1548    return rect;
1549}
1550
1551IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1552{
1553    return point;
1554}
1555
1556void ScrollView::platformSetScrollPosition(const IntPoint&)
1557{
1558}
1559
1560bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1561{
1562    return true;
1563}
1564
1565void ScrollView::platformRepaintContentRectangle(const IntRect&)
1566{
1567}
1568
1569bool ScrollView::platformIsOffscreen() const
1570{
1571    return false;
1572}
1573
1574#endif
1575
1576}
1577