Beispiel #1
0
void ScrollAnimator::removeMainThreadScrollingReason() {
  if (WebLayer* scrollLayer =
          toWebLayer(getScrollableArea()->layerForScrolling())) {
    scrollLayer->clearMainThreadScrollingReasons(
        MainThreadScrollingReason::kHandlingScrollFromMainThread);
  }
}
Beispiel #2
0
void ProgrammaticScrollAnimator::updateCompositorAnimations() {
  if (m_runState == RunState::PostAnimationCleanup) {
    // No special cleanup, simply reset animation state. We have this state
    // here because the state machine is shared with ScrollAnimator which
    // has to do some cleanup that requires the compositing state to be clean.
    return resetAnimationState();
  }

  if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor) {
    // If the current run state is WaitingToSendToCompositor but we have a
    // non-zero compositor animation id, there's a currently running
    // compositor animation that needs to be removed here before the new
    // animation is added below.
    ASSERT(m_runState == RunState::WaitingToCancelOnCompositor ||
           m_runState == RunState::WaitingToSendToCompositor);

    removeAnimation();

    m_compositorAnimationId = 0;
    m_compositorAnimationGroupId = 0;
    if (m_runState == RunState::WaitingToCancelOnCompositor) {
      resetAnimationState();
      return;
    }
  }

  if (m_runState == RunState::WaitingToSendToCompositor) {
    if (!m_compositorAnimationAttachedToElementId)
      reattachCompositorPlayerIfNeeded(
          getScrollableArea()->compositorAnimationTimeline());

    bool sentToCompositor = false;

    if (!m_scrollableArea->shouldScrollOnMainThread()) {
      std::unique_ptr<CompositorAnimation> animation =
          CompositorAnimation::create(
              *m_animationCurve, CompositorTargetProperty::SCROLL_OFFSET, 0, 0);

      int animationId = animation->id();
      int animationGroupId = animation->group();

      if (addAnimation(std::move(animation))) {
        sentToCompositor = true;
        m_runState = RunState::RunningOnCompositor;
        m_compositorAnimationId = animationId;
        m_compositorAnimationGroupId = animationGroupId;
      }
    }

    if (!sentToCompositor) {
      m_runState = RunState::RunningOnMainThread;
      m_animationCurve->setInitialValue(
          compositorOffsetFromBlinkOffset(m_scrollableArea->getScrollOffset()));
      if (!m_scrollableArea->scheduleAnimation()) {
        notifyOffsetChanged(m_targetOffset);
        resetAnimationState();
      }
    }
  }
}
Beispiel #3
0
bool ScrollAnimator::registerAndScheduleAnimation() {
  getScrollableArea()->registerForAnimation();
  if (!m_scrollableArea->scheduleAnimation()) {
    scrollToOffsetWithoutAnimation(m_targetOffset);
    resetAnimationState();
    return false;
  }
  return true;
}
void ScrollableAreaPainter::paintScrollCorner(
    GraphicsContext& context,
    const IntPoint& paintOffset,
    const CullRect& adjustedCullRect) {
  IntRect absRect = getScrollableArea().scrollCornerRect();
  if (absRect.isEmpty())
    return;
  absRect.moveBy(paintOffset);

  if (getScrollableArea().scrollCorner()) {
    if (!adjustedCullRect.intersectsCullRect(absRect))
      return;
    ScrollbarPainter::paintIntoRect(*getScrollableArea().scrollCorner(),
                                    context, paintOffset, LayoutRect(absRect));
    return;
  }

  // We don't want to paint white if we have overlay scrollbars, since we need
  // to see what is behind it.
  if (getScrollableArea().hasOverlayScrollbars())
    return;

  if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
          context, getScrollableArea().box(), DisplayItem::kScrollbarCorner))
    return;

  LayoutObjectDrawingRecorder recorder(context, getScrollableArea().box(),
                                       DisplayItem::kScrollbarCorner, absRect);
  context.fillRect(absRect, Color::white);
}
void ScrollableAreaPainter::paintResizer(GraphicsContext& context,
                                         const IntPoint& paintOffset,
                                         const CullRect& cullRect) {
  if (getScrollableArea().box().style()->resize() == RESIZE_NONE)
    return;

  IntRect absRect = getScrollableArea().resizerCornerRect(
      getScrollableArea().box().pixelSnappedBorderBoxRect(), ResizerForPointer);
  if (absRect.isEmpty())
    return;
  absRect.moveBy(paintOffset);

  if (getScrollableArea().resizer()) {
    if (!cullRect.intersectsCullRect(absRect))
      return;
    ScrollbarPainter::paintIntoRect(*getScrollableArea().resizer(), context,
                                    paintOffset, LayoutRect(absRect));
    return;
  }

  if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
          context, getScrollableArea().box(), DisplayItem::kResizer))
    return;

  LayoutObjectDrawingRecorder recorder(context, getScrollableArea().box(),
                                       DisplayItem::kResizer, absRect);

  drawPlatformResizerImage(context, absRect);

  // Draw a frame around the resizer (1px grey line) if there are any scrollbars
  // present.  Clipping will exclude the right and bottom edges of this frame.
  if (!getScrollableArea().hasOverlayScrollbars() &&
      getScrollableArea().hasScrollbar()) {
    GraphicsContextStateSaver stateSaver(context);
    context.clip(absRect);
    IntRect largerCorner = absRect;
    largerCorner.setSize(
        IntSize(largerCorner.width() + 1, largerCorner.height() + 1));
    context.setStrokeColor(Color(217, 217, 217));
    context.setStrokeThickness(1.0f);
    context.setFillColor(Color::transparent);
    context.drawRect(largerCorner);
  }
}
bool ScrollableAreaPainter::overflowControlsIntersectRect(
    const CullRect& cullRect) const {
  const IntRect borderBox =
      getScrollableArea().box().pixelSnappedBorderBoxRect();

  if (cullRect.intersectsCullRect(
          getScrollableArea().rectForHorizontalScrollbar(borderBox)))
    return true;

  if (cullRect.intersectsCullRect(
          getScrollableArea().rectForVerticalScrollbar(borderBox)))
    return true;

  if (cullRect.intersectsCullRect(getScrollableArea().scrollCornerRect()))
    return true;

  if (cullRect.intersectsCullRect(
          getScrollableArea().resizerCornerRect(borderBox, ResizerForPointer)))
    return true;

  return false;
}
void ScrollableAreaPainter::drawPlatformResizerImage(
    GraphicsContext& context,
    IntRect resizerCornerRect) {
  float deviceScaleFactor =
      blink::deviceScaleFactor(getScrollableArea().box().frame());

  RefPtr<Image> resizeCornerImage;
  IntSize cornerResizerSize;
  if (deviceScaleFactor >= 2) {
    DEFINE_STATIC_REF(Image, resizeCornerImageHiRes,
                      (Image::loadPlatformResource("textAreaResizeCorner@2x")));
    resizeCornerImage = resizeCornerImageHiRes;
    cornerResizerSize = resizeCornerImage->size();
    cornerResizerSize.scale(0.5f);
  } else {
    DEFINE_STATIC_REF(Image, resizeCornerImageLoRes,
                      (Image::loadPlatformResource("textAreaResizeCorner")));
    resizeCornerImage = resizeCornerImageLoRes;
    cornerResizerSize = resizeCornerImage->size();
  }

  if (getScrollableArea()
          .box()
          .shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
    context.save();
    context.translate(resizerCornerRect.x() + cornerResizerSize.width(),
                      resizerCornerRect.y() + resizerCornerRect.height() -
                          cornerResizerSize.height());
    context.scale(-1.0, 1.0);
    context.drawImage(resizeCornerImage.get(),
                      IntRect(IntPoint(), cornerResizerSize));
    context.restore();
    return;
  }
  IntRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize,
                    cornerResizerSize);
  context.drawImage(resizeCornerImage.get(), imageRect);
}
Beispiel #8
0
void ScrollAnimator::tickAnimation(double monotonicTime) {
  if (m_runState != RunState::RunningOnMainThread)
    return;

  TRACE_EVENT0("blink", "ScrollAnimator::tickAnimation");
  double elapsedTime = monotonicTime - m_startTime;

  bool isFinished = (elapsedTime > m_animationCurve->duration());
  ScrollOffset offset = blinkOffsetFromCompositorOffset(
      isFinished ? m_animationCurve->targetValue()
                 : m_animationCurve->getValue(elapsedTime));

  offset = m_scrollableArea->clampScrollOffset(offset);

  m_currentOffset = offset;

  if (isFinished)
    m_runState = RunState::PostAnimationCleanup;
  else
    getScrollableArea()->scheduleAnimation();

  TRACE_EVENT0("blink", "ScrollAnimator::notifyOffsetChanged");
  notifyOffsetChanged();
}
void ScrollableAreaPainter::paintOverflowControls(
    GraphicsContext& context,
    const IntPoint& paintOffset,
    const CullRect& cullRect,
    bool paintingOverlayControls) {
  // Don't do anything if we have no overflow.
  if (!getScrollableArea().box().hasOverflowClip())
    return;

  IntPoint adjustedPaintOffset = paintOffset;
  if (paintingOverlayControls)
    adjustedPaintOffset = getScrollableArea().cachedOverlayScrollbarOffset();

  CullRect adjustedCullRect(cullRect, -adjustedPaintOffset);

  // Overlay scrollbars paint in a second pass through the layer tree so that
  // they will paint on top of everything else. If this is the normal painting
  // pass, paintingOverlayControls will be false, and we should just tell the
  // root layer that there are overlay scrollbars that need to be painted. That
  // will cause the second pass through the layer tree to run, and we'll paint
  // the scrollbars then. In the meantime, cache tx and ty so that the second
  // pass doesn't need to re-enter the LayoutTree to get it right.
  if (getScrollableArea().hasOverlayScrollbars() && !paintingOverlayControls) {
    getScrollableArea().setCachedOverlayScrollbarOffset(paintOffset);
    // It's not necessary to do the second pass if the scrollbars paint into
    // layers.
    if ((getScrollableArea().horizontalScrollbar() &&
         getScrollableArea().layerForHorizontalScrollbar()) ||
        (getScrollableArea().verticalScrollbar() &&
         getScrollableArea().layerForVerticalScrollbar()))
      return;
    if (!overflowControlsIntersectRect(adjustedCullRect))
      return;

    LayoutView* layoutView = getScrollableArea().box().view();

    PaintLayer* paintingRoot =
        getScrollableArea().layer()->enclosingLayerWithCompositedLayerMapping(
            IncludeSelf);
    if (!paintingRoot)
      paintingRoot = layoutView->layer();

    paintingRoot->setContainsDirtyOverlayScrollbars(true);
    return;
  }

  // This check is required to avoid painting custom CSS scrollbars twice.
  if (paintingOverlayControls && !getScrollableArea().hasOverlayScrollbars())
    return;

  {
    Optional<ScopedPaintChunkProperties> scopedTransformProperty;
    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
      const auto* objectProperties =
          getScrollableArea().box().paintProperties();
      if (objectProperties && objectProperties->scrollbarPaintOffset()) {
        PaintChunkProperties properties(
            context.getPaintController().currentPaintChunkProperties());
        properties.transform = objectProperties->scrollbarPaintOffset();
        scopedTransformProperty.emplace(
            context.getPaintController(), getScrollableArea().box(),
            DisplayItem::kScrollOverflowControls, properties);
      }
    }
    if (getScrollableArea().horizontalScrollbar() &&
        !getScrollableArea().layerForHorizontalScrollbar()) {
      TransformRecorder translateRecorder(
          context, *getScrollableArea().horizontalScrollbar(),
          AffineTransform::translation(adjustedPaintOffset.x(),
                                       adjustedPaintOffset.y()));
      getScrollableArea().horizontalScrollbar()->paint(context,
                                                       adjustedCullRect);
    }
    if (getScrollableArea().verticalScrollbar() &&
        !getScrollableArea().layerForVerticalScrollbar()) {
      TransformRecorder translateRecorder(
          context, *getScrollableArea().verticalScrollbar(),
          AffineTransform::translation(adjustedPaintOffset.x(),
                                       adjustedPaintOffset.y()));
      getScrollableArea().verticalScrollbar()->paint(context, adjustedCullRect);
    }
  }

  if (getScrollableArea().layerForScrollCorner())
    return;

  // We fill our scroll corner with white if we have a scrollbar that doesn't
  // run all the way up to the edge of the box.
  paintScrollCorner(context, adjustedPaintOffset, cullRect);

  // Paint our resizer last, since it sits on top of the scroll corner.
  paintResizer(context, adjustedPaintOffset, cullRect);
}
Beispiel #10
0
void ScrollAnimator::updateCompositorAnimations() {
  ScrollAnimatorCompositorCoordinator::updateCompositorAnimations();

  if (m_runState == RunState::PostAnimationCleanup) {
    postAnimationCleanupAndReset();
    return;
  }

  if (m_runState == RunState::WaitingToCancelOnCompositor) {
    DCHECK(m_compositorAnimationId);
    abortAnimation();
    postAnimationCleanupAndReset();
    return;
  }

  if (m_runState == RunState::RunningOnCompositorButNeedsTakeover) {
    // The call to ::takeOverCompositorAnimation aborted the animation and
    // put us in this state. The assumption is that takeOver is called
    // because a main thread scrolling reason is added, and simply trying
    // to ::sendAnimationToCompositor will fail and we will run on the main
    // thread.
    resetAnimationIds();
    m_runState = RunState::WaitingToSendToCompositor;
  }

  if (m_runState == RunState::RunningOnCompositorButNeedsUpdate ||
      m_runState == RunState::WaitingToCancelOnCompositorButNewScroll ||
      m_runState == RunState::RunningOnCompositorButNeedsAdjustment) {
    // Abort the running animation before a new one with an updated
    // target is added.
    abortAnimation();
    resetAnimationIds();

    if (m_runState != RunState::RunningOnCompositorButNeedsAdjustment) {
      // When in RunningOnCompositorButNeedsAdjustment, the call to
      // ::adjustScrollOffsetAnimation should have made the necessary
      // adjustment to the curve.
      m_animationCurve->updateTarget(
          m_timeFunction() - m_startTime,
          compositorOffsetFromBlinkOffset(m_targetOffset));
    }

    if (m_runState == RunState::WaitingToCancelOnCompositorButNewScroll) {
      m_animationCurve->setInitialValue(
          compositorOffsetFromBlinkOffset(currentOffset()));
    }

    m_runState = RunState::WaitingToSendToCompositor;
  }

  if (m_runState == RunState::WaitingToSendToCompositor) {
    if (!m_compositorAnimationAttachedToElementId)
      reattachCompositorPlayerIfNeeded(
          getScrollableArea()->compositorAnimationTimeline());

    if (!m_animationCurve)
      createAnimationCurve();

    bool runningOnMainThread = false;
    bool sentToCompositor = sendAnimationToCompositor();
    if (!sentToCompositor) {
      runningOnMainThread = registerAndScheduleAnimation();
      if (runningOnMainThread)
        m_runState = RunState::RunningOnMainThread;
    }

    // Main thread should deal with the scroll animations it started.
    if (sentToCompositor || runningOnMainThread)
      addMainThreadScrollingReason();
    else
      removeMainThreadScrollingReason();
  }
}