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