• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "platform/scroll/ScrollableArea.h"
34 
35 #include "platform/graphics/GraphicsLayer.h"
36 #include "platform/geometry/FloatPoint.h"
37 #include "platform/scroll/ScrollbarTheme.h"
38 #include "wtf/PassOwnPtr.h"
39 
40 #include "platform/TraceEvent.h"
41 
42 static const int kPixelsPerLineStep = 40;
43 static const float kMinFractionToStepWhenPaging = 0.875f;
44 
45 namespace WebCore {
46 
47 struct SameSizeAsScrollableArea {
48     virtual ~SameSizeAsScrollableArea();
49     void* pointer;
50     unsigned bitfields : 16;
51     IntPoint origin;
52 };
53 
54 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
55 
pixelsPerLineStep()56 int ScrollableArea::pixelsPerLineStep()
57 {
58     return kPixelsPerLineStep;
59 }
60 
minFractionToStepWhenPaging()61 float ScrollableArea::minFractionToStepWhenPaging()
62 {
63     return kMinFractionToStepWhenPaging;
64 }
65 
maxOverlapBetweenPages()66 int ScrollableArea::maxOverlapBetweenPages()
67 {
68     static int maxOverlapBetweenPages = ScrollbarTheme::theme()->maxOverlapBetweenPages();
69     return maxOverlapBetweenPages;
70 }
71 
ScrollableArea()72 ScrollableArea::ScrollableArea()
73     : m_constrainsScrollingToContentEdge(true)
74     , m_inLiveResize(false)
75     , m_verticalScrollElasticity(ScrollElasticityNone)
76     , m_horizontalScrollElasticity(ScrollElasticityNone)
77     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
78     , m_scrollOriginChanged(false)
79 {
80 }
81 
~ScrollableArea()82 ScrollableArea::~ScrollableArea()
83 {
84 }
85 
scrollAnimator() const86 ScrollAnimator* ScrollableArea::scrollAnimator() const
87 {
88     if (!m_scrollAnimator)
89         m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
90 
91     return m_scrollAnimator.get();
92 }
93 
setScrollOrigin(const IntPoint & origin)94 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
95 {
96     if (m_scrollOrigin != origin) {
97         m_scrollOrigin = origin;
98         m_scrollOriginChanged = true;
99     }
100 }
101 
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier)102 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
103 {
104     ScrollbarOrientation orientation;
105 
106     if (direction == ScrollUp || direction == ScrollDown)
107         orientation = VerticalScrollbar;
108     else
109         orientation = HorizontalScrollbar;
110 
111     if (!userInputScrollable(orientation))
112         return false;
113 
114     float step = 0;
115     switch (granularity) {
116     case ScrollByLine:
117         step = lineStep(orientation);
118         break;
119     case ScrollByPage:
120         step = pageStep(orientation);
121         break;
122     case ScrollByDocument:
123         step = documentStep(orientation);
124         break;
125     case ScrollByPixel:
126     case ScrollByPrecisePixel:
127         step = pixelStep(orientation);
128         break;
129     }
130 
131     if (direction == ScrollUp || direction == ScrollLeft)
132         multiplier = -multiplier;
133 
134     return scrollAnimator()->scroll(orientation, granularity, step, multiplier);
135 }
136 
scrollToOffsetWithoutAnimation(const FloatPoint & offset)137 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
138 {
139     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
140 }
141 
scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation,float offset)142 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
143 {
144     if (orientation == HorizontalScrollbar)
145         scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
146     else
147         scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
148 }
149 
notifyScrollPositionChanged(const IntPoint & position)150 void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
151 {
152     scrollPositionChanged(position);
153     scrollAnimator()->setCurrentPosition(position);
154 }
155 
scrollPositionChanged(const IntPoint & position)156 void ScrollableArea::scrollPositionChanged(const IntPoint& position)
157 {
158     TRACE_EVENT0("webkit", "ScrollableArea::scrollPositionChanged");
159 
160     IntPoint oldPosition = scrollPosition();
161     // Tell the derived class to scroll its contents.
162     setScrollOffset(position);
163 
164     Scrollbar* verticalScrollbar = this->verticalScrollbar();
165 
166     // Tell the scrollbars to update their thumb postions.
167     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
168         horizontalScrollbar->offsetDidChange();
169         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
170             if (!verticalScrollbar)
171                 horizontalScrollbar->invalidate();
172             else {
173                 // If there is both a horizontalScrollbar and a verticalScrollbar,
174                 // then we must also invalidate the corner between them.
175                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
176                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
177                 horizontalScrollbar->invalidateRect(boundsAndCorner);
178             }
179         }
180     }
181     if (verticalScrollbar) {
182         verticalScrollbar->offsetDidChange();
183         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
184             verticalScrollbar->invalidate();
185     }
186 
187     if (scrollPosition() != oldPosition)
188         scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
189 }
190 
handleWheelEvent(const PlatformWheelEvent & wheelEvent)191 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
192 {
193     return scrollAnimator()->handleWheelEvent(wheelEvent);
194 }
195 
196 // NOTE: Only called from Internals for testing.
setScrollOffsetFromInternals(const IntPoint & offset)197 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
198 {
199     setScrollOffsetFromAnimation(offset);
200 }
201 
setScrollOffsetFromAnimation(const IntPoint & offset)202 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
203 {
204     scrollPositionChanged(offset);
205 }
206 
willStartLiveResize()207 void ScrollableArea::willStartLiveResize()
208 {
209     if (m_inLiveResize)
210         return;
211     m_inLiveResize = true;
212     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
213         scrollAnimator->willStartLiveResize();
214 }
215 
willEndLiveResize()216 void ScrollableArea::willEndLiveResize()
217 {
218     if (!m_inLiveResize)
219         return;
220     m_inLiveResize = false;
221     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
222         scrollAnimator->willEndLiveResize();
223 }
224 
contentAreaWillPaint() const225 void ScrollableArea::contentAreaWillPaint() const
226 {
227     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
228         scrollAnimator->contentAreaWillPaint();
229 }
230 
mouseEnteredContentArea() const231 void ScrollableArea::mouseEnteredContentArea() const
232 {
233     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
234         scrollAnimator->mouseEnteredContentArea();
235 }
236 
mouseExitedContentArea() const237 void ScrollableArea::mouseExitedContentArea() const
238 {
239     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
240         scrollAnimator->mouseEnteredContentArea();
241 }
242 
mouseMovedInContentArea() const243 void ScrollableArea::mouseMovedInContentArea() const
244 {
245     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
246         scrollAnimator->mouseMovedInContentArea();
247 }
248 
mouseEnteredScrollbar(Scrollbar * scrollbar) const249 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
250 {
251     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
252 }
253 
mouseExitedScrollbar(Scrollbar * scrollbar) const254 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
255 {
256     scrollAnimator()->mouseExitedScrollbar(scrollbar);
257 }
258 
contentAreaDidShow() const259 void ScrollableArea::contentAreaDidShow() const
260 {
261     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
262         scrollAnimator->contentAreaDidShow();
263 }
264 
contentAreaDidHide() const265 void ScrollableArea::contentAreaDidHide() const
266 {
267     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
268         scrollAnimator->contentAreaDidHide();
269 }
270 
finishCurrentScrollAnimations() const271 void ScrollableArea::finishCurrentScrollAnimations() const
272 {
273     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
274         scrollAnimator->finishCurrentScrollAnimations();
275 }
276 
didAddScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)277 void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
278 {
279     if (orientation == VerticalScrollbar)
280         scrollAnimator()->didAddVerticalScrollbar(scrollbar);
281     else
282         scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
283 
284     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
285     setScrollbarOverlayStyle(scrollbarOverlayStyle());
286 }
287 
willRemoveScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)288 void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
289 {
290     if (orientation == VerticalScrollbar)
291         scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
292     else
293         scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
294 }
295 
contentsResized()296 void ScrollableArea::contentsResized()
297 {
298     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
299         scrollAnimator->contentsResized();
300 }
301 
hasOverlayScrollbars() const302 bool ScrollableArea::hasOverlayScrollbars() const
303 {
304     return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
305         || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
306 }
307 
setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)308 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
309 {
310     m_scrollbarOverlayStyle = overlayStyle;
311 
312     if (horizontalScrollbar()) {
313         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(horizontalScrollbar());
314         horizontalScrollbar()->invalidate();
315     }
316 
317     if (verticalScrollbar()) {
318         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(verticalScrollbar());
319         verticalScrollbar()->invalidate();
320     }
321 }
322 
invalidateScrollbar(Scrollbar * scrollbar,const IntRect & rect)323 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
324 {
325     if (scrollbar == horizontalScrollbar()) {
326         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
327             graphicsLayer->setNeedsDisplay();
328             graphicsLayer->setContentsNeedsDisplay();
329             return;
330         }
331     } else if (scrollbar == verticalScrollbar()) {
332         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
333             graphicsLayer->setNeedsDisplay();
334             graphicsLayer->setContentsNeedsDisplay();
335             return;
336         }
337     }
338     invalidateScrollbarRect(scrollbar, rect);
339 }
340 
invalidateScrollCorner(const IntRect & rect)341 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
342 {
343     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
344         graphicsLayer->setNeedsDisplay();
345         return;
346     }
347     invalidateScrollCornerRect(rect);
348 }
349 
hasLayerForHorizontalScrollbar() const350 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
351 {
352     return layerForHorizontalScrollbar();
353 }
354 
hasLayerForVerticalScrollbar() const355 bool ScrollableArea::hasLayerForVerticalScrollbar() const
356 {
357     return layerForVerticalScrollbar();
358 }
359 
hasLayerForScrollCorner() const360 bool ScrollableArea::hasLayerForScrollCorner() const
361 {
362     return layerForScrollCorner();
363 }
364 
serviceScrollAnimations()365 void ScrollableArea::serviceScrollAnimations()
366 {
367     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
368         scrollAnimator->serviceScrollAnimations();
369 }
370 
visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const371 IntRect ScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
372 {
373     int verticalScrollbarWidth = 0;
374     int horizontalScrollbarHeight = 0;
375 
376     if (scrollbarInclusion == IncludeScrollbars) {
377         if (Scrollbar* verticalBar = verticalScrollbar())
378             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
379         if (Scrollbar* horizontalBar = horizontalScrollbar())
380             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
381     }
382 
383     return IntRect(scrollPosition().x(),
384                    scrollPosition().y(),
385                    std::max(0, visibleWidth() + verticalScrollbarWidth),
386                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
387 }
388 
clampScrollPosition(const IntPoint & scrollPosition) const389 IntPoint ScrollableArea::clampScrollPosition(const IntPoint& scrollPosition) const
390 {
391     return scrollPosition.shrunkTo(maximumScrollPosition()).expandedTo(minimumScrollPosition());
392 }
393 
lineStep(ScrollbarOrientation) const394 int ScrollableArea::lineStep(ScrollbarOrientation) const
395 {
396     return pixelsPerLineStep();
397 }
398 
documentStep(ScrollbarOrientation orientation) const399 int ScrollableArea::documentStep(ScrollbarOrientation orientation) const
400 {
401     return scrollSize(orientation);
402 }
403 
pixelStep(ScrollbarOrientation) const404 float ScrollableArea::pixelStep(ScrollbarOrientation) const
405 {
406     return 1;
407 }
408 
409 } // namespace WebCore
410