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