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