 Chromium Code Reviews
 Chromium Code Reviews Issue 2144823006:
  Reuse existing paint property node is possible  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 2144823006:
  Reuse existing paint property node is possible  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 1d394cb7cd37438feba099574ccad966af718cfb..1540c0f06011a2a59462e399d2bae459ee9cafb7 100644 | 
| --- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp | 
| +++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp | 
| @@ -21,17 +21,18 @@ namespace blink { | 
| void PaintPropertyTreeBuilder::buildTreeRootNodes(FrameView& rootFrame, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - RefPtr<TransformPaintPropertyNode> transformRoot = TransformPaintPropertyNode::create(TransformationMatrix(), FloatPoint3D(), nullptr); | 
| - context.current.transform = context.absolutePosition.transform = context.fixedPosition.transform = transformRoot.get(); | 
| - rootFrame.setRootTransform(std::move(transformRoot)); | 
| - | 
| - RefPtr<ClipPaintPropertyNode> clipRoot = ClipPaintPropertyNode::create(transformRoot, FloatRoundedRect(LayoutRect::infiniteIntRect()), nullptr); | 
| - context.current.clip = context.absolutePosition.clip = context.fixedPosition.clip = clipRoot.get(); | 
| - rootFrame.setRootClip(std::move(clipRoot)); | 
| + if (!rootFrame.rootTransform() || rootFrame.rootTransform()->parent()) { | 
| + rootFrame.setRootTransform(TransformPaintPropertyNode::create(nullptr, TransformationMatrix(), FloatPoint3D())); | 
| + rootFrame.setRootClip(ClipPaintPropertyNode::create(nullptr, rootFrame.rootTransform(), FloatRoundedRect(LayoutRect::infiniteIntRect()))); | 
| + rootFrame.setRootEffect(EffectPaintPropertyNode::create(nullptr, 1.0)); | 
| + } else { | 
| + DCHECK(rootFrame.rootClip() && !rootFrame.rootClip()->parent()); | 
| + DCHECK(rootFrame.rootEffect() && !rootFrame.rootEffect()->parent()); | 
| + } | 
| - RefPtr<EffectPaintPropertyNode> effectRoot = EffectPaintPropertyNode::create(1.0, nullptr); | 
| - context.currentEffect = effectRoot.get(); | 
| - rootFrame.setRootEffect(std::move(effectRoot)); | 
| + context.current.transform = context.absolutePosition.transform = context.fixedPosition.transform = rootFrame.rootTransform(); | 
| + context.current.clip = context.absolutePosition.clip = context.fixedPosition.clip = rootFrame.rootClip(); | 
| + context.currentEffect = rootFrame.rootEffect(); | 
| } | 
| void PaintPropertyTreeBuilder::buildTreeNodes(FrameView& frameView, PaintPropertyTreeBuilderContext& context) | 
| @@ -42,58 +43,84 @@ void PaintPropertyTreeBuilder::buildTreeNodes(FrameView& frameView, PaintPropert | 
| TransformationMatrix frameTranslate; | 
| frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), frameView.y() + context.current.paintOffset.y()); | 
| - RefPtr<TransformPaintPropertyNode> newTransformNodeForPreTranslation = TransformPaintPropertyNode::create(frameTranslate, FloatPoint3D(), context.current.transform); | 
| + if (TransformPaintPropertyNode* existingPreTranslation = frameView.preTranslation()) | 
| + existingPreTranslation->update(context.current.transform, frameTranslate, FloatPoint3D()); | 
| + else | 
| + frameView.setPreTranslation(TransformPaintPropertyNode::create(context.current.transform, frameTranslate, FloatPoint3D())); | 
| FloatRoundedRect contentClip(IntRect(IntPoint(), frameView.visibleContentSize())); | 
| - RefPtr<ClipPaintPropertyNode> newClipNodeForContentClip = ClipPaintPropertyNode::create(newTransformNodeForPreTranslation.get(), contentClip, context.current.clip); | 
| + if (ClipPaintPropertyNode* existingContentClip = frameView.contentClip()) | 
| + existingContentClip->update(context.current.clip, frameView.preTranslation(), contentClip); | 
| + else | 
| + frameView.setContentClip(ClipPaintPropertyNode::create(context.current.clip, frameView.preTranslation(), contentClip)); | 
| DoubleSize scrollOffset = frameView.scrollOffsetDouble(); | 
| TransformationMatrix frameScroll; | 
| frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); | 
| - RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = TransformPaintPropertyNode::create(frameScroll, FloatPoint3D(), newTransformNodeForPreTranslation); | 
| + if (TransformPaintPropertyNode* existingScrollTranslation = frameView.scrollTranslation()) | 
| + existingScrollTranslation->update(frameView.preTranslation(), frameScroll, FloatPoint3D()); | 
| + else | 
| + frameView.setScrollTranslation(TransformPaintPropertyNode::create(frameView.preTranslation(), frameScroll, FloatPoint3D())); | 
| // 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. | 
| - context.current.transform = newTransformNodeForScrollTranslation.get(); | 
| + context.current.transform = frameView.scrollTranslation(); | 
| context.current.paintOffset = LayoutPoint(); | 
| - context.current.clip = newClipNodeForContentClip.get(); | 
| + context.current.clip = frameView.contentClip(); | 
| context.absolutePosition = context.current; | 
| context.containerForAbsolutePosition = nullptr; | 
| context.fixedPosition = context.current; | 
| - context.fixedPosition.transform = newTransformNodeForPreTranslation.get(); | 
| + context.fixedPosition.transform = frameView.preTranslation(); | 
| +} | 
| - frameView.setPreTranslation(newTransformNodeForPreTranslation.release()); | 
| - frameView.setScrollTranslation(newTransformNodeForScrollTranslation.release()); | 
| - frameView.setContentClip(newClipNodeForContentClip.release()); | 
| +template <typename PropertyNode, void (ObjectPaintProperties::*Setter)(PassRefPtr<PropertyNode>)> | 
| +void PaintPropertyTreeBuilder::clearPaintProperty(const LayoutObject& object) | 
| +{ | 
| + if (ObjectPaintProperties* existingProperties = object.objectPaintProperties()) | 
| + (existingProperties->*Setter)(nullptr); | 
| +} | 
| + | 
| +template < | 
| + typename PropertyNode, | 
| + PropertyNode* (ObjectPaintProperties::*Getter)() const, | 
| + void (ObjectPaintProperties::*Setter)(PassRefPtr<PropertyNode>), | 
| + typename... Args> | 
| +void PaintPropertyTreeBuilder::updateOrCreatePaintProperty(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context, PropertyNode*& contextProperty, const Args&... args) | 
| +{ | 
| + ObjectPaintProperties* existingProperties = object.objectPaintProperties(); | 
| + PropertyNode* existingPropertyNode = existingProperties ? (existingProperties->*Getter)() : nullptr; | 
| + if (existingPropertyNode) { | 
| + existingPropertyNode->update(contextProperty, args...); | 
| + contextProperty = existingPropertyNode; | 
| 
jbroman
2016/07/24 01:08:23
Ex-post-facto: this was extremely confusing to me
 
Xianzhu
2016/07/24 22:18:42
Thanks for the suggestions. Addressed them in http
 | 
| + } else { | 
| + RefPtr<PropertyNode> newPropertyNode = PropertyNode::create(contextProperty, args...); | 
| + contextProperty = newPropertyNode.get(); | 
| + (object.getMutableForPainting().ensureObjectPaintProperties().*Setter)(newPropertyNode.release()); | 
| + } | 
| } | 
| void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - if (object.isBoxModelObject()) { | 
| + if (object.isBoxModelObject() && context.current.paintOffset != LayoutPoint()) { | 
| // TODO(trchen): Eliminate PaintLayer dependency. | 
| PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | 
| - if (!layer || !layer->paintsWithTransform(GlobalPaintNormalPhase)) | 
| + 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); | 
| + | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::paintOffsetTranslation, &ObjectPaintProperties::setPaintOffsetTranslation>( | 
| + object, context, context.current.transform, TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()), FloatPoint3D()); | 
| + context.current.paintOffset = fractionalPaintOffset; | 
| return; | 
| + } | 
| } | 
| - | 
| - if (context.current.paintOffset == LayoutPoint()) | 
| - return; | 
| - | 
| - // We should use the same subpixel paint offset values for snapping regardless of whether a | 
| - // transform is present. If there is a transform we round the paint offset but keep around | 
| - // the residual fractional component for the transformed content to paint with. | 
| - // In spv1 this was called "subpixel accumulation". For more information, see | 
| - // PaintLayer::subpixelAccumulation() and PaintLayerPainter::paintFragmentByApplyingTransform. | 
| - IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); | 
| - LayoutPoint fractionalPaintOffset = LayoutPoint(context.current.paintOffset - roundedPaintOffset); | 
| - | 
| - RefPtr<TransformPaintPropertyNode> paintOffsetTranslation = TransformPaintPropertyNode::create( | 
| - TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()), | 
| - FloatPoint3D(), context.current.transform); | 
| - context.current.transform = paintOffsetTranslation.get(); | 
| - context.current.paintOffset = fractionalPaintOffset; | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setPaintOffsetTranslation(paintOffsetTranslation.release()); | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setPaintOffsetTranslation>(object); | 
| } | 
| static FloatPoint3D transformOrigin(const LayoutBox& box) | 
| @@ -117,56 +144,52 @@ void PaintPropertyTreeBuilder::updateTransform(const LayoutObject& object, Paint | 
| // the animation passes through the identity matrix. | 
| // FIXME(pdr): Refactor this so all non-root SVG objects use the same transform function. | 
| const AffineTransform& transform = object.isSVGForeignObject() ? object.localSVGTransform() : object.localToSVGParentTransform(); | 
| - if (transform.isIdentity()) | 
| + if (!transform.isIdentity()) { | 
| + // The origin is included in the local transform, so leave origin empty. | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::transform, &ObjectPaintProperties::setTransform>( | 
| + object, context, context.current.transform, TransformationMatrix(transform), FloatPoint3D()); | 
| return; | 
| - | 
| - // The origin is included in the local transform, so use an empty origin. | 
| - RefPtr<TransformPaintPropertyNode> svgTransform = TransformPaintPropertyNode::create( | 
| - transform, FloatPoint3D(0, 0, 0), context.current.transform); | 
| - context.current.transform = svgTransform.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setTransform(svgTransform.release()); | 
| - return; | 
| + } | 
| + } else { | 
| + const ComputedStyle& style = object.styleRef(); | 
| + if (object.isBox() && style.hasTransform()) { | 
| + TransformationMatrix matrix; | 
| + style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, | 
| + ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); | 
| + FloatPoint3D origin = transformOrigin(toLayoutBox(object)); | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::transform, &ObjectPaintProperties::setTransform>( | 
| + object, context, context.current.transform, matrix, origin); | 
| + return; | 
| + } | 
| } | 
| - | 
| - const ComputedStyle& style = object.styleRef(); | 
| - if (!object.isBox() || !style.hasTransform()) | 
| - return; | 
| - | 
| - TransformationMatrix matrix; | 
| - style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, | 
| - ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); | 
| - RefPtr<TransformPaintPropertyNode> transformNode = TransformPaintPropertyNode::create( | 
| - matrix, transformOrigin(toLayoutBox(object)), context.current.transform); | 
| - context.current.transform = transformNode.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setTransform(transformNode.release()); | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setTransform>(object); | 
| } | 
| void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - if (!object.styleRef().hasOpacity()) | 
| + if (!object.styleRef().hasOpacity()) { | 
| + clearPaintProperty<EffectPaintPropertyNode, &ObjectPaintProperties::setEffect>(object); | 
| return; | 
| - RefPtr<EffectPaintPropertyNode> effectNode = EffectPaintPropertyNode::create(object.styleRef().opacity(), context.currentEffect); | 
| - context.currentEffect = effectNode.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setEffect(effectNode.release()); | 
| + } | 
| + | 
| + updateOrCreatePaintProperty<EffectPaintPropertyNode, &ObjectPaintProperties::effect, &ObjectPaintProperties::setEffect>( | 
| + object, context, context.currentEffect, object.styleRef().opacity()); | 
| } | 
| void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - if (!object.hasClip()) | 
| + 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); | 
| + updateOrCreatePaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::cssClip, &ObjectPaintProperties::setCssClip>( | 
| + object, context, context.current.clip, context.current.transform, FloatRoundedRect(FloatRect(clipRect))); | 
| return; | 
| - ASSERT(object.canContainAbsolutePositionObjects()); | 
| - | 
| - // 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. | 
| - LayoutRect clipRect = toLayoutBox(object).clipRect(context.current.paintOffset); | 
| - RefPtr<ClipPaintPropertyNode> clipNode = ClipPaintPropertyNode::create( | 
| - context.current.transform, | 
| - FloatRoundedRect(FloatRect(clipRect)), | 
| - context.current.clip); | 
| - context.current.clip = clipNode.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setCssClip(clipNode.release()); | 
| + } | 
| + clearPaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::setCssClip>(object); | 
| } | 
| void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context) | 
| @@ -186,20 +209,20 @@ void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& o | 
| void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context) | 
| { | 
| IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); | 
| - if (roundedPaintOffset == IntPoint()) | 
| - return; | 
| - | 
| - if (!object.isBoxModelObject()) | 
| - return; | 
| - PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(object).getScrollableArea(); | 
| - if (!scrollableArea) | 
| - return; | 
| - if (!scrollableArea->horizontalScrollbar() && !scrollableArea->verticalScrollbar()) | 
| - return; | 
| - | 
| - auto paintOffset = TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setScrollbarPaintOffset( | 
| - TransformPaintPropertyNode::create(paintOffset, FloatPoint3D(), context.current.transform)); | 
| + if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { | 
| + if (PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(object).getScrollableArea()) { | 
| + if (scrollableArea->horizontalScrollbar() || scrollableArea->verticalScrollbar()) { | 
| + auto paintOffset = TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()); | 
| + // Make a copy of context.current.transform because we don't want to set the scrollbarPaintOffset node | 
| + // as the current transform. | 
| + TransformPaintPropertyNode* parentTransform = context.current.transform; | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::scrollbarPaintOffset, &ObjectPaintProperties::setScrollbarPaintOffset>( | 
| + object, context, parentTransform, paintOffset, FloatPoint3D()); | 
| + return; | 
| + } | 
| + } | 
| + } | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setScrollbarPaintOffset>(object); | 
| } | 
| void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| @@ -213,27 +236,26 @@ void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, Pa | 
| // 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()) | 
| + if (box.hasControlClip()) { | 
| clipRect = box.controlClipRect(context.current.paintOffset); | 
| - else if (box.hasOverflowClip()) | 
| + } else if (box.hasOverflowClip()) { | 
| clipRect = box.overflowClipRect(context.current.paintOffset); | 
| - else | 
| + } else { | 
| + clearPaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::setOverflowClip>(object); | 
| return; | 
| + } | 
| + // This need to be in top-level block to hold the reference until we finish creating the normal clip node. | 
| RefPtr<ClipPaintPropertyNode> borderRadiusClip; | 
| if (box.styleRef().hasBorderRadius()) { | 
| auto innerBorder = box.styleRef().getRoundedInnerBorderFor( | 
| LayoutRect(context.current.paintOffset, box.size())); | 
| - borderRadiusClip = ClipPaintPropertyNode::create( | 
| - context.current.transform, innerBorder, context.current.clip); | 
| + borderRadiusClip = ClipPaintPropertyNode::create(context.current.clip, context.current.transform, innerBorder); | 
| + context.current.clip = borderRadiusClip.get(); | 
| } | 
| - RefPtr<ClipPaintPropertyNode> overflowClip = ClipPaintPropertyNode::create( | 
| - context.current.transform, | 
| - FloatRoundedRect(FloatRect(clipRect)), | 
| - borderRadiusClip ? borderRadiusClip.release() : context.current.clip); | 
| - context.current.clip = overflowClip.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setOverflowClip(overflowClip.release()); | 
| + updateOrCreatePaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::overflowClip, &ObjectPaintProperties::setOverflowClip>( | 
| + object, context, context.current.clip, context.current.transform, FloatRoundedRect(FloatRect(clipRect))); | 
| } | 
| static FloatPoint perspectiveOrigin(const LayoutBox& box) | 
| @@ -248,15 +270,14 @@ 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 (!object.isBox() || !style.hasPerspective()) { | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setPerspective>(object); | 
| return; | 
| + } | 
| - RefPtr<TransformPaintPropertyNode> perspective = TransformPaintPropertyNode::create( | 
| - TransformationMatrix().applyPerspective(style.perspective()), | 
| - perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.current.paintOffset), | 
| - context.current.transform); | 
| - context.current.transform = perspective.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setPerspective(perspective.release()); | 
| + FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.current.paintOffset); | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::perspective, &ObjectPaintProperties::setPerspective>( | 
| + object, context, context.current.transform, TransformationMatrix().applyPerspective(style.perspective()), origin); | 
| } | 
| void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| @@ -270,33 +291,30 @@ void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(const LayoutOb | 
| // offset internally. | 
| context.current.paintOffset = LayoutPoint(); | 
| - if (transformToBorderBox.isIdentity()) | 
| + if (transformToBorderBox.isIdentity()) { | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setSvgLocalToBorderBoxTransform>(object); | 
| return; | 
| + } | 
| - RefPtr<TransformPaintPropertyNode> svgLocalToBorderBoxTransform = TransformPaintPropertyNode::create( | 
| - transformToBorderBox, FloatPoint3D(0, 0, 0), context.current.transform); | 
| - context.current.transform = svgLocalToBorderBoxTransform.get(); | 
| - context.current.paintOffset = LayoutPoint(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setSvgLocalToBorderBoxTransform(svgLocalToBorderBoxTransform.release()); | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::svgLocalToBorderBoxTransform, &ObjectPaintProperties::setSvgLocalToBorderBoxTransform>( | 
| + object, context, context.current.transform, transformToBorderBox, FloatPoint3D()); | 
| } | 
| void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - if (!object.isBoxModelObject() || !object.hasOverflowClip()) | 
| - return; | 
| - | 
| - PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | 
| - ASSERT(layer); | 
| - DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); | 
| - if (scrollOffset.isZero() && !layer->scrollsOverflow()) | 
| - return; | 
| + if (object.isBoxModelObject() && object.hasOverflowClip()) { | 
| + PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | 
| + DCHECK(layer); | 
| + DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); | 
| - RefPtr<TransformPaintPropertyNode> scrollTranslation = TransformPaintPropertyNode::create( | 
| - TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()), | 
| - FloatPoint3D(), | 
| - context.current.transform); | 
| - context.current.transform = scrollTranslation.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setScrollTranslation(scrollTranslation.release()); | 
| + if (!scrollOffset.isZero() || layer->scrollsOverflow()) { | 
| + TransformationMatrix matrix = TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()); | 
| + updateOrCreatePaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::scrollTranslation, &ObjectPaintProperties::setScrollTranslation>( | 
| + object, context, context.current.transform, matrix, FloatPoint3D()); | 
| + return; | 
| + } | 
| + } | 
| + clearPaintProperty<TransformPaintPropertyNode, &ObjectPaintProperties::setScrollTranslation>(object); | 
| } | 
| void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| @@ -322,16 +340,13 @@ void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object | 
| // context has exactly the same clip. Reuse if possible. | 
| if (context.fixedPosition.clip == cssClip->parent()) { | 
| context.fixedPosition.clip = cssClip; | 
| + } else { | 
| + updateOrCreatePaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::cssClipFixedPosition, &ObjectPaintProperties::setCssClipFixedPosition>( | 
| + object, context, context.fixedPosition.clip, const_cast<TransformPaintPropertyNode*>(cssClip->localTransformSpace()), cssClip->clipRect()); | 
| return; | 
| } | 
| - | 
| - RefPtr<ClipPaintPropertyNode> clipFixedPosition = ClipPaintPropertyNode::create( | 
| - const_cast<TransformPaintPropertyNode*>(cssClip->localTransformSpace()), | 
| - cssClip->clipRect(), | 
| - context.fixedPosition.clip); | 
| - context.fixedPosition.clip = clipFixedPosition.get(); | 
| - object.getMutableForPainting().ensureObjectPaintProperties().setCssClipFixedPosition(clipFixedPosition.release()); | 
| } | 
| + clearPaintProperty<ClipPaintPropertyNode, &ObjectPaintProperties::setCssClipFixedPosition>(object); | 
| } | 
| static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| @@ -385,8 +400,6 @@ static void deriveBorderBoxFromContainerContext(const LayoutObject& object, Pain | 
| void PaintPropertyTreeBuilder::buildTreeNodes(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | 
| { | 
| - object.getMutableForPainting().clearObjectPaintProperties(); | 
| - | 
| if (!object.isBoxModelObject() && !object.isSVG()) | 
| return; |