Migrate from ints to unsigneds when referring to indices into strings
[WebKit-https.git] / Source / WebCore / platform / ScrollableArea.cpp
1 /*
2  * Copyright (c) 2010, Google Inc. All rights reserved.
3  * Copyright (C) 2008, 2011, 2014-2016 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 "ScrollableArea.h"
34
35 #include "GraphicsContext.h"
36 #include "GraphicsLayer.h"
37 #include "FloatPoint.h"
38 #include "LayoutRect.h"
39 #include "Logging.h"
40 #include "PlatformWheelEvent.h"
41 #include "ScrollAnimator.h"
42 #include "ScrollAnimatorMock.h"
43 #include "ScrollbarTheme.h"
44 #include "TextStream.h"
45
46 namespace WebCore {
47
48 struct SameSizeAsScrollableArea {
49     virtual ~SameSizeAsScrollableArea();
50 #if ENABLE(CSS_SCROLL_SNAP)
51     void* pointers[4];
52     unsigned currentIndices[2];
53 #else
54     void* pointer[2];
55 #endif
56     IntPoint origin;
57     unsigned bitfields : 16;
58 };
59
60 COMPILE_ASSERT(sizeof(ScrollableArea) == sizeof(SameSizeAsScrollableArea), ScrollableArea_should_stay_small);
61
62 ScrollableArea::ScrollableArea()
63     : m_constrainsScrollingToContentEdge(true)
64     , m_inLiveResize(false)
65     , m_verticalScrollElasticity(ScrollElasticityNone)
66     , m_horizontalScrollElasticity(ScrollElasticityNone)
67     , m_scrollbarOverlayStyle(ScrollbarOverlayStyleDefault)
68     , m_scrollOriginChanged(false)
69     , m_scrolledProgrammatically(false)
70 {
71 }
72
73 ScrollableArea::~ScrollableArea()
74 {
75 }
76
77 ScrollAnimator& ScrollableArea::scrollAnimator() const
78 {
79     if (!m_scrollAnimator) {
80         if (usesMockScrollAnimator()) {
81             m_scrollAnimator = std::make_unique<ScrollAnimatorMock>(const_cast<ScrollableArea&>(*this), [this](const String& message) {
82                 logMockScrollAnimatorMessage(message);
83             });
84         } else
85             m_scrollAnimator = ScrollAnimator::create(const_cast<ScrollableArea&>(*this));
86     }
87
88     ASSERT(m_scrollAnimator);
89     return *m_scrollAnimator.get();
90 }
91
92 void ScrollableArea::setScrollOrigin(const IntPoint& origin)
93 {
94     if (m_scrollOrigin != origin) {
95         m_scrollOrigin = origin;
96         m_scrollOriginChanged = true;
97     }
98 }
99
100 float ScrollableArea::adjustScrollStepForFixedContent(float step, ScrollbarOrientation, ScrollGranularity)
101 {
102     return step;
103 }
104
105 bool ScrollableArea::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
106 {
107     ScrollbarOrientation orientation;
108     Scrollbar* scrollbar;
109     if (direction == ScrollUp || direction == ScrollDown) {
110         orientation = VerticalScrollbar;
111         scrollbar = verticalScrollbar();
112     } else {
113         orientation = HorizontalScrollbar;
114         scrollbar = horizontalScrollbar();
115     }
116
117     if (!scrollbar)
118         return false;
119
120     float step = 0;
121     switch (granularity) {
122     case ScrollByLine:
123         step = scrollbar->lineStep();
124         break;
125     case ScrollByPage:
126         step = scrollbar->pageStep();
127         break;
128     case ScrollByDocument:
129         step = scrollbar->totalSize();
130         break;
131     case ScrollByPixel:
132     case ScrollByPrecisePixel:
133         step = scrollbar->pixelStep();
134         break;
135     }
136
137     if (direction == ScrollUp || direction == ScrollLeft)
138         multiplier = -multiplier;
139
140     step = adjustScrollStepForFixedContent(step, orientation, granularity);
141     return scrollAnimator().scroll(orientation, granularity, step, multiplier);
142 }
143
144 void ScrollableArea::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
145 {
146     LOG_WITH_STREAM(Scrolling, stream << "ScrollableArea " << this << " scrollToOffsetWithoutAnimation " << offset);
147     scrollAnimator().scrollToOffsetWithoutAnimation(offset);
148 }
149
150 void ScrollableArea::scrollToOffsetWithoutAnimation(ScrollbarOrientation orientation, float offset)
151 {
152     auto currentOffset = scrollOffsetFromPosition(IntPoint(scrollAnimator().currentPosition()));
153     if (orientation == HorizontalScrollbar)
154         scrollToOffsetWithoutAnimation(FloatPoint(offset, currentOffset.y()));
155     else
156         scrollToOffsetWithoutAnimation(FloatPoint(currentOffset.x(), offset));
157 }
158
159 void ScrollableArea::notifyScrollPositionChanged(const ScrollPosition& position)
160 {
161     scrollPositionChanged(position);
162     scrollAnimator().setCurrentPosition(position);
163 }
164
165 void ScrollableArea::scrollPositionChanged(const ScrollPosition& position)
166 {
167     IntPoint oldPosition = scrollPosition();
168     // Tell the derived class to scroll its contents.
169     setScrollOffset(scrollOffsetFromPosition(position));
170
171     Scrollbar* verticalScrollbar = this->verticalScrollbar();
172
173     // Tell the scrollbars to update their thumb postions.
174     if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) {
175         horizontalScrollbar->offsetDidChange();
176         if (horizontalScrollbar->isOverlayScrollbar() && !hasLayerForHorizontalScrollbar()) {
177             if (!verticalScrollbar)
178                 horizontalScrollbar->invalidate();
179             else {
180                 // If there is both a horizontalScrollbar and a verticalScrollbar,
181                 // then we must also invalidate the corner between them.
182                 IntRect boundsAndCorner = horizontalScrollbar->boundsRect();
183                 boundsAndCorner.setWidth(boundsAndCorner.width() + verticalScrollbar->width());
184                 horizontalScrollbar->invalidateRect(boundsAndCorner);
185             }
186         }
187     }
188     if (verticalScrollbar) {
189         verticalScrollbar->offsetDidChange();
190         if (verticalScrollbar->isOverlayScrollbar() && !hasLayerForVerticalScrollbar())
191             verticalScrollbar->invalidate();
192     }
193
194     if (scrollPosition() != oldPosition)
195         scrollAnimator().notifyContentAreaScrolled(scrollPosition() - oldPosition);
196 }
197
198 bool ScrollableArea::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
199 {
200     if (!isScrollableOrRubberbandable())
201         return false;
202
203     bool handledEvent = scrollAnimator().handleWheelEvent(wheelEvent);
204 #if ENABLE(CSS_SCROLL_SNAP)
205     if (scrollAnimator().activeScrollSnapIndexDidChange()) {
206         setCurrentHorizontalSnapPointIndex(scrollAnimator().activeScrollSnapIndexForAxis(ScrollEventAxis::Horizontal));
207         setCurrentVerticalSnapPointIndex(scrollAnimator().activeScrollSnapIndexForAxis(ScrollEventAxis::Vertical));
208     }
209 #endif
210     return handledEvent;
211 }
212
213 #if ENABLE(TOUCH_EVENTS)
214 bool ScrollableArea::handleTouchEvent(const PlatformTouchEvent& touchEvent)
215 {
216     return scrollAnimator().handleTouchEvent(touchEvent);
217 }
218 #endif
219
220 // NOTE: Only called from Internals for testing.
221 void ScrollableArea::setScrollOffsetFromInternals(const ScrollOffset& offset)
222 {
223     setScrollOffsetFromAnimation(offset);
224 }
225
226 void ScrollableArea::setScrollOffsetFromAnimation(const ScrollOffset& offset)
227 {
228     ScrollPosition position = scrollPositionFromOffset(offset);
229     if (requestScrollPositionUpdate(position))
230         return;
231
232     scrollPositionChanged(position);
233 }
234
235 void ScrollableArea::willStartLiveResize()
236 {
237     if (m_inLiveResize)
238         return;
239     m_inLiveResize = true;
240     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
241         scrollAnimator->willStartLiveResize();
242 }
243
244 void ScrollableArea::willEndLiveResize()
245 {
246     if (!m_inLiveResize)
247         return;
248     m_inLiveResize = false;
249     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
250         scrollAnimator->willEndLiveResize();
251 }    
252
253 void ScrollableArea::contentAreaWillPaint() const
254 {
255     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
256         scrollAnimator->contentAreaWillPaint();
257 }
258
259 void ScrollableArea::mouseEnteredContentArea() const
260 {
261     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
262         scrollAnimator->mouseEnteredContentArea();
263 }
264
265 void ScrollableArea::mouseExitedContentArea() const
266 {
267     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
268         scrollAnimator->mouseExitedContentArea();
269 }
270
271 void ScrollableArea::mouseMovedInContentArea() const
272 {
273     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
274         scrollAnimator->mouseMovedInContentArea();
275 }
276
277 void ScrollableArea::mouseEnteredScrollbar(Scrollbar* scrollbar) const
278 {
279     scrollAnimator().mouseEnteredScrollbar(scrollbar);
280 }
281
282 void ScrollableArea::mouseExitedScrollbar(Scrollbar* scrollbar) const
283 {
284     scrollAnimator().mouseExitedScrollbar(scrollbar);
285 }
286
287 void ScrollableArea::mouseIsDownInScrollbar(Scrollbar* scrollbar, bool mouseIsDown) const
288 {
289     scrollAnimator().mouseIsDownInScrollbar(scrollbar, mouseIsDown);
290 }
291
292 void ScrollableArea::contentAreaDidShow() const
293 {
294     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
295         scrollAnimator->contentAreaDidShow();
296 }
297
298 void ScrollableArea::contentAreaDidHide() const
299 {
300     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
301         scrollAnimator->contentAreaDidHide();
302 }
303
304 void ScrollableArea::lockOverlayScrollbarStateToHidden(bool shouldLockState) const
305 {
306     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
307         scrollAnimator->lockOverlayScrollbarStateToHidden(shouldLockState);
308 }
309
310 bool ScrollableArea::scrollbarsCanBeActive() const
311 {
312     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
313         return scrollAnimator->scrollbarsCanBeActive();
314     return true;
315 }
316
317 void ScrollableArea::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
318 {
319     if (orientation == VerticalScrollbar)
320         scrollAnimator().didAddVerticalScrollbar(scrollbar);
321     else
322         scrollAnimator().didAddHorizontalScrollbar(scrollbar);
323
324     // <rdar://problem/9797253> AppKit resets the scrollbar's style when you attach a scrollbar
325     setScrollbarOverlayStyle(scrollbarOverlayStyle());
326 }
327
328 void ScrollableArea::willRemoveScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
329 {
330     if (orientation == VerticalScrollbar)
331         scrollAnimator().willRemoveVerticalScrollbar(scrollbar);
332     else
333         scrollAnimator().willRemoveHorizontalScrollbar(scrollbar);
334 }
335
336 void ScrollableArea::contentsResized()
337 {
338     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
339         scrollAnimator->contentsResized();
340 }
341
342 void ScrollableArea::availableContentSizeChanged(AvailableSizeChangeReason)
343 {
344     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
345         scrollAnimator->contentsResized(); // This flashes overlay scrollbars.
346 }
347
348 bool ScrollableArea::hasOverlayScrollbars() const
349 {
350     return (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
351         || (horizontalScrollbar() && horizontalScrollbar()->isOverlayScrollbar());
352 }
353
354 void ScrollableArea::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
355 {
356     m_scrollbarOverlayStyle = overlayStyle;
357
358     if (horizontalScrollbar()) {
359         ScrollbarTheme::theme().updateScrollbarOverlayStyle(*horizontalScrollbar());
360         horizontalScrollbar()->invalidate();
361         if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
362             scrollAnimator->invalidateScrollbarPartLayers(horizontalScrollbar());
363     }
364     
365     if (verticalScrollbar()) {
366         ScrollbarTheme::theme().updateScrollbarOverlayStyle(*verticalScrollbar());
367         verticalScrollbar()->invalidate();
368         if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
369             scrollAnimator->invalidateScrollbarPartLayers(verticalScrollbar());
370     }
371 }
372
373 void ScrollableArea::invalidateScrollbar(Scrollbar& scrollbar, const IntRect& rect)
374 {
375     if (&scrollbar == horizontalScrollbar()) {
376         if (GraphicsLayer* graphicsLayer = layerForHorizontalScrollbar()) {
377             graphicsLayer->setNeedsDisplay();
378             graphicsLayer->setContentsNeedsDisplay();
379             return;
380         }
381     } else if (&scrollbar == verticalScrollbar()) {
382         if (GraphicsLayer* graphicsLayer = layerForVerticalScrollbar()) {
383             graphicsLayer->setNeedsDisplay();
384             graphicsLayer->setContentsNeedsDisplay();
385             return;
386         }
387     }
388
389     invalidateScrollbarRect(scrollbar, rect);
390 }
391
392 void ScrollableArea::invalidateScrollCorner(const IntRect& rect)
393 {
394     if (GraphicsLayer* graphicsLayer = layerForScrollCorner()) {
395         graphicsLayer->setNeedsDisplay();
396         return;
397     }
398
399     invalidateScrollCornerRect(rect);
400 }
401
402 void ScrollableArea::verticalScrollbarLayerDidChange()
403 {
404     scrollAnimator().verticalScrollbarLayerDidChange();
405 }
406
407 void ScrollableArea::horizontalScrollbarLayerDidChange()
408 {
409     scrollAnimator().horizontalScrollbarLayerDidChange();
410 }
411
412 bool ScrollableArea::hasLayerForHorizontalScrollbar() const
413 {
414     return layerForHorizontalScrollbar();
415 }
416
417 bool ScrollableArea::hasLayerForVerticalScrollbar() const
418 {
419     return layerForVerticalScrollbar();
420 }
421
422 bool ScrollableArea::hasLayerForScrollCorner() const
423 {
424     return layerForScrollCorner();
425 }
426
427 #if ENABLE(CSS_SCROLL_SNAP)
428 void ScrollableArea::setHorizontalSnapOffsets(std::unique_ptr<Vector<LayoutUnit>> horizontalSnapOffsets)
429 {
430     m_horizontalSnapOffsets = WTFMove(horizontalSnapOffsets);
431 }
432
433 void ScrollableArea::setVerticalSnapOffsets(std::unique_ptr<Vector<LayoutUnit>> verticalSnapOffsets)
434 {
435     m_verticalSnapOffsets = WTFMove(verticalSnapOffsets);
436 }
437
438 void ScrollableArea::clearHorizontalSnapOffsets()
439 {
440     m_horizontalSnapOffsets = nullptr;
441     m_currentHorizontalSnapPointIndex = 0;
442 }
443
444 void ScrollableArea::clearVerticalSnapOffsets()
445 {
446     m_verticalSnapOffsets = nullptr;
447     m_currentVerticalSnapPointIndex = 0;
448 }
449
450 IntPoint ScrollableArea::nearestActiveSnapPoint(const IntPoint& currentPosition)
451 {
452     if (!horizontalSnapOffsets() && !verticalSnapOffsets())
453         return currentPosition;
454     
455     if (!existingScrollAnimator())
456         return currentPosition;
457     
458     IntPoint correctedPosition = currentPosition;
459     
460     if (horizontalSnapOffsets()) {
461         const auto& horizontal = *horizontalSnapOffsets();
462         
463         size_t activeIndex = currentHorizontalSnapPointIndex();
464         if (activeIndex < horizontal.size())
465             correctedPosition.setX(horizontal[activeIndex].toInt());
466     }
467     
468     if (verticalSnapOffsets()) {
469         const auto& vertical = *verticalSnapOffsets();
470         
471         size_t activeIndex = currentVerticalSnapPointIndex();
472         if (activeIndex < vertical.size())
473             correctedPosition.setY(vertical[activeIndex].toInt());
474     }
475
476     return correctedPosition;
477 }
478
479 void ScrollableArea::updateScrollSnapState()
480 {
481     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
482         scrollAnimator->updateScrollSnapState();
483
484     if (isScrollSnapInProgress())
485         return;
486
487     IntPoint currentPosition = scrollPosition();
488     IntPoint correctedPosition = nearestActiveSnapPoint(currentPosition);
489     
490     if (correctedPosition != currentPosition)
491         scrollToOffsetWithoutAnimation(correctedPosition);
492 }
493 #else
494 void ScrollableArea::updateScrollSnapState()
495 {
496 }
497 #endif
498
499
500 void ScrollableArea::serviceScrollAnimations()
501 {
502     if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
503         scrollAnimator->serviceScrollAnimations();
504 }
505
506 #if PLATFORM(IOS)
507 bool ScrollableArea::isPinnedInBothDirections(const IntSize& scrollDelta) const
508 {
509     return isPinnedHorizontallyInDirection(scrollDelta.width()) && isPinnedVerticallyInDirection(scrollDelta.height());
510 }
511
512 bool ScrollableArea::isPinnedHorizontallyInDirection(int horizontalScrollDelta) const
513 {
514     if (horizontalScrollDelta < 0 && isHorizontalScrollerPinnedToMinimumPosition())
515         return true;
516     if (horizontalScrollDelta > 0 && isHorizontalScrollerPinnedToMaximumPosition())
517         return true;
518     return false;
519 }
520
521 bool ScrollableArea::isPinnedVerticallyInDirection(int verticalScrollDelta) const
522 {
523     if (verticalScrollDelta < 0 && isVerticalScrollerPinnedToMinimumPosition())
524         return true;
525     if (verticalScrollDelta > 0 && isVerticalScrollerPinnedToMaximumPosition())
526         return true;
527     return false;
528 }
529 #endif // PLATFORM(IOS)
530
531 int ScrollableArea::horizontalScrollbarIntrusion() const
532 {
533     return verticalScrollbar() ? verticalScrollbar()->occupiedWidth() : 0;
534 }
535
536 int ScrollableArea::verticalScrollbarIntrusion() const
537 {
538     return horizontalScrollbar() ? horizontalScrollbar()->occupiedHeight() : 0;
539 }
540
541 IntSize ScrollableArea::scrollbarIntrusion() const
542 {
543     return { horizontalScrollbarIntrusion(), verticalScrollbarIntrusion() };
544 }
545
546 ScrollPosition ScrollableArea::scrollPosition() const
547 {
548     // FIXME: This relationship seems to be inverted. Scrollbars should be 'view', not 'model', and should get their values from us.
549     int x = horizontalScrollbar() ? horizontalScrollbar()->value() : 0;
550     int y = verticalScrollbar() ? verticalScrollbar()->value() : 0;
551     return IntPoint(x, y);
552 }
553
554 ScrollPosition ScrollableArea::minimumScrollPosition() const
555 {
556     return scrollPositionFromOffset(ScrollPosition());
557 }
558
559 ScrollPosition ScrollableArea::maximumScrollPosition() const
560 {
561     return scrollPositionFromOffset(ScrollPosition(totalContentsSize() - visibleSize()));
562 }
563
564 ScrollOffset ScrollableArea::maximumScrollOffset() const
565 {
566     return ScrollOffset(totalContentsSize() - visibleSize());
567 }
568
569 ScrollPosition ScrollableArea::scrollPositionFromOffset(ScrollOffset offset) const
570 {
571     return scrollPositionFromOffset(offset, toIntSize(m_scrollOrigin));
572 }
573
574 ScrollOffset ScrollableArea::scrollOffsetFromPosition(ScrollPosition position) const
575 {
576     return scrollOffsetFromPosition(position, toIntSize(m_scrollOrigin));
577 }
578
579 bool ScrollableArea::scrolledToTop() const
580 {
581     return scrollPosition().y() <= minimumScrollPosition().y();
582 }
583
584 bool ScrollableArea::scrolledToBottom() const
585 {
586     return scrollPosition().y() >= maximumScrollPosition().y();
587 }
588
589 bool ScrollableArea::scrolledToLeft() const
590 {
591     return scrollPosition().x() <= minimumScrollPosition().x();
592 }
593
594 bool ScrollableArea::scrolledToRight() const
595 {
596     return scrollPosition().x() >= maximumScrollPosition().x();
597 }
598
599 void ScrollableArea::scrollbarStyleChanged(ScrollbarStyle, bool)
600 {
601     availableContentSizeChanged(AvailableSizeChangeReason::ScrollbarsChanged);
602 }
603
604 IntSize ScrollableArea::totalContentsSize() const
605 {
606     IntSize totalContentsSize = contentsSize();
607     totalContentsSize.setHeight(totalContentsSize.height() + headerHeight() + footerHeight());
608     return totalContentsSize;
609 }
610
611 IntRect ScrollableArea::visibleContentRect(VisibleContentRectBehavior visibleContentRectBehavior) const
612 {
613     return visibleContentRectInternal(ExcludeScrollbars, visibleContentRectBehavior);
614 }
615
616 IntRect ScrollableArea::visibleContentRectIncludingScrollbars(VisibleContentRectBehavior visibleContentRectBehavior) const
617 {
618     return visibleContentRectInternal(IncludeScrollbars, visibleContentRectBehavior);
619 }
620
621 IntRect ScrollableArea::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior) const
622 {
623     int verticalScrollbarWidth = 0;
624     int horizontalScrollbarHeight = 0;
625
626     if (scrollbarInclusion == IncludeScrollbars) {
627         if (Scrollbar* verticalBar = verticalScrollbar())
628             verticalScrollbarWidth = verticalBar->occupiedWidth();
629         if (Scrollbar* horizontalBar = horizontalScrollbar())
630             horizontalScrollbarHeight = horizontalBar->occupiedHeight();
631     }
632
633     return IntRect(scrollPosition().x(),
634                    scrollPosition().y(),
635                    std::max(0, visibleWidth() + verticalScrollbarWidth),
636                    std::max(0, visibleHeight() + horizontalScrollbarHeight));
637 }
638
639 LayoutPoint ScrollableArea::constrainScrollPositionForOverhang(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, int headerHeight, int footerHeight)
640 {
641     // The viewport rect that we're scrolling shouldn't be larger than our document.
642     LayoutSize idealScrollRectSize(std::min(visibleContentRect.width(), totalContentsSize.width()), std::min(visibleContentRect.height(), totalContentsSize.height()));
643     
644     LayoutRect scrollRect(scrollPosition + scrollOrigin - LayoutSize(0, headerHeight), idealScrollRectSize);
645     LayoutRect documentRect(LayoutPoint(), LayoutSize(totalContentsSize.width(), totalContentsSize.height() - headerHeight - footerHeight));
646
647     // Use intersection to constrain our ideal scroll rect by the document rect.
648     scrollRect.intersect(documentRect);
649
650     if (scrollRect.size() != idealScrollRectSize) {
651         // If the rect was clipped, restore its size, effectively pushing it "down" from the top left.
652         scrollRect.setSize(idealScrollRectSize);
653
654         // If we still clip, push our rect "up" from the bottom right.
655         scrollRect.intersect(documentRect);
656         if (scrollRect.width() < idealScrollRectSize.width())
657             scrollRect.move(-(idealScrollRectSize.width() - scrollRect.width()), 0);
658         if (scrollRect.height() < idealScrollRectSize.height())
659             scrollRect.move(0, -(idealScrollRectSize.height() - scrollRect.height()));
660     }
661
662     return scrollRect.location() - toLayoutSize(scrollOrigin);
663 }
664
665 LayoutPoint ScrollableArea::constrainScrollPositionForOverhang(const LayoutPoint& scrollPosition)
666 {
667     return constrainScrollPositionForOverhang(visibleContentRect(), totalContentsSize(), scrollPosition, scrollOrigin(), headerHeight(), footerHeight());
668 }
669
670 void ScrollableArea::computeScrollbarValueAndOverhang(float currentPosition, float totalSize, float visibleSize, float& doubleValue, float& overhangAmount)
671 {
672     doubleValue = 0;
673     overhangAmount = 0;
674     float maximum = totalSize - visibleSize;
675
676     if (currentPosition < 0) {
677         // Scrolled past the top.
678         doubleValue = 0;
679         overhangAmount = -currentPosition;
680     } else if (visibleSize + currentPosition > totalSize) {
681         // Scrolled past the bottom.
682         doubleValue = 1;
683         overhangAmount = currentPosition + visibleSize - totalSize;
684     } else {
685         // Within the bounds of the scrollable area.
686         if (maximum > 0)
687             doubleValue = currentPosition / maximum;
688         else
689             doubleValue = 0;
690     }
691 }
692
693 } // namespace WebCore