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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..35e4e56bcf07d328d80b13c22998aa914494df75 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp |
@@ -0,0 +1,240 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+#include "core/paint/PaintPropertyTreeBuilder.h" |
+ |
+#include "core/frame/FrameView.h" |
+#include "core/layout/LayoutView.h" |
+#include "core/paint/ObjectPaintProperties.h" |
+#include "core/paint/PaintLayer.h" |
+#include "platform/graphics/paint/TransformPaintPropertyNode.h" |
+#include "platform/transforms/TransformationMatrix.h" |
+ |
+namespace blink { |
+ |
+// The context for layout tree walk. |
+// The walk will be done in the primary tree order (= DOM order), thus the context will also be |
+// responsible for bookkeeping tree state in other order, for example, the most recent position |
+// container seen. |
+struct PaintPropertyTreeBuilderContext { |
+ PaintPropertyTreeBuilderContext() : currentTransform(nullptr), transformForOutOfFlowPositioned(nullptr), transformForFixedPositioned(nullptr) { } |
+ |
+ // The combination of a transform and paint offset describes a linear space. |
+ // When a layout object recur to its children, the main context is expected to refer |
+ // the object's border box, then the callee will derive its own border box by translating |
+ // the space with its own layout location. |
+ TransformPaintPropertyNode* currentTransform; |
+ LayoutPoint paintOffset; |
+ |
+ // Separate context for out-of-flow positioned and fixed positioned elements are needed |
+ // because they don't use DOM parent as their positioning parent (i.e. containing block). |
+ // These additional contexts normally pass through untouched, and are only copied from |
+ // the main context when the current element serves as the positioning parent of corresponding |
+ // positioned descendants. |
+ TransformPaintPropertyNode* transformForOutOfFlowPositioned; |
+ LayoutPoint paintOffsetForOutOfFlowPositioned; |
+ |
+ TransformPaintPropertyNode* transformForFixedPositioned; |
+ LayoutPoint paintOffsetForFixedPositioned; |
+}; |
+ |
+void PaintPropertyTreeBuilder::buildPropertyTrees(FrameView& rootFrame) |
+{ |
+ walk(rootFrame, PaintPropertyTreeBuilderContext()); |
+} |
+ |
+void PaintPropertyTreeBuilder::walk(FrameView& frameView, const PaintPropertyTreeBuilderContext& context) |
+{ |
+ PaintPropertyTreeBuilderContext localContext(context); |
+ |
+ TransformationMatrix frameTranslate; |
+ frameTranslate.translate(frameView.x(), frameView.y()); |
+ // The frame owner applies paint offset already. |
+ // This assumption may change in the future. |
+ ASSERT(context.paintOffset == LayoutPoint()); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForPreTranslation = TransformPaintPropertyNode::create(frameTranslate, FloatPoint3D(), context.currentTransform); |
+ localContext.transformForFixedPositioned = newTransformNodeForPreTranslation.get(); |
+ localContext.paintOffsetForFixedPositioned = LayoutPoint(); |
+ |
+ // This is going away in favor of Settings::rootLayerScrolls. |
+ DoubleSize scrollOffset = frameView.scrollOffsetDouble(); |
+ TransformationMatrix frameScroll; |
+ frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = TransformPaintPropertyNode::create(frameScroll, FloatPoint3D(), newTransformNodeForPreTranslation); |
+ localContext.currentTransform = localContext.transformForOutOfFlowPositioned = newTransformNodeForScrollTranslation.get(); |
+ localContext.paintOffset = localContext.paintOffsetForOutOfFlowPositioned = LayoutPoint(); |
+ |
+ frameView.setPreTranslation(newTransformNodeForPreTranslation.release()); |
+ frameView.setScrollTranslation(newTransformNodeForScrollTranslation.release()); |
+ |
+ if (LayoutView* layoutView = frameView.layoutView()) |
+ walk(*layoutView, localContext); |
+} |
+ |
+static void deriveBorderBoxFromContainerContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ // TODO(trchen): There is some insanity going on with tables. Double check results. |
+ switch (object.styleRef().position()) { |
+ case StaticPosition: |
+ break; |
+ case RelativePosition: |
+ context.paintOffset += object.offsetForInFlowPosition(); |
+ break; |
+ case AbsolutePosition: |
+ context.currentTransform = context.transformForOutOfFlowPositioned; |
+ context.paintOffset = context.paintOffsetForOutOfFlowPositioned; |
+ break; |
+ case StickyPosition: |
+ context.paintOffset += object.offsetForInFlowPosition(); |
+ break; |
+ case FixedPosition: |
+ context.currentTransform = context.transformForFixedPositioned; |
+ context.paintOffset = context.paintOffsetForFixedPositioned; |
+ break; |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ } |
+ if (object.isBox()) |
+ context.paintOffset += toLayoutBox(object).locationOffset(); |
+} |
+ |
+static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ // TODO(trchen): Eliminate PaintLayer dependency. |
+ bool shouldCreatePaintOffsetTranslationNode = object.layer() && object.layer()->paintsWithTransform(GlobalPaintNormalPhase); |
+ |
+ if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslationNode) |
+ return nullptr; |
+ |
+ TransformationMatrix matrix; |
+ matrix.translate(context.paintOffset.x(), context.paintOffset.y()); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = TransformPaintPropertyNode::create( |
+ TransformationMatrix().translate(context.paintOffset.x(), context.paintOffset.y()), |
+ FloatPoint3D(), context.currentTransform); |
+ context.currentTransform = newTransformNodeForPaintOffsetTranslation.get(); |
+ context.paintOffset = LayoutPoint(); |
+ return newTransformNodeForPaintOffsetTranslation.release(); |
+} |
+ |
+static FloatPoint3D transformOrigin(const LayoutBox& box) |
+{ |
+ const ComputedStyle& style = box.styleRef(); |
+ FloatSize borderBoxSize(box.size()); |
+ return FloatPoint3D( |
+ floatValueForLength(style.transformOriginX(), borderBoxSize.width()), |
+ floatValueForLength(style.transformOriginY(), borderBoxSize.height()), |
+ style.transformOriginZ()); |
+} |
+ |
+static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ const ComputedStyle& style = object.styleRef(); |
+ if (!object.isBox() || !style.hasTransform()) |
+ return nullptr; |
+ |
+ ASSERT(context.paintOffset == LayoutPoint()); |
+ |
+ TransformationMatrix matrix; |
+ style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, |
+ ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create( |
+ matrix, transformOrigin(toLayoutBox(object)), context.currentTransform); |
+ context.currentTransform = newTransformNodeForTransform.get(); |
+ return newTransformNodeForTransform.release(); |
+} |
+ |
+static FloatPoint perspectiveOrigin(const LayoutBox& box) |
+{ |
+ const ComputedStyle& style = box.styleRef(); |
+ FloatSize borderBoxSize(box.size()); |
+ return FloatPoint( |
+ floatValueForLength(style.perspectiveOriginX(), borderBoxSize.width()), |
+ floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height())); |
+} |
+ |
+static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ const ComputedStyle& style = object.styleRef(); |
+ if (!object.isBox() || !style.hasPerspective()) |
+ return nullptr; |
+ |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = TransformPaintPropertyNode::create( |
+ TransformationMatrix().applyPerspective(style.perspective()), |
+ perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffset), context.currentTransform); |
+ context.currentTransform = newTransformNodeForPerspective.get(); |
+ |
+ return newTransformNodeForPerspective.release(); |
+} |
+ |
+static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ if (!object.hasOverflowClip()) |
+ return nullptr; |
+ |
+ PaintLayer* layer = object.layer(); |
+ ASSERT(layer); |
+ DoubleSize scrollOffset = layer->scrollableArea()->scrollOffset(); |
+ if (scrollOffset.isZero() && !layer->scrollsOverflow()) |
+ return nullptr; |
+ |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = TransformPaintPropertyNode::create( |
+ TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()), |
+ FloatPoint3D(), context.currentTransform); |
+ context.currentTransform = newTransformNodeForScrollTranslation.get(); |
+ return newTransformNodeForScrollTranslation.release(); |
+} |
+ |
+static void updateOutOfFlowContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) |
+{ |
+ const ComputedStyle& style = object.styleRef(); |
+ bool hasTransform = object.isBox() && style.hasTransform(); |
+ if (style.position() != StaticPosition || hasTransform) { |
+ context.transformForOutOfFlowPositioned = context.currentTransform; |
+ context.paintOffsetForOutOfFlowPositioned = context.paintOffset; |
+ } |
+ if (hasTransform) { |
+ context.transformForFixedPositioned = context.currentTransform; |
+ context.paintOffsetForFixedPositioned = context.paintOffset; |
+ } |
+} |
+ |
+void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPropertyTreeBuilderContext& context) |
+{ |
+ ASSERT(object.isBox() != object.isLayoutInline()); // Either or. |
+ |
+ PaintPropertyTreeBuilderContext localContext(context); |
+ |
+ deriveBorderBoxFromContainerContext(object, localContext); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = createPaintOffsetTranslationIfNeeded(object, localContext); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = createTransformIfNeeded(object, localContext); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPerspectiveIfNeeded(object, localContext); |
+ RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = createScrollTranslationIfNeeded(object, localContext); |
+ updateOutOfFlowContext(object, localContext); |
+ |
+ if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransform || newTransformNodeForPerspective || newTransformNodeForScrollTranslation) { |
+ ObjectPaintProperties& properties = object.ensureObjectPaintProperties(); |
+ properties.setPaintOffsetTranslation(newTransformNodeForPaintOffsetTranslation.release()); |
+ properties.setTransform(newTransformNodeForTransform.release()); |
+ properties.setPerspective(newTransformNodeForPerspective.release()); |
+ properties.setScrollTranslation(newTransformNodeForScrollTranslation.release()); |
+ } else { |
+ object.clearObjectPaintProperties(); |
+ } |
+ |
+ // TODO(trchen): Walk subframes for LayoutFrame. |
+ |
+ // TODO(trchen): Implement SVG walk. |
+ if (object.isSVGRoot()) { |
+ return; |
+ } |
+ |
+ for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) { |
+ if (child->isText()) |
+ continue; |
+ walk(toLayoutBoxModelObject(*child), localContext); |
+ } |
+} |
+ |
+} // namespace blink |