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