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 | |
41 | namespace WebCore { |
42 | |
43 | ScrollView::ScrollView() = default; |
44 | |
45 | ScrollView::~ScrollView() = default; |
46 | |
47 | void 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 | |
57 | void 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 | |
66 | bool ScrollView::setHasHorizontalScrollbar(bool hasBar, bool* contentSizeAffected) |
67 | { |
68 | return setHasScrollbarInternal(m_horizontalScrollbar, HorizontalScrollbar, hasBar, contentSizeAffected); |
69 | } |
70 | |
71 | bool ScrollView::setHasVerticalScrollbar(bool hasBar, bool* contentSizeAffected) |
72 | { |
73 | return setHasScrollbarInternal(m_verticalScrollbar, VerticalScrollbar, hasBar, contentSizeAffected); |
74 | } |
75 | |
76 | bool 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 | |
103 | Ref<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) |
104 | { |
105 | return Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar); |
106 | } |
107 | |
108 | void 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 | |
138 | void 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 | |
148 | void 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 | |
168 | void ScrollView::setCanBlitOnScroll(bool b) |
169 | { |
170 | if (platformWidget()) { |
171 | platformSetCanBlitOnScroll(b); |
172 | return; |
173 | } |
174 | |
175 | m_canBlitOnScroll = b; |
176 | } |
177 | |
178 | bool ScrollView::canBlitOnScroll() const |
179 | { |
180 | if (platformWidget()) |
181 | return platformCanBlitOnScroll(); |
182 | |
183 | return m_canBlitOnScroll; |
184 | } |
185 | |
186 | void ScrollView::setPaintsEntireContents(bool paintsEntireContents) |
187 | { |
188 | m_paintsEntireContents = paintsEntireContents; |
189 | } |
190 | |
191 | void ScrollView::setDelegatesScrolling(bool delegatesScrolling) |
192 | { |
193 | if (m_delegatesScrolling == delegatesScrolling) |
194 | return; |
195 | |
196 | m_delegatesScrolling = delegatesScrolling; |
197 | delegatesScrollingDidChange(); |
198 | } |
199 | |
200 | IntPoint ScrollView::contentsScrollPosition() const |
201 | { |
202 | #if PLATFORM(IOS_FAMILY) |
203 | if (platformWidget()) |
204 | return actualScrollPosition(); |
205 | #endif |
206 | return scrollPosition(); |
207 | } |
208 | |
209 | void 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) |
219 | IntRect ScrollView::unobscuredContentRect(VisibleContentRectIncludesScrollbars scrollbarInclusion) const |
220 | { |
221 | return unobscuredContentRectInternal(scrollbarInclusion); |
222 | } |
223 | #endif |
224 | |
225 | IntRect 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 | |
232 | IntSize 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 | |
249 | IntSize 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 | |
265 | IntRect 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 | |
290 | IntSize ScrollView::layoutSize() const |
291 | { |
292 | return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? sizeForUnobscuredContent() : m_fixedLayoutSize; |
293 | } |
294 | |
295 | IntSize ScrollView::fixedLayoutSize() const |
296 | { |
297 | return m_fixedLayoutSize; |
298 | } |
299 | |
300 | void 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 | |
311 | bool ScrollView::useFixedLayout() const |
312 | { |
313 | return m_useFixedLayout; |
314 | } |
315 | |
316 | void 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 | |
325 | void 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 | |
336 | IntSize ScrollView::contentsSize() const |
337 | { |
338 | return m_contentsSize; |
339 | } |
340 | |
341 | void 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 | |
353 | ScrollPosition 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 | |
361 | ScrollPosition ScrollView::adjustScrollPositionWithinRange(const ScrollPosition& scrollPoint) const |
362 | { |
363 | if (!constrainsScrollingToContentEdge() || m_allowsUnclampedScrollPosition) |
364 | return scrollPoint; |
365 | |
366 | return scrollPoint.constrainedBetween(minimumScrollPosition(), maximumScrollPosition()); |
367 | } |
368 | |
369 | ScrollPosition ScrollView::documentScrollPositionRelativeToViewOrigin() const |
370 | { |
371 | return scrollPosition() - IntSize( |
372 | shouldPlaceBlockDirectionScrollbarOnLeft() && m_verticalScrollbar ? m_verticalScrollbar->occupiedWidth() : 0, |
373 | headerHeight() + topContentInset(TopContentInsetType::WebCoreOrPlatformContentInset)); |
374 | } |
375 | |
376 | ScrollPosition ScrollView::documentScrollPositionRelativeToScrollableAreaOrigin() const |
377 | { |
378 | return scrollPosition() - IntSize(0, headerHeight()); |
379 | } |
380 | |
381 | int 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 | |
394 | void ScrollView::notifyPageThatContentAreaWillPaint() const |
395 | { |
396 | } |
397 | |
398 | void 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 | |
409 | void 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 | |
422 | void 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 | |
440 | void 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 | |
470 | void ScrollView::completeUpdatesAfterScrollTo(const IntSize& scrollDelta) |
471 | { |
472 | updateLayerPositionsAfterScrolling(); |
473 | scrollContents(scrollDelta); |
474 | updateCompositingLayersAfterScrolling(); |
475 | } |
476 | |
477 | int 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 | |
490 | void 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 | |
513 | bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity) |
514 | { |
515 | if (platformWidget()) |
516 | return platformScroll(direction, granularity); |
517 | |
518 | return ScrollableArea::scroll(direction, granularity); |
519 | } |
520 | |
521 | bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity) |
522 | { |
523 | return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity); |
524 | } |
525 | |
526 | IntSize 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 | |
545 | bool 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 | |
558 | void 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 | |
764 | const int panIconSizeLength = 16; |
765 | |
766 | IntRect 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 | |
779 | void 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 | |
824 | void ScrollView::scrollContentsSlowPath(const IntRect& updateRect) |
825 | { |
826 | hostWindow()->invalidateContentsForSlowScroll(updateRect); |
827 | } |
828 | |
829 | IntPoint ScrollView::viewToContents(const IntPoint& point) const |
830 | { |
831 | if (delegatesScrolling()) |
832 | return point; |
833 | |
834 | return point + toIntSize(documentScrollPositionRelativeToViewOrigin()); |
835 | } |
836 | |
837 | IntPoint ScrollView::contentsToView(const IntPoint& point) const |
838 | { |
839 | if (delegatesScrolling()) |
840 | return point; |
841 | |
842 | return point - toIntSize(documentScrollPositionRelativeToViewOrigin()); |
843 | } |
844 | |
845 | FloatPoint ScrollView::viewToContents(const FloatPoint& point) const |
846 | { |
847 | if (delegatesScrolling()) |
848 | return point; |
849 | |
850 | return viewToContents(IntPoint(point)); |
851 | } |
852 | |
853 | FloatPoint ScrollView::contentsToView(const FloatPoint& point) const |
854 | { |
855 | if (delegatesScrolling()) |
856 | return point; |
857 | |
858 | return contentsToView(IntPoint(point)); |
859 | } |
860 | |
861 | IntRect ScrollView::viewToContents(IntRect rect) const |
862 | { |
863 | if (delegatesScrolling()) |
864 | return rect; |
865 | |
866 | rect.moveBy(documentScrollPositionRelativeToViewOrigin()); |
867 | return rect; |
868 | } |
869 | |
870 | FloatRect ScrollView::viewToContents(FloatRect rect) const |
871 | { |
872 | if (delegatesScrolling()) |
873 | return rect; |
874 | |
875 | rect.moveBy(documentScrollPositionRelativeToViewOrigin()); |
876 | return rect; |
877 | } |
878 | |
879 | IntRect ScrollView::contentsToView(IntRect rect) const |
880 | { |
881 | if (delegatesScrolling()) |
882 | return rect; |
883 | |
884 | rect.moveBy(-documentScrollPositionRelativeToViewOrigin()); |
885 | return rect; |
886 | } |
887 | |
888 | FloatRect ScrollView::contentsToView(FloatRect rect) const |
889 | { |
890 | if (delegatesScrolling()) |
891 | return rect; |
892 | |
893 | rect.moveBy(-documentScrollPositionRelativeToViewOrigin()); |
894 | return rect; |
895 | } |
896 | |
897 | IntPoint 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 | |
907 | IntRect 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 | |
917 | IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const |
918 | { |
919 | return viewToContents(convertFromRootView(rootViewPoint)); |
920 | } |
921 | |
922 | IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const |
923 | { |
924 | return convertToRootView(contentsToView(contentsPoint)); |
925 | } |
926 | |
927 | FloatPoint ScrollView::contentsToRootView(const FloatPoint& contentsPoint) const |
928 | { |
929 | return convertToRootView(contentsToView(contentsPoint)); |
930 | } |
931 | |
932 | IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const |
933 | { |
934 | return viewToContents(convertFromRootView(rootViewRect)); |
935 | } |
936 | |
937 | FloatRect ScrollView::rootViewToContents(const FloatRect& rootViewRect) const |
938 | { |
939 | return viewToContents(convertFromRootView(rootViewRect)); |
940 | } |
941 | |
942 | FloatRect ScrollView::contentsToRootView(const FloatRect& contentsRect) const |
943 | { |
944 | return convertToRootView(contentsToView(contentsRect)); |
945 | } |
946 | |
947 | IntPoint 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 | |
957 | IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const |
958 | { |
959 | return convertToRootView(contentsToView(contentsRect)); |
960 | } |
961 | |
962 | IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const |
963 | { |
964 | return viewToContents(convertFromContainingWindow(windowPoint)); |
965 | } |
966 | |
967 | IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const |
968 | { |
969 | return convertToContainingWindow(contentsToView(contentsPoint)); |
970 | } |
971 | |
972 | IntRect ScrollView::windowToContents(const IntRect& windowRect) const |
973 | { |
974 | return viewToContents(convertFromContainingWindow(windowRect)); |
975 | } |
976 | |
977 | IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const |
978 | { |
979 | return convertToContainingWindow(contentsToView(contentsRect)); |
980 | } |
981 | |
982 | IntRect 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 | |
992 | IntPoint 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 | |
1002 | void ScrollView::(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 | |
1022 | Scrollbar* 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 | |
1037 | void ScrollView::setScrollbarOverlayStyle(ScrollbarOverlayStyle overlayStyle) |
1038 | { |
1039 | ScrollableArea::setScrollbarOverlayStyle(overlayStyle); |
1040 | platformSetScrollbarOverlayStyle(overlayStyle); |
1041 | } |
1042 | |
1043 | void 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 | |
1060 | void ScrollView::frameRectsChanged() |
1061 | { |
1062 | if (platformWidget()) |
1063 | return; |
1064 | for (auto& child : m_children) |
1065 | child->frameRectsChanged(); |
1066 | } |
1067 | |
1068 | void ScrollView::clipRectChanged() |
1069 | { |
1070 | for (auto& child : m_children) |
1071 | child->clipRectChanged(); |
1072 | } |
1073 | |
1074 | static 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 | |
1096 | static 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 | |
1107 | void ScrollView::positionScrollbarLayers() |
1108 | { |
1109 | positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar()); |
1110 | positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar()); |
1111 | positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect()); |
1112 | } |
1113 | |
1114 | void 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 | |
1132 | IntRect 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 | |
1158 | bool ScrollView::isScrollCornerVisible() const |
1159 | { |
1160 | return !scrollCornerRect().isEmpty(); |
1161 | } |
1162 | |
1163 | void 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 | |
1173 | void ScrollView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect) |
1174 | { |
1175 | ScrollbarTheme::theme().paintScrollCorner(context, cornerRect); |
1176 | } |
1177 | |
1178 | void ScrollView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect) |
1179 | { |
1180 | bar.paint(context, rect); |
1181 | } |
1182 | |
1183 | void ScrollView::invalidateScrollCornerRect(const IntRect& rect) |
1184 | { |
1185 | invalidateRect(rect); |
1186 | } |
1187 | |
1188 | void 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 | |
1201 | void 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 | |
1210 | void 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 | |
1270 | void 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 | |
1308 | void 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 | |
1323 | void 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 | |
1328 | void 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 | |
1338 | bool 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 | |
1360 | bool ScrollView::scrollbarCornerPresent() const |
1361 | { |
1362 | return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) |
1363 | || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0); |
1364 | } |
1365 | |
1366 | IntRect 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 | |
1374 | IntRect 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 |
1383 | IntPoint 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 | |
1391 | IntPoint 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 | |
1399 | void 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 | |
1413 | void 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 | |
1426 | void 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 | |
1439 | bool 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 | |
1453 | void 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 | |
1463 | void 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 | |
1472 | void 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 | |
1489 | void ScrollView::styleDidChange() |
1490 | { |
1491 | if (m_horizontalScrollbar) |
1492 | m_horizontalScrollbar->styleChanged(); |
1493 | |
1494 | if (m_verticalScrollbar) |
1495 | m_verticalScrollbar->styleChanged(); |
1496 | } |
1497 | |
1498 | IntPoint 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 | |
1508 | void ScrollView::platformAddChild(Widget*) |
1509 | { |
1510 | } |
1511 | |
1512 | void ScrollView::platformRemoveChild(Widget*) |
1513 | { |
1514 | } |
1515 | |
1516 | #endif |
1517 | |
1518 | #if !PLATFORM(COCOA) |
1519 | |
1520 | void ScrollView::(bool) |
1521 | { |
1522 | } |
1523 | |
1524 | void ScrollView::platformSetScrollOrigin(const IntPoint&, bool, bool) |
1525 | { |
1526 | } |
1527 | |
1528 | void ScrollView::platformSetScrollbarOverlayStyle(ScrollbarOverlayStyle) |
1529 | { |
1530 | } |
1531 | |
1532 | #endif |
1533 | |
1534 | #if !PLATFORM(COCOA) |
1535 | |
1536 | void ScrollView::platformSetScrollbarModes() |
1537 | { |
1538 | } |
1539 | |
1540 | void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const |
1541 | { |
1542 | horizontal = ScrollbarAuto; |
1543 | vertical = ScrollbarAuto; |
1544 | } |
1545 | |
1546 | void ScrollView::platformSetCanBlitOnScroll(bool) |
1547 | { |
1548 | } |
1549 | |
1550 | bool ScrollView::platformCanBlitOnScroll() const |
1551 | { |
1552 | return false; |
1553 | } |
1554 | |
1555 | IntRect ScrollView::platformVisibleContentRect(bool) const |
1556 | { |
1557 | return IntRect(); |
1558 | } |
1559 | |
1560 | float ScrollView::platformTopContentInset() const |
1561 | { |
1562 | return 0; |
1563 | } |
1564 | |
1565 | void ScrollView::platformSetTopContentInset(float) |
1566 | { |
1567 | } |
1568 | |
1569 | IntSize ScrollView::platformVisibleContentSize(bool) const |
1570 | { |
1571 | return IntSize(); |
1572 | } |
1573 | |
1574 | IntRect ScrollView::platformVisibleContentRectIncludingObscuredArea(bool) const |
1575 | { |
1576 | return IntRect(); |
1577 | } |
1578 | |
1579 | IntSize ScrollView::platformVisibleContentSizeIncludingObscuredArea(bool) const |
1580 | { |
1581 | return IntSize(); |
1582 | } |
1583 | |
1584 | void ScrollView::platformSetContentsSize() |
1585 | { |
1586 | } |
1587 | |
1588 | IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const |
1589 | { |
1590 | return rect; |
1591 | } |
1592 | |
1593 | IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const |
1594 | { |
1595 | return point; |
1596 | } |
1597 | |
1598 | void ScrollView::platformSetScrollPosition(const IntPoint&) |
1599 | { |
1600 | } |
1601 | |
1602 | bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity) |
1603 | { |
1604 | return true; |
1605 | } |
1606 | |
1607 | void ScrollView::platformRepaintContentRectangle(const IntRect&) |
1608 | { |
1609 | } |
1610 | |
1611 | bool ScrollView::platformIsOffscreen() const |
1612 | { |
1613 | return false; |
1614 | } |
1615 | |
1616 | #endif |
1617 | |
1618 | } |
1619 | |