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