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