1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.
3  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
4  * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
5  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
6  * Copyright (C) 2008 Rob Buis <buis@kde.org>
7  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
8  * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved.
9  * Copyright (C) 2012 Google Inc.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
28 
29 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
30 #include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
31 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
32 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
33 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
34 #include "third_party/blink/renderer/core/layout/layout_state.h"
35 #include "third_party/blink/renderer/core/layout/pointer_events_hit_rules.h"
36 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline.h"
37 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
38 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
39 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
40 #include "third_party/blink/renderer/core/layout/svg/line/svg_root_inline_box.h"
41 #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
42 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
43 #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
44 #include "third_party/blink/renderer/core/layout/svg/svg_text_layout_attributes_builder.h"
45 #include "third_party/blink/renderer/core/layout/svg/transformed_hit_test_location.h"
46 #include "third_party/blink/renderer/core/paint/svg_text_painter.h"
47 #include "third_party/blink/renderer/core/style/shadow_list.h"
48 #include "third_party/blink/renderer/core/svg/svg_text_element.h"
49 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
50 
51 namespace blink {
52 
53 namespace {
54 
FindTextRoot(const LayoutObject * start)55 const LayoutSVGText* FindTextRoot(const LayoutObject* start) {
56   DCHECK(start);
57   for (; start; start = start->Parent()) {
58     if (start->IsSVGText())
59       return ToLayoutSVGText(start);
60   }
61   return nullptr;
62 }
63 
64 }  // namespace
65 
LayoutSVGText(SVGTextElement * node)66 LayoutSVGText::LayoutSVGText(SVGTextElement* node)
67     : LayoutSVGBlock(node),
68       needs_reordering_(false),
69       needs_positioning_values_update_(false),
70       needs_transform_update_(true),
71       needs_text_metrics_update_(false) {}
72 
~LayoutSVGText()73 LayoutSVGText::~LayoutSVGText() {
74   DCHECK(descendant_text_nodes_.IsEmpty());
75 }
76 
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)77 void LayoutSVGText::StyleDidChange(StyleDifference diff,
78                                    const ComputedStyle* old_style) {
79   LayoutSVGBlock::StyleDidChange(diff, old_style);
80   SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef());
81 }
82 
WillBeDestroyed()83 void LayoutSVGText::WillBeDestroyed() {
84   descendant_text_nodes_.clear();
85   SVGResources::ClearPaints(*GetElement(), Style());
86   LayoutSVGBlock::WillBeDestroyed();
87 }
88 
IsChildAllowed(LayoutObject * child,const ComputedStyle &) const89 bool LayoutSVGText::IsChildAllowed(LayoutObject* child,
90                                    const ComputedStyle&) const {
91   return child->IsSVGInline() ||
92          (child->IsText() && SVGLayoutSupport::IsLayoutableTextNode(child));
93 }
94 
LocateLayoutSVGTextAncestor(LayoutObject * start)95 LayoutSVGText* LayoutSVGText::LocateLayoutSVGTextAncestor(LayoutObject* start) {
96   return const_cast<LayoutSVGText*>(FindTextRoot(start));
97 }
98 
LocateLayoutSVGTextAncestor(const LayoutObject * start)99 const LayoutSVGText* LayoutSVGText::LocateLayoutSVGTextAncestor(
100     const LayoutObject* start) {
101   return FindTextRoot(start);
102 }
103 
CollectDescendantTextNodes(LayoutSVGText & text_root,Vector<LayoutSVGInlineText * > & descendant_text_nodes)104 static inline void CollectDescendantTextNodes(
105     LayoutSVGText& text_root,
106     Vector<LayoutSVGInlineText*>& descendant_text_nodes) {
107   for (LayoutObject* descendant = text_root.FirstChild(); descendant;
108        descendant = descendant->NextInPreOrder(&text_root)) {
109     if (descendant->IsSVGInlineText())
110       descendant_text_nodes.push_back(ToLayoutSVGInlineText(descendant));
111   }
112 }
113 
SubtreeStructureChanged(LayoutInvalidationReasonForTracing reason)114 void LayoutSVGText::SubtreeStructureChanged(
115     LayoutInvalidationReasonForTracing reason) {
116   if (BeingDestroyed() || !EverHadLayout()) {
117     DCHECK(descendant_text_nodes_.IsEmpty());
118     return;
119   }
120   if (DocumentBeingDestroyed())
121     return;
122 
123   // The positioning elements cache depends on the size of each text
124   // LayoutObject in the subtree. If this changes, clear the cache. It will be
125   // rebuilt on the next layout.
126   descendant_text_nodes_.clear();
127   SetNeedsPositioningValuesUpdate();
128   SetNeedsTextMetricsUpdate();
129   // TODO(fs): Restore the passing of |reason| here.
130   LayoutSVGResourceContainer::MarkForLayoutAndParentResourceInvalidation(*this);
131 }
132 
NotifySubtreeStructureChanged(LayoutObject * object,LayoutInvalidationReasonForTracing reason)133 void LayoutSVGText::NotifySubtreeStructureChanged(
134     LayoutObject* object,
135     LayoutInvalidationReasonForTracing reason) {
136   if (LayoutSVGText* layout_text = LocateLayoutSVGTextAncestor(object))
137     layout_text->SubtreeStructureChanged(reason);
138 }
139 
UpdateFontAndMetrics(LayoutSVGText & text_root)140 static inline void UpdateFontAndMetrics(LayoutSVGText& text_root) {
141   bool last_character_was_white_space = true;
142   for (LayoutObject* descendant = text_root.FirstChild(); descendant;
143        descendant = descendant->NextInPreOrder(&text_root)) {
144     if (!descendant->IsSVGInlineText())
145       continue;
146     LayoutSVGInlineText& text = ToLayoutSVGInlineText(*descendant);
147     text.UpdateScaledFont();
148     text.UpdateMetricsList(last_character_was_white_space);
149   }
150 }
151 
CheckDescendantTextNodeConsistency(LayoutSVGText & text,Vector<LayoutSVGInlineText * > & expected_descendant_text_nodes)152 static inline void CheckDescendantTextNodeConsistency(
153     LayoutSVGText& text,
154     Vector<LayoutSVGInlineText*>& expected_descendant_text_nodes) {
155 #if DCHECK_IS_ON()
156   Vector<LayoutSVGInlineText*> new_descendant_text_nodes;
157   CollectDescendantTextNodes(text, new_descendant_text_nodes);
158   DCHECK(new_descendant_text_nodes == expected_descendant_text_nodes);
159 #endif
160 }
161 
UpdateLayout()162 void LayoutSVGText::UpdateLayout() {
163   DCHECK(NeedsLayout());
164   // This flag is set and reset as needed only within this function.
165   DCHECK(!needs_reordering_);
166   LayoutAnalyzer::Scope analyzer(*this);
167 
168   ClearOffsetMappingIfNeeded();
169 
170   // When laying out initially, build the character data map and propagate
171   // resulting layout attributes to all LayoutSVGInlineText children in the
172   // subtree.
173   if (!EverHadLayout()) {
174     needs_positioning_values_update_ = true;
175     needs_text_metrics_update_ = true;
176   }
177 
178   bool update_parent_boundaries = false;
179 
180   // If the root layout size changed (eg. window size changes), or the screen
181   // scale factor has changed, then recompute the on-screen font size. Since
182   // the computation of layout attributes uses the text metrics, we need to
183   // update them before updating the layout attributes.
184   if (needs_text_metrics_update_) {
185     // Recompute the transform before updating font and corresponding
186     // metrics. At this point our bounding box may be incorrect, so
187     // any box relative transforms will be incorrect. Since the scaled
188     // font size only needs the scaling components to be correct, this
189     // should be fine. We update the transform again after computing
190     // the bounding box below, and after that we clear the
191     // |needs_transform_update_| flag.
192     if (needs_transform_update_) {
193       local_transform_ =
194           GetElement()->CalculateTransform(SVGElement::kIncludeMotionTransform);
195     }
196 
197     UpdateFontAndMetrics(*this);
198     // Font changes may change the size of the "em" unit, so we need to
199     // update positions that might depend on the font size. This is a big
200     // hammer but we have no simple way to determine if the positions of
201     // children depend on the font size.
202     needs_positioning_values_update_ = true;
203     needs_text_metrics_update_ = false;
204     update_parent_boundaries = true;
205   }
206 
207   // When the x/y/dx/dy/rotate lists change, we need to recompute the layout
208   // attributes.
209   if (needs_positioning_values_update_) {
210     descendant_text_nodes_.clear();
211     CollectDescendantTextNodes(*this, descendant_text_nodes_);
212 
213     SVGTextLayoutAttributesBuilder(*this).BuildLayoutAttributes();
214 
215     needs_positioning_values_update_ = false;
216     needs_reordering_ = true;
217     update_parent_boundaries = true;
218   }
219 
220   CheckDescendantTextNodeConsistency(*this, descendant_text_nodes_);
221 
222   // Reduced version of LayoutBlock::layoutBlock(), which only takes care of SVG
223   // text. All if branches that could cause early exit in LayoutBlocks
224   // layoutBlock() method are turned into assertions.
225   DCHECK(!IsInline());
226   DCHECK(!SimplifiedLayout());
227   DCHECK(!ScrollsOverflow());
228   DCHECK(!HasControlClip());
229   DCHECK(!PositionedObjects());
230   DCHECK(!IsAnonymousBlock());
231 
232   if (!FirstChild())
233     SetChildrenInline(true);
234 
235   // FIXME: We need to find a way to only layout the child boxes, if needed.
236   FloatRect old_boundaries = ObjectBoundingBox();
237   DCHECK(ChildrenInline());
238 
239   RebuildFloatsFromIntruding();
240 
241   LayoutUnit before_edge = BorderBefore() + PaddingBefore();
242   LayoutUnit after_edge =
243       BorderAfter() + PaddingAfter() + ScrollbarLogicalHeight();
244   SetLogicalHeight(before_edge);
245 
246   LayoutState state(*this);
247   LayoutInlineChildren(true, after_edge);
248 
249   needs_reordering_ = false;
250 
251   FloatRect new_boundaries = ObjectBoundingBox();
252   bool bounds_changed = old_boundaries != new_boundaries;
253 
254   // Update the transform after laying out. Update if the bounds
255   // changed too, since the transform could depend on the bounding
256   // box.
257   if (bounds_changed || needs_transform_update_) {
258     local_transform_ =
259         GetElement()->CalculateTransform(SVGElement::kIncludeMotionTransform);
260     needs_transform_update_ = false;
261     update_parent_boundaries = true;
262   }
263 
264   ClearLayoutOverflow();
265 
266   // Invalidate all resources of this client if our layout changed.
267   if (EverHadLayout() && SelfNeedsLayout())
268     SVGResourcesCache::ClientLayoutChanged(*this);
269 
270   // If our bounds changed, notify the parents.
271   if (update_parent_boundaries)
272     LayoutSVGBlock::SetNeedsBoundariesUpdate();
273 
274   DCHECK(!needs_reordering_);
275   DCHECK(!needs_transform_update_);
276   DCHECK(!needs_text_metrics_update_);
277   DCHECK(!needs_positioning_values_update_);
278   ClearSelfNeedsLayoutOverflowRecalc();
279   ClearNeedsLayout();
280 }
281 
RecalcVisualOverflow()282 void LayoutSVGText::RecalcVisualOverflow() {
283   ClearVisualOverflow();
284   LayoutObject::RecalcVisualOverflow();
285   AddSelfVisualOverflow(LayoutRect(ObjectBoundingBox()));
286   AddVisualEffectOverflow();
287 }
288 
CreateRootInlineBox()289 RootInlineBox* LayoutSVGText::CreateRootInlineBox() {
290   RootInlineBox* box = new SVGRootInlineBox(LineLayoutItem(this));
291   box->SetHasVirtualLogicalHeight();
292   return box;
293 }
294 
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction hit_test_action)295 bool LayoutSVGText::NodeAtPoint(HitTestResult& result,
296                                 const HitTestLocation& hit_test_location,
297                                 const PhysicalOffset& accumulated_offset,
298                                 HitTestAction hit_test_action) {
299   DCHECK_EQ(accumulated_offset, PhysicalOffset());
300   // We only draw in the foreground phase, so we only hit-test then.
301   if (hit_test_action != kHitTestForeground)
302     return false;
303 
304   TransformedHitTestLocation local_location(hit_test_location,
305                                             LocalToSVGParentTransform());
306   if (!local_location)
307     return false;
308   if (!SVGLayoutSupport::IntersectsClipPath(*this, ObjectBoundingBox(),
309                                             *local_location))
310     return false;
311 
312   if (LayoutBlock::NodeAtPoint(result, *local_location, accumulated_offset,
313                                hit_test_action))
314     return true;
315 
316   // Consider the bounding box if requested.
317   if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) {
318     if (IsObjectBoundingBoxValid() &&
319         local_location->Intersects(ObjectBoundingBox())) {
320       UpdateHitTestResult(result, PhysicalOffset::FromFloatPointRound(
321                                       local_location->TransformedPoint()));
322       if (result.AddNodeToListBasedTestResult(GetElement(), *local_location) ==
323           kStopHitTesting)
324         return true;
325     }
326   }
327   return false;
328 }
329 
PositionForPoint(const PhysicalOffset & point_in_contents) const330 PositionWithAffinity LayoutSVGText::PositionForPoint(
331     const PhysicalOffset& point_in_contents) const {
332   RootInlineBox* root_box = FirstRootBox();
333   if (!root_box)
334     return CreatePositionWithAffinity(0);
335 
336   PhysicalOffset clipped_point_in_contents(point_in_contents);
337   clipped_point_in_contents -= root_box->PhysicalLocation();
338   clipped_point_in_contents.ClampNegativeToZero();
339   clipped_point_in_contents += root_box->PhysicalLocation();
340 
341   DCHECK(!root_box->NextRootBox());
342   DCHECK(ChildrenInline());
343 
344   auto* closest_box =
345       To<SVGRootInlineBox>(root_box)->ClosestLeafChildForPosition(
346           clipped_point_in_contents);
347   if (!closest_box)
348     return CreatePositionWithAffinity(0);
349 
350   return closest_box->GetLineLayoutItem().PositionForPoint(
351       PhysicalOffset(clipped_point_in_contents.left, closest_box->Y()));
352 }
353 
AbsoluteQuads(Vector<FloatQuad> & quads,MapCoordinatesFlags mode) const354 void LayoutSVGText::AbsoluteQuads(Vector<FloatQuad>& quads,
355                                   MapCoordinatesFlags mode) const {
356   quads.push_back(LocalToAbsoluteQuad(StrokeBoundingBox(), mode));
357 }
358 
Paint(const PaintInfo & paint_info) const359 void LayoutSVGText::Paint(const PaintInfo& paint_info) const {
360   SVGTextPainter(*this).Paint(paint_info);
361 }
362 
ObjectBoundingBox() const363 FloatRect LayoutSVGText::ObjectBoundingBox() const {
364   if (const RootInlineBox* box = FirstRootBox())
365     return FloatRect(box->FrameRect());
366   return FloatRect();
367 }
368 
StrokeBoundingBox() const369 FloatRect LayoutSVGText::StrokeBoundingBox() const {
370   if (!FirstRootBox())
371     return FloatRect();
372   return SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, ObjectBoundingBox());
373 }
374 
VisualRectInLocalSVGCoordinates() const375 FloatRect LayoutSVGText::VisualRectInLocalSVGCoordinates() const {
376   if (!FirstRootBox())
377     return FloatRect();
378   const FloatRect object_bounds = ObjectBoundingBox();
379   return SVGLayoutSupport::ComputeVisualRectForText(*this, object_bounds,
380                                                     object_bounds);
381 }
382 
AddOutlineRects(Vector<PhysicalRect> & rects,const PhysicalOffset &,NGOutlineType) const383 void LayoutSVGText::AddOutlineRects(Vector<PhysicalRect>& rects,
384                                     const PhysicalOffset&,
385                                     NGOutlineType) const {
386   rects.push_back(PhysicalRect::EnclosingRect(ObjectBoundingBox()));
387 }
388 
IsObjectBoundingBoxValid() const389 bool LayoutSVGText::IsObjectBoundingBoxValid() const {
390   // If we don't have any line boxes, then consider the bbox invalid.
391   return FirstLineBox();
392 }
393 
AddChild(LayoutObject * child,LayoutObject * before_child)394 void LayoutSVGText::AddChild(LayoutObject* child, LayoutObject* before_child) {
395   LayoutSVGBlock::AddChild(child, before_child);
396 
397   SVGResourcesCache::ClientWasAddedToTree(*child);
398   SubtreeStructureChanged(layout_invalidation_reason::kChildChanged);
399 }
400 
RemoveChild(LayoutObject * child)401 void LayoutSVGText::RemoveChild(LayoutObject* child) {
402   SVGResourcesCache::ClientWillBeRemovedFromTree(*child);
403   SubtreeStructureChanged(layout_invalidation_reason::kChildChanged);
404 
405   LayoutSVGBlock::RemoveChild(child);
406 }
407 
408 }  // namespace blink
409