| // 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. |
| |
| #ifndef ObjectPaintProperties_h |
| #define ObjectPaintProperties_h |
| |
| #include <memory> |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "core/CoreExport.h" |
| #include "platform/graphics/paint/ClipPaintPropertyNode.h" |
| #include "platform/graphics/paint/EffectPaintPropertyNode.h" |
| #include "platform/graphics/paint/ScrollPaintPropertyNode.h" |
| #include "platform/graphics/paint/TransformPaintPropertyNode.h" |
| #include "platform/wtf/PtrUtil.h" |
| |
| namespace blink { |
| |
| // This class stores the paint property nodes created by a LayoutObject. The |
| // object owns each of the property nodes directly and RefPtrs are only used to |
| // harden against use-after-free bugs. These paint properties are built/updated |
| // by PaintPropertyTreeBuilder during the PrePaint lifecycle step. |
| // |
| // [update & clear implementation note] This class has Update[property](...) and |
| // Clear[property]() helper functions for efficiently creating and updating |
| // properties. The update functions returns a 3-state result to indicate whether |
| // the value or the existence of the node has changed. They use a create-or- |
| // update pattern of re-using existing properties for efficiency: |
| // 1. It avoids extra allocations. |
| // 2. It preserves existing child->parent pointers. |
| // The clear functions return true if an existing node is removed. Property |
| // nodes store parent pointers but not child pointers and these return values |
| // are important for catching property tree structure changes which require |
| // updating descendant's parent pointers. |
| class CORE_EXPORT ObjectPaintProperties { |
| USING_FAST_MALLOC(ObjectPaintProperties); |
| |
| public: |
| static std::unique_ptr<ObjectPaintProperties> Create() { |
| return WTF::WrapUnique(new ObjectPaintProperties()); |
| } |
| |
| // The hierarchy of the transform subtree created by a LayoutObject is as |
| // follows: |
| // [ paintOffsetTranslation ] Normally paint offset is accumulated |
| // | without creating a node until we see, |
| // | for example, transform or |
| // | position:fixed. |
| // +---[ transform ] The space created by CSS transform. |
| // | This is the local border box space. |
| // +---[ perspective ] The space created by CSS perspective. |
| // +---[ svgLocalToBorderBoxTransform ] Additional transform for |
| // children of the outermost root SVG. |
| // OR (SVG does not support scrolling.) |
| // +---[ scrollTranslation ] The space created by overflow clip. |
| const TransformPaintPropertyNode* PaintOffsetTranslation() const { |
| return paint_offset_translation_.get(); |
| } |
| const TransformPaintPropertyNode* Transform() const { |
| return transform_.get(); |
| } |
| const TransformPaintPropertyNode* Perspective() const { |
| return perspective_.get(); |
| } |
| const TransformPaintPropertyNode* SvgLocalToBorderBoxTransform() const { |
| return svg_local_to_border_box_transform_.get(); |
| } |
| const ScrollPaintPropertyNode* Scroll() const { return scroll_.get(); } |
| const TransformPaintPropertyNode* ScrollTranslation() const { |
| return scroll_translation_.get(); |
| } |
| |
| // The hierarchy of the effect subtree created by a LayoutObject is as |
| // follows: |
| // [ effect ] |
| // | Isolated group to apply various CSS effects, including opacity, |
| // | mix-blend-mode, and for isolation if a mask needs to be applied or |
| // | backdrop-dependent children are present. |
| // +-[ filter ] |
| // | Isolated group for CSS filter. |
| // +-[ mask ] |
| // Isolated group for painting the CSS mask. This node will have |
| // SkBlendMode::kDstIn and shall paint last, i.e. after masked contents. |
| const EffectPaintPropertyNode* Effect() const { return effect_.get(); } |
| const EffectPaintPropertyNode* Filter() const { return filter_.get(); } |
| const EffectPaintPropertyNode* Mask() const { return mask_.get(); } |
| |
| // The hierarchy of the clip subtree created by a LayoutObject is as follows: |
| // [ fragment clip ] |
| // | Clips to a fragment's bounds. |
| // This is only present for content under a fragmentation |
| // container. |
| // +-[ mask clip ] |
| // | Clip created by CSS mask. It serves two purposes: |
| // | 1. Cull painting of the masked subtree. Because anything outside of |
| // | the mask is never visible, it is pointless to paint them. |
| // | 2. Raster clip of the masked subtree. Because the mask implemented |
| // | as SkBlendMode::kDstIn, pixels outside of mask's bound will be |
| // | intact when they shall be masked out. This clip ensures no pixels |
| // | leak out. |
| // +-[ css clip ] |
| // | Clip created by CSS clip. CSS clip applies to all descendants, this |
| // | node only applies to containing block descendants. For descendants |
| // | not contained by this object, use [ css clip fixed position ]. |
| // +-[ inner border radius clip] |
| // | Clip created by a rounded border with overflow clip. This clip is |
| // | not inset by scrollbars. |
| // +-[ overflow clip ] |
| // Clip created by overflow clip and is inset by the scrollbar. |
| // [ css clip fixed position ] |
| // Clip created by CSS clip. Only exists if the current clip includes |
| // some clip that doesn't apply to our fixed position descendants. |
| const ClipPaintPropertyNode* FragmentClip() const { |
| return fragment_clip_.get(); |
| } |
| const ClipPaintPropertyNode* MaskClip() const { return mask_clip_.get(); } |
| const ClipPaintPropertyNode* CssClip() const { return css_clip_.get(); } |
| const ClipPaintPropertyNode* CssClipFixedPosition() const { |
| return css_clip_fixed_position_.get(); |
| } |
| const ClipPaintPropertyNode* InnerBorderRadiusClip() const { |
| return inner_border_radius_clip_.get(); |
| } |
| const ClipPaintPropertyNode* OverflowClip() const { |
| return overflow_clip_.get(); |
| } |
| |
| // The following clear* functions return true if the property tree structure |
| // changes (an existing node was deleted), and false otherwise. See the |
| // class-level comment ("update & clear implementation note") for details |
| // about why this is needed for efficient updates. |
| bool ClearPaintOffsetTranslation() { |
| return Clear(paint_offset_translation_); |
| } |
| bool ClearTransform() { return Clear(transform_); } |
| bool ClearEffect() { return Clear(effect_); } |
| bool ClearFilter() { return Clear(filter_); } |
| bool ClearMask() { return Clear(mask_); } |
| bool ClearFragmentClip() { return Clear(fragment_clip_); } |
| bool ClearMaskClip() { return Clear(mask_clip_); } |
| bool ClearCssClip() { return Clear(css_clip_); } |
| bool ClearCssClipFixedPosition() { return Clear(css_clip_fixed_position_); } |
| bool ClearInnerBorderRadiusClip() { return Clear(inner_border_radius_clip_); } |
| bool ClearOverflowClip() { return Clear(overflow_clip_); } |
| bool ClearPerspective() { return Clear(perspective_); } |
| bool ClearSvgLocalToBorderBoxTransform() { |
| return Clear(svg_local_to_border_box_transform_); |
| } |
| bool ClearScroll() { return Clear(scroll_); } |
| bool ClearScrollTranslation() { return Clear(scroll_translation_); } |
| |
| class UpdateResult { |
| public: |
| bool ValueChanged() const { return result_ == kValueChanged; } |
| bool NewNodeCreated() const { return result_ == kNewNodeCreated; } |
| |
| private: |
| friend class ObjectPaintProperties; |
| enum Result { kUnchanged, kValueChanged, kNewNodeCreated }; |
| UpdateResult(Result r) : result_(r) {} |
| Result result_; |
| }; |
| |
| template <typename... Args> |
| UpdateResult UpdatePaintOffsetTranslation(Args&&... args) { |
| return Update(paint_offset_translation_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateTransform(Args&&... args) { |
| return Update(transform_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdatePerspective(Args&&... args) { |
| return Update(perspective_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateSvgLocalToBorderBoxTransform(Args&&... args) { |
| DCHECK(!ScrollTranslation()) << "SVG elements cannot scroll so there " |
| "should never be both a scroll translation " |
| "and an SVG local to border box transform."; |
| return Update(svg_local_to_border_box_transform_, |
| std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateScroll(Args&&... args) { |
| return Update(scroll_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateScrollTranslation(Args&&... args) { |
| DCHECK(!SvgLocalToBorderBoxTransform()) |
| << "SVG elements cannot scroll so there should never be both a scroll " |
| "translation and an SVG local to border box transform."; |
| return Update(scroll_translation_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateEffect(Args&&... args) { |
| return Update(effect_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateFilter(Args&&... args) { |
| return Update(filter_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateMask(Args&&... args) { |
| return Update(mask_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateFragmentClip(Args&&... args) { |
| return Update(fragment_clip_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateMaskClip(Args&&... args) { |
| return Update(mask_clip_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateCssClip(Args&&... args) { |
| return Update(css_clip_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateCssClipFixedPosition(Args&&... args) { |
| return Update(css_clip_fixed_position_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateInnerBorderRadiusClip(Args&&... args) { |
| return Update(inner_border_radius_clip_, std::forward<Args>(args)...); |
| } |
| template <typename... Args> |
| UpdateResult UpdateOverflowClip(Args&&... args) { |
| return Update(overflow_clip_, std::forward<Args>(args)...); |
| } |
| |
| #if DCHECK_IS_ON() |
| // Used by FindPropertiesNeedingUpdate.h for recording the current properties. |
| std::unique_ptr<ObjectPaintProperties> Clone() const { |
| std::unique_ptr<ObjectPaintProperties> cloned = Create(); |
| if (paint_offset_translation_) |
| cloned->paint_offset_translation_ = paint_offset_translation_->Clone(); |
| if (transform_) |
| cloned->transform_ = transform_->Clone(); |
| if (effect_) |
| cloned->effect_ = effect_->Clone(); |
| if (filter_) |
| cloned->filter_ = filter_->Clone(); |
| if (mask_) |
| cloned->mask_ = mask_->Clone(); |
| if (fragment_clip_) |
| cloned->fragment_clip_ = fragment_clip_->Clone(); |
| if (mask_clip_) |
| cloned->mask_clip_ = mask_clip_->Clone(); |
| if (css_clip_) |
| cloned->css_clip_ = css_clip_->Clone(); |
| if (css_clip_fixed_position_) |
| cloned->css_clip_fixed_position_ = css_clip_fixed_position_->Clone(); |
| if (inner_border_radius_clip_) |
| cloned->inner_border_radius_clip_ = inner_border_radius_clip_->Clone(); |
| if (overflow_clip_) |
| cloned->overflow_clip_ = overflow_clip_->Clone(); |
| if (perspective_) |
| cloned->perspective_ = perspective_->Clone(); |
| if (svg_local_to_border_box_transform_) { |
| cloned->svg_local_to_border_box_transform_ = |
| svg_local_to_border_box_transform_->Clone(); |
| } |
| if (scroll_) |
| cloned->scroll_ = scroll_->Clone(); |
| if (scroll_translation_) |
| cloned->scroll_translation_ = scroll_translation_->Clone(); |
| return cloned; |
| } |
| #endif |
| |
| private: |
| ObjectPaintProperties() = default; |
| |
| // Return true if the property tree structure changes (an existing node was |
| // deleted), and false otherwise. See the class-level comment ("update & clear |
| // implementation note") for details about why this is needed for efficiency. |
| template <typename PaintPropertyNode> |
| bool Clear(scoped_refptr<PaintPropertyNode>& field) { |
| if (field) { |
| field = nullptr; |
| return true; |
| } |
| return false; |
| } |
| |
| // Return true if the property tree structure changes (a new node was |
| // created), and false otherwise. See the class-level comment ("update & clear |
| // implementation note") for details about why this is needed for efficiency. |
| template <typename PaintPropertyNode, typename... Args> |
| UpdateResult Update(scoped_refptr<PaintPropertyNode>& field, Args&&... args) { |
| if (field) { |
| return field->Update(std::forward<Args>(args)...) |
| ? UpdateResult::kValueChanged |
| : UpdateResult::kUnchanged; |
| } |
| field = PaintPropertyNode::Create(std::forward<Args>(args)...); |
| return UpdateResult::kNewNodeCreated; |
| } |
| |
| // ATTENTION! Make sure to keep FindPropertiesNeedingUpdate.h in sync when |
| // new properites are added! |
| scoped_refptr<TransformPaintPropertyNode> paint_offset_translation_; |
| scoped_refptr<TransformPaintPropertyNode> transform_; |
| scoped_refptr<EffectPaintPropertyNode> effect_; |
| scoped_refptr<EffectPaintPropertyNode> filter_; |
| scoped_refptr<EffectPaintPropertyNode> mask_; |
| scoped_refptr<ClipPaintPropertyNode> fragment_clip_; |
| scoped_refptr<ClipPaintPropertyNode> mask_clip_; |
| scoped_refptr<ClipPaintPropertyNode> css_clip_; |
| scoped_refptr<ClipPaintPropertyNode> css_clip_fixed_position_; |
| scoped_refptr<ClipPaintPropertyNode> inner_border_radius_clip_; |
| scoped_refptr<ClipPaintPropertyNode> overflow_clip_; |
| scoped_refptr<TransformPaintPropertyNode> perspective_; |
| // TODO(pdr): Only LayoutSVGRoot needs this and it should be moved there. |
| scoped_refptr<TransformPaintPropertyNode> svg_local_to_border_box_transform_; |
| scoped_refptr<ScrollPaintPropertyNode> scroll_; |
| scoped_refptr<TransformPaintPropertyNode> scroll_translation_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ObjectPaintProperties); |
| }; |
| |
| } // namespace blink |
| |
| #endif // ObjectPaintProperties_h |