1/*
2 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ScrollView.h"
28
29#include "GraphicsContext.h"
30#include "GraphicsLayer.h"
31#include "HostWindow.h"
32#include "Logging.h"
33#include "PlatformMouseEvent.h"
34#include "PlatformWheelEvent.h"
35#include "ScrollAnimator.h"
36#include "Scrollbar.h"
37#include "ScrollbarTheme.h"
38#include <wtf/StdLibExtras.h>
39#include <wtf/text/TextStream.h>
40
41namespace WebCore {
42
43ScrollView::ScrollView() = default;
44
45ScrollView::~ScrollView() = default;
46
47void ScrollView::addChild(Widget& child)
48{
49 ASSERT(&child != this);
50 ASSERT(!child.parent());
51 child.setParent(this);
52 m_children.add(child);
53 if (child.platformWidget())
54 platformAddChild(&child);
55}
56
57void ScrollView::removeChild(Widget& child)
58{
59 ASSERT(child.parent() == this);
60 child.setParent(nullptr);
61 m_children.remove(&child);
62 if (child.platformWidget())
63 platformRemoveChild(&child);
64}
65
66bool ScrollView::setHasHorizontalScrollbar(bool hasBar, bool* contentSizeAffected)
67{
68 return setHasScrollbarInternal(m_horizontalScrollbar, HorizontalScrollbar, hasBar, contentSizeAffected);
69}
70
71bool ScrollView::setHasVerticalScrollbar(bool hasBar, bool* contentSizeAffected)
72{
73 return setHasScrollbarInternal(m_verticalScrollbar, VerticalScrollbar, hasBar, contentSizeAffected);
74}
75
76bool ScrollView::setHasScrollbarInternal(RefPtr<Scrollbar>& scrollbar, ScrollbarOrientation orientation, bool hasBar, bool* contentSizeAffected)
77{
78 ASSERT(!hasBar || !avoidScrollbarCreation());
79
80 if (hasBar && !scrollbar) {
81 scrollbar = createScrollbar(orientation);
82 addChild(*scrollbar);
83 didAddScrollbar(scrollbar.get(), orientation);
84 scrollbar->styleChanged();
85 if (contentSizeAffected)
86 *contentSizeAffected = !scrollbar->isOverlayScrollbar();
87 return true;
88 }
89
90 if (!hasBar && scrollbar) {
91 bool wasOverlayScrollbar = scrollbar->isOverlayScrollbar();
92 willRemoveScrollbar(scrollbar.get(), orientation);
93 removeChild(*scrollbar);
94 scrollbar = nullptr;
95 if (contentSizeAffected)
96 *contentSizeAffected = !wasOverlayScrollbar;
97 return true;
98 }
99
100 return false;
101}
102
103Ref<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
104{
105 return Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
106}
107
108void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
109 bool horizontalLock, bool verticalLock)
110{
111 bool needsUpdate = false;
112
113 if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
114 m_horizontalScrollbarMode = horizontalMode;
115 needsUpdate = true;
116 }
117
118 if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
119 m_verticalScrollbarMode = verticalMode;
120 needsUpdate = true;
121 }
122
123 if (horizontalLock)
124 setHorizontalScrollbarLock();
125
126 if (verticalLock)
127 setVerticalScrollbarLock();
128
129 if (!needsUpdate)
130 return;
131
132 if (platformWidget())
133 platformSetScrollbarModes();
134 else
135 updateScrollbars(scrollPosition());
136}
137
138void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
139{
140 if (platformWidget()) {
141 platformScrollbarModes(horizontalMode, verticalMode);
142 return;
143 }
144 horizontalMode = m_horizontalScrollbarMode;
145 verticalMode = m_verticalScrollbarMode;
146}
147
148void ScrollView::setCanHaveScrollbars(bool canScroll)
149{
150 ScrollbarMode newHorizontalMode;
151 ScrollbarMode newVerticalMode;
152
153 scrollbarModes(newHorizontalMode, newVerticalMode);
154
155 if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
156 newVerticalMode = ScrollbarAuto;
157 else if (!canScroll)
158 newVerticalMode = ScrollbarAlwaysOff;
159
160 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
161 newHorizontalMode = ScrollbarAuto;
162 else if (!canScroll)
163 newHorizontalMode = ScrollbarAlwaysOff;
164
165 setScrollbarModes(newHorizontalMode, newVerticalMode);
166}
167
168void ScrollView::setCanBlitOnScroll(bool b)
169{
170 if (platformWidget()) {
171 platformSetCanBlitOnScroll(b);
172 return;
173 }
174
175 m_canBlitOnScroll = b;
176}
177
178bool ScrollView::canBlitOnScroll() const
179{
180 if (platformWidget())
181 return platformCanBlitOnScroll();
182
183 return m_canBlitOnScroll;
184}
185
186void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
187{
188 m_paintsEntireContents = paintsEntireContents;
189}
190
191void ScrollView::setDelegatesScrolling(bool delegatesScrolling)
192{
193 if (m_delegatesScrolling == delegatesScrolling)
194 return;
195
196 m_delegatesScrolling = delegatesScrolling;
197 delegatesScrollingDidChange();
198}
199
200IntPoint ScrollView::contentsScrollPosition() const
201{
202#if PLATFORM(IOS_FAMILY)
203 if (platformWidget())
204 return actualScrollPosition();
205#endif
206 return scrollPosition();
207}
208
209void ScrollView::setContentsScrollPosition(const IntPoint& position)
210{
211#if PLATFORM(IOS_FAMILY)
212 if (platformWidget())
213 setActualScrollPosition(position);
214#endif
215 setScrollPosition(position);
216}
217
218#if !PLATFORM(IOS_FAMILY)
219IntRect ScrollView::unobscuredContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
220{
221 return unobscuredContentRectInternal(scrollbarInclusion);
222}
223#endif
224
225IntRect ScrollView::unobscuredContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
226{
227 FloatSize visibleContentSize = sizeForUnobscuredContent(scrollbarInclusion);
228 visibleContentSize.scale(1 / visibleContentScaleFactor());
229 return IntRect(m_scrollPosition, expandedIntSize(visibleContentSize));
230}
231
232IntSize ScrollView::sizeForVisibleContent(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
233{
234 if (platformWidget())
235 return platformVisibleContentSizeIncludingObscuredArea(scrollbarInclusion == IncludeScrollbars);
236
237#if USE(COORDINATED_GRAPHICS)
238 if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
239 return m_fixedVisibleContentRect.size();
240#endif
241
242 IntSize scrollbarSpace;
243 if (scrollbarInclusion == ExcludeScrollbars)
244 scrollbarSpace = scrollbarIntrusion();
245
246 return IntSize(width() - scrollbarSpace.width(), height() - scrollbarSpace.height()).expandedTo(IntSize());
247}
248
249IntSize ScrollView::sizeForUnobscuredContent(VisibleContentRectIncludesScrollbars scrollbarInclusion) const
250{
251 if (platformWidget())
252 return platformVisibleContentSize(scrollbarInclusion == IncludeScrollbars);
253
254 IntSize visibleContentSize = sizeForVisibleContent(scrollbarInclusion);
255
256#if USE(COORDINATED_GRAPHICS)
257 if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
258 return visibleContentSize;
259#endif
260
261 visibleContentSize.setHeight(visibleContentSize.height() - topContentInset());
262 return visibleContentSize;
263}
264
265IntRect ScrollView::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior visibleContentRectBehavior) const
266{
267#if PLATFORM(IOS_FAMILY)
268 if (visibleContentRectBehavior == LegacyIOSDocumentViewRect) {
269 if (platformWidget())
270 return platformVisibleContentRect(scrollbarInclusion == IncludeScrollbars);
271 }
272
273 if (platformWidget())
274 return unobscuredContentRect(scrollbarInclusion);
275#else
276 UNUSED_PARAM(visibleContentRectBehavior);
277#endif
278
279 if (platformWidget())
280 return platformVisibleContentRect(scrollbarInclusion == IncludeScrollbars);
281
282#if USE(COORDINATED_GRAPHICS)
283 if (m_useFixedLayout && !m_fixedVisibleContentRect.isEmpty())
284 return m_fixedVisibleContentRect;
285#endif
286
287 return unobscuredContentRect(scrollbarInclusion);
288}
289
290IntSize ScrollView::layoutSize() const
291{
292 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? sizeForUnobscuredContent() : m_fixedLayoutSize;
293}
294
295IntSize ScrollView::fixedLayoutSize() const
296{
297 return m_fixedLayoutSize;
298}
299
300void ScrollView::setFixedLayoutSize(const IntSize& newSize)
301{
302 if (fixedLayoutSize() == newSize)
303 return;
304
305 LOG_WITH_STREAM(Layout, stream << "ScrollView " << this << " setFixedLayoutSize " << newSize);
306 m_fixedLayoutSize = newSize;
307 if (m_useFixedLayout)
308 availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
309}
310
311bool ScrollView::useFixedLayout() const
312{
313 return m_useFixedLayout;
314}
315
316void ScrollView::setUseFixedLayout(bool enable)
317{
318 if (useFixedLayout() == enable)
319 return;
320 m_useFixedLayout = enable;
321 if (!m_fixedLayoutSize.isEmpty())
322 availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
323}
324
325void ScrollView::availableContentSizeChanged(AvailableSizeChangeReason reason)
326{
327 ScrollableArea::availableContentSizeChanged(reason);
328
329 if (platformWidget())
330 return;
331
332 if (reason != AvailableSizeChangeReason::ScrollbarsChanged)
333 updateScrollbars(scrollPosition());
334}
335
336IntSize ScrollView::contentsSize() const
337{
338 return m_contentsSize;
339}
340
341void ScrollView::setContentsSize(const IntSize& newSize)
342{
343 if (contentsSize() == newSize)
344 return;
345 m_contentsSize = newSize;
346 if (platformWidget())
347 platformSetContentsSize();
348 else
349 updateScrollbars(scrollPosition());
350 updateOverhangAreas();
351}
352
353ScrollPosition ScrollView::maximumScrollPosition() const
354{
355 ScrollPosition maximumPosition = ScrollableArea::maximumScrollPosition();
356 // FIXME: can this be moved into the base class?
357 maximumPosition.clampNegativeToZero();
358 return maximumPosition;
359}
360
361ScrollPosition ScrollView::adjustScrollPositionWithinRange(const ScrollPosition& scrollPoint) const
362{
363 if (!constrainsScrollingToContentEdge() || m_allowsUnclampedScrollPosition)
364 return scrollPoint;
365
366 return scrollPoint.constrainedBetween(minimumScrollPosition(), maximumScrollPosition());
367}
368
369ScrollPosition ScrollView::documentScrollPositionRelativeToViewOrigin() const
370{
371 return scrollPosition() - IntSize(
372 shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0,
373 headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
374}
375
376ScrollPosition ScrollView::documentScrollPositionRelativeToScrollableAreaOrigin() const
377{
378 return scrollPosition() - IntSize(0, headerHeight());
379}
380
381int ScrollView::scrollSize(ScrollbarOrientation orientation) const
382{
383 // If no scrollbars are present, it does not indicate content is not be scrollable.
384 if (!m_horizontalScrollbar && !m_verticalScrollbar && !prohibitsScrolling()) {
385 IntSize scrollSize = m_contentsSize - visibleContentRect(LegacyIOSDocumentVisibleRect).size();
386 scrollSize.clampNegativeToZero();
387 return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height();
388 }
389
390 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get();
391 return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
392}
393
394void ScrollView::notifyPageThatContentAreaWillPaint() const
395{
396}
397
398void ScrollView::setScrollOffset(const ScrollOffset& offset)
399{
400 LOG_WITH_STREAM(Scrolling, stream << "\nScrollView::setScrollOffset " << offset << " constrains " << constrainsScrollingToContentEdge());
401
402 IntPoint constrainedOffset = offset;
403 if (constrainsScrollingToContentEdge())
404 constrainedOffset = constrainedOffset.constrainedBetween(IntPoint(), maximumScrollOffset());
405
406 scrollTo(scrollPositionFromOffset(constrainedOffset));
407}
408
409void ScrollView::scrollOffsetChangedViaPlatformWidget(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
410{
411 // We should not attempt to actually modify (paint) platform widgets if the layout phase
412 // is not complete. Instead, defer the scroll event until the layout finishes.
413 if (shouldDeferScrollUpdateAfterContentSizeChange()) {
414 // We only care about the most recent scroll position change request
415 m_deferredScrollOffsets = std::make_pair(oldOffset, newOffset);
416 return;
417 }
418
419 scrollOffsetChangedViaPlatformWidgetImpl(oldOffset, newOffset);
420}
421
422void ScrollView::handleDeferredScrollUpdateAfterContentSizeChange()
423{
424 ASSERT(!shouldDeferScrollUpdateAfterContentSizeChange());
425
426 if (!m_deferredScrollDelta && !m_deferredScrollOffsets)
427 return;
428
429 ASSERT(static_cast<bool>(m_deferredScrollDelta) != static_cast<bool>(m_deferredScrollOffsets));
430
431 if (m_deferredScrollDelta)
432 completeUpdatesAfterScrollTo(m_deferredScrollDelta.value());
433 else if (m_deferredScrollOffsets)
434 scrollOffsetChangedViaPlatformWidgetImpl(m_deferredScrollOffsets.value().first, m_deferredScrollOffsets.value().second);
435
436 m_deferredScrollDelta = WTF::nullopt;
437 m_deferredScrollOffsets = WTF::nullopt;
438}
439
440void ScrollView::scrollTo(const ScrollPosition& newPosition)
441{
442 LOG_WITH_STREAM(Scrolling, stream << "ScrollView::scrollTo " << newPosition << " min: " << minimumScrollPosition() << " max: " << maximumScrollPosition());
443
444 IntSize scrollDelta = newPosition - m_scrollPosition;
445 if (scrollDelta.isZero())
446 return;
447
448 m_scrollPosition = newPosition;
449
450 if (scrollbarsSuppressed())
451 return;
452
453#if USE(COORDINATED_GRAPHICS)
454 if (delegatesScrolling()) {
455 requestScrollPositionUpdate(newPosition);
456 return;
457 }
458#endif
459 // We should not attempt to actually modify layer contents if the layout phase
460 // is not complete. Instead, defer the scroll event until the layout finishes.
461 if (shouldDeferScrollUpdateAfterContentSizeChange()) {
462 ASSERT(!m_deferredScrollDelta);
463 m_deferredScrollDelta = scrollDelta;
464 return;
465 }
466
467 completeUpdatesAfterScrollTo(scrollDelta);
468}
469
470void ScrollView::completeUpdatesAfterScrollTo(const IntSize& scrollDelta)
471{
472 updateLayerPositionsAfterScrolling();
473 scrollContents(scrollDelta);
474 updateCompositingLayersAfterScrolling();
475}
476
477int ScrollView::scrollOffset(ScrollbarOrientation orientation) const
478{
479 ScrollOffset offset = scrollOffsetFromPosition(scrollPosition());
480
481 if (orientation == HorizontalScrollbar)
482 return offset.x();
483
484 if (orientation == VerticalScrollbar)
485 return offset.y();
486
487 return 0;
488}
489
490void ScrollView::setScrollPosition(const ScrollPosition& scrollPosition)
491{
492 LOG_WITH_STREAM(Scrolling, stream << "ScrollView::setScrollPosition " << scrollPosition);
493
494 if (prohibitsScrolling())
495 return;
496
497 if (platformWidget()) {
498 platformSetScrollPosition(scrollPosition);
499 return;
500 }
501
502 ScrollPosition newScrollPosition = !delegatesScrolling() ? adjustScrollPositionWithinRange(scrollPosition) : scrollPosition;
503
504 if ((!delegatesScrolling() || currentScrollType() == ScrollType::User) && newScrollPosition == this->scrollPosition())
505 return;
506
507 if (requestScrollPositionUpdate(newScrollPosition))
508 return;
509
510 updateScrollbars(newScrollPosition);
511}
512
513bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
514{
515 if (platformWidget())
516 return platformScroll(direction, granularity);
517
518 return ScrollableArea::scroll(direction, granularity);
519}
520
521bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity)
522{
523 return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity);
524}
525
526IntSize ScrollView::overhangAmount() const
527{
528 IntSize stretch;
529
530 // FIXME: use maximumScrollOffset()
531 ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
532 if (scrollOffset.y() < 0)
533 stretch.setHeight(scrollOffset.y());
534 else if (totalContentsSize().height() && scrollOffset.y() > totalContentsSize().height() - visibleHeight())
535 stretch.setHeight(scrollOffset.y() - (totalContentsSize().height() - visibleHeight()));
536
537 if (scrollOffset.x() < 0)
538 stretch.setWidth(scrollOffset.x());
539 else if (contentsWidth() && scrollOffset.x() > contentsWidth() - visibleWidth())
540 stretch.setWidth(scrollOffset.x() - (contentsWidth() - visibleWidth()));
541
542 return stretch;
543}
544
545bool ScrollView::managesScrollbars() const
546{
547#if PLATFORM(IOS_FAMILY)
548 return false;
549#else
550 if (platformWidget())
551 return false;
552 if (delegatesScrolling())
553 return false;
554 return true;
555#endif
556}
557
558void ScrollView::updateScrollbars(const ScrollPosition& desiredPosition)
559{
560 LOG_WITH_STREAM(Scrolling, stream << "ScrollView::updateScrollbars " << desiredPosition);
561
562 if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
563 return;
564
565 if (!managesScrollbars()) {
566 if (scrollOriginChanged()) {
567 ScrollableArea::scrollToOffsetWithoutAnimation(scrollOffsetFromPosition(desiredPosition));
568 resetScrollOriginChanged();
569 }
570 return;
571 }
572
573 bool hasOverlayScrollbars = (!m_horizontalScrollbar || m_horizontalScrollbar->isOverlayScrollbar()) && (!m_verticalScrollbar || m_verticalScrollbar->isOverlayScrollbar());
574
575 // If we came in here with the view already needing a layout then do that first.
576 // (This will be the common case, e.g., when the page changes due to window resizing for example).
577 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
578 if (!m_scrollbarsSuppressed && !hasOverlayScrollbars) {
579 m_inUpdateScrollbars = true;
580 updateContentsSize();
581 m_inUpdateScrollbars = false;
582 }
583
584 IntRect oldScrollCornerRect = scrollCornerRect();
585
586 bool hasHorizontalScrollbar = m_horizontalScrollbar;
587 bool hasVerticalScrollbar = m_verticalScrollbar;
588
589 bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
590 bool newHasVerticalScrollbar = hasVerticalScrollbar;
591
592 ScrollbarMode hScroll = m_horizontalScrollbarMode;
593 ScrollbarMode vScroll = m_verticalScrollbarMode;
594
595 if (hScroll != ScrollbarAuto)
596 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
597 if (vScroll != ScrollbarAuto)
598 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
599
600 bool scrollbarAddedOrRemoved = false;
601
602 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
603 if (hasHorizontalScrollbar != newHasHorizontalScrollbar && (hasHorizontalScrollbar || !avoidScrollbarCreation())) {
604 if (setHasHorizontalScrollbar(newHasHorizontalScrollbar))
605 scrollbarAddedOrRemoved = true;
606 }
607
608 if (hasVerticalScrollbar != newHasVerticalScrollbar && (hasVerticalScrollbar || !avoidScrollbarCreation())) {
609 if (setHasVerticalScrollbar(newHasVerticalScrollbar))
610 scrollbarAddedOrRemoved = true;
611 }
612 } else {
613 bool sendContentResizedNotification = false;
614
615 IntSize docSize = totalContentsSize();
616 IntSize fullVisibleSize = unobscuredContentRectIncludingScrollbars().size();
617
618 if (hScroll == ScrollbarAuto)
619 newHasHorizontalScrollbar = docSize.width() > visibleWidth();
620 if (vScroll == ScrollbarAuto)
621 newHasVerticalScrollbar = docSize.height() > visibleHeight();
622
623 bool needAnotherPass = false;
624 if (!hasOverlayScrollbars) {
625 // If we ever turn one scrollbar off, do not turn the other one on. Never ever
626 // try to both gain/lose a scrollbar in the same pass.
627 if (!m_updateScrollbarsPass && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height()) {
628 if (hScroll == ScrollbarAuto)
629 newHasHorizontalScrollbar = false;
630 if (vScroll == ScrollbarAuto)
631 newHasVerticalScrollbar = false;
632 }
633 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn && !hasVerticalScrollbar) {
634 newHasVerticalScrollbar = false;
635 needAnotherPass = true;
636 }
637 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn && !hasHorizontalScrollbar) {
638 newHasHorizontalScrollbar = false;
639 needAnotherPass = true;
640 }
641 }
642
643 if (hasHorizontalScrollbar != newHasHorizontalScrollbar && (hasHorizontalScrollbar || !avoidScrollbarCreation())) {
644 if (scrollOrigin().y() && !newHasHorizontalScrollbar)
645 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x(), scrollOrigin().y() - m_horizontalScrollbar->occupiedHeight()));
646 if (m_horizontalScrollbar)
647 m_horizontalScrollbar->invalidate();
648
649 bool changeAffectsContentSize = false;
650 if (setHasHorizontalScrollbar(newHasHorizontalScrollbar, &changeAffectsContentSize)) {
651 scrollbarAddedOrRemoved = true;
652 sendContentResizedNotification |= changeAffectsContentSize;
653 }
654 }
655
656 if (hasVerticalScrollbar != newHasVerticalScrollbar && (hasVerticalScrollbar || !avoidScrollbarCreation())) {
657 if (scrollOrigin().x() && !newHasVerticalScrollbar)
658 ScrollableArea::setScrollOrigin(IntPoint(scrollOrigin().x() - m_verticalScrollbar->occupiedWidth(), scrollOrigin().y()));
659 if (m_verticalScrollbar)
660 m_verticalScrollbar->invalidate();
661
662 bool changeAffectsContentSize = false;
663 if (setHasVerticalScrollbar(newHasVerticalScrollbar, &changeAffectsContentSize)) {
664 scrollbarAddedOrRemoved = true;
665 sendContentResizedNotification |= changeAffectsContentSize;
666 }
667 }
668
669 const unsigned cMaxUpdateScrollbarsPass = 2;
670 if ((sendContentResizedNotification || needAnotherPass) && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
671 m_updateScrollbarsPass++;
672 availableContentSizeChanged(AvailableSizeChangeReason::ScrollbarsChanged);
673 updateContentsSize();
674 IntSize newDocSize = totalContentsSize();
675 if (newDocSize == docSize) {
676 // The layout with the new scroll state had no impact on
677 // the document's overall size, so updateScrollbars didn't get called.
678 // Recur manually.
679 updateScrollbars(desiredPosition);
680 }
681 m_updateScrollbarsPass--;
682 }
683 }
684
685 if (scrollbarAddedOrRemoved)
686 addedOrRemovedScrollbar();
687
688 // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
689 // doing it multiple times).
690 if (m_updateScrollbarsPass)
691 return;
692
693 m_inUpdateScrollbars = true;
694
695 if (m_horizontalScrollbar) {
696 int clientWidth = visibleWidth();
697 int pageStep = Scrollbar::pageStep(clientWidth);
698 IntRect oldRect(m_horizontalScrollbar->frameRect());
699 IntRect hBarRect(shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0,
700 height() - m_horizontalScrollbar->height(),
701 width() - (m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0),
702 m_horizontalScrollbar->height());
703 m_horizontalScrollbar->setFrameRect(hBarRect);
704 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
705 m_horizontalScrollbar->invalidate();
706
707 if (m_scrollbarsSuppressed)
708 m_horizontalScrollbar->setSuppressInvalidation(true);
709 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
710 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
711 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
712 if (m_scrollbarsSuppressed)
713 m_horizontalScrollbar->setSuppressInvalidation(false);
714 }
715
716 if (m_verticalScrollbar) {
717 int clientHeight = visibleHeight();
718 int pageStep = Scrollbar::pageStep(clientHeight);
719 IntRect oldRect(m_verticalScrollbar->frameRect());
720 IntRect vBarRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : width() - m_verticalScrollbar->width(),
721 topContentInset(),
722 m_verticalScrollbar->width(),
723 height() - topContentInset() - (m_horizontalScrollbar ? m_horizontalScrollbar->occupiedHeight() : 0));
724 m_verticalScrollbar->setFrameRect(vBarRect);
725 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
726 m_verticalScrollbar->invalidate();
727
728 if (m_scrollbarsSuppressed)
729 m_verticalScrollbar->setSuppressInvalidation(true);
730 m_verticalScrollbar->setEnabled(totalContentsSize().height() > clientHeight);
731 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
732 m_verticalScrollbar->setProportion(clientHeight, totalContentsSize().height());
733 if (m_scrollbarsSuppressed)
734 m_verticalScrollbar->setSuppressInvalidation(false);
735 }
736
737 if (hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar) {
738 // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed?
739 frameRectsChanged();
740 positionScrollbarLayers();
741 updateScrollCorner();
742 if (!m_horizontalScrollbar && !m_verticalScrollbar)
743 invalidateScrollCornerRect(oldScrollCornerRect);
744 }
745
746 IntPoint adjustedScrollPosition = desiredPosition;
747 if (!isRubberBandInProgress())
748 adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition);
749
750 if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) {
751 ScrollableArea::scrollToOffsetWithoutAnimation(scrollOffsetFromPosition(adjustedScrollPosition));
752 resetScrollOriginChanged();
753 }
754
755 // Make sure the scrollbar offsets are up to date.
756 if (m_horizontalScrollbar)
757 m_horizontalScrollbar->offsetDidChange();
758 if (m_verticalScrollbar)
759 m_verticalScrollbar->offsetDidChange();
760
761 m_inUpdateScrollbars = false;
762}
763
764const int panIconSizeLength = 16;
765
766IntRect ScrollView::rectToCopyOnScroll() const
767{
768 IntRect scrollViewRect = convertToRootView(IntRect(0, 0, visibleWidth(), visibleHeight()));
769 if (hasOverlayScrollbars()) {
770 int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0;
771 int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0;
772
773 scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth);
774 scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight);
775 }
776 return scrollViewRect;
777}
778
779void ScrollView::scrollContents(const IntSize& scrollDelta)
780{
781 HostWindow* window = hostWindow();
782 if (!window)
783 return;
784
785 // Since scrolling is double buffered, we will be blitting the scroll view's intersection
786 // with the clip rect every time to keep it smooth.
787 IntRect clipRect = windowClipRect();
788 IntRect scrollViewRect = rectToCopyOnScroll();
789 IntRect updateRect = clipRect;
790 updateRect.intersect(scrollViewRect);
791
792 // Invalidate the root view (not the backing store).
793 window->invalidateRootView(updateRect);
794
795 if (m_drawPanScrollIcon) {
796 // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers.
797 // https://bugs.webkit.org/show_bug.cgi?id=47837
798 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + std::max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
799 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
800 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
801 panScrollIconDirtyRect.intersect(clipRect);
802 window->invalidateContentsAndRootView(panScrollIconDirtyRect);
803 }
804
805 if (canBlitOnScroll()) { // The main frame can just blit the WebView window
806 // FIXME: Find a way to scroll subframes with this faster path
807 if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
808 scrollContentsSlowPath(updateRect);
809 } else {
810 // We need to repaint the entire backing store. Do it now before moving the windowed plugins.
811 scrollContentsSlowPath(updateRect);
812 }
813
814 // Invalidate the overhang areas if they are visible.
815 updateOverhangAreas();
816
817 // This call will move children with native widgets (plugins) and invalidate them as well.
818 frameRectsChanged();
819
820 // Now blit the backingstore into the window which should be very fast.
821 window->invalidateRootView(IntRect());
822}
823
824void ScrollView::scrollContentsSlowPath(const IntRect& updateRect)
825{
826 hostWindow()->invalidateContentsForSlowScroll(updateRect);
827}
828
829IntPoint ScrollView::viewToContents(const IntPoint& point) const
830{
831 if (delegatesScrolling())
832 return point;
833
834 return point + toIntSize(documentScrollPositionRelativeToViewOrigin());
835}
836
837IntPoint ScrollView::contentsToView(const IntPoint& point) const
838{
839 if (delegatesScrolling())
840 return point;
841
842 return point - toIntSize(documentScrollPositionRelativeToViewOrigin());
843}
844
845FloatPoint ScrollView::viewToContents(const FloatPoint& point) const
846{
847 if (delegatesScrolling())
848 return point;
849
850 return viewToContents(IntPoint(point));
851}
852
853FloatPoint ScrollView::contentsToView(const FloatPoint& point) const
854{
855 if (delegatesScrolling())
856 return point;
857
858 return contentsToView(IntPoint(point));
859}
860
861IntRect ScrollView::viewToContents(IntRect rect) const
862{
863 if (delegatesScrolling())
864 return rect;
865
866 rect.moveBy(documentScrollPositionRelativeToViewOrigin());
867 return rect;
868}
869
870FloatRect ScrollView::viewToContents(FloatRect rect) const
871{
872 if (delegatesScrolling())
873 return rect;
874
875 rect.moveBy(documentScrollPositionRelativeToViewOrigin());
876 return rect;
877}
878
879IntRect ScrollView::contentsToView(IntRect rect) const
880{
881 if (delegatesScrolling())
882 return rect;
883
884 rect.moveBy(-documentScrollPositionRelativeToViewOrigin());
885 return rect;
886}
887
888FloatRect ScrollView::contentsToView(FloatRect rect) const
889{
890 if (delegatesScrolling())
891 return rect;
892
893 rect.moveBy(-documentScrollPositionRelativeToViewOrigin());
894 return rect;
895}
896
897IntPoint ScrollView::contentsToContainingViewContents(const IntPoint& point) const
898{
899 if (const ScrollView* parentScrollView = parent()) {
900 IntPoint pointInContainingView = convertToContainingView(contentsToView(point));
901 return parentScrollView->viewToContents(pointInContainingView);
902 }
903
904 return contentsToView(point);
905}
906
907IntRect ScrollView::contentsToContainingViewContents(IntRect rect) const
908{
909 if (const ScrollView* parentScrollView = parent()) {
910 IntRect rectInContainingView = convertToContainingView(contentsToView(rect));
911 return parentScrollView->viewToContents(rectInContainingView);
912 }
913
914 return contentsToView(rect);
915}
916
917IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const
918{
919 return viewToContents(convertFromRootView(rootViewPoint));
920}
921
922IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const
923{
924 return convertToRootView(contentsToView(contentsPoint));
925}
926
927FloatPoint ScrollView::contentsToRootView(const FloatPoint& contentsPoint) const
928{
929 return convertToRootView(contentsToView(contentsPoint));
930}
931
932IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const
933{
934 return viewToContents(convertFromRootView(rootViewRect));
935}
936
937FloatRect ScrollView::rootViewToContents(const FloatRect& rootViewRect) const
938{
939 return viewToContents(convertFromRootView(rootViewRect));
940}
941
942FloatRect ScrollView::contentsToRootView(const FloatRect& contentsRect) const
943{
944 return convertToRootView(contentsToView(contentsRect));
945}
946
947IntPoint ScrollView::rootViewToTotalContents(const IntPoint& rootViewPoint) const
948{
949 if (delegatesScrolling())
950 return convertFromRootView(rootViewPoint);
951
952 IntPoint viewPoint = convertFromRootView(rootViewPoint);
953 // Like rootViewToContents(), but ignores headerHeight.
954 return viewPoint + toIntSize(scrollPosition()) - IntSize(0, topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset));
955}
956
957IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const
958{
959 return convertToRootView(contentsToView(contentsRect));
960}
961
962IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
963{
964 return viewToContents(convertFromContainingWindow(windowPoint));
965}
966
967IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
968{
969 return convertToContainingWindow(contentsToView(contentsPoint));
970}
971
972IntRect ScrollView::windowToContents(const IntRect& windowRect) const
973{
974 return viewToContents(convertFromContainingWindow(windowRect));
975}
976
977IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
978{
979 return convertToContainingWindow(contentsToView(contentsRect));
980}
981
982IntRect ScrollView::contentsToScreen(const IntRect& rect) const
983{
984 HostWindow* window = hostWindow();
985 if (platformWidget())
986 return platformContentsToScreen(rect);
987 if (!window)
988 return IntRect();
989 return window->rootViewToScreen(contentsToRootView(rect));
990}
991
992IntPoint ScrollView::screenToContents(const IntPoint& point) const
993{
994 HostWindow* window = hostWindow();
995 if (platformWidget())
996 return platformScreenToContents(point);
997 if (!window)
998 return IntPoint();
999 return rootViewToContents(window->screenToRootView(point));
1000}
1001
1002void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
1003{
1004 if (suppressed == m_scrollbarsSuppressed)
1005 return;
1006
1007 m_scrollbarsSuppressed = suppressed;
1008
1009 if (platformWidget())
1010 platformSetScrollbarsSuppressed(repaintOnUnsuppress);
1011 else if (repaintOnUnsuppress && !suppressed) {
1012 if (m_horizontalScrollbar)
1013 m_horizontalScrollbar->invalidate();
1014 if (m_verticalScrollbar)
1015 m_verticalScrollbar->invalidate();
1016
1017 // Invalidate the scroll corner too on unsuppress.
1018 invalidateRect(scrollCornerRect());
1019 }
1020}
1021
1022Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
1023{
1024 if (platformWidget())
1025 return 0;
1026
1027 // convertFromContainingWindow doesn't do what it sounds like it does. We need it here just to get this
1028 // point into the right coordinates if this is the ScrollView of a sub-frame.
1029 IntPoint convertedPoint = convertFromContainingWindow(windowPoint);
1030 if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(convertedPoint))
1031 return m_horizontalScrollbar.get();
1032 if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(convertedPoint))
1033 return m_verticalScrollbar.get();
1034 return 0;
1035}
1036
1037void ScrollView::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle)
1038{
1039 ScrollableArea::setScrollbarOverlayStyle(overlayStyle);
1040 platformSetScrollbarOverlayStyle(overlayStyle);
1041}
1042
1043void ScrollView::setFrameRect(const IntRect& newRect)
1044{
1045 Ref<ScrollView> protectedThis(*this);
1046 IntRect oldRect = frameRect();
1047
1048 if (newRect == oldRect)
1049 return;
1050
1051 Widget::setFrameRect(newRect);
1052 frameRectsChanged();
1053
1054 updateScrollbars(scrollPosition());
1055
1056 if (!m_useFixedLayout && oldRect.size() != newRect.size())
1057 availableContentSizeChanged(AvailableSizeChangeReason::AreaSizeChanged);
1058}
1059
1060void ScrollView::frameRectsChanged()
1061{
1062 if (platformWidget())
1063 return;
1064 for (auto& child : m_children)
1065 child->frameRectsChanged();
1066}
1067
1068void ScrollView::clipRectChanged()
1069{
1070 for (auto& child : m_children)
1071 child->clipRectChanged();
1072}
1073
1074static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar)
1075{
1076 if (!graphicsLayer || !scrollbar)
1077 return;
1078
1079 IntRect scrollbarRect = scrollbar->frameRect();
1080 graphicsLayer->setPosition(scrollbarRect.location());
1081
1082 if (scrollbarRect.size() == graphicsLayer->size())
1083 return;
1084
1085 graphicsLayer->setSize(scrollbarRect.size());
1086
1087 if (graphicsLayer->usesContentsLayer()) {
1088 graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height()));
1089 return;
1090 }
1091
1092 graphicsLayer->setDrawsContent(true);
1093 graphicsLayer->setNeedsDisplay();
1094}
1095
1096static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect)
1097{
1098 if (!graphicsLayer)
1099 return;
1100 graphicsLayer->setDrawsContent(!cornerRect.isEmpty());
1101 graphicsLayer->setPosition(cornerRect.location());
1102 if (cornerRect.size() != graphicsLayer->size())
1103 graphicsLayer->setNeedsDisplay();
1104 graphicsLayer->setSize(cornerRect.size());
1105}
1106
1107void ScrollView::positionScrollbarLayers()
1108{
1109 positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar());
1110 positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar());
1111 positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect());
1112}
1113
1114void ScrollView::repaintContentRectangle(const IntRect& rect)
1115{
1116 IntRect paintRect = rect;
1117 if (!paintsEntireContents())
1118 paintRect.intersect(visibleContentRect(LegacyIOSDocumentVisibleRect));
1119 if (paintRect.isEmpty())
1120 return;
1121
1122 if (platformWidget()) {
1123 notifyPageThatContentAreaWillPaint();
1124 platformRepaintContentRectangle(paintRect);
1125 return;
1126 }
1127
1128 if (HostWindow* window = hostWindow())
1129 window->invalidateContentsAndRootView(contentsToWindow(paintRect));
1130}
1131
1132IntRect ScrollView::scrollCornerRect() const
1133{
1134 IntRect cornerRect;
1135
1136 if (hasOverlayScrollbars())
1137 return cornerRect;
1138
1139 int heightTrackedByScrollbar = height() - topContentInset();
1140
1141 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
1142 cornerRect.unite(IntRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(),
1143 height() - m_horizontalScrollbar->height(),
1144 width() - m_horizontalScrollbar->width(),
1145 m_horizontalScrollbar->height()));
1146 }
1147
1148 if (m_verticalScrollbar && heightTrackedByScrollbar - m_verticalScrollbar->height() > 0) {
1149 cornerRect.unite(IntRect(shouldPlaceBlockDirectionScrollbarOnLeft() ? 0 : width() - m_verticalScrollbar->width(),
1150 m_verticalScrollbar->height() + topContentInset(),
1151 m_verticalScrollbar->width(),
1152 heightTrackedByScrollbar - m_verticalScrollbar->height()));
1153 }
1154
1155 return cornerRect;
1156}
1157
1158bool ScrollView::isScrollCornerVisible() const
1159{
1160 return !scrollCornerRect().isEmpty();
1161}
1162
1163void ScrollView::scrollbarStyleChanged(ScrollbarStyle newStyle, bool forceUpdate)
1164{
1165 ScrollableArea::scrollbarStyleChanged(newStyle, forceUpdate);
1166 if (!forceUpdate)
1167 return;
1168
1169 updateScrollbars(scrollPosition());
1170 positionScrollbarLayers();
1171}
1172
1173void ScrollView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
1174{
1175 ScrollbarTheme::theme().paintScrollCorner(context, cornerRect);
1176}
1177
1178void ScrollView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
1179{
1180 bar.paint(context, rect);
1181}
1182
1183void ScrollView::invalidateScrollCornerRect(const IntRect& rect)
1184{
1185 invalidateRect(rect);
1186}
1187
1188void ScrollView::paintScrollbars(GraphicsContext& context, const IntRect& rect)
1189{
1190 if (m_horizontalScrollbar && !layerForHorizontalScrollbar())
1191 paintScrollbar(context, *m_horizontalScrollbar.get(), rect);
1192 if (m_verticalScrollbar && !layerForVerticalScrollbar())
1193 paintScrollbar(context, *m_verticalScrollbar.get(), rect);
1194
1195 if (layerForScrollCorner())
1196 return;
1197
1198 paintScrollCorner(context, scrollCornerRect());
1199}
1200
1201void ScrollView::paintPanScrollIcon(GraphicsContext& context)
1202{
1203 static Image& panScrollIcon = Image::loadPlatformResource("panIcon").leakRef();
1204 IntPoint iconGCPoint = m_panScrollIconPoint;
1205 if (parent())
1206 iconGCPoint = parent()->windowToContents(iconGCPoint);
1207 context.drawImage(panScrollIcon, iconGCPoint);
1208}
1209
1210void ScrollView::paint(GraphicsContext& context, const IntRect& rect, SecurityOriginPaintPolicy securityOriginPaintPolicy)
1211{
1212 if (platformWidget()) {
1213 Widget::paint(context, rect);
1214 return;
1215 }
1216
1217 if (context.paintingDisabled() && !context.performingPaintInvalidation())
1218 return;
1219
1220 notifyPageThatContentAreaWillPaint();
1221
1222 IntRect documentDirtyRect = rect;
1223 if (!paintsEntireContents()) {
1224 IntRect visibleAreaWithoutScrollbars(locationOfContents(), visibleContentRect(LegacyIOSDocumentVisibleRect).size());
1225 documentDirtyRect.intersect(visibleAreaWithoutScrollbars);
1226 }
1227
1228 if (!documentDirtyRect.isEmpty()) {
1229 GraphicsContextStateSaver stateSaver(context);
1230
1231 IntPoint locationOfContents = this->locationOfContents();
1232 context.translate(locationOfContents.x(), locationOfContents.y());
1233 documentDirtyRect.moveBy(-locationOfContents);
1234
1235 if (!paintsEntireContents()) {
1236 context.translate(-scrollX(), -scrollY());
1237 documentDirtyRect.moveBy(scrollPosition());
1238
1239 context.clip(visibleContentRect(LegacyIOSDocumentVisibleRect));
1240 }
1241
1242 paintContents(context, documentDirtyRect, securityOriginPaintPolicy);
1243 }
1244
1245#if ENABLE(RUBBER_BANDING)
1246 if (!layerForOverhangAreas())
1247 calculateAndPaintOverhangAreas(context, rect);
1248#else
1249 calculateAndPaintOverhangAreas(context, rect);
1250#endif
1251
1252 // Now paint the scrollbars.
1253 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
1254 GraphicsContextStateSaver stateSaver(context);
1255 IntRect scrollViewDirtyRect = rect;
1256 IntRect visibleAreaWithScrollbars(location(), unobscuredContentRectIncludingScrollbars().size());
1257 scrollViewDirtyRect.intersect(visibleAreaWithScrollbars);
1258 context.translate(x(), y());
1259 scrollViewDirtyRect.moveBy(-location());
1260 context.clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size()));
1261
1262 paintScrollbars(context, scrollViewDirtyRect);
1263 }
1264
1265 // Paint the panScroll Icon
1266 if (m_drawPanScrollIcon)
1267 paintPanScrollIcon(context);
1268}
1269
1270void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect)
1271{
1272 IntSize scrollbarSpace = scrollbarIntrusion();
1273
1274 // FIXME: use maximumScrollOffset().
1275 ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
1276 if (scrollOffset.y() < 0) {
1277 horizontalOverhangRect = frameRect();
1278 horizontalOverhangRect.setHeight(-scrollOffset.y());
1279 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - scrollbarSpace.width());
1280 } else if (totalContentsSize().height() && scrollOffset.y() > totalContentsSize().height() - visibleHeight()) {
1281 int height = scrollOffset.y() - (totalContentsSize().height() - visibleHeight());
1282 horizontalOverhangRect = frameRect();
1283 horizontalOverhangRect.setY(frameRect().maxY() - height - scrollbarSpace.height());
1284 horizontalOverhangRect.setHeight(height);
1285 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - scrollbarSpace.width());
1286 }
1287
1288 if (scrollOffset.x() < 0) {
1289 verticalOverhangRect.setWidth(-scrollOffset.x());
1290 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - scrollbarSpace.height());
1291 verticalOverhangRect.setX(frameRect().x());
1292 if (horizontalOverhangRect.y() == frameRect().y())
1293 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
1294 else
1295 verticalOverhangRect.setY(frameRect().y());
1296 } else if (contentsWidth() && scrollOffset.x() > contentsWidth() - visibleWidth()) {
1297 int width = scrollOffset.x() - (contentsWidth() - visibleWidth());
1298 verticalOverhangRect.setWidth(width);
1299 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - scrollbarSpace.height());
1300 verticalOverhangRect.setX(frameRect().maxX() - width - scrollbarSpace.width());
1301 if (horizontalOverhangRect.y() == frameRect().y())
1302 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height());
1303 else
1304 verticalOverhangRect.setY(frameRect().y());
1305 }
1306}
1307
1308void ScrollView::updateOverhangAreas()
1309{
1310 HostWindow* window = hostWindow();
1311 if (!window)
1312 return;
1313
1314 IntRect horizontalOverhangRect;
1315 IntRect verticalOverhangRect;
1316 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
1317 if (!horizontalOverhangRect.isEmpty())
1318 window->invalidateContentsAndRootView(horizontalOverhangRect);
1319 if (!verticalOverhangRect.isEmpty())
1320 window->invalidateContentsAndRootView(verticalOverhangRect);
1321}
1322
1323void ScrollView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
1324{
1325 ScrollbarTheme::theme().paintOverhangAreas(*this, context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
1326}
1327
1328void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext& context, const IntRect& dirtyRect)
1329{
1330 IntRect horizontalOverhangRect;
1331 IntRect verticalOverhangRect;
1332 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect);
1333
1334 if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect))
1335 paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect);
1336}
1337
1338bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
1339{
1340 if (!scrollbarCornerPresent())
1341 return false;
1342
1343 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
1344
1345 if (m_horizontalScrollbar) {
1346 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
1347 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
1348 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
1349
1350 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
1351 }
1352
1353 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
1354 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
1355 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
1356
1357 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
1358}
1359
1360bool ScrollView::scrollbarCornerPresent() const
1361{
1362 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0)
1363 || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
1364}
1365
1366IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& localRect) const
1367{
1368 // Scrollbars won't be transformed within us
1369 IntRect newRect = localRect;
1370 newRect.moveBy(scrollbar.location());
1371 return newRect;
1372}
1373
1374IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const
1375{
1376 IntRect newRect = parentRect;
1377 // Scrollbars won't be transformed within us
1378 newRect.moveBy(-scrollbar.location());
1379 return newRect;
1380}
1381
1382// FIXME: test these on windows
1383IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& localPoint) const
1384{
1385 // Scrollbars won't be transformed within us
1386 IntPoint newPoint = localPoint;
1387 newPoint.moveBy(scrollbar.location());
1388 return newPoint;
1389}
1390
1391IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const
1392{
1393 IntPoint newPoint = parentPoint;
1394 // Scrollbars won't be transformed within us
1395 newPoint.moveBy(-scrollbar.location());
1396 return newPoint;
1397}
1398
1399void ScrollView::setParentVisible(bool visible)
1400{
1401 if (isParentVisible() == visible)
1402 return;
1403
1404 Widget::setParentVisible(visible);
1405
1406 if (!isSelfVisible())
1407 return;
1408
1409 for (auto& child : m_children)
1410 child->setParentVisible(visible);
1411}
1412
1413void ScrollView::show()
1414{
1415 if (!isSelfVisible()) {
1416 setSelfVisible(true);
1417 if (isParentVisible()) {
1418 for (auto& child : m_children)
1419 child->setParentVisible(true);
1420 }
1421 }
1422
1423 Widget::show();
1424}
1425
1426void ScrollView::hide()
1427{
1428 if (isSelfVisible()) {
1429 if (isParentVisible()) {
1430 for (auto& child : m_children)
1431 child->setParentVisible(false);
1432 }
1433 setSelfVisible(false);
1434 }
1435
1436 Widget::hide();
1437}
1438
1439bool ScrollView::isOffscreen() const
1440{
1441 if (platformWidget())
1442 return platformIsOffscreen();
1443
1444 if (!isVisible())
1445 return true;
1446
1447 // FIXME: Add a HostWindow::isOffscreen method here. Since only Mac implements this method
1448 // currently, we can add the method when the other platforms decide to implement this concept.
1449 return false;
1450}
1451
1452
1453void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
1454{
1455 HostWindow* window = hostWindow();
1456 if (!window)
1457 return;
1458 m_drawPanScrollIcon = true;
1459 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
1460 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1461}
1462
1463void ScrollView::removePanScrollIcon()
1464{
1465 HostWindow* window = hostWindow();
1466 if (!window)
1467 return;
1468 m_drawPanScrollIcon = false;
1469 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)));
1470}
1471
1472void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously)
1473{
1474 if (scrollOrigin() == origin)
1475 return;
1476
1477 ScrollableArea::setScrollOrigin(origin);
1478
1479 if (platformWidget()) {
1480 platformSetScrollOrigin(origin, updatePositionAtAll, updatePositionSynchronously);
1481 return;
1482 }
1483
1484 // Update if the scroll origin changes, since our position will be different if the content size did not change.
1485 if (updatePositionAtAll && updatePositionSynchronously)
1486 updateScrollbars(scrollPosition());
1487}
1488
1489void ScrollView::styleDidChange()
1490{
1491 if (m_horizontalScrollbar)
1492 m_horizontalScrollbar->styleChanged();
1493
1494 if (m_verticalScrollbar)
1495 m_verticalScrollbar->styleChanged();
1496}
1497
1498IntPoint ScrollView::locationOfContents() const
1499{
1500 IntPoint result = location();
1501 if (shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar)
1502 result.move(m_verticalScrollbar->occupiedWidth(), 0);
1503 return result;
1504}
1505
1506#if !PLATFORM(COCOA)
1507
1508void ScrollView::platformAddChild(Widget*)
1509{
1510}
1511
1512void ScrollView::platformRemoveChild(Widget*)
1513{
1514}
1515
1516#endif
1517
1518#if !PLATFORM(COCOA)
1519
1520void ScrollView::platformSetScrollbarsSuppressed(bool)
1521{
1522}
1523
1524void ScrollView::platformSetScrollOrigin(const IntPoint&, bool, bool)
1525{
1526}
1527
1528void ScrollView::platformSetScrollbarOverlayStyle(ScrollbarOverlayStyle)
1529{
1530}
1531
1532#endif
1533
1534#if !PLATFORM(COCOA)
1535
1536void ScrollView::platformSetScrollbarModes()
1537{
1538}
1539
1540void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
1541{
1542 horizontal = ScrollbarAuto;
1543 vertical = ScrollbarAuto;
1544}
1545
1546void ScrollView::platformSetCanBlitOnScroll(bool)
1547{
1548}
1549
1550bool ScrollView::platformCanBlitOnScroll() const
1551{
1552 return false;
1553}
1554
1555IntRect ScrollView::platformVisibleContentRect(bool) const
1556{
1557 return IntRect();
1558}
1559
1560float ScrollView::platformTopContentInset() const
1561{
1562 return 0;
1563}
1564
1565void ScrollView::platformSetTopContentInset(float)
1566{
1567}
1568
1569IntSize ScrollView::platformVisibleContentSize(bool) const
1570{
1571 return IntSize();
1572}
1573
1574IntRect ScrollView::platformVisibleContentRectIncludingObscuredArea(bool) const
1575{
1576 return IntRect();
1577}
1578
1579IntSize ScrollView::platformVisibleContentSizeIncludingObscuredArea(bool) const
1580{
1581 return IntSize();
1582}
1583
1584void ScrollView::platformSetContentsSize()
1585{
1586}
1587
1588IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1589{
1590 return rect;
1591}
1592
1593IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1594{
1595 return point;
1596}
1597
1598void ScrollView::platformSetScrollPosition(const IntPoint&)
1599{
1600}
1601
1602bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1603{
1604 return true;
1605}
1606
1607void ScrollView::platformRepaintContentRectangle(const IntRect&)
1608{
1609}
1610
1611bool ScrollView::platformIsOffscreen() const
1612{
1613 return false;
1614}
1615
1616#endif
1617
1618}
1619