1 /* 2 * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 COMPUTER, 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 "platform/scroll/ScrollView.h" 28 29 #include "platform/graphics/GraphicsContextStateSaver.h" 30 #include "platform/graphics/GraphicsLayer.h" 31 #include "platform/HostWindow.h" 32 #include "platform/scroll/ScrollbarTheme.h" 33 #include "wtf/StdLibExtras.h" 34 35 using namespace std; 36 37 namespace WebCore { 38 39 ScrollView::ScrollView() 40 : m_horizontalScrollbarMode(ScrollbarAuto) 41 , m_verticalScrollbarMode(ScrollbarAuto) 42 , m_horizontalScrollbarLock(false) 43 , m_verticalScrollbarLock(false) 44 , m_scrollbarsAvoidingResizer(0) 45 , m_scrollbarsSuppressed(false) 46 , m_inUpdateScrollbars(false) 47 , m_drawPanScrollIcon(false) 48 , m_paintsEntireContents(false) 49 , m_clipsRepaints(true) 50 { 51 } 52 53 ScrollView::~ScrollView() 54 { 55 } 56 57 void ScrollView::addChild(PassRefPtr<Widget> prpChild) 58 { 59 Widget* child = prpChild.get(); 60 ASSERT(child != this && !child->parent()); 61 child->setParent(this); 62 m_children.add(prpChild); 63 } 64 65 void ScrollView::removeChild(Widget* child) 66 { 67 ASSERT(child->parent() == this); 68 child->setParent(0); 69 m_children.remove(child); 70 } 71 72 void ScrollView::setHasHorizontalScrollbar(bool hasBar) 73 { 74 if (hasBar && !m_horizontalScrollbar) { 75 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar); 76 addChild(m_horizontalScrollbar.get()); 77 didAddScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar); 78 m_horizontalScrollbar->styleChanged(); 79 } else if (!hasBar && m_horizontalScrollbar) { 80 willRemoveScrollbar(m_horizontalScrollbar.get(), HorizontalScrollbar); 81 removeChild(m_horizontalScrollbar.get()); 82 m_horizontalScrollbar = nullptr; 83 } 84 } 85 86 void ScrollView::setHasVerticalScrollbar(bool hasBar) 87 { 88 if (hasBar && !m_verticalScrollbar) { 89 m_verticalScrollbar = createScrollbar(VerticalScrollbar); 90 addChild(m_verticalScrollbar.get()); 91 didAddScrollbar(m_verticalScrollbar.get(), VerticalScrollbar); 92 m_verticalScrollbar->styleChanged(); 93 } else if (!hasBar && m_verticalScrollbar) { 94 willRemoveScrollbar(m_verticalScrollbar.get(), VerticalScrollbar); 95 removeChild(m_verticalScrollbar.get()); 96 m_verticalScrollbar = nullptr; 97 } 98 } 99 100 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) 101 { 102 return Scrollbar::create(this, orientation, RegularScrollbar); 103 } 104 105 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode, 106 bool horizontalLock, bool verticalLock) 107 { 108 bool needsUpdate = false; 109 110 if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) { 111 m_horizontalScrollbarMode = horizontalMode; 112 needsUpdate = true; 113 } 114 115 if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) { 116 m_verticalScrollbarMode = verticalMode; 117 needsUpdate = true; 118 } 119 120 if (horizontalLock) 121 setHorizontalScrollbarLock(); 122 123 if (verticalLock) 124 setVerticalScrollbarLock(); 125 126 if (!needsUpdate) 127 return; 128 129 updateScrollbars(scrollOffset()); 130 131 if (!layerForScrolling()) 132 return; 133 blink::WebLayer* layer = layerForScrolling()->platformLayer(); 134 if (!layer) 135 return; 136 layer->setUserScrollable(userInputScrollable(HorizontalScrollbar), userInputScrollable(VerticalScrollbar)); 137 } 138 139 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const 140 { 141 horizontalMode = m_horizontalScrollbarMode; 142 verticalMode = m_verticalScrollbarMode; 143 } 144 145 void ScrollView::setCanHaveScrollbars(bool canScroll) 146 { 147 ScrollbarMode newHorizontalMode; 148 ScrollbarMode newVerticalMode; 149 150 scrollbarModes(newHorizontalMode, newVerticalMode); 151 152 if (canScroll && newVerticalMode == ScrollbarAlwaysOff) 153 newVerticalMode = ScrollbarAuto; 154 else if (!canScroll) 155 newVerticalMode = ScrollbarAlwaysOff; 156 157 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff) 158 newHorizontalMode = ScrollbarAuto; 159 else if (!canScroll) 160 newHorizontalMode = ScrollbarAlwaysOff; 161 162 setScrollbarModes(newHorizontalMode, newVerticalMode); 163 } 164 165 bool ScrollView::shouldAttemptToScrollUsingFastPath() const 166 { 167 return true; 168 } 169 170 void ScrollView::setPaintsEntireContents(bool paintsEntireContents) 171 { 172 m_paintsEntireContents = paintsEntireContents; 173 } 174 175 void ScrollView::setClipsRepaints(bool clipsRepaints) 176 { 177 m_clipsRepaints = clipsRepaints; 178 } 179 180 IntSize ScrollView::unscaledVisibleContentSize(IncludeScrollbarsInRect scrollbarInclusion) const 181 { 182 return scrollbarInclusion == ExcludeScrollbars ? excludeScrollbars(frameRect().size()) : frameRect().size(); 183 } 184 185 IntSize ScrollView::excludeScrollbars(const IntSize& size) const 186 { 187 int verticalScrollbarWidth = 0; 188 int horizontalScrollbarHeight = 0; 189 190 if (Scrollbar* verticalBar = verticalScrollbar()) 191 verticalScrollbarWidth = !verticalBar->isOverlayScrollbar() ? verticalBar->width() : 0; 192 if (Scrollbar* horizontalBar = horizontalScrollbar()) 193 horizontalScrollbarHeight = !horizontalBar->isOverlayScrollbar() ? horizontalBar->height() : 0; 194 195 return IntSize(max(0, size.width() - verticalScrollbarWidth), 196 max(0, size.height() - horizontalScrollbarHeight)); 197 198 } 199 200 IntRect ScrollView::visibleContentRect(IncludeScrollbarsInRect scollbarInclusion) const 201 { 202 FloatSize visibleContentSize = unscaledVisibleContentSize(scollbarInclusion); 203 visibleContentSize.scale(1 / visibleContentScaleFactor()); 204 return IntRect(IntPoint(m_scrollOffset), expandedIntSize(visibleContentSize)); 205 } 206 207 IntSize ScrollView::contentsSize() const 208 { 209 return m_contentsSize; 210 } 211 212 void ScrollView::setContentsSize(const IntSize& newSize) 213 { 214 if (contentsSize() == newSize) 215 return; 216 m_contentsSize = newSize; 217 updateScrollbars(scrollOffset()); 218 updateOverhangAreas(); 219 } 220 221 IntPoint ScrollView::maximumScrollPosition() const 222 { 223 IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), contentsHeight() - visibleHeight() - scrollOrigin().y()); 224 maximumOffset.clampNegativeToZero(); 225 return maximumOffset; 226 } 227 228 IntPoint ScrollView::minimumScrollPosition() const 229 { 230 return IntPoint(-scrollOrigin().x(), -scrollOrigin().y()); 231 } 232 233 IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint) const 234 { 235 if (!constrainsScrollingToContentEdge()) 236 return scrollPoint; 237 238 IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition()); 239 newScrollPosition = newScrollPosition.expandedTo(minimumScrollPosition()); 240 return newScrollPosition; 241 } 242 243 int ScrollView::scrollSize(ScrollbarOrientation orientation) const 244 { 245 Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get(); 246 247 // If no scrollbars are present, the content may still be scrollable. 248 if (!scrollbar) { 249 IntSize scrollSize = m_contentsSize - visibleContentRect().size(); 250 scrollSize.clampNegativeToZero(); 251 return orientation == HorizontalScrollbar ? scrollSize.width() : scrollSize.height(); 252 } 253 254 return scrollbar->totalSize() - scrollbar->visibleSize(); 255 } 256 257 void ScrollView::notifyPageThatContentAreaWillPaint() const 258 { 259 } 260 261 void ScrollView::setScrollOffset(const IntPoint& offset) 262 { 263 scrollTo(toIntSize(adjustScrollPositionWithinRange(offset))); 264 } 265 266 void ScrollView::scrollTo(const IntSize& newOffset) 267 { 268 IntSize scrollDelta = newOffset - m_scrollOffset; 269 if (scrollDelta == IntSize()) 270 return; 271 m_scrollOffset = newOffset; 272 273 if (scrollbarsSuppressed()) 274 return; 275 276 if (isFrameView()) 277 m_pendingScrollDelta += scrollDelta; 278 else 279 scrollContents(scrollDelta); 280 } 281 282 void ScrollView::setScrollPosition(const IntPoint& scrollPoint) 283 { 284 IntPoint newScrollPosition = adjustScrollPositionWithinRange(scrollPoint); 285 286 if (newScrollPosition == scrollPosition()) 287 return; 288 289 updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y())); 290 } 291 292 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity) 293 { 294 ScrollDirection physicalDirection = 295 toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument()); 296 297 return ScrollableArea::scroll(physicalDirection, granularity); 298 } 299 300 IntSize ScrollView::overhangAmount() const 301 { 302 IntSize stretch; 303 304 IntPoint currentScrollPosition = scrollPosition(); 305 IntPoint minScrollPosition = minimumScrollPosition(); 306 IntPoint maxScrollPosition = maximumScrollPosition(); 307 308 if (currentScrollPosition.x() < minScrollPosition.x()) 309 stretch.setWidth(currentScrollPosition.x() - minScrollPosition.x()); 310 if (currentScrollPosition.x() > maxScrollPosition.x()) 311 stretch.setWidth(currentScrollPosition.x() - maxScrollPosition.x()); 312 313 if (currentScrollPosition.y() < minScrollPosition.y()) 314 stretch.setHeight(currentScrollPosition.y() - minScrollPosition.y()); 315 if (currentScrollPosition.y() > maxScrollPosition.y()) 316 stretch.setHeight(currentScrollPosition.y() - maxScrollPosition.y()); 317 318 return stretch; 319 } 320 321 void ScrollView::windowResizerRectChanged() 322 { 323 updateScrollbars(scrollOffset()); 324 } 325 326 static bool useOverlayScrollbars() 327 { 328 // FIXME: Need to detect the presence of CSS custom scrollbars, which are non-overlay regardless the ScrollbarTheme. 329 return ScrollbarTheme::theme()->usesOverlayScrollbars(); 330 } 331 332 void ScrollView::computeScrollbarExistence(bool& newHasHorizontalScrollbar, bool& newHasVerticalScrollbar, ComputeScrollbarExistenceOption option) const 333 { 334 bool hasHorizontalScrollbar = m_horizontalScrollbar; 335 bool hasVerticalScrollbar = m_verticalScrollbar; 336 337 newHasHorizontalScrollbar = hasHorizontalScrollbar; 338 newHasVerticalScrollbar = hasVerticalScrollbar; 339 340 ScrollbarMode hScroll = m_horizontalScrollbarMode; 341 ScrollbarMode vScroll = m_verticalScrollbarMode; 342 343 if (hScroll != ScrollbarAuto) 344 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn); 345 if (vScroll != ScrollbarAuto) 346 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn); 347 348 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) 349 return; 350 351 IntSize docSize = contentsSize(); 352 353 if (hScroll == ScrollbarAuto) 354 newHasHorizontalScrollbar = docSize.width() > visibleWidth(); 355 if (vScroll == ScrollbarAuto) 356 newHasVerticalScrollbar = docSize.height() > visibleHeight(); 357 358 if (useOverlayScrollbars()) 359 return; 360 361 IntSize fullVisibleSize = visibleContentRect(IncludeScrollbars).size(); 362 363 bool attemptToRemoveScrollbars = (option == FirstPass 364 && docSize.width() <= fullVisibleSize.width() && docSize.height() <= fullVisibleSize.height()); 365 if (attemptToRemoveScrollbars) { 366 if (hScroll == ScrollbarAuto) 367 newHasHorizontalScrollbar = false; 368 if (vScroll == ScrollbarAuto) 369 newHasVerticalScrollbar = false; 370 } 371 372 // If we ever turn one scrollbar off, always turn the other one off too. 373 // Never ever try to both gain/lose a scrollbar in the same pass. 374 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn) 375 newHasVerticalScrollbar = false; 376 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn) 377 newHasHorizontalScrollbar = false; 378 } 379 380 void ScrollView::updateScrollbarGeometry() 381 { 382 if (m_horizontalScrollbar) { 383 int clientWidth = visibleWidth(); 384 IntRect oldRect(m_horizontalScrollbar->frameRect()); 385 IntRect hBarRect((shouldPlaceVerticalScrollbarOnLeft() && m_verticalScrollbar) ? m_verticalScrollbar->width() : 0, 386 height() - m_horizontalScrollbar->height(), 387 width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0), 388 m_horizontalScrollbar->height()); 389 m_horizontalScrollbar->setFrameRect(hBarRect); 390 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect()) 391 m_horizontalScrollbar->invalidate(); 392 393 if (m_scrollbarsSuppressed) 394 m_horizontalScrollbar->setSuppressInvalidation(true); 395 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth); 396 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth()); 397 m_horizontalScrollbar->offsetDidChange(); 398 if (m_scrollbarsSuppressed) 399 m_horizontalScrollbar->setSuppressInvalidation(false); 400 } 401 402 if (m_verticalScrollbar) { 403 int clientHeight = visibleHeight(); 404 IntRect oldRect(m_verticalScrollbar->frameRect()); 405 IntRect vBarRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()), 406 0, 407 m_verticalScrollbar->width(), 408 height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0)); 409 m_verticalScrollbar->setFrameRect(vBarRect); 410 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect()) 411 m_verticalScrollbar->invalidate(); 412 413 if (m_scrollbarsSuppressed) 414 m_verticalScrollbar->setSuppressInvalidation(true); 415 m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight); 416 m_verticalScrollbar->setProportion(clientHeight, contentsHeight()); 417 m_verticalScrollbar->offsetDidChange(); 418 if (m_scrollbarsSuppressed) 419 m_verticalScrollbar->setSuppressInvalidation(false); 420 } 421 } 422 423 bool ScrollView::adjustScrollbarExistence(ComputeScrollbarExistenceOption option) 424 { 425 ASSERT(m_inUpdateScrollbars); 426 427 // If we came in here with the view already needing a layout, then go ahead and do that 428 // first. (This will be the common case, e.g., when the page changes due to window resizing for example). 429 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total. 430 if (!m_scrollbarsSuppressed) 431 scrollbarExistenceDidChange(); 432 433 bool hasHorizontalScrollbar = m_horizontalScrollbar; 434 bool hasVerticalScrollbar = m_verticalScrollbar; 435 436 bool newHasHorizontalScrollbar = false; 437 bool newHasVerticalScrollbar = false; 438 computeScrollbarExistence(newHasHorizontalScrollbar, newHasVerticalScrollbar, option); 439 440 bool scrollbarExistenceChanged = hasHorizontalScrollbar != newHasHorizontalScrollbar || hasVerticalScrollbar != newHasVerticalScrollbar; 441 if (!scrollbarExistenceChanged) 442 return false; 443 444 setHasHorizontalScrollbar(newHasHorizontalScrollbar); 445 setHasVerticalScrollbar(newHasVerticalScrollbar); 446 447 if (m_scrollbarsSuppressed) 448 return true; 449 450 if (!useOverlayScrollbars()) 451 contentsResized(); 452 scrollbarExistenceDidChange(); 453 return true; 454 } 455 456 void ScrollView::updateScrollbars(const IntSize& desiredOffset) 457 { 458 if (m_inUpdateScrollbars) 459 return; 460 InUpdateScrollbarsScope inUpdateScrollbarsScope(this); 461 462 IntSize oldVisibleSize = visibleSize(); 463 464 bool scrollbarExistenceChanged = false; 465 int maxUpdateScrollbarsPass = useOverlayScrollbars() || m_scrollbarsSuppressed ? 1 : 3; 466 for (int updateScrollbarsPass = 0; updateScrollbarsPass < maxUpdateScrollbarsPass; updateScrollbarsPass++) { 467 if (!adjustScrollbarExistence(updateScrollbarsPass ? Incremental : FirstPass)) 468 break; 469 scrollbarExistenceChanged = true; 470 } 471 472 updateScrollbarGeometry(); 473 474 if (scrollbarExistenceChanged) { 475 // FIXME: Is frameRectsChanged really necessary here? Have any frame rects changed? 476 frameRectsChanged(); 477 positionScrollbarLayers(); 478 updateScrollCorner(); 479 } 480 481 // FIXME: We don't need to do this if we are composited. 482 IntSize newVisibleSize = visibleSize(); 483 if (newVisibleSize.width() > oldVisibleSize.width()) { 484 if (shouldPlaceVerticalScrollbarOnLeft()) 485 invalidateRect(IntRect(0, 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height())); 486 else 487 invalidateRect(IntRect(oldVisibleSize.width(), 0, newVisibleSize.width() - oldVisibleSize.width(), newVisibleSize.height())); 488 } 489 if (newVisibleSize.height() > oldVisibleSize.height()) 490 invalidateRect(IntRect(0, oldVisibleSize.height(), newVisibleSize.width(), newVisibleSize.height() - oldVisibleSize.height())); 491 492 IntPoint adjustedScrollPosition = IntPoint(desiredOffset); 493 if (!isRubberBandInProgress()) 494 adjustedScrollPosition = adjustScrollPositionWithinRange(adjustedScrollPosition); 495 if (adjustedScrollPosition != scrollPosition() || scrollOriginChanged()) { 496 ScrollableArea::scrollToOffsetWithoutAnimation(adjustedScrollPosition); 497 resetScrollOriginChanged(); 498 } 499 } 500 501 const int panIconSizeLength = 16; 502 503 IntRect ScrollView::rectToCopyOnScroll() const 504 { 505 IntRect scrollViewRect = convertToRootView(IntRect((shouldPlaceVerticalScrollbarOnLeft() && verticalScrollbar()) ? verticalScrollbar()->width() : 0, 0, visibleWidth(), visibleHeight())); 506 if (hasOverlayScrollbars()) { 507 int verticalScrollbarWidth = (verticalScrollbar() && !hasLayerForVerticalScrollbar()) ? verticalScrollbar()->width() : 0; 508 int horizontalScrollbarHeight = (horizontalScrollbar() && !hasLayerForHorizontalScrollbar()) ? horizontalScrollbar()->height() : 0; 509 510 scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth); 511 scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight); 512 } 513 return scrollViewRect; 514 } 515 516 void ScrollView::scrollContentsIfNeeded() 517 { 518 if (m_pendingScrollDelta.isZero()) 519 return; 520 IntSize scrollDelta = m_pendingScrollDelta; 521 m_pendingScrollDelta = IntSize(); 522 scrollContents(scrollDelta); 523 } 524 525 void ScrollView::scrollContents(const IntSize& scrollDelta) 526 { 527 HostWindow* window = hostWindow(); 528 if (!window) 529 return; 530 531 // Since scrolling is double buffered, we will be blitting the scroll view's intersection 532 // with the clip rect every time to keep it smooth. 533 IntRect clipRect = windowClipRect(); 534 IntRect scrollViewRect = rectToCopyOnScroll(); 535 IntRect updateRect = clipRect; 536 updateRect.intersect(scrollViewRect); 537 538 if (m_drawPanScrollIcon) { 539 // FIXME: the pan icon is broken when accelerated compositing is on, since it will draw under the compositing layers. 540 // https://bugs.webkit.org/show_bug.cgi?id=47837 541 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary 542 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2)); 543 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation, IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength)); 544 panScrollIconDirtyRect.intersect(clipRect); 545 window->invalidateContentsAndRootView(panScrollIconDirtyRect); 546 } 547 548 if (!shouldAttemptToScrollUsingFastPath() || !scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect)) 549 scrollContentsSlowPath(updateRect); 550 551 // Invalidate the overhang areas if they are visible. 552 updateOverhangAreas(); 553 554 // This call will move children with native widgets (plugins) and invalidate them as well. 555 frameRectsChanged(); 556 } 557 558 bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) 559 { 560 hostWindow()->scroll(scrollDelta, rectToScroll, clipRect); 561 return true; 562 } 563 564 void ScrollView::scrollContentsSlowPath(const IntRect& updateRect) 565 { 566 hostWindow()->invalidateContentsForSlowScroll(updateRect); 567 } 568 569 IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const 570 { 571 IntPoint viewPoint = convertFromRootView(rootViewPoint); 572 return viewPoint + scrollOffset(); 573 } 574 575 IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const 576 { 577 IntPoint viewPoint = contentsPoint - scrollOffset(); 578 return convertToRootView(viewPoint); 579 } 580 581 IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const 582 { 583 IntRect viewRect = convertFromRootView(rootViewRect); 584 viewRect.move(scrollOffset()); 585 return viewRect; 586 } 587 588 IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const 589 { 590 IntRect viewRect = contentsRect; 591 viewRect.move(-scrollOffset()); 592 return convertToRootView(viewRect); 593 } 594 595 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const 596 { 597 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 598 return viewPoint + scrollOffset(); 599 } 600 601 FloatPoint ScrollView::windowToContents(const FloatPoint& windowPoint) const 602 { 603 FloatPoint viewPoint = convertFromContainingWindow(windowPoint); 604 return viewPoint + scrollOffset(); 605 } 606 607 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const 608 { 609 IntPoint viewPoint = contentsPoint - scrollOffset(); 610 return convertToContainingWindow(viewPoint); 611 } 612 613 IntRect ScrollView::windowToContents(const IntRect& windowRect) const 614 { 615 IntRect viewRect = convertFromContainingWindow(windowRect); 616 viewRect.move(scrollOffset()); 617 return viewRect; 618 } 619 620 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const 621 { 622 IntRect viewRect = contentsRect; 623 viewRect.move(-scrollOffset()); 624 return convertToContainingWindow(viewRect); 625 } 626 627 IntRect ScrollView::contentsToScreen(const IntRect& rect) const 628 { 629 HostWindow* window = hostWindow(); 630 if (!window) 631 return IntRect(); 632 return window->rootViewToScreen(contentsToRootView(rect)); 633 } 634 635 bool ScrollView::containsScrollbarsAvoidingResizer() const 636 { 637 return !m_scrollbarsAvoidingResizer; 638 } 639 640 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta) 641 { 642 int oldCount = m_scrollbarsAvoidingResizer; 643 m_scrollbarsAvoidingResizer += overlapDelta; 644 if (parent()) 645 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(overlapDelta); 646 else if (!scrollbarsSuppressed()) { 647 // If we went from n to 0 or from 0 to n and we're the outermost view, 648 // we need to invalidate the windowResizerRect(), since it will now need to paint 649 // differently. 650 if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) || 651 (oldCount == 0 && m_scrollbarsAvoidingResizer > 0)) 652 invalidateRect(windowResizerRect()); 653 } 654 } 655 656 void ScrollView::setParent(Widget* parentView) 657 { 658 if (parentView == parent()) 659 return; 660 661 if (m_scrollbarsAvoidingResizer && parent()) 662 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer); 663 664 Widget::setParent(parentView); 665 666 if (m_scrollbarsAvoidingResizer && parent()) 667 toScrollView(parent())->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer); 668 } 669 670 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress) 671 { 672 if (suppressed == m_scrollbarsSuppressed) 673 return; 674 675 m_scrollbarsSuppressed = suppressed; 676 677 if (repaintOnUnsuppress && !suppressed) { 678 if (m_horizontalScrollbar) 679 m_horizontalScrollbar->invalidate(); 680 if (m_verticalScrollbar) 681 m_verticalScrollbar->invalidate(); 682 683 // Invalidate the scroll corner too on unsuppress. 684 invalidateRect(scrollCornerRect()); 685 } 686 } 687 688 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint) 689 { 690 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 691 if (m_horizontalScrollbar && m_horizontalScrollbar->shouldParticipateInHitTesting() && m_horizontalScrollbar->frameRect().contains(viewPoint)) 692 return m_horizontalScrollbar.get(); 693 if (m_verticalScrollbar && m_verticalScrollbar->shouldParticipateInHitTesting() && m_verticalScrollbar->frameRect().contains(viewPoint)) 694 return m_verticalScrollbar.get(); 695 return 0; 696 } 697 698 void ScrollView::setFrameRect(const IntRect& newRect) 699 { 700 IntRect oldRect = frameRect(); 701 702 if (newRect == oldRect) 703 return; 704 705 Widget::setFrameRect(newRect); 706 707 updateScrollbars(scrollOffset()); 708 709 frameRectsChanged(); 710 } 711 712 void ScrollView::frameRectsChanged() 713 { 714 HashSet<RefPtr<Widget> >::const_iterator end = m_children.end(); 715 for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current) 716 (*current)->frameRectsChanged(); 717 } 718 719 static void positionScrollbarLayer(GraphicsLayer* graphicsLayer, Scrollbar* scrollbar) 720 { 721 if (!graphicsLayer || !scrollbar) 722 return; 723 724 IntRect scrollbarRect = scrollbar->frameRect(); 725 graphicsLayer->setPosition(scrollbarRect.location()); 726 727 if (scrollbarRect.size() == graphicsLayer->size()) 728 return; 729 730 graphicsLayer->setSize(scrollbarRect.size()); 731 732 if (graphicsLayer->hasContentsLayer()) { 733 graphicsLayer->setContentsRect(IntRect(0, 0, scrollbarRect.width(), scrollbarRect.height())); 734 return; 735 } 736 737 graphicsLayer->setDrawsContent(true); 738 graphicsLayer->setNeedsDisplay(); 739 } 740 741 static void positionScrollCornerLayer(GraphicsLayer* graphicsLayer, const IntRect& cornerRect) 742 { 743 if (!graphicsLayer) 744 return; 745 graphicsLayer->setDrawsContent(!cornerRect.isEmpty()); 746 graphicsLayer->setPosition(cornerRect.location()); 747 if (cornerRect.size() != graphicsLayer->size()) 748 graphicsLayer->setNeedsDisplay(); 749 graphicsLayer->setSize(cornerRect.size()); 750 } 751 752 void ScrollView::positionScrollbarLayers() 753 { 754 positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar()); 755 positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar()); 756 positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect()); 757 } 758 759 bool ScrollView::userInputScrollable(ScrollbarOrientation orientation) const 760 { 761 ScrollbarMode mode = (orientation == HorizontalScrollbar) ? 762 m_horizontalScrollbarMode : m_verticalScrollbarMode; 763 764 return mode == ScrollbarAuto || mode == ScrollbarAlwaysOn; 765 } 766 767 bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const 768 { 769 return false; 770 } 771 772 void ScrollView::contentRectangleForPaintInvalidation(const IntRect& rect) 773 { 774 IntRect paintRect = rect; 775 if (clipsPaintInvalidations() && !paintsEntireContents()) 776 paintRect.intersect(visibleContentRect()); 777 if (paintRect.isEmpty()) 778 return; 779 780 if (HostWindow* window = hostWindow()) 781 window->invalidateContentsAndRootView(contentsToWindow(paintRect)); 782 } 783 784 IntRect ScrollView::scrollCornerRect() const 785 { 786 IntRect cornerRect; 787 788 if (hasOverlayScrollbars()) 789 return cornerRect; 790 791 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) { 792 cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : m_horizontalScrollbar->width(), 793 height() - m_horizontalScrollbar->height(), 794 width() - m_horizontalScrollbar->width(), 795 m_horizontalScrollbar->height())); 796 } 797 798 if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) { 799 cornerRect.unite(IntRect(shouldPlaceVerticalScrollbarOnLeft() ? 0 : (width() - m_verticalScrollbar->width()), 800 m_verticalScrollbar->height(), 801 m_verticalScrollbar->width(), 802 height() - m_verticalScrollbar->height())); 803 } 804 805 return cornerRect; 806 } 807 808 bool ScrollView::isScrollCornerVisible() const 809 { 810 return !scrollCornerRect().isEmpty(); 811 } 812 813 void ScrollView::scrollbarStyleChanged() 814 { 815 contentsResized(); 816 updateScrollbars(scrollOffset()); 817 positionScrollbarLayers(); 818 } 819 820 void ScrollView::updateScrollCorner() 821 { 822 } 823 824 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect) 825 { 826 ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect); 827 } 828 829 void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect) 830 { 831 bar->paint(context, rect); 832 } 833 834 void ScrollView::invalidateScrollCornerRect(const IntRect& rect) 835 { 836 invalidateRect(rect); 837 } 838 839 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect) 840 { 841 if (m_horizontalScrollbar && !layerForHorizontalScrollbar()) 842 paintScrollbar(context, m_horizontalScrollbar.get(), rect); 843 if (m_verticalScrollbar && !layerForVerticalScrollbar()) 844 paintScrollbar(context, m_verticalScrollbar.get(), rect); 845 846 if (layerForScrollCorner()) 847 return; 848 paintScrollCorner(context, scrollCornerRect()); 849 } 850 851 void ScrollView::paintPanScrollIcon(GraphicsContext* context) 852 { 853 DEFINE_STATIC_REF(Image, panScrollIcon, (Image::loadPlatformResource("panIcon"))); 854 IntPoint iconGCPoint = m_panScrollIconPoint; 855 if (parent()) 856 iconGCPoint = toScrollView(parent())->windowToContents(iconGCPoint); 857 context->drawImage(panScrollIcon, iconGCPoint); 858 } 859 860 void ScrollView::paint(GraphicsContext* context, const IntRect& rect) 861 { 862 if (context->paintingDisabled() && !context->updatingControlTints()) 863 return; 864 865 notifyPageThatContentAreaWillPaint(); 866 867 IntRect documentDirtyRect = rect; 868 if (!paintsEntireContents()) { 869 IntRect visibleAreaWithoutScrollbars(location(), visibleContentRect().size()); 870 documentDirtyRect.intersect(visibleAreaWithoutScrollbars); 871 } 872 873 if (!documentDirtyRect.isEmpty()) { 874 GraphicsContextStateSaver stateSaver(*context); 875 876 context->translate(x(), y()); 877 documentDirtyRect.moveBy(-location()); 878 879 if (!paintsEntireContents()) { 880 context->translate(-scrollX(), -scrollY()); 881 documentDirtyRect.moveBy(scrollPosition()); 882 883 context->clip(visibleContentRect()); 884 } 885 886 paintContents(context, documentDirtyRect); 887 } 888 889 calculateAndPaintOverhangAreas(context, rect); 890 891 // Now paint the scrollbars. 892 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) { 893 GraphicsContextStateSaver stateSaver(*context); 894 IntRect scrollViewDirtyRect = rect; 895 IntRect visibleAreaWithScrollbars(location(), visibleContentRect(IncludeScrollbars).size()); 896 scrollViewDirtyRect.intersect(visibleAreaWithScrollbars); 897 context->translate(x(), y()); 898 scrollViewDirtyRect.moveBy(-location()); 899 context->clip(IntRect(IntPoint(), visibleAreaWithScrollbars.size())); 900 901 paintScrollbars(context, scrollViewDirtyRect); 902 } 903 904 // Paint the panScroll Icon 905 if (m_drawPanScrollIcon) 906 paintPanScrollIcon(context); 907 } 908 909 void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect) 910 { 911 int verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) 912 ? verticalScrollbar()->width() : 0; 913 int horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) 914 ? horizontalScrollbar()->height() : 0; 915 916 int physicalScrollY = scrollPosition().y() + scrollOrigin().y(); 917 if (physicalScrollY < 0) { 918 horizontalOverhangRect = frameRect(); 919 horizontalOverhangRect.setHeight(-physicalScrollY); 920 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth); 921 } else if (contentsHeight() && physicalScrollY > contentsHeight() - visibleHeight()) { 922 int height = physicalScrollY - (contentsHeight() - visibleHeight()); 923 horizontalOverhangRect = frameRect(); 924 horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight); 925 horizontalOverhangRect.setHeight(height); 926 horizontalOverhangRect.setWidth(horizontalOverhangRect.width() - verticalScrollbarWidth); 927 } 928 929 int physicalScrollX = scrollPosition().x() + scrollOrigin().x(); 930 if (physicalScrollX < 0) { 931 verticalOverhangRect.setWidth(-physicalScrollX); 932 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight); 933 verticalOverhangRect.setX(frameRect().x()); 934 if (horizontalOverhangRect.y() == frameRect().y()) 935 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height()); 936 else 937 verticalOverhangRect.setY(frameRect().y()); 938 } else if (contentsWidth() && physicalScrollX > contentsWidth() - visibleWidth()) { 939 int width = physicalScrollX - (contentsWidth() - visibleWidth()); 940 verticalOverhangRect.setWidth(width); 941 verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height() - horizontalScrollbarHeight); 942 verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth); 943 if (horizontalOverhangRect.y() == frameRect().y()) 944 verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height()); 945 else 946 verticalOverhangRect.setY(frameRect().y()); 947 } 948 } 949 950 void ScrollView::updateOverhangAreas() 951 { 952 HostWindow* window = hostWindow(); 953 if (!window) 954 return; 955 956 IntRect horizontalOverhangRect; 957 IntRect verticalOverhangRect; 958 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect); 959 if (!horizontalOverhangRect.isEmpty()) 960 window->invalidateContentsAndRootView(horizontalOverhangRect); 961 if (!verticalOverhangRect.isEmpty()) 962 window->invalidateContentsAndRootView(verticalOverhangRect); 963 } 964 965 void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect) 966 { 967 ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect); 968 ScrollbarTheme::theme()->paintOverhangShadows(context, scrollOffset(), horizontalOverhangRect, verticalOverhangRect, dirtyRect); 969 } 970 971 void ScrollView::calculateAndPaintOverhangAreas(GraphicsContext* context, const IntRect& dirtyRect) 972 { 973 IntRect horizontalOverhangRect; 974 IntRect verticalOverhangRect; 975 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect); 976 977 if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect)) 978 paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect); 979 } 980 981 void ScrollView::calculateAndPaintOverhangBackground(GraphicsContext* context, const IntRect& dirtyRect) 982 { 983 IntRect horizontalOverhangRect; 984 IntRect verticalOverhangRect; 985 calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect); 986 987 if (dirtyRect.intersects(horizontalOverhangRect) || dirtyRect.intersects(verticalOverhangRect)) 988 ScrollbarTheme::theme()->paintOverhangBackground(context, horizontalOverhangRect, verticalOverhangRect, dirtyRect); 989 } 990 991 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint) 992 { 993 if (!scrollbarCornerPresent()) 994 return false; 995 996 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 997 998 if (m_horizontalScrollbar) { 999 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y(); 1000 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height(); 1001 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width(); 1002 1003 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin; 1004 } 1005 1006 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x(); 1007 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width(); 1008 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height(); 1009 1010 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin; 1011 } 1012 1013 bool ScrollView::scrollbarCornerPresent() const 1014 { 1015 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) 1016 || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0); 1017 } 1018 1019 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const 1020 { 1021 // Scrollbars won't be transformed within us 1022 IntRect newRect = localRect; 1023 newRect.moveBy(scrollbar->location()); 1024 return newRect; 1025 } 1026 1027 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const 1028 { 1029 IntRect newRect = parentRect; 1030 // Scrollbars won't be transformed within us 1031 newRect.moveBy(-scrollbar->location()); 1032 return newRect; 1033 } 1034 1035 // FIXME: test these on windows 1036 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const 1037 { 1038 // Scrollbars won't be transformed within us 1039 IntPoint newPoint = localPoint; 1040 newPoint.moveBy(scrollbar->location()); 1041 return newPoint; 1042 } 1043 1044 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const 1045 { 1046 IntPoint newPoint = parentPoint; 1047 // Scrollbars won't be transformed within us 1048 newPoint.moveBy(-scrollbar->location()); 1049 return newPoint; 1050 } 1051 1052 void ScrollView::setParentVisible(bool visible) 1053 { 1054 if (isParentVisible() == visible) 1055 return; 1056 1057 Widget::setParentVisible(visible); 1058 1059 if (!isSelfVisible()) 1060 return; 1061 1062 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 1063 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 1064 (*it)->setParentVisible(visible); 1065 } 1066 1067 void ScrollView::show() 1068 { 1069 if (!isSelfVisible()) { 1070 setSelfVisible(true); 1071 if (isParentVisible()) { 1072 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 1073 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 1074 (*it)->setParentVisible(true); 1075 } 1076 } 1077 1078 Widget::show(); 1079 } 1080 1081 void ScrollView::hide() 1082 { 1083 if (isSelfVisible()) { 1084 if (isParentVisible()) { 1085 HashSet<RefPtr<Widget> >::iterator end = m_children.end(); 1086 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it) 1087 (*it)->setParentVisible(false); 1088 } 1089 setSelfVisible(false); 1090 } 1091 1092 Widget::hide(); 1093 } 1094 1095 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition) 1096 { 1097 HostWindow* window = hostWindow(); 1098 if (!window) 1099 return; 1100 m_drawPanScrollIcon = true; 1101 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ; 1102 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength))); 1103 } 1104 1105 void ScrollView::removePanScrollIcon() 1106 { 1107 HostWindow* window = hostWindow(); 1108 if (!window) 1109 return; 1110 m_drawPanScrollIcon = false; 1111 window->invalidateContentsAndRootView(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength))); 1112 } 1113 1114 void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously) 1115 { 1116 if (scrollOrigin() == origin) 1117 return; 1118 1119 ScrollableArea::setScrollOrigin(origin); 1120 1121 // Update if the scroll origin changes, since our position will be different if the content size did not change. 1122 if (updatePositionAtAll && updatePositionSynchronously) 1123 updateScrollbars(scrollOffset()); 1124 } 1125 1126 } // namespace WebCore 1127