| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/paint/PaintPropertyTreeBuilder.h" | |
| 6 | |
| 7 #include "core/frame/FrameView.h" | |
| 8 #include "core/layout/LayoutPart.h" | |
| 9 #include "core/layout/LayoutView.h" | |
| 10 #include "core/paint/ObjectPaintProperties.h" | |
| 11 #include "core/paint/PaintLayer.h" | |
| 12 #include "platform/graphics/paint/ClipPaintPropertyNode.h" | |
| 13 #include "platform/graphics/paint/TransformPaintPropertyNode.h" | |
| 14 #include "platform/transforms/TransformationMatrix.h" | |
| 15 | |
| 16 namespace blink { | |
| 17 | |
| 18 // The context for layout tree walk. | |
| 19 // The walk will be done in the primary tree order (= DOM order), thus the conte
xt will also be | |
| 20 // responsible for bookkeeping tree state in other order, for example, the most
recent position | |
| 21 // container seen. | |
| 22 struct PaintPropertyTreeBuilderContext { | |
| 23 PaintPropertyTreeBuilderContext() | |
| 24 : currentTransform(nullptr) | |
| 25 , currentClip(nullptr) | |
| 26 , transformForAbsolutePosition(nullptr) | |
| 27 , clipForAbsolutePosition(nullptr) | |
| 28 , transformForFixedPosition(nullptr) | |
| 29 , clipForFixedPosition(nullptr) | |
| 30 , currentEffect(nullptr) { } | |
| 31 | |
| 32 // The combination of a transform and paint offset describes a linear space. | |
| 33 // When a layout object recur to its children, the main context is expected
to refer | |
| 34 // the object's border box, then the callee will derive its own border box b
y translating | |
| 35 // the space with its own layout location. | |
| 36 TransformPaintPropertyNode* currentTransform; | |
| 37 LayoutPoint paintOffset; | |
| 38 // The clip node describes the accumulated raster clip for the current subtr
ee. | |
| 39 // Note that the computed raster region in canvas space for a clip node is i
ndependent from | |
| 40 // the transform and paint offset above. Also the actual raster region may b
e affected | |
| 41 // by layerization and occlusion tracking. | |
| 42 ClipPaintPropertyNode* currentClip; | |
| 43 | |
| 44 // Separate context for out-of-flow positioned and fixed positioned elements
are needed | |
| 45 // because they don't use DOM parent as their containing block. | |
| 46 // These additional contexts normally pass through untouched, and are only c
opied from | |
| 47 // the main context when the current element serves as the containing block
of corresponding | |
| 48 // positioned descendants. | |
| 49 // Overflow clips are also inherited by containing block tree instead of DOM
tree, thus they | |
| 50 // are included in the additional context too. | |
| 51 TransformPaintPropertyNode* transformForAbsolutePosition; | |
| 52 LayoutPoint paintOffsetForAbsolutePosition; | |
| 53 ClipPaintPropertyNode* clipForAbsolutePosition; | |
| 54 | |
| 55 TransformPaintPropertyNode* transformForFixedPosition; | |
| 56 LayoutPoint paintOffsetForFixedPosition; | |
| 57 ClipPaintPropertyNode* clipForFixedPosition; | |
| 58 | |
| 59 // The effect hierarchy is applied by the stacking context tree. It is guara
nteed that every | |
| 60 // DOM descendant is also a stacking context descendant. Therefore, we don't
need extra | |
| 61 // bookkeeping for effect nodes and can generate the effect tree from a DOM-
order traversal. | |
| 62 EffectPaintPropertyNode* currentEffect; | |
| 63 }; | |
| 64 | |
| 65 void PaintPropertyTreeBuilder::buildPropertyTrees(FrameView& rootFrame) | |
| 66 { | |
| 67 walk(rootFrame, PaintPropertyTreeBuilderContext()); | |
| 68 } | |
| 69 | |
| 70 void PaintPropertyTreeBuilder::walk(FrameView& frameView, const PaintPropertyTre
eBuilderContext& context) | |
| 71 { | |
| 72 PaintPropertyTreeBuilderContext localContext(context); | |
| 73 | |
| 74 // TODO(pdr): Creating paint properties for FrameView here will not be | |
| 75 // needed once settings()->rootLayerScrolls() is enabled. | |
| 76 // TODO(pdr): Make this conditional on the rootLayerScrolls setting. | |
| 77 | |
| 78 TransformationMatrix frameTranslate; | |
| 79 frameTranslate.translate(frameView.x() + context.paintOffset.x(), frameView.
y() + context.paintOffset.y()); | |
| 80 RefPtr<TransformPaintPropertyNode> newTransformNodeForPreTranslation = Trans
formPaintPropertyNode::create(frameTranslate, FloatPoint3D(), context.currentTra
nsform); | |
| 81 localContext.transformForFixedPosition = newTransformNodeForPreTranslation.g
et(); | |
| 82 localContext.paintOffsetForFixedPosition = LayoutPoint(); | |
| 83 | |
| 84 FloatRoundedRect contentClip(IntRect(IntPoint(), frameView.visibleContentSiz
e())); | |
| 85 RefPtr<ClipPaintPropertyNode> newClipNodeForContentClip = ClipPaintPropertyN
ode::create(newTransformNodeForPreTranslation.get(), contentClip, localContext.c
urrentClip); | |
| 86 localContext.currentClip = localContext.clipForAbsolutePosition = localConte
xt.clipForFixedPosition = newClipNodeForContentClip.get(); | |
| 87 | |
| 88 DoubleSize scrollOffset = frameView.scrollOffsetDouble(); | |
| 89 TransformationMatrix frameScroll; | |
| 90 frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); | |
| 91 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = Tr
ansformPaintPropertyNode::create(frameScroll, FloatPoint3D(), newTransformNodeFo
rPreTranslation); | |
| 92 localContext.currentTransform = localContext.transformForAbsolutePosition =
newTransformNodeForScrollTranslation.get(); | |
| 93 localContext.paintOffset = localContext.paintOffsetForAbsolutePosition = Lay
outPoint(); | |
| 94 | |
| 95 frameView.setPreTranslation(newTransformNodeForPreTranslation.release()); | |
| 96 frameView.setScrollTranslation(newTransformNodeForScrollTranslation.release(
)); | |
| 97 frameView.setContentClip(newClipNodeForContentClip.release()); | |
| 98 | |
| 99 if (LayoutView* layoutView = frameView.layoutView()) | |
| 100 walk(*layoutView, localContext); | |
| 101 } | |
| 102 | |
| 103 static void deriveBorderBoxFromContainerContext(const LayoutObject& object, Pain
tPropertyTreeBuilderContext& context) | |
| 104 { | |
| 105 if (!object.isBoxModelObject()) | |
| 106 return; | |
| 107 | |
| 108 const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object); | |
| 109 | |
| 110 // TODO(trchen): There is some insanity going on with tables. Double check r
esults. | |
| 111 switch (object.styleRef().position()) { | |
| 112 case StaticPosition: | |
| 113 break; | |
| 114 case RelativePosition: | |
| 115 context.paintOffset += boxModelObject.offsetForInFlowPosition(); | |
| 116 break; | |
| 117 case AbsolutePosition: | |
| 118 context.currentTransform = context.transformForAbsolutePosition; | |
| 119 context.paintOffset = context.paintOffsetForAbsolutePosition; | |
| 120 context.currentClip = context.clipForAbsolutePosition; | |
| 121 break; | |
| 122 case StickyPosition: | |
| 123 context.paintOffset += boxModelObject.offsetForInFlowPosition(); | |
| 124 break; | |
| 125 case FixedPosition: | |
| 126 context.currentTransform = context.transformForFixedPosition; | |
| 127 context.paintOffset = context.paintOffsetForFixedPosition; | |
| 128 context.currentClip = context.clipForFixedPosition; | |
| 129 break; | |
| 130 default: | |
| 131 ASSERT_NOT_REACHED(); | |
| 132 } | |
| 133 if (boxModelObject.isBox()) | |
| 134 context.paintOffset += toLayoutBox(boxModelObject).locationOffset(); | |
| 135 } | |
| 136 | |
| 137 static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeed
ed(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) | |
| 138 { | |
| 139 bool shouldCreatePaintOffsetTranslationNode = false; | |
| 140 if (object.isSVGRoot()) { | |
| 141 // SVG doesn't use paint offset internally so emit a paint offset at the
html->svg boundary. | |
| 142 shouldCreatePaintOffsetTranslationNode = true; | |
| 143 } else if (object.isBoxModelObject()) { | |
| 144 // TODO(trchen): Eliminate PaintLayer dependency. | |
| 145 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | |
| 146 shouldCreatePaintOffsetTranslationNode = layer && layer->paintsWithTrans
form(GlobalPaintNormalPhase); | |
| 147 } | |
| 148 | |
| 149 if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslat
ionNode) | |
| 150 return nullptr; | |
| 151 | |
| 152 RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation
= TransformPaintPropertyNode::create( | |
| 153 TransformationMatrix().translate(context.paintOffset.x(), context.paintO
ffset.y()), | |
| 154 FloatPoint3D(), context.currentTransform); | |
| 155 context.currentTransform = newTransformNodeForPaintOffsetTranslation.get(); | |
| 156 context.paintOffset = LayoutPoint(); | |
| 157 return newTransformNodeForPaintOffsetTranslation.release(); | |
| 158 } | |
| 159 | |
| 160 static FloatPoint3D transformOrigin(const LayoutBox& box) | |
| 161 { | |
| 162 const ComputedStyle& style = box.styleRef(); | |
| 163 FloatSize borderBoxSize(box.size()); | |
| 164 return FloatPoint3D( | |
| 165 floatValueForLength(style.transformOriginX(), borderBoxSize.width()), | |
| 166 floatValueForLength(style.transformOriginY(), borderBoxSize.height()), | |
| 167 style.transformOriginZ()); | |
| 168 } | |
| 169 | |
| 170 static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const Layo
utObject& object, PaintPropertyTreeBuilderContext& context) | |
| 171 { | |
| 172 if (object.isSVG() && !object.isSVGRoot()) { | |
| 173 const AffineTransform& transform = object.localToParentTransform(); | |
| 174 if (transform.isIdentity()) | |
| 175 return nullptr; | |
| 176 | |
| 177 // SVG's transform origin is baked into the localToParentTransform. | |
| 178 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = Transf
ormPaintPropertyNode::create( | |
| 179 transform, FloatPoint3D(0, 0, 0), context.currentTransform); | |
| 180 context.currentTransform = newTransformNodeForTransform.get(); | |
| 181 return newTransformNodeForTransform.release(); | |
| 182 } | |
| 183 | |
| 184 const ComputedStyle& style = object.styleRef(); | |
| 185 if (!object.isBox() || !style.hasTransform()) | |
| 186 return nullptr; | |
| 187 | |
| 188 ASSERT(context.paintOffset == LayoutPoint()); | |
| 189 | |
| 190 TransformationMatrix matrix; | |
| 191 style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::Excl
udeTransformOrigin, | |
| 192 ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTrans
formProperties); | |
| 193 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformP
aintPropertyNode::create( | |
| 194 matrix, transformOrigin(toLayoutBox(object)), context.currentTransform); | |
| 195 context.currentTransform = newTransformNodeForTransform.get(); | |
| 196 return newTransformNodeForTransform.release(); | |
| 197 } | |
| 198 | |
| 199 static PassRefPtr<EffectPaintPropertyNode> createEffectIfNeeded(const LayoutObje
ct& object, PaintPropertyTreeBuilderContext& context) | |
| 200 { | |
| 201 const ComputedStyle& style = object.styleRef(); | |
| 202 if (!style.hasOpacity()) | |
| 203 return nullptr; | |
| 204 RefPtr<EffectPaintPropertyNode> newEffectNode = EffectPaintPropertyNode::cre
ate(style.opacity(), context.currentEffect); | |
| 205 context.currentEffect = newEffectNode.get(); | |
| 206 return newEffectNode.release(); | |
| 207 } | |
| 208 | |
| 209 static PassRefPtr<ClipPaintPropertyNode> createOverflowClipIfNeeded(const Layout
Object& object, PaintPropertyTreeBuilderContext& context) | |
| 210 { | |
| 211 if (!object.isBox()) | |
| 212 return nullptr; | |
| 213 const LayoutBox& box = toLayoutBox(object); | |
| 214 | |
| 215 // The <input> elements can't have contents thus CSS overflow property doesn
't apply. | |
| 216 // However for layout purposes we do generate child layout objects for them,
e.g. button label. | |
| 217 // We should clip the overflow from those children. This is called control c
lip and we | |
| 218 // technically treat them like overflow clip. | |
| 219 LayoutRect clipRect; | |
| 220 if (box.hasControlClip()) | |
| 221 clipRect = box.controlClipRect(context.paintOffset); | |
| 222 else if (box.hasOverflowClip()) | |
| 223 clipRect = box.overflowClipRect(context.paintOffset); | |
| 224 else | |
| 225 return nullptr; | |
| 226 | |
| 227 RefPtr<ClipPaintPropertyNode> newClipNodeForBorderRadiusClip; | |
| 228 const ComputedStyle& style = box.styleRef(); | |
| 229 if (style.hasBorderRadius()) { | |
| 230 newClipNodeForBorderRadiusClip = ClipPaintPropertyNode::create( | |
| 231 context.currentTransform, | |
| 232 style.getRoundedInnerBorderFor(LayoutRect(context.paintOffset, box.s
ize())), | |
| 233 context.currentClip); | |
| 234 } | |
| 235 | |
| 236 RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = ClipPaintProperty
Node::create( | |
| 237 context.currentTransform, | |
| 238 FloatRoundedRect(FloatRect(clipRect)), | |
| 239 newClipNodeForBorderRadiusClip ? newClipNodeForBorderRadiusClip.release(
) : context.currentClip); | |
| 240 context.currentClip = newClipNodeForOverflowClip.get(); | |
| 241 return newClipNodeForOverflowClip.release(); | |
| 242 } | |
| 243 | |
| 244 static FloatPoint perspectiveOrigin(const LayoutBox& box) | |
| 245 { | |
| 246 const ComputedStyle& style = box.styleRef(); | |
| 247 FloatSize borderBoxSize(box.size()); | |
| 248 return FloatPoint( | |
| 249 floatValueForLength(style.perspectiveOriginX(), borderBoxSize.width()), | |
| 250 floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height()))
; | |
| 251 } | |
| 252 | |
| 253 static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const La
youtObject& object, PaintPropertyTreeBuilderContext& context) | |
| 254 { | |
| 255 const ComputedStyle& style = object.styleRef(); | |
| 256 if (!object.isBox() || !style.hasPerspective()) | |
| 257 return nullptr; | |
| 258 | |
| 259 RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = Transfor
mPaintPropertyNode::create( | |
| 260 TransformationMatrix().applyPerspective(style.perspective()), | |
| 261 perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffse
t), context.currentTransform); | |
| 262 context.currentTransform = newTransformNodeForPerspective.get(); | |
| 263 return newTransformNodeForPerspective.release(); | |
| 264 } | |
| 265 | |
| 266 static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(co
nst LayoutObject& object, PaintPropertyTreeBuilderContext& context) | |
| 267 { | |
| 268 if (!object.isBoxModelObject() || !object.hasOverflowClip()) | |
| 269 return nullptr; | |
| 270 | |
| 271 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | |
| 272 ASSERT(layer); | |
| 273 DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); | |
| 274 if (scrollOffset.isZero() && !layer->scrollsOverflow()) | |
| 275 return nullptr; | |
| 276 | |
| 277 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = Tr
ansformPaintPropertyNode::create( | |
| 278 TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.he
ight()), | |
| 279 FloatPoint3D(), context.currentTransform); | |
| 280 context.currentTransform = newTransformNodeForScrollTranslation.get(); | |
| 281 return newTransformNodeForScrollTranslation.release(); | |
| 282 } | |
| 283 | |
| 284 static void updateOutOfFlowContext(const LayoutObject& object, PaintPropertyTree
BuilderContext& context) | |
| 285 { | |
| 286 // At the html->svg boundary (see: createPaintOffsetTranslationIfNeeded) the
currentTransform is | |
| 287 // up-to-date for all children of the svg root element. Additionally, inside
SVG, all positioning | |
| 288 // uses transforms. Therefore, we only need to check createdNewTransform and
isSVGRoot() to | |
| 289 // ensure out-of-flow and fixed positioning is correct at the svg->html boun
dary. | |
| 290 | |
| 291 if (object.canContainAbsolutePositionObjects()) { | |
| 292 context.transformForAbsolutePosition = context.currentTransform; | |
| 293 context.paintOffsetForAbsolutePosition = context.paintOffset; | |
| 294 context.clipForAbsolutePosition = context.currentClip; | |
| 295 } | |
| 296 | |
| 297 // TODO(pdr): Remove the !object.isLayoutView() condition when removing Fram
eView | |
| 298 // paint properties for rootLayerScrolls. | |
| 299 if (!object.isLayoutView() && object.canContainFixedPositionObjects()) { | |
| 300 context.transformForFixedPosition = context.currentTransform; | |
| 301 context.paintOffsetForFixedPosition = context.paintOffset; | |
| 302 context.clipForFixedPosition = context.currentClip; | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 static PassOwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> recordTreeCon
textIfNeeded(LayoutObject& object, const PaintPropertyTreeBuilderContext& contex
t) | |
| 307 { | |
| 308 // Note: Currently only layer painter makes use of the pre-computed context. | |
| 309 // This condition may be loosened with no adverse effects beside memory use. | |
| 310 if (!object.hasLayer()) | |
| 311 return nullptr; | |
| 312 | |
| 313 OwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> recordedContext = ad
optPtr(new ObjectPaintProperties::LocalBorderBoxProperties); | |
| 314 recordedContext->paintOffset = context.paintOffset; | |
| 315 recordedContext->transform = context.currentTransform; | |
| 316 recordedContext->clip = context.currentClip; | |
| 317 recordedContext->effect = context.currentEffect; | |
| 318 return recordedContext.release(); | |
| 319 } | |
| 320 | |
| 321 void PaintPropertyTreeBuilder::walk(LayoutObject& object, const PaintPropertyTre
eBuilderContext& context) | |
| 322 { | |
| 323 PaintPropertyTreeBuilderContext localContext(context); | |
| 324 | |
| 325 deriveBorderBoxFromContainerContext(object, localContext); | |
| 326 RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation
= createPaintOffsetTranslationIfNeeded(object, localContext); | |
| 327 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = createTran
sformIfNeeded(object, localContext); | |
| 328 RefPtr<EffectPaintPropertyNode> newEffectNode = createEffectIfNeeded(object,
localContext); | |
| 329 OwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> newRecordedContext =
recordTreeContextIfNeeded(object, localContext); | |
| 330 RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = createOverflowCli
pIfNeeded(object, localContext); | |
| 331 // TODO(trchen): Insert flattening transform here, as specified by | |
| 332 // http://www.w3.org/TR/css3-transforms/#transform-style-property | |
| 333 RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPe
rspectiveIfNeeded(object, localContext); | |
| 334 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = cr
eateScrollTranslationIfNeeded(object, localContext); | |
| 335 updateOutOfFlowContext(object, localContext); | |
| 336 | |
| 337 if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransfor
m || newEffectNode || newClipNodeForOverflowClip || newTransformNodeForPerspecti
ve || newTransformNodeForScrollTranslation || newRecordedContext) { | |
| 338 OwnPtr<ObjectPaintProperties> updatedPaintProperties = ObjectPaintProper
ties::create( | |
| 339 newTransformNodeForPaintOffsetTranslation.release(), | |
| 340 newTransformNodeForTransform.release(), | |
| 341 newEffectNode.release(), | |
| 342 newClipNodeForOverflowClip.release(), | |
| 343 newTransformNodeForPerspective.release(), | |
| 344 newTransformNodeForScrollTranslation.release(), | |
| 345 newRecordedContext.release()); | |
| 346 object.setObjectPaintProperties(updatedPaintProperties.release()); | |
| 347 } else { | |
| 348 object.clearObjectPaintProperties(); | |
| 349 } | |
| 350 | |
| 351 for (LayoutObject* child = object.slowFirstChild(); child; child = child->ne
xtSibling()) { | |
| 352 if (child->isBoxModelObject() || child->isSVG()) | |
| 353 walk(*child, localContext); | |
| 354 } | |
| 355 | |
| 356 if (object.isLayoutPart()) { | |
| 357 Widget* widget = toLayoutPart(object).widget(); | |
| 358 if (widget && widget->isFrameView()) | |
| 359 walk(*toFrameView(widget), localContext); | |
| 360 // TODO(pdr): Investigate RemoteFrameView (crbug.com/579281). | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 } // namespace blink | |
| OLD | NEW |