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