Chromium Code Reviews| 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..f0168e41bb2891165f2a8fd264540558a31cb6c2 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,97 +477,111 @@ 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; |
| } |
| - return; |
| } |
| + 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() && |
| + SVGResourcesCache::CachedResourcesForLayoutObject(&object)) |
| + return true; |
| + |
| + 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. |
| @@ -569,6 +599,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 +615,70 @@ void PaintPropertyTreeBuilder::UpdateEffect( |
| } |
| } |
| +static bool NeedsFilter(const LayoutObject& object) { |
| + // TODO(trchen): SVG caches filters in SVGResources. Implement it. |
| + return object.IsBoxModelObject() && object.StyleRef().HasFilter() && |
| + ToLayoutBoxModelObject(object).Layer(); |
| +} |
| + |
| 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 +694,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 +710,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 +743,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 +760,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 +811,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 +829,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 +846,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 +864,10 @@ void PaintPropertyTreeBuilder::UpdatePerspective( |
| } |
| } |
| +static bool NeedsSVGLocalToBorderBoxTransform(const LayoutObject& object) { |
| + return object.IsSVGRoot(); |
| +} |
| + |
| void PaintPropertyTreeBuilder::UpdateSvgLocalToBorderBoxTransform( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| @@ -832,8 +878,8 @@ 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 (NeedsSVGLocalToBorderBoxTransform(object)) { |
| + auto& properties = *object.GetMutableForPainting().PaintProperties(); |
| context.force_subtree_update |= |
| properties.UpdateSvgLocalToBorderBoxTransform( |
| context.current.transform, transform_to_border_box, |
| @@ -870,56 +916,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 +982,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 +1019,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 +1028,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 +1158,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 has_paint_properties = object.PaintProperties(); |
| + |
| + if (needs_paint_properties) |
| + object.GetMutableForPainting().EnsurePaintProperties(); |
| + else |
| + object.GetMutableForPainting().ClearPaintProperties(); |
| + |
| + if (needs_paint_properties != has_paint_properties) |
| + context.force_subtree_update = true; |
|
chrishtr
2017/04/21 22:02:36
Updated to force subtree update if we change alloc
|
| +} |
| + |
| void PaintPropertyTreeBuilder::UpdatePropertiesForSelf( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| @@ -1118,6 +1190,8 @@ void PaintPropertyTreeBuilder::UpdatePropertiesForSelf( |
| context = PaintPropertyTreeBuilderContext(); |
| } |
| + UpdatePaintProperties(object, context); |
| + |
| // This is not in FindObjectPropertiesNeedingUpdateScope because paint offset |
| // can change without needsPaintPropertyUpdate. |
| UpdateForObjectLocationAndSize(object, context); |