void ScrollAnimatorCompositorCoordinator::compositorAnimationFinished(
    int groupId)
{
    if (m_compositorAnimationGroupId != groupId)
        return;

    m_compositorAnimationId = 0;
    m_compositorAnimationGroupId = 0;

    switch (m_runState) {
    case RunState::Idle:
    case RunState::PostAnimationCleanup:
    case RunState::RunningOnMainThread:
        ASSERT_NOT_REACHED();
        break;
    case RunState::WaitingToSendToCompositor:
        break;
    case RunState::RunningOnCompositor:
    case RunState::RunningOnCompositorButNeedsUpdate:
    case RunState::RunningOnCompositorButNeedsTakeover:
    case RunState::WaitingToCancelOnCompositor:
        m_runState = RunState::PostAnimationCleanup;
        // Get serviced the next time compositor updates are allowed.
        if (scrollableArea())
            scrollableArea()->registerForAnimation();
        else
            resetAnimationState();
    }
}
bool ScrollAnimatorCompositorCoordinator::reattachCompositorPlayerIfNeeded(
    CompositorAnimationTimeline* timeline)
{
    bool reattached = false;
    int compositorAnimationAttachedToLayerId = 0;
    if (scrollableArea()->layerForScrolling())
        compositorAnimationAttachedToLayerId = scrollableArea()->layerForScrolling()->platformLayer()->id();

    if (compositorAnimationAttachedToLayerId != m_compositorAnimationAttachedToLayerId) {
        if (m_compositorPlayer && timeline) {
            // Detach from old layer (if any).
            if (m_compositorAnimationAttachedToLayerId) {
                if (m_compositorPlayer->isLayerAttached())
                    m_compositorPlayer->detachLayer();
                timeline->playerDestroyed(*this);
            }
            // Attach to new layer (if any).
            if (compositorAnimationAttachedToLayerId) {
                ASSERT(!m_compositorPlayer->isLayerAttached());
                timeline->playerAttached(*this);
                m_compositorPlayer->attachLayer(
                    scrollableArea()->layerForScrolling()->platformLayer());
                reattached = true;
            }
            m_compositorAnimationAttachedToLayerId = compositorAnimationAttachedToLayerId;
        }
    }

    return reattached;
}
void ScrollableAreaPainter::drawPlatformResizerImage(GraphicsContext* context, IntRect resizerCornerRect)
{
    float deviceScaleFactor = blink::deviceScaleFactor(scrollableArea().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 (scrollableArea().box().style()->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);
}
void ScrollAnimatorCompositorCoordinator::cancelAnimation()
{
    switch (m_runState) {
    case RunState::Idle:
    case RunState::WaitingToCancelOnCompositor:
    case RunState::PostAnimationCleanup:
        break;
    case RunState::RunningOnCompositorButNeedsTakeover:
    case RunState::WaitingToSendToCompositor:
        if (m_compositorAnimationId) {
            // We still have a previous animation running on the compositor.
            m_runState = RunState::WaitingToCancelOnCompositor;
        } else {
            resetAnimationState();
        }
        break;
    case RunState::RunningOnMainThread:
        m_runState = RunState::PostAnimationCleanup;
        break;
    case RunState::RunningOnCompositorButNeedsUpdate:
    case RunState::RunningOnCompositor:
        m_runState = RunState::WaitingToCancelOnCompositor;

        // Get serviced the next time compositor updates are allowed.
        scrollableArea()->registerForAnimation();
    }
}
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());
    FloatPoint offset = isFinished ? m_animationCurve->targetValue()
        : m_animationCurve->getValue(elapsedTime);

    offset = FloatPoint(m_scrollableArea->clampScrollPosition(offset));

    m_currentPosX = offset.x();
    m_currentPosY = offset.y();

    if (isFinished)
        resetAnimationState();
    else
        scrollableArea()->scheduleAnimation();

    TRACE_EVENT0("blink", "ScrollAnimator::notifyPositionChanged");
    notifyPositionChanged();
}
bool ScrollableAreaPainter::overflowControlsIntersectRect(const IntRect& localRect) const
{
    const IntRect borderBox = scrollableArea().box().pixelSnappedBorderBoxRect();

    if (scrollableArea().rectForHorizontalScrollbar(borderBox).intersects(localRect))
        return true;

    if (scrollableArea().rectForVerticalScrollbar(borderBox).intersects(localRect))
        return true;

    if (scrollableArea().scrollCornerRect().intersects(localRect))
        return true;

    if (scrollableArea().resizerCornerRect(borderBox, ResizerForPointer).intersects(localRect))
        return true;

    return false;
}
void ScrollAnimatorCompositorCoordinator::removeAnimation()
{
    if (m_compositorPlayer) {
        if (m_compositorPlayer->isLayerAttached())
            m_compositorPlayer->removeAnimation(m_compositorAnimationId);
    } else {
        if (GraphicsLayer* layer = scrollableArea()->layerForScrolling())
            layer->removeAnimation(m_compositorAnimationId);
    }
}
bool ScrollAnimator::registerAndScheduleAnimation()
{
    scrollableArea()->registerForAnimation();
    if (!m_scrollableArea->scheduleAnimation()) {
        scrollToOffsetWithoutAnimation(m_targetOffset);
        resetAnimationState();
        return false;
    }
    return true;
}
void ScrollableAreaPainter::paintScrollCorner(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
{
    IntRect absRect = scrollableArea().scrollCornerRect();
    if (absRect.isEmpty())
        return;
    absRect.moveBy(paintOffset);

    if (scrollableArea().scrollCorner()) {
        if (!absRect.intersects(damageRect))
            return;
        ScrollbarPainter::paintIntoRect(scrollableArea().scrollCorner(), context, paintOffset, LayoutRect(absRect));
        return;
    }

    if (!RuntimeEnabledFeatures::slimmingPaintEnabled() && !absRect.intersects(damageRect))
        return;

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

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, scrollableArea().box(), DisplayItem::ScrollbarCorner))
        return;

    LayoutObjectDrawingRecorder recorder(*context, scrollableArea().box(), DisplayItem::ScrollbarCorner, absRect);
    context->fillRect(absRect, Color::white);
}
bool ScrollAnimatorCompositorCoordinator::addAnimation(
    PassOwnPtr<CompositorAnimation> animation)
{
    if (m_compositorPlayer) {
        if (m_compositorPlayer->isLayerAttached()) {
            m_compositorPlayer->addAnimation(animation.leakPtr());
            return true;
        }
    } else {
        return scrollableArea()->layerForScrolling()->addAnimation(animation);
    }
    return false;
}
void ScrollableAreaPainter::paintResizer(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect)
{
    if (scrollableArea().box().style()->resize() == RESIZE_NONE)
        return;

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

    if (scrollableArea().resizer()) {
        if (!absRect.intersects(damageRect))
            return;
        ScrollbarPainter::paintIntoRect(scrollableArea().resizer(), context, paintOffset, LayoutRect(absRect));
        return;
    }

    if (!RuntimeEnabledFeatures::slimmingPaintEnabled() && !absRect.intersects(damageRect))
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, scrollableArea().box(), DisplayItem::Resizer))
        return;

    LayoutObjectDrawingRecorder recorder(*context, scrollableArea().box(), DisplayItem::Resizer, 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 (!scrollableArea().hasOverlayScrollbars() && scrollableArea().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);
    }
}
Exemple #12
0
ScrollResultOneDimensional ScrollAnimator::userScroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta)
{
    if (!m_scrollableArea->scrollAnimatorEnabled())
        return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta);

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

    // FIXME: get the type passed in. MouseWheel could also be by line, but should still have different
    // animation parameters than the keyboard.
    Parameters parameters;
    switch (granularity) {
    case ScrollByDocument:
    case ScrollByLine:
    case ScrollByPage:
    case ScrollByPixel:
        parameters = parametersForScrollGranularity(granularity);
        break;
    case ScrollByPrecisePixel:
        return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta);
    }

    // If the individual input setting is disabled, bail.
    if (!parameters.m_isEnabled)
        return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta);

    // This is an animatable scroll. Set the animation in motion using the appropriate parameters.
    float scrollableSize = static_cast<float>(m_scrollableArea->scrollSize(orientation));

    PerAxisData& data = (orientation == VerticalScrollbar) ? m_verticalData : m_horizontalData;
    bool needToScroll = data.updateDataFromParameters(step, delta, scrollableSize, WTF::monotonicallyIncreasingTime(), &parameters);
    float unusedDelta = needToScroll ? delta - (data.m_desiredPosition - *data.m_currentPosition) : delta;
    if (needToScroll && !animationTimerActive()) {
        m_startTime = data.m_startTime;
        animationWillStart();
        animationTimerFired();
        scrollableArea()->registerForAnimation();
    }
    return ScrollResultOneDimensional(needToScroll, unusedDelta);
}
void ScrollAnimatorCompositorCoordinator::takeoverCompositorAnimation()
{
    switch (m_runState) {
    case RunState::Idle:
    case RunState::WaitingToCancelOnCompositor:
    case RunState::PostAnimationCleanup:
    case RunState::RunningOnCompositorButNeedsTakeover:
    case RunState::WaitingToSendToCompositor:
    case RunState::RunningOnMainThread:
        break;
    case RunState::RunningOnCompositorButNeedsUpdate:
    case RunState::RunningOnCompositor:
        // We call abortAnimation that makes changes to the animation running on
        // the compositor. Thus, this function should only be called when in
        // CompositingClean state.
        abortAnimation();

        m_runState = RunState::RunningOnCompositorButNeedsTakeover;

        // Get serviced the next time compositor updates are allowed.
        scrollableArea()->registerForAnimation();
    }
}
void ScrollAnimatorNone::startNextTimer()
{
    if (scrollableArea()->scheduleAnimation())
        m_animationActive = true;
}
void ScrollableAreaPainter::paintOverflowControls(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
{
    // Don't do anything if we have no overflow.
    if (!scrollableArea().box().hasOverflowClip())
        return;

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

    IntRect localDamageRect = damageRect;
    localDamageRect.moveBy(-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 (scrollableArea().hasOverlayScrollbars() && !paintingOverlayControls) {
        scrollableArea().setCachedOverlayScrollbarOffset(paintOffset);
        // It's not necessary to do the second pass if the scrollbars paint into layers.
        if ((scrollableArea().horizontalScrollbar() && scrollableArea().layerForHorizontalScrollbar()) || (scrollableArea().verticalScrollbar() && scrollableArea().layerForVerticalScrollbar()))
            return;
        if (!overflowControlsIntersectRect(localDamageRect))
            return;

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

        DeprecatedPaintLayer* paintingRoot = scrollableArea().layer()->enclosingLayerWithCompositedDeprecatedPaintLayerMapping(IncludeSelf);
        if (!paintingRoot)
            paintingRoot = layoutView->layer();

        paintingRoot->setContainsDirtyOverlayScrollbars(true);
        return;
    }

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

    {
        if (scrollableArea().horizontalScrollbar() && !scrollableArea().layerForHorizontalScrollbar()) {
            TransformRecorder translateRecorder(*context, *scrollableArea().horizontalScrollbar(), AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()));
            scrollableArea().horizontalScrollbar()->paint(context, localDamageRect);
        }
        if (scrollableArea().verticalScrollbar() && !scrollableArea().layerForVerticalScrollbar()) {
            TransformRecorder translateRecorder(*context, *scrollableArea().verticalScrollbar(), AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()));
            scrollableArea().verticalScrollbar()->paint(context, localDamageRect);
        }
    }

    if (scrollableArea().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, damageRect);

    // Paint our resizer last, since it sits on top of the scroll corner.
    paintResizer(context, adjustedPaintOffset, damageRect);
}
void ScrollAnimatorNone::updateVisibleLengths()
{
    m_horizontalData.updateVisibleLength(scrollableArea()->visibleWidth());
    m_verticalData.updateVisibleLength(scrollableArea()->visibleHeight());
}
FloatPoint ScrollAnimatorCompositorCoordinator::blinkOffsetFromCompositorOffset(FloatPoint offset)
{
    offset.moveBy(-scrollableArea()->scrollOrigin());
    return offset;
}