• 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     unsigned damageBits : 2;
50     IntRect scrollbarDamage[2];
51     void* pointer;
52     unsigned bitfields : 16;
53     IntPoint origin;
54 };
55 
56 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
57 
pixelsPerLineStep()58 int ScrollableArea::pixelsPerLineStep()
59 {
60     return kPixelsPerLineStep;
61 }
62 
minFractionToStepWhenPaging()63 float ScrollableArea::minFractionToStepWhenPaging()
64 {
65     return kMinFractionToStepWhenPaging;
66 }
67 
maxOverlapBetweenPages()68 int ScrollableArea::maxOverlapBetweenPages()
69 {
70     static int maxOverlapBetweenPages = ScrollbarTheme::theme()->maxOverlapBetweenPages();
71     return maxOverlapBetweenPages;
72 }
73 
ScrollableArea()74 ScrollableArea::ScrollableArea()
75     : m_hasHorizontalBarDamage(false)
76     , m_hasVerticalBarDamage(false)
77     , m_constrainsScrollingToContentEdge(true)
78     , m_inLiveResize(false)
79     , m_verticalScrollElasticity(ScrollElasticityNone)
80     , m_horizontalScrollElasticity(ScrollElasticityNone)
81     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
82     , m_scrollOriginChanged(false)
83 {
84 }
85 
~ScrollableArea()86 ScrollableArea::~ScrollableArea()
87 {
88 }
89 
scrollAnimator() const90 ScrollAnimator* ScrollableArea::scrollAnimator() const
91 {
92     if (!m_scrollAnimator)
93         m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
94 
95     return m_scrollAnimator.get();
96 }
97 
setScrollOrigin(const IntPoint & origin)98 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
99 {
100     if (m_scrollOrigin != origin) {
101         m_scrollOrigin = origin;
102         m_scrollOriginChanged = true;
103     }
104 }
105 
layerForContainer() const106 GraphicsLayer* ScrollableArea::layerForContainer() const
107 {
108     return layerForScrolling() ? layerForScrolling()->parent() : 0;
109 }
110 
scroll(ScrollDirection direction,ScrollGranularity granularity,float delta)111 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float delta)
112 {
113     ScrollbarOrientation orientation;
114 
115     if (direction == ScrollUp || direction == ScrollDown)
116         orientation = VerticalScrollbar;
117     else
118         orientation = HorizontalScrollbar;
119 
120     if (!userInputScrollable(orientation))
121         return false;
122 
123     float step = 0;
124     switch (granularity) {
125     case ScrollByLine:
126         step = lineStep(orientation);
127         break;
128     case ScrollByPage:
129         step = pageStep(orientation);
130         break;
131     case ScrollByDocument:
132         step = documentStep(orientation);
133         break;
134     case ScrollByPixel:
135     case ScrollByPrecisePixel:
136         step = pixelStep(orientation);
137         break;
138     }
139 
140     if (direction == ScrollUp || direction == ScrollLeft)
141         delta = -delta;
142 
143     return scrollAnimator()->scroll(orientation, granularity, step, delta);
144 }
145 
scrollToOffsetWithoutAnimation(const FloatPoint & offset)146 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
147 {
148     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
149 }
150 
scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation,float offset)151 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
152 {
153     if (orientation == HorizontalScrollbar)
154         scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
155     else
156         scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
157 }
158 
notifyScrollPositionChanged(const IntPoint & position)159 void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
160 {
161     scrollPositionChanged(position);
162     scrollAnimator()->setCurrentPosition(position);
163 }
164 
scrollPositionChanged(const IntPoint & position)165 void ScrollableArea::scrollPositionChanged(const IntPoint& position)
166 {
167     TRACE_EVENT0("webkit", "ScrollableArea::scrollPositionChanged");
168 
169     IntPoint oldPosition = scrollPosition();
170     // Tell the derived class to scroll its contents.
171     setScrollOffset(position);
172 
173     Scrollbar* verticalScrollbar = this->verticalScrollbar();
174 
175     // Tell the scrollbars to update their thumb postions.
176     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
177         horizontalScrollbar->offsetDidChange();
178         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
179             if (!verticalScrollbar)
180                 horizontalScrollbar->invalidate();
181             else {
182                 // If there is both a horizontalScrollbar and a verticalScrollbar,
183                 // then we must also invalidate the corner between them.
184                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
185                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
186                 horizontalScrollbar->invalidateRect(boundsAndCorner);
187             }
188         }
189     }
190     if (verticalScrollbar) {
191         verticalScrollbar->offsetDidChange();
192         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
193             verticalScrollbar->invalidate();
194     }
195 
196     if (scrollPosition() != oldPosition)
197         scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
198 }
199 
scrollBehaviorFromString(const String & behaviorString,ScrollBehavior & behavior)200 bool ScrollableArea::scrollBehaviorFromString(const String& behaviorString, ScrollBehavior& behavior)
201 {
202     if (behaviorString == "auto")
203         behavior = ScrollBehaviorAuto;
204     else if (behaviorString == "instant")
205         behavior = ScrollBehaviorInstant;
206     else if (behaviorString == "smooth")
207         behavior = ScrollBehaviorSmooth;
208     else
209         return false;
210 
211     return true;
212 }
213 
handleWheelEvent(const PlatformWheelEvent & wheelEvent)214 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
215 {
216     // ctrl+wheel events are used to trigger zooming, not scrolling.
217     if (wheelEvent.modifiers() & PlatformEvent::CtrlKey)
218         return false;
219 
220     return scrollAnimator()->handleWheelEvent(wheelEvent);
221 }
222 
223 // NOTE: Only called from Internals for testing.
setScrollOffsetFromInternals(const IntPoint & offset)224 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
225 {
226     setScrollOffsetFromAnimation(offset);
227 }
228 
setScrollOffsetFromAnimation(const IntPoint & offset)229 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
230 {
231     scrollPositionChanged(offset);
232 }
233 
willStartLiveResize()234 void ScrollableArea::willStartLiveResize()
235 {
236     if (m_inLiveResize)
237         return;
238     m_inLiveResize = true;
239     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
240         scrollAnimator->willStartLiveResize();
241 }
242 
willEndLiveResize()243 void ScrollableArea::willEndLiveResize()
244 {
245     if (!m_inLiveResize)
246         return;
247     m_inLiveResize = false;
248     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
249         scrollAnimator->willEndLiveResize();
250 }
251 
contentAreaWillPaint() const252 void ScrollableArea::contentAreaWillPaint() const
253 {
254     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
255         scrollAnimator->contentAreaWillPaint();
256 }
257 
mouseEnteredContentArea() const258 void ScrollableArea::mouseEnteredContentArea() const
259 {
260     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
261         scrollAnimator->mouseEnteredContentArea();
262 }
263 
mouseExitedContentArea() const264 void ScrollableArea::mouseExitedContentArea() const
265 {
266     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
267         scrollAnimator->mouseEnteredContentArea();
268 }
269 
mouseMovedInContentArea() const270 void ScrollableArea::mouseMovedInContentArea() const
271 {
272     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
273         scrollAnimator->mouseMovedInContentArea();
274 }
275 
mouseEnteredScrollbar(Scrollbar * scrollbar) const276 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
277 {
278     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
279 }
280 
mouseExitedScrollbar(Scrollbar * scrollbar) const281 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
282 {
283     scrollAnimator()->mouseExitedScrollbar(scrollbar);
284 }
285 
contentAreaDidShow() const286 void ScrollableArea::contentAreaDidShow() const
287 {
288     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
289         scrollAnimator->contentAreaDidShow();
290 }
291 
contentAreaDidHide() const292 void ScrollableArea::contentAreaDidHide() const
293 {
294     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
295         scrollAnimator->contentAreaDidHide();
296 }
297 
finishCurrentScrollAnimations() const298 void ScrollableArea::finishCurrentScrollAnimations() const
299 {
300     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
301         scrollAnimator->finishCurrentScrollAnimations();
302 }
303 
didAddScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)304 void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
305 {
306     if (orientation == VerticalScrollbar)
307         scrollAnimator()->didAddVerticalScrollbar(scrollbar);
308     else
309         scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
310 
311     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
312     setScrollbarOverlayStyle(scrollbarOverlayStyle());
313 }
314 
willRemoveScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)315 void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
316 {
317     if (orientation == VerticalScrollbar)
318         scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
319     else
320         scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
321 }
322 
contentsResized()323 void ScrollableArea::contentsResized()
324 {
325     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
326         scrollAnimator->contentsResized();
327 }
328 
hasOverlayScrollbars() const329 bool ScrollableArea::hasOverlayScrollbars() const
330 {
331     Scrollbar* vScrollbar = verticalScrollbar();
332     if (vScrollbar && vScrollbar->isOverlayScrollbar())
333         return true;
334     Scrollbar* hScrollbar = horizontalScrollbar();
335     return hScrollbar && hScrollbar->isOverlayScrollbar();
336 }
337 
setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)338 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
339 {
340     m_scrollbarOverlayStyle = overlayStyle;
341 
342     if (Scrollbar* scrollbar = horizontalScrollbar()) {
343         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(scrollbar);
344         scrollbar->invalidate();
345     }
346 
347     if (Scrollbar* scrollbar = verticalScrollbar()) {
348         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(scrollbar);
349         scrollbar->invalidate();
350     }
351 }
352 
invalidateScrollbar(Scrollbar * scrollbar,const IntRect & rect)353 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
354 {
355     if (scrollbar == horizontalScrollbar()) {
356         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
357             graphicsLayer->setNeedsDisplay();
358             graphicsLayer->setContentsNeedsDisplay();
359             return;
360         }
361     } else if (scrollbar == verticalScrollbar()) {
362         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
363             graphicsLayer->setNeedsDisplay();
364             graphicsLayer->setContentsNeedsDisplay();
365             return;
366         }
367     }
368     invalidateScrollbarRect(scrollbar, rect);
369 }
370 
invalidateScrollCorner(const IntRect & rect)371 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
372 {
373     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
374         graphicsLayer->setNeedsDisplay();
375         return;
376     }
377     invalidateScrollCornerRect(rect);
378 }
379 
hasLayerForHorizontalScrollbar() const380 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
381 {
382     return layerForHorizontalScrollbar();
383 }
384 
hasLayerForVerticalScrollbar() const385 bool ScrollableArea::hasLayerForVerticalScrollbar() const
386 {
387     return layerForVerticalScrollbar();
388 }
389 
hasLayerForScrollCorner() const390 bool ScrollableArea::hasLayerForScrollCorner() const
391 {
392     return layerForScrollCorner();
393 }
394 
serviceScrollAnimations()395 void ScrollableArea::serviceScrollAnimations()
396 {
397     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
398         scrollAnimator->serviceScrollAnimations();
399 }
400 
visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const401 IntRect ScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
402 {
403     int verticalScrollbarWidth = 0;
404     int horizontalScrollbarHeight = 0;
405 
406     if (scrollbarInclusion == IncludeScrollbars) {
407         if (Scrollbar* verticalBar = verticalScrollbar())
408             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
409         if (Scrollbar* horizontalBar = horizontalScrollbar())
410             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
411     }
412 
413     return IntRect(scrollPosition().x(),
414                    scrollPosition().y(),
415                    std::max(0, visibleWidth() + verticalScrollbarWidth),
416                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
417 }
418 
clampScrollPosition(const IntPoint & scrollPosition) const419 IntPoint ScrollableArea::clampScrollPosition(const IntPoint& scrollPosition) const
420 {
421     return scrollPosition.shrunkTo(maximumScrollPosition()).expandedTo(minimumScrollPosition());
422 }
423 
lineStep(ScrollbarOrientation) const424 int ScrollableArea::lineStep(ScrollbarOrientation) const
425 {
426     return pixelsPerLineStep();
427 }
428 
pageStep(ScrollbarOrientation orientation) const429 int ScrollableArea::pageStep(ScrollbarOrientation orientation) const
430 {
431     int length = (orientation == HorizontalScrollbar) ? visibleWidth() : visibleHeight();
432     int minPageStep = static_cast<float>(length) * minFractionToStepWhenPaging();
433     int pageStep = std::max(minPageStep, length - maxOverlapBetweenPages());
434 
435     return std::max(pageStep, 1);
436 }
437 
documentStep(ScrollbarOrientation orientation) const438 int ScrollableArea::documentStep(ScrollbarOrientation orientation) const
439 {
440     return scrollSize(orientation);
441 }
442 
pixelStep(ScrollbarOrientation) const443 float ScrollableArea::pixelStep(ScrollbarOrientation) const
444 {
445     return 1;
446 }
447 
448 } // namespace WebCore
449