| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/paint/PaintPropertyTreeBuilder.h" | 5 #include "core/paint/PaintPropertyTreeBuilder.h" |
| 6 | 6 |
| 7 #include "core/frame/FrameView.h" | 7 #include "core/frame/FrameView.h" |
| 8 #include "core/frame/LocalFrame.h" | 8 #include "core/frame/LocalFrame.h" |
| 9 #include "core/frame/Settings.h" | 9 #include "core/frame/Settings.h" |
| 10 #include "core/layout/LayoutInline.h" | 10 #include "core/layout/LayoutInline.h" |
| 11 #include "core/layout/LayoutPart.h" | 11 #include "core/layout/LayoutPart.h" |
| 12 #include "core/layout/LayoutView.h" | 12 #include "core/layout/LayoutView.h" |
| 13 #include "core/layout/svg/LayoutSVGRoot.h" | 13 #include "core/layout/svg/LayoutSVGRoot.h" |
| 14 #include "core/paint/FindPropertiesNeedingUpdate.h" |
| 14 #include "core/paint/ObjectPaintProperties.h" | 15 #include "core/paint/ObjectPaintProperties.h" |
| 15 #include "core/paint/PaintLayer.h" | 16 #include "core/paint/PaintLayer.h" |
| 16 #include "core/paint/SVGRootPainter.h" | 17 #include "core/paint/SVGRootPainter.h" |
| 17 #include "platform/transforms/TransformationMatrix.h" | 18 #include "platform/transforms/TransformationMatrix.h" |
| 18 #include "wtf/PtrUtil.h" | 19 #include "wtf/PtrUtil.h" |
| 19 #include <memory> | 20 #include <memory> |
| 20 | 21 |
| 21 namespace blink { | 22 namespace blink { |
| 22 | 23 |
| 23 namespace { | 24 namespace { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 context.current.scroll = context.absolutePosition.scroll = | 66 context.current.scroll = context.absolutePosition.scroll = |
| 66 context.fixedPosition.scroll = rootScrollNode(); | 67 context.fixedPosition.scroll = rootScrollNode(); |
| 67 | 68 |
| 68 // Ensure scroll tree properties are reset. They will be rebuilt during the | 69 // Ensure scroll tree properties are reset. They will be rebuilt during the |
| 69 // tree walk. | 70 // tree walk. |
| 70 rootScrollNode()->clearMainThreadScrollingReasons(); | 71 rootScrollNode()->clearMainThreadScrollingReasons(); |
| 71 | 72 |
| 72 return context; | 73 return context; |
| 73 } | 74 } |
| 74 | 75 |
| 75 const TransformPaintPropertyNode* updateFrameViewPreTranslation( | 76 void updateFrameViewPreTranslation( |
| 76 FrameView& frameView, | 77 FrameView& frameView, |
| 77 PassRefPtr<const TransformPaintPropertyNode> parent, | 78 PassRefPtr<const TransformPaintPropertyNode> parent, |
| 78 const TransformationMatrix& matrix, | 79 const TransformationMatrix& matrix, |
| 79 const FloatPoint3D& origin) { | 80 const FloatPoint3D& origin) { |
| 80 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); | 81 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| 81 if (TransformPaintPropertyNode* existingPreTranslation = | 82 if (auto* existingPreTranslation = frameView.preTranslation()) { |
| 82 frameView.preTranslation()) | |
| 83 existingPreTranslation->update(std::move(parent), matrix, origin); | 83 existingPreTranslation->update(std::move(parent), matrix, origin); |
| 84 else | 84 } else { |
| 85 frameView.setPreTranslation( | 85 frameView.setPreTranslation( |
| 86 TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); | 86 TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); |
| 87 return frameView.preTranslation(); | 87 } |
| 88 } | 88 } |
| 89 | 89 |
| 90 const ClipPaintPropertyNode* updateFrameViewContentClip( | 90 void updateFrameViewContentClip( |
| 91 FrameView& frameView, | 91 FrameView& frameView, |
| 92 PassRefPtr<const ClipPaintPropertyNode> parent, | 92 PassRefPtr<const ClipPaintPropertyNode> parent, |
| 93 PassRefPtr<const TransformPaintPropertyNode> localTransformSpace, | 93 PassRefPtr<const TransformPaintPropertyNode> localTransformSpace, |
| 94 const FloatRoundedRect& clipRect) { | 94 const FloatRoundedRect& clipRect) { |
| 95 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); | 95 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| 96 if (ClipPaintPropertyNode* existingContentClip = frameView.contentClip()) | 96 if (auto* existingContentClip = frameView.contentClip()) { |
| 97 existingContentClip->update(std::move(parent), | 97 existingContentClip->update(std::move(parent), |
| 98 std::move(localTransformSpace), clipRect); | 98 std::move(localTransformSpace), clipRect); |
| 99 else | 99 } else { |
| 100 frameView.setContentClip(ClipPaintPropertyNode::create( | 100 frameView.setContentClip(ClipPaintPropertyNode::create( |
| 101 std::move(parent), std::move(localTransformSpace), clipRect)); | 101 std::move(parent), std::move(localTransformSpace), clipRect)); |
| 102 return frameView.contentClip(); | 102 } |
| 103 } | 103 } |
| 104 | 104 |
| 105 const TransformPaintPropertyNode* updateFrameViewScrollTranslation( | 105 void updateFrameViewScrollTranslation( |
| 106 FrameView& frameView, | 106 FrameView& frameView, |
| 107 PassRefPtr<const TransformPaintPropertyNode> parent, | 107 PassRefPtr<const TransformPaintPropertyNode> parent, |
| 108 const TransformationMatrix& matrix, | 108 const TransformationMatrix& matrix, |
| 109 const FloatPoint3D& origin) { | 109 const FloatPoint3D& origin) { |
| 110 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); | 110 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| 111 if (TransformPaintPropertyNode* existingScrollTranslation = | 111 if (auto* existingScrollTranslation = frameView.scrollTranslation()) { |
| 112 frameView.scrollTranslation()) | |
| 113 existingScrollTranslation->update(std::move(parent), matrix, origin); | 112 existingScrollTranslation->update(std::move(parent), matrix, origin); |
| 114 else | 113 } else { |
| 115 frameView.setScrollTranslation( | 114 frameView.setScrollTranslation( |
| 116 TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); | 115 TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); |
| 117 return frameView.scrollTranslation(); | 116 } |
| 118 } | 117 } |
| 119 | 118 |
| 120 ScrollPaintPropertyNode* updateFrameViewScroll( | 119 void updateFrameViewScroll( |
| 121 FrameView& frameView, | 120 FrameView& frameView, |
| 122 PassRefPtr<ScrollPaintPropertyNode> parent, | 121 PassRefPtr<ScrollPaintPropertyNode> parent, |
| 123 PassRefPtr<const TransformPaintPropertyNode> scrollOffset, | 122 PassRefPtr<const TransformPaintPropertyNode> scrollOffset, |
| 124 const IntSize& clip, | 123 const IntSize& clip, |
| 125 const IntSize& bounds, | 124 const IntSize& bounds, |
| 126 bool userScrollableHorizontal, | 125 bool userScrollableHorizontal, |
| 127 bool userScrollableVertical) { | 126 bool userScrollableVertical) { |
| 128 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); | 127 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| 129 if (ScrollPaintPropertyNode* existingScroll = frameView.scroll()) | 128 if (auto* existingScroll = frameView.scroll()) { |
| 130 existingScroll->update(std::move(parent), std::move(scrollOffset), clip, | 129 existingScroll->update(std::move(parent), std::move(scrollOffset), clip, |
| 131 bounds, userScrollableHorizontal, | 130 bounds, userScrollableHorizontal, |
| 132 userScrollableVertical); | 131 userScrollableVertical); |
| 133 else | 132 } else { |
| 134 frameView.setScroll(ScrollPaintPropertyNode::create( | 133 frameView.setScroll(ScrollPaintPropertyNode::create( |
| 135 std::move(parent), std::move(scrollOffset), clip, bounds, | 134 std::move(parent), std::move(scrollOffset), clip, bounds, |
| 136 userScrollableHorizontal, userScrollableVertical)); | 135 userScrollableHorizontal, userScrollableVertical)); |
| 137 return frameView.scroll(); | 136 } |
| 138 } | 137 } |
| 139 | 138 |
| 140 void PaintPropertyTreeBuilder::buildTreeNodes( | 139 void PaintPropertyTreeBuilder::updateProperties( |
| 141 FrameView& frameView, | 140 FrameView& frameView, |
| 142 PaintPropertyTreeBuilderContext& context) { | 141 PaintPropertyTreeBuilderContext& context) { |
| 143 if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { | 142 if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { |
| 144 LayoutView* layoutView = frameView.layoutView(); | 143 LayoutView* layoutView = frameView.layoutView(); |
| 145 if (!layoutView) | 144 if (!layoutView) |
| 146 return; | 145 return; |
| 147 | 146 |
| 148 TransformationMatrix frameTranslate; | 147 #if DCHECK_IS_ON() |
| 149 frameTranslate.translate(frameView.x() + layoutView->location().x() + | 148 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(*layoutView); |
| 150 context.current.paintOffset.x(), | 149 #endif |
| 151 frameView.y() + layoutView->location().y() + | 150 |
| 152 context.current.paintOffset.y()); | 151 if (layoutView->needsPaintPropertyUpdate()) { |
| 153 context.current.transform = | 152 TransformationMatrix frameTranslate; |
| 154 layoutView->getMutableForPainting() | 153 frameTranslate.translate(frameView.x() + layoutView->location().x() + |
| 155 .ensurePaintProperties() | 154 context.current.paintOffset.x(), |
| 156 .updatePaintOffsetTranslation(context.current.transform, | 155 frameView.y() + layoutView->location().y() + |
| 157 frameTranslate, FloatPoint3D()); | 156 context.current.paintOffset.y()); |
| 157 layoutView->getMutableForPainting() |
| 158 .ensurePaintProperties() |
| 159 .updatePaintOffsetTranslation(context.current.transform, |
| 160 frameTranslate, FloatPoint3D()); |
| 161 } |
| 162 |
| 163 const auto* properties = layoutView->paintProperties(); |
| 164 DCHECK(properties && properties->paintOffsetTranslation()); |
| 165 context.current.transform = properties->paintOffsetTranslation(); |
| 158 context.current.paintOffset = LayoutPoint(); | 166 context.current.paintOffset = LayoutPoint(); |
| 159 context.current.renderingContextID = 0; | 167 context.current.renderingContextID = 0; |
| 160 context.current.shouldFlattenInheritedTransform = true; | 168 context.current.shouldFlattenInheritedTransform = true; |
| 161 context.absolutePosition = context.current; | 169 context.absolutePosition = context.current; |
| 162 context.containerForAbsolutePosition = | 170 context.containerForAbsolutePosition = |
| 163 nullptr; // This will get set in updateOutOfFlowContext(). | 171 nullptr; // This will get set in updateOutOfFlowContext(). |
| 164 context.fixedPosition = context.current; | 172 context.fixedPosition = context.current; |
| 165 return; | 173 return; |
| 166 } | 174 } |
| 167 | 175 |
| 168 TransformationMatrix frameTranslate; | 176 #if DCHECK_IS_ON() |
| 169 frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), | 177 FindFrameViewPropertiesNeedingUpdateScope checkNeedsUpdateScope(&frameView); |
| 170 frameView.y() + context.current.paintOffset.y()); | 178 #endif |
| 171 context.current.transform = updateFrameViewPreTranslation( | |
| 172 frameView, context.current.transform, frameTranslate, FloatPoint3D()); | |
| 173 | 179 |
| 174 FloatRoundedRect contentClip( | 180 if (frameView.needsPaintPropertyUpdate()) { |
| 175 IntRect(IntPoint(), frameView.visibleContentSize())); | 181 TransformationMatrix frameTranslate; |
| 176 context.current.clip = updateFrameViewContentClip( | 182 frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), |
| 177 frameView, context.current.clip, frameView.preTranslation(), contentClip); | 183 frameView.y() + context.current.paintOffset.y()); |
| 184 updateFrameViewPreTranslation(frameView, context.current.transform, |
| 185 frameTranslate, FloatPoint3D()); |
| 178 | 186 |
| 179 // Record the fixed properties before any scrolling occurs. | 187 FloatRoundedRect contentClip( |
| 180 const auto* fixedTransformNode = context.current.transform; | 188 IntRect(IntPoint(), frameView.visibleContentSize())); |
| 181 auto* fixedScrollNode = context.current.scroll; | 189 updateFrameViewContentClip(frameView, context.current.clip, |
| 190 frameView.preTranslation(), contentClip); |
| 182 | 191 |
| 183 ScrollOffset scrollOffset = frameView.scrollOffset(); | 192 ScrollOffset scrollOffset = frameView.scrollOffset(); |
| 184 if (frameView.isScrollable() || !scrollOffset.isZero()) { | 193 if (frameView.isScrollable() || !scrollOffset.isZero()) { |
| 185 TransformationMatrix frameScroll; | 194 TransformationMatrix frameScroll; |
| 186 frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); | 195 frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); |
| 187 context.current.transform = updateFrameViewScrollTranslation( | 196 updateFrameViewScrollTranslation(frameView, frameView.preTranslation(), |
| 188 frameView, frameView.preTranslation(), frameScroll, FloatPoint3D()); | 197 frameScroll, FloatPoint3D()); |
| 189 | 198 |
| 190 IntSize scrollClip = frameView.visibleContentSize(); | 199 IntSize scrollClip = frameView.visibleContentSize(); |
| 191 IntSize scrollBounds = frameView.contentsSize(); | 200 IntSize scrollBounds = frameView.contentsSize(); |
| 192 bool userScrollableHorizontal = | 201 bool userScrollableHorizontal = |
| 193 frameView.userInputScrollable(HorizontalScrollbar); | 202 frameView.userInputScrollable(HorizontalScrollbar); |
| 194 bool userScrollableVertical = | 203 bool userScrollableVertical = |
| 195 frameView.userInputScrollable(VerticalScrollbar); | 204 frameView.userInputScrollable(VerticalScrollbar); |
| 196 context.current.scroll = updateFrameViewScroll( | 205 updateFrameViewScroll(frameView, context.current.scroll, |
| 197 frameView, context.current.scroll, frameView.scrollTranslation(), | 206 frameView.scrollTranslation(), scrollClip, |
| 198 scrollClip, scrollBounds, userScrollableHorizontal, | 207 scrollBounds, userScrollableHorizontal, |
| 199 userScrollableVertical); | 208 userScrollableVertical); |
| 200 } else { | 209 } else { |
| 201 // Ensure pre-existing properties are cleared when there is no scrolling. | 210 // Ensure pre-existing properties are cleared when there is no scrolling. |
| 202 frameView.setScrollTranslation(nullptr); | 211 frameView.setScrollTranslation(nullptr); |
| 203 frameView.setScroll(nullptr); | 212 frameView.setScroll(nullptr); |
| 213 } |
| 204 } | 214 } |
| 205 | 215 |
| 206 // Initialize the context for current, absolute and fixed position cases. | 216 // Initialize the context for current, absolute and fixed position cases. |
| 207 // They are the same, except that scroll translation does not apply to | 217 // They are the same, except that scroll translation does not apply to |
| 208 // fixed position descendants. | 218 // fixed position descendants. |
| 219 const auto* fixedTransformNode = frameView.preTranslation() |
| 220 ? frameView.preTranslation() |
| 221 : context.current.transform; |
| 222 auto* fixedScrollNode = context.current.scroll; |
| 223 DCHECK(frameView.preTranslation()); |
| 224 context.current.transform = frameView.preTranslation(); |
| 225 DCHECK(frameView.contentClip()); |
| 226 context.current.clip = frameView.contentClip(); |
| 227 if (const auto* scrollTranslation = frameView.scrollTranslation()) |
| 228 context.current.transform = scrollTranslation; |
| 229 if (auto* scroll = frameView.scroll()) |
| 230 context.current.scroll = scroll; |
| 209 context.current.paintOffset = LayoutPoint(); | 231 context.current.paintOffset = LayoutPoint(); |
| 210 context.current.renderingContextID = 0; | 232 context.current.renderingContextID = 0; |
| 211 context.current.shouldFlattenInheritedTransform = true; | 233 context.current.shouldFlattenInheritedTransform = true; |
| 212 context.absolutePosition = context.current; | 234 context.absolutePosition = context.current; |
| 213 context.containerForAbsolutePosition = nullptr; | 235 context.containerForAbsolutePosition = nullptr; |
| 214 context.fixedPosition = context.current; | 236 context.fixedPosition = context.current; |
| 215 context.fixedPosition.transform = fixedTransformNode; | 237 context.fixedPosition.transform = fixedTransformNode; |
| 216 context.fixedPosition.scroll = fixedScrollNode; | 238 context.fixedPosition.scroll = fixedScrollNode; |
| 217 | 239 |
| 218 std::unique_ptr<PropertyTreeState> contentsState( | 240 std::unique_ptr<PropertyTreeState> contentsState( |
| 219 new PropertyTreeState(context.current.transform, context.current.clip, | 241 new PropertyTreeState(context.current.transform, context.current.clip, |
| 220 context.currentEffect, context.current.scroll)); | 242 context.currentEffect, context.current.scroll)); |
| 221 frameView.setTotalPropertyTreeStateForContents(std::move(contentsState)); | 243 frameView.setTotalPropertyTreeStateForContents(std::move(contentsState)); |
| 222 } | 244 } |
| 223 | 245 |
| 224 void PaintPropertyTreeBuilder::updatePaintOffsetTranslation( | 246 void PaintPropertyTreeBuilder::updatePaintOffsetTranslation( |
| 225 const LayoutObject& object, | 247 const LayoutObject& object, |
| 226 PaintPropertyTreeBuilderContext& context) { | 248 PaintPropertyTreeBuilderContext& context) { |
| 227 // LayoutView's paint offset is updated in the FrameView property update. | 249 // LayoutView's paint offset is updated in the FrameView property update. |
| 228 if (object.isLayoutView()) { | 250 if (object.isLayoutView()) { |
| 229 DCHECK(context.current.paintOffset == LayoutPoint()); | 251 DCHECK(context.current.paintOffset == LayoutPoint()); |
| 230 return; | 252 return; |
| 231 } | 253 } |
| 232 | 254 |
| 255 bool usesPaintOffsetTranslation = false; |
| 233 if (object.isBoxModelObject() && | 256 if (object.isBoxModelObject() && |
| 234 context.current.paintOffset != LayoutPoint()) { | 257 context.current.paintOffset != LayoutPoint()) { |
| 235 // TODO(trchen): Eliminate PaintLayer dependency. | 258 // TODO(trchen): Eliminate PaintLayer dependency. |
| 236 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | 259 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); |
| 237 if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase)) { | 260 if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase)) |
| 238 // We should use the same subpixel paint offset values for snapping | 261 usesPaintOffsetTranslation = true; |
| 239 // regardless of whether a transform is present. If there is a transform | 262 } |
| 240 // we round the paint offset but keep around the residual fractional | |
| 241 // component for the transformed content to paint with. In spv1 this was | |
| 242 // called "subpixel accumulation". For more information, see | |
| 243 // PaintLayer::subpixelAccumulation() and | |
| 244 // PaintLayerPainter::paintFragmentByApplyingTransform. | |
| 245 IntPoint roundedPaintOffset = | |
| 246 roundedIntPoint(context.current.paintOffset); | |
| 247 LayoutPoint fractionalPaintOffset = | |
| 248 LayoutPoint(context.current.paintOffset - roundedPaintOffset); | |
| 249 | 263 |
| 250 context.current.transform = | 264 // We should use the same subpixel paint offset values for snapping |
| 251 object.getMutableForPainting() | 265 // regardless of whether a transform is present. If there is a transform |
| 252 .ensurePaintProperties() | 266 // we round the paint offset but keep around the residual fractional |
| 253 .updatePaintOffsetTranslation( | 267 // component for the transformed content to paint with. In spv1 this was |
| 254 context.current.transform, | 268 // called "subpixel accumulation". For more information, see |
| 255 TransformationMatrix().translate(roundedPaintOffset.x(), | 269 // PaintLayer::subpixelAccumulation() and |
| 256 roundedPaintOffset.y()), | 270 // PaintLayerPainter::paintFragmentByApplyingTransform. |
| 257 FloatPoint3D(), | 271 IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); |
| 258 context.current.shouldFlattenInheritedTransform, | 272 LayoutPoint fractionalPaintOffset = |
| 259 context.current.renderingContextID); | 273 LayoutPoint(context.current.paintOffset - roundedPaintOffset); |
| 260 context.current.paintOffset = fractionalPaintOffset; | 274 |
| 261 return; | 275 if (object.needsPaintPropertyUpdate()) { |
| 276 if (usesPaintOffsetTranslation) { |
| 277 object.getMutableForPainting() |
| 278 .ensurePaintProperties() |
| 279 .updatePaintOffsetTranslation( |
| 280 context.current.transform, |
| 281 TransformationMatrix().translate(roundedPaintOffset.x(), |
| 282 roundedPaintOffset.y()), |
| 283 FloatPoint3D(), context.current.shouldFlattenInheritedTransform, |
| 284 context.current.renderingContextID); |
| 285 } else { |
| 286 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 287 properties->clearPaintOffsetTranslation(); |
| 262 } | 288 } |
| 263 } | 289 } |
| 264 | 290 |
| 265 if (auto* properties = object.getMutableForPainting().paintProperties()) | 291 const auto* properties = object.paintProperties(); |
| 266 properties->clearPaintOffsetTranslation(); | 292 if (properties && properties->paintOffsetTranslation()) { |
| 293 context.current.transform = properties->paintOffsetTranslation(); |
| 294 context.current.paintOffset = fractionalPaintOffset; |
| 295 } |
| 267 } | 296 } |
| 268 | 297 |
| 269 static FloatPoint3D transformOrigin(const LayoutBox& box) { | 298 static FloatPoint3D transformOrigin(const LayoutBox& box) { |
| 270 const ComputedStyle& style = box.styleRef(); | 299 const ComputedStyle& style = box.styleRef(); |
| 271 FloatSize borderBoxSize(box.size()); | 300 FloatSize borderBoxSize(box.size()); |
| 272 return FloatPoint3D( | 301 return FloatPoint3D( |
| 273 floatValueForLength(style.transformOriginX(), borderBoxSize.width()), | 302 floatValueForLength(style.transformOriginX(), borderBoxSize.width()), |
| 274 floatValueForLength(style.transformOriginY(), borderBoxSize.height()), | 303 floatValueForLength(style.transformOriginY(), borderBoxSize.height()), |
| 275 style.transformOriginZ()); | 304 style.transformOriginZ()); |
| 276 } | 305 } |
| 277 | 306 |
| 278 // SVG does not use the general transform update of |updateTransform|, instead | 307 // SVG does not use the general transform update of |updateTransform|, instead |
| 279 // creating a transform node for SVG-specific transforms without 3D. | 308 // creating a transform node for SVG-specific transforms without 3D. |
| 280 void PaintPropertyTreeBuilder::updateTransformForNonRootSVG( | 309 void PaintPropertyTreeBuilder::updateTransformForNonRootSVG( |
| 281 const LayoutObject& object, | 310 const LayoutObject& object, |
| 282 PaintPropertyTreeBuilderContext& context) { | 311 PaintPropertyTreeBuilderContext& context) { |
| 283 DCHECK(object.isSVG() && !object.isSVGRoot()); | 312 DCHECK(object.isSVG() && !object.isSVGRoot()); |
| 284 // SVG (other than SVGForeignObject) does not use paint offset internally. | 313 // SVG (other than SVGForeignObject) does not use paint offset internally. |
| 285 DCHECK(object.isSVGForeignObject() || | 314 DCHECK(object.isSVGForeignObject() || |
| 286 context.current.paintOffset == LayoutPoint()); | 315 context.current.paintOffset == LayoutPoint()); |
| 287 | 316 |
| 288 // FIXME(pdr): Refactor this so all non-root SVG objects use the same | 317 if (object.needsPaintPropertyUpdate()) { |
| 289 // transform function. | 318 // TODO(pdr): Refactor this so all non-root SVG objects use the same |
| 290 const AffineTransform& transform = object.isSVGForeignObject() | 319 // transform function. |
| 291 ? object.localSVGTransform() | 320 const AffineTransform& transform = object.isSVGForeignObject() |
| 292 : object.localToSVGParentTransform(); | 321 ? object.localSVGTransform() |
| 322 : object.localToSVGParentTransform(); |
| 323 // TODO(pdr): Check for the presence of a transform instead of the value. |
| 324 // Checking for an identity matrix will cause the property tree structure |
| 325 // to change during animations if the animation passes through the |
| 326 // identity matrix. |
| 327 if (!transform.isIdentity()) { |
| 328 // The origin is included in the local transform, so leave origin empty. |
| 329 object.getMutableForPainting().ensurePaintProperties().updateTransform( |
| 330 context.current.transform, TransformationMatrix(transform), |
| 331 FloatPoint3D()); |
| 332 } else { |
| 333 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 334 properties->clearTransform(); |
| 335 } |
| 336 } |
| 293 | 337 |
| 294 // FIXME(pdr): Check for the presence of a transform instead of the value. | 338 if (object.paintProperties() && object.paintProperties()->transform()) { |
| 295 // Checking for an identity matrix will cause the property tree structure to | 339 context.current.transform = object.paintProperties()->transform(); |
| 296 // change during animations if the animation passes through the identity | 340 context.current.shouldFlattenInheritedTransform = false; |
| 297 // matrix. | |
| 298 if (!transform.isIdentity()) { | |
| 299 // The origin is included in the local transform, so leave origin empty. | |
| 300 context.current.transform = | |
| 301 object.getMutableForPainting().ensurePaintProperties().updateTransform( | |
| 302 context.current.transform, TransformationMatrix(transform), | |
| 303 FloatPoint3D()); | |
| 304 context.current.renderingContextID = 0; | 341 context.current.renderingContextID = 0; |
| 305 context.current.shouldFlattenInheritedTransform = false; | |
| 306 } else { | |
| 307 if (auto* properties = object.getMutableForPainting().paintProperties()) | |
| 308 properties->clearTransform(); | |
| 309 } | 342 } |
| 310 } | 343 } |
| 311 | 344 |
| 312 void PaintPropertyTreeBuilder::updateTransform( | 345 void PaintPropertyTreeBuilder::updateTransform( |
| 313 const LayoutObject& object, | 346 const LayoutObject& object, |
| 314 PaintPropertyTreeBuilderContext& context) { | 347 PaintPropertyTreeBuilderContext& context) { |
| 315 if (object.isSVG() && !object.isSVGRoot()) { | 348 if (object.isSVG() && !object.isSVGRoot()) { |
| 316 updateTransformForNonRootSVG(object, context); | 349 updateTransformForNonRootSVG(object, context); |
| 317 return; | 350 return; |
| 318 } | 351 } |
| 319 | 352 |
| 320 const ComputedStyle& style = object.styleRef(); | 353 if (object.needsPaintPropertyUpdate()) { |
| 321 if (object.isBox() && (style.hasTransform() || style.preserves3D())) { | 354 const ComputedStyle& style = object.styleRef(); |
| 322 TransformationMatrix matrix; | 355 if (object.isBox() && (style.hasTransform() || style.preserves3D())) { |
| 323 style.applyTransform(matrix, toLayoutBox(object).size(), | 356 TransformationMatrix matrix; |
| 324 ComputedStyle::ExcludeTransformOrigin, | 357 style.applyTransform( |
| 325 ComputedStyle::IncludeMotionPath, | 358 matrix, toLayoutBox(object).size(), |
| 326 ComputedStyle::IncludeIndependentTransformProperties); | 359 ComputedStyle::ExcludeTransformOrigin, |
| 327 FloatPoint3D origin = transformOrigin(toLayoutBox(object)); | 360 ComputedStyle::IncludeMotionPath, |
| 361 ComputedStyle::IncludeIndependentTransformProperties); |
| 328 | 362 |
| 329 unsigned renderingContextID = context.current.renderingContextID; | 363 // TODO(trchen): transform-style should only be respected if a PaintLayer |
| 330 unsigned renderingContextIDForChildren = 0; | 364 // is created. |
| 331 bool flattensInheritedTransform = | |
| 332 context.current.shouldFlattenInheritedTransform; | |
| 333 bool childrenFlattenInheritedTransform = true; | |
| 334 | |
| 335 // TODO(trchen): transform-style should only be respected if a PaintLayer | |
| 336 // is created. | |
| 337 if (style.preserves3D()) { | |
| 338 // If a node with transform-style: preserve-3d does not exist in an | 365 // If a node with transform-style: preserve-3d does not exist in an |
| 339 // existing rendering context, it establishes a new one. | 366 // existing rendering context, it establishes a new one. |
| 340 if (!renderingContextID) | 367 unsigned renderingContextID = context.current.renderingContextID; |
| 368 if (style.preserves3D() && !renderingContextID) |
| 341 renderingContextID = PtrHash<const LayoutObject>::hash(&object); | 369 renderingContextID = PtrHash<const LayoutObject>::hash(&object); |
| 342 renderingContextIDForChildren = renderingContextID; | 370 |
| 343 childrenFlattenInheritedTransform = false; | 371 object.getMutableForPainting().ensurePaintProperties().updateTransform( |
| 372 context.current.transform, matrix, |
| 373 transformOrigin(toLayoutBox(object)), |
| 374 context.current.shouldFlattenInheritedTransform, renderingContextID); |
| 375 } else { |
| 376 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 377 properties->clearTransform(); |
| 344 } | 378 } |
| 379 } |
| 345 | 380 |
| 346 context.current.transform = | 381 const auto* properties = object.paintProperties(); |
| 347 object.getMutableForPainting().ensurePaintProperties().updateTransform( | 382 if (properties && properties->transform()) { |
| 348 context.current.transform, matrix, origin, | 383 context.current.transform = properties->transform(); |
| 349 flattensInheritedTransform, renderingContextID); | 384 unsigned renderingContextID = properties->transform()->renderingContextID(); |
| 350 context.current.renderingContextID = renderingContextIDForChildren; | 385 if (context.current.renderingContextID != renderingContextID) { |
| 351 context.current.shouldFlattenInheritedTransform = | 386 context.current.renderingContextID = renderingContextID; |
| 352 childrenFlattenInheritedTransform; | 387 context.current.shouldFlattenInheritedTransform = false; |
| 353 } else { | 388 } else { |
| 354 if (auto* properties = object.getMutableForPainting().paintProperties()) | 389 context.current.renderingContextID = 0; |
| 355 properties->clearTransform(); | 390 context.current.shouldFlattenInheritedTransform = true; |
| 391 } |
| 356 } | 392 } |
| 357 } | 393 } |
| 358 | 394 |
| 359 void PaintPropertyTreeBuilder::updateEffect( | 395 void PaintPropertyTreeBuilder::updateEffect( |
| 360 const LayoutObject& object, | 396 const LayoutObject& object, |
| 361 PaintPropertyTreeBuilderContext& context) { | 397 PaintPropertyTreeBuilderContext& context) { |
| 362 const ComputedStyle& style = object.styleRef(); | 398 const ComputedStyle& style = object.styleRef(); |
| 363 | 399 |
| 364 if (!style.isStackingContext()) { | 400 if (!style.isStackingContext()) { |
| 365 if (ObjectPaintProperties* properties = | 401 if (object.needsPaintPropertyUpdate()) { |
| 366 object.getMutableForPainting().paintProperties()) | 402 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 367 properties->clearEffect(); | 403 properties->clearEffect(); |
| 404 } |
| 368 return; | 405 return; |
| 369 } | 406 } |
| 370 | 407 |
| 371 // TODO(trchen): Can't omit effect node if we have 3D children. | 408 if (object.needsPaintPropertyUpdate()) { |
| 372 // TODO(trchen): Can't omit effect node if we have blending children. | 409 // TODO(trchen): Can't omit effect node if we have 3D children. |
| 373 bool effectNodeNeeded = false; | 410 // TODO(trchen): Can't omit effect node if we have blending children. |
| 411 bool effectNodeNeeded = false; |
| 374 | 412 |
| 375 float opacity = style.opacity(); | 413 float opacity = style.opacity(); |
| 376 if (opacity != 1.0f) | 414 if (opacity != 1.0f) |
| 377 effectNodeNeeded = true; | 415 effectNodeNeeded = true; |
| 378 | 416 |
| 379 CompositorFilterOperations filter; | 417 CompositorFilterOperations filter; |
| 380 if (object.isSVG() && !object.isSVGRoot()) { | 418 if (object.isSVG() && !object.isSVGRoot()) { |
| 381 // TODO(trchen): SVG caches filters in SVGResources. Implement it. | 419 // TODO(trchen): SVG caches filters in SVGResources. Implement it. |
| 382 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { | 420 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { |
| 383 // TODO(trchen): Eliminate PaintLayer dependency. | 421 // TODO(trchen): Eliminate PaintLayer dependency. |
| 384 filter = layer->createCompositorFilterOperationsForFilter(style); | 422 filter = layer->createCompositorFilterOperationsForFilter(style); |
| 423 } |
| 424 |
| 425 const ClipPaintPropertyNode* outputClip = rootClipNode(); |
| 426 // The CSS filter spec didn't specify how filters interact with overflow |
| 427 // clips. The implementation here mimics the old Blink/WebKit behavior for |
| 428 // backward compatibility. |
| 429 // Basically the output of the filter will be affected by clips that applies |
| 430 // to the current element. The descendants that paints into the input of the |
| 431 // filter ignores any clips collected so far. For example: |
| 432 // <div style="overflow:scroll"> |
| 433 // <div style="filter:blur(1px);"> |
| 434 // <div>A</div> |
| 435 // <div style="position:absolute;">B</div> |
| 436 // </div> |
| 437 // </div> |
| 438 // In this example "A" should be clipped if the filter was not present. |
| 439 // With the filter, "A" will be rastered without clipping, but instead |
| 440 // the blurred result will be clipped. |
| 441 // On the other hand, "B" should not be clipped because the overflow clip is |
| 442 // not in its containing block chain, but as the filter output will be |
| 443 // clipped, so a blurred "B" may still be invisible. |
| 444 if (!filter.isEmpty()) { |
| 445 effectNodeNeeded = true; |
| 446 outputClip = context.current.clip; |
| 447 |
| 448 // TODO(trchen): A filter may contain spatial operations such that an |
| 449 // output pixel may depend on an input pixel outside of the output clip. |
| 450 // We should generate a special clip node to represent this expansion. |
| 451 } |
| 452 |
| 453 if (effectNodeNeeded) { |
| 454 object.getMutableForPainting().ensurePaintProperties().updateEffect( |
| 455 context.currentEffect, context.current.transform, outputClip, |
| 456 std::move(filter), opacity); |
| 457 } else { |
| 458 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 459 properties->clearEffect(); |
| 460 } |
| 385 } | 461 } |
| 386 | 462 |
| 387 const ClipPaintPropertyNode* outputClip = rootClipNode(); | 463 const auto* properties = object.paintProperties(); |
| 388 // The CSS filter spec didn't specify how filters interact with overflow | 464 if (properties && properties->effect()) { |
| 389 // clips. The implementation here mimics the old Blink/WebKit behavior for | 465 context.currentEffect = properties->effect(); |
| 390 // backward compatibility. | 466 // TODO(pdr): Once the expansion clip node is created above, it should be |
| 391 // Basically the output of the filter will be affected by clips that applies | 467 // used here to update all current clip nodes; |
| 392 // to the current element. The descendants that paints into the input of the | |
| 393 // filter ignores any clips collected so far. For example: | |
| 394 // <div style="overflow:scroll"> | |
| 395 // <div style="filter:blur(1px);"> | |
| 396 // <div>A</div> | |
| 397 // <div style="position:absolute;">B</div> | |
| 398 // </div> | |
| 399 // </div> | |
| 400 // In this example "A" should be clipped if the filter was not present. | |
| 401 // With the filter, "A" will be rastered without clipping, but instead | |
| 402 // the blurred result will be clipped. | |
| 403 // On the other hand, "B" should not be clipped because the overflow clip is | |
| 404 // not in its containing block chain, but as the filter output will be | |
| 405 // clipped, so a blurred "B" may still be invisible. | |
| 406 if (!filter.isEmpty()) { | |
| 407 effectNodeNeeded = true; | |
| 408 outputClip = context.current.clip; | |
| 409 | |
| 410 // TODO(trchen): A filter may contain spatial operations such that an output | |
| 411 // pixel may depend on an input pixel outside of the output clip. Need to | |
| 412 // generate special clip node to hint how to expand clip / cull rect. | |
| 413 const ClipPaintPropertyNode* expansionHint = context.current.clip; | 468 const ClipPaintPropertyNode* expansionHint = context.current.clip; |
| 414 context.current.clip = context.absolutePosition.clip = | 469 context.current.clip = context.absolutePosition.clip = |
| 415 context.fixedPosition.clip = expansionHint; | 470 context.fixedPosition.clip = expansionHint; |
| 416 } | 471 } |
| 417 | |
| 418 if (!effectNodeNeeded) { | |
| 419 if (ObjectPaintProperties* properties = | |
| 420 object.getMutableForPainting().paintProperties()) | |
| 421 properties->clearEffect(); | |
| 422 return; | |
| 423 } | |
| 424 | |
| 425 context.currentEffect = | |
| 426 object.getMutableForPainting().ensurePaintProperties().updateEffect( | |
| 427 context.currentEffect, context.current.transform, outputClip, | |
| 428 std::move(filter), opacity); | |
| 429 } | 472 } |
| 430 | 473 |
| 431 void PaintPropertyTreeBuilder::updateCssClip( | 474 void PaintPropertyTreeBuilder::updateCssClip( |
| 432 const LayoutObject& object, | 475 const LayoutObject& object, |
| 433 PaintPropertyTreeBuilderContext& context) { | 476 PaintPropertyTreeBuilderContext& context) { |
| 434 if (object.hasClip()) { | 477 if (object.needsPaintPropertyUpdate()) { |
| 435 // Create clip node for descendants that are not fixed position. | 478 if (object.hasClip()) { |
| 436 // We don't have to setup context.absolutePosition.clip here because this | 479 // Create clip node for descendants that are not fixed position. |
| 437 // object must be a container for absolute position descendants, and will | 480 // We don't have to setup context.absolutePosition.clip here because this |
| 438 // copy from in-flow context later at updateOutOfFlowContext() step. | 481 // object must be a container for absolute position descendants, and will |
| 439 DCHECK(object.canContainAbsolutePositionObjects()); | 482 // copy from in-flow context later at updateOutOfFlowContext() step. |
| 440 LayoutRect clipRect = | 483 DCHECK(object.canContainAbsolutePositionObjects()); |
| 441 toLayoutBox(object).clipRect(context.current.paintOffset); | 484 LayoutRect clipRect = |
| 442 context.current.clip = | 485 toLayoutBox(object).clipRect(context.current.paintOffset); |
| 443 object.getMutableForPainting().ensurePaintProperties().updateCssClip( | 486 object.getMutableForPainting().ensurePaintProperties().updateCssClip( |
| 444 context.current.clip, context.current.transform, | 487 context.current.clip, context.current.transform, |
| 445 FloatRoundedRect(FloatRect(clipRect))); | 488 FloatRoundedRect(FloatRect(clipRect))); |
| 446 return; | 489 } else { |
| 490 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 491 properties->clearCssClip(); |
| 492 } |
| 447 } | 493 } |
| 448 | 494 |
| 449 if (auto* properties = object.getMutableForPainting().paintProperties()) | 495 const auto* properties = object.paintProperties(); |
| 450 properties->clearCssClip(); | 496 if (properties && properties->cssClip()) |
| 497 context.current.clip = properties->cssClip(); |
| 451 } | 498 } |
| 452 | 499 |
| 453 void PaintPropertyTreeBuilder::updateLocalBorderBoxContext( | 500 void PaintPropertyTreeBuilder::updateLocalBorderBoxContext( |
| 454 const LayoutObject& object, | 501 const LayoutObject& object, |
| 455 PaintPropertyTreeBuilderContext& context) { | 502 PaintPropertyTreeBuilderContext& context) { |
| 456 // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since | 503 if (object.needsPaintPropertyUpdate()) { |
| 457 // we don't need them at the moment. | 504 // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since |
| 458 if (!object.isBox() && !object.hasLayer()) | 505 // we don't need them at the moment. |
| 459 return; | 506 if (!object.isBox() && !object.hasLayer()) { |
| 460 | 507 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 461 std::unique_ptr<ObjectPaintProperties::PropertyTreeStateWithOffset> | 508 properties->clearLocalBorderBoxProperties(); |
| 462 borderBoxContext = | 509 } else { |
| 463 wrapUnique(new ObjectPaintProperties::PropertyTreeStateWithOffset( | 510 std::unique_ptr<ObjectPaintProperties::PropertyTreeStateWithOffset> |
| 464 context.current.paintOffset, | 511 borderBoxContext = |
| 465 PropertyTreeState(context.current.transform, context.current.clip, | 512 wrapUnique(new ObjectPaintProperties::PropertyTreeStateWithOffset( |
| 466 context.currentEffect, | 513 context.current.paintOffset, |
| 467 context.current.scroll))); | 514 PropertyTreeState(context.current.transform, |
| 468 object.getMutableForPainting() | 515 context.current.clip, context.currentEffect, |
| 469 .ensurePaintProperties() | 516 context.current.scroll))); |
| 470 .setLocalBorderBoxProperties(std::move(borderBoxContext)); | 517 object.getMutableForPainting() |
| 518 .ensurePaintProperties() |
| 519 .setLocalBorderBoxProperties(std::move(borderBoxContext)); |
| 520 } |
| 521 } |
| 471 } | 522 } |
| 472 | 523 |
| 473 // TODO(trchen): Remove this once we bake the paint offset into frameRect. | 524 // TODO(trchen): Remove this once we bake the paint offset into frameRect. |
| 474 void PaintPropertyTreeBuilder::updateScrollbarPaintOffset( | 525 void PaintPropertyTreeBuilder::updateScrollbarPaintOffset( |
| 475 const LayoutObject& object, | 526 const LayoutObject& object, |
| 476 const PaintPropertyTreeBuilderContext& context) { | 527 const PaintPropertyTreeBuilderContext& context) { |
| 528 if (!object.needsPaintPropertyUpdate()) |
| 529 return; |
| 530 |
| 477 IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); | 531 IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); |
| 478 if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { | 532 if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { |
| 479 if (PaintLayerScrollableArea* scrollableArea = | 533 if (PaintLayerScrollableArea* scrollableArea = |
| 480 toLayoutBoxModelObject(object).getScrollableArea()) { | 534 toLayoutBoxModelObject(object).getScrollableArea()) { |
| 481 if (scrollableArea->horizontalScrollbar() || | 535 if (scrollableArea->horizontalScrollbar() || |
| 482 scrollableArea->verticalScrollbar()) { | 536 scrollableArea->verticalScrollbar()) { |
| 483 auto paintOffset = TransformationMatrix().translate( | 537 auto paintOffset = TransformationMatrix().translate( |
| 484 roundedPaintOffset.x(), roundedPaintOffset.y()); | 538 roundedPaintOffset.x(), roundedPaintOffset.y()); |
| 485 object.getMutableForPainting() | 539 object.getMutableForPainting() |
| 486 .ensurePaintProperties() | 540 .ensurePaintProperties() |
| 487 .updateScrollbarPaintOffset(context.current.transform, paintOffset, | 541 .updateScrollbarPaintOffset(context.current.transform, paintOffset, |
| 488 FloatPoint3D()); | 542 FloatPoint3D()); |
| 489 return; | 543 return; |
| 490 } | 544 } |
| 491 } | 545 } |
| 492 } | 546 } |
| 493 | 547 |
| 494 if (auto* properties = object.getMutableForPainting().paintProperties()) | 548 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 495 properties->clearScrollbarPaintOffset(); | 549 properties->clearScrollbarPaintOffset(); |
| 496 } | 550 } |
| 497 | 551 |
| 498 void PaintPropertyTreeBuilder::updateMainThreadScrollingReasons( | 552 void PaintPropertyTreeBuilder::updateMainThreadScrollingReasons( |
| 499 const LayoutObject& object, | 553 const LayoutObject& object, |
| 500 PaintPropertyTreeBuilderContext& context) { | 554 PaintPropertyTreeBuilderContext& context) { |
| 555 // TODO(pdr): Mark properties as needing an update for main thread scroll |
| 556 // reasons and ensure reason changes are propagated to ancestors to account |
| 557 // for the parent walk below. |
| 501 if (context.current.scroll && | 558 if (context.current.scroll && |
| 502 !object.document().settings()->threadedScrollingEnabled()) | 559 !object.document().settings()->threadedScrollingEnabled()) { |
| 503 context.current.scroll->addMainThreadScrollingReasons( | 560 context.current.scroll->addMainThreadScrollingReasons( |
| 504 MainThreadScrollingReason::kThreadedScrollingDisabled); | 561 MainThreadScrollingReason::kThreadedScrollingDisabled); |
| 562 } |
| 505 | 563 |
| 506 if (object.isBackgroundAttachmentFixedObject()) { | 564 if (object.isBackgroundAttachmentFixedObject()) { |
| 507 auto* scrollNode = context.current.scroll; | 565 auto* scrollNode = context.current.scroll; |
| 508 while ( | 566 while ( |
| 509 scrollNode && | 567 scrollNode && |
| 510 !scrollNode->hasMainThreadScrollingReasons( | 568 !scrollNode->hasMainThreadScrollingReasons( |
| 511 MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects)) { | 569 MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects)) { |
| 512 scrollNode->addMainThreadScrollingReasons( | 570 scrollNode->addMainThreadScrollingReasons( |
| 513 MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); | 571 MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| 514 scrollNode = scrollNode->parent(); | 572 scrollNode = scrollNode->parent(); |
| 515 } | 573 } |
| 516 } | 574 } |
| 517 } | 575 } |
| 518 | 576 |
| 519 void PaintPropertyTreeBuilder::updateOverflowClip( | 577 void PaintPropertyTreeBuilder::updateOverflowClip( |
| 520 const LayoutObject& object, | 578 const LayoutObject& object, |
| 521 PaintPropertyTreeBuilderContext& context) { | 579 PaintPropertyTreeBuilderContext& context) { |
| 522 if (!object.isBox()) | 580 if (!object.isBox()) |
| 523 return; | 581 return; |
| 524 const LayoutBox& box = toLayoutBox(object); | 582 const LayoutBox& box = toLayoutBox(object); |
| 583 if (object.needsPaintPropertyUpdate()) { |
| 584 // The <input> elements can't have contents thus CSS overflow property |
| 585 // doesn't apply. However for layout purposes we do generate child layout |
| 586 // objects for them, e.g. button label. We should clip the overflow from |
| 587 // those children. This is called control clip and we technically treat them |
| 588 // like overflow clip. |
| 589 LayoutRect clipRect; |
| 590 if (box.hasControlClip()) { |
| 591 clipRect = box.controlClipRect(context.current.paintOffset); |
| 592 } else if (box.hasOverflowClip() || box.styleRef().containsPaint() || |
| 593 (box.isSVGRoot() && |
| 594 toLayoutSVGRoot(box).shouldApplyViewportClip())) { |
| 595 clipRect = LayoutRect(pixelSnappedIntRect( |
| 596 box.overflowClipRect(context.current.paintOffset))); |
| 597 } else { |
| 598 if (auto* properties = object.getMutableForPainting().paintProperties()) { |
| 599 properties->clearInnerBorderRadiusClip(); |
| 600 properties->clearOverflowClip(); |
| 601 } |
| 602 return; |
| 603 } |
| 525 | 604 |
| 526 // The <input> elements can't have contents thus CSS overflow property doesn't | 605 const auto* currentClip = context.current.clip; |
| 527 // apply. However for layout purposes we do generate child layout objects for | 606 if (box.styleRef().hasBorderRadius()) { |
| 528 // them, e.g. button label. We should clip the overflow from those children. | 607 auto innerBorder = box.styleRef().getRoundedInnerBorderFor( |
| 529 // This is called control clip and we technically treat them like overflow | 608 LayoutRect(context.current.paintOffset, box.size())); |
| 530 // clip. | 609 object.getMutableForPainting() |
| 531 LayoutRect clipRect; | 610 .ensurePaintProperties() |
| 532 if (box.hasControlClip()) { | 611 .updateInnerBorderRadiusClip(context.current.clip, |
| 533 clipRect = box.controlClipRect(context.current.paintOffset); | 612 context.current.transform, innerBorder); |
| 534 } else if (box.hasOverflowClip() || box.styleRef().containsPaint() || | 613 currentClip = object.paintProperties()->innerBorderRadiusClip(); |
| 535 (box.isSVGRoot() && | 614 } else if (auto* properties = |
| 536 toLayoutSVGRoot(box).shouldApplyViewportClip())) { | 615 object.getMutableForPainting().paintProperties()) { |
| 537 clipRect = LayoutRect( | |
| 538 pixelSnappedIntRect(box.overflowClipRect(context.current.paintOffset))); | |
| 539 } else { | |
| 540 if (auto* properties = object.getMutableForPainting().paintProperties()) { | |
| 541 properties->clearInnerBorderRadiusClip(); | 616 properties->clearInnerBorderRadiusClip(); |
| 542 properties->clearOverflowClip(); | |
| 543 } | 617 } |
| 544 return; | 618 |
| 619 object.getMutableForPainting().ensurePaintProperties().updateOverflowClip( |
| 620 currentClip, context.current.transform, |
| 621 FloatRoundedRect(FloatRect(clipRect))); |
| 545 } | 622 } |
| 546 | 623 |
| 547 if (box.styleRef().hasBorderRadius()) { | 624 const auto* properties = object.paintProperties(); |
| 548 auto innerBorder = box.styleRef().getRoundedInnerBorderFor( | 625 if (properties && properties->overflowClip()) |
| 549 LayoutRect(context.current.paintOffset, box.size())); | 626 context.current.clip = properties->overflowClip(); |
| 550 context.current.clip = | |
| 551 object.getMutableForPainting() | |
| 552 .ensurePaintProperties() | |
| 553 .updateInnerBorderRadiusClip( | |
| 554 context.current.clip, context.current.transform, innerBorder); | |
| 555 } else if (auto* properties = | |
| 556 object.getMutableForPainting().paintProperties()) { | |
| 557 properties->clearInnerBorderRadiusClip(); | |
| 558 } | |
| 559 | |
| 560 context.current.clip = | |
| 561 object.getMutableForPainting().ensurePaintProperties().updateOverflowClip( | |
| 562 context.current.clip, context.current.transform, | |
| 563 FloatRoundedRect(FloatRect(clipRect))); | |
| 564 } | 627 } |
| 565 | 628 |
| 566 static FloatPoint perspectiveOrigin(const LayoutBox& box) { | 629 static FloatPoint perspectiveOrigin(const LayoutBox& box) { |
| 567 const ComputedStyle& style = box.styleRef(); | 630 const ComputedStyle& style = box.styleRef(); |
| 568 FloatSize borderBoxSize(box.size()); | 631 FloatSize borderBoxSize(box.size()); |
| 569 return FloatPoint( | 632 return FloatPoint( |
| 570 floatValueForLength(style.perspectiveOriginX(), borderBoxSize.width()), | 633 floatValueForLength(style.perspectiveOriginX(), borderBoxSize.width()), |
| 571 floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height())); | 634 floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height())); |
| 572 } | 635 } |
| 573 | 636 |
| 574 void PaintPropertyTreeBuilder::updatePerspective( | 637 void PaintPropertyTreeBuilder::updatePerspective( |
| 575 const LayoutObject& object, | 638 const LayoutObject& object, |
| 576 PaintPropertyTreeBuilderContext& context) { | 639 PaintPropertyTreeBuilderContext& context) { |
| 577 const ComputedStyle& style = object.styleRef(); | 640 if (object.needsPaintPropertyUpdate()) { |
| 578 if (!object.isBox() || !style.hasPerspective()) { | 641 const ComputedStyle& style = object.styleRef(); |
| 579 if (auto* properties = object.getMutableForPainting().paintProperties()) | 642 if (object.isBox() && style.hasPerspective()) { |
| 580 properties->clearPerspective(); | 643 // The perspective node must not flatten (else nothing will get |
| 581 return; | 644 // perspective), but it should still extend the rendering context as |
| 582 } | 645 // most transform nodes do. |
| 583 | 646 TransformationMatrix matrix = |
| 584 // The perspective node must not flatten (else nothing will get | 647 TransformationMatrix().applyPerspective(style.perspective()); |
| 585 // perspective), but it should still extend the rendering context as most | 648 FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) + |
| 586 // transform nodes do. | 649 toLayoutSize(context.current.paintOffset); |
| 587 TransformationMatrix matrix = | |
| 588 TransformationMatrix().applyPerspective(style.perspective()); | |
| 589 FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) + | |
| 590 toLayoutSize(context.current.paintOffset); | |
| 591 context.current.transform = | |
| 592 object.getMutableForPainting().ensurePaintProperties().updatePerspective( | 650 object.getMutableForPainting().ensurePaintProperties().updatePerspective( |
| 593 context.current.transform, matrix, origin, | 651 context.current.transform, matrix, origin, |
| 594 context.current.shouldFlattenInheritedTransform, | 652 context.current.shouldFlattenInheritedTransform, |
| 595 context.current.renderingContextID); | 653 context.current.renderingContextID); |
| 596 context.current.shouldFlattenInheritedTransform = false; | 654 } else { |
| 655 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 656 properties->clearPerspective(); |
| 657 } |
| 658 } |
| 659 |
| 660 const auto* properties = object.paintProperties(); |
| 661 if (properties && properties->perspective()) { |
| 662 context.current.transform = properties->perspective(); |
| 663 context.current.shouldFlattenInheritedTransform = false; |
| 664 } |
| 597 } | 665 } |
| 598 | 666 |
| 599 void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform( | 667 void PaintPropertyTreeBuilder::updateSvgLocalToBorderBoxTransform( |
| 600 const LayoutObject& object, | 668 const LayoutObject& object, |
| 601 PaintPropertyTreeBuilderContext& context) { | 669 PaintPropertyTreeBuilderContext& context) { |
| 602 if (!object.isSVGRoot()) | 670 if (!object.isSVGRoot()) |
| 603 return; | 671 return; |
| 604 | 672 |
| 605 AffineTransform transformToBorderBox = | 673 if (object.needsPaintPropertyUpdate()) { |
| 606 SVGRootPainter(toLayoutSVGRoot(object)) | 674 AffineTransform transformToBorderBox = |
| 607 .transformToPixelSnappedBorderBox(context.current.paintOffset); | 675 SVGRootPainter(toLayoutSVGRoot(object)) |
| 608 | 676 .transformToPixelSnappedBorderBox(context.current.paintOffset); |
| 609 // The paint offset is included in |transformToBorderBox| so SVG does not need | 677 if (!transformToBorderBox.isIdentity()) { |
| 610 // to handle paint offset internally. | |
| 611 context.current.paintOffset = LayoutPoint(); | |
| 612 | |
| 613 if (transformToBorderBox.isIdentity()) { | |
| 614 if (auto* properties = object.getMutableForPainting().paintProperties()) | |
| 615 properties->clearSvgLocalToBorderBoxTransform(); | |
| 616 return; | |
| 617 } | |
| 618 | |
| 619 context.current.transform = | |
| 620 object.getMutableForPainting() | 678 object.getMutableForPainting() |
| 621 .ensurePaintProperties() | 679 .ensurePaintProperties() |
| 622 .updateSvgLocalToBorderBoxTransform( | 680 .updateSvgLocalToBorderBoxTransform( |
| 623 context.current.transform, transformToBorderBox, FloatPoint3D()); | 681 context.current.transform, transformToBorderBox, FloatPoint3D()); |
| 624 context.current.shouldFlattenInheritedTransform = false; | 682 } else { |
| 625 context.current.renderingContextID = 0; | 683 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 684 properties->clearSvgLocalToBorderBoxTransform(); |
| 685 } |
| 686 } |
| 687 |
| 688 const auto* properties = object.paintProperties(); |
| 689 if (properties && properties->svgLocalToBorderBoxTransform()) { |
| 690 context.current.transform = properties->svgLocalToBorderBoxTransform(); |
| 691 context.current.shouldFlattenInheritedTransform = false; |
| 692 context.current.renderingContextID = 0; |
| 693 } |
| 694 // The paint offset is included in |transformToBorderBox| so SVG does not need |
| 695 // to handle paint offset internally. |
| 696 context.current.paintOffset = LayoutPoint(); |
| 626 } | 697 } |
| 627 | 698 |
| 628 void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation( | 699 void PaintPropertyTreeBuilder::updateScrollAndScrollTranslation( |
| 629 const LayoutObject& object, | 700 const LayoutObject& object, |
| 630 PaintPropertyTreeBuilderContext& context) { | 701 PaintPropertyTreeBuilderContext& context) { |
| 631 if (object.hasOverflowClip()) { | 702 if (object.needsPaintPropertyUpdate()) { |
| 632 const LayoutBox& box = toLayoutBox(object); | 703 if (object.hasOverflowClip()) { |
| 633 const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea(); | 704 const LayoutBox& box = toLayoutBox(object); |
| 634 IntSize scrollOffset = box.scrolledContentOffset(); | 705 const PaintLayerScrollableArea* scrollableArea = box.getScrollableArea(); |
| 635 if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) { | 706 IntSize scrollOffset = box.scrolledContentOffset(); |
| 636 TransformationMatrix matrix = TransformationMatrix().translate( | 707 if (!scrollOffset.isZero() || scrollableArea->scrollsOverflow()) { |
| 637 -scrollOffset.width(), -scrollOffset.height()); | 708 TransformationMatrix matrix = TransformationMatrix().translate( |
| 638 context.current.transform = | 709 -scrollOffset.width(), -scrollOffset.height()); |
| 639 object.getMutableForPainting() | 710 object.getMutableForPainting() |
| 640 .ensurePaintProperties() | 711 .ensurePaintProperties() |
| 641 .updateScrollTranslation( | 712 .updateScrollTranslation( |
| 642 context.current.transform, matrix, FloatPoint3D(), | 713 context.current.transform, matrix, FloatPoint3D(), |
| 643 context.current.shouldFlattenInheritedTransform, | 714 context.current.shouldFlattenInheritedTransform, |
| 644 context.current.renderingContextID); | 715 context.current.renderingContextID); |
| 645 | 716 |
| 646 IntSize scrollClip = scrollableArea->visibleContentRect().size(); | 717 IntSize scrollClip = scrollableArea->visibleContentRect().size(); |
| 647 IntSize scrollBounds = scrollableArea->contentsSize(); | 718 IntSize scrollBounds = scrollableArea->contentsSize(); |
| 648 bool userScrollableHorizontal = | 719 bool userScrollableHorizontal = |
| 649 scrollableArea->userInputScrollable(HorizontalScrollbar); | 720 scrollableArea->userInputScrollable(HorizontalScrollbar); |
| 650 bool userScrollableVertical = | 721 bool userScrollableVertical = |
| 651 scrollableArea->userInputScrollable(VerticalScrollbar); | 722 scrollableArea->userInputScrollable(VerticalScrollbar); |
| 652 context.current.scroll = | 723 object.getMutableForPainting().ensurePaintProperties().updateScroll( |
| 653 object.getMutableForPainting().ensurePaintProperties().updateScroll( | 724 context.current.scroll, |
| 654 context.current.scroll, context.current.transform, scrollClip, | 725 object.paintProperties()->scrollTranslation(), scrollClip, |
| 655 scrollBounds, userScrollableHorizontal, userScrollableVertical); | 726 scrollBounds, userScrollableHorizontal, userScrollableVertical); |
| 656 | 727 } else { |
| 657 context.current.shouldFlattenInheritedTransform = false; | 728 // Ensure pre-existing properties are cleared when there is no |
| 658 return; | 729 // scrolling. |
| 730 auto* properties = object.getMutableForPainting().paintProperties(); |
| 731 if (properties) { |
| 732 properties->clearScrollTranslation(); |
| 733 properties->clearScroll(); |
| 734 } |
| 735 } |
| 659 } | 736 } |
| 660 } | 737 } |
| 661 | 738 |
| 662 if (auto* properties = object.getMutableForPainting().paintProperties()) { | 739 if (object.paintProperties() && object.paintProperties()->scroll()) { |
| 663 properties->clearScrollTranslation(); | 740 context.current.transform = object.paintProperties()->scrollTranslation(); |
| 664 properties->clearScroll(); | 741 const auto* scroll = object.paintProperties()->scroll(); |
| 742 // TODO(pdr): Remove this const cast. |
| 743 context.current.scroll = const_cast<ScrollPaintPropertyNode*>(scroll); |
| 744 context.current.shouldFlattenInheritedTransform = false; |
| 665 } | 745 } |
| 666 } | 746 } |
| 667 | 747 |
| 668 void PaintPropertyTreeBuilder::updateOutOfFlowContext( | 748 void PaintPropertyTreeBuilder::updateOutOfFlowContext( |
| 669 const LayoutObject& object, | 749 const LayoutObject& object, |
| 670 PaintPropertyTreeBuilderContext& context) { | 750 PaintPropertyTreeBuilderContext& context) { |
| 671 if (object.canContainAbsolutePositionObjects()) { | 751 if (object.canContainAbsolutePositionObjects()) { |
| 672 context.absolutePosition = context.current; | 752 context.absolutePosition = context.current; |
| 673 context.containerForAbsolutePosition = &object; | 753 context.containerForAbsolutePosition = &object; |
| 674 } | 754 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 694 // absolute position container. However for fixed-position descendants we | 774 // absolute position container. However for fixed-position descendants we |
| 695 // need to insert the clip here if we are not a containing block ancestor of | 775 // need to insert the clip here if we are not a containing block ancestor of |
| 696 // them. | 776 // them. |
| 697 auto* cssClip = object.getMutableForPainting().paintProperties()->cssClip(); | 777 auto* cssClip = object.getMutableForPainting().paintProperties()->cssClip(); |
| 698 | 778 |
| 699 // Before we actually create anything, check whether in-flow context and | 779 // Before we actually create anything, check whether in-flow context and |
| 700 // fixed-position context has exactly the same clip. Reuse if possible. | 780 // fixed-position context has exactly the same clip. Reuse if possible. |
| 701 if (context.fixedPosition.clip == cssClip->parent()) { | 781 if (context.fixedPosition.clip == cssClip->parent()) { |
| 702 context.fixedPosition.clip = cssClip; | 782 context.fixedPosition.clip = cssClip; |
| 703 } else { | 783 } else { |
| 704 context.fixedPosition.clip = | 784 if (object.needsPaintPropertyUpdate()) { |
| 705 object.getMutableForPainting() | 785 object.getMutableForPainting() |
| 706 .ensurePaintProperties() | 786 .ensurePaintProperties() |
| 707 .updateCssClipFixedPosition( | 787 .updateCssClipFixedPosition(context.fixedPosition.clip, |
| 708 context.fixedPosition.clip, | 788 const_cast<TransformPaintPropertyNode*>( |
| 709 const_cast<TransformPaintPropertyNode*>( | 789 cssClip->localTransformSpace()), |
| 710 cssClip->localTransformSpace()), | 790 cssClip->clipRect()); |
| 711 cssClip->clipRect()); | 791 } |
| 792 const auto* properties = object.paintProperties(); |
| 793 if (properties && properties->cssClipFixedPosition()) |
| 794 context.fixedPosition.clip = properties->cssClipFixedPosition(); |
| 712 return; | 795 return; |
| 713 } | 796 } |
| 714 } | 797 } |
| 715 | 798 |
| 716 if (auto* properties = object.getMutableForPainting().paintProperties()) | 799 if (object.needsPaintPropertyUpdate()) { |
| 717 properties->clearCssClipFixedPosition(); | 800 if (auto* properties = object.getMutableForPainting().paintProperties()) |
| 801 properties->clearCssClipFixedPosition(); |
| 802 } |
| 718 } | 803 } |
| 719 | 804 |
| 720 // Override ContainingBlockContext based on the properties of a containing block | 805 // Override ContainingBlockContext based on the properties of a containing block |
| 721 // that was previously walked in a subtree other than the current subtree being | 806 // that was previously walked in a subtree other than the current subtree being |
| 722 // walked. Used for out-of-flow positioned descendants of multi-column spanner | 807 // walked. Used for out-of-flow positioned descendants of multi-column spanner |
| 723 // when the containing block is not in the normal tree walk order. | 808 // when the containing block is not in the normal tree walk order. |
| 724 // For example: | 809 // For example: |
| 725 // <div id="columns" style="columns: 2"> | 810 // <div id="columns" style="columns: 2"> |
| 726 // <div id="relative" style="position: relative"> | 811 // <div id="relative" style="position: relative"> |
| 727 // <div id="spanner" style="column-span: all"> | 812 // <div id="spanner" style="column-span: all"> |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 832 // Similar adjustment is done in LayoutTableCell::offsetFromContainer(). | 917 // Similar adjustment is done in LayoutTableCell::offsetFromContainer(). |
| 833 if (boxModelObject.isTableCell()) { | 918 if (boxModelObject.isTableCell()) { |
| 834 LayoutObject* parentRow = boxModelObject.parent(); | 919 LayoutObject* parentRow = boxModelObject.parent(); |
| 835 ASSERT(parentRow && parentRow->isTableRow()); | 920 ASSERT(parentRow && parentRow->isTableRow()); |
| 836 context.current.paintOffset.moveBy( | 921 context.current.paintOffset.moveBy( |
| 837 -toLayoutBox(parentRow)->topLeftLocation()); | 922 -toLayoutBox(parentRow)->topLeftLocation()); |
| 838 } | 923 } |
| 839 } | 924 } |
| 840 } | 925 } |
| 841 | 926 |
| 842 void PaintPropertyTreeBuilder::buildTreeNodesForSelf( | 927 void PaintPropertyTreeBuilder::updatePropertiesForSelf( |
| 843 const LayoutObject& object, | 928 const LayoutObject& object, |
| 844 PaintPropertyTreeBuilderContext& context) { | 929 PaintPropertyTreeBuilderContext& context) { |
| 845 if (!object.isBoxModelObject() && !object.isSVG()) | 930 if (!object.isBoxModelObject() && !object.isSVG()) |
| 846 return; | 931 return; |
| 847 | 932 |
| 933 #if DCHECK_IS_ON() |
| 934 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object); |
| 935 #endif |
| 936 |
| 848 deriveBorderBoxFromContainerContext(object, context); | 937 deriveBorderBoxFromContainerContext(object, context); |
| 849 | 938 |
| 850 updatePaintOffsetTranslation(object, context); | 939 updatePaintOffsetTranslation(object, context); |
| 851 updateTransform(object, context); | 940 updateTransform(object, context); |
| 852 updateEffect(object, context); | 941 updateEffect(object, context); |
| 853 updateCssClip(object, context); | 942 updateCssClip(object, context); |
| 854 updateLocalBorderBoxContext(object, context); | 943 updateLocalBorderBoxContext(object, context); |
| 855 updateScrollbarPaintOffset(object, context); | 944 updateScrollbarPaintOffset(object, context); |
| 856 updateMainThreadScrollingReasons(object, context); | 945 updateMainThreadScrollingReasons(object, context); |
| 857 } | 946 } |
| 858 | 947 |
| 859 void PaintPropertyTreeBuilder::buildTreeNodesForChildren( | 948 void PaintPropertyTreeBuilder::updatePropertiesForChildren( |
| 860 const LayoutObject& object, | 949 const LayoutObject& object, |
| 861 PaintPropertyTreeBuilderContext& context) { | 950 PaintPropertyTreeBuilderContext& context) { |
| 862 if (!object.isBoxModelObject() && !object.isSVG()) | 951 if (!object.isBoxModelObject() && !object.isSVG()) |
| 863 return; | 952 return; |
| 864 | 953 |
| 954 #if DCHECK_IS_ON() |
| 955 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object); |
| 956 #endif |
| 957 |
| 865 updateOverflowClip(object, context); | 958 updateOverflowClip(object, context); |
| 866 updatePerspective(object, context); | 959 updatePerspective(object, context); |
| 867 updateSvgLocalToBorderBoxTransform(object, context); | 960 updateSvgLocalToBorderBoxTransform(object, context); |
| 868 updateScrollAndScrollTranslation(object, context); | 961 updateScrollAndScrollTranslation(object, context); |
| 869 updateOutOfFlowContext(object, context); | 962 updateOutOfFlowContext(object, context); |
| 870 } | 963 } |
| 871 | 964 |
| 872 } // namespace blink | 965 } // namespace blink |
| OLD | NEW |