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 cded36c119223a695f1dd4a29739292a7a3d101d..664c5a607f76a99089c75435d1b3553f46942738 100644 |
| --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp |
| +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp |
| @@ -11,6 +11,7 @@ |
| #include "core/layout/LayoutPart.h" |
| #include "core/layout/LayoutView.h" |
| #include "core/layout/svg/LayoutSVGRoot.h" |
| +#include "core/paint/FindPropertiesNeedingUpdate.h" |
| #include "core/paint/ObjectPaintProperties.h" |
| #include "core/paint/PaintLayer.h" |
| #include "core/paint/SVGRootPainter.h" |
| @@ -72,52 +73,50 @@ PaintPropertyTreeBuilder::setupInitialContext() { |
| return context; |
| } |
| -const TransformPaintPropertyNode* updateFrameViewPreTranslation( |
| +void updateFrameViewPreTranslation( |
| FrameView& frameView, |
| PassRefPtr<const TransformPaintPropertyNode> parent, |
| const TransformationMatrix& matrix, |
| const FloatPoint3D& origin) { |
| DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| - if (TransformPaintPropertyNode* existingPreTranslation = |
| - frameView.preTranslation()) |
| + if (auto* existingPreTranslation = frameView.preTranslation()) { |
| existingPreTranslation->update(std::move(parent), matrix, origin); |
| - else |
| + } else { |
| frameView.setPreTranslation( |
| TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); |
| - return frameView.preTranslation(); |
| + } |
| } |
| -const ClipPaintPropertyNode* updateFrameViewContentClip( |
| +void updateFrameViewContentClip( |
| FrameView& frameView, |
| PassRefPtr<const ClipPaintPropertyNode> parent, |
| PassRefPtr<const TransformPaintPropertyNode> localTransformSpace, |
| const FloatRoundedRect& clipRect) { |
| DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| - if (ClipPaintPropertyNode* existingContentClip = frameView.contentClip()) |
| + if (auto* existingContentClip = frameView.contentClip()) { |
| existingContentClip->update(std::move(parent), |
| std::move(localTransformSpace), clipRect); |
| - else |
| + } else { |
| frameView.setContentClip(ClipPaintPropertyNode::create( |
| std::move(parent), std::move(localTransformSpace), clipRect)); |
| - return frameView.contentClip(); |
| + } |
| } |
| -const TransformPaintPropertyNode* updateFrameViewScrollTranslation( |
| +void updateFrameViewScrollTranslation( |
| FrameView& frameView, |
| PassRefPtr<const TransformPaintPropertyNode> parent, |
| const TransformationMatrix& matrix, |
| const FloatPoint3D& origin) { |
| DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| - if (TransformPaintPropertyNode* existingScrollTranslation = |
| - frameView.scrollTranslation()) |
| + if (auto* existingScrollTranslation = frameView.scrollTranslation()) { |
| existingScrollTranslation->update(std::move(parent), matrix, origin); |
| - else |
| + } else { |
| frameView.setScrollTranslation( |
| TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); |
| - return frameView.scrollTranslation(); |
| + } |
| } |
| -ScrollPaintPropertyNode* updateFrameViewScroll( |
| +void updateFrameViewScroll( |
| FrameView& frameView, |
| PassRefPtr<ScrollPaintPropertyNode> parent, |
| PassRefPtr<const TransformPaintPropertyNode> scrollOffset, |
| @@ -126,18 +125,18 @@ ScrollPaintPropertyNode* updateFrameViewScroll( |
| bool userScrollableHorizontal, |
| bool userScrollableVertical) { |
| DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| - if (ScrollPaintPropertyNode* existingScroll = frameView.scroll()) |
| + if (auto* existingScroll = frameView.scroll()) { |
| existingScroll->update(std::move(parent), std::move(scrollOffset), clip, |
| bounds, userScrollableHorizontal, |
| userScrollableVertical); |
| - else |
| + } else { |
| frameView.setScroll(ScrollPaintPropertyNode::create( |
| std::move(parent), std::move(scrollOffset), clip, bounds, |
| userScrollableHorizontal, userScrollableVertical)); |
| - return frameView.scroll(); |
| + } |
| } |
| -void PaintPropertyTreeBuilder::buildTreeNodes( |
| +void PaintPropertyTreeBuilder::updateProperties( |
| FrameView& frameView, |
| PaintPropertyTreeBuilderContext& context) { |
| if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { |
| @@ -145,16 +144,26 @@ void PaintPropertyTreeBuilder::buildTreeNodes( |
| if (!layoutView) |
| return; |
| - TransformationMatrix frameTranslate; |
| - frameTranslate.translate(frameView.x() + layoutView->location().x() + |
| - context.current.paintOffset.x(), |
| - frameView.y() + layoutView->location().y() + |
| - context.current.paintOffset.y()); |
| - context.current.transform = |
| - layoutView->getMutableForPainting() |
| - .ensurePaintProperties() |
| - .updatePaintOffsetTranslation(context.current.transform, |
| - frameTranslate, FloatPoint3D()); |
| +#if DCHECK_IS_ON() |
| + auto findObjectPropertiesNeedingUpdateScope = |
| + FindObjectPropertiesNeedingUpdateScope::create(*layoutView); |
| +#endif |
| + |
| + if (layoutView->needsPaintPropertyUpdate()) { |
| + TransformationMatrix frameTranslate; |
| + frameTranslate.translate(frameView.x() + layoutView->location().x() + |
| + context.current.paintOffset.x(), |
| + frameView.y() + layoutView->location().y() + |
| + context.current.paintOffset.y()); |
| + layoutView->getMutableForPainting() |
| + .ensurePaintProperties() |
| + .updatePaintOffsetTranslation(context.current.transform, |
| + frameTranslate, FloatPoint3D()); |
| + } |
| + |
| + const auto* properties = layoutView->paintProperties(); |
| + DCHECK(properties && properties->paintOffsetTranslation()); |
| + context.current.transform = properties->paintOffsetTranslation(); |
| context.current.paintOffset = LayoutPoint(); |
| context.current.renderingContextID = 0; |
| context.current.shouldFlattenInheritedTransform = true; |
| @@ -165,47 +174,62 @@ void PaintPropertyTreeBuilder::buildTreeNodes( |
| return; |
| } |
| - TransformationMatrix frameTranslate; |
| - frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), |
| - frameView.y() + context.current.paintOffset.y()); |
| - context.current.transform = updateFrameViewPreTranslation( |
| - frameView, context.current.transform, frameTranslate, FloatPoint3D()); |
| +#if DCHECK_IS_ON() |
| + auto findFrameViewPropertiesNeedingUpdateScope = |
| + FindFrameViewPropertiesNeedingUpdateScope::create(&frameView); |
| +#endif |
| - FloatRoundedRect contentClip( |
| - IntRect(IntPoint(), frameView.visibleContentSize())); |
| - context.current.clip = updateFrameViewContentClip( |
| - frameView, context.current.clip, frameView.preTranslation(), contentClip); |
| - |
| - // Record the fixed properties before any scrolling occurs. |
| - const auto* fixedTransformNode = context.current.transform; |
| - auto* fixedScrollNode = context.current.scroll; |
| - |
| - ScrollOffset scrollOffset = frameView.scrollOffset(); |
| - if (frameView.isScrollable() || !scrollOffset.isZero()) { |
| - TransformationMatrix frameScroll; |
| - frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); |
| - context.current.transform = updateFrameViewScrollTranslation( |
| - frameView, frameView.preTranslation(), frameScroll, FloatPoint3D()); |
| - |
| - IntSize scrollClip = frameView.visibleContentSize(); |
| - IntSize scrollBounds = frameView.contentsSize(); |
| - bool userScrollableHorizontal = |
| - frameView.userInputScrollable(HorizontalScrollbar); |
| - bool userScrollableVertical = |
| - frameView.userInputScrollable(VerticalScrollbar); |
| - context.current.scroll = updateFrameViewScroll( |
| - frameView, context.current.scroll, frameView.scrollTranslation(), |
| - scrollClip, scrollBounds, userScrollableHorizontal, |
| - userScrollableVertical); |
| - } else { |
| - // Ensure pre-existing properties are cleared when there is no scrolling. |
| - frameView.setScrollTranslation(nullptr); |
| - frameView.setScroll(nullptr); |
| + if (frameView.needsPaintPropertyUpdate()) { |
| + TransformationMatrix frameTranslate; |
| + frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), |
| + frameView.y() + context.current.paintOffset.y()); |
| + updateFrameViewPreTranslation(frameView, context.current.transform, |
| + frameTranslate, FloatPoint3D()); |
| + |
| + FloatRoundedRect contentClip( |
| + IntRect(IntPoint(), frameView.visibleContentSize())); |
| + updateFrameViewContentClip(frameView, context.current.clip, |
| + frameView.preTranslation(), contentClip); |
| + |
| + ScrollOffset scrollOffset = frameView.scrollOffset(); |
| + if (frameView.isScrollable() || !scrollOffset.isZero()) { |
| + TransformationMatrix frameScroll; |
| + frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); |
| + updateFrameViewScrollTranslation(frameView, frameView.preTranslation(), |
| + frameScroll, FloatPoint3D()); |
| + |
| + IntSize scrollClip = frameView.visibleContentSize(); |
| + IntSize scrollBounds = frameView.contentsSize(); |
| + bool userScrollableHorizontal = |
| + frameView.userInputScrollable(HorizontalScrollbar); |
| + bool userScrollableVertical = |
| + frameView.userInputScrollable(VerticalScrollbar); |
| + updateFrameViewScroll(frameView, context.current.scroll, |
| + frameView.scrollTranslation(), scrollClip, |
| + scrollBounds, userScrollableHorizontal, |
| + userScrollableVertical); |
| + } else { |
| + // Ensure pre-existing properties are cleared when there is no scrolling. |
| + frameView.setScrollTranslation(nullptr); |
| + frameView.setScroll(nullptr); |
| + } |
| } |
| // Initialize the context for current, absolute and fixed position cases. |
| // They are the same, except that scroll translation does not apply to |
| // fixed position descendants. |
| + const auto* fixedTransformNode = frameView.preTranslation() |
| + ? frameView.preTranslation() |
| + : context.current.transform; |
| + auto* fixedScrollNode = context.current.scroll; |
| + DCHECK(frameView.preTranslation()); |
| + context.current.transform = frameView.preTranslation(); |
| + DCHECK(frameView.contentClip()); |
| + context.current.clip = frameView.contentClip(); |
| + if (const auto* scrollTranslation = frameView.scrollTranslation()) |
| + context.current.transform = scrollTranslation; |
| + if (auto* scroll = frameView.scroll()) |
| + context.current.scroll = scroll; |
| context.current.paintOffset = LayoutPoint(); |
| context.current.renderingContextID = 0; |
| context.current.shouldFlattenInheritedTransform = true; |
| @@ -230,40 +254,47 @@ void PaintPropertyTreeBuilder::updatePaintOffsetTranslation( |
| return; |
| } |
| + bool usesPaintOffsetTranslation = false; |
| 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 (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 (object.needsPaintPropertyUpdate()) { |
| + 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(); |
| } |
| } |
| - 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; |
| + } |
| } |
| static FloatPoint3D transformOrigin(const LayoutBox& box) { |
| @@ -285,27 +316,31 @@ void PaintPropertyTreeBuilder::updateTransformForNonRootSVG( |
| DCHECK(object.isSVGForeignObject() || |
| context.current.paintOffset == LayoutPoint()); |
| - // FIXME(pdr): Refactor this so all non-root SVG objects use the same |
| - // transform function. |
| - const AffineTransform& transform = object.isSVGForeignObject() |
| - ? object.localSVGTransform() |
| - : object.localToSVGParentTransform(); |
| - |
| - // 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. |
| - 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; |
| + if (object.needsPaintPropertyUpdate()) { |
| + // FIXME(pdr): Refactor this so all non-root SVG objects use the same |
|
Xianzhu
2016/10/26 19:21:51
Nit: s/FIXME/TODO/
pdr.
2016/10/26 23:10:19
Done
|
| + // transform function. |
| + const AffineTransform& transform = object.isSVGForeignObject() |
| + ? object.localSVGTransform() |
| + : object.localToSVGParentTransform(); |
| + // 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. |
| + 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; |
| - } else { |
| - if (auto* properties = object.getMutableForPainting().paintProperties()) |
| - properties->clearTransform(); |
| + context.current.renderingContextID = 0; |
| } |
| } |
| @@ -317,42 +352,45 @@ void PaintPropertyTreeBuilder::updateTransform( |
| 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); |
| - 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 (object.needsPaintPropertyUpdate()) { |
| + 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. |
| - if (!renderingContextID) |
| + unsigned renderingContextID = context.current.renderingContextID; |
| + if (style.preserves3D() && !renderingContextID) |
| renderingContextID = PtrHash<const LayoutObject>::hash(&object); |
| - renderingContextIDForChildren = renderingContextID; |
| - childrenFlattenInheritedTransform = false; |
| + |
| + object.getMutableForPainting().ensurePaintProperties().updateTransform( |
| + context.current.transform, matrix, |
| + transformOrigin(toLayoutBox(object)), |
| + context.current.shouldFlattenInheritedTransform, renderingContextID); |
| + } else { |
| + if (auto* properties = object.getMutableForPainting().paintProperties()) |
| + properties->clearTransform(); |
| } |
| + } |
| - context.current.transform = |
| - object.getMutableForPainting().ensurePaintProperties().updateTransform( |
| - context.current.transform, matrix, origin, |
| - flattensInheritedTransform, renderingContextID); |
| - context.current.renderingContextID = renderingContextIDForChildren; |
| - context.current.shouldFlattenInheritedTransform = |
| - childrenFlattenInheritedTransform; |
| - } else { |
| - if (auto* properties = object.getMutableForPainting().paintProperties()) |
| - properties->clearTransform(); |
| + const auto* properties = object.paintProperties(); |
| + if (properties && properties->transform()) { |
| + context.current.transform = properties->transform(); |
| + unsigned renderingContextID = properties->transform()->renderingContextID(); |
| + if (context.current.renderingContextID != renderingContextID) { |
| + context.current.renderingContextID = renderingContextID; |
| + context.current.shouldFlattenInheritedTransform = false; |
| + } else { |
| + context.current.renderingContextID = 0; |
| + context.current.shouldFlattenInheritedTransform = true; |
| + } |
| } |
| } |
| @@ -362,118 +400,136 @@ void PaintPropertyTreeBuilder::updateEffect( |
| const ComputedStyle& style = object.styleRef(); |
| if (!style.isStackingContext()) { |
| - if (ObjectPaintProperties* properties = |
| - object.getMutableForPainting().paintProperties()) |
| - properties->clearEffect(); |
| + if (object.needsPaintPropertyUpdate()) { |
| + 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; |
| + if (object.needsPaintPropertyUpdate()) { |
| + // 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); |
| + } |
| - float opacity = style.opacity(); |
| - if (opacity != 1.0f) |
| - effectNodeNeeded = true; |
| + const ClipPaintPropertyNode* outputClip = rootClipNode(); |
| + // 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. |
| + } |
| - 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 = rootClipNode(); |
| - // 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. Need to |
| - // generate special clip node to hint how to expand clip / cull rect. |
| + 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; |
| } |
| - |
| - if (!effectNodeNeeded) { |
| - if (ObjectPaintProperties* properties = |
| - object.getMutableForPainting().paintProperties()) |
| - properties->clearEffect(); |
| - return; |
| - } |
| - |
| - context.currentEffect = |
| - object.getMutableForPainting().ensurePaintProperties().updateEffect( |
| - context.currentEffect, context.current.transform, outputClip, |
| - std::move(filter), opacity); |
| } |
| 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 (object.needsPaintPropertyUpdate()) { |
| + 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); |
| + object.getMutableForPainting().ensurePaintProperties().updateCssClip( |
| + context.current.clip, context.current.transform, |
| + FloatRoundedRect(FloatRect(clipRect))); |
| + } else { |
| + if (auto* properties = object.getMutableForPainting().paintProperties()) |
| + properties->clearCssClip(); |
| + } |
| } |
| - if (auto* properties = object.getMutableForPainting().paintProperties()) |
| - properties->clearCssClip(); |
| + const auto* properties = object.paintProperties(); |
| + if (properties && properties->cssClip()) |
| + context.current.clip = properties->cssClip(); |
| } |
| 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()) |
| - return; |
| - |
| - 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)); |
| + if (object.needsPaintPropertyUpdate()) { |
| + // 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)); |
| + } |
| + } |
| } |
| // TODO(trchen): Remove this once we bake the paint offset into frameRect. |
| void PaintPropertyTreeBuilder::updateScrollbarPaintOffset( |
| const LayoutObject& object, |
| const PaintPropertyTreeBuilderContext& context) { |
| + if (!object.needsPaintPropertyUpdate()) |
| + return; |
| + |
| IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); |
| if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { |
| if (PaintLayerScrollableArea* scrollableArea = |
| @@ -498,10 +554,14 @@ void PaintPropertyTreeBuilder::updateScrollbarPaintOffset( |
| void PaintPropertyTreeBuilder::updateMainThreadScrollingReasons( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| + // TODO(pdr): Mark properties as needing an update for main thread scroll |
| + // reasons and ensure reason changes are propagated to ancestors to account |
| + // for the parent walk below. |
| if (context.current.scroll && |
| - !object.document().settings()->threadedScrollingEnabled()) |
| + !object.document().settings()->threadedScrollingEnabled()) { |
| context.current.scroll->addMainThreadScrollingReasons( |
| MainThreadScrollingReason::kThreadedScrollingDisabled); |
| + } |
| if (object.isBackgroundAttachmentFixedObject()) { |
| auto* scrollNode = context.current.scroll; |
| @@ -522,45 +582,50 @@ void PaintPropertyTreeBuilder::updateOverflowClip( |
| if (!object.isBox()) |
| return; |
| const LayoutBox& box = toLayoutBox(object); |
| + if (object.needsPaintPropertyUpdate()) { |
| + // 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; |
| + } |
| - // 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()) { |
| + 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(); |
| - properties->clearOverflowClip(); |
| } |
| - return; |
| - } |
| - if (box.styleRef().hasBorderRadius()) { |
| - auto innerBorder = box.styleRef().getRoundedInnerBorderFor( |
| - LayoutRect(context.current.paintOffset, box.size())); |
| - context.current.clip = |
| - object.getMutableForPainting() |
| - .ensurePaintProperties() |
| - .updateInnerBorderRadiusClip( |
| - context.current.clip, context.current.transform, innerBorder); |
| - } else if (auto* properties = |
| - object.getMutableForPainting().paintProperties()) { |
| - properties->clearInnerBorderRadiusClip(); |
| + object.getMutableForPainting().ensurePaintProperties().updateOverflowClip( |
| + currentClip, context.current.transform, |
| + FloatRoundedRect(FloatRect(clipRect))); |
| } |
| - context.current.clip = |
| - object.getMutableForPainting().ensurePaintProperties().updateOverflowClip( |
| - context.current.clip, context.current.transform, |
| - FloatRoundedRect(FloatRect(clipRect))); |
| + const auto* properties = object.paintProperties(); |
| + if (properties && properties->overflowClip()) |
| + context.current.clip = properties->overflowClip(); |
| } |
| static FloatPoint perspectiveOrigin(const LayoutBox& box) { |
| @@ -574,26 +639,31 @@ static FloatPoint perspectiveOrigin(const LayoutBox& box) { |
| void PaintPropertyTreeBuilder::updatePerspective( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| - const ComputedStyle& style = object.styleRef(); |
| - if (!object.isBox() || !style.hasPerspective()) { |
| - if (auto* properties = object.getMutableForPainting().paintProperties()) |
| - properties->clearPerspective(); |
| - return; |
| - } |
| - |
| - // 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); |
| - context.current.transform = |
| + if (object.needsPaintPropertyUpdate()) { |
| + 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); |
| - context.current.shouldFlattenInheritedTransform = false; |
| + } 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; |
| + } |
| } |
| void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform( |
| @@ -602,66 +672,78 @@ void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform( |
| 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 = |
| + if (object.needsPaintPropertyUpdate()) { |
| + AffineTransform transformToBorderBox = |
| + SVGRootPainter(toLayoutSVGRoot(object)) |
| + .transformToPixelSnappedBorderBox(context.current.paintOffset); |
| + if (!transformToBorderBox.isIdentity()) { |
| object.getMutableForPainting() |
| .ensurePaintProperties() |
| .updateSvgLocalToBorderBoxTransform( |
| context.current.transform, transformToBorderBox, FloatPoint3D()); |
| - context.current.shouldFlattenInheritedTransform = false; |
| - context.current.renderingContextID = 0; |
| + } else { |
| + if (auto* properties = object.getMutableForPainting().paintProperties()) |
| + properties->clearSvgLocalToBorderBoxTransform(); |
| + } |
| + } |
| + |
| + const auto* properties = object.paintProperties(); |
| + if (properties && properties->svgLocalToBorderBoxTransform()) { |
| + context.current.transform = properties->svgLocalToBorderBoxTransform(); |
| + context.current.shouldFlattenInheritedTransform = false; |
| + context.current.renderingContextID = 0; |
| + } |
| + // The paint offset is included in |transformToBorderBox| so SVG does not need |
| + // to handle paint offset internally. |
| + context.current.paintOffset = LayoutPoint(); |
| } |
| 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 (object.needsPaintPropertyUpdate()) { |
| + 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 (auto* properties = object.getMutableForPainting().paintProperties()) { |
| - 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; |
| } |
| } |
| @@ -701,20 +783,25 @@ void PaintPropertyTreeBuilder::updateOutOfFlowContext( |
| if (context.fixedPosition.clip == cssClip->parent()) { |
| context.fixedPosition.clip = cssClip; |
| } else { |
| - context.fixedPosition.clip = |
| - object.getMutableForPainting() |
| - .ensurePaintProperties() |
| - .updateCssClipFixedPosition( |
| - context.fixedPosition.clip, |
| - const_cast<TransformPaintPropertyNode*>( |
| - cssClip->localTransformSpace()), |
| - cssClip->clipRect()); |
| + if (object.needsPaintPropertyUpdate()) { |
| + 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(); |
| + if (object.needsPaintPropertyUpdate()) { |
| + if (auto* properties = object.getMutableForPainting().paintProperties()) |
| + properties->clearCssClipFixedPosition(); |
| + } |
| } |
| // Override ContainingBlockContext based on the properties of a containing block |
| @@ -839,12 +926,17 @@ static void deriveBorderBoxFromContainerContext( |
| } |
| } |
| -void PaintPropertyTreeBuilder::buildTreeNodesForSelf( |
| +void PaintPropertyTreeBuilder::updatePropertiesForSelf( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| if (!object.isBoxModelObject() && !object.isSVG()) |
| return; |
| +#if DCHECK_IS_ON() |
| + auto findObjectPropertiesNeedingUpdateScope = |
| + FindObjectPropertiesNeedingUpdateScope::create(object); |
| +#endif |
| + |
| deriveBorderBoxFromContainerContext(object, context); |
| updatePaintOffsetTranslation(object, context); |
| @@ -856,12 +948,17 @@ void PaintPropertyTreeBuilder::buildTreeNodesForSelf( |
| updateMainThreadScrollingReasons(object, context); |
| } |
| -void PaintPropertyTreeBuilder::buildTreeNodesForChildren( |
| +void PaintPropertyTreeBuilder::updatePropertiesForChildren( |
| const LayoutObject& object, |
| PaintPropertyTreeBuilderContext& context) { |
| if (!object.isBoxModelObject() && !object.isSVG()) |
| return; |
| +#if DCHECK_IS_ON() |
| + auto findObjectPropertiesNeedingUpdateScope = |
| + FindObjectPropertiesNeedingUpdateScope::create(object); |
| +#endif |
| + |
| updateOverflowClip(object, context); |
| updatePerspective(object, context); |
| updateSvgLocalToBorderBoxTransform(object, context); |