| Index: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
|
| diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
|
| index 2e972ab9b6ea04132c69352a6c00af5979f89f04..99a5ed9e309dd1977f3d5371cc93dafcb64fb835 100644
|
| --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
|
| +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
|
| @@ -224,56 +224,75 @@ void PaintPropertyTreeBuilder::UpdateProperties(
|
| frame_view.SetTotalPropertyTreeStateForContents(std::move(contents_state));
|
| }
|
|
|
| -void PaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
|
| - const LayoutBoxModelObject& object,
|
| - PaintPropertyTreeBuilderContext& context) {
|
| - bool uses_paint_offset_translation = false;
|
| +static bool NeedsPaintOffsetTranslation(const LayoutObject& object) {
|
| + if (!object.IsBoxModelObject())
|
| + return false;
|
| + const LayoutBoxModelObject& box_model = ToLayoutBoxModelObject(object);
|
| if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
|
| - object.IsLayoutView()) {
|
| + box_model.IsLayoutView()) {
|
| // Root layer scrolling always creates a translation node for LayoutView to
|
| // ensure fixed and absolute contexts use the correct transform space.
|
| - uses_paint_offset_translation = true;
|
| - } else if (object.HasLayer() &&
|
| - context.current.paint_offset != LayoutPoint() &&
|
| - object.Layer()->PaintsWithTransform(
|
| + return true;
|
| + } else if (box_model.HasLayer() &&
|
| + box_model.Layer()->PaintsWithTransform(
|
| kGlobalPaintFlattenCompositingLayers)) {
|
| - uses_paint_offset_translation = true;
|
| + return true;
|
| }
|
| + return false;
|
| +}
|
|
|
| - if (!uses_paint_offset_translation) {
|
| +void PaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
|
| + const LayoutBoxModelObject& object,
|
| + PaintPropertyTreeBuilderContext& context) {
|
| + if (NeedsPaintOffsetTranslation(object) &&
|
| + // As an optimization, skip these paint offset translation nodes when
|
| + // the offset is an identity. An exception is the layout view because root
|
| + // layer scrolling needs a transform node to ensure fixed and absolute
|
| + // descendants use the correct transform space.
|
| + (object.IsLayoutView() ||
|
| + context.current.paint_offset != LayoutPoint())) {
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| + // 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 rounded_paint_offset =
|
| + RoundedIntPoint(context.current.paint_offset);
|
| + LayoutPoint fractional_paint_offset =
|
| + LayoutPoint(context.current.paint_offset - rounded_paint_offset);
|
| +
|
| + context.force_subtree_update |= properties.UpdatePaintOffsetTranslation(
|
| + context.current.transform,
|
| + TransformationMatrix().Translate(rounded_paint_offset.X(),
|
| + rounded_paint_offset.Y()),
|
| + FloatPoint3D(), context.current.should_flatten_inherited_transform,
|
| + context.current.rendering_context_id);
|
| +
|
| + context.current.transform = properties.PaintOffsetTranslation();
|
| + context.current.paint_offset = fractional_paint_offset;
|
| + if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
|
| + object.IsLayoutView()) {
|
| + context.absolute_position.transform = properties.PaintOffsetTranslation();
|
| + context.fixed_position.transform = properties.PaintOffsetTranslation();
|
| + context.absolute_position.paint_offset = LayoutPoint();
|
| + context.fixed_position.paint_offset = LayoutPoint();
|
| + }
|
| + } else {
|
| if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| context.force_subtree_update |= properties->ClearPaintOffsetTranslation();
|
| - 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 rounded_paint_offset = RoundedIntPoint(context.current.paint_offset);
|
| - LayoutPoint fractional_paint_offset =
|
| - LayoutPoint(context.current.paint_offset - rounded_paint_offset);
|
| -
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| - context.force_subtree_update |= properties.UpdatePaintOffsetTranslation(
|
| - context.current.transform,
|
| - TransformationMatrix().Translate(rounded_paint_offset.X(),
|
| - rounded_paint_offset.Y()),
|
| - FloatPoint3D(), context.current.should_flatten_inherited_transform,
|
| - context.current.rendering_context_id);
|
| -
|
| - context.current.transform = properties.PaintOffsetTranslation();
|
| - context.current.paint_offset = fractional_paint_offset;
|
| - if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
|
| - object.IsLayoutView()) {
|
| - context.absolute_position.transform = properties.PaintOffsetTranslation();
|
| - context.fixed_position.transform = properties.PaintOffsetTranslation();
|
| - context.absolute_position.paint_offset = LayoutPoint();
|
| - context.fixed_position.paint_offset = LayoutPoint();
|
| - }
|
| +static bool NeedsTransformForNonRootSVG(const LayoutObject& object) {
|
| + // 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.
|
| + return object.IsSVGChild() &&
|
| + !object.LocalToSVGParentTransform().IsIdentity();
|
| }
|
|
|
| // SVG does not use the general transform update of |updateTransform|, instead
|
| @@ -289,13 +308,9 @@ void PaintPropertyTreeBuilder::UpdateTransformForNonRootSVG(
|
|
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| AffineTransform transform = 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()) {
|
| + if (NeedsTransformForNonRootSVG(object)) {
|
| // The origin is included in the local transform, so leave origin empty.
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| context.force_subtree_update |= properties.UpdateTransform(
|
| context.current.transform, TransformationMatrix(transform),
|
| FloatPoint3D());
|
| @@ -347,6 +362,14 @@ static FloatPoint3D TransformOrigin(const LayoutBox& box) {
|
| style.TransformOriginZ());
|
| }
|
|
|
| +static bool NeedsTransform(const LayoutObject& object) {
|
| + if (!object.IsBox())
|
| + return false;
|
| + return object.StyleRef().HasTransform() || object.StyleRef().Preserves3D() ||
|
| + CompositingReasonsForTransform(ToLayoutBox(object)) !=
|
| + kCompositingReasonNone;
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateTransform(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| @@ -362,44 +385,37 @@ void PaintPropertyTreeBuilder::UpdateTransform(
|
| // direct compositing reason. The latter is required because this is the
|
| // only way to represent compositing both an element and its stacking
|
| // descendants.
|
| - bool has_transform = false;
|
| - if (object.IsBox()) {
|
| + if (NeedsTransform(object)) {
|
| auto& box = ToLayoutBox(object);
|
|
|
| CompositingReasons compositing_reasons =
|
| CompositingReasonsForTransform(box);
|
|
|
| - if (style.HasTransform() || style.Preserves3D() ||
|
| - compositing_reasons != kCompositingReasonNone) {
|
| - TransformationMatrix matrix;
|
| - style.ApplyTransform(
|
| - matrix, box.Size(), ComputedStyle::kExcludeTransformOrigin,
|
| - ComputedStyle::kIncludeMotionPath,
|
| - ComputedStyle::kIncludeIndependentTransformProperties);
|
| -
|
| - // 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 rendering_context_id = context.current.rendering_context_id;
|
| - if (style.Preserves3D() && !rendering_context_id)
|
| - rendering_context_id = PtrHash<const LayoutObject>::GetHash(&object);
|
| -
|
| - CompositorElementId compositor_element_id =
|
| - style.HasCurrentTransformAnimation()
|
| - ? CreateDomNodeBasedCompositorElementId(object)
|
| - : CompositorElementId();
|
| -
|
| - auto& properties =
|
| - object.GetMutableForPainting().EnsurePaintProperties();
|
| - context.force_subtree_update |= properties.UpdateTransform(
|
| - context.current.transform, matrix, TransformOrigin(box),
|
| - context.current.should_flatten_inherited_transform,
|
| - rendering_context_id, compositing_reasons, compositor_element_id);
|
| - has_transform = true;
|
| - }
|
| - }
|
| - if (!has_transform) {
|
| + TransformationMatrix matrix;
|
| + style.ApplyTransform(
|
| + matrix, box.Size(), ComputedStyle::kExcludeTransformOrigin,
|
| + ComputedStyle::kIncludeMotionPath,
|
| + ComputedStyle::kIncludeIndependentTransformProperties);
|
| +
|
| + // 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 rendering_context_id = context.current.rendering_context_id;
|
| + if (style.Preserves3D() && !rendering_context_id)
|
| + rendering_context_id = PtrHash<const LayoutObject>::GetHash(&object);
|
| +
|
| + CompositorElementId compositor_element_id =
|
| + style.HasCurrentTransformAnimation()
|
| + ? CreateDomNodeBasedCompositorElementId(object)
|
| + : CompositorElementId();
|
| +
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| + context.force_subtree_update |= properties.UpdateTransform(
|
| + context.current.transform, matrix, TransformOrigin(box),
|
| + context.current.should_flatten_inherited_transform,
|
| + rendering_context_id, compositing_reasons, compositor_element_id);
|
| + } else {
|
| if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| context.force_subtree_update |= properties->ClearTransform();
|
| }
|
| @@ -461,102 +477,121 @@ static bool ComputeMaskParameters(IntRect& mask_clip,
|
| return true;
|
| }
|
|
|
| -void PaintPropertyTreeBuilder::UpdateEffect(
|
| - const LayoutObject& object,
|
| - PaintPropertyTreeBuilderContext& context) {
|
| +static bool NeedsEffect(const LayoutObject& object) {
|
| const ComputedStyle& style = object.StyleRef();
|
|
|
| const bool is_css_isolated_group =
|
| object.IsBoxModelObject() && style.IsStackingContext();
|
| - if (!is_css_isolated_group && !object.IsSVGChild()) {
|
| - if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - if (auto* properties = object.GetMutableForPainting().PaintProperties()) {
|
| - context.force_subtree_update |= properties->ClearEffect();
|
| - context.force_subtree_update |= properties->ClearMask();
|
| - context.force_subtree_update |= properties->ClearMaskClip();
|
| - }
|
| +
|
| + if (!is_css_isolated_group && !object.IsSVGChild())
|
| + return false;
|
| +
|
| + if (object.IsSVG()) {
|
| + // This handles SVGRoot objects which have PaintLayers.
|
| + if (object.IsSVGRoot() && object.HasNonIsolatedBlendingDescendants())
|
| + return true;
|
| + if (SVGLayoutSupport::IsIsolationRequired(&object))
|
| + return true;
|
| + } else if (object.IsBoxModelObject()) {
|
| + if (PaintLayer* layer = ToLayoutBoxModelObject(object).Layer()) {
|
| + if (layer->HasNonIsolatedDescendantWithBlendMode())
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + SkBlendMode blend_mode = object.IsBlendingAllowed()
|
| + ? WebCoreCompositeToSkiaComposite(
|
| + kCompositeSourceOver, style.BlendMode())
|
| + : SkBlendMode::kSrcOver;
|
| + if (blend_mode != SkBlendMode::kSrcOver)
|
| + return true;
|
| +
|
| + float opacity = style.Opacity();
|
| + if (opacity != 1.0f)
|
| + return true;
|
| +
|
| + if (CompositingReasonFinder::RequiresCompositingForOpacityAnimation(style))
|
| + return true;
|
| +
|
| + if (object.IsSVGChild()) {
|
| + if (SVGResources* resources =
|
| + SVGResourcesCache::CachedResourcesForLayoutObject(&object)) {
|
| + if (resources->Masker())
|
| + return true;
|
| }
|
| - return;
|
| }
|
|
|
| + if (object.StyleRef().HasMask())
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void PaintPropertyTreeBuilder::UpdateEffect(
|
| + const LayoutObject& object,
|
| + PaintPropertyTreeBuilderContext& context) {
|
| + const ComputedStyle& style = object.StyleRef();
|
| +
|
| // TODO(trchen): Can't omit effect node if we have 3D children.
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| const ClipPaintPropertyNode* output_clip =
|
| context.input_clip_of_current_effect;
|
|
|
| - bool effect_node_needed = false;
|
| + if (NeedsEffect(object)) {
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
|
|
| - // Can't omit effect node if we have paint children with exotic blending.
|
| - if (object.IsSVG()) {
|
| - // This handles SVGRoot objects which have PaintLayers.
|
| - if (object.IsSVGRoot() && object.HasNonIsolatedBlendingDescendants())
|
| - effect_node_needed = true;
|
| - else if (SVGLayoutSupport::IsIsolationRequired(&object))
|
| - effect_node_needed = true;
|
| - } else if (PaintLayer* layer = ToLayoutBoxModelObject(object).Layer()) {
|
| - if (layer->HasNonIsolatedDescendantWithBlendMode())
|
| - effect_node_needed = true;
|
| - }
|
| + // We may begin to composite our subtree prior to an animation starts,
|
| + // but a compositor element ID is only needed when an animation is
|
| + // current.
|
| + CompositingReasons compositing_reasons = kCompositingReasonNone;
|
| + if (CompositingReasonFinder::RequiresCompositingForOpacityAnimation(
|
| + style)) {
|
| + compositing_reasons = kCompositingReasonActiveAnimation;
|
| + }
|
|
|
| - SkBlendMode blend_mode = object.IsBlendingAllowed()
|
| - ? WebCoreCompositeToSkiaComposite(
|
| - kCompositeSourceOver, style.BlendMode())
|
| - : SkBlendMode::kSrcOver;
|
| - if (blend_mode != SkBlendMode::kSrcOver)
|
| - effect_node_needed = true;
|
| -
|
| - float opacity = style.Opacity();
|
| - if (opacity != 1.0f)
|
| - effect_node_needed = true;
|
| -
|
| - // We may begin to composite our subtree prior to an animation starts,
|
| - // but a compositor element ID is only needed when an animation is current.
|
| - CompositorElementId compositor_element_id =
|
| - style.HasCurrentOpacityAnimation()
|
| - ? CreateDomNodeBasedCompositorElementId(object)
|
| - : CompositorElementId();
|
| - CompositingReasons compositing_reasons = kCompositingReasonNone;
|
| - if (CompositingReasonFinder::RequiresCompositingForOpacityAnimation(
|
| - style)) {
|
| - compositing_reasons = kCompositingReasonActiveAnimation;
|
| - effect_node_needed = true;
|
| - }
|
| - DCHECK(!style.HasCurrentOpacityAnimation() ||
|
| - compositing_reasons != kCompositingReasonNone);
|
| -
|
| - IntRect mask_clip;
|
| - ColorFilter mask_color_filter;
|
| - bool has_mask = ComputeMaskParameters(mask_clip, mask_color_filter, object,
|
| - context.current.paint_offset);
|
| - if (has_mask) {
|
| - effect_node_needed = true;
|
| -
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| - context.force_subtree_update |= properties.UpdateMaskClip(
|
| - context.current.clip, context.current.transform,
|
| - FloatRoundedRect(mask_clip));
|
| - output_clip = properties.MaskClip();
|
| + IntRect mask_clip;
|
| + ColorFilter mask_color_filter;
|
| + bool has_mask = ComputeMaskParameters(
|
| + mask_clip, mask_color_filter, object, context.current.paint_offset);
|
| + if (has_mask) {
|
| + context.force_subtree_update |= properties.UpdateMaskClip(
|
| + context.current.clip, context.current.transform,
|
| + FloatRoundedRect(mask_clip));
|
| + output_clip = properties.MaskClip();
|
|
|
| - // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
|
| - // grouping (i.e. descendant-dependent compositing reason) properly yet.
|
| - // This forces masked subtree always create a layer for now.
|
| - compositing_reasons |= kCompositingReasonIsolateCompositedDescendants;
|
| - } else {
|
| - if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| - context.force_subtree_update |= properties->ClearMaskClip();
|
| - }
|
| + // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
|
| + // grouping (i.e. descendant-dependent compositing reason) properly
|
| + // yet. This forces masked subtree always create a layer for now.
|
| + compositing_reasons |= kCompositingReasonIsolateCompositedDescendants;
|
| + } else {
|
| + if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| + context.force_subtree_update |= properties->ClearMaskClip();
|
| + }
|
| +
|
| + SkBlendMode blend_mode =
|
| + object.IsBlendingAllowed()
|
| + ? WebCoreCompositeToSkiaComposite(kCompositeSourceOver,
|
| + style.BlendMode())
|
| + : SkBlendMode::kSrcOver;
|
| +
|
| + CompositorElementId compositor_element_id =
|
| + style.HasCurrentOpacityAnimation()
|
| + ? CreateDomNodeBasedCompositorElementId(object)
|
| + : CompositorElementId();
|
| +
|
| + DCHECK(!style.HasCurrentOpacityAnimation() ||
|
| + compositing_reasons != kCompositingReasonNone);
|
|
|
| - if (effect_node_needed) {
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| context.force_subtree_update |= properties.UpdateEffect(
|
| context.current_effect, context.current.transform, output_clip,
|
| - kColorFilterNone, CompositorFilterOperations(), opacity, blend_mode,
|
| - compositing_reasons, compositor_element_id);
|
| + kColorFilterNone, CompositorFilterOperations(), style.Opacity(),
|
| + blend_mode, compositing_reasons, compositor_element_id);
|
| if (has_mask) {
|
| // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
|
| - // grouping (i.e. descendant-dependent compositing reason) properly yet.
|
| - // Adding CompositingReasonSquashingDisallowed forces mask not getting
|
| - // squashed into a child effect. Have no compositing reason otherwise.
|
| + // grouping (i.e. descendant-dependent compositing reason) properly
|
| + // yet. Adding CompositingReasonSquashingDisallowed forces mask not
|
| + // getting squashed into a child effect. Have no compositing reason
|
| + // otherwise.
|
| context.force_subtree_update |= properties.UpdateMask(
|
| properties.Effect(), context.current.transform, output_clip,
|
| mask_color_filter, CompositorFilterOperations(), 1.f,
|
| @@ -569,6 +604,7 @@ void PaintPropertyTreeBuilder::UpdateEffect(
|
| if (auto* properties = object.GetMutableForPainting().PaintProperties()) {
|
| context.force_subtree_update |= properties->ClearEffect();
|
| context.force_subtree_update |= properties->ClearMask();
|
| + context.force_subtree_update |= properties->ClearMaskClip();
|
| }
|
| }
|
| }
|
| @@ -584,66 +620,70 @@ void PaintPropertyTreeBuilder::UpdateEffect(
|
| }
|
| }
|
|
|
| +static bool NeedsFilter(const LayoutObject& object) {
|
| + // TODO(trchen): SVG caches filters in SVGResources. Implement it.
|
| + return object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
|
| + (object.StyleRef().HasFilter() || object.HasReflection());
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateFilter(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| const ComputedStyle& style = object.StyleRef();
|
|
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - CompositorFilterOperations filter;
|
| - if (object.IsSVGChild()) {
|
| - // 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);
|
| - }
|
| + if (NeedsFilter(object)) {
|
| + CompositorFilterOperations filter =
|
| + ToLayoutBoxModelObject(object)
|
| + .Layer()
|
| + ->CreateCompositorFilterOperationsForFilter(style);
|
| +
|
| + // 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.
|
| + const ClipPaintPropertyNode* output_clip = 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.
|
| +
|
| + // We may begin to composite our subtree prior to an animation starts,
|
| + // but a compositor element ID is only needed when an animation is
|
| + // current.
|
| + CompositorElementId compositor_element_id =
|
| + style.HasCurrentFilterAnimation()
|
| + ? CreateDomNodeBasedCompositorElementId(object)
|
| + : CompositorElementId();
|
| + CompositingReasons compositing_reasons =
|
| + CompositingReasonFinder::RequiresCompositingForFilterAnimation(style)
|
| + ? kCompositingReasonActiveAnimation
|
| + : kCompositingReasonNone;
|
| + DCHECK(!style.HasCurrentFilterAnimation() ||
|
| + compositing_reasons != kCompositingReasonNone);
|
|
|
| - // 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.
|
| - const ClipPaintPropertyNode* output_clip = 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.
|
| -
|
| - // We may begin to composite our subtree prior to an animation starts,
|
| - // but a compositor element ID is only needed when an animation is current.
|
| - CompositorElementId compositor_element_id =
|
| - style.HasCurrentFilterAnimation()
|
| - ? CreateDomNodeBasedCompositorElementId(object)
|
| - : CompositorElementId();
|
| - CompositingReasons compositing_reasons =
|
| - CompositingReasonFinder::RequiresCompositingForFilterAnimation(style)
|
| - ? kCompositingReasonActiveAnimation
|
| - : kCompositingReasonNone;
|
| - DCHECK(!style.HasCurrentFilterAnimation() ||
|
| - compositing_reasons != kCompositingReasonNone);
|
| -
|
| - if (compositing_reasons == kCompositingReasonNone && filter.IsEmpty()) {
|
| - if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| - context.force_subtree_update |= properties->ClearFilter();
|
| - } else {
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| context.force_subtree_update |= properties.UpdateFilter(
|
| context.current_effect, context.current.transform, output_clip,
|
| kColorFilterNone, std::move(filter), 1.f, SkBlendMode::kSrcOver,
|
| compositing_reasons, compositor_element_id);
|
| + } else {
|
| + if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| + context.force_subtree_update |= properties->ClearFilter();
|
| }
|
| }
|
|
|
| @@ -659,11 +699,15 @@ void PaintPropertyTreeBuilder::UpdateFilter(
|
| }
|
| }
|
|
|
| +static bool NeedsCssClip(const LayoutObject& object) {
|
| + return object.HasClip();
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateCssClip(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - if (object.HasClip()) {
|
| + if (NeedsCssClip(object)) {
|
| // 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
|
| @@ -671,7 +715,7 @@ void PaintPropertyTreeBuilder::UpdateCssClip(
|
| DCHECK(object.CanContainAbsolutePositionObjects());
|
| LayoutRect clip_rect =
|
| ToLayoutBox(object).ClipRect(context.current.paint_offset);
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| context.force_subtree_update |= properties.UpdateCssClip(
|
| context.current.clip, context.current.transform,
|
| FloatRoundedRect(FloatRect(clip_rect)));
|
| @@ -704,6 +748,16 @@ void PaintPropertyTreeBuilder::UpdateLocalBorderBoxContext(
|
| }
|
| }
|
|
|
| +static bool NeedsScrollbarPaintOffset(const LayoutObject& object) {
|
| + if (object.IsBoxModelObject()) {
|
| + if (auto* area = ToLayoutBoxModelObject(object).GetScrollableArea()) {
|
| + if (area->HorizontalScrollbar() || area->VerticalScrollbar())
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| // TODO(trchen): Remove this once we bake the paint offset into frameRect.
|
| void PaintPropertyTreeBuilder::UpdateScrollbarPaintOffset(
|
| const LayoutObject& object,
|
| @@ -711,44 +765,49 @@ void PaintPropertyTreeBuilder::UpdateScrollbarPaintOffset(
|
| if (!object.NeedsPaintPropertyUpdate() && !context.force_subtree_update)
|
| return;
|
|
|
| - bool needs_scrollbar_paint_offset = false;
|
| - IntPoint rounded_paint_offset = RoundedIntPoint(context.current.paint_offset);
|
| - if (rounded_paint_offset != IntPoint() && object.IsBoxModelObject()) {
|
| - if (auto* area = ToLayoutBoxModelObject(object).GetScrollableArea()) {
|
| - if (area->HorizontalScrollbar() || area->VerticalScrollbar()) {
|
| - auto paint_offset = TransformationMatrix().Translate(
|
| - rounded_paint_offset.X(), rounded_paint_offset.Y());
|
| - auto& properties =
|
| - object.GetMutableForPainting().EnsurePaintProperties();
|
| - context.force_subtree_update |= properties.UpdateScrollbarPaintOffset(
|
| - context.current.transform, paint_offset, FloatPoint3D());
|
| - needs_scrollbar_paint_offset = true;
|
| - }
|
| - }
|
| + if (NeedsScrollbarPaintOffset(object)) {
|
| + IntPoint rounded_paint_offset =
|
| + RoundedIntPoint(context.current.paint_offset);
|
| + auto paint_offset = TransformationMatrix().Translate(
|
| + rounded_paint_offset.X(), rounded_paint_offset.Y());
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| + context.force_subtree_update |= properties.UpdateScrollbarPaintOffset(
|
| + context.current.transform, paint_offset, FloatPoint3D());
|
| + } else {
|
| + if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| + context.force_subtree_update |= properties->ClearScrollbarPaintOffset();
|
| }
|
| +}
|
|
|
| - auto* properties = object.GetMutableForPainting().PaintProperties();
|
| - if (!needs_scrollbar_paint_offset && properties)
|
| - context.force_subtree_update |= properties->ClearScrollbarPaintOffset();
|
| +static bool NeedsOverflowScroll(const LayoutObject& object) {
|
| + return object.IsBox() && ToLayoutBox(object).ShouldClipOverflow();
|
| }
|
|
|
| void PaintPropertyTreeBuilder::UpdateOverflowClip(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| - if (!object.IsBox())
|
| - return;
|
| -
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - 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 clip_rect;
|
| - if (box.ShouldClipOverflow()) {
|
| + if (NeedsOverflowScroll(object)) {
|
| + const LayoutBox& box = ToLayoutBox(object);
|
| + LayoutRect clip_rect;
|
| clip_rect =
|
| LayoutRect(box.OverflowClipRect(context.current.paint_offset));
|
| +
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| + const auto* current_clip = context.current.clip;
|
| + if (box.StyleRef().HasBorderRadius()) {
|
| + auto inner_border = box.StyleRef().GetRoundedInnerBorderFor(
|
| + LayoutRect(context.current.paint_offset, box.Size()));
|
| + context.force_subtree_update |= properties.UpdateInnerBorderRadiusClip(
|
| + context.current.clip, context.current.transform, inner_border);
|
| + current_clip = properties.InnerBorderRadiusClip();
|
| + } else {
|
| + context.force_subtree_update |= properties.ClearInnerBorderRadiusClip();
|
| + }
|
| +
|
| + context.force_subtree_update |=
|
| + properties.UpdateOverflowClip(current_clip, context.current.transform,
|
| + FloatRoundedRect(FloatRect(clip_rect)));
|
| } else {
|
| if (auto* properties = object.GetMutableForPainting().PaintProperties()) {
|
| context.force_subtree_update |=
|
| @@ -757,22 +816,6 @@ void PaintPropertyTreeBuilder::UpdateOverflowClip(
|
| }
|
| return;
|
| }
|
| -
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| - const auto* current_clip = context.current.clip;
|
| - if (box.StyleRef().HasBorderRadius()) {
|
| - auto inner_border = box.StyleRef().GetRoundedInnerBorderFor(
|
| - LayoutRect(context.current.paint_offset, box.Size()));
|
| - context.force_subtree_update |= properties.UpdateInnerBorderRadiusClip(
|
| - context.current.clip, context.current.transform, inner_border);
|
| - current_clip = properties.InnerBorderRadiusClip();
|
| - } else {
|
| - context.force_subtree_update |= properties.ClearInnerBorderRadiusClip();
|
| - }
|
| -
|
| - context.force_subtree_update |=
|
| - properties.UpdateOverflowClip(current_clip, context.current.transform,
|
| - FloatRoundedRect(FloatRect(clip_rect)));
|
| }
|
|
|
| const auto* properties = object.PaintProperties();
|
| @@ -791,12 +834,16 @@ static FloatPoint PerspectiveOrigin(const LayoutBox& box) {
|
| border_box_size.Height()));
|
| }
|
|
|
| +static bool NeedsPerspective(const LayoutObject& object) {
|
| + return object.IsBox() && object.StyleRef().HasPerspective();
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdatePerspective(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - const ComputedStyle& style = object.StyleRef();
|
| - if (object.IsBox() && style.HasPerspective()) {
|
| + if (NeedsPerspective(object)) {
|
| + const ComputedStyle& style = object.StyleRef();
|
| // The perspective node must not flatten (else nothing will get
|
| // perspective), but it should still extend the rendering context as
|
| // most transform nodes do.
|
| @@ -804,7 +851,7 @@ void PaintPropertyTreeBuilder::UpdatePerspective(
|
| TransformationMatrix().ApplyPerspective(style.Perspective());
|
| FloatPoint3D origin = PerspectiveOrigin(ToLayoutBox(object)) +
|
| ToLayoutSize(context.current.paint_offset);
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| context.force_subtree_update |= properties.UpdatePerspective(
|
| context.current.transform, matrix, origin,
|
| context.current.should_flatten_inherited_transform,
|
| @@ -822,6 +869,10 @@ void PaintPropertyTreeBuilder::UpdatePerspective(
|
| }
|
| }
|
|
|
| +static bool NeedsSVGLocalToBorderBoxTransform(const LayoutObject& object) {
|
| + return object.IsSVGRoot();
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateSvgLocalToBorderBoxTransform(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| @@ -832,8 +883,9 @@ void PaintPropertyTreeBuilder::UpdateSvgLocalToBorderBoxTransform(
|
| AffineTransform transform_to_border_box =
|
| SVGRootPainter(ToLayoutSVGRoot(object))
|
| .TransformToPixelSnappedBorderBox(context.current.paint_offset);
|
| - if (!transform_to_border_box.IsIdentity()) {
|
| - auto& properties = object.GetMutableForPainting().EnsurePaintProperties();
|
| + if (!transform_to_border_box.IsIdentity() &&
|
| + NeedsSVGLocalToBorderBoxTransform(object)) {
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| context.force_subtree_update |=
|
| properties.UpdateSvgLocalToBorderBoxTransform(
|
| context.current.transform, transform_to_border_box,
|
| @@ -870,56 +922,58 @@ static MainThreadScrollingReasons GetMainThreadScrollingReasons(
|
| ancestor_reasons);
|
| }
|
|
|
| +static bool NeedsScrollTranslation(const LayoutObject& object) {
|
| + if (object.HasOverflowClip()) {
|
| + const LayoutBox& box = ToLayoutBox(object);
|
| + auto* scrollable_area = box.GetScrollableArea();
|
| + IntSize scroll_offset = box.ScrolledContentOffset();
|
| + if (!scroll_offset.IsZero() || scrollable_area->ScrollsOverflow())
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - bool needs_scroll_properties = false;
|
| - if (object.HasOverflowClip()) {
|
| + if (NeedsScrollTranslation(object)) {
|
| + const LayoutBox& box = ToLayoutBox(object);
|
| + auto* scrollable_area = box.GetScrollableArea();
|
| + IntSize scroll_offset = box.ScrolledContentOffset();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| +
|
| + IntSize scroll_clip = scrollable_area->VisibleContentRect().Size();
|
| + IntSize scroll_bounds = scrollable_area->ContentsSize();
|
| + bool user_scrollable_horizontal =
|
| + scrollable_area->UserInputScrollable(kHorizontalScrollbar);
|
| + bool user_scrollable_vertical =
|
| + scrollable_area->UserInputScrollable(kVerticalScrollbar);
|
| +
|
| auto ancestor_reasons =
|
| context.current.scroll->GetMainThreadScrollingReasons();
|
| auto reasons = GetMainThreadScrollingReasons(object, ancestor_reasons);
|
|
|
| - const LayoutBox& box = ToLayoutBox(object);
|
| - auto* scrollable_area = box.GetScrollableArea();
|
| - IntSize scroll_offset = box.ScrolledContentOffset();
|
| - if (!scroll_offset.IsZero() || scrollable_area->ScrollsOverflow()) {
|
| - needs_scroll_properties = true;
|
| - auto& properties =
|
| - object.GetMutableForPainting().EnsurePaintProperties();
|
| -
|
| - IntSize scroll_clip = scrollable_area->VisibleContentRect().Size();
|
| - IntSize scroll_bounds = scrollable_area->ContentsSize();
|
| - bool user_scrollable_horizontal =
|
| - scrollable_area->UserInputScrollable(kHorizontalScrollbar);
|
| - bool user_scrollable_vertical =
|
| - scrollable_area->UserInputScrollable(kVerticalScrollbar);
|
| -
|
| - // Main thread scrolling reasons depend on their ancestor's reasons
|
| - // so ensure the entire subtree is updated when reasons change.
|
| - if (auto* existing_scroll_translation =
|
| - properties.ScrollTranslation()) {
|
| - auto* existing_scroll_node =
|
| - existing_scroll_translation->ScrollNode();
|
| - if (existing_scroll_node->GetMainThreadScrollingReasons() != reasons)
|
| - context.force_subtree_update = true;
|
| - }
|
| -
|
| - CompositorElementId compositor_element_id =
|
| - CreateDomNodeBasedCompositorElementId(object);
|
| - TransformationMatrix matrix = TransformationMatrix().Translate(
|
| - -scroll_offset.Width(), -scroll_offset.Height());
|
| - context.force_subtree_update |= properties.UpdateScrollTranslation(
|
| - context.current.transform, matrix, FloatPoint3D(),
|
| - context.current.should_flatten_inherited_transform,
|
| - context.current.rendering_context_id, kCompositingReasonNone,
|
| - compositor_element_id, context.current.scroll, scroll_clip,
|
| - scroll_bounds, user_scrollable_horizontal, user_scrollable_vertical,
|
| - reasons, scrollable_area);
|
| + // Main thread scrolling reasons depend on their ancestor's reasons
|
| + // so ensure the entire subtree is updated when reasons change.
|
| + if (auto* existing_scroll_translation = properties.ScrollTranslation()) {
|
| + auto* existing_scroll_node = existing_scroll_translation->ScrollNode();
|
| + if (existing_scroll_node->GetMainThreadScrollingReasons() != reasons)
|
| + context.force_subtree_update = true;
|
| }
|
| - }
|
|
|
| - if (!needs_scroll_properties) {
|
| + CompositorElementId compositor_element_id =
|
| + CreateDomNodeBasedCompositorElementId(object);
|
| + TransformationMatrix matrix = TransformationMatrix().Translate(
|
| + -scroll_offset.Width(), -scroll_offset.Height());
|
| + context.force_subtree_update |= properties.UpdateScrollTranslation(
|
| + context.current.transform, matrix, FloatPoint3D(),
|
| + context.current.should_flatten_inherited_transform,
|
| + context.current.rendering_context_id, kCompositingReasonNone,
|
| + compositor_element_id, context.current.scroll, scroll_clip,
|
| + scroll_bounds, user_scrollable_horizontal, user_scrollable_vertical,
|
| + reasons, scrollable_area);
|
| + } else {
|
| // Ensure pre-existing properties are cleared.
|
| if (auto* properties = object.GetMutableForPainting().PaintProperties())
|
| context.force_subtree_update |= properties->ClearScrollTranslation();
|
| @@ -934,6 +988,11 @@ void PaintPropertyTreeBuilder::UpdateScrollAndScrollTranslation(
|
| }
|
| }
|
|
|
| +static bool NeedsCssClipFixedPosition(const LayoutObject& object) {
|
| + return !object.IsLayoutView() && !object.CanContainFixedPositionObjects() &&
|
| + NeedsCssClip(object);
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdateOutOfFlowContext(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| @@ -966,8 +1025,8 @@ void PaintPropertyTreeBuilder::UpdateOutOfFlowContext(
|
| // 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* css_clip =
|
| - object.GetMutableForPainting().PaintProperties()->CssClip();
|
| + auto& properties = *object.GetMutableForPainting().PaintProperties();
|
| + auto* css_clip = properties.CssClip();
|
|
|
| // Before we actually create anything, check whether in-flow context and
|
| // fixed-position context has exactly the same clip. Reuse if possible.
|
| @@ -975,17 +1034,14 @@ void PaintPropertyTreeBuilder::UpdateOutOfFlowContext(
|
| context.fixed_position.clip = css_clip;
|
| } else {
|
| if (object.NeedsPaintPropertyUpdate() || context.force_subtree_update) {
|
| - auto& properties =
|
| - object.GetMutableForPainting().EnsurePaintProperties();
|
| context.force_subtree_update |= properties.UpdateCssClipFixedPosition(
|
| context.fixed_position.clip,
|
| const_cast<TransformPaintPropertyNode*>(
|
| css_clip->LocalTransformSpace()),
|
| css_clip->ClipRect());
|
| }
|
| - const auto* properties = object.PaintProperties();
|
| - if (properties && properties->CssClipFixedPosition())
|
| - context.fixed_position.clip = properties->CssClipFixedPosition();
|
| + if (properties.CssClipFixedPosition())
|
| + context.fixed_position.clip = properties.CssClipFixedPosition();
|
| return;
|
| }
|
| }
|
| @@ -1108,6 +1164,28 @@ void PaintPropertyTreeBuilder::UpdateForObjectLocationAndSize(
|
| box.GetMutableForPainting().SetNeedsPaintPropertyUpdate();
|
| }
|
|
|
| +void PaintPropertyTreeBuilder::UpdatePaintProperties(
|
| + const LayoutObject& object,
|
| + PaintPropertyTreeBuilderContext& context) {
|
| + bool needs_paint_properties =
|
| + NeedsPaintOffsetTranslation(object) || NeedsTransform(object) ||
|
| + NeedsEffect(object) || NeedsTransformForNonRootSVG(object) ||
|
| + NeedsFilter(object) || NeedsCssClip(object) ||
|
| + NeedsScrollbarPaintOffset(object) || NeedsOverflowScroll(object) ||
|
| + NeedsPerspective(object) || NeedsSVGLocalToBorderBoxTransform(object) ||
|
| + NeedsScrollTranslation(object) || NeedsCssClipFixedPosition(object);
|
| +
|
| + bool had_paint_properties = object.PaintProperties();
|
| +
|
| + if (needs_paint_properties) {
|
| + object.GetMutableForPainting().EnsurePaintProperties();
|
| + } else {
|
| + object.GetMutableForPainting().ClearPaintProperties();
|
| + if (had_paint_properties)
|
| + context.force_subtree_update = true;
|
| + }
|
| +}
|
| +
|
| void PaintPropertyTreeBuilder::UpdatePropertiesForSelf(
|
| const LayoutObject& object,
|
| PaintPropertyTreeBuilderContext& context) {
|
| @@ -1118,6 +1196,8 @@ void PaintPropertyTreeBuilder::UpdatePropertiesForSelf(
|
| context = PaintPropertyTreeBuilderContext();
|
| }
|
|
|
| + UpdatePaintProperties(object, context);
|
| +
|
| // This is not in FindObjectPropertiesNeedingUpdateScope because paint offset
|
| // can change without needsPaintPropertyUpdate.
|
| UpdateForObjectLocationAndSize(object, context);
|
|
|