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