TEST_F(ScrollableAreaTest, InvalidatesNonCompositedScrollbarsWhenThumbMoves)
{
    ScrollbarThemeWithMockInvalidation theme;
    OwnPtrWillBeRawPtr<MockScrollableArea> scrollableArea = MockScrollableArea::create(IntPoint(100, 100));
    RefPtrWillBeRawPtr<Scrollbar> horizontalScrollbar = Scrollbar::createForTesting(scrollableArea.get(), HorizontalScrollbar, RegularScrollbar, &theme);
    RefPtrWillBeRawPtr<Scrollbar> verticalScrollbar = Scrollbar::createForTesting(scrollableArea.get(), VerticalScrollbar, RegularScrollbar, &theme);
    EXPECT_CALL(*scrollableArea, horizontalScrollbar()).WillRepeatedly(Return(horizontalScrollbar.get()));
    EXPECT_CALL(*scrollableArea, verticalScrollbar()).WillRepeatedly(Return(verticalScrollbar.get()));

    // Regardless of whether the theme invalidates any parts, non-composited
    // scrollbars have to be repainted if the thumb moves.
    EXPECT_CALL(*scrollableArea, layerForHorizontalScrollbar()).WillRepeatedly(Return(nullptr));
    EXPECT_CALL(*scrollableArea, layerForVerticalScrollbar()).WillRepeatedly(Return(nullptr));
    ASSERT_FALSE(scrollableArea->hasLayerForVerticalScrollbar());
    ASSERT_FALSE(scrollableArea->hasLayerForHorizontalScrollbar());
    EXPECT_CALL(theme, shouldRepaintAllPartsOnInvalidation()).WillRepeatedly(Return(false));
    EXPECT_CALL(theme, invalidateOnThumbPositionChange(_, _, _)).WillRepeatedly(Return(NoPart));

    // A scroll in each direction should only invalidate one scrollbar.
    scrollableArea->setScrollPosition(DoublePoint(0, 50), ProgrammaticScroll);
    EXPECT_FALSE(scrollableArea->horizontalScrollbarNeedsPaintInvalidation());
    EXPECT_TRUE(scrollableArea->verticalScrollbarNeedsPaintInvalidation());
    scrollableArea->clearNeedsPaintInvalidationForScrollControls();
    scrollableArea->setScrollPosition(DoublePoint(50, 50), ProgrammaticScroll);
    EXPECT_TRUE(scrollableArea->horizontalScrollbarNeedsPaintInvalidation());
    EXPECT_FALSE(scrollableArea->verticalScrollbarNeedsPaintInvalidation());
    scrollableArea->clearNeedsPaintInvalidationForScrollControls();

    // Forced GC in order to finalize objects depending on the mock object.
    Heap::collectAllGarbage();
}
void ScrollableArea::setScrollPositionSingleAxis(ScrollbarOrientation orientation, double position, ScrollType scrollType, ScrollBehavior behavior)
{
    DoublePoint newPosition;
    if (orientation == HorizontalScrollbar)
        newPosition = DoublePoint(position, scrollAnimator()->currentPosition().y());
    else
        newPosition = DoublePoint(scrollAnimator()->currentPosition().x(), position);

    // TODO(bokan): Note, this doesn't use the derived class versions since this method is currently used
    // exclusively by code that adjusts the position by the scroll origin and the derived class versions
    // differ on whether they take that into account or not.
    ScrollableArea::setScrollPosition(newPosition, scrollType, behavior);
}
예제 #3
0
TEST_F(VisualRectMappingTest, LayoutViewSubpixelRounding) {
  document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
  setBodyInnerHTML(
      "<style>body { margin: 0; }</style>"
      "<div id=frameContainer style='position: relative; left: 0.5px'>"
      "  <iframe style='position: relative; left: 0.5px' "
      "src='http://test.com' width='200' height='200' frameBorder='0'></iframe>"
      "</div>");
  setChildFrameHTML(
      "<style>body { margin: 0; }</style><div id='target' style='position: "
      "relative; width: 100px; height: 100px; left: 0.5px'>");

  document().view()->updateAllLifecyclePhases();

  LayoutBlock* frameContainer =
      toLayoutBlock(getLayoutObjectByElementId("frameContainer"));
  LayoutObject* target =
      childDocument().getElementById("target")->layoutObject();
  LayoutRect rect(0, 0, 100, 100);
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(frameContainer, rect));
  // When passing from the iframe to the parent frame, the rect of (0.5, 0, 100,
  // 100) is expanded to (0, 0, 100, 100), and then offset by the 0.5 offset of
  // frameContainer.
  EXPECT_EQ(LayoutRect(LayoutPoint(DoublePoint(0.5, 0)), LayoutSize(101, 100)),
            rect);
}
LayoutRect RootFrameViewport::scrollIntoView(const LayoutRect& rectInContent, const ScrollAlignment& alignX, const ScrollAlignment& alignY, ScrollType scrollType)
{
    // We want to move the rect into the viewport that excludes the scrollbars so we intersect
    // the visual viewport with the scrollbar-excluded frameView content rect. However, we don't
    // use visibleContentRect directly since it floors the scroll position. Instead, we use
    // ScrollAnimatorBase::currentPosition and construct a LayoutRect from that.

    LayoutRect frameRectInContent = LayoutRect(
        layoutViewport().scrollAnimator().currentPosition(),
        layoutViewport().visibleContentRect().size());
    LayoutRect visualRectInContent = LayoutRect(
        scrollOffsetFromScrollAnimators(),
        visualViewport().visibleContentRect().size());

    // Intersect layout and visual rects to exclude the scrollbar from the view rect.
    LayoutRect viewRectInContent = intersection(visualRectInContent, frameRectInContent);
    LayoutRect targetViewport =
        ScrollAlignment::getRectToExpose(viewRectInContent, rectInContent, alignX, alignY);
    if (targetViewport != viewRectInContent)
        setScrollPosition(DoublePoint(targetViewport.x(), targetViewport.y()), scrollType);

    // RootFrameViewport only changes the viewport relative to the document so we can't change the input
    // rect's location relative to the document origin.
    return rectInContent;
}
TEST_F(EventHandlerTest, dragSelectionAfterScroll)
{
    setHtmlInnerHTML("<style> body { margin: 0px; } .upper { width: 300px; height: 400px; }"
        ".lower { margin: 0px; width: 300px; height: 400px; } .line { display: block; width: 300px; height: 30px; } </style>"
        "<div class='upper'></div>"
        "<div class='lower'>"
        "<span class='line'>Line 1</span><span class='line'>Line 2</span><span class='line'>Line 3</span><span class='line'>Line 4</span><span class='line'>Line 5</span>"
        "<span class='line'>Line 6</span><span class='line'>Line 7</span><span class='line'>Line 8</span><span class='line'>Line 9</span><span class='line'>Line 10</span>"
        "</div>");

    FrameView* frameView = document().view();
    frameView->scrollTo(DoublePoint(0, 400));

    PlatformMouseEvent mouseDownEvent(
        IntPoint(0, 0),
        IntPoint(100, 200),
        LeftButton,
        PlatformEvent::MousePressed,
        1,
        static_cast<PlatformEvent::Modifiers>(0),
        WTF::currentTime());
    document().frame()->eventHandler().handleMousePressEvent(mouseDownEvent);

    PlatformMouseEvent mouseMoveEvent(
        IntPoint(100, 50),
        IntPoint(200, 250),
        LeftButton,
        PlatformEvent::MouseMoved,
        1,
        static_cast<PlatformEvent::Modifiers>(0),
        WTF::currentTime());
    document().frame()->eventHandler().handleMouseMoveEvent(mouseMoveEvent);

    page().autoscrollController().animate(WTF::currentTime());
    page().animator().serviceScriptedAnimations(WTF::currentTime());

    PlatformMouseEvent mouseUpEvent(
        IntPoint(100, 50),
        IntPoint(200, 250),
        LeftButton,
        PlatformEvent::MouseReleased,
        1,
        static_cast<PlatformEvent::Modifiers>(0),
        WTF::currentTime());
    document().frame()->eventHandler().handleMouseReleaseEvent(mouseUpEvent);

    FrameSelection& selection = document().frame()->selection();
    ASSERT_TRUE(selection.isRange());
    RefPtrWillBeRawPtr<Range> range = createRange(selection.selection().toNormalizedEphemeralRange());
    ASSERT_TRUE(range.get());
    EXPECT_EQ("Line 1\nLine 2", range->text());
}
TEST_F(ScrollingCoordinatorTest, fractionalScrollingNonLayerFixedPosition)
{
    registerMockedHttpURLLoad("fractional-scroll-fixed-position.html");
    navigateTo(m_baseURL + "fractional-scroll-fixed-position.html");
    // Prevent fixed-position element from getting its own layer.
    webViewImpl()->settings()->setPreferCompositingToLCDTextEnabled(false);
    forceFullCompositingUpdate();

    FrameView* frameView = frame()->view();
    frameView->scrollTo(DoublePoint(1.5, 1.5));
    WebLayer* rootScrollLayer = getRootScrollLayer();
    // Scroll on main if there is non-composited fixed position element.
    // And the containing scroll layer should not get fractional scroll offset.
    ASSERT_TRUE(rootScrollLayer->shouldScrollOnMainThread());
    ASSERT_EQ(1.0, rootScrollLayer->scrollPositionDouble().x);
    ASSERT_EQ(1.0, rootScrollLayer->scrollPositionDouble().y);
    ASSERT_EQ(0.0, rootScrollLayer->position().x);
    ASSERT_EQ(0.0, rootScrollLayer->position().y);
}
void ScrollableArea::scrollIntoRect(const LayoutRect& rectInContent, const FloatRect& targetRectInFrame)
{
    // Use |pixelSnappedIntRect| for rounding to pixel as opposed to |enclosingIntRect|. It gives a better
    // combined (location and size) rounding error resulting in a more accurate scroll offset.
    // FIXME: It would probably be best to do the whole calculation in LayoutUnits but contentsToRootFrame
    // and friends don't have LayoutRect/Point versions yet.
    IntRect boundsInContent = pixelSnappedIntRect(rectInContent);
    IntRect boundsInFrame(boundsInContent.location() - toIntSize(scrollPosition()), boundsInContent.size());

    int centeringOffsetX = (targetRectInFrame.width() - boundsInFrame.width()) / 2;
    int centeringOffsetY = (targetRectInFrame.height() - boundsInFrame.height()) / 2;

    IntSize scrollDelta(
        boundsInFrame.x() - centeringOffsetX - targetRectInFrame.x(),
        boundsInFrame.y() - centeringOffsetY - targetRectInFrame.y());

    DoublePoint targetOffset = DoublePoint(scrollPosition() + scrollDelta);

    setScrollPosition(targetOffset, ProgrammaticScroll);
}
ScrollResult RootFrameViewport::handleWheel(const PlatformWheelEvent& event)
{
    updateScrollAnimator();

    ScrollableArea& primary = !m_invertScrollOrder ? layoutViewport() : visualViewport();
    ScrollableArea& secondary = !m_invertScrollOrder ? visualViewport() : layoutViewport();

    ScrollResult viewScrollResult = primary.handleWheel(event);

    // The visual viewport will only accept pixel scrolls.
    if (!event.canScroll() || event.granularity() == ScrollByPageWheelEvent)
        return viewScrollResult;

    // TODO(sataya.m) : The delta in PlatformWheelEvent is negative when scrolling the
    // wheel towards the user, so negate it to get the scroll delta that should be applied
    // to the page. unusedScrollDelta computed in the ScrollResult is also negative. Say
    // there is WheelEvent({0, -10} and page scroll by 2px and unusedScrollDelta computed
    // is {0, -8}. Due to which we have to negate the unusedScrollDelta to obtain the expected
    // animation.Please address http://crbug.com/504389.
    DoublePoint oldOffset = secondary.scrollPositionDouble();
    DoublePoint locationDelta;
    if (viewScrollResult.didScroll()) {
        locationDelta = -DoublePoint(viewScrollResult.unusedScrollDeltaX, viewScrollResult.unusedScrollDeltaY);
    } else {
        if (event.railsMode() != PlatformEvent::RailsModeVertical)
            locationDelta.setX(-event.deltaX());
        if (event.railsMode() != PlatformEvent::RailsModeHorizontal)
            locationDelta.setY(-event.deltaY());
    }

    DoublePoint targetPosition = secondary.clampScrollPosition(
        secondary.scrollPositionDouble() + toDoubleSize(locationDelta));
    secondary.setScrollPosition(targetPosition, UserScroll);

    DoublePoint usedLocationDelta(secondary.scrollPositionDouble() - oldOffset);

    bool didScrollX = viewScrollResult.didScrollX || usedLocationDelta.x();
    bool didScrollY = viewScrollResult.didScrollY || usedLocationDelta.y();
    return ScrollResult(didScrollX, didScrollY, -viewScrollResult.unusedScrollDeltaX - usedLocationDelta.x(), -viewScrollResult.unusedScrollDeltaY - usedLocationDelta.y());
}
DoublePoint VisualViewport::maximumScrollPositionDouble() const
{
    if (!mainFrame())
        return IntPoint();

    // FIXME: We probably shouldn't be storing the bounds in a float. crbug.com/422331.
    FloatSize frameViewSize(contentsSize());

    if (m_topControlsAdjustment) {
        float minScale = frameHost().pageScaleConstraintsSet().finalConstraints().minimumScale;
        frameViewSize.expand(0, m_topControlsAdjustment / minScale);
    }

    frameViewSize.scale(m_scale);
    frameViewSize = flooredIntSize(frameViewSize);

    FloatSize viewportSize(m_size);
    viewportSize.expand(0, m_topControlsAdjustment);

    FloatSize maxPosition = frameViewSize - viewportSize;
    maxPosition.scale(1 / m_scale);
    return DoublePoint(maxPosition);
}
예제 #10
0
// NOTE: Only called from Internals for testing.
void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
{
    setScrollOffsetFromAnimation(DoublePoint(offset));
}
예제 #11
0
void ScrollableArea::notifyScrollPositionChanged(const IntPoint& position)
{
    scrollPositionChanged(DoublePoint(position));
    scrollAnimator()->setCurrentPosition(position);
}
void VisualViewport::setScrollOffset(const IntPoint& offset, ScrollType scrollType)
{
    setScrollOffset(DoublePoint(offset), scrollType);
}
TEST_F(ScrollableAreaTest, InvalidatesCompositedScrollbarsIfPartsNeedRepaint)
{
    ScrollbarThemeWithMockInvalidation theme;
    OwnPtrWillBeRawPtr<MockScrollableArea> scrollableArea = MockScrollableArea::create(IntPoint(100, 100));
    RefPtrWillBeRawPtr<Scrollbar> horizontalScrollbar = Scrollbar::createForTesting(scrollableArea.get(), HorizontalScrollbar, RegularScrollbar, &theme);
    horizontalScrollbar->clearTrackNeedsRepaint();
    horizontalScrollbar->clearThumbNeedsRepaint();
    RefPtrWillBeRawPtr<Scrollbar> verticalScrollbar = Scrollbar::createForTesting(scrollableArea.get(), VerticalScrollbar, RegularScrollbar, &theme);
    verticalScrollbar->clearTrackNeedsRepaint();
    verticalScrollbar->clearThumbNeedsRepaint();
    EXPECT_CALL(*scrollableArea, horizontalScrollbar()).WillRepeatedly(Return(horizontalScrollbar.get()));
    EXPECT_CALL(*scrollableArea, verticalScrollbar()).WillRepeatedly(Return(verticalScrollbar.get()));

    // Composited scrollbars only need repainting when parts become invalid
    // (e.g. if the track changes appearance when the thumb reaches the end).
    MockGraphicsLayerClient graphicsLayerClient;
    MockGraphicsLayer layerForHorizontalScrollbar(&graphicsLayerClient);
    layerForHorizontalScrollbar.setDrawsContent(true);
    layerForHorizontalScrollbar.setSize(FloatSize(10, 10));
    MockGraphicsLayer layerForVerticalScrollbar(&graphicsLayerClient);
    layerForVerticalScrollbar.setDrawsContent(true);
    layerForVerticalScrollbar.setSize(FloatSize(10, 10));
    EXPECT_CALL(*scrollableArea, layerForHorizontalScrollbar()).WillRepeatedly(Return(&layerForHorizontalScrollbar));
    EXPECT_CALL(*scrollableArea, layerForVerticalScrollbar()).WillRepeatedly(Return(&layerForVerticalScrollbar));
    ASSERT_TRUE(scrollableArea->hasLayerForHorizontalScrollbar());
    ASSERT_TRUE(scrollableArea->hasLayerForVerticalScrollbar());
    EXPECT_CALL(theme, shouldRepaintAllPartsOnInvalidation()).WillRepeatedly(Return(false));

    // First, we'll scroll horizontally, and the theme will require repainting
    // the back button (i.e. the track).
    EXPECT_CALL(theme, invalidateOnThumbPositionChange(_, _, _)).WillOnce(Return(BackButtonStartPart));
    scrollableArea->setScrollPosition(DoublePoint(50, 0), ProgrammaticScroll);
    EXPECT_TRUE(layerForHorizontalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_FALSE(layerForVerticalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_TRUE(horizontalScrollbar->trackNeedsRepaint());
    EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint());
    layerForHorizontalScrollbar.resetTrackedPaintInvalidations();
    horizontalScrollbar->clearTrackNeedsRepaint();

    // Next, we'll scroll vertically, but invalidate the thumb.
    EXPECT_CALL(theme, invalidateOnThumbPositionChange(_, _, _)).WillOnce(Return(ThumbPart));
    scrollableArea->setScrollPosition(DoublePoint(50, 50), ProgrammaticScroll);
    EXPECT_FALSE(layerForHorizontalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_TRUE(layerForVerticalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint());
    EXPECT_TRUE(verticalScrollbar->thumbNeedsRepaint());
    layerForVerticalScrollbar.resetTrackedPaintInvalidations();
    verticalScrollbar->clearThumbNeedsRepaint();

    // Next we'll scroll in both, but the thumb position moving requires no
    // invalidations. Nonetheless the GraphicsLayer should be invalidated,
    // because we still need to update the underlying layer (though no
    // rasterization will be required).
    EXPECT_CALL(theme, invalidateOnThumbPositionChange(_, _, _)).Times(2).WillRepeatedly(Return(NoPart));
    scrollableArea->setScrollPosition(DoublePoint(70, 70), ProgrammaticScroll);
    EXPECT_TRUE(layerForHorizontalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_TRUE(layerForVerticalScrollbar.hasTrackedPaintInvalidations());
    EXPECT_FALSE(horizontalScrollbar->trackNeedsRepaint());
    EXPECT_FALSE(horizontalScrollbar->thumbNeedsRepaint());
    EXPECT_FALSE(verticalScrollbar->trackNeedsRepaint());
    EXPECT_FALSE(verticalScrollbar->thumbNeedsRepaint());

    // Forced GC in order to finalize objects depending on the mock object.
    Heap::collectAllGarbage();
}
// NOTE: Only called from Internals for testing.
void ScrollableArea::setScrollOffsetFromInternals(const IntPoint& offset)
{
    scrollPositionChanged(DoublePoint(offset), ProgrammaticScroll);
}
예제 #15
0
void RootFrameViewport::setScrollOffset(const IntPoint& offset, ScrollType scrollType)
{
    setScrollOffset(DoublePoint(offset), scrollType);
}
void RootFrameViewport::setScrollOffset(const DoublePoint& offset, ScrollType scrollType)
{
    distributeScrollBetweenViewports(DoublePoint(offset), scrollType, ScrollBehaviorInstant);
}