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