• 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/HostWindow.h"
36 #include "platform/Logging.h"
37 #include "platform/graphics/GraphicsLayer.h"
38 #include "platform/geometry/FloatPoint.h"
39 #include "platform/scroll/ProgrammaticScrollAnimator.h"
40 #include "platform/scroll/ScrollbarTheme.h"
41 #include "wtf/PassOwnPtr.h"
42 
43 #include "platform/TraceEvent.h"
44 
45 static const int kPixelsPerLineStep = 40;
46 static const float kMinFractionToStepWhenPaging = 0.875f;
47 
48 namespace blink {
49 
50 struct SameSizeAsScrollableArea {
51     virtual ~SameSizeAsScrollableArea();
52     IntRect scrollbarDamage[2];
53     void* pointer;
54     unsigned bitfields : 16;
55     IntPoint origin;
56 };
57 
58 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
59 
pixelsPerLineStep()60 int ScrollableArea::pixelsPerLineStep()
61 {
62     return kPixelsPerLineStep;
63 }
64 
minFractionToStepWhenPaging()65 float ScrollableArea::minFractionToStepWhenPaging()
66 {
67     return kMinFractionToStepWhenPaging;
68 }
69 
maxOverlapBetweenPages()70 int ScrollableArea::maxOverlapBetweenPages()
71 {
72     static int maxOverlapBetweenPages = ScrollbarTheme::theme()->maxOverlapBetweenPages();
73     return maxOverlapBetweenPages;
74 }
75 
ScrollableArea()76 ScrollableArea::ScrollableArea()
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_animators)
93         m_animators = adoptPtr(new ScrollableAreaAnimators);
94 
95     if (!m_animators->scrollAnimator)
96         m_animators->scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea*>(this));
97 
98     return m_animators->scrollAnimator.get();
99 }
100 
programmaticScrollAnimator() const101 ProgrammaticScrollAnimator* ScrollableArea::programmaticScrollAnimator() const
102 {
103     if (!m_animators)
104         m_animators = adoptPtr(new ScrollableAreaAnimators);
105 
106     if (!m_animators->programmaticScrollAnimator)
107         m_animators->programmaticScrollAnimator = ProgrammaticScrollAnimator::create(const_cast<ScrollableArea*>(this));
108 
109     return m_animators->programmaticScrollAnimator.get();
110 }
111 
setScrollOrigin(const IntPoint & origin)112 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
113 {
114     if (m_scrollOrigin != origin) {
115         m_scrollOrigin = origin;
116         m_scrollOriginChanged = true;
117     }
118 }
119 
layerForContainer() const120 GraphicsLayer* ScrollableArea::layerForContainer() const
121 {
122     return layerForScrolling() ? layerForScrolling()->parent() : 0;
123 }
124 
scroll(ScrollDirection direction,ScrollGranularity granularity,float delta)125 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float delta)
126 {
127     ScrollbarOrientation orientation;
128 
129     if (direction == ScrollUp || direction == ScrollDown)
130         orientation = VerticalScrollbar;
131     else
132         orientation = HorizontalScrollbar;
133 
134     if (!userInputScrollable(orientation))
135         return false;
136 
137     cancelProgrammaticScrollAnimation();
138 
139     float step = 0;
140     switch (granularity) {
141     case ScrollByLine:
142         step = lineStep(orientation);
143         break;
144     case ScrollByPage:
145         step = pageStep(orientation);
146         break;
147     case ScrollByDocument:
148         step = documentStep(orientation);
149         break;
150     case ScrollByPixel:
151     case ScrollByPrecisePixel:
152         step = pixelStep(orientation);
153         break;
154     }
155 
156     if (direction == ScrollUp || direction == ScrollLeft)
157         delta = -delta;
158 
159     return scrollAnimator()->scroll(orientation, granularity, step, delta);
160 }
161 
scrollToOffsetWithoutAnimation(const FloatPoint & offset)162 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
163 {
164     cancelProgrammaticScrollAnimation();
165     scrollAnimator()->scrollToOffsetWithoutAnimation(offset);
166 }
167 
scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation,float offset)168 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
169 {
170     if (orientation == HorizontalScrollbar)
171         scrollToOffsetWithoutAnimation(FloatPoint(offset, scrollAnimator()->currentPosition().y()));
172     else
173         scrollToOffsetWithoutAnimation(FloatPoint(scrollAnimator()->currentPosition().x(), offset));
174 }
175 
programmaticallyScrollSmoothlyToOffset(const FloatPoint & offset)176 void ScrollableArea::programmaticallyScrollSmoothlyToOffset(const FloatPoint& offset)
177 {
178     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
179         scrollAnimator->cancelAnimations();
180     programmaticScrollAnimator()->animateToOffset(offset);
181 }
182 
notifyScrollPositionChanged(const IntPoint & position)183 void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
184 {
185     scrollPositionChanged(position);
186     scrollAnimator()->setCurrentPosition(position);
187 }
188 
scrollPositionChanged(const IntPoint & position)189 void ScrollableArea::scrollPositionChanged(const IntPoint& position)
190 {
191     TRACE_EVENT0("blink", "ScrollableArea::scrollPositionChanged");
192 
193     IntPoint oldPosition = scrollPosition();
194     // Tell the derived class to scroll its contents.
195     setScrollOffset(position);
196 
197     Scrollbar* verticalScrollbar = this->verticalScrollbar();
198 
199     // Tell the scrollbars to update their thumb postions.
200     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
201         horizontalScrollbar->offsetDidChange();
202         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
203             if (!verticalScrollbar)
204                 horizontalScrollbar->invalidate();
205             else {
206                 // If there is both a horizontalScrollbar and a verticalScrollbar,
207                 // then we must also invalidate the corner between them.
208                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
209                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
210                 horizontalScrollbar->invalidateRect(boundsAndCorner);
211             }
212         }
213     }
214     if (verticalScrollbar) {
215         verticalScrollbar->offsetDidChange();
216         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
217             verticalScrollbar->invalidate();
218     }
219 
220     if (scrollPosition() != oldPosition)
221         scrollAnimator()->notifyContentAreaScrolled(scrollPosition() - oldPosition);
222 }
223 
scrollBehaviorFromString(const String & behaviorString,ScrollBehavior & behavior)224 bool ScrollableArea::scrollBehaviorFromString(const String& behaviorString, ScrollBehavior& behavior)
225 {
226     if (behaviorString == "auto")
227         behavior = ScrollBehaviorAuto;
228     else if (behaviorString == "instant")
229         behavior = ScrollBehaviorInstant;
230     else if (behaviorString == "smooth")
231         behavior = ScrollBehaviorSmooth;
232     else
233         return false;
234 
235     return true;
236 }
237 
handleWheelEvent(const PlatformWheelEvent & wheelEvent)238 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
239 {
240     // ctrl+wheel events are used to trigger zooming, not scrolling.
241     if (wheelEvent.modifiers() & PlatformEvent::CtrlKey)
242         return false;
243 
244     cancelProgrammaticScrollAnimation();
245     return scrollAnimator()->handleWheelEvent(wheelEvent);
246 }
247 
248 // NOTE: Only called from Internals for testing.
setScrollOffsetFromInternals(const IntPoint & offset)249 void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
250 {
251     setScrollOffsetFromAnimation(offset);
252 }
253 
setScrollOffsetFromAnimation(const IntPoint & offset)254 void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset)
255 {
256     scrollPositionChanged(offset);
257 }
258 
willStartLiveResize()259 void ScrollableArea::willStartLiveResize()
260 {
261     if (m_inLiveResize)
262         return;
263     m_inLiveResize = true;
264     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
265         scrollAnimator->willStartLiveResize();
266 }
267 
willEndLiveResize()268 void ScrollableArea::willEndLiveResize()
269 {
270     if (!m_inLiveResize)
271         return;
272     m_inLiveResize = false;
273     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
274         scrollAnimator->willEndLiveResize();
275 }
276 
contentAreaWillPaint() const277 void ScrollableArea::contentAreaWillPaint() const
278 {
279     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
280         scrollAnimator->contentAreaWillPaint();
281 }
282 
mouseEnteredContentArea() const283 void ScrollableArea::mouseEnteredContentArea() const
284 {
285     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
286         scrollAnimator->mouseEnteredContentArea();
287 }
288 
mouseExitedContentArea() const289 void ScrollableArea::mouseExitedContentArea() const
290 {
291     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
292         scrollAnimator->mouseEnteredContentArea();
293 }
294 
mouseMovedInContentArea() const295 void ScrollableArea::mouseMovedInContentArea() const
296 {
297     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
298         scrollAnimator->mouseMovedInContentArea();
299 }
300 
mouseEnteredScrollbar(Scrollbar * scrollbar) const301 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
302 {
303     scrollAnimator()->mouseEnteredScrollbar(scrollbar);
304 }
305 
mouseExitedScrollbar(Scrollbar * scrollbar) const306 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
307 {
308     scrollAnimator()->mouseExitedScrollbar(scrollbar);
309 }
310 
contentAreaDidShow() const311 void ScrollableArea::contentAreaDidShow() const
312 {
313     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
314         scrollAnimator->contentAreaDidShow();
315 }
316 
contentAreaDidHide() const317 void ScrollableArea::contentAreaDidHide() const
318 {
319     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
320         scrollAnimator->contentAreaDidHide();
321 }
322 
finishCurrentScrollAnimations() const323 void ScrollableArea::finishCurrentScrollAnimations() const
324 {
325     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
326         scrollAnimator->finishCurrentScrollAnimations();
327 }
328 
didAddScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)329 void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
330 {
331     if (orientation == VerticalScrollbar)
332         scrollAnimator()->didAddVerticalScrollbar(scrollbar);
333     else
334         scrollAnimator()->didAddHorizontalScrollbar(scrollbar);
335 
336     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
337     setScrollbarOverlayStyle(scrollbarOverlayStyle());
338 }
339 
willRemoveScrollbar(Scrollbar * scrollbar,ScrollbarOrientation orientation)340 void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
341 {
342     if (orientation == VerticalScrollbar)
343         scrollAnimator()->willRemoveVerticalScrollbar(scrollbar);
344     else
345         scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar);
346 }
347 
contentsResized()348 void ScrollableArea::contentsResized()
349 {
350     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
351         scrollAnimator->contentsResized();
352 }
353 
hasOverlayScrollbars() const354 bool ScrollableArea::hasOverlayScrollbars() const
355 {
356     Scrollbar* vScrollbar = verticalScrollbar();
357     if (vScrollbar && vScrollbar->isOverlayScrollbar())
358         return true;
359     Scrollbar* hScrollbar = horizontalScrollbar();
360     return hScrollbar && hScrollbar->isOverlayScrollbar();
361 }
362 
setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)363 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
364 {
365     m_scrollbarOverlayStyle = overlayStyle;
366 
367     if (Scrollbar* scrollbar = horizontalScrollbar()) {
368         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(scrollbar);
369         scrollbar->invalidate();
370     }
371 
372     if (Scrollbar* scrollbar = verticalScrollbar()) {
373         ScrollbarTheme::theme()->updateScrollbarOverlayStyle(scrollbar);
374         scrollbar->invalidate();
375     }
376 }
377 
invalidateScrollbar(Scrollbar * scrollbar,const IntRect & rect)378 void ScrollableArea::invalidateScrollbar(Scrollbar* scrollbar, const IntRect& rect)
379 {
380     if (scrollbar == horizontalScrollbar()) {
381         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
382             graphicsLayer->setNeedsDisplay();
383             graphicsLayer->setContentsNeedsDisplay();
384             return;
385         }
386     } else if (scrollbar == verticalScrollbar()) {
387         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
388             graphicsLayer->setNeedsDisplay();
389             graphicsLayer->setContentsNeedsDisplay();
390             return;
391         }
392     }
393     invalidateScrollbarRect(scrollbar, rect);
394 }
395 
invalidateScrollCorner(const IntRect & rect)396 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
397 {
398     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
399         graphicsLayer->setNeedsDisplay();
400         return;
401     }
402     invalidateScrollCornerRect(rect);
403 }
404 
hasLayerForHorizontalScrollbar() const405 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
406 {
407     return layerForHorizontalScrollbar();
408 }
409 
hasLayerForVerticalScrollbar() const410 bool ScrollableArea::hasLayerForVerticalScrollbar() const
411 {
412     return layerForVerticalScrollbar();
413 }
414 
hasLayerForScrollCorner() const415 bool ScrollableArea::hasLayerForScrollCorner() const
416 {
417     return layerForScrollCorner();
418 }
419 
scheduleAnimation()420 bool ScrollableArea::scheduleAnimation()
421 {
422     if (HostWindow* window = hostWindow()) {
423         window->scheduleAnimation();
424         return true;
425     }
426     return false;
427 }
428 
serviceScrollAnimations(double monotonicTime)429 void ScrollableArea::serviceScrollAnimations(double monotonicTime)
430 {
431     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
432         scrollAnimator->serviceScrollAnimations();
433     if (ProgrammaticScrollAnimator* programmaticScrollAnimator = existingProgrammaticScrollAnimator())
434         programmaticScrollAnimator->tickAnimation(monotonicTime);
435 }
436 
cancelProgrammaticScrollAnimation()437 void ScrollableArea::cancelProgrammaticScrollAnimation()
438 {
439     if (ProgrammaticScrollAnimator* programmaticScrollAnimator = existingProgrammaticScrollAnimator())
440         programmaticScrollAnimator->cancelAnimation();
441 }
442 
visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const443 IntRect ScrollableArea::visibleContentRect(IncludeScrollbarsInRect scrollbarInclusion) const
444 {
445     int verticalScrollbarWidth = 0;
446     int horizontalScrollbarHeight = 0;
447 
448     if (scrollbarInclusion == IncludeScrollbars) {
449         if (Scrollbar* verticalBar = verticalScrollbar())
450             verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0;
451         if (Scrollbar* horizontalBar = horizontalScrollbar())
452             horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0;
453     }
454 
455     return IntRect(scrollPosition().x(),
456                    scrollPosition().y(),
457                    std::max(0, visibleWidth() + verticalScrollbarWidth),
458                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
459 }
460 
clampScrollPosition(const IntPoint & scrollPosition) const461 IntPoint ScrollableArea::clampScrollPosition(const IntPoint& scrollPosition) const
462 {
463     return scrollPosition.shrunkTo(maximumScrollPosition()).expandedTo(minimumScrollPosition());
464 }
465 
lineStep(ScrollbarOrientation) const466 int ScrollableArea::lineStep(ScrollbarOrientation) const
467 {
468     return pixelsPerLineStep();
469 }
470 
pageStep(ScrollbarOrientation orientation) const471 int ScrollableArea::pageStep(ScrollbarOrientation orientation) const
472 {
473     int length = (orientation == HorizontalScrollbar) ? visibleWidth() : visibleHeight();
474     int minPageStep = static_cast<float>(length) * minFractionToStepWhenPaging();
475     int pageStep = std::max(minPageStep, length - maxOverlapBetweenPages());
476 
477     return std::max(pageStep, 1);
478 }
479 
documentStep(ScrollbarOrientation orientation) const480 int ScrollableArea::documentStep(ScrollbarOrientation orientation) const
481 {
482     return scrollSize(orientation);
483 }
484 
pixelStep(ScrollbarOrientation) const485 float ScrollableArea::pixelStep(ScrollbarOrientation) const
486 {
487     return 1;
488 }
489 
490 } // namespace blink
491