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/svg/layout_svg_inline_text.h"
30 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h"
31 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
32 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
33 #include "third_party/blink/renderer/core/layout/svg/layout_svg_transformable_container.h"
34 #include "third_party/blink/renderer/core/layout/svg/layout_svg_viewport_container.h"
35 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
36 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
37 #include "third_party/blink/renderer/core/page/page.h"
38 #include "third_party/blink/renderer/core/paint/paint_layer.h"
39 #include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
40 #include "third_party/blink/renderer/core/svg/svg_element.h"
41 #include "third_party/blink/renderer/core/svg/svg_length_context.h"
42 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
43 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
44 
45 namespace blink {
46 
47 struct SearchCandidate {
SearchCandidateblink::SearchCandidate48   SearchCandidate()
49       : layout_object(nullptr), distance(std::numeric_limits<float>::max()) {}
SearchCandidateblink::SearchCandidate50   SearchCandidate(LayoutObject* layout_object, float distance)
51       : layout_object(layout_object), distance(distance) {}
52   LayoutObject* layout_object;
53   float distance;
54 };
55 
LocalVisualRect(const LayoutObject & object)56 FloatRect SVGLayoutSupport::LocalVisualRect(const LayoutObject& object) {
57   // For LayoutSVGRoot, use LayoutSVGRoot::localVisualRect() instead.
58   DCHECK(!object.IsSVGRoot());
59 
60   // Return early for any cases where we don't actually paint
61   if (object.StyleRef().Visibility() != EVisibility::kVisible &&
62       !object.EnclosingLayer()->HasVisibleContent())
63     return FloatRect();
64 
65   FloatRect visual_rect = object.VisualRectInLocalSVGCoordinates();
66   if (int outline_outset = object.StyleRef().OutlineOutsetExtent())
67     visual_rect.Inflate(outline_outset);
68   return visual_rect;
69 }
70 
VisualRectInAncestorSpace(const LayoutObject & object,const LayoutBoxModelObject & ancestor,VisualRectFlags flags)71 PhysicalRect SVGLayoutSupport::VisualRectInAncestorSpace(
72     const LayoutObject& object,
73     const LayoutBoxModelObject& ancestor,
74     VisualRectFlags flags) {
75   PhysicalRect rect;
76   MapToVisualRectInAncestorSpace(object, &ancestor, LocalVisualRect(object),
77                                  rect, flags);
78   return rect;
79 }
80 
MapToSVGRootIncludingFilter(const LayoutObject & object,const FloatRect & local_visual_rect)81 static FloatRect MapToSVGRootIncludingFilter(
82     const LayoutObject& object,
83     const FloatRect& local_visual_rect) {
84   DCHECK(object.IsSVGChild());
85 
86   FloatRect visual_rect = local_visual_rect;
87   const LayoutObject* parent = &object;
88   for (; !parent->IsSVGRoot(); parent = parent->Parent()) {
89     const ComputedStyle& style = parent->StyleRef();
90     if (style.HasFilter())
91       visual_rect = style.Filter().MapRect(visual_rect);
92     visual_rect = parent->LocalToSVGParentTransform().MapRect(visual_rect);
93   }
94 
95   return To<LayoutSVGRoot>(*parent).LocalToBorderBoxTransform().MapRect(
96       visual_rect);
97 }
98 
ComputeTransformToSVGRoot(const LayoutObject & object,AffineTransform & root_border_box_transform,bool * filter_skipped)99 static const LayoutSVGRoot& ComputeTransformToSVGRoot(
100     const LayoutObject& object,
101     AffineTransform& root_border_box_transform,
102     bool* filter_skipped) {
103   DCHECK(object.IsSVGChild());
104 
105   const LayoutObject* parent = &object;
106   for (; !parent->IsSVGRoot(); parent = parent->Parent()) {
107     if (filter_skipped && parent->StyleRef().HasFilter())
108       *filter_skipped = true;
109     root_border_box_transform.PreMultiply(parent->LocalToSVGParentTransform());
110   }
111 
112   const auto& svg_root = To<LayoutSVGRoot>(*parent);
113   root_border_box_transform.PreMultiply(svg_root.LocalToBorderBoxTransform());
114   return svg_root;
115 }
116 
MapToVisualRectInAncestorSpace(const LayoutObject & object,const LayoutBoxModelObject * ancestor,const FloatRect & local_visual_rect,PhysicalRect & result_rect,VisualRectFlags visual_rect_flags)117 bool SVGLayoutSupport::MapToVisualRectInAncestorSpace(
118     const LayoutObject& object,
119     const LayoutBoxModelObject* ancestor,
120     const FloatRect& local_visual_rect,
121     PhysicalRect& result_rect,
122     VisualRectFlags visual_rect_flags) {
123   AffineTransform root_border_box_transform;
124   bool filter_skipped = false;
125   const LayoutSVGRoot& svg_root = ComputeTransformToSVGRoot(
126       object, root_border_box_transform, &filter_skipped);
127 
128   FloatRect adjusted_rect;
129   if (filter_skipped)
130     adjusted_rect = MapToSVGRootIncludingFilter(object, local_visual_rect);
131   else
132     adjusted_rect = root_border_box_transform.MapRect(local_visual_rect);
133 
134   if (adjusted_rect.IsEmpty()) {
135     result_rect = PhysicalRect();
136   } else {
137     // Use EnclosingIntRect because we cannot properly apply subpixel offset of
138     // the SVGRoot since we don't know the desired subpixel accumulation at this
139     // point.
140     result_rect = PhysicalRect(EnclosingIntRect(adjusted_rect));
141   }
142 
143   // Apply initial viewport clip.
144   if (svg_root.ShouldApplyViewportClip()) {
145     PhysicalRect clip_rect(svg_root.OverflowClipRect(PhysicalOffset()));
146     if (visual_rect_flags & kEdgeInclusive) {
147       if (!result_rect.InclusiveIntersect(clip_rect))
148         return false;
149     } else {
150       result_rect.Intersect(clip_rect);
151     }
152   }
153   return svg_root.MapToVisualRectInAncestorSpace(ancestor, result_rect,
154                                                  visual_rect_flags);
155 }
156 
MapLocalToAncestor(const LayoutObject * object,const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags flags)157 void SVGLayoutSupport::MapLocalToAncestor(const LayoutObject* object,
158                                           const LayoutBoxModelObject* ancestor,
159                                           TransformState& transform_state,
160                                           MapCoordinatesFlags flags) {
161   transform_state.ApplyTransform(object->LocalToSVGParentTransform());
162 
163   LayoutObject* parent = object->Parent();
164 
165   // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the
166   // localToBorderBoxTransform to map an element from SVG viewport coordinates
167   // to CSS box coordinates.
168   // LayoutSVGRoot's mapLocalToAncestor method expects CSS box coordinates.
169   if (parent->IsSVGRoot()) {
170     transform_state.ApplyTransform(
171         To<LayoutSVGRoot>(parent)->LocalToBorderBoxTransform());
172   }
173 
174   parent->MapLocalToAncestor(ancestor, transform_state, flags);
175 }
176 
MapAncestorToLocal(const LayoutObject & object,const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags flags)177 void SVGLayoutSupport::MapAncestorToLocal(const LayoutObject& object,
178                                           const LayoutBoxModelObject* ancestor,
179                                           TransformState& transform_state,
180                                           MapCoordinatesFlags flags) {
181   // |object| is either a LayoutSVGModelObject or a LayoutSVGBlock here. In
182   // the former case, |object| can never be an ancestor while in the latter
183   // the caller is responsible for doing the ancestor check. Because of this,
184   // computing the transform to the SVG root is always what we want to do here.
185   DCHECK_NE(ancestor, &object);
186   DCHECK(object.IsSVGContainer() || object.IsSVGShape() ||
187          object.IsSVGImage() || object.IsSVGText() ||
188          object.IsSVGForeignObject());
189   AffineTransform local_to_svg_root;
190   const LayoutSVGRoot& svg_root =
191       ComputeTransformToSVGRoot(object, local_to_svg_root, nullptr);
192 
193   svg_root.MapAncestorToLocal(ancestor, transform_state, flags);
194 
195   transform_state.ApplyTransform(local_to_svg_root);
196 }
197 
PushMappingToContainer(const LayoutObject * object,const LayoutBoxModelObject * ancestor_to_stop_at,LayoutGeometryMap & geometry_map)198 const LayoutObject* SVGLayoutSupport::PushMappingToContainer(
199     const LayoutObject* object,
200     const LayoutBoxModelObject* ancestor_to_stop_at,
201     LayoutGeometryMap& geometry_map) {
202   DCHECK_NE(ancestor_to_stop_at, object);
203 
204   LayoutObject* parent = object->Parent();
205 
206   // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the
207   // localToBorderBoxTransform to map an element from SVG viewport coordinates
208   // to CSS box coordinates.
209   // LayoutSVGRoot's mapLocalToAncestor method expects CSS box coordinates.
210   if (parent->IsSVGRoot()) {
211     TransformationMatrix matrix(
212         To<LayoutSVGRoot>(parent)->LocalToBorderBoxTransform());
213     matrix.Multiply(object->LocalToSVGParentTransform());
214     geometry_map.Push(object, matrix);
215   } else {
216     geometry_map.Push(object, object->LocalToSVGParentTransform());
217   }
218 
219   return parent;
220 }
221 
LayoutSizeOfNearestViewportChanged(const LayoutObject * start)222 bool SVGLayoutSupport::LayoutSizeOfNearestViewportChanged(
223     const LayoutObject* start) {
224   for (; start; start = start->Parent()) {
225     if (start->IsSVGRoot())
226       return To<LayoutSVGRoot>(start)->IsLayoutSizeChanged();
227     if (start->IsSVGViewportContainer())
228       return To<LayoutSVGViewportContainer>(start)->IsLayoutSizeChanged();
229   }
230   NOTREACHED();
231   return false;
232 }
233 
ScreenScaleFactorChanged(const LayoutObject * ancestor)234 bool SVGLayoutSupport::ScreenScaleFactorChanged(const LayoutObject* ancestor) {
235   for (; ancestor; ancestor = ancestor->Parent()) {
236     if (ancestor->IsSVGRoot())
237       return To<LayoutSVGRoot>(ancestor)->DidScreenScaleFactorChange();
238     if (ancestor->IsSVGTransformableContainer())
239       return To<LayoutSVGTransformableContainer>(ancestor)
240           ->DidScreenScaleFactorChange();
241     if (ancestor->IsSVGViewportContainer())
242       return To<LayoutSVGViewportContainer>(ancestor)
243           ->DidScreenScaleFactorChange();
244   }
245   NOTREACHED();
246   return false;
247 }
248 
IsOverflowHidden(const LayoutObject & object)249 bool SVGLayoutSupport::IsOverflowHidden(const LayoutObject& object) {
250   // LayoutSVGRoot should never query for overflow state - it should always clip
251   // itself to the initial viewport size.
252   DCHECK(!object.IsDocumentElement());
253   return IsOverflowHidden(object.StyleRef());
254 }
255 
IsOverflowHidden(const ComputedStyle & style)256 bool SVGLayoutSupport::IsOverflowHidden(const ComputedStyle& style) {
257   return style.OverflowX() == EOverflow::kHidden ||
258          style.OverflowX() == EOverflow::kScroll;
259 }
260 
AdjustWithClipPathAndMask(const LayoutObject & layout_object,const FloatRect & object_bounding_box,FloatRect & visual_rect)261 void SVGLayoutSupport::AdjustWithClipPathAndMask(
262     const LayoutObject& layout_object,
263     const FloatRect& object_bounding_box,
264     FloatRect& visual_rect) {
265   SVGResources* resources =
266       SVGResourcesCache::CachedResourcesForLayoutObject(layout_object);
267   if (!resources)
268     return;
269   if (LayoutSVGResourceClipper* clipper = resources->Clipper())
270     visual_rect.Intersect(clipper->ResourceBoundingBox(object_bounding_box));
271   if (LayoutSVGResourceMasker* masker = resources->Masker())
272     visual_rect.Intersect(masker->ResourceBoundingBox(object_bounding_box, 1));
273 }
274 
ExtendTextBBoxWithStroke(const LayoutObject & layout_object,const FloatRect & text_bounds)275 FloatRect SVGLayoutSupport::ExtendTextBBoxWithStroke(
276     const LayoutObject& layout_object,
277     const FloatRect& text_bounds) {
278   DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
279   FloatRect bounds = text_bounds;
280   const SVGComputedStyle& svg_style = layout_object.StyleRef().SvgStyle();
281   if (svg_style.HasStroke()) {
282     SVGLengthContext length_context(To<SVGElement>(layout_object.GetNode()));
283     // TODO(fs): This approximation doesn't appear to be conservative enough
284     // since while text (usually?) won't have caps it could have joins and thus
285     // miters.
286     bounds.Inflate(length_context.ValueForLength(svg_style.StrokeWidth()));
287   }
288   return bounds;
289 }
290 
ComputeVisualRectForText(const LayoutObject & layout_object,const FloatRect & text_bounds)291 FloatRect SVGLayoutSupport::ComputeVisualRectForText(
292     const LayoutObject& layout_object,
293     const FloatRect& text_bounds) {
294   DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
295   FloatRect visual_rect = ExtendTextBBoxWithStroke(layout_object, text_bounds);
296   if (const ShadowList* text_shadow = layout_object.StyleRef().TextShadow())
297     text_shadow->AdjustRectForShadow(visual_rect);
298   return visual_rect;
299 }
300 
IntersectsClipPath(const LayoutObject & object,const FloatRect & reference_box,const HitTestLocation & location)301 bool SVGLayoutSupport::IntersectsClipPath(const LayoutObject& object,
302                                           const FloatRect& reference_box,
303                                           const HitTestLocation& location) {
304   ClipPathOperation* clip_path_operation = object.StyleRef().ClipPath();
305   if (!clip_path_operation)
306     return true;
307   if (clip_path_operation->GetType() == ClipPathOperation::SHAPE) {
308     ShapeClipPathOperation& clip_path =
309         To<ShapeClipPathOperation>(*clip_path_operation);
310     return clip_path.GetPath(reference_box, 1)
311         .Contains(location.TransformedPoint());
312   }
313   DCHECK_EQ(clip_path_operation->GetType(), ClipPathOperation::REFERENCE);
314   SVGResources* resources =
315       SVGResourcesCache::CachedResourcesForLayoutObject(object);
316   if (!resources || !resources->Clipper())
317     return true;
318   return resources->Clipper()->HitTestClipContent(reference_box, location);
319 }
320 
ResolveSVGDashArray(const SVGDashArray & svg_dash_array,const ComputedStyle & style,const SVGLengthContext & length_context)321 DashArray SVGLayoutSupport::ResolveSVGDashArray(
322     const SVGDashArray& svg_dash_array,
323     const ComputedStyle& style,
324     const SVGLengthContext& length_context) {
325   DashArray dash_array;
326   for (const Length& dash_length : svg_dash_array.data)
327     dash_array.push_back(length_context.ValueForLength(dash_length, style));
328   return dash_array;
329 }
330 
ApplyStrokeStyleToStrokeData(StrokeData & stroke_data,const ComputedStyle & style,const LayoutObject & object,float dash_scale_factor)331 void SVGLayoutSupport::ApplyStrokeStyleToStrokeData(StrokeData& stroke_data,
332                                                     const ComputedStyle& style,
333                                                     const LayoutObject& object,
334                                                     float dash_scale_factor) {
335   DCHECK(object.GetNode());
336   DCHECK(object.GetNode()->IsSVGElement());
337 
338   const SVGComputedStyle& svg_style = style.SvgStyle();
339 
340   SVGLengthContext length_context(To<SVGElement>(object.GetNode()));
341   stroke_data.SetThickness(
342       length_context.ValueForLength(svg_style.StrokeWidth()));
343   stroke_data.SetLineCap(svg_style.CapStyle());
344   stroke_data.SetLineJoin(svg_style.JoinStyle());
345   stroke_data.SetMiterLimit(svg_style.StrokeMiterLimit());
346 
347   DashArray dash_array =
348       ResolveSVGDashArray(*svg_style.StrokeDashArray(), style, length_context);
349   float dash_offset =
350       length_context.ValueForLength(svg_style.StrokeDashOffset(), style);
351   // Apply scaling from 'pathLength'.
352   if (dash_scale_factor != 1) {
353     DCHECK_GE(dash_scale_factor, 0);
354     dash_offset *= dash_scale_factor;
355     for (auto& dash_item : dash_array)
356       dash_item *= dash_scale_factor;
357   }
358   stroke_data.SetLineDash(dash_array, dash_offset);
359 }
360 
IsLayoutableTextNode(const LayoutObject * object)361 bool SVGLayoutSupport::IsLayoutableTextNode(const LayoutObject* object) {
362   DCHECK(object->IsText());
363   // <br> is marked as text, but is not handled by the SVG layout code-path.
364   return object->IsSVGInlineText() &&
365          !To<LayoutSVGInlineText>(object)->HasEmptyText();
366 }
367 
WillIsolateBlendingDescendantsForStyle(const ComputedStyle & style)368 bool SVGLayoutSupport::WillIsolateBlendingDescendantsForStyle(
369     const ComputedStyle& style) {
370   return style.HasGroupingProperty(style.BoxReflect()) ||
371          style.SvgStyle().HasMasker();
372 }
373 
WillIsolateBlendingDescendantsForObject(const LayoutObject * object)374 bool SVGLayoutSupport::WillIsolateBlendingDescendantsForObject(
375     const LayoutObject* object) {
376   if (object->IsSVGHiddenContainer())
377     return false;
378   if (!object->IsSVGRoot() && !object->IsSVGContainer())
379     return false;
380   return WillIsolateBlendingDescendantsForStyle(object->StyleRef());
381 }
382 
IsIsolationRequired(const LayoutObject * object)383 bool SVGLayoutSupport::IsIsolationRequired(const LayoutObject* object) {
384   if (SVGResources* resources =
385           SVGResourcesCache::CachedResourcesForLayoutObject(*object)) {
386     if (resources->Masker())
387       return true;
388   }
389   return WillIsolateBlendingDescendantsForObject(object) &&
390          object->HasNonIsolatedBlendingDescendants();
391 }
392 
393 AffineTransform::Transform
394     SubtreeContentTransformScope::current_content_transformation_ =
395         IDENTITY_TRANSFORM;
396 
SubtreeContentTransformScope(const AffineTransform & subtree_content_transformation)397 SubtreeContentTransformScope::SubtreeContentTransformScope(
398     const AffineTransform& subtree_content_transformation)
399     : saved_content_transformation_(current_content_transformation_) {
400   AffineTransform content_transformation =
401       subtree_content_transformation *
402       AffineTransform(current_content_transformation_);
403   content_transformation.CopyTransformTo(current_content_transformation_);
404 }
405 
~SubtreeContentTransformScope()406 SubtreeContentTransformScope::~SubtreeContentTransformScope() {
407   saved_content_transformation_.CopyTransformTo(
408       current_content_transformation_);
409 }
410 
DeprecatedCalculateTransformToLayer(const LayoutObject * layout_object)411 AffineTransform SVGLayoutSupport::DeprecatedCalculateTransformToLayer(
412     const LayoutObject* layout_object) {
413   AffineTransform transform;
414   while (layout_object) {
415     transform = layout_object->LocalToSVGParentTransform() * transform;
416     if (layout_object->IsSVGRoot())
417       break;
418     layout_object = layout_object->Parent();
419   }
420 
421   // Continue walking up the layer tree, accumulating CSS transforms.
422   // FIXME: this queries layer compositing state - which is not
423   // supported during layout. Hence, the result may not include all CSS
424   // transforms.
425   PaintLayer* layer = layout_object ? layout_object->EnclosingLayer() : nullptr;
426   while (layer && layer->IsAllowedToQueryCompositingState()) {
427     // We can stop at compositing layers, to match the backing resolution.
428     // FIXME: should we be computing the transform to the nearest composited
429     // layer, or the nearest composited layer that does not paint into its
430     // ancestor? I think this is the nearest composited ancestor since we will
431     // inherit its transforms in the composited layer tree.
432     if (layer->GetCompositingState() != kNotComposited)
433       break;
434 
435     if (TransformationMatrix* layer_transform = layer->Transform())
436       transform = layer_transform->ToAffineTransform() * transform;
437 
438     layer = layer->Parent();
439   }
440 
441   return transform;
442 }
443 
CalculateScreenFontSizeScalingFactor(const LayoutObject * layout_object)444 float SVGLayoutSupport::CalculateScreenFontSizeScalingFactor(
445     const LayoutObject* layout_object) {
446   DCHECK(layout_object);
447 
448   // FIXME: trying to compute a device space transform at record time is wrong.
449   // All clients should be updated to avoid relying on this information, and the
450   // method should be removed.
451   AffineTransform ctm =
452       DeprecatedCalculateTransformToLayer(layout_object) *
453       SubtreeContentTransformScope::CurrentContentTransformation();
454   ctm.Scale(
455       layout_object->GetDocument().GetPage()->DeviceScaleFactorDeprecated());
456 
457   return clampTo<float>(sqrt((ctm.XScaleSquared() + ctm.YScaleSquared()) / 2));
458 }
459 
CompareCandidateDistance(const SearchCandidate & r1,const SearchCandidate & r2)460 static inline bool CompareCandidateDistance(const SearchCandidate& r1,
461                                             const SearchCandidate& r2) {
462   return r1.distance < r2.distance;
463 }
464 
DistanceToChildLayoutObject(LayoutObject * child,const FloatPoint & point)465 static inline float DistanceToChildLayoutObject(LayoutObject* child,
466                                                 const FloatPoint& point) {
467   const AffineTransform& local_to_parent_transform =
468       child->LocalToSVGParentTransform();
469   if (!local_to_parent_transform.IsInvertible())
470     return std::numeric_limits<float>::max();
471   FloatPoint child_local_point =
472       local_to_parent_transform.Inverse().MapPoint(point);
473   return child->ObjectBoundingBox().SquaredDistanceTo(child_local_point);
474 }
475 
SearchTreeForFindClosestLayoutSVGText(const LayoutObject * layout_object,const FloatPoint & point)476 static SearchCandidate SearchTreeForFindClosestLayoutSVGText(
477     const LayoutObject* layout_object,
478     const FloatPoint& point) {
479   // Try to find the closest LayoutSVGText.
480   SearchCandidate closest_text;
481   Vector<SearchCandidate> candidates;
482 
483   // Find the closest LayoutSVGText on this tree level, and also collect any
484   // containers that could contain LayoutSVGTexts that are closer.
485   for (LayoutObject* child = layout_object->SlowLastChild(); child;
486        child = child->PreviousSibling()) {
487     if (child->IsSVGText()) {
488       float distance = DistanceToChildLayoutObject(child, point);
489       if (distance >= closest_text.distance)
490         continue;
491       candidates.clear();
492       closest_text.layout_object = child;
493       closest_text.distance = distance;
494       continue;
495     }
496 
497     if (child->IsSVGContainer() && !layout_object->IsSVGHiddenContainer()) {
498       float distance = DistanceToChildLayoutObject(child, point);
499       if (distance > closest_text.distance)
500         continue;
501       candidates.push_back(SearchCandidate(child, distance));
502     }
503   }
504 
505   // If a LayoutSVGText was found and there are no potentially closer sub-trees,
506   // just return |closestText|.
507   if (closest_text.layout_object && candidates.IsEmpty())
508     return closest_text;
509 
510   std::stable_sort(candidates.begin(), candidates.end(),
511                    CompareCandidateDistance);
512 
513   // Find the closest LayoutSVGText in the sub-trees in |candidates|.
514   // If a LayoutSVGText is found that is strictly closer than any previous
515   // candidate, then end the search.
516   for (const SearchCandidate& search_candidate : candidates) {
517     if (closest_text.distance < search_candidate.distance)
518       break;
519     LayoutObject* candidate_layout_object = search_candidate.layout_object;
520     FloatPoint candidate_local_point =
521         candidate_layout_object->LocalToSVGParentTransform().Inverse().MapPoint(
522             point);
523 
524     SearchCandidate candidate_text = SearchTreeForFindClosestLayoutSVGText(
525         candidate_layout_object, candidate_local_point);
526 
527     if (candidate_text.distance < closest_text.distance)
528       closest_text = candidate_text;
529   }
530 
531   return closest_text;
532 }
533 
FindClosestLayoutSVGText(const LayoutObject * layout_object,const FloatPoint & point)534 LayoutObject* SVGLayoutSupport::FindClosestLayoutSVGText(
535     const LayoutObject* layout_object,
536     const FloatPoint& point) {
537   return SearchTreeForFindClosestLayoutSVGText(layout_object, point)
538       .layout_object;
539 }
540 
NotifySVGRootOfChangedCompositingReasons(const LayoutObject * object)541 void SVGLayoutSupport::NotifySVGRootOfChangedCompositingReasons(
542     const LayoutObject* object) {
543   for (auto* ancestor = object->Parent(); ancestor;
544        ancestor = ancestor->Parent()) {
545     if (ancestor->IsSVGRoot()) {
546       To<LayoutSVGRoot>(ancestor)->NotifyDescendantCompositingReasonsChanged();
547       break;
548     }
549   }
550 }
551 
552 }  // namespace blink
553