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 35using namespace std; 36 37namespace WebCore { 38 39ScrollView::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 53ScrollView::~ScrollView() 54{ 55} 56 57void 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 65void ScrollView::removeChild(Widget* child) 66{ 67 ASSERT(child->parent() == this); 68 child->setParent(0); 69 m_children.remove(child); 70} 71 72void 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 86void 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 100PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation) 101{ 102 return Scrollbar::create(this, orientation, RegularScrollbar); 103} 104 105void 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 139void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const 140{ 141 horizontalMode = m_horizontalScrollbarMode; 142 verticalMode = m_verticalScrollbarMode; 143} 144 145void 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 165bool ScrollView::shouldAttemptToScrollUsingFastPath() const 166{ 167 return true; 168} 169 170void ScrollView::setPaintsEntireContents(bool paintsEntireContents) 171{ 172 m_paintsEntireContents = paintsEntireContents; 173} 174 175void ScrollView::setClipsRepaints(bool clipsRepaints) 176{ 177 m_clipsRepaints = clipsRepaints; 178} 179 180IntSize ScrollView::unscaledVisibleContentSize(IncludeScrollbarsInRect scrollbarInclusion) const 181{ 182 return scrollbarInclusion == ExcludeScrollbars ? excludeScrollbars(frameRect().size()) : frameRect().size(); 183} 184 185IntSize 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 200IntRect 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 207IntSize ScrollView::contentsSize() const 208{ 209 return m_contentsSize; 210} 211 212void ScrollView::setContentsSize(const IntSize& newSize) 213{ 214 if (contentsSize() == newSize) 215 return; 216 m_contentsSize = newSize; 217 updateScrollbars(scrollOffset()); 218 updateOverhangAreas(); 219} 220 221IntPoint ScrollView::maximumScrollPosition() const 222{ 223 IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), contentsHeight() - visibleHeight() - scrollOrigin().y()); 224 maximumOffset.clampNegativeToZero(); 225 return maximumOffset; 226} 227 228IntPoint ScrollView::minimumScrollPosition() const 229{ 230 return IntPoint(-scrollOrigin().x(), -scrollOrigin().y()); 231} 232 233IntPoint 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 243int 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 257void ScrollView::notifyPageThatContentAreaWillPaint() const 258{ 259} 260 261void ScrollView::setScrollOffset(const IntPoint& offset) 262{ 263 scrollTo(toIntSize(adjustScrollPositionWithinRange(offset))); 264} 265 266void 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 282void 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 292bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity) 293{ 294 ScrollDirection physicalDirection = 295 toPhysicalDirection(direction, isVerticalDocument(), isFlippedDocument()); 296 297 return ScrollableArea::scroll(physicalDirection, granularity); 298} 299 300IntSize 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 321void ScrollView::windowResizerRectChanged() 322{ 323 updateScrollbars(scrollOffset()); 324} 325 326static 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 332void 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 380void 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 423bool 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 456void 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 501const int panIconSizeLength = 16; 502 503IntRect 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 516void ScrollView::scrollContentsIfNeeded() 517{ 518 if (m_pendingScrollDelta.isZero()) 519 return; 520 IntSize scrollDelta = m_pendingScrollDelta; 521 m_pendingScrollDelta = IntSize(); 522 scrollContents(scrollDelta); 523} 524 525void 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 558bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) 559{ 560 hostWindow()->scroll(scrollDelta, rectToScroll, clipRect); 561 return true; 562} 563 564void ScrollView::scrollContentsSlowPath(const IntRect& updateRect) 565{ 566 hostWindow()->invalidateContentsForSlowScroll(updateRect); 567} 568 569IntPoint ScrollView::rootViewToContents(const IntPoint& rootViewPoint) const 570{ 571 IntPoint viewPoint = convertFromRootView(rootViewPoint); 572 return viewPoint + scrollOffset(); 573} 574 575IntPoint ScrollView::contentsToRootView(const IntPoint& contentsPoint) const 576{ 577 IntPoint viewPoint = contentsPoint - scrollOffset(); 578 return convertToRootView(viewPoint); 579} 580 581IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const 582{ 583 IntRect viewRect = convertFromRootView(rootViewRect); 584 viewRect.move(scrollOffset()); 585 return viewRect; 586} 587 588IntRect ScrollView::contentsToRootView(const IntRect& contentsRect) const 589{ 590 IntRect viewRect = contentsRect; 591 viewRect.move(-scrollOffset()); 592 return convertToRootView(viewRect); 593} 594 595IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const 596{ 597 IntPoint viewPoint = convertFromContainingWindow(windowPoint); 598 return viewPoint + scrollOffset(); 599} 600 601FloatPoint ScrollView::windowToContents(const FloatPoint& windowPoint) const 602{ 603 FloatPoint viewPoint = convertFromContainingWindow(windowPoint); 604 return viewPoint + scrollOffset(); 605} 606 607IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const 608{ 609 IntPoint viewPoint = contentsPoint - scrollOffset(); 610 return convertToContainingWindow(viewPoint); 611} 612 613IntRect ScrollView::windowToContents(const IntRect& windowRect) const 614{ 615 IntRect viewRect = convertFromContainingWindow(windowRect); 616 viewRect.move(scrollOffset()); 617 return viewRect; 618} 619 620IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const 621{ 622 IntRect viewRect = contentsRect; 623 viewRect.move(-scrollOffset()); 624 return convertToContainingWindow(viewRect); 625} 626 627IntRect 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 635bool ScrollView::containsScrollbarsAvoidingResizer() const 636{ 637 return !m_scrollbarsAvoidingResizer; 638} 639 640void 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 656void 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 670void 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 688Scrollbar* 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 698void 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 712void 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 719static 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 741static 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 752void ScrollView::positionScrollbarLayers() 753{ 754 positionScrollbarLayer(layerForHorizontalScrollbar(), horizontalScrollbar()); 755 positionScrollbarLayer(layerForVerticalScrollbar(), verticalScrollbar()); 756 positionScrollCornerLayer(layerForScrollCorner(), scrollCornerRect()); 757} 758 759bool 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 767bool ScrollView::shouldPlaceVerticalScrollbarOnLeft() const 768{ 769 return false; 770} 771 772void 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 784IntRect 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 808bool ScrollView::isScrollCornerVisible() const 809{ 810 return !scrollCornerRect().isEmpty(); 811} 812 813void ScrollView::scrollbarStyleChanged() 814{ 815 contentsResized(); 816 updateScrollbars(scrollOffset()); 817 positionScrollbarLayers(); 818} 819 820void ScrollView::updateScrollCorner() 821{ 822} 823 824void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect) 825{ 826 ScrollbarTheme::theme()->paintScrollCorner(context, cornerRect); 827} 828 829void ScrollView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect) 830{ 831 bar->paint(context, rect); 832} 833 834void ScrollView::invalidateScrollCornerRect(const IntRect& rect) 835{ 836 invalidateRect(rect); 837} 838 839void 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 851void 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 860void 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 909void 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 950void 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 965void 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 971void 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 981void 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 991bool 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 1013bool ScrollView::scrollbarCornerPresent() const 1014{ 1015 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) 1016 || (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0); 1017} 1018 1019IntRect 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 1027IntRect 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 1036IntPoint 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 1044IntPoint 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 1052void 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 1067void 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 1081void 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 1095void 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 1105void 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 1114void 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