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