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