1 /*
2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org>
4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2009 Google, Inc. All rights reserved.
6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "third_party/blink/renderer/core/layout/svg/layout_svg_container.h"
25
26 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
27 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
28 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
29 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
30 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
31 #include "third_party/blink/renderer/core/layout/svg/transform_helper.h"
32 #include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
33 #include "third_party/blink/renderer/core/paint/svg_container_painter.h"
34
35 namespace blink {
36
LayoutSVGContainer(SVGElement * node)37 LayoutSVGContainer::LayoutSVGContainer(SVGElement* node)
38 : LayoutSVGModelObject(node),
39 object_bounding_box_valid_(false),
40 needs_boundaries_update_(true),
41 did_screen_scale_factor_change_(false),
42 has_non_isolated_blending_descendants_(false),
43 has_non_isolated_blending_descendants_dirty_(false) {}
44
45 LayoutSVGContainer::~LayoutSVGContainer() = default;
46
UpdateLayout()47 void LayoutSVGContainer::UpdateLayout() {
48 DCHECK(NeedsLayout());
49 LayoutAnalyzer::Scope analyzer(*this);
50
51 // Update the local transform in subclasses.
52 SVGTransformChange transform_change = CalculateLocalTransform();
53 did_screen_scale_factor_change_ =
54 transform_change == SVGTransformChange::kFull ||
55 SVGLayoutSupport::ScreenScaleFactorChanged(Parent());
56
57 // When hasRelativeLengths() is false, no descendants have relative lengths
58 // (hence no one is interested in viewport size changes).
59 bool layout_size_changed =
60 GetElement()->HasRelativeLengths() &&
61 SVGLayoutSupport::LayoutSizeOfNearestViewportChanged(this);
62
63 SVGLayoutSupport::LayoutChildren(FirstChild(), false,
64 did_screen_scale_factor_change_,
65 layout_size_changed);
66
67 // Invalidate all resources of this client if our layout changed.
68 if (EverHadLayout() && NeedsLayout())
69 SVGResourcesCache::ClientLayoutChanged(*this);
70
71 if (needs_boundaries_update_ ||
72 transform_change != SVGTransformChange::kNone) {
73 UpdateCachedBoundaries();
74 needs_boundaries_update_ = false;
75
76 // If our bounds changed, notify the parents.
77 LayoutSVGModelObject::SetNeedsBoundariesUpdate();
78 }
79
80 DCHECK(!needs_boundaries_update_);
81 ClearNeedsLayout();
82 }
83
AddChild(LayoutObject * child,LayoutObject * before_child)84 void LayoutSVGContainer::AddChild(LayoutObject* child,
85 LayoutObject* before_child) {
86 LayoutSVGModelObject::AddChild(child, before_child);
87 SVGResourcesCache::ClientWasAddedToTree(*child);
88
89 bool should_isolate_descendants =
90 (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) ||
91 child->HasNonIsolatedBlendingDescendants();
92 if (should_isolate_descendants)
93 DescendantIsolationRequirementsChanged(kDescendantIsolationRequired);
94 }
95
RemoveChild(LayoutObject * child)96 void LayoutSVGContainer::RemoveChild(LayoutObject* child) {
97 SVGResourcesCache::ClientWillBeRemovedFromTree(*child);
98 LayoutSVGModelObject::RemoveChild(child);
99
100 bool had_non_isolated_descendants =
101 (child->IsBlendingAllowed() && child->StyleRef().HasBlendMode()) ||
102 child->HasNonIsolatedBlendingDescendants();
103 if (had_non_isolated_descendants)
104 DescendantIsolationRequirementsChanged(kDescendantIsolationNeedsUpdate);
105 }
106
SelfWillPaint() const107 bool LayoutSVGContainer::SelfWillPaint() const {
108 return SVGLayoutSupport::HasFilterResource(*this);
109 }
110
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)111 void LayoutSVGContainer::StyleDidChange(StyleDifference diff,
112 const ComputedStyle* old_style) {
113 LayoutSVGModelObject::StyleDidChange(diff, old_style);
114
115 bool had_isolation =
116 old_style && !IsSVGHiddenContainer() &&
117 SVGLayoutSupport::WillIsolateBlendingDescendantsForStyle(*old_style);
118
119 bool will_isolate_blending_descendants =
120 SVGLayoutSupport::WillIsolateBlendingDescendantsForObject(this);
121
122 bool isolation_changed = had_isolation != will_isolate_blending_descendants;
123
124 if (isolation_changed)
125 SetNeedsPaintPropertyUpdate();
126
127 if (!Parent() || !isolation_changed)
128 return;
129
130 if (HasNonIsolatedBlendingDescendants()) {
131 Parent()->DescendantIsolationRequirementsChanged(
132 will_isolate_blending_descendants ? kDescendantIsolationNeedsUpdate
133 : kDescendantIsolationRequired);
134 }
135 }
136
HasNonIsolatedBlendingDescendants() const137 bool LayoutSVGContainer::HasNonIsolatedBlendingDescendants() const {
138 if (has_non_isolated_blending_descendants_dirty_) {
139 has_non_isolated_blending_descendants_ =
140 SVGLayoutSupport::ComputeHasNonIsolatedBlendingDescendants(this);
141 has_non_isolated_blending_descendants_dirty_ = false;
142 }
143 return has_non_isolated_blending_descendants_;
144 }
145
DescendantIsolationRequirementsChanged(DescendantIsolationState state)146 void LayoutSVGContainer::DescendantIsolationRequirementsChanged(
147 DescendantIsolationState state) {
148 switch (state) {
149 case kDescendantIsolationRequired:
150 has_non_isolated_blending_descendants_ = true;
151 has_non_isolated_blending_descendants_dirty_ = false;
152 break;
153 case kDescendantIsolationNeedsUpdate:
154 if (has_non_isolated_blending_descendants_dirty_)
155 return;
156 has_non_isolated_blending_descendants_dirty_ = true;
157 break;
158 }
159 if (SVGLayoutSupport::WillIsolateBlendingDescendantsForObject(this)) {
160 SetNeedsPaintPropertyUpdate();
161 return;
162 }
163 if (Parent())
164 Parent()->DescendantIsolationRequirementsChanged(state);
165 }
166
Paint(const PaintInfo & paint_info) const167 void LayoutSVGContainer::Paint(const PaintInfo& paint_info) const {
168 SVGContainerPainter(*this).Paint(paint_info);
169 }
170
UpdateCachedBoundaries()171 void LayoutSVGContainer::UpdateCachedBoundaries() {
172 SVGLayoutSupport::ComputeContainerBoundingBoxes(
173 this, object_bounding_box_, object_bounding_box_valid_,
174 stroke_bounding_box_, local_visual_rect_);
175 }
176
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction hit_test_action)177 bool LayoutSVGContainer::NodeAtPoint(HitTestResult& result,
178 const HitTestLocation& hit_test_location,
179 const PhysicalOffset& accumulated_offset,
180 HitTestAction hit_test_action) {
181 DCHECK_EQ(accumulated_offset, PhysicalOffset());
182 TransformedHitTestLocation local_location(hit_test_location,
183 LocalToSVGParentTransform());
184 if (!local_location)
185 return false;
186 if (!SVGLayoutSupport::IntersectsClipPath(*this, object_bounding_box_,
187 *local_location))
188 return false;
189
190 if (!PaintBlockedByDisplayLock(DisplayLockLifecycleTarget::kChildren) &&
191 SVGLayoutSupport::HitTestChildren(LastChild(), result, *local_location,
192 accumulated_offset, hit_test_action))
193 return true;
194
195 // pointer-events: bounding-box makes it possible for containers to be direct
196 // targets.
197 if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) {
198 // Check for a valid bounding box because it will be invalid for empty
199 // containers.
200 if (IsObjectBoundingBoxValid() &&
201 local_location->Intersects(ObjectBoundingBox())) {
202 UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
203 local_location->TransformedPoint()));
204 if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
205 kStopHitTesting)
206 return true;
207 }
208 }
209 // 16.4: "If there are no graphics elements whose relevant graphics content is
210 // under the pointer (i.e., there is no target element), the event is not
211 // dispatched."
212 return false;
213 }
214
CalculateLocalTransform()215 SVGTransformChange LayoutSVGContainer::CalculateLocalTransform() {
216 return SVGTransformChange::kNone;
217 }
218
219 } // namespace blink
220