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