We don't need to always repaint overlay scrollbars if they're in layers
[WebKit-https.git] / Source / WebCore / platform / ScrollableArea.cpp
1 /*
2  * Copyright (c) 2010, Google Inc. All rights reserved.
3  * Copyright (C) 2008, 2011 Apple Inc. All Rights Reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  * 
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "ScrollableArea.h"
34
35 #include "GraphicsContext.h"
36 #include "GraphicsLayer.h"
37 #include "FloatPoint.h"
38 #include "PlatformWheelEvent.h"
39 #include "ScrollAnimator.h"
40 #include "ScrollbarTheme.h"
41 #include <wtf/PassOwnPtr.h>
42
43 namespace WebCore {
44
45 ScrollableArea::ScrollableArea()
46     : m_constrainsScrollingToContentEdge(true)
47     , m_inLiveResize(false)
48     , m_verticalScrollElasticity(ScrollElasticityNone)
49     , m_horizontalScrollElasticity(ScrollElasticityNone)
50     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
51     , m_scrollOriginChanged(false)
52 {
53 }
54
55 ScrollableArea::~ScrollableArea()
56 {
57 }
58
59 ScrollAnimator* ScrollableArea::scrollAnimator() const
60 {
61     if (!m_scrollAnimator)
62         m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
63
64     return m_scrollAnimator.get();
65 }
66
67 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
68 {
69     if (m_scrollOrigin != origin) {
70         m_scrollOrigin = origin;
71         m_scrollOriginChanged = true;
72     }
73 }
74  
75 void ScrollableArea::setScrollOriginX(int x)
76 {
77     if (m_scrollOrigin.x() != x) {
78         m_scrollOrigin.setX(x);
79         m_scrollOriginChanged = true;
80     }
81 }
82
83 void ScrollableArea::setScrollOriginY(int y)
84 {
85     if (m_scrollOrigin.y() != y) {
86         m_scrollOrigin.setY(y);
87         m_scrollOriginChanged = true;
88     }
89 }
90
91 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
92 {
93     ScrollbarOrientation orientation;
94     Scrollbar* scrollbar;
95     if (direction == ScrollUp || direction == ScrollDown) {
96         orientation = VerticalScrollbar;
97         scrollbar = verticalScrollbar();
98     } else {
99         orientation = HorizontalScrollbar;
100         scrollbar = horizontalScrollbar();
101     }
102
103     if (!scrollbar)
104         return false;
105
106     float step = 0;
107     switch (granularity) {
108     case ScrollByLine:
109         step = scrollbar->lineStep();
110         break;
111     case ScrollByPage:
112         step = scrollbar->pageStep();
113         break;
114     case ScrollByDocument:
115         step = scrollbar->totalSize();
116         break;
117     case ScrollByPixel:
118         step = scrollbar->pixelStep();
119         break;
120     }
121
122     if (direction == ScrollUp || direction == ScrollLeft)
123         multiplier = -multiplier;
124
125     return scrollAnimator()->scroll(orientation, granularity, step, multiplier);
126 }
127
128 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
129 {
130     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
131 }
132
133 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
134 {
135     if (orientation == HorizontalScrollbar)
136         scrollToXOffsetWithoutAnimation(offset);
137     else
138         scrollToYOffsetWithoutAnimation(offset);
139 }
140
141 void ScrollableArea::scrollToXOffsetWithoutAnimation(float x)
142 {
143     scrollToOffsetWithoutAnimation(FloatPoint(x, scrollAnimator()->currentPosition().y()));
144 }
145
146 void ScrollableArea::scrollToYOffsetWithoutAnimation(float y)
147 {
148     scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), y));
149 }
150
151 void ScrollableArea::zoomAnimatorTransformChanged(float, float, float, ZoomAnimationState)
152 {
153     // Requires FrameView to override this.
154 }
155
156 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
157 {
158     return scrollAnimator()->handleWheelEvent(wheelEvent);
159 }
160
161 #if ENABLE(GESTURE_EVENTS)
162 void ScrollableArea::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
163 {
164     scrollAnimator()->handleGestureEvent(gestureEvent);
165 }
166 #endif
167
168 // NOTE: Only called from Internals for testing.
169 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
170 {
171     setScrollOffsetFromAnimation(offset);
172 }
173
174 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
175 {
176     // Tell the derived class to scroll its contents.
177     setScrollOffset(offset);
178
179     Scrollbar* verticalScrollbar = this->verticalScrollbar();
180
181     // Tell the scrollbars to update their thumb postions.
182     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
183         horizontalScrollbar->offsetDidChange();
184         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
185             if (!verticalScrollbar)
186                 horizontalScrollbar->invalidate();
187             else {
188                 // If there is both a horizontalScrollbar and a verticalScrollbar,
189                 // then we must also invalidate the corner between them.
190                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
191                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
192                 horizontalScrollbar->invalidateRect(boundsAndCorner);
193             }
194         }
195     }
196     if (verticalScrollbar) {
197         verticalScrollbar->offsetDidChange();
198         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
199             verticalScrollbar->invalidate();
200     }
201 }
202
203 void ScrollableArea::willStartLiveResize()
204 {
205     if (m_inLiveResize)
206         return;
207     m_inLiveResize = true;
208     scrollAnimator()->willStartLiveResize();
209 }
210
211 void ScrollableArea::willEndLiveResize()
212 {
213     if (!m_inLiveResize)
214         return;
215     m_inLiveResize = false;
216     scrollAnimator()->willEndLiveResize();
217 }    
218
219 void ScrollableArea::contentAreaWillPaint() const
220 {
221     scrollAnimator()->contentAreaWillPaint();
222 }
223
224 void ScrollableArea::mouseEnteredContentArea() const
225 {
226     scrollAnimator()->mouseEnteredContentArea();
227 }
228
229 void ScrollableArea::mouseExitedContentArea() const
230 {
231     scrollAnimator()->mouseEnteredContentArea();
232 }
233
234 void ScrollableArea::mouseMovedInContentArea() const
235 {
236     scrollAnimator()->mouseMovedInContentArea();
237 }
238
239 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
240 {
241     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
242 }
243
244 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
245 {
246     scrollAnimator()->mouseExitedScrollbar(scrollbar);
247 }
248
249 void ScrollableArea::contentAreaDidShow() const
250 {
251     scrollAnimator()->contentAreaDidShow();
252 }
253
254 void ScrollableArea::contentAreaDidHide() const
255 {
256     scrollAnimator()->contentAreaDidHide();
257 }
258
259 void ScrollableArea::didAddVerticalScrollbar(Scrollbar* scrollbar)
260 {
261     scrollAnimator()->didAddVerticalScrollbar(scrollbar);
262
263     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
264     setScrollbarOverlayStyle(scrollbarOverlayStyle());
265 }
266
267 void ScrollableArea::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
268 {
269     scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
270 }
271
272 void ScrollableArea::didAddHorizontalScrollbar(Scrollbar* scrollbar)
273 {
274     scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
275
276     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
277     setScrollbarOverlayStyle(scrollbarOverlayStyle());
278 }
279
280 void ScrollableArea::contentsResized()
281 {
282     scrollAnimator()->contentsResized();
283 }
284
285 void ScrollableArea::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
286 {
287     scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
288 }
289
290 bool ScrollableArea::hasOverlayScrollbars() const
291 {
292     return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
293         || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
294 }
295
296 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
297 {
298     m_scrollbarOverlayStyle = overlayStyle;
299
300     if (horizontalScrollbar()) {
301         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(horizontalScrollbar());
302         horizontalScrollbar()->invalidate();
303     }
304     
305     if (verticalScrollbar()) {
306         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(verticalScrollbar());
307         verticalScrollbar()->invalidate();
308     }
309 }
310
311 bool ScrollableArea::isPinnedInBothDirections(const IntSize& scrollDelta) const
312 {
313     return isPinnedHorizontallyInDirection(scrollDelta.width()) && isPinnedVerticallyInDirection(scrollDelta.height());
314 }
315
316 bool ScrollableArea::isPinnedHorizontallyInDirection(int horizontalScrollDelta) const
317 {
318     if (horizontalScrollDelta < 0 && isHorizontalScrollerPinnedToMinimumPosition())
319         return true;
320     if (horizontalScrollDelta > 0 && isHorizontalScrollerPinnedToMaximumPosition())
321         return true;
322     return false;
323 }
324
325 bool ScrollableArea::isPinnedVerticallyInDirection(int verticalScrollDelta) const
326 {
327     if (verticalScrollDelta < 0 && isVerticalScrollerPinnedToMinimumPosition())
328         return true;
329     if (verticalScrollDelta > 0 && isVerticalScrollerPinnedToMaximumPosition())
330         return true;
331     return false;
332 }
333
334 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
335 {
336 #if USE(ACCELERATED_COMPOSITING)
337     if (scrollbar == horizontalScrollbar()) {
338         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
339             graphicsLayer->setNeedsDisplay();
340             return;
341         }
342     } else if (scrollbar == verticalScrollbar()) {
343         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
344             graphicsLayer->setNeedsDisplay();
345             return;
346         }
347     }
348 #endif
349     invalidateScrollbarRect(scrollbar, rect);
350 }
351
352 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
353 {
354 #if USE(ACCELERATED_COMPOSITING)
355     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
356         graphicsLayer->setNeedsDisplay();
357         return;
358     }
359 #endif
360     invalidateScrollCornerRect(rect);
361 }
362
363 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
364 {
365 #if USE(ACCELERATED_COMPOSITING)
366     return layerForHorizontalScrollbar();
367 #else
368     return false;
369 #endif
370 }
371
372 bool ScrollableArea::hasLayerForVerticalScrollbar() const
373 {
374 #if USE(ACCELERATED_COMPOSITING)
375     return layerForVerticalScrollbar();
376 #else
377     return false;
378 #endif
379 }
380
381 bool ScrollableArea::hasLayerForScrollCorner() const
382 {
383 #if USE(ACCELERATED_COMPOSITING)
384     return layerForScrollCorner();
385 #else
386     return false;
387 #endif
388 }
389
390 } // namespace WebCore