1 /*
2  * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
3  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@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  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
26 
27 #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
28 #include "third_party/blink/renderer/core/layout/layout_geometry_map.h"
29 #include "third_party/blink/renderer/core/layout/subtree_layout_scope.h"
30 #include "third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.h"
31 #include "third_party/blink/renderer/core/layout/svg/layout_svg_image.h"
32 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
33 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h"
34 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h"
35 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
36 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
37 #include "third_party/blink/renderer/core/layout/svg/layout_svg_shape.h"
38 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
39 #include "third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.h"
40 #include "third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h"
41 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
42 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
43 #include "third_party/blink/renderer/core/page/page.h"
44 #include "third_party/blink/renderer/core/paint/paint_layer.h"
45 #include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
46 #include "third_party/blink/renderer/core/svg/svg_element.h"
47 #include "third_party/blink/renderer/core/svg/svg_length_context.h"
48 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
49 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
50 
51 namespace blink {
52 
53 struct SearchCandidate {
SearchCandidateblink::SearchCandidate54   SearchCandidate()
55       : layout_object(nullptr), distance(std::numeric_limits<float>::max()) {}
SearchCandidateblink::SearchCandidate56   SearchCandidate(LayoutObject* layout_object, float distance)
57       : layout_object(layout_object), distance(distance) {}
58   LayoutObject* layout_object;
59   float distance;
60 };
61 
LocalVisualRect(const LayoutObject & object)62 FloatRect SVGLayoutSupport::LocalVisualRect(const LayoutObject& object) {
63   // For LayoutSVGRoot, use LayoutSVGRoot::localVisualRect() instead.
64   DCHECK(!object.IsSVGRoot());
65 
66   // Return early for any cases where we don't actually paint
67   if (object.StyleRef().Visibility() != EVisibility::kVisible &&
68       !object.EnclosingLayer()->HasVisibleContent())
69     return FloatRect();
70 
71   FloatRect visual_rect = object.VisualRectInLocalSVGCoordinates();
72   if (int outline_outset = object.StyleRef().OutlineOutsetExtent())
73     visual_rect.Inflate(outline_outset);
74   return visual_rect;
75 }
76 
VisualRectInAncestorSpace(const LayoutObject & object,const LayoutBoxModelObject & ancestor,VisualRectFlags flags)77 PhysicalRect SVGLayoutSupport::VisualRectInAncestorSpace(
78     const LayoutObject& object,
79     const LayoutBoxModelObject& ancestor,
80     VisualRectFlags flags) {
81   PhysicalRect rect;
82   MapToVisualRectInAncestorSpace(object, &ancestor, LocalVisualRect(object),
83                                  rect, flags);
84   return rect;
85 }
86 
TransformVisualRect(const LayoutObject & object,const AffineTransform & root_transform,const FloatRect & local_rect)87 PhysicalRect SVGLayoutSupport::TransformVisualRect(
88     const LayoutObject& object,
89     const AffineTransform& root_transform,
90     const FloatRect& local_rect) {
91   FloatRect adjusted_rect = root_transform.MapRect(local_rect);
92 
93   if (adjusted_rect.IsEmpty())
94     return PhysicalRect();
95 
96   // Use EnclosingIntRect because we cannot properly apply subpixel offset of
97   // the SVGRoot since we don't know the desired subpixel accumulation at this
98   // point.
99   return PhysicalRect(EnclosingIntRect(adjusted_rect));
100 }
101 
ComputeTransformToSVGRoot(const LayoutObject & object,AffineTransform & root_border_box_transform)102 static const LayoutSVGRoot& ComputeTransformToSVGRoot(
103     const LayoutObject& object,
104     AffineTransform& root_border_box_transform) {
105   DCHECK(object.IsSVGChild());
106 
107   const LayoutObject* parent;
108   for (parent = &object; !parent->IsSVGRoot(); parent = parent->Parent())
109     root_border_box_transform.PreMultiply(parent->LocalToSVGParentTransform());
110 
111   const LayoutSVGRoot& svg_root = ToLayoutSVGRoot(*parent);
112   root_border_box_transform.PreMultiply(svg_root.LocalToBorderBoxTransform());
113   return svg_root;
114 }
115 
MapToVisualRectInAncestorSpace(const LayoutObject & object,const LayoutBoxModelObject * ancestor,const FloatRect & local_visual_rect,PhysicalRect & result_rect,VisualRectFlags visual_rect_flags)116 bool SVGLayoutSupport::MapToVisualRectInAncestorSpace(
117     const LayoutObject& object,
118     const LayoutBoxModelObject* ancestor,
119     const FloatRect& local_visual_rect,
120     PhysicalRect& result_rect,
121     VisualRectFlags visual_rect_flags) {
122   AffineTransform root_border_box_transform;
123   const LayoutSVGRoot& svg_root =
124       ComputeTransformToSVGRoot(object, root_border_box_transform);
125   result_rect =
126       TransformVisualRect(object, root_border_box_transform, local_visual_rect);
127 
128   // Apply initial viewport clip.
129   if (svg_root.ShouldApplyViewportClip()) {
130     PhysicalRect clip_rect(svg_root.OverflowClipRect(PhysicalOffset()));
131     if (visual_rect_flags & kEdgeInclusive) {
132       if (!result_rect.InclusiveIntersect(clip_rect))
133         return false;
134     } else {
135       result_rect.Intersect(clip_rect);
136     }
137   }
138   return svg_root.MapToVisualRectInAncestorSpace(ancestor, result_rect,
139                                                  visual_rect_flags);
140 }
141 
MapLocalToAncestor(const LayoutObject * object,const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags flags)142 void SVGLayoutSupport::MapLocalToAncestor(const LayoutObject* object,
143                                           const LayoutBoxModelObject* ancestor,
144                                           TransformState& transform_state,
145                                           MapCoordinatesFlags flags) {
146   transform_state.ApplyTransform(object->LocalToSVGParentTransform());
147 
148   LayoutObject* parent = object->Parent();
149 
150   // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the
151   // localToBorderBoxTransform to map an element from SVG viewport coordinates
152   // to CSS box coordinates.
153   // LayoutSVGRoot's mapLocalToAncestor method expects CSS box coordinates.
154   if (parent->IsSVGRoot())
155     transform_state.ApplyTransform(
156         ToLayoutSVGRoot(parent)->LocalToBorderBoxTransform());
157 
158   parent->MapLocalToAncestor(ancestor, transform_state, flags);
159 }
160 
MapAncestorToLocal(const LayoutObject & object,const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags flags)161 void SVGLayoutSupport::MapAncestorToLocal(const LayoutObject& object,
162                                           const LayoutBoxModelObject* ancestor,
163                                           TransformState& transform_state,
164                                           MapCoordinatesFlags flags) {
165   // |object| is either a LayoutSVGModelObject or a LayoutSVGBlock here. In
166   // the former case, |object| can never be an ancestor while in the latter
167   // the caller is responsible for doing the ancestor check. Because of this,
168   // computing the transform to the SVG root is always what we want to do here.
169   DCHECK_NE(ancestor, &object);
170   DCHECK(object.IsSVGContainer() || object.IsSVGShape() ||
171          object.IsSVGImage() || object.IsSVGText() ||
172          object.IsSVGForeignObject());
173   AffineTransform local_to_svg_root;
174   const LayoutSVGRoot& svg_root =
175       ComputeTransformToSVGRoot(object, local_to_svg_root);
176 
177   svg_root.MapAncestorToLocal(ancestor, transform_state, flags);
178 
179   transform_state.ApplyTransform(local_to_svg_root);
180 }
181 
PushMappingToContainer(const LayoutObject * object,const LayoutBoxModelObject * ancestor_to_stop_at,LayoutGeometryMap & geometry_map)182 const LayoutObject* SVGLayoutSupport::PushMappingToContainer(
183     const LayoutObject* object,
184     const LayoutBoxModelObject* ancestor_to_stop_at,
185     LayoutGeometryMap& geometry_map) {
186   DCHECK_NE(ancestor_to_stop_at, object);
187 
188   LayoutObject* parent = object->Parent();
189 
190   // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the
191   // localToBorderBoxTransform to map an element from SVG viewport coordinates
192   // to CSS box coordinates.
193   // LayoutSVGRoot's mapLocalToAncestor method expects CSS box coordinates.
194   if (parent->IsSVGRoot()) {
195     TransformationMatrix matrix(
196         ToLayoutSVGRoot(parent)->LocalToBorderBoxTransform());
197     matrix.Multiply(object->LocalToSVGParentTransform());
198     geometry_map.Push(object, matrix);
199   } else {
200     geometry_map.Push(object, object->LocalToSVGParentTransform());
201   }
202 
203   return parent;
204 }
205 
206 // Update a bounding box taking into account the validity of the other bounding
207 // box.
UpdateObjectBoundingBox(FloatRect & object_bounding_box,bool & object_bounding_box_valid,LayoutObject * other,FloatRect other_bounding_box)208 inline void SVGLayoutSupport::UpdateObjectBoundingBox(
209     FloatRect& object_bounding_box,
210     bool& object_bounding_box_valid,
211     LayoutObject* other,
212     FloatRect other_bounding_box) {
213   auto* svg_container = DynamicTo<LayoutSVGContainer>(other);
214   bool other_valid =
215       svg_container ? svg_container->IsObjectBoundingBoxValid() : true;
216   if (!other_valid)
217     return;
218 
219   if (!object_bounding_box_valid) {
220     object_bounding_box = other_bounding_box;
221     object_bounding_box_valid = true;
222     return;
223   }
224 
225   object_bounding_box.UniteEvenIfEmpty(other_bounding_box);
226 }
227 
HasValidBoundingBoxForContainer(const LayoutObject * object)228 static bool HasValidBoundingBoxForContainer(const LayoutObject* object) {
229   if (object->IsSVGShape())
230     return !ToLayoutSVGShape(object)->IsShapeEmpty();
231 
232   if (object->IsSVGText())
233     return ToLayoutSVGText(object)->IsObjectBoundingBoxValid();
234 
235   if (object->IsSVGHiddenContainer())
236     return false;
237 
238   if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(object))
239     return foreign_object->IsObjectBoundingBoxValid();
240 
241   if (object->IsSVGImage())
242     return ToLayoutSVGImage(object)->IsObjectBoundingBoxValid();
243 
244   // TODO(fs): Can we refactor this code to include the container case
245   // in a more natural way?
246   return true;
247 }
248 
ComputeContainerBoundingBoxes(const LayoutObject * container,FloatRect & object_bounding_box,bool & object_bounding_box_valid,FloatRect & stroke_bounding_box,FloatRect & local_visual_rect)249 void SVGLayoutSupport::ComputeContainerBoundingBoxes(
250     const LayoutObject* container,
251     FloatRect& object_bounding_box,
252     bool& object_bounding_box_valid,
253     FloatRect& stroke_bounding_box,
254     FloatRect& local_visual_rect) {
255   object_bounding_box = FloatRect();
256   object_bounding_box_valid = false;
257   stroke_bounding_box = FloatRect();
258 
259   // When computing the strokeBoundingBox, we use the visualRects of
260   // the container's children so that the container's stroke includes the
261   // resources applied to the children (such as clips and filters). This allows
262   // filters applied to containers to correctly bound the children, and also
263   // improves inlining of SVG content, as the stroke bound is used in that
264   // situation also.
265   for (LayoutObject* current = container->SlowFirstChild(); current;
266        current = current->NextSibling()) {
267     // Don't include elements that are not rendered in the union.
268     if (!HasValidBoundingBoxForContainer(current))
269       continue;
270 
271     const AffineTransform& transform = current->LocalToSVGParentTransform();
272     UpdateObjectBoundingBox(object_bounding_box, object_bounding_box_valid,
273                             current,
274                             transform.MapRect(current->ObjectBoundingBox()));
275     stroke_bounding_box.Unite(
276         transform.MapRect(current->VisualRectInLocalSVGCoordinates()));
277   }
278 
279   local_visual_rect = stroke_bounding_box;
280   AdjustVisualRectWithResources(*container, object_bounding_box,
281                                 local_visual_rect);
282 }
283 
LayoutSizeOfNearestViewportChanged(const LayoutObject * start)284 bool SVGLayoutSupport::LayoutSizeOfNearestViewportChanged(
285     const LayoutObject* start) {
286   for (; start; start = start->Parent()) {
287     if (start->IsSVGRoot())
288       return ToLayoutSVGRoot(start)->IsLayoutSizeChanged();
289     if (start->IsSVGViewportContainer())
290       return ToLayoutSVGViewportContainer(start)->IsLayoutSizeChanged();
291   }
292   NOTREACHED();
293   return false;
294 }
295 
ScreenScaleFactorChanged(const LayoutObject * ancestor)296 bool SVGLayoutSupport::ScreenScaleFactorChanged(const LayoutObject* ancestor) {
297   for (; ancestor; ancestor = ancestor->Parent()) {
298     if (ancestor->IsSVGRoot())
299       return ToLayoutSVGRoot(ancestor)->DidScreenScaleFactorChange();
300     if (ancestor->IsSVGTransformableContainer())
301       return ToLayoutSVGTransformableContainer(ancestor)
302           ->DidScreenScaleFactorChange();
303     if (ancestor->IsSVGViewportContainer())
304       return ToLayoutSVGViewportContainer(ancestor)
305           ->DidScreenScaleFactorChange();
306   }
307   NOTREACHED();
308   return false;
309 }
310 
LayoutChildren(LayoutObject * first_child,bool force_layout,bool screen_scaling_factor_changed,bool layout_size_changed)311 void SVGLayoutSupport::LayoutChildren(LayoutObject* first_child,
312                                       bool force_layout,
313                                       bool screen_scaling_factor_changed,
314                                       bool layout_size_changed) {
315   for (LayoutObject* child = first_child; child; child = child->NextSibling()) {
316     bool force_child_layout = force_layout;
317 
318     if (screen_scaling_factor_changed) {
319       // If the screen scaling factor changed we need to update the text
320       // metrics (note: this also happens for layoutSizeChanged=true).
321       if (child->IsSVGText())
322         ToLayoutSVGText(child)->SetNeedsTextMetricsUpdate();
323       force_child_layout = true;
324     }
325 
326     if (layout_size_changed) {
327       // When selfNeedsLayout is false and the layout size changed, we have to
328       // check whether this child uses relative lengths
329       if (auto* element = DynamicTo<SVGElement>(child->GetNode())) {
330         if (element->HasRelativeLengths()) {
331           // FIXME: this should be done on invalidation, not during layout.
332           // When the layout size changed and when using relative values tell
333           // the LayoutSVGShape to update its shape object
334           if (child->IsSVGShape()) {
335             ToLayoutSVGShape(child)->SetNeedsShapeUpdate();
336           } else if (child->IsSVGText()) {
337             ToLayoutSVGText(child)->SetNeedsTextMetricsUpdate();
338             ToLayoutSVGText(child)->SetNeedsPositioningValuesUpdate();
339           }
340 
341           force_child_layout = true;
342         }
343       }
344     }
345 
346     // Resource containers are nasty: they can invalidate clients outside the
347     // current SubtreeLayoutScope.
348     // Since they only care about viewport size changes (to resolve their
349     // relative lengths), we trigger their invalidation directly from
350     // SVGSVGElement::svgAttributeChange() or at a higher SubtreeLayoutScope (in
351     // LayoutView::layout()). We do not create a SubtreeLayoutScope for
352     // resources because their ability to reference each other leads to circular
353     // layout. We protect against that within the layout code for resources, but
354     // it causes assertions if we use a SubTreeLayoutScope for them.
355     if (child->IsSVGResourceContainer()) {
356       // Lay out any referenced resources before the child.
357       LayoutResourcesIfNeeded(*child);
358       child->LayoutIfNeeded();
359     } else {
360       SubtreeLayoutScope layout_scope(*child);
361       if (force_child_layout) {
362         layout_scope.SetNeedsLayout(child,
363                                     layout_invalidation_reason::kSvgChanged);
364       }
365 
366       // Lay out any referenced resources before the child.
367       LayoutResourcesIfNeeded(*child);
368       child->LayoutIfNeeded();
369     }
370   }
371 }
372 
LayoutResourcesIfNeeded(const LayoutObject & object)373 void SVGLayoutSupport::LayoutResourcesIfNeeded(const LayoutObject& object) {
374   SVGResources* resources =
375       SVGResourcesCache::CachedResourcesForLayoutObject(object);
376   if (resources)
377     resources->LayoutIfNeeded();
378 }
379 
IsOverflowHidden(const LayoutObject & object)380 bool SVGLayoutSupport::IsOverflowHidden(const LayoutObject& object) {
381   // LayoutSVGRoot should never query for overflow state - it should always clip
382   // itself to the initial viewport size.
383   DCHECK(!object.IsDocumentElement());
384   return IsOverflowHidden(object.StyleRef());
385 }
386 
IsOverflowHidden(const ComputedStyle & style)387 bool SVGLayoutSupport::IsOverflowHidden(const ComputedStyle& style) {
388   return style.OverflowX() == EOverflow::kHidden ||
389          style.OverflowX() == EOverflow::kScroll;
390 }
391 
AdjustVisualRectWithResources(const LayoutObject & layout_object,const FloatRect & object_bounding_box,FloatRect & visual_rect)392 void SVGLayoutSupport::AdjustVisualRectWithResources(
393     const LayoutObject& layout_object,
394     const FloatRect& object_bounding_box,
395     FloatRect& visual_rect) {
396   SVGResources* resources =
397       SVGResourcesCache::CachedResourcesForLayoutObject(layout_object);
398   if (!resources)
399     return;
400 
401   if (LayoutSVGResourceFilter* filter = resources->Filter())
402     visual_rect = filter->ResourceBoundingBox(object_bounding_box);
403 
404   if (LayoutSVGResourceClipper* clipper = resources->Clipper())
405     visual_rect.Intersect(clipper->ResourceBoundingBox(object_bounding_box));
406 
407   if (LayoutSVGResourceMasker* masker = resources->Masker())
408     visual_rect.Intersect(masker->ResourceBoundingBox(object_bounding_box));
409 }
410 
ExtendTextBBoxWithStroke(const LayoutObject & layout_object,const FloatRect & text_bounds)411 FloatRect SVGLayoutSupport::ExtendTextBBoxWithStroke(
412     const LayoutObject& layout_object,
413     const FloatRect& text_bounds) {
414   DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
415   FloatRect bounds = text_bounds;
416   const SVGComputedStyle& svg_style = layout_object.StyleRef().SvgStyle();
417   if (svg_style.HasStroke()) {
418     SVGLengthContext length_context(To<SVGElement>(layout_object.GetNode()));
419     // TODO(fs): This approximation doesn't appear to be conservative enough
420     // since while text (usually?) won't have caps it could have joins and thus
421     // miters.
422     bounds.Inflate(length_context.ValueForLength(svg_style.StrokeWidth()));
423   }
424   return bounds;
425 }
426 
ComputeVisualRectForText(const LayoutObject & layout_object,const FloatRect & text_bounds,const FloatRect & reference_box)427 FloatRect SVGLayoutSupport::ComputeVisualRectForText(
428     const LayoutObject& layout_object,
429     const FloatRect& text_bounds,
430     const FloatRect& reference_box) {
431   DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
432   FloatRect visual_rect = ExtendTextBBoxWithStroke(layout_object, text_bounds);
433   if (const ShadowList* text_shadow = layout_object.StyleRef().TextShadow())
434     text_shadow->AdjustRectForShadow(visual_rect);
435   AdjustVisualRectWithResources(layout_object, reference_box, visual_rect);
436   return visual_rect;
437 }
438 
HasFilterResource(const LayoutObject & object)439 bool SVGLayoutSupport::HasFilterResource(const LayoutObject& object) {
440   SVGResources* resources =
441       SVGResourcesCache::CachedResourcesForLayoutObject(object);
442   return resources && resources->Filter();
443 }
444 
IntersectsClipPath(const LayoutObject & object,const FloatRect & reference_box,const HitTestLocation & location)445 bool SVGLayoutSupport::IntersectsClipPath(const LayoutObject& object,
446                                           const FloatRect& reference_box,
447                                           const HitTestLocation& location) {
448   ClipPathOperation* clip_path_operation = object.StyleRef().ClipPath();
449   if (!clip_path_operation)
450     return true;
451   if (clip_path_operation->GetType() == ClipPathOperation::SHAPE) {
452     ShapeClipPathOperation& clip_path =
453         To<ShapeClipPathOperation>(*clip_path_operation);
454     return clip_path.GetPath(reference_box)
455         .Contains(location.TransformedPoint());
456   }
457   DCHECK_EQ(clip_path_operation->GetType(), ClipPathOperation::REFERENCE);
458   SVGResources* resources =
459       SVGResourcesCache::CachedResourcesForLayoutObject(object);
460   if (!resources || !resources->Clipper())
461     return true;
462   return resources->Clipper()->HitTestClipContent(reference_box, location);
463 }
464 
HitTestChildren(LayoutObject * last_child,HitTestResult & result,const HitTestLocation & location,const PhysicalOffset & accumulated_offset,HitTestAction hit_test_action)465 bool SVGLayoutSupport::HitTestChildren(LayoutObject* last_child,
466                                        HitTestResult& result,
467                                        const HitTestLocation& location,
468                                        const PhysicalOffset& accumulated_offset,
469                                        HitTestAction hit_test_action) {
470   for (LayoutObject* child = last_child; child;
471        child = child->PreviousSibling()) {
472     if (auto* foreign_object = DynamicTo<LayoutSVGForeignObject>(child)) {
473       if (foreign_object->NodeAtPointFromSVG(
474               result, location, accumulated_offset, hit_test_action))
475         return true;
476     } else {
477       if (child->NodeAtPoint(result, location, accumulated_offset,
478                              hit_test_action))
479         return true;
480     }
481   }
482   return false;
483 }
484 
ResolveSVGDashArray(const SVGDashArray & svg_dash_array,const ComputedStyle & style,const SVGLengthContext & length_context)485 DashArray SVGLayoutSupport::ResolveSVGDashArray(
486     const SVGDashArray& svg_dash_array,
487     const ComputedStyle& style,
488     const SVGLengthContext& length_context) {
489   DashArray dash_array;
490   for (const Length& dash_length : svg_dash_array.data)
491     dash_array.push_back(length_context.ValueForLength(dash_length, style));
492   return dash_array;
493 }
494 
ApplyStrokeStyleToStrokeData(StrokeData & stroke_data,const ComputedStyle & style,const LayoutObject & object,float dash_scale_factor)495 void SVGLayoutSupport::ApplyStrokeStyleToStrokeData(StrokeData& stroke_data,
496                                                     const ComputedStyle& style,
497                                                     const LayoutObject& object,
498                                                     float dash_scale_factor) {
499   DCHECK(object.GetNode());
500   DCHECK(object.GetNode()->IsSVGElement());
501 
502   const SVGComputedStyle& svg_style = style.SvgStyle();
503 
504   SVGLengthContext length_context(To<SVGElement>(object.GetNode()));
505   stroke_data.SetThickness(
506       length_context.ValueForLength(svg_style.StrokeWidth()));
507   stroke_data.SetLineCap(svg_style.CapStyle());
508   stroke_data.SetLineJoin(svg_style.JoinStyle());
509   stroke_data.SetMiterLimit(svg_style.StrokeMiterLimit());
510 
511   DashArray dash_array =
512       ResolveSVGDashArray(*svg_style.StrokeDashArray(), style, length_context);
513   float dash_offset =
514       length_context.ValueForLength(svg_style.StrokeDashOffset(), style);
515   // Apply scaling from 'pathLength'.
516   if (dash_scale_factor != 1) {
517     DCHECK_GE(dash_scale_factor, 0);
518     dash_offset *= dash_scale_factor;
519     for (auto& dash_item : dash_array)
520       dash_item *= dash_scale_factor;
521   }
522   stroke_data.SetLineDash(dash_array, dash_offset);
523 }
524 
IsLayoutableTextNode(const LayoutObject * object)525 bool SVGLayoutSupport::IsLayoutableTextNode(const LayoutObject* object) {
526   DCHECK(object->IsText());
527   // <br> is marked as text, but is not handled by the SVG layout code-path.
528   return object->IsSVGInlineText() &&
529          !ToLayoutSVGInlineText(object)->HasEmptyText();
530 }
531 
WillIsolateBlendingDescendantsForStyle(const ComputedStyle & style)532 bool SVGLayoutSupport::WillIsolateBlendingDescendantsForStyle(
533     const ComputedStyle& style) {
534   const SVGComputedStyle& svg_style = style.SvgStyle();
535 
536   return style.HasIsolation() || style.HasOpacity() || style.HasBlendMode() ||
537          style.HasFilter() || svg_style.HasMasker() || style.ClipPath();
538 }
539 
WillIsolateBlendingDescendantsForObject(const LayoutObject * object)540 bool SVGLayoutSupport::WillIsolateBlendingDescendantsForObject(
541     const LayoutObject* object) {
542   if (object->IsSVGHiddenContainer())
543     return false;
544   if (!object->IsSVGRoot() && !object->IsSVGContainer())
545     return false;
546   return WillIsolateBlendingDescendantsForStyle(object->StyleRef());
547 }
548 
IsIsolationRequired(const LayoutObject * object)549 bool SVGLayoutSupport::IsIsolationRequired(const LayoutObject* object) {
550   return WillIsolateBlendingDescendantsForObject(object) &&
551          object->HasNonIsolatedBlendingDescendants();
552 }
553 
554 AffineTransform::Transform
555     SubtreeContentTransformScope::current_content_transformation_ =
556         IDENTITY_TRANSFORM;
557 
SubtreeContentTransformScope(const AffineTransform & subtree_content_transformation)558 SubtreeContentTransformScope::SubtreeContentTransformScope(
559     const AffineTransform& subtree_content_transformation)
560     : saved_content_transformation_(current_content_transformation_) {
561   AffineTransform content_transformation =
562       subtree_content_transformation *
563       AffineTransform(current_content_transformation_);
564   content_transformation.CopyTransformTo(current_content_transformation_);
565 }
566 
~SubtreeContentTransformScope()567 SubtreeContentTransformScope::~SubtreeContentTransformScope() {
568   saved_content_transformation_.CopyTransformTo(
569       current_content_transformation_);
570 }
571 
DeprecatedCalculateTransformToLayer(const LayoutObject * layout_object)572 AffineTransform SVGLayoutSupport::DeprecatedCalculateTransformToLayer(
573     const LayoutObject* layout_object) {
574   AffineTransform transform;
575   while (layout_object) {
576     transform = layout_object->LocalToSVGParentTransform() * transform;
577     if (layout_object->IsSVGRoot())
578       break;
579     layout_object = layout_object->Parent();
580   }
581 
582   // Continue walking up the layer tree, accumulating CSS transforms.
583   // FIXME: this queries layer compositing state - which is not
584   // supported during layout. Hence, the result may not include all CSS
585   // transforms.
586   PaintLayer* layer = layout_object ? layout_object->EnclosingLayer() : nullptr;
587   while (layer && layer->IsAllowedToQueryCompositingState()) {
588     // We can stop at compositing layers, to match the backing resolution.
589     // FIXME: should we be computing the transform to the nearest composited
590     // layer, or the nearest composited layer that does not paint into its
591     // ancestor? I think this is the nearest composited ancestor since we will
592     // inherit its transforms in the composited layer tree.
593     if (layer->GetCompositingState() != kNotComposited)
594       break;
595 
596     if (TransformationMatrix* layer_transform = layer->Transform())
597       transform = layer_transform->ToAffineTransform() * transform;
598 
599     layer = layer->Parent();
600   }
601 
602   return transform;
603 }
604 
CalculateScreenFontSizeScalingFactor(const LayoutObject * layout_object)605 float SVGLayoutSupport::CalculateScreenFontSizeScalingFactor(
606     const LayoutObject* layout_object) {
607   DCHECK(layout_object);
608 
609   // FIXME: trying to compute a device space transform at record time is wrong.
610   // All clients should be updated to avoid relying on this information, and the
611   // method should be removed.
612   AffineTransform ctm =
613       DeprecatedCalculateTransformToLayer(layout_object) *
614       SubtreeContentTransformScope::CurrentContentTransformation();
615   ctm.Scale(
616       layout_object->GetDocument().GetPage()->DeviceScaleFactorDeprecated());
617 
618   return clampTo<float>(sqrt((ctm.XScaleSquared() + ctm.YScaleSquared()) / 2));
619 }
620 
CompareCandidateDistance(const SearchCandidate & r1,const SearchCandidate & r2)621 static inline bool CompareCandidateDistance(const SearchCandidate& r1,
622                                             const SearchCandidate& r2) {
623   return r1.distance < r2.distance;
624 }
625 
DistanceToChildLayoutObject(LayoutObject * child,const FloatPoint & point)626 static inline float DistanceToChildLayoutObject(LayoutObject* child,
627                                                 const FloatPoint& point) {
628   const AffineTransform& local_to_parent_transform =
629       child->LocalToSVGParentTransform();
630   if (!local_to_parent_transform.IsInvertible())
631     return std::numeric_limits<float>::max();
632   FloatPoint child_local_point =
633       local_to_parent_transform.Inverse().MapPoint(point);
634   return child->ObjectBoundingBox().SquaredDistanceTo(child_local_point);
635 }
636 
SearchTreeForFindClosestLayoutSVGText(const LayoutObject * layout_object,const FloatPoint & point)637 static SearchCandidate SearchTreeForFindClosestLayoutSVGText(
638     const LayoutObject* layout_object,
639     const FloatPoint& point) {
640   // Try to find the closest LayoutSVGText.
641   SearchCandidate closest_text;
642   Vector<SearchCandidate> candidates;
643 
644   // Find the closest LayoutSVGText on this tree level, and also collect any
645   // containers that could contain LayoutSVGTexts that are closer.
646   for (LayoutObject* child = layout_object->SlowLastChild(); child;
647        child = child->PreviousSibling()) {
648     if (child->IsSVGText()) {
649       float distance = DistanceToChildLayoutObject(child, point);
650       if (distance >= closest_text.distance)
651         continue;
652       candidates.clear();
653       closest_text.layout_object = child;
654       closest_text.distance = distance;
655       continue;
656     }
657 
658     if (child->IsSVGContainer() && !layout_object->IsSVGHiddenContainer()) {
659       float distance = DistanceToChildLayoutObject(child, point);
660       if (distance > closest_text.distance)
661         continue;
662       candidates.push_back(SearchCandidate(child, distance));
663     }
664   }
665 
666   // If a LayoutSVGText was found and there are no potentially closer sub-trees,
667   // just return |closestText|.
668   if (closest_text.layout_object && candidates.IsEmpty())
669     return closest_text;
670 
671   std::stable_sort(candidates.begin(), candidates.end(),
672                    CompareCandidateDistance);
673 
674   // Find the closest LayoutSVGText in the sub-trees in |candidates|.
675   // If a LayoutSVGText is found that is strictly closer than any previous
676   // candidate, then end the search.
677   for (const SearchCandidate& search_candidate : candidates) {
678     if (closest_text.distance < search_candidate.distance)
679       break;
680     LayoutObject* candidate_layout_object = search_candidate.layout_object;
681     FloatPoint candidate_local_point =
682         candidate_layout_object->LocalToSVGParentTransform().Inverse().MapPoint(
683             point);
684 
685     SearchCandidate candidate_text = SearchTreeForFindClosestLayoutSVGText(
686         candidate_layout_object, candidate_local_point);
687 
688     if (candidate_text.distance < closest_text.distance)
689       closest_text = candidate_text;
690   }
691 
692   return closest_text;
693 }
694 
FindClosestLayoutSVGText(const LayoutObject * layout_object,const FloatPoint & point)695 LayoutObject* SVGLayoutSupport::FindClosestLayoutSVGText(
696     const LayoutObject* layout_object,
697     const FloatPoint& point) {
698   return SearchTreeForFindClosestLayoutSVGText(layout_object, point)
699       .layout_object;
700 }
701 
702 }  // namespace blink
703