Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / scroll / 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 "platform/scroll/ScrollView.h"
28
29 #include "platform/graphics/GraphicsContextStateSaver.h"
30 #include "platform/graphics/GraphicsLayer.h"
31 #include "platform/HostWindow.h"
32 #include "platform/scroll/ScrollbarTheme.h"
33 #include "wtf/StdLibExtras.h"
34
35 using namespace std;
36
37 namespace WebCore {
38
39 ScrollView::ScrollView()
40     : m_horizontalScrollbarMode(ScrollbarAuto)
41     , m_verticalScrollbarMode(ScrollbarAuto)
42     , m_horizontalScrollbarLock(false)
43     , m_verticalScrollbarLock(false)
44     , m_scrollbarsAvoidingResizer(0)
45     , m_scrollbarsSuppressed(false)
46     , m_inUpdateScrollbars(false)
47     , m_updateScrollbarsPass(0)
48     , m_drawPanScrollIcon(false)
49     , m_paintsEntireContents(false)
50     , m_clipsRepaints(true)
51 {
52 }
53
54 ScrollView::~ScrollView()
55 {
56 }
57
58 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
59 {
60     Widget* child = prpChild.get();
61     ASSERT(child != this && !child->parent());
62     child->setParent(this);
63     m_children.add(prpChild);
64 }
65
66 void ScrollView::removeChild(Widget* child)
67 {
68     ASSERT(child->parent() == this);
69     child->setParent(0);
70     m_children.remove(child);
71 }
72
73 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
74 {
75     if (hasBar && !m_horizontalScrollbar) {
76         m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
77         addChild(m_horizontalScrollbar.get());
78         didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
79         m_horizontalScrollbar->styleChanged();
80     } else if (!hasBar && m_horizontalScrollbar) {
81         willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar);
82         removeChild(m_horizontalScrollbar.get());
83         m_horizontalScrollbar = nullptr;
84     }
85 }
86
87 void ScrollView::setHasVerticalScrollbar(bool hasBar)
88 {
89     if (hasBar && !m_verticalScrollbar) {
90         m_verticalScrollbar = createScrollbar(VerticalScrollbar);
91         addChild(m_verticalScrollbar.get());
92         didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
93         m_verticalScrollbar->styleChanged();
94     } else if (!hasBar && m_verticalScrollbar) {
95         willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar);
96         removeChild(m_verticalScrollbar.get());
97         m_verticalScrollbar = nullptr;
98     }
99 }
100
101 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
102 {
103     return Scrollbar::create(this, orientation, RegularScrollbar);
104 }
105
106 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
107                                    bool horizontalLock, bool verticalLock)
108 {
109     bool needsUpdate = false;
110
111     if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
112         m_horizontalScrollbarMode = horizontalMode;
113         needsUpdate = true;
114     }
115
116     if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
117         m_verticalScrollbarMode = verticalMode;
118         needsUpdate = true;
119     }
120
121     if (horizontalLock)
122         setHorizontalScrollbarLock();
123
124     if (verticalLock)
125         setVerticalScrollbarLock();
126
127     if (!needsUpdate)
128         return;
129
130     updateScrollbars(scrollOffset());
131
132     if (!layerForScrolling())
133         return;
134     blink::WebLayer* layer = layerForScrolling()->platformLayer();
135     if (!layer)
136         return;
137     layer->setUserScrollable(userInputScrollable(HorizontalScrollbar), userInputScrollable(VerticalScrollbar));
138 }
139
140 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
141 {
142     horizontalMode = m_horizontalScrollbarMode;
143     verticalMode = m_verticalScrollbarMode;
144 }
145
146 void ScrollView::setCanHaveScrollbars(bool canScroll)
147 {
148     ScrollbarMode newHorizontalMode;
149     ScrollbarMode newVerticalMode;
150
151     scrollbarModes(newHorizontalMode, newVerticalMode);
152
153     if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
154         newVerticalMode = ScrollbarAuto;
155     else if (!canScroll)
156         newVerticalMode = ScrollbarAlwaysOff;
157
158     if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
159         newHorizontalMode = ScrollbarAuto;
160     else if (!canScroll)
161         newHorizontalMode = ScrollbarAlwaysOff;
162
163     setScrollbarModes(newHorizontalMode, newVerticalMode);
164 }
165
166 bool ScrollView::shouldAttemptToScrollUsingFastPath() const
167 {
168     return true;
169 }
170
171 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
172 {
173     m_paintsEntireContents = paintsEntireContents;
174 }
175
176 void ScrollView::setClipsRepaints(bool clipsRepaints)
177 {
178     m_clipsRepaints = clipsRepaints;
179 }
180
181 IntSize ScrollView::unscaledVisibleContentSize(IncludeScrollbarsInRect scrollbarInclusion) const
182 {
183     return scrollbarInclusion == ExcludeScrollbars ? excludeScrollbars(frameRect().size()) : frameRect().size();
184 }
185
186 IntSize ScrollView::excludeScrollbars(const IntSize& size) const
187 {
188     int verticalScrollbarWidth = 0;
189     int horizontalScrollbarHeight = 0;
190
191     if (Scrollbar* verticalBar = verticalScrollbar())
192         verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
193     if (Scrollbar* horizontalBar = horizontalScrollbar())
194         horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
195
196     return IntSize(max(0, size.width() - verticalScrollbarWidth),
197         max(0, size.height() - horizontalScrollbarHeight));
198
199 }
200
201 IntRect ScrollView::visibleContentRect(IncludeScrollbarsInRect scollbarInclusion) const
202 {
203     FloatSize visibleContentSize = unscaledVisibleContentSize(scollbarInclusion);
204     visibleContentSize.scale(1 / visibleContentScaleFactor());
205     return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize));
206 }
207
208 IntSize ScrollView::contentsSize() const
209 {
210     return m_contentsSize;
211 }
212
213 void ScrollView::setContentsSize(const IntSize& newSize)
214 {
215     if (contentsSize() == newSize)
216         return;
217     m_contentsSize = newSize;
218     updateScrollbars(scrollOffset());
219     updateOverhangAreas();
220 }
221
222 IntPoint ScrollView::maximumScrollPosition() const
223 {
224     IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), contentsHeight() - visibleHeight() - scrollOrigin().y());
225     maximumOffset.clampNegativeToZero();
226     return maximumOffset;
227 }
228
229 IntPoint ScrollView::minimumScrollPosition() const
230 {
231     return IntPoint(-scrollOrigin().x(), -scrollOrigin().y());
232 }
233
234 IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const
235 {
236     if (!constrainsScrollingToContentEdge())
237         return scrollPoint;
238
239     IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
240     newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition());
241     return newScrollPosition;
242 }
243
244 int ScrollView::scrollSize(ScrollbarOrientation orientation) const
245 {
246     Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
247
248     // If no scrollbars are present, the content may still be scrollable.
249     if (!scrollbar) {
250         IntSize scrollSize = m_contentsSize - visibleContentRect().size();
251         scrollSize.clampNegativeToZero();
252         return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height();
253     }
254
255     return scrollbar->totalSize() - scrollbar->visibleSize();
256 }
257
258 void ScrollView::notifyPageThatContentAreaWillPaint() const
259 {
260 }
261
262 void ScrollView::setScrollOffset(const IntPoint& offset)
263 {
264     scrollTo(toIntSize(adjustScrollPositionWithinRange(offset)));
265 }
266
267 void ScrollView::scrollTo(const IntSize& newOffset)
268 {
269     IntSize scrollDelta = newOffset - m_scrollOffset;
270     if (scrollDelta == IntSize())
271         return;
272     m_scrollOffset = newOffset;
273
274     if (scrollbarsSuppressed())
275         return;
276
277     repaintFixedElementsAfterScrolling();
278     scrollContents(scrollDelta);
279     updateFixedElementsAfterScrolling();
280 }
281
282 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
283 {
284     IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
285
286     if (newScrollPosition == scrollPosition())
287         return;
288
289     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
290 }
291
292 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
293 {
294     ScrollDirection physicalDirection =
295         toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument());
296
297     return ScrollableArea::scroll(physicalDirection, granularity);
298 }
299
300 IntSize ScrollView::overhangAmount() const
301 {
302     IntSize stretch;
303
304     IntPoint currentScrollPosition = scrollPosition();
305     IntPoint minScrollPosition = minimumScrollPosition();
306     IntPoint maxScrollPosition = maximumScrollPosition();
307
308     if (currentScrollPosition.x() < minScrollPosition.x())
309         stretch.setWidth(currentScrollPosition.x() - minScrollPosition.x());
310     if (currentScrollPosition.x() > maxScrollPosition.x())
311         stretch.setWidth(currentScrollPosition.x() - maxScrollPosition.x());
312
313     if (currentScrollPosition.y() < minScrollPosition.y())
314         stretch.setHeight(currentScrollPosition.y() - minScrollPosition.y());
315     if (currentScrollPosition.y() > maxScrollPosition.y())
316         stretch.setHeight(currentScrollPosition.y() - maxScrollPosition.y());
317
318     return stretch;
319 }
320
321 void ScrollView::windowResizerRectChanged()
322 {
323     updateScrollbars(scrollOffset());
324 }
325
326 static const unsigned cMaxUpdateScrollbarsPass = 2;
327
328 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
329 {
330     if (m_inUpdateScrollbars)
331         return;
332
333     // If we came in here with the view already needing a layout, then go ahead and do that
334     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
335     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
336     if (!m_scrollbarsSuppressed) {
337         m_inUpdateScrollbars = true;
338         scrollbarExistenceDidChange();
339         m_inUpdateScrollbars = false;
340     }
341
342     IntRect oldScrollCornerRect = scrollCornerRect();
343
344     bool hasHorizontalScrollbar = m_horizontalScrollbar;
345     bool hasVerticalScrollbar = m_verticalScrollbar;
346
347     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
348     bool newHasVerticalScrollbar = hasVerticalScrollbar;
349
350     ScrollbarMode hScroll = m_horizontalScrollbarMode;
351     ScrollbarMode vScroll = m_verticalScrollbarMode;
352
353     if (hScroll != ScrollbarAuto)
354         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
355     if (vScroll != ScrollbarAuto)
356         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
357
358     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
359         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
360             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
361         if (hasVerticalScrollbar != newHasVerticalScrollbar)
362             setHasVerticalScrollbar(newHasVerticalScrollbar);
363     } else {
364         bool scrollbarExistenceChanged = false;
365
366         IntSize docSize = contentsSize();
367         IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size();
368
369         bool scrollbarsAreOverlay = ScrollbarTheme::theme()->usesOverlayScrollbars();
370
371         if (hScroll == ScrollbarAuto) {
372             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
373             if (!scrollbarsAreOverlay && newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
374                 newHasHorizontalScrollbar = false;
375         }
376         if (vScroll == ScrollbarAuto) {
377             newHasVerticalScrollbar = docSize.height() > visibleHeight();
378             if (!scrollbarsAreOverlay && newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
379                 newHasVerticalScrollbar = false;
380         }
381
382         if (!scrollbarsAreOverlay) {
383             // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
384             // try to both gain/lose a scrollbar in the same pass.
385             if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
386                 newHasVerticalScrollbar = false;
387             if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
388                 newHasHorizontalScrollbar = false;
389         }
390
391         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
392             scrollbarExistenceChanged = true;
393             if (scrollOrigin().y() && !newHasHorizontalScrollbar && !scrollbarsAreOverlay)
394                 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->height()));
395             if (hasHorizontalScrollbar)
396                 m_horizontalScrollbar->invalidate();
397             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
398         }
399
400         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
401             scrollbarExistenceChanged = true;
402             if (scrollOrigin().x() && !newHasVerticalScrollbar && !scrollbarsAreOverlay)
403                 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->width(), scrollOrigin().y()));
404             if (hasVerticalScrollbar)
405                 m_verticalScrollbar->invalidate();
406             setHasVerticalScrollbar(newHasVerticalScrollbar);
407         }
408
409         if (scrollbarExistenceChanged) {
410             if (scrollbarsAreOverlay) {
411                 // Synchronize status of scrollbar layers if necessary.
412                 m_inUpdateScrollbars = true;
413                 scrollbarExistenceDidChange();
414                 m_inUpdateScrollbars = false;
415             } else if (m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
416                 m_updateScrollbarsPass++;
417                 contentsResized();
418                 scrollbarExistenceDidChange();
419                 IntSize newDocSize = contentsSize();
420                 if (newDocSize == docSize) {
421                     // The layout with the new scroll state had no impact on
422                     // the document's overall size, so updateScrollbars didn't get called.
423                     // Recur manually.
424                     updateScrollbars(desiredOffset);
425                 }
426                 m_updateScrollbarsPass--;
427             }
428         }
429     }
430
431     // Set up the range, 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
438     if (m_horizontalScrollbar) {
439         int clientWidth = visibleWidth();
440         IntRect oldRect(m_horizontalScrollbar->frameRect());
441         IntRect hBarRect((shouldPlaceVerticalScrollbarOnLeft() && m_verticalScrollbar) ? m_verticalScrollbar->width() : 0,
442                         height() - m_horizontalScrollbar->height(),
443                         width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
444                         m_horizontalScrollbar->height());
445         m_horizontalScrollbar->setFrameRect(hBarRect);
446         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
447             m_horizontalScrollbar->invalidate();
448
449         if (m_scrollbarsSuppressed)
450             m_horizontalScrollbar->setSuppressInvalidation(true);
451         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
452         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
453         if (m_scrollbarsSuppressed)
454             m_horizontalScrollbar->setSuppressInvalidation(false);
455     }
456
457     if (m_verticalScrollbar) {
458         int clientHeight = visibleHeight();
459         IntRect oldRect(m_verticalScrollbar->frameRect());
460         IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
461                          0,
462                          m_verticalScrollbar->width(),
463                          height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
464         m_verticalScrollbar->setFrameRect(vBarRect);
465         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
466             m_verticalScrollbar->invalidate();
467
468         if (m_scrollbarsSuppressed)
469             m_verticalScrollbar->setSuppressInvalidation(true);
470         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
471         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
472         if (m_scrollbarsSuppressed)
473             m_verticalScrollbar->setSuppressInvalidation(false);
474     }
475
476     if (hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar) {
477         // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
478         frameRectsChanged();
479         positionScrollbarLayers();
480         updateScrollCorner();
481         if (!m_horizontalScrollbar && !m_verticalScrollbar)
482             invalidateScrollCornerRect(oldScrollCornerRect);
483     }
484
485     IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
486     if (!isRubberBandInProgress())
487         adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
488
489     if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
490         ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition);
491         resetScrollOriginChanged();
492     }
493
494     // Make sure the scrollbar offsets are up to date.
495     if (m_horizontalScrollbar)
496         m_horizontalScrollbar->offsetDidChange();
497     if (m_verticalScrollbar)
498         m_verticalScrollbar->offsetDidChange();
499
500     m_inUpdateScrollbars = false;
501 }
502
503 const int panIconSizeLength = 16;
504
505 IntRect ScrollView::rectToCopyOnScroll() const
506 {
507     IntRect scrollViewRect = convertToRootView(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight()));
508     if (hasOverlayScrollbars()) {
509         int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
510         int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
511
512         scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
513         scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
514     }
515     return scrollViewRect;
516 }
517
518 void ScrollView::scrollContents(const IntSize& scrollDelta)
519 {
520     HostWindow* window = hostWindow();
521     if (!window)
522         return;
523
524     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
525     // with the clip rect every time to keep it smooth.
526     IntRect clipRect = windowClipRect();
527     IntRect scrollViewRect = rectToCopyOnScroll();
528     IntRect updateRect = clipRect;
529     updateRect.intersect(scrollViewRect);
530
531     if (m_drawPanScrollIcon) {
532         // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
533         // https://bugs.webkit.org/show_bug.cgi?id=47837
534         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
535         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
536         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
537         panScrollIconDirtyRect.intersect(clipRect);
538         window->invalidateContentsAndRootView(panScrollIconDirtyRect);
539     }
540
541     if (!shouldAttemptToScrollUsingFastPath() || !scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
542         scrollContentsSlowPath(updateRect);
543
544     // Invalidate the overhang areas if they are visible.
545     updateOverhangAreas();
546
547     // This call will move children with native widgets (plugins) and invalidate them as well.
548     frameRectsChanged();
549 }
550
551 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
552 {
553     hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
554     return true;
555 }
556
557 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
558 {
559     hostWindow()->invalidateContentsForSlowScroll(updateRect);
560 }
561
562 IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
563 {
564     IntPoint viewPoint = convertFromRootView(rootViewPoint);
565     return viewPoint + scrollOffset();
566 }
567
568 IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
569 {
570     IntPoint viewPoint = contentsPoint - scrollOffset();
571     return convertToRootView(viewPoint);
572 }
573
574 IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
575 {
576     IntRect viewRect = convertFromRootView(rootViewRect);
577     viewRect.move(scrollOffset());
578     return viewRect;
579 }
580
581 IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
582 {
583     IntRect viewRect = contentsRect;
584     viewRect.move(-scrollOffset());
585     return convertToRootView(viewRect);
586 }
587
588 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
589 {
590     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
591     return viewPoint + scrollOffset();
592 }
593
594 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
595 {
596     IntPoint viewPoint = contentsPoint - scrollOffset();
597     return convertToContainingWindow(viewPoint);
598 }
599
600 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
601 {
602     IntRect viewRect = convertFromContainingWindow(windowRect);
603     viewRect.move(scrollOffset());
604     return viewRect;
605 }
606
607 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
608 {
609     IntRect viewRect = contentsRect;
610     viewRect.move(-scrollOffset());
611     return convertToContainingWindow(viewRect);
612 }
613
614 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
615 {
616     HostWindow* window = hostWindow();
617     if (!window)
618         return IntRect();
619     return window->rootViewToScreen(contentsToRootView(rect));
620 }
621
622 bool ScrollView::containsScrollbarsAvoidingResizer() const
623 {
624     return !m_scrollbarsAvoidingResizer;
625 }
626
627 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
628 {
629     int oldCount = m_scrollbarsAvoidingResizer;
630     m_scrollbarsAvoidingResizer += overlapDelta;
631     if (parent())
632         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(overlapDelta);
633     else if (!scrollbarsSuppressed()) {
634         // If we went from n to 0 or from 0 to n and we're the outermost view,
635         // we need to invalidate the windowResizerRect(), since it will now need to paint
636         // differently.
637         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
638             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
639             invalidateRect(windowResizerRect());
640     }
641 }
642
643 void ScrollView::setParent(Widget* parentView)
644 {
645     if (parentView == parent())
646         return;
647
648     if (m_scrollbarsAvoidingResizer && parent())
649         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
650
651     Widget::setParent(parentView);
652
653     if (m_scrollbarsAvoidingResizer && parent())
654         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
655 }
656
657 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
658 {
659     if (suppressed == m_scrollbarsSuppressed)
660         return;
661
662     m_scrollbarsSuppressed = suppressed;
663
664     if (repaintOnUnsuppress && !suppressed) {
665         if (m_horizontalScrollbar)
666             m_horizontalScrollbar->invalidate();
667         if (m_verticalScrollbar)
668             m_verticalScrollbar->invalidate();
669
670         // Invalidate the scroll corner too on unsuppress.
671         invalidateRect(scrollCornerRect());
672     }
673 }
674
675 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
676 {
677     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
678     if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
679         return m_horizontalScrollbar.get();
680     if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
681         return m_verticalScrollbar.get();
682     return 0;
683 }
684
685 void ScrollView::setFrameRect(const IntRect& newRect)
686 {
687     IntRect oldRect = frameRect();
688
689     if (newRect == oldRect)
690         return;
691
692     Widget::setFrameRect(newRect);
693
694     updateScrollbars(scrollOffset());
695
696     frameRectsChanged();
697 }
698
699 void ScrollView::frameRectsChanged()
700 {
701     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
702     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
703         (*current)->frameRectsChanged();
704 }
705
706 static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
707 {
708     if (!graphicsLayer || !scrollbar)
709         return;
710
711     IntRect scrollbarRect = scrollbar->frameRect();
712     graphicsLayer->setPosition(scrollbarRect.location());
713
714     if (scrollbarRect.size() == graphicsLayer->size())
715         return;
716
717     graphicsLayer->setSize(scrollbarRect.size());
718
719     if (graphicsLayer->hasContentsLayer()) {
720         graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
721         return;
722     }
723
724     graphicsLayer->setDrawsContent(true);
725     graphicsLayer->setNeedsDisplay();
726 }
727
728 static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
729 {
730     if (!graphicsLayer)
731         return;
732     graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
733     graphicsLayer->setPosition(cornerRect.location());
734     if (cornerRect.size() != graphicsLayer->size())
735         graphicsLayer->setNeedsDisplay();
736     graphicsLayer->setSize(cornerRect.size());
737 }
738
739 void ScrollView::positionScrollbarLayers()
740 {
741     positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
742     positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
743     positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
744 }
745
746 bool ScrollView::userInputScrollable(ScrollbarOrientation orientation) const
747 {
748     ScrollbarMode mode = (orientation == HorizontalScrollbar) ?
749         m_horizontalScrollbarMode : m_verticalScrollbarMode;
750
751     return mode == ScrollbarAuto || mode == ScrollbarAlwaysOn;
752 }
753
754 bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const
755 {
756     return false;
757 }
758
759 void ScrollView::repaintContentRectangle(const IntRect& rect)
760 {
761     IntRect paintRect = rect;
762     if (clipsRepaints() && !paintsEntireContents())
763         paintRect.intersect(visibleContentRect());
764     if (paintRect.isEmpty())
765         return;
766
767     if (HostWindow* window = hostWindow())
768         window->invalidateContentsAndRootView(contentsToWindow(paintRect));
769 }
770
771 IntRect ScrollView::scrollCornerRect() const
772 {
773     IntRect cornerRect;
774
775     if (hasOverlayScrollbars())
776         return cornerRect;
777
778     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
779         cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
780                                  height() - m_horizontalScrollbar->height(),
781                                  width() - m_horizontalScrollbar->width(),
782                                  m_horizontalScrollbar->height()));
783     }
784
785     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
786         cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
787                                  m_verticalScrollbar->height(),
788                                  m_verticalScrollbar->width(),
789                                  height() - m_verticalScrollbar->height()));
790     }
791
792     return cornerRect;
793 }
794
795 bool ScrollView::isScrollCornerVisible() const
796 {
797     return !scrollCornerRect().isEmpty();
798 }
799
800 void ScrollView::scrollbarStyleChanged(int, bool forceUpdate)
801 {
802     if (!forceUpdate)
803         return;
804
805     contentsResized();
806     updateScrollbars(scrollOffset());
807     positionScrollbarLayers();
808 }
809
810 void ScrollView::updateScrollCorner()
811 {
812 }
813
814 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
815 {
816     ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect);
817 }
818
819 void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
820 {
821     bar->paint(context, rect);
822 }
823
824 void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
825 {
826     invalidateRect(rect);
827 }
828
829 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
830 {
831     if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
832         paintScrollbar(context, m_horizontalScrollbar.get(), rect);
833     if (m_verticalScrollbar && !layerForVerticalScrollbar())
834         paintScrollbar(context, m_verticalScrollbar.get(), rect);
835
836     if (layerForScrollCorner())
837         return;
838     paintScrollCorner(context, scrollCornerRect());
839 }
840
841 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
842 {
843     DEFINE_STATIC_REF(Image, panScrollIcon, (Image::loadPlatformResource("panIcon")));
844     IntPoint iconGCPoint = m_panScrollIconPoint;
845     if (parent())
846         iconGCPoint = toScrollView(parent())->windowToContents(iconGCPoint);
847     context->drawImage(panScrollIcon, iconGCPoint);
848 }
849
850 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
851 {
852     if (context->paintingDisabled() && !context->updatingControlTints())
853         return;
854
855     notifyPageThatContentAreaWillPaint();
856
857     IntRect documentDirtyRect = rect;
858     if (!paintsEntireContents()) {
859         IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect().size());
860         documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
861     }
862
863     if (!documentDirtyRect.isEmpty()) {
864         GraphicsContextStateSaver stateSaver(*context);
865
866         context->translate(x(), y());
867         documentDirtyRect.moveBy(-location());
868
869         if (!paintsEntireContents()) {
870             context->translate(-scrollX(), -scrollY());
871             documentDirtyRect.moveBy(scrollPosition());
872
873             context->clip(visibleContentRect());
874         }
875
876         paintContents(context, documentDirtyRect);
877     }
878
879     calculateAndPaintOverhangAreas(context, rect);
880
881     // Now paint the scrollbars.
882     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
883         GraphicsContextStateSaver stateSaver(*context);
884         IntRect scrollViewDirtyRect = rect;
885         IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size());
886         scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
887         context->translate(x(), y());
888         scrollViewDirtyRect.moveBy(-location());
889         context->clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
890
891         paintScrollbars(context, scrollViewDirtyRect);
892     }
893
894     // Paint the panScroll Icon
895     if (m_drawPanScrollIcon)
896         paintPanScrollIcon(context);
897 }
898
899 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
900 {
901     int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
902         ? verticalScrollbar()->width() : 0;
903     int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
904         ? horizontalScrollbar()->height() : 0;
905
906     int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
907     if (physicalScrollY < 0) {
908         horizontalOverhangRect = frameRect();
909         horizontalOverhangRect.setHeight(-physicalScrollY);
910         horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
911     } else if (contentsHeight() && physicalScrollY > contentsHeight() - visibleHeight()) {
912         int height = physicalScrollY - (contentsHeight() - visibleHeight());
913         horizontalOverhangRect = frameRect();
914         horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
915         horizontalOverhangRect.setHeight(height);
916         horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
917     }
918
919     int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
920     if (physicalScrollX < 0) {
921         verticalOverhangRect.setWidth(-physicalScrollX);
922         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
923         verticalOverhangRect.setX(frameRect().x());
924         if (horizontalOverhangRect.y() == frameRect().y())
925             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
926         else
927             verticalOverhangRect.setY(frameRect().y());
928     } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
929         int width = physicalScrollX - (contentsWidth() - visibleWidth());
930         verticalOverhangRect.setWidth(width);
931         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
932         verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
933         if (horizontalOverhangRect.y() == frameRect().y())
934             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
935         else
936             verticalOverhangRect.setY(frameRect().y());
937     }
938 }
939
940 void ScrollView::updateOverhangAreas()
941 {
942     HostWindow* window = hostWindow();
943     if (!window)
944         return;
945
946     IntRect horizontalOverhangRect;
947     IntRect verticalOverhangRect;
948     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
949     if (!horizontalOverhangRect.isEmpty())
950         window->invalidateContentsAndRootView(horizontalOverhangRect);
951     if (!verticalOverhangRect.isEmpty())
952         window->invalidateContentsAndRootView(verticalOverhangRect);
953 }
954
955 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
956 {
957     ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
958     ScrollbarTheme::theme()->paintOverhangShadows(context, scrollOffset(), horizontalOverhangRect, verticalOverhangRect, dirtyRect);
959 }
960
961 void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
962 {
963     IntRect horizontalOverhangRect;
964     IntRect verticalOverhangRect;
965     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
966
967     if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
968         paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
969 }
970
971 void ScrollView::calculateAndPaintOverhangBackground(GraphicsContext* context, const IntRect& dirtyRect)
972 {
973     IntRect horizontalOverhangRect;
974     IntRect verticalOverhangRect;
975     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
976
977     if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
978         ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
979 }
980
981 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
982 {
983     if (!scrollbarCornerPresent())
984         return false;
985
986     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
987
988     if (m_horizontalScrollbar) {
989         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
990         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
991         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
992
993         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
994     }
995
996     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
997     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
998     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
999
1000     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1001 }
1002
1003 bool ScrollView::scrollbarCornerPresent() const
1004 {
1005     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1006         || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1007 }
1008
1009 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1010 {
1011     // Scrollbars won't be transformed within us
1012     IntRect newRect = localRect;
1013     newRect.moveBy(scrollbar->location());
1014     return newRect;
1015 }
1016
1017 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1018 {
1019     IntRect newRect = parentRect;
1020     // Scrollbars won't be transformed within us
1021     newRect.moveBy(-scrollbar->location());
1022     return newRect;
1023 }
1024
1025 // FIXME: test these on windows
1026 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1027 {
1028     // Scrollbars won't be transformed within us
1029     IntPoint newPoint = localPoint;
1030     newPoint.moveBy(scrollbar->location());
1031     return newPoint;
1032 }
1033
1034 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1035 {
1036     IntPoint newPoint = parentPoint;
1037     // Scrollbars won't be transformed within us
1038     newPoint.moveBy(-scrollbar->location());
1039     return newPoint;
1040 }
1041
1042 void ScrollView::setParentVisible(bool visible)
1043 {
1044     if (isParentVisible() == visible)
1045         return;
1046
1047     Widget::setParentVisible(visible);
1048
1049     if (!isSelfVisible())
1050         return;
1051
1052     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1053     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1054         (*it)->setParentVisible(visible);
1055 }
1056
1057 void ScrollView::show()
1058 {
1059     if (!isSelfVisible()) {
1060         setSelfVisible(true);
1061         if (isParentVisible()) {
1062             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1063             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1064                 (*it)->setParentVisible(true);
1065         }
1066     }
1067
1068     Widget::show();
1069 }
1070
1071 void ScrollView::hide()
1072 {
1073     if (isSelfVisible()) {
1074         if (isParentVisible()) {
1075             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1076             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1077                 (*it)->setParentVisible(false);
1078         }
1079         setSelfVisible(false);
1080     }
1081
1082     Widget::hide();
1083 }
1084
1085 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1086 {
1087     HostWindow* window = hostWindow();
1088     if (!window)
1089         return;
1090     m_drawPanScrollIcon = true;
1091     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1092     window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1093 }
1094
1095 void ScrollView::removePanScrollIcon()
1096 {
1097     HostWindow* window = hostWindow();
1098     if (!window)
1099         return;
1100     m_drawPanScrollIcon = false;
1101     window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1102 }
1103
1104 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1105 {
1106     if (scrollOrigin() == origin)
1107         return;
1108
1109     ScrollableArea::setScrollOrigin(origin);
1110
1111     // Update if the scroll origin changes, since our position will be different if the content size did not change.
1112     if (updatePositionAtAll && updatePositionSynchronously)
1113         updateScrollbars(scrollOffset());
1114 }
1115
1116 } // namespace WebCore