Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(928)

Unified Diff: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp

Issue 2161523002: Isolation of subtrees of stacking contexts and out-of-flow positioned objects (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@CachePaintProperties
Patch Set: - Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 76989710eff9a59504b15dbd6a61fbaf23455f2c..0ea39143ab8bc1eb315f76a4c60f5f2803cd496e 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp
@@ -24,7 +24,7 @@ void PaintPropertyTreeBuilder::buildTreeRootNodes(FrameView& rootFrame, PaintPro
if (!rootFrame.rootTransform() || rootFrame.rootTransform()->parent()) {
rootFrame.setRootTransform(TransformPaintPropertyNode::create(TransformationMatrix(), FloatPoint3D(), nullptr));
rootFrame.setRootClip(ClipPaintPropertyNode::create(rootFrame.rootTransform(), FloatRoundedRect(LayoutRect::infiniteIntRect()), nullptr));
- rootFrame.setRootEffect(EffectPaintPropertyNode::create(1.0, nullptr));
+ rootFrame.setRootEffect(EffectPaintPropertyNode::create(1.0f, nullptr));
} else {
DCHECK(rootFrame.rootClip() && !rootFrame.rootClip()->parent());
DCHECK(rootFrame.rootEffect() && !rootFrame.rootEffect()->parent());
@@ -72,12 +72,13 @@ template <
typename PropertyNode,
PropertyNode* (ObjectPaintProperties::*Getter)() const,
void (ObjectPaintProperties::*Setter)(PassRefPtr<PropertyNode>),
- bool (*ComputeProperty)(const LayoutObject&, PaintPropertyTreeBuilderContext&, typename PropertyNode::Value&)>
+ PaintPropertyTreeBuilder::PaintPropertyNodeType (*ComputeProperty)(const LayoutObject&, PaintPropertyTreeBuilderContext&, typename PropertyNode::Value&)>
PropertyNode* PaintPropertyTreeBuilder::updateObjectPaintProperty(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, PropertyNode*& contextProperty)
{
ObjectPaintProperties* previousProperties = object.objectPaintProperties();
typename PropertyNode::Value newPropertyValue;
- if (!ComputeProperty(object, context, newPropertyValue)) {
+ PaintPropertyNodeType nodeType = ComputeProperty(object, context, newPropertyValue);
+ if (nodeType == NoNode) {
if (previousProperties)
(previousProperties->*Setter)(nullptr);
return nullptr;
@@ -87,27 +88,71 @@ PropertyNode* PaintPropertyTreeBuilder::updateObjectPaintProperty(const LayoutOb
if (previousPropertyNode && previousPropertyNode->value() == newPropertyValue) {
previousPropertyNode->setParent(contextProperty);
contextProperty = previousPropertyNode;
- return previousPropertyNode;
+ } else {
+ RefPtr<PropertyNode> newPropertyNode = PropertyNode::create(newPropertyValue, contextProperty);
+ contextProperty = newPropertyNode.get();
+ (object.getMutableForPainting().ensureObjectPaintProperties().*Setter)(newPropertyNode.release());
}
- RefPtr<PropertyNode> newPropertyNode = PropertyNode::create(newPropertyValue, contextProperty);
- contextProperty = newPropertyNode.get();
- (object.getMutableForPainting().ensureObjectPaintProperties().*Setter)(newPropertyNode.release());
+ if (nodeType == IsolationNode)
+ contextProperty->setIsIsolationNode();
return contextProperty;
}
-bool PaintPropertyTreeBuilder::computePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+// We treat out-of-flow positioned objects and stacking context objects as "paint property isolation
+// boundaries". That is, we will create a no-op property nodes if needed that serve as the isolated
+// roots of the paint property subtrees, to ensure that there is no paint property parent references
+// from descendants to ancestors crossing the isolated boundaries. When a whole stacking context is
+// unchanged in the current document cycle, no property node in the subtree will need update.
+//
+// An exception of the above no-reference-crossing-isolation-boundaries rule is for references from a
+// descendant which is a isolation boundary, e.g. from nodes for an out-of-flow positioned object to
+// nodes for the container or ancestor of the container of the out-of-flow positioned object, crossing
+// stacking contexts (isolation boundaries) which are are not the containers of the out-of-flow
+// positioned object. For example, in
+//
+// <div id="transform" style="transform: translate(10px, 10px)">
+// <div id="opacity" style="opacity: 0.5">
+// <div id="absolute" style="position: absolute"></div>
+// <div>
+// </div>
+//
+// the property nodes for 'absolute' can directly reference nodes of 'transform', crossing 'opacity'
+// which is a stacking context and isolation boundary.
+//
+// The exception avoids the necessity to create multiple no-op property nodes for normal descendants
+// and out-of-flow positioned descendants in different tree paths.
+static bool isPaintPropertyIsolationBoundary(const LayoutObject& object)
+{
+ return
+ // This condition excludes objects that have stacking-context style but don't actually
+ // create stacking contexts (e.g. SVG objects having opacity).
+ object.hasLayer()
+ // For now we already have property isolation for LayoutView by FrameView paint properties.
+ // TODO(wangxianzhu): Revisit this for root-layer-scrolls.
+ && !object.isLayoutView()
+ && (object.isOutOfFlowPositioned() || object.styleRef().isStackingContext());
+}
+
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
if (!object.isBoxModelObject())
- return false;
+ return NoNode;
// TODO(trchen): Eliminate PaintLayer dependency.
PaintLayer* layer = toLayoutBoxModelObject(object).layer();
- if (!layer || !layer->paintsWithTransform(GlobalPaintNormalPhase))
- return false;
-
- if (context.current.paintOffset == LayoutPoint())
- return false;
+ if (!layer)
+ return NoNode;
+
+ if (context.current.paintOffset == LayoutPoint() || !layer->paintsWithTransform(GlobalPaintNormalPhase)) {
+ if (isPaintPropertyIsolationBoundary(object)) {
+ // Will create a no-op paintOffsetTranslation node, if the object is a isolation boundary
+ // and it won't create other transform nodes.
+ if (!object.styleRef().hasTransform() && !object.styleRef().hasPerspective())
+ return IsolationNode;
+ }
+ return NoNode;
+ }
// 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
@@ -119,7 +164,7 @@ bool PaintPropertyTreeBuilder::computePaintOffsetTranslation(const LayoutObject&
value.matrix.translate(roundedPaintOffset.x(), roundedPaintOffset.y());
context.current.paintOffset = fractionalPaintOffset;
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -141,7 +186,7 @@ static FloatPoint3D transformOrigin(const LayoutBox& box)
style.transformOriginZ());
}
-bool PaintPropertyTreeBuilder::computeTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
if (object.isSVG() && !object.isSVGRoot()) {
// SVG does not use paint offset internally.
@@ -153,21 +198,21 @@ bool PaintPropertyTreeBuilder::computeTransform(const LayoutObject& object, Pain
// FIXME(pdr): Refactor this so all non-root SVG objects use the same transform function.
const AffineTransform& transform = object.isSVGForeignObject() ? object.localSVGTransform() : object.localToSVGParentTransform();
if (transform.isIdentity())
- return false;
+ return NoNode;
value.matrix = transform;
// The origin is included in the local transform, so leave origin empty.
- return true;
+ return NormalNode;
}
const ComputedStyle& style = object.styleRef();
if (!object.isBox() || !style.hasTransform())
- return false;
+ return NoNode;
style.applyTransform(value.matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin,
ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties);
value.origin = transformOrigin(toLayoutBox(object));
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -179,13 +224,19 @@ void PaintPropertyTreeBuilder::updateTransform(const LayoutObject& object, Paint
&computeTransform>(object, context, context.current.transform);
}
-bool PaintPropertyTreeBuilder::computeEffect(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, EffectPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeEffect(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, EffectPaintPropertyNode::Value& value)
{
- if (!object.styleRef().hasOpacity())
- return false;
+ if (!object.styleRef().hasOpacity()) {
+ if (isPaintPropertyIsolationBoundary(object)) {
+ // Will create a no-op effect node if the object is a isolation boundary.
+ value = 1.0f;
+ return IsolationNode;
+ }
+ return NoNode;
+ }
value = object.styleRef().opacity();
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -197,10 +248,11 @@ void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPro
&computeEffect>(object, context, context.currentEffect);
}
-bool PaintPropertyTreeBuilder::computeCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
{
if (!object.hasClip())
- return false;
+ return NoNode;
+
DCHECK(object.canContainAbsolutePositionObjects());
// Create clip node for descendants that are not fixed position.
@@ -209,7 +261,7 @@ bool PaintPropertyTreeBuilder::computeCssClip(const LayoutObject& object, PaintP
// at updateOutOfFlowContext() step.
LayoutRect clipRect = toLayoutBox(object).clipRect(context.current.paintOffset);
value = FloatRoundedRect(FloatRect(clipRect));
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -236,22 +288,22 @@ void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& o
}
// TODO(trchen): Remove this once we bake the paint offset into frameRect.
-bool PaintPropertyTreeBuilder::computeScrollbarPaintOffset(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeScrollbarPaintOffset(const LayoutObject& object, const PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset);
if (roundedPaintOffset == IntPoint())
- return false;
+ return NoNode;
if (!object.isBoxModelObject())
- return false;
+ return NoNode;
PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(object).getScrollableArea();
if (!scrollableArea)
- return false;
+ return NoNode;
if (!scrollableArea->horizontalScrollbar() && !scrollableArea->verticalScrollbar())
- return false;
+ return NoNode;
value.matrix.translate(roundedPaintOffset.x(), roundedPaintOffset.y());
- return true;
+ return NormalNode;
}
// TODO(trchen): Remove this once we bake the paint offset into frameRect.
@@ -277,39 +329,47 @@ void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(const LayoutObject& ob
TransformPaintPropertyNode::create(paintOffset, context.current.transform));
}
-bool PaintPropertyTreeBuilder::computeOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
{
- const LayoutBox& box = toLayoutBox(object);
-
- // The <input> elements can't have contents thus CSS overflow property doesn't apply.
- // However for layout purposes we do generate child layout objects for them, e.g. button label.
- // We should clip the overflow from those children. This is called control clip and we
- // technically treat them like overflow clip.
- LayoutRect clipRect;
- if (box.hasControlClip())
- clipRect = box.controlClipRect(context.current.paintOffset);
- else if (box.hasOverflowClip())
- clipRect = box.overflowClipRect(context.current.paintOffset);
- else
- return false;
+ if (object.isBox()) {
+ 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.
+ if (box.hasControlClip()) {
+ value = FloatRoundedRect(FloatRect(box.controlClipRect(context.current.paintOffset)));
+ return NormalNode;
+ }
+ if (box.hasOverflowClip()) {
+ value = FloatRoundedRect(FloatRect(box.overflowClipRect(context.current.paintOffset)));
+ return NormalNode;
+ }
+ }
- value = FloatRoundedRect(FloatRect(clipRect));
- return true;
+ if (isPaintPropertyIsolationBoundary(object)) {
+ // Will create a no-op overflowClip node, if the object is an isolation boundary
+ // and it hasn't create a cssClip node.
+ if (!object.hasClip()) {
+ value = FloatRoundedRect(LayoutRect::infiniteIntRect());
+ return IsolationNode;
+ }
+ }
+ return NoNode;
}
void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
- if (!object.isBox())
- return;
- const LayoutBox& box = toLayoutBox(object);
-
// 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.hasControlClip() || box.hasOverflowClip()) && box.styleRef().hasBorderRadius()) {
- auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
- LayoutRect(context.current.paintOffset, box.size()));
- borderRadiusClip = ClipPaintPropertyNode::create(context.current.transform, innerBorder, context.current.clip);
- context.current.clip = borderRadiusClip.get();
+ if (object.isBox()) {
+ const LayoutBox& box = toLayoutBox(object);
+ if ((box.hasControlClip() || box.hasOverflowClip()) && box.styleRef().hasBorderRadius()) {
+ auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
+ LayoutRect(context.current.paintOffset, box.size()));
+ borderRadiusClip = ClipPaintPropertyNode::create(context.current.transform, innerBorder, context.current.clip);
+ context.current.clip = borderRadiusClip.get();
+ }
}
if (ClipPaintPropertyNode* node = updateObjectPaintProperty<
@@ -329,15 +389,15 @@ static FloatPoint perspectiveOrigin(const LayoutBox& box)
floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height()));
}
-bool PaintPropertyTreeBuilder::computePerspective(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computePerspective(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
const ComputedStyle& style = object.styleRef();
if (!object.isBox() || !style.hasPerspective())
- return false;
+ return NoNode;
value.matrix.applyPerspective(style.perspective());
value.origin = perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.current.paintOffset);
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updatePerspective(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -349,10 +409,10 @@ void PaintPropertyTreeBuilder::updatePerspective(const LayoutObject& object, Pai
&computePerspective>(object, context, context.current.transform);
}
-bool PaintPropertyTreeBuilder::computeSvgLocalToBorderBoxTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeSvgLocalToBorderBoxTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
if (!object.isSVGRoot())
- return false;
+ return NoNode;
AffineTransform transformToBorderBox = SVGRootPainter(toLayoutSVGRoot(object)).transformToPixelSnappedBorderBox(context.current.paintOffset);
@@ -361,11 +421,11 @@ bool PaintPropertyTreeBuilder::computeSvgLocalToBorderBoxTransform(const LayoutO
context.current.paintOffset = LayoutPoint();
if (transformToBorderBox.isIdentity())
- return false;
+ return NoNode;
value.matrix = transformToBorderBox;
context.current.paintOffset = LayoutPoint();
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -377,19 +437,19 @@ void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform(const LayoutOb
&computeSvgLocalToBorderBoxTransform>(object, context, context.current.transform);
}
-bool PaintPropertyTreeBuilder::computeScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, TransformPaintPropertyNode::Value& value)
{
if (!object.isBoxModelObject() || !object.hasOverflowClip())
- return false;
+ return NoNode;
PaintLayer* layer = toLayoutBoxModelObject(object).layer();
ASSERT(layer);
DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset();
if (scrollOffset.isZero() && !layer->scrollsOverflow())
- return false;
+ return NoNode;
value.matrix.translate(-scrollOffset.width(), -scrollOffset.height());
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -402,7 +462,7 @@ void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& objec
}
// Returns true if we need to create a cssClip node for fixed position.
-bool PaintPropertyTreeBuilder::computeOutOfFlowContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
+PaintPropertyTreeBuilder::PaintPropertyNodeType PaintPropertyTreeBuilder::computeOutOfFlowContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context, ClipPaintPropertyNode::Value& value)
{
if (object.canContainAbsolutePositionObjects()) {
context.absolutePosition = context.current;
@@ -413,11 +473,11 @@ bool PaintPropertyTreeBuilder::computeOutOfFlowContext(const LayoutObject& objec
// paint properties for rootLayerScrolls.
if (!object.isLayoutView() && object.canContainFixedPositionObjects()) {
context.fixedPosition = context.current;
- return false;
+ return NoNode;
}
if (!object.objectPaintProperties() || !object.objectPaintProperties()->cssClip())
- return false;
+ return NoNode;
// CSS clip applies to all descendants, even if this object is not a containing block
// ancestor of the descendant. It is okay for absolute-position descendants because
@@ -430,11 +490,11 @@ bool PaintPropertyTreeBuilder::computeOutOfFlowContext(const LayoutObject& objec
// context has exactly the same clip. Reuse if possible.
if (context.fixedPosition.clip == cssClip->parent()) {
context.fixedPosition.clip = cssClip;
- return false;
+ return NoNode;
}
value = cssClip->clipRect();
- return true;
+ return NormalNode;
}
void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
@@ -496,6 +556,20 @@ static void deriveBorderBoxFromContainerContext(const LayoutObject& object, Pain
}
}
+#if DCHECK_IS_ON()
+void PaintPropertyTreeBuilder::checkPropertyIsolationBoundaryForStackingContext(const LayoutObject& object)
+{
+ if (!isPaintPropertyIsolationBoundary(object))
+ return;
+
+ ObjectPaintProperties* properties = object.objectPaintProperties();
+ DCHECK(properties);
+ DCHECK(properties->paintOffsetTranslation() || properties->transform() || properties->perspective());
+ DCHECK(properties->effect());
+ DCHECK(properties->cssClip() || properties->overflowClip());
+}
+#endif
+
void PaintPropertyTreeBuilder::buildTreeNodes(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
if (!object.isBoxModelObject() && !object.isSVG())
@@ -516,6 +590,9 @@ void PaintPropertyTreeBuilder::buildTreeNodes(const LayoutObject& object, PaintP
updateSvgLocalToBorderBoxTransform(object, context);
updateScrollTranslation(object, context);
updateOutOfFlowContext(object, context);
+#if DCHECK_IS_ON()
+ checkPropertyIsolationBoundaryForStackingContext(object);
+#endif
}
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698