eddaabf5e961071a427c836809d6b58d0048e437
[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     m_pendingScrollDelta += scrollDelta;
278 }
279
280 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
281 {
282     IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint);
283
284     if (newScrollPosition == scrollPosition())
285         return;
286
287     updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
288 }
289
290 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
291 {
292     ScrollDirection physicalDirection =
293         toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument());
294
295     return ScrollableArea::scroll(physicalDirection, granularity);
296 }
297
298 IntSize ScrollView::overhangAmount() const
299 {
300     IntSize stretch;
301
302     IntPoint currentScrollPosition = scrollPosition();
303     IntPoint minScrollPosition = minimumScrollPosition();
304     IntPoint maxScrollPosition = maximumScrollPosition();
305
306     if (currentScrollPosition.x() < minScrollPosition.x())
307         stretch.setWidth(currentScrollPosition.x() - minScrollPosition.x());
308     if (currentScrollPosition.x() > maxScrollPosition.x())
309         stretch.setWidth(currentScrollPosition.x() - maxScrollPosition.x());
310
311     if (currentScrollPosition.y() < minScrollPosition.y())
312         stretch.setHeight(currentScrollPosition.y() - minScrollPosition.y());
313     if (currentScrollPosition.y() > maxScrollPosition.y())
314         stretch.setHeight(currentScrollPosition.y() - maxScrollPosition.y());
315
316     return stretch;
317 }
318
319 void ScrollView::windowResizerRectChanged()
320 {
321     updateScrollbars(scrollOffset());
322 }
323
324 static const unsigned cMaxUpdateScrollbarsPass = 2;
325
326 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
327 {
328     if (m_inUpdateScrollbars)
329         return;
330
331     // If we came in here with the view already needing a layout, then go ahead and do that
332     // first.  (This will be the common case, e.g., when the page changes due to window resizing for example).
333     // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
334     if (!m_scrollbarsSuppressed) {
335         m_inUpdateScrollbars = true;
336         scrollbarExistenceDidChange();
337         m_inUpdateScrollbars = false;
338     }
339
340     IntRect oldScrollCornerRect = scrollCornerRect();
341
342     bool hasHorizontalScrollbar = m_horizontalScrollbar;
343     bool hasVerticalScrollbar = m_verticalScrollbar;
344
345     bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
346     bool newHasVerticalScrollbar = hasVerticalScrollbar;
347
348     ScrollbarMode hScroll = m_horizontalScrollbarMode;
349     ScrollbarMode vScroll = m_verticalScrollbarMode;
350
351     if (hScroll != ScrollbarAuto)
352         newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
353     if (vScroll != ScrollbarAuto)
354         newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
355
356     if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
357         if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
358             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
359         if (hasVerticalScrollbar != newHasVerticalScrollbar)
360             setHasVerticalScrollbar(newHasVerticalScrollbar);
361     } else {
362         bool scrollbarExistenceChanged = false;
363
364         IntSize docSize = contentsSize();
365         IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size();
366
367         bool scrollbarsAreOverlay = ScrollbarTheme::theme()->usesOverlayScrollbars();
368
369         if (hScroll == ScrollbarAuto) {
370             newHasHorizontalScrollbar = docSize.width() > visibleWidth();
371             if (!scrollbarsAreOverlay && newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
372                 newHasHorizontalScrollbar = false;
373         }
374         if (vScroll == ScrollbarAuto) {
375             newHasVerticalScrollbar = docSize.height() > visibleHeight();
376             if (!scrollbarsAreOverlay && newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height())
377                 newHasVerticalScrollbar = false;
378         }
379
380         if (!scrollbarsAreOverlay) {
381             // If we ever turn one scrollbar off, always turn the other one off too.  Never ever
382             // try to both gain/lose a scrollbar in the same pass.
383             if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
384                 newHasVerticalScrollbar = false;
385             if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
386                 newHasHorizontalScrollbar = false;
387         }
388
389         if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
390             scrollbarExistenceChanged = true;
391             if (scrollOrigin().y() && !newHasHorizontalScrollbar && !scrollbarsAreOverlay)
392                 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->height()));
393             if (hasHorizontalScrollbar)
394                 m_horizontalScrollbar->invalidate();
395             setHasHorizontalScrollbar(newHasHorizontalScrollbar);
396         }
397
398         if (hasVerticalScrollbar != newHasVerticalScrollbar) {
399             scrollbarExistenceChanged = true;
400             if (scrollOrigin().x() && !newHasVerticalScrollbar && !scrollbarsAreOverlay)
401                 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->width(), scrollOrigin().y()));
402             if (hasVerticalScrollbar)
403                 m_verticalScrollbar->invalidate();
404             setHasVerticalScrollbar(newHasVerticalScrollbar);
405         }
406
407         if (scrollbarExistenceChanged) {
408             if (scrollbarsAreOverlay) {
409                 // Synchronize status of scrollbar layers if necessary.
410                 m_inUpdateScrollbars = true;
411                 scrollbarExistenceDidChange();
412                 m_inUpdateScrollbars = false;
413             } else if (m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
414                 m_updateScrollbarsPass++;
415                 contentsResized();
416                 scrollbarExistenceDidChange();
417                 IntSize newDocSize = contentsSize();
418                 if (newDocSize == docSize) {
419                     // The layout with the new scroll state had no impact on
420                     // the document's overall size, so updateScrollbars didn't get called.
421                     // Recur manually.
422                     updateScrollbars(desiredOffset);
423                 }
424                 m_updateScrollbarsPass--;
425             }
426         }
427     }
428
429     // Set up the range, but only do this if we're not in a nested call (to avoid
430     // doing it multiple times).
431     if (m_updateScrollbarsPass)
432         return;
433
434     m_inUpdateScrollbars = true;
435
436     if (m_horizontalScrollbar) {
437         int clientWidth = visibleWidth();
438         IntRect oldRect(m_horizontalScrollbar->frameRect());
439         IntRect hBarRect((shouldPlaceVerticalScrollbarOnLeft() && m_verticalScrollbar) ? m_verticalScrollbar->width() : 0,
440                         height() - m_horizontalScrollbar->height(),
441                         width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
442                         m_horizontalScrollbar->height());
443         m_horizontalScrollbar->setFrameRect(hBarRect);
444         if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
445             m_horizontalScrollbar->invalidate();
446
447         if (m_scrollbarsSuppressed)
448             m_horizontalScrollbar->setSuppressInvalidation(true);
449         m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
450         m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
451         if (m_scrollbarsSuppressed)
452             m_horizontalScrollbar->setSuppressInvalidation(false);
453     }
454
455     if (m_verticalScrollbar) {
456         int clientHeight = visibleHeight();
457         IntRect oldRect(m_verticalScrollbar->frameRect());
458         IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
459                          0,
460                          m_verticalScrollbar->width(),
461                          height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
462         m_verticalScrollbar->setFrameRect(vBarRect);
463         if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
464             m_verticalScrollbar->invalidate();
465
466         if (m_scrollbarsSuppressed)
467             m_verticalScrollbar->setSuppressInvalidation(true);
468         m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
469         m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
470         if (m_scrollbarsSuppressed)
471             m_verticalScrollbar->setSuppressInvalidation(false);
472     }
473
474     if (hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar) {
475         // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
476         frameRectsChanged();
477         positionScrollbarLayers();
478         updateScrollCorner();
479         if (!m_horizontalScrollbar && !m_verticalScrollbar)
480             invalidateScrollCornerRect(oldScrollCornerRect);
481     }
482
483     IntPoint adjustedScrollPosition = IntPoint(desiredOffset);
484     if (!isRubberBandInProgress())
485         adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
486
487     if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
488         ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition);
489         resetScrollOriginChanged();
490     }
491
492     // Make sure the scrollbar offsets are up to date.
493     if (m_horizontalScrollbar)
494         m_horizontalScrollbar->offsetDidChange();
495     if (m_verticalScrollbar)
496         m_verticalScrollbar->offsetDidChange();
497
498     m_inUpdateScrollbars = false;
499 }
500
501 const int panIconSizeLength = 16;
502
503 IntRect ScrollView::rectToCopyOnScroll() const
504 {
505     IntRect scrollViewRect = convertToRootView(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight()));
506     if (hasOverlayScrollbars()) {
507         int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
508         int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
509
510         scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
511         scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
512     }
513     return scrollViewRect;
514 }
515
516 void ScrollView::scrollContentsIfNeeded()
517 {
518     if (m_pendingScrollDelta.isZero())
519         return;
520     IntSize scrollDelta = m_pendingScrollDelta;
521     m_pendingScrollDelta = IntSize();
522     scrollContents(scrollDelta);
523 }
524
525 void ScrollView::scrollContents(const IntSize& scrollDelta)
526 {
527     HostWindow* window = hostWindow();
528     if (!window)
529         return;
530
531     // Since scrolling is double buffered, we will be blitting the scroll view's intersection
532     // with the clip rect every time to keep it smooth.
533     IntRect clipRect = windowClipRect();
534     IntRect scrollViewRect = rectToCopyOnScroll();
535     IntRect updateRect = clipRect;
536     updateRect.intersect(scrollViewRect);
537
538     if (m_drawPanScrollIcon) {
539         // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
540         // https://bugs.webkit.org/show_bug.cgi?id=47837
541         int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
542         IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
543         IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
544         panScrollIconDirtyRect.intersect(clipRect);
545         window->invalidateContentsAndRootView(panScrollIconDirtyRect);
546     }
547
548     if (!shouldAttemptToScrollUsingFastPath() || !scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
549         scrollContentsSlowPath(updateRect);
550
551     // Invalidate the overhang areas if they are visible.
552     updateOverhangAreas();
553
554     // This call will move children with native widgets (plugins) and invalidate them as well.
555     frameRectsChanged();
556 }
557
558 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
559 {
560     hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
561     return true;
562 }
563
564 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
565 {
566     hostWindow()->invalidateContentsForSlowScroll(updateRect);
567 }
568
569 IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
570 {
571     IntPoint viewPoint = convertFromRootView(rootViewPoint);
572     return viewPoint + scrollOffset();
573 }
574
575 IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
576 {
577     IntPoint viewPoint = contentsPoint - scrollOffset();
578     return convertToRootView(viewPoint);
579 }
580
581 IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
582 {
583     IntRect viewRect = convertFromRootView(rootViewRect);
584     viewRect.move(scrollOffset());
585     return viewRect;
586 }
587
588 IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
589 {
590     IntRect viewRect = contentsRect;
591     viewRect.move(-scrollOffset());
592     return convertToRootView(viewRect);
593 }
594
595 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
596 {
597     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
598     return viewPoint + scrollOffset();
599 }
600
601 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
602 {
603     IntPoint viewPoint = contentsPoint - scrollOffset();
604     return convertToContainingWindow(viewPoint);
605 }
606
607 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
608 {
609     IntRect viewRect = convertFromContainingWindow(windowRect);
610     viewRect.move(scrollOffset());
611     return viewRect;
612 }
613
614 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
615 {
616     IntRect viewRect = contentsRect;
617     viewRect.move(-scrollOffset());
618     return convertToContainingWindow(viewRect);
619 }
620
621 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
622 {
623     HostWindow* window = hostWindow();
624     if (!window)
625         return IntRect();
626     return window->rootViewToScreen(contentsToRootView(rect));
627 }
628
629 bool ScrollView::containsScrollbarsAvoidingResizer() const
630 {
631     return !m_scrollbarsAvoidingResizer;
632 }
633
634 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
635 {
636     int oldCount = m_scrollbarsAvoidingResizer;
637     m_scrollbarsAvoidingResizer += overlapDelta;
638     if (parent())
639         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(overlapDelta);
640     else if (!scrollbarsSuppressed()) {
641         // If we went from n to 0 or from 0 to n and we're the outermost view,
642         // we need to invalidate the windowResizerRect(), since it will now need to paint
643         // differently.
644         if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
645             (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
646             invalidateRect(windowResizerRect());
647     }
648 }
649
650 void ScrollView::setParent(Widget* parentView)
651 {
652     if (parentView == parent())
653         return;
654
655     if (m_scrollbarsAvoidingResizer && parent())
656         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
657
658     Widget::setParent(parentView);
659
660     if (m_scrollbarsAvoidingResizer && parent())
661         toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
662 }
663
664 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
665 {
666     if (suppressed == m_scrollbarsSuppressed)
667         return;
668
669     m_scrollbarsSuppressed = suppressed;
670
671     if (repaintOnUnsuppress && !suppressed) {
672         if (m_horizontalScrollbar)
673             m_horizontalScrollbar->invalidate();
674         if (m_verticalScrollbar)
675             m_verticalScrollbar->invalidate();
676
677         // Invalidate the scroll corner too on unsuppress.
678         invalidateRect(scrollCornerRect());
679     }
680 }
681
682 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
683 {
684     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
685     if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint))
686         return m_horizontalScrollbar.get();
687     if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint))
688         return m_verticalScrollbar.get();
689     return 0;
690 }
691
692 void ScrollView::setFrameRect(const IntRect& newRect)
693 {
694     IntRect oldRect = frameRect();
695
696     if (newRect == oldRect)
697         return;
698
699     Widget::setFrameRect(newRect);
700
701     updateScrollbars(scrollOffset());
702
703     frameRectsChanged();
704 }
705
706 void ScrollView::frameRectsChanged()
707 {
708     HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
709     for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
710         (*current)->frameRectsChanged();
711 }
712
713 static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
714 {
715     if (!graphicsLayer || !scrollbar)
716         return;
717
718     IntRect scrollbarRect = scrollbar->frameRect();
719     graphicsLayer->setPosition(scrollbarRect.location());
720
721     if (scrollbarRect.size() == graphicsLayer->size())
722         return;
723
724     graphicsLayer->setSize(scrollbarRect.size());
725
726     if (graphicsLayer->hasContentsLayer()) {
727         graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
728         return;
729     }
730
731     graphicsLayer->setDrawsContent(true);
732     graphicsLayer->setNeedsDisplay();
733 }
734
735 static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
736 {
737     if (!graphicsLayer)
738         return;
739     graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
740     graphicsLayer->setPosition(cornerRect.location());
741     if (cornerRect.size() != graphicsLayer->size())
742         graphicsLayer->setNeedsDisplay();
743     graphicsLayer->setSize(cornerRect.size());
744 }
745
746 void ScrollView::positionScrollbarLayers()
747 {
748     positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
749     positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
750     positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
751 }
752
753 bool ScrollView::userInputScrollable(ScrollbarOrientation orientation) const
754 {
755     ScrollbarMode mode = (orientation == HorizontalScrollbar) ?
756         m_horizontalScrollbarMode : m_verticalScrollbarMode;
757
758     return mode == ScrollbarAuto || mode == ScrollbarAlwaysOn;
759 }
760
761 bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const
762 {
763     return false;
764 }
765
766 void ScrollView::repaintContentRectangle(const IntRect& rect)
767 {
768     IntRect paintRect = rect;
769     if (clipsRepaints() && !paintsEntireContents())
770         paintRect.intersect(visibleContentRect());
771     if (paintRect.isEmpty())
772         return;
773
774     if (HostWindow* window = hostWindow())
775         window->invalidateContentsAndRootView(contentsToWindow(paintRect));
776 }
777
778 IntRect ScrollView::scrollCornerRect() const
779 {
780     IntRect cornerRect;
781
782     if (hasOverlayScrollbars())
783         return cornerRect;
784
785     if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
786         cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
787                                  height() - m_horizontalScrollbar->height(),
788                                  width() - m_horizontalScrollbar->width(),
789                                  m_horizontalScrollbar->height()));
790     }
791
792     if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
793         cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()),
794                                  m_verticalScrollbar->height(),
795                                  m_verticalScrollbar->width(),
796                                  height() - m_verticalScrollbar->height()));
797     }
798
799     return cornerRect;
800 }
801
802 bool ScrollView::isScrollCornerVisible() const
803 {
804     return !scrollCornerRect().isEmpty();
805 }
806
807 void ScrollView::scrollbarStyleChanged(int, bool forceUpdate)
808 {
809     if (!forceUpdate)
810         return;
811
812     contentsResized();
813     updateScrollbars(scrollOffset());
814     positionScrollbarLayers();
815 }
816
817 void ScrollView::updateScrollCorner()
818 {
819 }
820
821 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
822 {
823     ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect);
824 }
825
826 void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect)
827 {
828     bar->paint(context, rect);
829 }
830
831 void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
832 {
833     invalidateRect(rect);
834 }
835
836 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
837 {
838     if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
839         paintScrollbar(context, m_horizontalScrollbar.get(), rect);
840     if (m_verticalScrollbar && !layerForVerticalScrollbar())
841         paintScrollbar(context, m_verticalScrollbar.get(), rect);
842
843     if (layerForScrollCorner())
844         return;
845     paintScrollCorner(context, scrollCornerRect());
846 }
847
848 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
849 {
850     DEFINE_STATIC_REF(Image, panScrollIcon, (Image::loadPlatformResource("panIcon")));
851     IntPoint iconGCPoint = m_panScrollIconPoint;
852     if (parent())
853         iconGCPoint = toScrollView(parent())->windowToContents(iconGCPoint);
854     context->drawImage(panScrollIcon, iconGCPoint);
855 }
856
857 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
858 {
859     if (context->paintingDisabled() && !context->updatingControlTints())
860         return;
861
862     notifyPageThatContentAreaWillPaint();
863
864     IntRect documentDirtyRect = rect;
865     if (!paintsEntireContents()) {
866         IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect().size());
867         documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
868     }
869
870     if (!documentDirtyRect.isEmpty()) {
871         GraphicsContextStateSaver stateSaver(*context);
872
873         context->translate(x(), y());
874         documentDirtyRect.moveBy(-location());
875
876         if (!paintsEntireContents()) {
877             context->translate(-scrollX(), -scrollY());
878             documentDirtyRect.moveBy(scrollPosition());
879
880             context->clip(visibleContentRect());
881         }
882
883         paintContents(context, documentDirtyRect);
884     }
885
886     calculateAndPaintOverhangAreas(context, rect);
887
888     // Now paint the scrollbars.
889     if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
890         GraphicsContextStateSaver stateSaver(*context);
891         IntRect scrollViewDirtyRect = rect;
892         IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size());
893         scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
894         context->translate(x(), y());
895         scrollViewDirtyRect.moveBy(-location());
896
897         paintScrollbars(context, scrollViewDirtyRect);
898     }
899
900     // Paint the panScroll Icon
901     if (m_drawPanScrollIcon)
902         paintPanScrollIcon(context);
903 }
904
905 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
906 {
907     int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar())
908         ? verticalScrollbar()->width() : 0;
909     int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar())
910         ? horizontalScrollbar()->height() : 0;
911
912     int physicalScrollY = scrollPosition().y() + scrollOrigin().y();
913     if (physicalScrollY < 0) {
914         horizontalOverhangRect = frameRect();
915         horizontalOverhangRect.setHeight(-physicalScrollY);
916         horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
917     } else if (contentsHeight() && physicalScrollY > contentsHeight() - visibleHeight()) {
918         int height = physicalScrollY - (contentsHeight() - visibleHeight());
919         horizontalOverhangRect = frameRect();
920         horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight);
921         horizontalOverhangRect.setHeight(height);
922         horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth);
923     }
924
925     int physicalScrollX = scrollPosition().x() + scrollOrigin().x();
926     if (physicalScrollX < 0) {
927         verticalOverhangRect.setWidth(-physicalScrollX);
928         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
929         verticalOverhangRect.setX(frameRect().x());
930         if (horizontalOverhangRect.y() == frameRect().y())
931             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
932         else
933             verticalOverhangRect.setY(frameRect().y());
934     } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) {
935         int width = physicalScrollX - (contentsWidth() - visibleWidth());
936         verticalOverhangRect.setWidth(width);
937         verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight);
938         verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth);
939         if (horizontalOverhangRect.y() == frameRect().y())
940             verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
941         else
942             verticalOverhangRect.setY(frameRect().y());
943     }
944 }
945
946 void ScrollView::updateOverhangAreas()
947 {
948     HostWindow* window = hostWindow();
949     if (!window)
950         return;
951
952     IntRect horizontalOverhangRect;
953     IntRect verticalOverhangRect;
954     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
955     if (!horizontalOverhangRect.isEmpty())
956         window->invalidateContentsAndRootView(horizontalOverhangRect);
957     if (!verticalOverhangRect.isEmpty())
958         window->invalidateContentsAndRootView(verticalOverhangRect);
959 }
960
961 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
962 {
963     ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
964     ScrollbarTheme::theme()->paintOverhangShadows(context, scrollOffset(), horizontalOverhangRect, verticalOverhangRect, dirtyRect);
965 }
966
967 void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect)
968 {
969     IntRect horizontalOverhangRect;
970     IntRect verticalOverhangRect;
971     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
972
973     if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
974         paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
975 }
976
977 void ScrollView::calculateAndPaintOverhangBackground(GraphicsContext* context, const IntRect& dirtyRect)
978 {
979     IntRect horizontalOverhangRect;
980     IntRect verticalOverhangRect;
981     calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
982
983     if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
984         ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
985 }
986
987 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
988 {
989     if (!scrollbarCornerPresent())
990         return false;
991
992     IntPoint viewPoint = convertFromContainingWindow(windowPoint);
993
994     if (m_horizontalScrollbar) {
995         int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
996         int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
997         int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
998
999         return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1000     }
1001
1002     int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1003     int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1004     int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1005
1006     return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1007 }
1008
1009 bool ScrollView::scrollbarCornerPresent() const
1010 {
1011     return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1012         || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1013 }
1014
1015 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
1016 {
1017     // Scrollbars won't be transformed within us
1018     IntRect newRect = localRect;
1019     newRect.moveBy(scrollbar->location());
1020     return newRect;
1021 }
1022
1023 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
1024 {
1025     IntRect newRect = parentRect;
1026     // Scrollbars won't be transformed within us
1027     newRect.moveBy(-scrollbar->location());
1028     return newRect;
1029 }
1030
1031 // FIXME: test these on windows
1032 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
1033 {
1034     // Scrollbars won't be transformed within us
1035     IntPoint newPoint = localPoint;
1036     newPoint.moveBy(scrollbar->location());
1037     return newPoint;
1038 }
1039
1040 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
1041 {
1042     IntPoint newPoint = parentPoint;
1043     // Scrollbars won't be transformed within us
1044     newPoint.moveBy(-scrollbar->location());
1045     return newPoint;
1046 }
1047
1048 void ScrollView::setParentVisible(bool visible)
1049 {
1050     if (isParentVisible() == visible)
1051         return;
1052
1053     Widget::setParentVisible(visible);
1054
1055     if (!isSelfVisible())
1056         return;
1057
1058     HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1059     for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1060         (*it)->setParentVisible(visible);
1061 }
1062
1063 void ScrollView::show()
1064 {
1065     if (!isSelfVisible()) {
1066         setSelfVisible(true);
1067         if (isParentVisible()) {
1068             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1069             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1070                 (*it)->setParentVisible(true);
1071         }
1072     }
1073
1074     Widget::show();
1075 }
1076
1077 void ScrollView::hide()
1078 {
1079     if (isSelfVisible()) {
1080         if (isParentVisible()) {
1081             HashSet<RefPtr<Widget> >::iterator end = m_children.end();
1082             for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
1083                 (*it)->setParentVisible(false);
1084         }
1085         setSelfVisible(false);
1086     }
1087
1088     Widget::hide();
1089 }
1090
1091 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1092 {
1093     HostWindow* window = hostWindow();
1094     if (!window)
1095         return;
1096     m_drawPanScrollIcon = true;
1097     m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1098     window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1099 }
1100
1101 void ScrollView::removePanScrollIcon()
1102 {
1103     HostWindow* window = hostWindow();
1104     if (!window)
1105         return;
1106     m_drawPanScrollIcon = false;
1107     window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1108 }
1109
1110 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1111 {
1112     if (scrollOrigin() == origin)
1113         return;
1114
1115     ScrollableArea::setScrollOrigin(origin);
1116
1117     // Update if the scroll origin changes, since our position will be different if the content size did not change.
1118     if (updatePositionAtAll && updatePositionSynchronously)
1119         updateScrollbars(scrollOffset());
1120 }
1121
1122 } // namespace WebCore