Exemplo n.º 1
0
void PaintPropertyTreeBuilder::updatePerspective(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  const ComputedStyle& style = object.styleRef();
  if (object.isBox() && style.hasPerspective()) {
    // The perspective node must not flatten (else nothing will get
    // perspective), but it should still extend the rendering context as
    // most transform nodes do.
    TransformationMatrix matrix =
        TransformationMatrix().applyPerspective(style.perspective());
    FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) +
                          toLayoutSize(context.current.paintOffset);
    object.getMutableForPainting().ensurePaintProperties().updatePerspective(
        context.current.transform, matrix, origin,
        context.current.shouldFlattenInheritedTransform,
        context.current.renderingContextID);
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearPerspective();
  }

  const auto* properties = object.paintProperties();
  if (properties && properties->perspective()) {
    context.current.transform = properties->perspective();
    context.current.shouldFlattenInheritedTransform = false;
  }
}
Exemplo n.º 2
0
// SVG does not use the general transform update of |updateTransform|, instead
// creating a transform node for SVG-specific transforms without 3D.
void PaintPropertyTreeBuilder::updateTransformForNonRootSVG(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  DCHECK(object.isSVG() && !object.isSVGRoot());
  // SVG (other than SVGForeignObject) does not use paint offset internally.
  DCHECK(object.isSVGForeignObject() ||
         context.current.paintOffset == LayoutPoint());

  // TODO(pdr): Refactor this so all non-root SVG objects use the same
  // transform function.
  const AffineTransform& transform = object.isSVGForeignObject()
                                         ? object.localSVGTransform()
                                         : object.localToSVGParentTransform();
  // TODO(pdr): Check for the presence of a transform instead of the value.
  // Checking for an identity matrix will cause the property tree structure
  // to change during animations if the animation passes through the
  // identity matrix.
  if (!transform.isIdentity()) {
    // The origin is included in the local transform, so leave origin empty.
    object.getMutableForPainting().ensurePaintProperties().updateTransform(
        context.current.transform, TransformationMatrix(transform),
        FloatPoint3D());
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearTransform();
  }

  if (object.paintProperties() && object.paintProperties()->transform()) {
    context.current.transform = object.paintProperties()->transform();
    context.current.shouldFlattenInheritedTransform = false;
    context.current.renderingContextID = 0;
  }
}
void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (!object.isSVGRoot())
    return;

  AffineTransform transformToBorderBox =
      SVGRootPainter(toLayoutSVGRoot(object))
          .transformToPixelSnappedBorderBox(context.current.paintOffset);

  // The paint offset is included in |transformToBorderBox| so SVG does not need
  // to handle paint offset internally.
  context.current.paintOffset = LayoutPoint();

  if (transformToBorderBox.isIdentity()) {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearSvgLocalToBorderBoxTransform();
    return;
  }

  context.current.transform =
      object.getMutableForPainting()
          .ensurePaintProperties()
          .updateSvgLocalToBorderBoxTransform(
              context.current.transform, transformToBorderBox, FloatPoint3D());
  context.current.shouldFlattenInheritedTransform = false;
  context.current.renderingContextID = 0;
}
void PaintPropertyTreeBuilder::updateTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (object.isSVG() && !object.isSVGRoot()) {
        // SVG does not use paint offset internally.
        DCHECK(context.paintOffset == LayoutPoint());

        // FIXME(pdr): Check for the presence of a transform instead of the value. Checking for an
        // identity matrix will cause the property tree structure to change during animations if
        // the animation passes through the identity matrix.
        // FIXME(pdr): Refactor this so all non-root SVG objects use the same transform function.
        const AffineTransform& transform = object.isSVGForeignObject() ? object.localSVGTransform() : object.localToSVGParentTransform();
        if (transform.isIdentity())
            return;

        // The origin is included in the local transform, so use an empty origin.
        RefPtr<TransformPaintPropertyNode> svgTransform = TransformPaintPropertyNode::create(
            transform, FloatPoint3D(0, 0, 0), context.currentTransform);
        context.currentTransform = svgTransform.get();
        object.getMutableForPainting().ensureObjectPaintProperties().setTransform(svgTransform.release());
        return;
    }

    const ComputedStyle& style = object.styleRef();
    if (!object.isBox() || !style.hasTransform())
        return;

    TransformationMatrix matrix;
    style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin,
        ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties);
    RefPtr<TransformPaintPropertyNode> transformNode = TransformPaintPropertyNode::create(
        matrix, transformOrigin(toLayoutBox(object)), context.currentTransform);
    context.currentTransform = transformNode.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setTransform(transformNode.release());
}
Exemplo n.º 5
0
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  bool usesPaintOffsetTranslation = false;
  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
      object.isLayoutView()) {
    // Root layer scrolling always creates a translation node for LayoutView to
    // ensure fixed and absolute contexts use the correct transform space.
    usesPaintOffsetTranslation = true;
  } else if (object.isBoxModelObject() &&
             context.current.paintOffset != LayoutPoint()) {
    // TODO(trchen): Eliminate PaintLayer dependency.
    PaintLayer* layer = toLayoutBoxModelObject(object).layer();
    if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase))
      usesPaintOffsetTranslation = true;
  }

  // We should use the same subpixel paint offset values for snapping
  // regardless of whether a transform is present. If there is a transform
  // we round the paint offset but keep around the residual fractional
  // component for the transformed content to paint with.  In spv1 this was
  // called "subpixel accumulation". For more information, see
  // PaintLayer::subpixelAccumulation() and
  // PaintLayerPainter::paintFragmentByApplyingTransform.
  IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset);
  LayoutPoint fractionalPaintOffset =
      LayoutPoint(context.current.paintOffset - roundedPaintOffset);

  if (usesPaintOffsetTranslation) {
    object.getMutableForPainting()
        .ensurePaintProperties()
        .updatePaintOffsetTranslation(
            context.current.transform,
            TransformationMatrix().translate(roundedPaintOffset.x(),
                                             roundedPaintOffset.y()),
            FloatPoint3D(), context.current.shouldFlattenInheritedTransform,
            context.current.renderingContextID);
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearPaintOffsetTranslation();
  }

  const auto* properties = object.paintProperties();
  if (properties && properties->paintOffsetTranslation()) {
    context.current.transform = properties->paintOffsetTranslation();
    context.current.paintOffset = fractionalPaintOffset;
    if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
        object.isLayoutView()) {
      context.absolutePosition.transform = properties->paintOffsetTranslation();
      context.fixedPosition.transform = properties->paintOffsetTranslation();
      context.absolutePosition.paintOffset = LayoutPoint();
      context.fixedPosition.paintOffset = LayoutPoint();
    }
  }
}
Exemplo n.º 6
0
void PaintPropertyTreeBuilder::updateOutOfFlowContext(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.canContainAbsolutePositionObjects()) {
    context.absolutePosition = context.current;
    context.containerForAbsolutePosition = &object;
  }

  if (object.isLayoutView()) {
    if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
      const auto* initialFixedTransform = context.fixedPosition.transform;
      auto* initialFixedScroll = context.fixedPosition.scroll;

      context.fixedPosition = context.current;

      // Fixed position transform and scroll nodes should not be affected.
      context.fixedPosition.transform = initialFixedTransform;
      context.fixedPosition.scroll = initialFixedScroll;
    }
  } else if (object.canContainFixedPositionObjects()) {
    context.fixedPosition = context.current;
  } else if (object.getMutableForPainting().paintProperties() &&
             object.paintProperties()->cssClip()) {
    // CSS clip applies to all descendants, even if this object is not a
    // containing block ancestor of the descendant. It is okay for
    // absolute-position descendants because having CSS clip implies being
    // absolute position container. However for fixed-position descendants we
    // need to insert the clip here if we are not a containing block ancestor of
    // them.
    auto* cssClip = object.getMutableForPainting().paintProperties()->cssClip();

    // Before we actually create anything, check whether in-flow context and
    // fixed-position context has exactly the same clip. Reuse if possible.
    if (context.fixedPosition.clip == cssClip->parent()) {
      context.fixedPosition.clip = cssClip;
    } else {
      object.getMutableForPainting()
          .ensurePaintProperties()
          .updateCssClipFixedPosition(context.fixedPosition.clip,
                                      const_cast<TransformPaintPropertyNode*>(
                                          cssClip->localTransformSpace()),
                                      cssClip->clipRect());
      const auto* properties = object.paintProperties();
      if (properties && properties->cssClipFixedPosition())
        context.fixedPosition.clip = properties->cssClipFixedPosition();
      return;
    }
  }

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearCssClipFixedPosition();
}
void PaintPropertyTreeBuilder::updateEffect(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (!object.styleRef().hasOpacity()) {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearEffect();
    return;
  }

  context.currentEffect =
      object.getMutableForPainting().ensurePaintProperties().updateEffect(
          context.currentEffect, object.styleRef().opacity());
}
Exemplo n.º 8
0
void PaintPropertyTreeBuilder::updateOverflowClip(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (!object.isBox())
    return;
  const LayoutBox& box = toLayoutBox(object);
  // The <input> elements can't have contents thus CSS overflow property
  // doesn't apply.  However for layout purposes we do generate child layout
  // objects for them, e.g. button label.  We should clip the overflow from
  // those children. This is called control clip and we technically treat them
  // like overflow clip.
  LayoutRect clipRect;
  if (box.hasControlClip()) {
    clipRect = box.controlClipRect(context.current.paintOffset);
  } else if (box.hasOverflowClip() || box.styleRef().containsPaint() ||
             (box.isSVGRoot() &&
              toLayoutSVGRoot(box).shouldApplyViewportClip())) {
    clipRect = LayoutRect(
        pixelSnappedIntRect(box.overflowClipRect(context.current.paintOffset)));
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties()) {
      properties->clearInnerBorderRadiusClip();
      properties->clearOverflowClip();
    }
    return;
  }

  const auto* currentClip = context.current.clip;
  if (box.styleRef().hasBorderRadius()) {
    auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
        LayoutRect(context.current.paintOffset, box.size()));
    object.getMutableForPainting()
        .ensurePaintProperties()
        .updateInnerBorderRadiusClip(context.current.clip,
                                     context.current.transform, innerBorder);
    currentClip = object.paintProperties()->innerBorderRadiusClip();
  } else if (auto* properties =
                 object.getMutableForPainting().paintProperties()) {
    properties->clearInnerBorderRadiusClip();
  }

  object.getMutableForPainting().ensurePaintProperties().updateOverflowClip(
      currentClip, context.current.transform,
      FloatRoundedRect(FloatRect(clipRect)));

  const auto* properties = object.paintProperties();
  if (properties && properties->overflowClip())
    context.current.clip = properties->overflowClip();
}
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (object.isBoxModelObject()) {
        // TODO(trchen): Eliminate PaintLayer dependency.
        PaintLayer* layer = toLayoutBoxModelObject(object).layer();
        if (!layer || !layer->paintsWithTransform(GlobalPaintNormalPhase))
            return;
    }

    if (context.paintOffset == LayoutPoint())
        return;

    // We should use the same subpixel paint offset values for snapping regardless of whether a
    // transform is present. If there is a transform we round the paint offset but keep around
    // the residual fractional component for the transformed content to paint with.
    // In spv1 this was called "subpixel accumulation". For more information, see
    // PaintLayer::subpixelAccumulation() and PaintLayerPainter::paintFragmentByApplyingTransform.
    IntPoint roundedPaintOffset = roundedIntPoint(context.paintOffset);
    LayoutPoint fractionalPaintOffset = LayoutPoint(context.paintOffset - roundedPaintOffset);

    RefPtr<TransformPaintPropertyNode> paintOffsetTranslation = TransformPaintPropertyNode::create(
        TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()),
        FloatPoint3D(), context.currentTransform);
    context.currentTransform = paintOffsetTranslation.get();
    context.paintOffset = fractionalPaintOffset;
    object.getMutableForPainting().ensureObjectPaintProperties().setPaintOffsetTranslation(paintOffsetTranslation.release());
}
void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBox())
        return;
    const LayoutBox& box = toLayoutBox(object);

    // The <input> elements can't have contents thus CSS overflow property doesn't apply.
    // However for layout purposes we do generate child layout objects for them, e.g. button label.
    // We should clip the overflow from those children. This is called control clip and we
    // technically treat them like overflow clip.
    LayoutRect clipRect;
    if (box.hasControlClip())
        clipRect = box.controlClipRect(context.paintOffset);
    else if (box.hasOverflowClip())
        clipRect = box.overflowClipRect(context.paintOffset);
    else
        return;

    RefPtr<ClipPaintPropertyNode> borderRadiusClip;
    if (box.styleRef().hasBorderRadius()) {
        auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
            LayoutRect(context.paintOffset, box.size()));
        borderRadiusClip = ClipPaintPropertyNode::create(
            context.currentTransform, innerBorder, context.currentClip);
    }

    RefPtr<ClipPaintPropertyNode> overflowClip = ClipPaintPropertyNode::create(
        context.currentTransform,
        FloatRoundedRect(FloatRect(clipRect)),
        borderRadiusClip ? borderRadiusClip.release() : context.currentClip);
    context.currentClip = overflowClip.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setOverflowClip(overflowClip.release());
}
void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.styleRef().hasOpacity())
        return;
    RefPtr<EffectPaintPropertyNode> effectNode = EffectPaintPropertyNode::create(object.styleRef().opacity(), context.currentEffect);
    context.currentEffect = effectNode.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setEffect(effectNode.release());
}
Exemplo n.º 12
0
void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.hasOverflowClip()) {
    const LayoutBox& box = toLayoutBox(object);
    const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea();
    IntSize scrollOffset = box.scrolledContentOffset();
    if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) {
      TransformationMatrix matrix = TransformationMatrix().translate(
          -scrollOffset.width(), -scrollOffset.height());
      object.getMutableForPainting()
          .ensurePaintProperties()
          .updateScrollTranslation(
              context.current.transform, matrix, FloatPoint3D(),
              context.current.shouldFlattenInheritedTransform,
              context.current.renderingContextID);

      IntSize scrollClip = scrollableArea->visibleContentRect().size();
      IntSize scrollBounds = scrollableArea->contentsSize();
      bool userScrollableHorizontal =
          scrollableArea->userInputScrollable(HorizontalScrollbar);
      bool userScrollableVertical =
          scrollableArea->userInputScrollable(VerticalScrollbar);
      object.getMutableForPainting().ensurePaintProperties().updateScroll(
          context.current.scroll, object.paintProperties()->scrollTranslation(),
          scrollClip, scrollBounds, userScrollableHorizontal,
          userScrollableVertical);
    } else {
      // Ensure pre-existing properties are cleared when there is no
      // scrolling.
      auto* properties = object.getMutableForPainting().paintProperties();
      if (properties) {
        properties->clearScrollTranslation();
        properties->clearScroll();
      }
    }
  }

  if (object.paintProperties() && object.paintProperties()->scroll()) {
    context.current.transform = object.paintProperties()->scrollTranslation();
    const auto* scroll = object.paintProperties()->scroll();
    // TODO(pdr): Remove this const cast.
    context.current.scroll = const_cast<ScrollPaintPropertyNode*>(scroll);
    context.current.shouldFlattenInheritedTransform = false;
  }
}
Exemplo n.º 13
0
void PaintPropertyTreeBuilder::updateTransform(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.isSVG() && !object.isSVGRoot()) {
    updateTransformForNonRootSVG(object, context);
    return;
  }

  const ComputedStyle& style = object.styleRef();
  if (object.isBox() && (style.hasTransform() || style.preserves3D())) {
    TransformationMatrix matrix;
    style.applyTransform(matrix, toLayoutBox(object).size(),
                         ComputedStyle::ExcludeTransformOrigin,
                         ComputedStyle::IncludeMotionPath,
                         ComputedStyle::IncludeIndependentTransformProperties);

    // TODO(trchen): transform-style should only be respected if a PaintLayer
    // is created.
    // If a node with transform-style: preserve-3d does not exist in an
    // existing rendering context, it establishes a new one.
    unsigned renderingContextID = context.current.renderingContextID;
    if (style.preserves3D() && !renderingContextID)
      renderingContextID = PtrHash<const LayoutObject>::hash(&object);

    object.getMutableForPainting().ensurePaintProperties().updateTransform(
        context.current.transform, matrix, transformOrigin(toLayoutBox(object)),
        context.current.shouldFlattenInheritedTransform, renderingContextID);
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearTransform();
  }

  const auto* properties = object.paintProperties();
  if (properties && properties->transform()) {
    context.current.transform = properties->transform();
    if (object.styleRef().preserves3D()) {
      context.current.renderingContextID =
          properties->transform()->renderingContextID();
      context.current.shouldFlattenInheritedTransform = false;
    } else {
      context.current.renderingContextID = 0;
      context.current.shouldFlattenInheritedTransform = true;
    }
  }
}
Exemplo n.º 14
0
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.isBoxModelObject() &&
      context.current.paintOffset != LayoutPoint()) {
    // TODO(trchen): Eliminate PaintLayer dependency.
    PaintLayer* layer = toLayoutBoxModelObject(object).layer();
    if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase)) {
      // We should use the same subpixel paint offset values for snapping
      // regardless of whether a transform is present. If there is a transform
      // we round the paint offset but keep around the residual fractional
      // component for the transformed content to paint with.  In spv1 this was
      // called "subpixel accumulation". For more information, see
      // PaintLayer::subpixelAccumulation() and
      // PaintLayerPainter::paintFragmentByApplyingTransform.
      IntPoint roundedPaintOffset =
          roundedIntPoint(context.current.paintOffset);
      LayoutPoint fractionalPaintOffset =
          LayoutPoint(context.current.paintOffset - roundedPaintOffset);

      context.current.transform =
          object.getMutableForPainting()
              .ensurePaintProperties()
              .updatePaintOffsetTranslation(
                  context.current.transform,
                  TransformationMatrix().translate(roundedPaintOffset.x(),
                                                   roundedPaintOffset.y()),
                  FloatPoint3D(),
                  context.current.shouldFlattenInheritedTransform,
                  context.current.renderingContextID);
      context.current.paintOffset = fractionalPaintOffset;
      return;
    }
  }

  if (object.isLayoutView())
    return;

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearPaintOffsetTranslation();
}
Exemplo n.º 15
0
void PaintPropertyTreeBuilder::updateCssClip(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.hasClip()) {
    // Create clip node for descendants that are not fixed position.
    // We don't have to setup context.absolutePosition.clip here because this
    // object must be a container for absolute position descendants, and will
    // copy from in-flow context later at updateOutOfFlowContext() step.
    DCHECK(object.canContainAbsolutePositionObjects());
    LayoutRect clipRect =
        toLayoutBox(object).clipRect(context.current.paintOffset);
    context.current.clip =
        object.getMutableForPainting().ensurePaintProperties().updateCssClip(
            context.current.clip, context.current.transform,
            FloatRoundedRect(FloatRect(clipRect)));
    return;
  }

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearCssClip();
}
Exemplo n.º 16
0
void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since
  // we don't need them at the moment.
  if (!object.isBox() && !object.hasLayer()) {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearLocalBorderBoxProperties();
  } else {
    std::unique_ptr<ObjectPaintProperties::PropertyTreeStateWithOffset>
        borderBoxContext =
            wrapUnique(new ObjectPaintProperties::PropertyTreeStateWithOffset(
                context.current.paintOffset,
                PropertyTreeState(context.current.transform,
                                  context.current.clip, context.currentEffect,
                                  context.current.scroll)));
    object.getMutableForPainting()
        .ensurePaintProperties()
        .setLocalBorderBoxProperties(std::move(borderBoxContext));
  }
}
Exemplo n.º 17
0
void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.hasOverflowClip()) {
    const LayoutBox& box = toLayoutBox(object);
    const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea();
    IntSize scrollOffset = box.scrolledContentOffset();
    if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) {
      TransformationMatrix matrix = TransformationMatrix().translate(
          -scrollOffset.width(), -scrollOffset.height());
      context.current.transform =
          object.getMutableForPainting()
              .ensurePaintProperties()
              .updateScrollTranslation(
                  context.current.transform, matrix, FloatPoint3D(),
                  context.current.shouldFlattenInheritedTransform,
                  context.current.renderingContextID);

      IntSize scrollClip = scrollableArea->visibleContentRect().size();
      IntSize scrollBounds = scrollableArea->contentsSize();
      bool userScrollableHorizontal =
          scrollableArea->userInputScrollable(HorizontalScrollbar);
      bool userScrollableVertical =
          scrollableArea->userInputScrollable(VerticalScrollbar);
      context.current.scroll =
          object.getMutableForPainting().ensurePaintProperties().updateScroll(
              context.current.scroll, context.current.transform, scrollClip,
              scrollBounds, userScrollableHorizontal, userScrollableVertical);

      context.current.shouldFlattenInheritedTransform = false;
      return;
    }
  }

  if (auto* properties = object.getMutableForPainting().paintProperties()) {
    properties->clearScrollTranslation();
    properties->clearScroll();
  }
}
void PaintPropertyTreeBuilder::updatePerspective(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    const ComputedStyle& style = object.styleRef();
    if (!object.isBox() || !style.hasPerspective())
        return;

    RefPtr<TransformPaintPropertyNode> perspective = TransformPaintPropertyNode::create(
        TransformationMatrix().applyPerspective(style.perspective()),
        perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffset),
        context.currentTransform);
    context.currentTransform = perspective.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setPerspective(perspective.release());
}
Exemplo n.º 19
0
// TODO(trchen): Remove this once we bake the paint offset into frameRect.
void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(
    const LayoutObject& object,
    const PaintPropertyTreeBuilderContext& context) {
  IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset);
  if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) {
    if (PaintLayerScrollableArea* scrollableArea =
            toLayoutBoxModelObject(object).getScrollableArea()) {
      if (scrollableArea->horizontalScrollbar() ||
          scrollableArea->verticalScrollbar()) {
        auto paintOffset = TransformationMatrix().translate(
            roundedPaintOffset.x(), roundedPaintOffset.y());
        object.getMutableForPainting()
            .ensurePaintProperties()
            .updateScrollbarPaintOffset(context.current.transform, paintOffset,
                                        FloatPoint3D());
        return;
      }
    }
  }

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearScrollbarPaintOffset();
}
void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context)
{
    // Note: Currently only layer painter makes use of the pre-computed context.
    // This condition may be loosened with no adverse effects beside memory use.
    if (!object.hasLayer())
        return;

    std::unique_ptr<ObjectPaintProperties::LocalBorderBoxProperties> borderBoxContext =
        wrapUnique(new ObjectPaintProperties::LocalBorderBoxProperties);
    borderBoxContext->paintOffset = context.paintOffset;
    borderBoxContext->transform = context.currentTransform;
    borderBoxContext->clip = context.currentClip;
    borderBoxContext->effect = context.currentEffect;
    object.getMutableForPainting().ensureObjectPaintProperties().setLocalBorderBoxProperties(std::move(borderBoxContext));
}
void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isSVGRoot())
        return;

    AffineTransform transform = AffineTransform::translation(context.paintOffset.x().toFloat(), context.paintOffset.y().toFloat());
    transform *= toLayoutSVGRoot(object).localToBorderBoxTransform();
    if (transform.isIdentity())
        return;

    RefPtr<TransformPaintPropertyNode> svgLocalToBorderBoxTransform = TransformPaintPropertyNode::create(
        transform, FloatPoint3D(0, 0, 0), context.currentTransform);
    context.currentTransform = svgLocalToBorderBoxTransform.get();
    context.paintOffset = LayoutPoint();
    object.getMutableForPainting().ensureObjectPaintProperties().setSvgLocalToBorderBoxTransform(svgLocalToBorderBoxTransform.release());
}
void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.hasClip())
        return;
    ASSERT(object.canContainAbsolutePositionObjects());

    // Create clip node for descendants that are not fixed position.
    // We don't have to setup context.clipForAbsolutePosition here because this object must be
    // a container for absolute position descendants, and will copy from in-flow context later
    // at updateOutOfFlowContext() step.
    LayoutRect clipRect = toLayoutBox(object).clipRect(context.paintOffset);
    RefPtr<ClipPaintPropertyNode> clipNode = ClipPaintPropertyNode::create(
        context.currentTransform,
        FloatRoundedRect(FloatRect(clipRect)),
        context.currentClip);
    context.currentClip = clipNode.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setCssClip(clipNode.release());
}
// TODO(trchen): Remove this once we bake the paint offset into frameRect.
void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context)
{
    IntPoint roundedPaintOffset = roundedIntPoint(context.paintOffset);
    if (roundedPaintOffset == IntPoint())
        return;

    if (!object.isBoxModelObject())
        return;
    PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(object).getScrollableArea();
    if (!scrollableArea)
        return;
    if (!scrollableArea->horizontalScrollbar() && !scrollableArea->verticalScrollbar())
        return;

    auto paintOffset = TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y());
    object.getMutableForPainting().ensureObjectPaintProperties().setScrollbarPaintOffset(
        TransformPaintPropertyNode::create(paintOffset, FloatPoint3D(), context.currentTransform));
}
void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject() || !object.hasOverflowClip())
        return;

    PaintLayer* layer = toLayoutBoxModelObject(object).layer();
    ASSERT(layer);
    DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset();
    if (scrollOffset.isZero() && !layer->scrollsOverflow())
        return;

    RefPtr<TransformPaintPropertyNode> scrollTranslation = TransformPaintPropertyNode::create(
        TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()),
        FloatPoint3D(),
        context.currentTransform);
    context.currentTransform = scrollTranslation.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setScrollTranslation(scrollTranslation.release());
}
void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (object.canContainAbsolutePositionObjects()) {
        context.transformForAbsolutePosition = context.currentTransform;
        context.paintOffsetForAbsolutePosition = context.paintOffset;
        context.clipForAbsolutePosition = context.currentClip;
        context.containerForAbsolutePosition = &object;
    }

    // TODO(pdr): Remove the !object.isLayoutView() condition when removing FrameView
    // paint properties for rootLayerScrolls.
    if (!object.isLayoutView() && object.canContainFixedPositionObjects()) {
        context.transformForFixedPosition = context.currentTransform;
        context.paintOffsetForFixedPosition = context.paintOffset;
        context.clipForFixedPosition = context.currentClip;
    } else if (object.objectPaintProperties() && object.objectPaintProperties()->cssClip()) {
        // CSS clip applies to all descendants, even if this object is not a containing block
        // ancestor of the descendant. It is okay for absolute-position descendants because
        // having CSS clip implies being absolute position container. However for fixed-position
        // descendants we need to insert the clip here if we are not a containing block ancestor
        // of them.
        auto* cssClip = object.objectPaintProperties()->cssClip();

        // Before we actually create anything, check whether in-flow context and fixed-position
        // context has exactly the same clip. Reuse if possible.
        if (context.clipForFixedPosition == cssClip->parent()) {
            context.clipForFixedPosition = cssClip;
            return;
        }

        RefPtr<ClipPaintPropertyNode> clipFixedPosition = ClipPaintPropertyNode::create(
            const_cast<TransformPaintPropertyNode*>(cssClip->localTransformSpace()),
            cssClip->clipRect(),
            context.clipForFixedPosition);
        context.clipForFixedPosition = clipFixedPosition.get();
        object.getMutableForPainting().ensureObjectPaintProperties().setCssClipFixedPosition(clipFixedPosition.release());
    }
}
void PaintPropertyTreeBuilder::buildTreeNodes(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject() && !object.isSVG())
        return;

    object.getMutableForPainting().clearObjectPaintProperties();

    deriveBorderBoxFromContainerContext(object, context);

    updatePaintOffsetTranslation(object, context);
    updateTransform(object, context);
    updateEffect(object, context);
    updateCssClip(object, context);
    updateLocalBorderBoxContext(object, context);
    updateScrollbarPaintOffset(object, context);
    updateOverflowClip(object, context);
    // TODO(trchen): Insert flattening transform here, as specified by
    // http://www.w3.org/TR/css3-transforms/#transform-style-property
    updatePerspective(object, context);
    updateSvgLocalToBorderBoxTransform(object, context);
    updateScrollTranslation(object, context);
    updateOutOfFlowContext(object, context);
}
Exemplo n.º 27
0
void PaintPropertyTreeBuilder::updateTransform(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.isSVG() && !object.isSVGRoot()) {
    // SVG (other than SVGForeignObject) does not use paint offset internally.
    DCHECK(object.isSVGForeignObject() ||
           context.current.paintOffset == LayoutPoint());

    // FIXME(pdr): Check for the presence of a transform instead of the value.
    // Checking for an identity matrix will cause the property tree structure to
    // change during animations if the animation passes through the identity
    // matrix.
    // FIXME(pdr): Refactor this so all non-root SVG objects use the same
    // transform function.
    const AffineTransform& transform = object.isSVGForeignObject()
                                           ? object.localSVGTransform()
                                           : object.localToSVGParentTransform();
    if (!transform.isIdentity()) {
      // The origin is included in the local transform, so leave origin empty.
      context.current.transform =
          object.getMutableForPainting()
              .ensurePaintProperties()
              .updateTransform(context.current.transform,
                               TransformationMatrix(transform), FloatPoint3D());
      context.current.renderingContextID = 0;
      context.current.shouldFlattenInheritedTransform = false;
      return;
    }
  } else {
    const ComputedStyle& style = object.styleRef();
    if (object.isBox() && (style.hasTransform() || style.preserves3D())) {
      TransformationMatrix matrix;
      style.applyTransform(
          matrix, toLayoutBox(object).size(),
          ComputedStyle::ExcludeTransformOrigin,
          ComputedStyle::IncludeMotionPath,
          ComputedStyle::IncludeIndependentTransformProperties);
      FloatPoint3D origin = transformOrigin(toLayoutBox(object));

      unsigned renderingContextID = context.current.renderingContextID;
      unsigned renderingContextIDForChildren = 0;
      bool flattensInheritedTransform =
          context.current.shouldFlattenInheritedTransform;
      bool childrenFlattenInheritedTransform = true;

      // TODO(trchen): transform-style should only be respected if a PaintLayer
      // is created.
      if (style.preserves3D()) {
        // If a node with transform-style: preserve-3d does not exist in an
        // existing rendering context, it establishes a new one.
        if (!renderingContextID)
          renderingContextID = PtrHash<const LayoutObject>::hash(&object);
        renderingContextIDForChildren = renderingContextID;
        childrenFlattenInheritedTransform = false;
      }

      context.current.transform =
          object.getMutableForPainting()
              .ensurePaintProperties()
              .updateTransform(context.current.transform, matrix, origin,
                               flattensInheritedTransform, renderingContextID);
      context.current.renderingContextID = renderingContextIDForChildren;
      context.current.shouldFlattenInheritedTransform =
          childrenFlattenInheritedTransform;
      return;
    }
  }

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearTransform();
}
Exemplo n.º 28
0
void PaintPropertyTreeBuilder::updateEffect(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  const ComputedStyle& style = object.styleRef();

  if (!style.isStackingContext()) {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearEffect();
    return;
  }

  // TODO(trchen): Can't omit effect node if we have 3D children.
  // TODO(trchen): Can't omit effect node if we have blending children.
  bool effectNodeNeeded = false;

  float opacity = style.opacity();
  if (opacity != 1.0f)
    effectNodeNeeded = true;

  CompositorFilterOperations filter;
  if (object.isSVG() && !object.isSVGRoot()) {
    // TODO(trchen): SVG caches filters in SVGResources. Implement it.
  } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) {
    // TODO(trchen): Eliminate PaintLayer dependency.
    filter = layer->createCompositorFilterOperationsForFilter(style);
  }

  const ClipPaintPropertyNode* outputClip = ClipPaintPropertyNode::root();
  // The CSS filter spec didn't specify how filters interact with overflow
  // clips. The implementation here mimics the old Blink/WebKit behavior for
  // backward compatibility.
  // Basically the output of the filter will be affected by clips that applies
  // to the current element. The descendants that paints into the input of the
  // filter ignores any clips collected so far. For example:
  // <div style="overflow:scroll">
  //   <div style="filter:blur(1px);">
  //     <div>A</div>
  //     <div style="position:absolute;">B</div>
  //   </div>
  // </div>
  // In this example "A" should be clipped if the filter was not present.
  // With the filter, "A" will be rastered without clipping, but instead
  // the blurred result will be clipped.
  // On the other hand, "B" should not be clipped because the overflow clip is
  // not in its containing block chain, but as the filter output will be
  // clipped, so a blurred "B" may still be invisible.
  if (!filter.isEmpty()) {
    effectNodeNeeded = true;
    outputClip = context.current.clip;

    // TODO(trchen): A filter may contain spatial operations such that an
    // output pixel may depend on an input pixel outside of the output clip.
    // We should generate a special clip node to represent this expansion.
  }

  if (effectNodeNeeded) {
    object.getMutableForPainting().ensurePaintProperties().updateEffect(
        context.currentEffect, context.current.transform, outputClip,
        std::move(filter), opacity);
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearEffect();
  }

  const auto* properties = object.paintProperties();
  if (properties && properties->effect()) {
    context.currentEffect = properties->effect();
    // TODO(pdr): Once the expansion clip node is created above, it should be
    // used here to update all current clip nodes;
    const ClipPaintPropertyNode* expansionHint = context.current.clip;
    context.current.clip = context.absolutePosition.clip =
        context.fixedPosition.clip = expansionHint;
  }
}