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