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