示例#1
0
TEST(ScrollAnimatorTest, MainThreadAnimationTargetAdjustment) {
  MockScrollableArea* scrollableArea = MockScrollableArea::create(
      true, ScrollOffset(-100, -100), ScrollOffset(1000, 1000));
  ScrollAnimator* animator = new ScrollAnimator(scrollableArea, getMockedTime);
  scrollableArea->setScrollAnimator(animator);

  // Twice from tickAnimation, once from reset, and twice from
  // adjustAnimationAndSetScrollOffset.
  EXPECT_CALL(*scrollableArea, updateScrollOffset(_, _)).Times(5);
  // One from call to userScroll and one from updateCompositorAnimations.
  EXPECT_CALL(*scrollableArea, registerForAnimation()).Times(2);
  EXPECT_CALL(*scrollableArea, scheduleAnimation())
      .Times(AtLeast(1))
      .WillRepeatedly(Return(true));

  // Idle
  EXPECT_FALSE(animator->hasAnimationThatRequiresService());
  EXPECT_EQ(ScrollOffset(), animator->currentOffset());

  // WaitingToSendToCompositor
  animator->userScroll(ScrollByLine, ScrollOffset(100, 100));

  // RunningOnMainThread
  gMockedTime += 0.05;
  animator->updateCompositorAnimations();
  animator->tickAnimation(getMockedTime());
  ScrollOffset offset = animator->currentOffset();
  EXPECT_EQ(ScrollOffset(100, 100), animator->desiredTargetOffset());
  EXPECT_GT(offset.width(), 0);
  EXPECT_GT(offset.height(), 0);

  // Adjustment
  ScrollOffset newOffset = offset + ScrollOffset(10, -10);
  animator->adjustAnimationAndSetScrollOffset(newOffset, AnchoringScroll);
  EXPECT_EQ(ScrollOffset(110, 90), animator->desiredTargetOffset());

  // Adjusting after finished animation should do nothing.
  gMockedTime += 1.0;
  animator->updateCompositorAnimations();
  animator->tickAnimation(getMockedTime());
  EXPECT_EQ(
      animator->runStateForTesting(),
      ScrollAnimatorCompositorCoordinator::RunState::PostAnimationCleanup);
  newOffset = animator->currentOffset() + ScrollOffset(10, -10);
  animator->adjustAnimationAndSetScrollOffset(newOffset, AnchoringScroll);
  EXPECT_EQ(
      animator->runStateForTesting(),
      ScrollAnimatorCompositorCoordinator::RunState::PostAnimationCleanup);
  EXPECT_EQ(ScrollOffset(110, 90), animator->desiredTargetOffset());

  reset(*animator);

  // Forced GC in order to finalize objects depending on the mock object.
  ThreadState::current()->collectAllGarbage();
}
示例#2
0
void Frame::scrollOverflowLayer(RenderLayer* layer, const IntRect& visibleRect, const IntRect& exposeRect)
{
    if (!layer)
        return;

    RenderBox* box = layer->renderBox();
    if (!box)
        return;

    if (visibleRect.intersects(exposeRect))
        return;

    // FIXME: Why isn't this just calling RenderLayer::scrollRectToVisible()?
    ScrollOffset scrollOffset = layer->scrollOffset();
    int exposeLeft = exposeRect.x();
    int exposeRight = exposeLeft + exposeRect.width();
    int clientWidth = roundToInt(box->clientWidth());
    if (exposeLeft <= 0)
        scrollOffset.setX(std::max(0, scrollOffset.x() + exposeLeft - clientWidth / 2));
    else if (exposeRight >= clientWidth)
        scrollOffset.setX(std::min(box->scrollWidth() - clientWidth, scrollOffset.x() + clientWidth / 2));

    int exposeTop = exposeRect.y();
    int exposeBottom = exposeTop + exposeRect.height();
    int clientHeight = roundToInt(box->clientHeight());
    if (exposeTop <= 0)
        scrollOffset.setY(std::max(0, scrollOffset.y() + exposeTop - clientHeight / 2));
    else if (exposeBottom >= clientHeight)
        scrollOffset.setY(std::min(box->scrollHeight() - clientHeight, scrollOffset.y() + clientHeight / 2));

    layer->scrollToOffset(scrollOffset);
    selection().setCaretRectNeedsUpdate();
    selection().updateAppearance();
}
示例#3
0
void Scrollbar::moveThumb(int pos, bool draggingDocument) {
  if (!m_scrollableArea)
    return;

  int delta = pos - m_pressedPos;

  if (draggingDocument) {
    if (m_draggingDocument)
      delta = pos - m_documentDragPos;
    m_draggingDocument = true;
    ScrollOffset currentPosition =
        m_scrollableArea->scrollAnimator().currentOffset();
    float destinationPosition =
        (m_orientation == HorizontalScrollbar ? currentPosition.width()
                                              : currentPosition.height()) +
        delta;
    destinationPosition =
        m_scrollableArea->clampScrollOffset(m_orientation, destinationPosition);
    m_scrollableArea->setScrollOffsetSingleAxis(
        m_orientation, destinationPosition, UserScroll);
    m_documentDragPos = pos;
    return;
  }

  if (m_draggingDocument) {
    delta += m_pressedPos - m_documentDragPos;
    m_draggingDocument = false;
  }

  // Drag the thumb.
  int thumbPos = theme().thumbPosition(*this);
  int thumbLen = theme().thumbLength(*this);
  int trackLen = theme().trackLength(*this);
  ASSERT(thumbLen <= trackLen);
  if (thumbLen == trackLen)
    return;

  if (delta > 0)
    delta = std::min(trackLen - thumbLen - thumbPos, delta);
  else if (delta < 0)
    delta = std::max(-thumbPos, delta);

  float minOffset = m_scrollableArea->minimumScrollOffset(m_orientation);
  float maxOffset = m_scrollableArea->maximumScrollOffset(m_orientation);
  if (delta) {
    float newOffset = static_cast<float>(thumbPos + delta) *
                          (maxOffset - minOffset) / (trackLen - thumbLen) +
                      minOffset;
    m_scrollableArea->setScrollOffsetSingleAxis(m_orientation, newOffset,
                                                UserScroll);
  }
}
示例#4
0
void LocalFrame::setPageAndTextZoomFactors(float pageZoomFactor,
                                           float textZoomFactor) {
  if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor)
    return;

  Page* page = this->page();
  if (!page)
    return;

  Document* document = this->document();
  if (!document)
    return;

  // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents.
  // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG
  // WG clarification.
  if (document->isSVGDocument()) {
    if (!document->accessSVGExtensions().zoomAndPanEnabled())
      return;
  }

  if (m_pageZoomFactor != pageZoomFactor) {
    if (FrameView* view = this->view()) {
      // Update the scroll position when doing a full page zoom, so the content
      // stays in relatively the same position.
      ScrollOffset scrollOffset = view->scrollOffset();
      float percentDifference = (pageZoomFactor / m_pageZoomFactor);
      view->setScrollOffset(
          ScrollOffset(scrollOffset.width() * percentDifference,
                       scrollOffset.height() * percentDifference),
          ProgrammaticScroll);
    }
  }

  m_pageZoomFactor = pageZoomFactor;
  m_textZoomFactor = textZoomFactor;

  for (Frame* child = tree().firstChild(); child;
       child = child->tree().nextSibling()) {
    if (child->isLocalFrame())
      toLocalFrame(child)->setPageAndTextZoomFactors(m_pageZoomFactor,
                                                     m_textZoomFactor);
  }

  document->mediaQueryAffectingValueChanged();
  document->setNeedsStyleRecalc(
      SubtreeStyleChange,
      StyleChangeReasonForTracing::create(StyleChangeReason::Zoom));
  document->updateStyleAndLayoutIgnorePendingStylesheets();
}
示例#5
0
 ScrollOffset clampedScrollOffset(const ScrollOffset& offset) {
   ScrollOffset minOffset = minimumScrollOffset();
   ScrollOffset maxOffset = maximumScrollOffset();
   float width = std::min(std::max(offset.width(), minOffset.width()),
                          maxOffset.width());
   float height = std::min(std::max(offset.height(), minOffset.height()),
                           maxOffset.height());
   return ScrollOffset(width, height);
 }
示例#6
0
void RootFrameViewport::restoreToAnchor(const ScrollOffset& targetOffset) {
  // Clamp the scroll offset of each viewport now so that we force any invalid
  // offsets to become valid so we can compute the correct deltas.
  visualViewport().setScrollOffset(visualViewport().getScrollOffset(),
                                   ProgrammaticScroll);
  layoutViewport().setScrollOffset(layoutViewport().getScrollOffset(),
                                   ProgrammaticScroll);

  ScrollOffset delta = targetOffset - getScrollOffset();

  visualViewport().setScrollOffset(visualViewport().getScrollOffset() + delta,
                                   ProgrammaticScroll);

  delta = targetOffset - getScrollOffset();

  // Since the main thread FrameView has integer scroll offsets, scroll it to
  // the next pixel and then we'll scroll the visual viewport again to
  // compensate for the sub-pixel offset. We need this "overscroll" to ensure
  // the pixel of which we want to be partially in appears fully inside the
  // FrameView since the VisualViewport is bounded by the FrameView.
  IntSize layoutDelta = IntSize(
      delta.width() < 0 ? floor(delta.width()) : ceil(delta.width()),
      delta.height() < 0 ? floor(delta.height()) : ceil(delta.height()));

  layoutViewport().setScrollOffset(
      ScrollOffset(layoutViewport().scrollOffsetInt() + layoutDelta),
      ProgrammaticScroll);

  delta = targetOffset - getScrollOffset();
  visualViewport().setScrollOffset(visualViewport().getScrollOffset() + delta,
                                   ProgrammaticScroll);
}
示例#7
0
bool ViewportScrollCallback::shouldScrollBrowserControls(
    const ScrollOffset& delta,
    ScrollGranularity granularity) const {
  if (granularity != ScrollByPixel && granularity != ScrollByPrecisePixel)
    return false;

  if (!m_rootFrameViewport)
    return false;

  ScrollOffset maxScroll = m_rootFrameViewport->maximumScrollOffset();
  ScrollOffset scrollOffset = m_rootFrameViewport->getScrollOffset();

  // Always give the delta to the browser controls if the scroll is in
  // the direction to show the browser controls. If it's in the
  // direction to hide the browser controls, only give the delta to the
  // browser controls when the frame can scroll.
  return delta.height() < 0 || scrollOffset.height() < maxScroll.height();
}
示例#8
0
ScrollResult ScrollAnimatorBase::userScroll(ScrollGranularity,
                                            const ScrollOffset& delta) {
  ScrollOffset consumedDelta = computeDeltaToConsume(delta);
  ScrollOffset newPos = m_currentOffset + consumedDelta;
  if (m_currentOffset == newPos)
    return ScrollResult(false, false, delta.width(), delta.height());

  m_currentOffset = newPos;

  notifyOffsetChanged();

  return ScrollResult(consumedDelta.width(), consumedDelta.height(),
                      delta.width() - consumedDelta.width(),
                      delta.height() - consumedDelta.height());
}
示例#9
0
ScrollResult ScrollAnimator::userScroll(ScrollGranularity granularity,
                                        const ScrollOffset& delta) {
  if (!m_scrollableArea->scrollAnimatorEnabled())
    return ScrollAnimatorBase::userScroll(granularity, delta);

  TRACE_EVENT0("blink", "ScrollAnimator::scroll");

  if (granularity == ScrollByPrecisePixel) {
    // Cancel scroll animation because asked to instant scroll.
    if (hasRunningAnimation())
      cancelAnimation();
    return ScrollAnimatorBase::userScroll(granularity, delta);
  }

  bool needsPostAnimationCleanup = m_runState == RunState::PostAnimationCleanup;
  if (m_runState == RunState::PostAnimationCleanup)
    resetAnimationState();

  ScrollOffset consumedDelta = computeDeltaToConsume(delta);
  ScrollOffset targetOffset = desiredTargetOffset();
  targetOffset += consumedDelta;

  if (willAnimateToOffset(targetOffset)) {
    m_lastGranularity = granularity;
    // Report unused delta only if there is no animation running. See
    // comment below regarding scroll latching.
    // TODO(bokan): Need to standardize how ScrollAnimators report
    // unusedDelta. This differs from ScrollAnimatorMac currently.
    return ScrollResult(true, true, 0, 0);
  }

  // If the run state when this method was called was PostAnimationCleanup and
  // we're not starting an animation, stay in PostAnimationCleanup state so
  // that the main thread scrolling reason can be removed.
  if (needsPostAnimationCleanup)
    m_runState = RunState::PostAnimationCleanup;

  // Report unused delta only if there is no animation and we are not
  // starting one. This ensures we latch for the duration of the
  // animation rather than animating multiple scrollers at the same time.
  return ScrollResult(false, false, delta.width(), delta.height());
}
示例#10
0
void RootFrameViewport::distributeScrollBetweenViewports(
    const ScrollOffset& offset,
    ScrollType scrollType,
    ScrollBehavior behavior,
    ViewportToScrollFirst scrollFirst) {
  // Make sure we use the scroll offsets as reported by each viewport's
  // ScrollAnimatorBase, since its ScrollableArea's offset may have the
  // fractional part truncated off.
  // TODO(szager): Now that scroll offsets are stored as floats, can we take the
  // scroll offset directly from the ScrollableArea's rather than the animators?
  ScrollOffset oldOffset = scrollOffsetFromScrollAnimators();

  ScrollOffset delta = offset - oldOffset;

  if (delta.isZero())
    return;

  ScrollableArea& primary =
      scrollFirst == VisualViewport ? visualViewport() : layoutViewport();
  ScrollableArea& secondary =
      scrollFirst == VisualViewport ? layoutViewport() : visualViewport();

  ScrollOffset targetOffset = primary.clampScrollOffset(
      primary.scrollAnimator().currentOffset() + delta);

  primary.setScrollOffset(targetOffset, scrollType, behavior);

  // Scroll the secondary viewport if all of the scroll was not applied to the
  // primary viewport.
  ScrollOffset updatedOffset =
      secondary.scrollAnimator().currentOffset() + FloatSize(targetOffset);
  ScrollOffset applied = updatedOffset - oldOffset;
  delta -= applied;

  if (delta.isZero())
    return;

  targetOffset = secondary.clampScrollOffset(
      secondary.scrollAnimator().currentOffset() + delta);
  secondary.setScrollOffset(targetOffset, scrollType, behavior);
}
示例#11
0
void ScrollAnchor::notifyBeforeLayout() {
  if (m_queued) {
    m_scrollAnchorDisablingStyleChanged |=
        computeScrollAnchorDisablingStyleChanged();
    return;
  }
  DCHECK(m_scroller);
  ScrollOffset scrollOffset = m_scroller->getScrollOffset();
  float blockDirectionScrollOffset =
      scrollerLayoutBox(m_scroller)->isHorizontalWritingMode()
          ? scrollOffset.height()
          : scrollOffset.width();
  if (blockDirectionScrollOffset == 0) {
    clearSelf();
    return;
  }

  if (!m_anchorObject) {
    findAnchor();
    if (!m_anchorObject)
      return;

    m_anchorObject->setIsScrollAnchorObject();
    m_savedRelativeOffset =
        computeRelativeOffset(m_anchorObject, m_scroller, m_corner);
  }

  m_scrollAnchorDisablingStyleChanged =
      computeScrollAnchorDisablingStyleChanged();

  FrameView* frameView = scrollerLayoutBox(m_scroller)->frameView();
  ScrollableArea* owningScroller =
      m_scroller->isRootFrameViewport()
          ? &toRootFrameViewport(m_scroller)->layoutViewport()
          : m_scroller.get();
  frameView->enqueueScrollAnchoringAdjustment(owningScroller);
  m_queued = true;
}
void PaintPropertyTreeBuilder::buildTreeNodes(
    FrameView& frameView,
    PaintPropertyTreeBuilderContext& context) {
  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
    LayoutView* layoutView = frameView.layoutView();
    if (!layoutView)
      return;

    TransformationMatrix frameTranslate;
    frameTranslate.translate(frameView.x() + layoutView->location().x() +
                                 context.current.paintOffset.x(),
                             frameView.y() + layoutView->location().y() +
                                 context.current.paintOffset.y());
    context.current.transform =
        layoutView->getMutableForPainting()
            .ensurePaintProperties()
            .updatePaintOffsetTranslation(context.current.transform,
                                          frameTranslate, FloatPoint3D());
    context.current.paintOffset = LayoutPoint();
    context.current.renderingContextID = 0;
    context.current.shouldFlattenInheritedTransform = true;
    context.absolutePosition = context.current;
    context.containerForAbsolutePosition =
        nullptr;  // This will get set in updateOutOfFlowContext().
    context.fixedPosition = context.current;
    return;
  }

  TransformationMatrix frameTranslate;
  frameTranslate.translate(frameView.x() + context.current.paintOffset.x(),
                           frameView.y() + context.current.paintOffset.y());
  context.current.transform = updateFrameViewPreTranslation(
      frameView, context.current.transform, frameTranslate, FloatPoint3D());

  FloatRoundedRect contentClip(
      IntRect(IntPoint(), frameView.visibleContentSize()));
  context.current.clip = updateFrameViewContentClip(
      frameView, context.current.clip, frameView.preTranslation(), contentClip);

  // Record the fixed properties before any scrolling occurs.
  const auto* fixedTransformNode = context.current.transform;
  auto* fixedScrollNode = context.current.scroll;

  ScrollOffset scrollOffset = frameView.scrollOffset();
  if (frameView.isScrollable() || !scrollOffset.isZero()) {
    TransformationMatrix frameScroll;
    frameScroll.translate(-scrollOffset.width(), -scrollOffset.height());
    context.current.transform = updateFrameViewScrollTranslation(
        frameView, frameView.preTranslation(), frameScroll, FloatPoint3D());

    IntSize scrollClip = frameView.visibleContentSize();
    IntSize scrollBounds = frameView.contentsSize();
    bool userScrollableHorizontal =
        frameView.userInputScrollable(HorizontalScrollbar);
    bool userScrollableVertical =
        frameView.userInputScrollable(VerticalScrollbar);
    context.current.scroll = updateFrameViewScroll(
        frameView, context.current.scroll, frameView.scrollTranslation(),
        scrollClip, scrollBounds, userScrollableHorizontal,
        userScrollableVertical);
  } else {
    // Ensure pre-existing properties are cleared when there is no scrolling.
    frameView.setScrollTranslation(nullptr);
    frameView.setScroll(nullptr);
  }

  // Initialize the context for current, absolute and fixed position cases.
  // They are the same, except that scroll translation does not apply to
  // fixed position descendants.
  context.current.paintOffset = LayoutPoint();
  context.current.renderingContextID = 0;
  context.current.shouldFlattenInheritedTransform = true;
  context.absolutePosition = context.current;
  context.containerForAbsolutePosition = nullptr;
  context.fixedPosition = context.current;
  context.fixedPosition.transform = fixedTransformNode;
  context.fixedPosition.scroll = fixedScrollNode;

  std::unique_ptr<PropertyTreeState> contentsState(
      new PropertyTreeState(context.current.transform, context.current.clip,
                            context.currentEffect, context.current.scroll));
  frameView.setTotalPropertyTreeStateForContents(std::move(contentsState));
}
void PaintPropertyTreeBuilder::updateFramePropertiesAndContext(
    FrameView& frameView,
    PaintPropertyTreeBuilderContext& context) {
  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
    // With root layer scrolling, the LayoutView (a LayoutObject) properties are
    // updated like other objects (see updatePropertiesAndContextForSelf and
    // updatePropertiesAndContextForChildren) instead of needing LayoutView-
    // specific property updates here.
    context.current.paintOffset.moveBy(frameView.location());
    context.current.renderingContextID = 0;
    context.current.shouldFlattenInheritedTransform = true;
    context.absolutePosition = context.current;
    context.containerForAbsolutePosition = nullptr;
    context.fixedPosition = context.current;
    return;
  }

  TransformationMatrix frameTranslate;
  frameTranslate.translate(frameView.x() + context.current.paintOffset.x(),
                           frameView.y() + context.current.paintOffset.y());
  updateFrameViewPreTranslation(frameView, context.current.transform,
                                frameTranslate, FloatPoint3D());

  FloatRoundedRect contentClip(
      IntRect(IntPoint(), frameView.visibleContentSize()));
  updateFrameViewContentClip(frameView, context.current.clip,
                             frameView.preTranslation(), contentClip);

  ScrollOffset scrollOffset = frameView.scrollOffset();
  if (frameView.isScrollable() || !scrollOffset.isZero()) {
    TransformationMatrix frameScroll;
    frameScroll.translate(-scrollOffset.width(), -scrollOffset.height());
    updateFrameViewScrollTranslation(frameView, frameView.preTranslation(),
                                     frameScroll, FloatPoint3D());

    IntSize scrollClip = frameView.visibleContentSize();
    IntSize scrollBounds = frameView.contentsSize();
    bool userScrollableHorizontal =
        frameView.userInputScrollable(HorizontalScrollbar);
    bool userScrollableVertical =
        frameView.userInputScrollable(VerticalScrollbar);
    updateFrameViewScroll(frameView, context.current.scroll,
                          frameView.scrollTranslation(), scrollClip,
                          scrollBounds, userScrollableHorizontal,
                          userScrollableVertical);
  } else {
    // Ensure pre-existing properties are cleared when there is no scrolling.
    frameView.setScrollTranslation(nullptr);
    frameView.setScroll(nullptr);
  }

  // Initialize the context for current, absolute and fixed position cases.
  // They are the same, except that scroll translation does not apply to
  // fixed position descendants.
  const auto* fixedTransformNode = frameView.preTranslation()
                                       ? frameView.preTranslation()
                                       : context.current.transform;
  auto* fixedScrollNode = context.current.scroll;
  DCHECK(frameView.preTranslation());
  context.current.transform = frameView.preTranslation();
  DCHECK(frameView.contentClip());
  context.current.clip = frameView.contentClip();
  if (const auto* scrollTranslation = frameView.scrollTranslation())
    context.current.transform = scrollTranslation;
  if (auto* scroll = frameView.scroll())
    context.current.scroll = scroll;
  context.current.paintOffset = LayoutPoint();
  context.current.renderingContextID = 0;
  context.current.shouldFlattenInheritedTransform = true;
  context.absolutePosition = context.current;
  context.containerForAbsolutePosition = nullptr;
  context.fixedPosition = context.current;
  context.fixedPosition.transform = fixedTransformNode;
  context.fixedPosition.scroll = fixedScrollNode;

  std::unique_ptr<PropertyTreeState> contentsState(
      new PropertyTreeState(context.current.transform, context.current.clip,
                            context.currentEffect, context.current.scroll));
  frameView.setTotalPropertyTreeStateForContents(std::move(contentsState));
}
示例#14
0
WebPoint WebHistoryItem::scrollOffset() const {
  ScrollOffset offset = m_private->scrollOffset();
  return WebPoint(offset.width(), offset.height());
}
示例#15
0
WebFloatPoint WebHistoryItem::visualViewportScrollOffset() const {
  ScrollOffset offset = m_private->visualViewportScrollOffset();
  return WebFloatPoint(offset.width(), offset.height());
}