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