3fdca580d1e410415744f216367af5c6022bdbb1
[WebKit-https.git] / Source / WebCore / platform / ScrollView.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ScrollView.h"
28
29 #include "AXObjectCache.h"
30 #include "GraphicsContext.h"
31 #include "HostWindow.h"
32 #include "PlatformMouseEvent.h"
33 #include "PlatformWheelEvent.h"
34 #include "Scrollbar.h"
35 #include "ScrollbarTheme.h"
36 #include <wtf/StdLibExtras.h>
37
38 using namespace std;
39
40 namespace WebCore {
41
42 ScrollView::ScrollView()
43     : m_horizontalScrollbarMode(ScrollbarAuto)
44     , m_verticalScrollbarMode(ScrollbarAuto)
45     , m_horizontalScrollbarLock(false)
46     , m_verticalScrollbarLock(false)
47     , m_prohibitsScrolling(false)
48     , m_canBlitOnScroll(true)
49     , m_scrollbarsAvoidingResizer(0)
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     platformInit();
60 }
61
62 ScrollView::~ScrollView()
63 {
64     platformDestroy();
65 }
66
67 void ScrollView::addChild(PassRefPtr<Widget> prpChild) 
68 {
69     Widget* child = prpChild.get();
70     ASSERT(child != this && !child->parent());
71     child->setParent(this);
72     m_children.add(prpChild);
73     if (child->platformWidget())
74         platformAddChild(child);
75 }
76
77 void ScrollView::removeChild(Widget* child)
78 {
79     ASSERT(child->parent() == this);
80     child->setParent(0);
81     m_children.remove(child);
82     if (child->platformWidget())
83         platformRemoveChild(child);
84 }
85
86 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
87 {
88     if (hasBar && avoidScrollbarCreation())
89         return;
90
91     if (hasBar && !m_horizontalScrollbar) {
92         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
93         addChild(m_horizontalScrollbar.get());
94         m_horizontalScrollbar->styleChanged();
95     } else if (!hasBar && m_horizontalScrollbar) {
96         removeChild(m_horizontalScrollbar.get());
97         m_horizontalScrollbar = 0;
98     }
99     
100     if (AXObjectCache::accessibilityEnabled() && axObjectCache())
101         axObjectCache()->handleScrollbarUpdate(this);
102 }
103
104 void ScrollView::setHasVerticalScrollbar(bool hasBar)
105 {
106     if (hasBar && avoidScrollbarCreation())
107         return;
108
109     if (hasBar && !m_verticalScrollbar) {
110         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
111         addChild(m_verticalScrollbar.get());
112         m_verticalScrollbar->styleChanged();
113     } else if (!hasBar && m_verticalScrollbar) {
114         removeChild(m_verticalScrollbar.get());
115         m_verticalScrollbar = 0;
116     }
117     
118     if (AXObjectCache::accessibilityEnabled() && axObjectCache())
119         axObjectCache()->handleScrollbarUpdate(this);
120 }
121
122 #if !PLATFORM(GTK)
123 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
124 {
125     return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
126 }
127
128 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
129                                    bool horizontalLock, bool verticalLock)
130 {
131     bool needsUpdate = false;
132
133     if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
134         m_horizontalScrollbarMode = horizontalMode;
135         needsUpdate = true;
136     }
137
138     if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
139         m_verticalScrollbarMode = verticalMode;
140         needsUpdate = true;
141     }
142
143     if (horizontalLock)
144         setHorizontalScrollbarLock();
145
146     if (verticalLock)
147         setVerticalScrollbarLock();
148
149     if (!needsUpdate)
150         return;
151
152     if (platformWidget())
153         platformSetScrollbarModes();
154     else
155         updateScrollbars(scrollOffset());
156 }
157 #endif
158
159 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
160 {
161     if (platformWidget()) {
162         platformScrollbarModes(horizontalMode, verticalMode);
163         return;
164     }
165     horizontalMode = m_horizontalScrollbarMode;
166     verticalMode = m_verticalScrollbarMode;
167 }
168
169 void ScrollView::setCanHaveScrollbars(bool canScroll)
170 {
171     ScrollbarMode newHorizontalMode;
172     ScrollbarMode newVerticalMode;
173     
174     scrollbarModes(newHorizontalMode, newVerticalMode);
175     
176     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
177         newVerticalMode = ScrollbarAuto;
178     else if (!canScroll)
179         newVerticalMode = ScrollbarAlwaysOff;
180     
181     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
182         newHorizontalMode = ScrollbarAuto;
183     else if (!canScroll)
184         newHorizontalMode = ScrollbarAlwaysOff;
185     
186     setScrollbarModes(newHorizontalMode, newVerticalMode);
187 }
188
189 void ScrollView::setCanBlitOnScroll(bool b)
190 {
191     if (platformWidget()) {
192         platformSetCanBlitOnScroll(b);
193         return;
194     }
195
196     m_canBlitOnScroll = b;
197 }
198
199 bool ScrollView::canBlitOnScroll() const
200 {
201     if (platformWidget())
202         return platformCanBlitOnScroll();
203
204     return m_canBlitOnScroll;
205 }
206
207 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
208 {
209     m_paintsEntireContents = paintsEntireContents;
210 }
211
212 void ScrollView::setClipsRepaints(bool clipsRepaints)
213 {
214     m_clipsRepaints = clipsRepaints;
215 }
216
217 void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
218 {
219     m_delegatesScrolling = delegatesScrolling;
220 }
221
222 #if !PLATFORM(GTK)
223 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
224 {
225     if (platformWidget())
226         return platformVisibleContentRect(includeScrollbars);
227
228     if (paintsEntireContents())
229         return IntRect(IntPoint(0, 0), contentsSize());
230
231     bool hasOverlayScrollbars = ScrollbarTheme::nativeTheme()->usesOverlayScrollbars();
232     int verticalScrollbarWidth = verticalScrollbar() && !hasOverlayScrollbars && !includeScrollbars
233         ? verticalScrollbar()->width() : 0;
234     int horizontalScrollbarHeight = horizontalScrollbar() && !hasOverlayScrollbars && !includeScrollbars
235         ? horizontalScrollbar()->height() : 0;
236
237     return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
238                    IntSize(max(0, width() - verticalScrollbarWidth), 
239                            max(0, height() - horizontalScrollbarHeight)));
240 }
241 #endif
242
243 int ScrollView::layoutWidth() const
244 {
245     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
246 }
247
248 int ScrollView::layoutHeight() const
249 {
250     return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
251 }
252
253 IntSize ScrollView::fixedLayoutSize() const
254 {
255     return m_fixedLayoutSize;
256 }
257
258 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
259 {
260     if (fixedLayoutSize() == newSize)
261         return;
262     m_fixedLayoutSize = newSize;
263     updateScrollbars(scrollOffset());
264 }
265
266 bool ScrollView::useFixedLayout() const
267 {
268     return m_useFixedLayout;
269 }
270
271 void ScrollView::setUseFixedLayout(bool enable)
272 {
273     if (useFixedLayout() == enable)
274         return;
275     m_useFixedLayout = enable;
276     updateScrollbars(scrollOffset());
277 }
278
279 IntSize ScrollView::contentsSize() const
280 {
281     if (platformWidget())
282         return platformContentsSize();
283     return m_contentsSize;
284 }
285
286 void ScrollView::setContentsSize(const IntSize& newSize)
287 {
288     if (contentsSize() == newSize)
289         return;
290     m_contentsSize = newSize;
291     if (platformWidget())
292         platformSetContentsSize();
293     else
294         updateScrollbars(scrollOffset());
295 }
296
297 IntPoint ScrollView::maximumScrollPosition() const
298 {
299     IntPoint maximumOffset(contentsWidth() - visibleWidth() - m_scrollOrigin.x(), contentsHeight() - visibleHeight() - m_scrollOrigin.y());
300     maximumOffset.clampNegativeToZero();
301     return maximumOffset;
302 }
303
304 IntPoint ScrollView::minimumScrollPosition() const
305 {
306     return IntPoint(-m_scrollOrigin.x(), -m_scrollOrigin.y());
307 }
308
309 IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
310 {
311     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
312     newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
313     return newScrollPosition;
314 }
315
316 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
317 {
318     Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
319     return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
320 }
321
322 void ScrollView::setScrollOffset(const IntPoint& offset)
323 {
324     int horizontalOffset = offset.x();
325     int verticalOffset = offset.y();
326     if (constrainsScrollingToContentEdge()) {
327         horizontalOffset = max(min(horizontalOffset, contentsWidth() - visibleWidth()), 0);
328         verticalOffset = max(min(verticalOffset, contentsHeight() - visibleHeight()), 0);
329     }
330
331     IntSize newOffset = m_scrollOffset;
332     newOffset.setWidth(horizontalOffset - m_scrollOrigin.x());
333     newOffset.setHeight(verticalOffset - m_scrollOrigin.y());
334
335     scrollTo(newOffset);
336 }
337
338 void ScrollView::scrollTo(const IntSize& newOffset)
339 {
340     IntSize scrollDelta = newOffset - m_scrollOffset;
341     if (scrollDelta == IntSize())
342         return;
343     m_scrollOffset = newOffset;
344
345     if (scrollbarsSuppressed())
346         return;
347
348     repaintFixedElementsAfterScrolling();
349     scrollContents(scrollDelta);
350 }
351
352 int ScrollView::scrollPosition(Scrollbar* scrollbar) const
353 {
354     if (scrollbar->orientation() == HorizontalScrollbar)
355         return scrollPosition().x() + m_scrollOrigin.x();
356     if (scrollbar->orientation() == VerticalScrollbar)
357         return scrollPosition().y() + m_scrollOrigin.y();
358     return 0;
359 }
360
361 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
362 {
363     if (prohibitsScrolling())
364         return;
365
366     if (platformWidget()) {
367         platformSetScrollPosition(scrollPoint);
368         return;
369     }
370
371 #if ENABLE(TILED_BACKING_STORE)
372     if (delegatesScrolling()) {
373         hostWindow()->delegatedScrollRequested(IntSize(scrollPoint.x(), scrollPoint.y()));
374         if (!m_actualVisibleContentRect.isEmpty())
375             m_actualVisibleContentRect.setLocation(scrollPoint);
376         return;
377     }
378 #endif
379
380     IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
381
382     if (newScrollPosition == scrollPosition())
383         return;
384
385     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
386 }
387
388 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
389 {
390     if (platformWidget())
391         return platformScroll(direction, granularity);
392
393     return ScrollableArea::scroll(direction, granularity);
394 }
395
396 bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity)
397 {
398     return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity);
399 }
400
401 IntSize ScrollView::overhangAmount() const
402 {
403     IntSize stretch;
404     if (scrollY() < 0)
405         stretch.setHeight(scrollY());
406     else if (scrollY() > contentsHeight() - visibleContentRect().height())
407         stretch.setHeight(scrollY() - (contentsHeight() - visibleContentRect().height()));
408
409     if (scrollX() < 0)
410         stretch.setWidth(scrollX());
411     else if (scrollX() > contentsWidth() - visibleContentRect().width())
412         stretch.setWidth(scrollX() - (contentsWidth() - visibleContentRect().width()));
413
414     return stretch;
415 }
416
417 void ScrollView::windowResizerRectChanged()
418 {
419     if (platformWidget())
420         return;
421
422     updateScrollbars(scrollOffset());
423 }
424
425 static const unsigned cMaxUpdateScrollbarsPass = 2;
426
427 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
428 {
429     if (m_inUpdateScrollbars || prohibitsScrolling() || delegatesScrolling() || platformWidget())
430         return;
431
432     // If we came in here with the view already needing a layout, then go ahead and do that
433     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
434     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
435     if (!m_scrollbarsSuppressed) {
436         m_inUpdateScrollbars = true;
437         visibleContentsResized();
438         m_inUpdateScrollbars = false;
439     }
440
441     bool hasHorizontalScrollbar = m_horizontalScrollbar;
442     bool hasVerticalScrollbar = m_verticalScrollbar;
443     
444     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
445     bool newHasVerticalScrollbar = hasVerticalScrollbar;
446    
447     ScrollbarMode hScroll = m_horizontalScrollbarMode;
448     ScrollbarMode vScroll = m_verticalScrollbarMode;
449
450     if (hScroll != ScrollbarAuto)
451         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
452     if (vScroll != ScrollbarAuto)
453         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
454
455     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
456         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
457             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
458         if (hasVerticalScrollbar != newHasVerticalScrollbar)
459             setHasVerticalScrollbar(newHasVerticalScrollbar);
460     } else {
461         bool sendContentResizedNotification = false;
462         
463         IntSize docSize = contentsSize();
464         IntSize frameSize = frameRect().size();
465
466         if (hScroll == ScrollbarAuto) {
467             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
468             if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
469                 newHasHorizontalScrollbar = false;
470         }
471         if (vScroll == ScrollbarAuto) {
472             newHasVerticalScrollbar = docSize.height() > visibleHeight();
473             if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
474                 newHasVerticalScrollbar = false;
475         }
476
477         // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
478         // try to both gain/lose a scrollbar in the same pass.
479         if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
480             newHasVerticalScrollbar = false;
481         if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
482             newHasHorizontalScrollbar = false;
483
484         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
485             if (m_scrollOrigin.y() && !newHasHorizontalScrollbar)
486                 m_scrollOrigin.setY(m_scrollOrigin.y() - m_horizontalScrollbar->height());
487             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
488             sendContentResizedNotification = true;
489         }
490
491         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
492             if (m_scrollOrigin.x() && !newHasVerticalScrollbar)
493                 m_scrollOrigin.setX(m_scrollOrigin.x() - m_verticalScrollbar->width());
494             setHasVerticalScrollbar(newHasVerticalScrollbar);
495             sendContentResizedNotification = true;
496         }
497
498         if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
499             m_updateScrollbarsPass++;
500             contentsResized();
501             visibleContentsResized();
502             IntSize newDocSize = contentsSize();
503             if (newDocSize == docSize) {
504                 // The layout with the new scroll state had no impact on
505                 // the document's overall size, so updateScrollbars didn't get called.
506                 // Recur manually.
507                 updateScrollbars(desiredOffset);
508             }
509             m_updateScrollbarsPass--;
510         }
511     }
512     
513     // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
514     // doing it multiple times).
515     if (m_updateScrollbarsPass)
516         return;
517
518     m_inUpdateScrollbars = true;
519
520     IntPoint scrollPoint = adjustScrollPositionWithinRange(IntPoint(desiredOffset));
521     IntSize scroll(scrollPoint.x(), scrollPoint.y());
522
523     if (m_horizontalScrollbar) {
524         int clientWidth = visibleWidth();
525         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
526         int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
527         IntRect oldRect(m_horizontalScrollbar->frameRect());
528         IntRect hBarRect = IntRect(0,
529                                    height() - m_horizontalScrollbar->height(),
530                                    width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
531                                    m_horizontalScrollbar->height());
532         m_horizontalScrollbar->setFrameRect(hBarRect);
533         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
534             m_horizontalScrollbar->invalidate();
535
536         if (m_scrollbarsSuppressed)
537             m_horizontalScrollbar->setSuppressInvalidation(true);
538         m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
539         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
540         if (m_scrollbarsSuppressed)
541             m_horizontalScrollbar->setSuppressInvalidation(false); 
542     } 
543
544     if (m_verticalScrollbar) {
545         int clientHeight = visibleHeight();
546         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
547         int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
548         IntRect oldRect(m_verticalScrollbar->frameRect());
549         IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(), 
550                                    0,
551                                    m_verticalScrollbar->width(),
552                                    height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
553         m_verticalScrollbar->setFrameRect(vBarRect);
554         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
555             m_verticalScrollbar->invalidate();
556
557         if (m_scrollbarsSuppressed)
558             m_verticalScrollbar->setSuppressInvalidation(true);
559         m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
560         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
561         if (m_scrollbarsSuppressed)
562             m_verticalScrollbar->setSuppressInvalidation(false);
563     }
564
565     if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
566         frameRectsChanged();
567         updateScrollCorner();
568     }
569
570     ScrollableArea::scrollToOffsetWithoutAnimation(FloatPoint(scroll.width() + m_scrollOrigin.x(), scroll.height() + m_scrollOrigin.y()));
571
572     m_inUpdateScrollbars = false;
573 }
574
575 const int panIconSizeLength = 16;
576
577 void ScrollView::scrollContents(const IntSize& scrollDelta)
578 {
579     if (!hostWindow())
580         return;
581
582     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
583     // with the clip rect every time to keep it smooth.
584     IntRect clipRect = windowClipRect();
585     IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
586     IntRect updateRect = clipRect;
587     updateRect.intersect(scrollViewRect);
588
589     // Invalidate the window (not the backing store).
590     hostWindow()->invalidateWindow(updateRect, false /*immediate*/);
591
592     if (m_drawPanScrollIcon) {
593         // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
594         // https://bugs.webkit.org/show_bug.cgi?id=47837
595         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
596         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
597         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
598         panScrollIconDirtyRect.intersect(clipRect);
599         hostWindow()->invalidateContentsAndWindow(panScrollIconDirtyRect, false /*immediate*/);
600     }
601
602     if (canBlitOnScroll()) { // The main frame can just blit the WebView window
603         // FIXME: Find a way to scroll subframes with this faster path
604         if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
605             scrollContentsSlowPath(updateRect);
606     } else { 
607        // We need to go ahead and repaint the entire backing store.  Do it now before moving the
608        // windowed plugins.
609        scrollContentsSlowPath(updateRect);
610     }
611
612     // This call will move children with native widgets (plugins) and invalidate them as well.
613     frameRectsChanged();
614
615     // Now blit the backingstore into the window which should be very fast.
616     hostWindow()->invalidateWindow(IntRect(), true);
617 }
618
619 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
620 {
621     hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
622     return true;
623 }
624
625 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
626 {
627     hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
628 }
629
630 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
631 {
632     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
633     return viewPoint + scrollOffset();
634 }
635
636 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
637 {
638     IntPoint viewPoint = contentsPoint - scrollOffset();
639     return convertToContainingWindow(viewPoint);  
640 }
641
642 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
643 {
644     IntRect viewRect = convertFromContainingWindow(windowRect);
645     viewRect.move(scrollOffset());
646     return viewRect;
647 }
648
649 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
650 {
651     IntRect viewRect = contentsRect;
652     viewRect.move(-scrollOffset());
653     return convertToContainingWindow(viewRect);
654 }
655
656 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
657 {
658     if (platformWidget())
659         return platformContentsToScreen(rect);
660     if (!hostWindow())
661         return IntRect();
662     return hostWindow()->windowToScreen(contentsToWindow(rect));
663 }
664
665 IntPoint ScrollView::screenToContents(const IntPoint& point) const
666 {
667     if (platformWidget())
668         return platformScreenToContents(point);
669     if (!hostWindow())
670         return IntPoint();
671     return windowToContents(hostWindow()->screenToWindow(point));
672 }
673
674 bool ScrollView::containsScrollbarsAvoidingResizer() const
675 {
676     return !m_scrollbarsAvoidingResizer;
677 }
678
679 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
680 {
681     int oldCount = m_scrollbarsAvoidingResizer;
682     m_scrollbarsAvoidingResizer += overlapDelta;
683     if (parent())
684         parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
685     else if (!scrollbarsSuppressed()) {
686         // If we went from n to 0 or from 0 to n and we're the outermost view,
687         // we need to invalidate the windowResizerRect(), since it will now need to paint
688         // differently.
689         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
690             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
691             invalidateRect(windowResizerRect());
692     }
693 }
694
695 void ScrollView::setParent(ScrollView* parentView)
696 {
697     if (parentView == parent())
698         return;
699
700     if (m_scrollbarsAvoidingResizer && parent())
701         parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
702
703     Widget::setParent(parentView);
704
705     if (m_scrollbarsAvoidingResizer && parent())
706         parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
707 }
708
709 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
710 {
711     if (suppressed == m_scrollbarsSuppressed)
712         return;
713
714     m_scrollbarsSuppressed = suppressed;
715
716     if (platformWidget())
717         platformSetScrollbarsSuppressed(repaintOnUnsuppress);
718     else if (repaintOnUnsuppress && !suppressed) {
719         if (m_horizontalScrollbar)
720             m_horizontalScrollbar->invalidate();
721         if (m_verticalScrollbar)
722             m_verticalScrollbar->invalidate();
723
724         // Invalidate the scroll corner too on unsuppress.
725         invalidateRect(scrollCornerRect());
726     }
727 }
728
729 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
730 {
731     if (platformWidget())
732         return 0;
733
734     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
735     if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
736         return m_horizontalScrollbar.get();
737     if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
738         return m_verticalScrollbar.get();
739     return 0;
740 }
741
742 void ScrollView::wheelEvent(PlatformWheelEvent& e)
743 {
744     // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
745 #if PLATFORM(WX)
746     if (!canHaveScrollbars()) {
747 #else
748     if (!canHaveScrollbars() || platformWidget()) {
749 #endif
750         return;
751     }
752
753     ScrollableArea::handleWheelEvent(e);
754 }
755
756 #if ENABLE(GESTURE_EVENTS)
757 void ScrollView::gestureEvent(const PlatformGestureEvent& gestureEvent)
758 {
759     if (platformWidget())
760         return;
761
762     ScrollableArea::handleGestureEvent(gestureEvent);
763 }
764 #endif
765
766 void ScrollView::setFrameRect(const IntRect& newRect)
767 {
768     IntRect oldRect = frameRect();
769     
770     if (newRect == oldRect)
771         return;
772
773     Widget::setFrameRect(newRect);
774
775     if (platformWidget())
776         return;
777     
778     if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
779         updateScrollbars(m_scrollOffset);
780         if (!m_useFixedLayout)
781             contentsResized();
782     }
783
784     frameRectsChanged();
785 }
786
787 void ScrollView::frameRectsChanged()
788 {
789     if (platformWidget())
790         return;
791
792     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
793     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
794         (*current)->frameRectsChanged();
795 }
796
797 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
798 {
799     IntRect paintRect = rect;
800     if (clipsRepaints() && !paintsEntireContents())
801         paintRect.intersect(visibleContentRect());
802     if (paintRect.isEmpty())
803         return;
804
805     if (platformWidget()) {
806         platformRepaintContentRectangle(paintRect, now);
807         return;
808     }
809
810     if (hostWindow())
811         hostWindow()->invalidateContentsAndWindow(contentsToWindow(paintRect), now /*immediate*/);
812 }
813
814 IntRect ScrollView::scrollCornerRect() const
815 {
816     IntRect cornerRect;
817
818     if (ScrollbarTheme::nativeTheme()->usesOverlayScrollbars())
819         return cornerRect;
820
821     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
822         cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
823                                  height() - m_horizontalScrollbar->height(),
824                                  width() - m_horizontalScrollbar->width(),
825                                  m_horizontalScrollbar->height()));
826     }
827
828     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
829         cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
830                                  m_verticalScrollbar->height(),
831                                  m_verticalScrollbar->width(),
832                                  height() - m_verticalScrollbar->height()));
833     }
834     
835     return cornerRect;
836 }
837
838 void ScrollView::updateScrollCorner()
839 {
840 }
841
842 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
843 {
844     ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
845 }
846
847 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
848 {
849     if (m_horizontalScrollbar)
850         m_horizontalScrollbar->paint(context, rect);
851     if (m_verticalScrollbar)
852         m_verticalScrollbar->paint(context, rect);
853
854     paintScrollCorner(context, scrollCornerRect());
855 }
856
857 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
858 {
859     static Image* panScrollIcon = Image::loadPlatformResource("panIcon").releaseRef();
860     context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, m_panScrollIconPoint);
861 }
862
863 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
864 {
865     if (platformWidget()) {
866         Widget::paint(context, rect);
867         return;
868     }
869
870     if (context->paintingDisabled() && !context->updatingControlTints())
871         return;
872
873     IntRect documentDirtyRect = rect;
874     documentDirtyRect.intersect(frameRect());
875
876     context->save();
877
878     context->translate(x(), y());
879     documentDirtyRect.move(-x(), -y());
880
881     if (!paintsEntireContents()) {
882         context->translate(-scrollX(), -scrollY());
883         documentDirtyRect.move(scrollX(), scrollY());
884
885         context->clip(visibleContentRect());
886     }
887
888     paintContents(context, documentDirtyRect);
889
890     context->restore();
891
892     IntRect horizontalOverhangRect;
893     IntRect verticalOverhangRect;
894     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
895
896     if (!horizontalOverhangRect.isEmpty() || !verticalOverhangRect.isEmpty())
897         paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect);
898
899     // Now paint the scrollbars.
900     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
901         context->save();
902         IntRect scrollViewDirtyRect = rect;
903         scrollViewDirtyRect.intersect(frameRect());
904         context->translate(x(), y());
905         scrollViewDirtyRect.move(-x(), -y());
906
907         paintScrollbars(context, scrollViewDirtyRect);
908
909         context->restore();
910     }
911
912     // Paint the panScroll Icon
913     if (m_drawPanScrollIcon)
914         paintPanScrollIcon(context);
915 }
916
917 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
918 {
919     if (scrollY() < 0) {
920         horizontalOverhangRect = frameRect();
921         horizontalOverhangRect.setHeight(-scrollY());
922     } else if (scrollY() > contentsHeight() - visibleContentRect(true).height()) {
923         int height = scrollY() - (contentsHeight() - visibleContentRect(true).height());
924         horizontalOverhangRect = frameRect();
925         horizontalOverhangRect.setY(frameRect().bottom() - height);
926         horizontalOverhangRect.setHeight(height);
927     }
928
929     if (scrollX() < 0) {
930         verticalOverhangRect.setWidth(-scrollX());
931         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height());
932         verticalOverhangRect.setX(frameRect().x());
933         if (horizontalOverhangRect.y() == frameRect().y())
934             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
935         else
936             verticalOverhangRect.setY(frameRect().y());
937     } else if (scrollX() > contentsWidth() - visibleContentRect(true).width()) {
938         int width = scrollX() - (contentsWidth() - visibleContentRect(true).width());
939         verticalOverhangRect.setWidth(width);
940         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height());
941         verticalOverhangRect.setX(frameRect().right() - width);
942         if (horizontalOverhangRect.y() == frameRect().y())
943             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
944         else
945             verticalOverhangRect.setY(frameRect().y());
946     }
947 }
948
949 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect)
950 {
951     // FIXME: This should be checking the dirty rect.
952
953     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
954     if (!horizontalOverhangRect.isEmpty())
955         context->fillRect(horizontalOverhangRect);
956
957     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
958     if (!verticalOverhangRect.isEmpty())
959         context->fillRect(verticalOverhangRect);
960 }
961
962 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
963 {
964     if (!scrollbarCornerPresent())
965         return false;
966
967     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
968
969     if (m_horizontalScrollbar) {
970         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
971         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
972         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
973
974         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
975     }
976
977     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
978     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
979     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
980     
981     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
982 }
983
984 bool ScrollView::scrollbarCornerPresent() const
985 {
986     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
987            (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
988 }
989
990 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
991 {
992     // Scrollbars won't be transformed within us
993     IntRect newRect = localRect;
994     newRect.move(scrollbar->x(), scrollbar->y());
995     return newRect;
996 }
997
998 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
999 {
1000     IntRect newRect = parentRect;
1001     // Scrollbars won't be transformed within us
1002     newRect.move(-scrollbar->x(), -scrollbar->y());
1003     return newRect;
1004 }
1005
1006 // FIXME: test these on windows
1007 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1008 {
1009     // Scrollbars won't be transformed within us
1010     IntPoint newPoint = localPoint;
1011     newPoint.move(scrollbar->x(), scrollbar->y());
1012     return newPoint;
1013 }
1014
1015 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1016 {
1017     IntPoint newPoint = parentPoint;
1018     // Scrollbars won't be transformed within us
1019     newPoint.move(-scrollbar->x(), -scrollbar->y());
1020     return newPoint;
1021 }
1022
1023 void ScrollView::setParentVisible(bool visible)
1024 {
1025     if (isParentVisible() == visible)
1026         return;
1027     
1028     Widget::setParentVisible(visible);
1029
1030     if (!isSelfVisible())
1031         return;
1032         
1033     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1034     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1035         (*it)->setParentVisible(visible);
1036 }
1037
1038 void ScrollView::show()
1039 {
1040     if (!isSelfVisible()) {
1041         setSelfVisible(true);
1042         if (isParentVisible()) {
1043             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1044             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1045                 (*it)->setParentVisible(true);
1046         }
1047     }
1048
1049     Widget::show();
1050 }
1051
1052 void ScrollView::hide()
1053 {
1054     if (isSelfVisible()) {
1055         if (isParentVisible()) {
1056             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1057             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1058                 (*it)->setParentVisible(false);
1059         }
1060         setSelfVisible(false);
1061     }
1062
1063     Widget::hide();
1064 }
1065
1066 bool ScrollView::isOffscreen() const
1067 {
1068     if (platformWidget())
1069         return platformIsOffscreen();
1070     
1071     if (!isVisible())
1072         return true;
1073     
1074     // FIXME: Add a HostWindow::isOffscreen method here.  Since only Mac implements this method
1075     // currently, we can add the method when the other platforms decide to implement this concept.
1076     return false;
1077 }
1078
1079
1080 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1081 {
1082     if (!hostWindow())
1083         return;
1084     m_drawPanScrollIcon = true;    
1085     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1086     hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1087 }
1088
1089 void ScrollView::removePanScrollIcon()
1090 {
1091     if (!hostWindow())
1092         return;
1093     m_drawPanScrollIcon = false; 
1094     hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
1095 }
1096
1097 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePosition)
1098 {
1099     if (m_scrollOrigin == origin)
1100         return;
1101
1102     m_scrollOrigin = origin;
1103
1104     if (platformWidget()) {
1105         platformSetScrollOrigin(origin, updatePosition);
1106         return;
1107     }
1108     
1109     // Update if the scroll origin changes, since our position will be different if the content size did not change.
1110     if (updatePosition)
1111         updateScrollbars(scrollOffset());
1112 }
1113
1114 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(EFL)
1115
1116 void ScrollView::platformInit()
1117 {
1118 }
1119
1120 void ScrollView::platformDestroy()
1121 {
1122 }
1123
1124 #endif
1125
1126 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
1127
1128 void ScrollView::platformAddChild(Widget*)
1129 {
1130 }
1131
1132 void ScrollView::platformRemoveChild(Widget*)
1133 {
1134 }
1135
1136 #endif
1137
1138 #if !PLATFORM(MAC)
1139
1140 void ScrollView::platformSetScrollbarsSuppressed(bool)
1141 {
1142 }
1143
1144 void ScrollView::platformSetScrollOrigin(const IntPoint&, bool updatePosition)
1145 {
1146 }
1147
1148 #endif
1149
1150 #if !PLATFORM(MAC) && !PLATFORM(WX)
1151
1152 void ScrollView::platformSetScrollbarModes()
1153 {
1154 }
1155
1156 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
1157 {
1158     horizontal = ScrollbarAuto;
1159     vertical = ScrollbarAuto;
1160 }
1161
1162 void ScrollView::platformSetCanBlitOnScroll(bool)
1163 {
1164 }
1165
1166 bool ScrollView::platformCanBlitOnScroll() const
1167 {
1168     return false;
1169 }
1170
1171 IntRect ScrollView::platformVisibleContentRect(bool) const
1172 {
1173     return IntRect();
1174 }
1175
1176 IntSize ScrollView::platformContentsSize() const
1177 {
1178     return IntSize();
1179 }
1180
1181 void ScrollView::platformSetContentsSize()
1182 {
1183 }
1184
1185 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1186 {
1187     return rect;
1188 }
1189
1190 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1191 {
1192     return point;
1193 }
1194
1195 void ScrollView::platformSetScrollPosition(const IntPoint&)
1196 {
1197 }
1198
1199 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1200 {
1201     return true;
1202 }
1203
1204 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1205 {
1206 }
1207
1208 bool ScrollView::platformIsOffscreen() const
1209 {
1210     return false;
1211 }
1212
1213 #endif
1214
1215 }