1 /*
2 * Copyright (C) 2006 Apple Computer, Inc.
3 * Copyright (C) 2009 Google, Inc.
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h"
23
24 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
25 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
26 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
27 #include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
28 #include "third_party/blink/renderer/core/paint/paint_layer.h"
29 #include "third_party/blink/renderer/core/paint/svg_foreign_object_painter.h"
30 #include "third_party/blink/renderer/core/svg/svg_foreign_object_element.h"
31
32 namespace blink {
33
LayoutSVGForeignObject(SVGForeignObjectElement * node)34 LayoutSVGForeignObject::LayoutSVGForeignObject(SVGForeignObjectElement* node)
35 : LayoutSVGBlock(node), needs_transform_update_(true) {}
36
37 LayoutSVGForeignObject::~LayoutSVGForeignObject() = default;
38
IsChildAllowed(LayoutObject * child,const ComputedStyle & style) const39 bool LayoutSVGForeignObject::IsChildAllowed(LayoutObject* child,
40 const ComputedStyle& style) const {
41 // Disallow arbitary SVG content. Only allow proper <svg xmlns="svgNS">
42 // subdocuments.
43 return !child->IsSVGChild();
44 }
45
Paint(const PaintInfo & paint_info) const46 void LayoutSVGForeignObject::Paint(const PaintInfo& paint_info) const {
47 SVGForeignObjectPainter(*this).Paint(paint_info);
48 }
49
ElementX() const50 LayoutUnit LayoutSVGForeignObject::ElementX() const {
51 return LayoutUnit(
52 roundf(SVGLengthContext(GetElement())
53 .ValueForLength(StyleRef().SvgStyle().X(), StyleRef(),
54 SVGLengthMode::kWidth)));
55 }
56
ElementY() const57 LayoutUnit LayoutSVGForeignObject::ElementY() const {
58 return LayoutUnit(
59 roundf(SVGLengthContext(GetElement())
60 .ValueForLength(StyleRef().SvgStyle().Y(), StyleRef(),
61 SVGLengthMode::kHeight)));
62 }
63
ElementWidth() const64 LayoutUnit LayoutSVGForeignObject::ElementWidth() const {
65 return LayoutUnit(SVGLengthContext(GetElement())
66 .ValueForLength(StyleRef().Width(), StyleRef(),
67 SVGLengthMode::kWidth));
68 }
69
ElementHeight() const70 LayoutUnit LayoutSVGForeignObject::ElementHeight() const {
71 return LayoutUnit(SVGLengthContext(GetElement())
72 .ValueForLength(StyleRef().Height(), StyleRef(),
73 SVGLengthMode::kHeight));
74 }
75
UpdateLogicalWidth()76 void LayoutSVGForeignObject::UpdateLogicalWidth() {
77 SetLogicalWidth(StyleRef().IsHorizontalWritingMode() ? ElementWidth()
78 : ElementHeight());
79 }
80
ComputeLogicalHeight(LayoutUnit,LayoutUnit logical_top,LogicalExtentComputedValues & computed_values) const81 void LayoutSVGForeignObject::ComputeLogicalHeight(
82 LayoutUnit,
83 LayoutUnit logical_top,
84 LogicalExtentComputedValues& computed_values) const {
85 computed_values.extent_ =
86 StyleRef().IsHorizontalWritingMode() ? ElementHeight() : ElementWidth();
87 computed_values.position_ = logical_top;
88 }
89
UpdateLayout()90 void LayoutSVGForeignObject::UpdateLayout() {
91 DCHECK(NeedsLayout());
92
93 auto* foreign = To<SVGForeignObjectElement>(GetElement());
94
95 bool update_cached_boundaries_in_parents = false;
96 if (needs_transform_update_) {
97 local_transform_ =
98 foreign->CalculateTransform(SVGElement::kIncludeMotionTransform);
99 needs_transform_update_ = false;
100 update_cached_boundaries_in_parents = true;
101 }
102
103 LayoutRect old_viewport = FrameRect();
104
105 // Set box origin to the foreignObject x/y translation, so positioned objects
106 // in XHTML content get correct positions. A regular LayoutBoxModelObject
107 // would pull this information from ComputedStyle - in SVG those properties
108 // are ignored for non <svg> elements, so we mimic what happens when
109 // specifying them through CSS.
110 SetX(ElementX());
111 SetY(ElementY());
112
113 bool layout_changed = EverHadLayout() && SelfNeedsLayout();
114 LayoutBlock::UpdateLayout();
115 DCHECK(!NeedsLayout());
116
117 // If our bounds changed, notify the parents.
118 if (!update_cached_boundaries_in_parents)
119 update_cached_boundaries_in_parents = old_viewport != FrameRect();
120 if (update_cached_boundaries_in_parents)
121 LayoutSVGBlock::SetNeedsBoundariesUpdate();
122
123 // Invalidate all resources of this client if our layout changed.
124 if (layout_changed)
125 SVGResourcesCache::ClientLayoutChanged(*this);
126 }
127
NodeAtPointFromSVG(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction)128 bool LayoutSVGForeignObject::NodeAtPointFromSVG(
129 HitTestResult& result,
130 const HitTestLocation& hit_test_location,
131 const PhysicalOffset& accumulated_offset,
132 HitTestAction) {
133 DCHECK_EQ(accumulated_offset, PhysicalOffset());
134 TransformedHitTestLocation local_location(hit_test_location,
135 LocalSVGTransform());
136 if (!local_location)
137 return false;
138
139 // |local_location| already includes the offset of the <foreignObject>
140 // element, but PaintLayer::HitTestLayer assumes it has not been.
141 HitTestLocation local_without_offset(*local_location, -PhysicalLocation());
142 HitTestResult layer_result(result.GetHitTestRequest(), local_without_offset);
143 layer_result.SetInertNode(result.InertNode());
144 bool retval = Layer()->HitTest(local_without_offset, layer_result,
145 PhysicalRect(PhysicalRect::InfiniteIntRect()));
146
147 // Preserve the "point in inner node frame" from the original request,
148 // since |layer_result| is a hit test rooted at the <foreignObject> element,
149 // not the frame, due to the constructor above using
150 // |point_in_foreign_object| as its "point in inner node frame".
151 // TODO(chrishtr): refactor the PaintLayer and HitTestResults code around
152 // this, to better support hit tests that don't start at frame boundaries.
153 PhysicalOffset original_point_in_inner_node_frame =
154 result.PointInInnerNodeFrame();
155 if (result.GetHitTestRequest().ListBased())
156 result.Append(layer_result);
157 else
158 result = layer_result;
159 result.SetPointInInnerNodeFrame(original_point_in_inner_node_frame);
160 return retval;
161 }
162
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction hit_test_action)163 bool LayoutSVGForeignObject::NodeAtPoint(
164 HitTestResult& result,
165 const HitTestLocation& hit_test_location,
166 const PhysicalOffset& accumulated_offset,
167 HitTestAction hit_test_action) {
168 // Skip LayoutSVGBlock's override.
169 return LayoutBlockFlow::NodeAtPoint(result, hit_test_location,
170 accumulated_offset, hit_test_action);
171 }
172
LayerTypeRequired() const173 PaintLayerType LayoutSVGForeignObject::LayerTypeRequired() const {
174 // Skip LayoutSVGBlock's override.
175 return LayoutBlockFlow::LayerTypeRequired();
176 }
177
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)178 void LayoutSVGForeignObject::StyleDidChange(StyleDifference diff,
179 const ComputedStyle* old_style) {
180 LayoutSVGBlock::StyleDidChange(diff, old_style);
181
182 if (old_style && (SVGLayoutSupport::IsOverflowHidden(*old_style) !=
183 SVGLayoutSupport::IsOverflowHidden(StyleRef()))) {
184 // See NeedsOverflowClip() in PaintPropertyTreeBuilder for the reason.
185 SetNeedsPaintPropertyUpdate();
186
187 if (Layer())
188 Layer()->SetNeedsCompositingInputsUpdate();
189 }
190 }
191
192 } // namespace blink
193