Introduce RenderTreeBuilder
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGText.cpp
1 /*
2  * Copyright (C) 2006 Apple 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 "config.h"
28 #include "RenderSVGText.h"
29
30 #include "FloatQuad.h"
31 #include "Font.h"
32 #include "GraphicsContext.h"
33 #include "HitTestRequest.h"
34 #include "HitTestResult.h"
35 #include "LayoutRepainter.h"
36 #include "PointerEventsHitRules.h"
37 #include "RenderIterator.h"
38 #include "RenderSVGInline.h"
39 #include "RenderSVGInlineText.h"
40 #include "RenderSVGResource.h"
41 #include "RenderSVGRoot.h"
42 #include "SVGLengthList.h"
43 #include "SVGResourcesCache.h"
44 #include "SVGRootInlineBox.h"
45 #include "SVGTextElement.h"
46 #include "SVGURIReference.h"
47 #include "TransformState.h"
48 #include "VisiblePosition.h"
49 #include <wtf/IsoMallocInlines.h>
50 #include <wtf/StackStats.h>
51
52 namespace WebCore {
53
54 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSVGText);
55
56 RenderSVGText::RenderSVGText(SVGTextElement& element, RenderStyle&& style)
57     : RenderSVGBlock(element, WTFMove(style))
58     , m_needsReordering(false)
59     , m_needsPositioningValuesUpdate(false)
60     , m_needsTransformUpdate(true)
61     , m_needsTextMetricsUpdate(false)
62 {
63 }
64
65 RenderSVGText::~RenderSVGText()
66 {
67     ASSERT(m_layoutAttributes.isEmpty());
68 }
69
70 SVGTextElement& RenderSVGText::textElement() const
71 {
72     return downcast<SVGTextElement>(RenderSVGBlock::graphicsElement());
73 }
74
75 bool RenderSVGText::isChildAllowed(const RenderObject& child, const RenderStyle&) const
76 {
77     return child.isInline();
78 }
79
80 RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject& start)
81 {
82     return lineageOfType<RenderSVGText>(start).first();
83 }
84
85 const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject& start)
86 {
87     return lineageOfType<RenderSVGText>(start).first();
88 }
89
90 LayoutRect RenderSVGText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
91 {
92     return SVGRenderSupport::clippedOverflowRectForRepaint(*this, repaintContainer);
93 }
94
95 LayoutRect RenderSVGText::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, RepaintContext context) const
96 {
97     return enclosingLayoutRect(computeFloatRectForRepaint(rect, repaintContainer, context.m_hasPositionFixedDescendant));
98 }
99
100 FloatRect RenderSVGText::computeFloatRectForRepaint(const FloatRect& repaintRect, const RenderLayerModelObject* repaintContainer, bool fixed) const
101 {
102     return SVGRenderSupport::computeFloatRectForRepaint(*this, repaintRect, repaintContainer, fixed);
103 }
104
105 void RenderSVGText::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const
106 {
107     SVGRenderSupport::mapLocalToContainer(*this, repaintContainer, transformState, wasFixed);
108 }
109
110 const RenderObject* RenderSVGText::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
111 {
112     return SVGRenderSupport::pushMappingToContainer(*this, ancestorToStopAt, geometryMap);
113 }
114
115 static inline void collectLayoutAttributes(RenderObject* text, Vector<SVGTextLayoutAttributes*>& attributes)
116 {
117     for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
118         if (is<RenderSVGInlineText>(*descendant))
119             attributes.append(downcast<RenderSVGInlineText>(*descendant).layoutAttributes());
120     }
121 }
122
123 static inline bool findPreviousAndNextAttributes(RenderElement& start, RenderSVGInlineText* locateElement, bool& stopAfterNext, SVGTextLayoutAttributes*& previous, SVGTextLayoutAttributes*& next)
124 {
125     ASSERT(locateElement);
126     // FIXME: Make this iterative.
127     for (auto& child : childrenOfType<RenderObject>(start)) {
128         if (is<RenderSVGInlineText>(child)) {
129             auto& text = downcast<RenderSVGInlineText>(child);
130             if (locateElement != &text) {
131                 if (stopAfterNext) {
132                     next = text.layoutAttributes();
133                     return true;
134                 }
135
136                 previous = text.layoutAttributes();
137                 continue;
138             }
139
140             stopAfterNext = true;
141             continue;
142         }
143
144         if (!is<RenderSVGInline>(child))
145             continue;
146
147         if (findPreviousAndNextAttributes(downcast<RenderElement>(child), locateElement, stopAfterNext, previous, next))
148             return true;
149     }
150
151     return false;
152 }
153
154 inline bool RenderSVGText::shouldHandleSubtreeMutations() const
155 {
156     if (beingDestroyed() || !everHadLayout()) {
157         ASSERT(m_layoutAttributes.isEmpty());
158         ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements());
159         return false;
160     }
161     return true;
162 }
163
164 void RenderSVGText::subtreeChildWasAdded(RenderObject* child)
165 {
166     ASSERT(child);
167     if (!shouldHandleSubtreeMutations() || renderTreeBeingDestroyed())
168         return;
169
170     // The positioning elements cache doesn't include the new 'child' yet. Clear the
171     // cache, as the next buildLayoutAttributesForTextRenderer() call rebuilds it.
172     m_layoutAttributesBuilder.clearTextPositioningElements();
173
174     if (!child->isSVGInlineText() && !child->isSVGInline())
175         return;
176
177     // Detect changes in layout attributes and only measure those text parts that have changed!
178     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
179     collectLayoutAttributes(this, newLayoutAttributes);
180     if (newLayoutAttributes.isEmpty()) {
181         ASSERT(m_layoutAttributes.isEmpty());
182         return;
183     }
184
185     // Compare m_layoutAttributes with newLayoutAttributes to figure out which attribute got added.
186     size_t size = newLayoutAttributes.size();
187     SVGTextLayoutAttributes* attributes = 0;
188     for (size_t i = 0; i < size; ++i) {
189         attributes = newLayoutAttributes[i];
190         if (m_layoutAttributes.find(attributes) == notFound) {
191             // Every time this is invoked, there's only a single new entry in the newLayoutAttributes list, compared to the old in m_layoutAttributes.
192             bool stopAfterNext = false;
193             SVGTextLayoutAttributes* previous = 0;
194             SVGTextLayoutAttributes* next = 0;
195             ASSERT_UNUSED(child, &attributes->context() == child);
196             findPreviousAndNextAttributes(*this, &attributes->context(), stopAfterNext, previous, next);
197
198             if (previous)
199                 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(previous->context());
200             m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(attributes->context());
201             if (next)
202                 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(next->context());
203             break;
204         }
205     }
206
207 #ifndef NDEBUG
208     // Verify that m_layoutAttributes only differs by a maximum of one entry.
209     for (size_t i = 0; i < size; ++i)
210         ASSERT(m_layoutAttributes.find(newLayoutAttributes[i]) != notFound || newLayoutAttributes[i] == attributes);
211 #endif
212
213     m_layoutAttributes = newLayoutAttributes;
214 }
215
216 static inline void checkLayoutAttributesConsistency(RenderSVGText* text, Vector<SVGTextLayoutAttributes*>& expectedLayoutAttributes)
217 {
218 #ifndef NDEBUG
219     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
220     collectLayoutAttributes(text, newLayoutAttributes);
221     ASSERT(newLayoutAttributes == expectedLayoutAttributes);
222 #else
223     UNUSED_PARAM(text);
224     UNUSED_PARAM(expectedLayoutAttributes);
225 #endif
226 }
227
228 void RenderSVGText::willBeDestroyed()
229 {
230     m_layoutAttributes.clear();
231     m_layoutAttributesBuilder.clearTextPositioningElements();
232
233     RenderSVGBlock::willBeDestroyed();
234 }
235
236 void RenderSVGText::subtreeChildWillBeRemoved(RenderObject* child, Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes)
237 {
238     ASSERT(child);
239     if (!shouldHandleSubtreeMutations())
240         return;
241
242     checkLayoutAttributesConsistency(this, m_layoutAttributes);
243
244     // The positioning elements cache depends on the size of each text renderer in the
245     // subtree. If this changes, clear the cache. It's going to be rebuilt below.
246     m_layoutAttributesBuilder.clearTextPositioningElements();
247     if (m_layoutAttributes.isEmpty() || !child->isSVGInlineText())
248         return;
249
250     // This logic requires that the 'text' child is still inserted in the tree.
251     auto& text = downcast<RenderSVGInlineText>(*child);
252     bool stopAfterNext = false;
253     SVGTextLayoutAttributes* previous = nullptr;
254     SVGTextLayoutAttributes* next = nullptr;
255     if (!renderTreeBeingDestroyed())
256         findPreviousAndNextAttributes(*this, &text, stopAfterNext, previous, next);
257
258     if (previous)
259         affectedAttributes.append(previous);
260     if (next)
261         affectedAttributes.append(next);
262
263     bool removed = m_layoutAttributes.removeFirst(text.layoutAttributes());
264     ASSERT_UNUSED(removed, removed);
265 }
266
267 void RenderSVGText::subtreeChildWasRemoved(const Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes)
268 {
269     if (!shouldHandleSubtreeMutations() || renderTreeBeingDestroyed()) {
270         ASSERT(affectedAttributes.isEmpty());
271         return;
272     }
273
274     // This is called immediately after subtreeChildWillBeDestroyed, once the RenderSVGInlineText::willBeDestroyed() method
275     // passes on to the base class, which removes us from the render tree. At this point we can update the layout attributes.
276     unsigned size = affectedAttributes.size();
277     for (unsigned i = 0; i < size; ++i)
278         m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(affectedAttributes[i]->context());
279 }
280
281 void RenderSVGText::subtreeStyleDidChange(RenderSVGInlineText* text)
282 {
283     ASSERT(text);
284     if (!shouldHandleSubtreeMutations() || renderTreeBeingDestroyed())
285         return;
286
287     checkLayoutAttributesConsistency(this, m_layoutAttributes);
288
289     // Only update the metrics cache, but not the text positioning element cache
290     // nor the layout attributes cached in the leaf #text renderers.
291     for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
292         if (is<RenderSVGInlineText>(*descendant))
293             m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(downcast<RenderSVGInlineText>(*descendant));
294     }
295 }
296
297 void RenderSVGText::subtreeTextDidChange(RenderSVGInlineText* text)
298 {
299     ASSERT(text);
300     ASSERT(!beingDestroyed());
301     if (!everHadLayout()) {
302         ASSERT(m_layoutAttributes.isEmpty());
303         ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements());
304         return;
305     }
306     // Text transforms can cause text change to be signaled during addChild before m_layoutAttributes has been updated.
307     if (!m_layoutAttributes.contains(text->layoutAttributes())) {
308         ASSERT(!text->everHadLayout());
309         return;
310     }
311
312     // The positioning elements cache depends on the size of each text renderer in the
313     // subtree. If this changes, clear the cache. It's going to be rebuilt below.
314     m_layoutAttributesBuilder.clearTextPositioningElements();
315
316     checkLayoutAttributesConsistency(this, m_layoutAttributes);
317     for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
318         if (is<RenderSVGInlineText>(*descendant))
319             m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(downcast<RenderSVGInlineText>(*descendant));
320     }
321 }
322
323 static inline void updateFontInAllDescendants(RenderObject* start, SVGTextLayoutAttributesBuilder* builder = nullptr)
324 {
325     for (RenderObject* descendant = start; descendant; descendant = descendant->nextInPreOrder(start)) {
326         if (!is<RenderSVGInlineText>(*descendant))
327             continue;
328         auto& text = downcast<RenderSVGInlineText>(*descendant);
329         text.updateScaledFont();
330         if (builder)
331             builder->rebuildMetricsForTextRenderer(text);
332     }
333 }
334
335 void RenderSVGText::layout()
336 {
337     StackStats::LayoutCheckPoint layoutCheckPoint;
338     ASSERT(needsLayout());
339     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(*this));
340
341     bool updateCachedBoundariesInParents = false;
342     if (m_needsTransformUpdate) {
343         m_localTransform = textElement().animatedLocalTransform();
344         m_needsTransformUpdate = false;
345         updateCachedBoundariesInParents = true;
346     }
347
348     if (!everHadLayout()) {
349         // When laying out initially, collect all layout attributes, build the character data map,
350         // and propogate resulting SVGLayoutAttributes to all RenderSVGInlineText children in the subtree.
351         ASSERT(m_layoutAttributes.isEmpty());
352         collectLayoutAttributes(this, m_layoutAttributes);
353         updateFontInAllDescendants(this);
354         m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(*this);
355
356         m_needsReordering = true;
357         m_needsTextMetricsUpdate = false;
358         m_needsPositioningValuesUpdate = false;
359         updateCachedBoundariesInParents = true;
360     } else if (m_needsPositioningValuesUpdate) {
361         // When the x/y/dx/dy/rotate lists change, recompute the layout attributes, and eventually
362         // update the on-screen font objects as well in all descendants.
363         if (m_needsTextMetricsUpdate) {
364             updateFontInAllDescendants(this);
365             m_needsTextMetricsUpdate = false;
366         }
367
368         m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(*this);
369         m_needsReordering = true;
370         m_needsPositioningValuesUpdate = false;
371         updateCachedBoundariesInParents = true;
372     } else if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(*this)->isLayoutSizeChanged()) {
373         // If the root layout size changed (eg. window size changes) or the transform to the root
374         // context has changed then recompute the on-screen font size.
375         updateFontInAllDescendants(this, &m_layoutAttributesBuilder);
376
377         ASSERT(!m_needsReordering);
378         ASSERT(!m_needsPositioningValuesUpdate);
379         m_needsTextMetricsUpdate = false;
380         updateCachedBoundariesInParents = true;
381     }
382
383     checkLayoutAttributesConsistency(this, m_layoutAttributes);
384
385     // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
386     // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions.
387     ASSERT(!isInline());
388     ASSERT(!simplifiedLayout());
389     ASSERT(!scrollsOverflow());
390     ASSERT(!hasControlClip());
391     ASSERT(!multiColumnFlow());
392     ASSERT(!positionedObjects());
393     ASSERT(!m_overflow);
394     ASSERT(!isAnonymousBlock());
395
396     if (!firstChild())
397         setChildrenInline(true);
398
399     // FIXME: We need to find a way to only layout the child boxes, if needed.
400     FloatRect oldBoundaries = objectBoundingBox();
401     ASSERT(childrenInline());
402     LayoutUnit repaintLogicalTop = 0;
403     LayoutUnit repaintLogicalBottom = 0;
404     rebuildFloatingObjectSetFromIntrudingFloats();
405     layoutInlineChildren(true, repaintLogicalTop, repaintLogicalBottom);
406
407     if (m_needsReordering)
408         m_needsReordering = false;
409
410     if (!updateCachedBoundariesInParents)
411         updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
412
413     // Invalidate all resources of this client if our layout changed.
414     if (everHadLayout() && selfNeedsLayout())
415         SVGResourcesCache::clientLayoutChanged(*this);
416
417     // If our bounds changed, notify the parents.
418     if (updateCachedBoundariesInParents)
419         RenderSVGBlock::setNeedsBoundariesUpdate();
420
421     repainter.repaintAfterLayout();
422     clearNeedsLayout();
423 }
424
425 std::unique_ptr<RootInlineBox> RenderSVGText::createRootInlineBox()
426 {
427     auto box = std::make_unique<SVGRootInlineBox>(*this);
428     box->setHasVirtualLogicalHeight();
429     return WTFMove(box);
430 }
431
432 bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
433 {
434     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style().pointerEvents());
435     bool isVisible = (style().visibility() == VISIBLE);
436     if (isVisible || !hitRules.requireVisible) {
437         if ((hitRules.canHitStroke && (style().svgStyle().hasStroke() || !hitRules.requireStroke))
438             || (hitRules.canHitFill && (style().svgStyle().hasFill() || !hitRules.requireFill))) {
439             FloatPoint localPoint = localToParentTransform().inverse().value_or(AffineTransform()).mapPoint(pointInParent);
440
441             if (!SVGRenderSupport::pointInClippingArea(*this, localPoint))
442                 return false;       
443
444             HitTestLocation hitTestLocation(LayoutPoint(flooredIntPoint(localPoint)));
445             return RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), hitTestAction);
446         }
447     }
448
449     return false;
450 }
451
452 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint&, HitTestAction)
453 {
454     ASSERT_NOT_REACHED();
455     return false;
456 }
457
458 VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents, const RenderFragmentContainer* fragment)
459 {
460     RootInlineBox* rootBox = firstRootBox();
461     if (!rootBox)
462         return createVisiblePosition(0, DOWNSTREAM);
463
464     ASSERT(!rootBox->nextRootBox());
465     ASSERT(childrenInline());
466
467     InlineBox* closestBox = downcast<SVGRootInlineBox>(*rootBox).closestLeafChildForPosition(pointInContents);
468     if (!closestBox)
469         return createVisiblePosition(0, DOWNSTREAM);
470
471     return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()), fragment);
472 }
473
474 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
475 {
476     quads.append(localToAbsoluteQuad(strokeBoundingBox(), UseTransforms, wasFixed));
477 }
478
479 void RenderSVGText::paint(PaintInfo& paintInfo, const LayoutPoint&)
480 {
481     if (paintInfo.context().paintingDisabled())
482         return;
483
484     if (paintInfo.phase != PaintPhaseForeground
485      && paintInfo.phase != PaintPhaseSelection)
486          return;
487
488     PaintInfo blockInfo(paintInfo);
489     GraphicsContextStateSaver stateSaver(blockInfo.context());
490     blockInfo.applyTransform(localToParentTransform());
491     RenderBlock::paint(blockInfo, LayoutPoint());
492
493     // Paint the outlines, if any
494     if (paintInfo.phase == PaintPhaseForeground) {
495         blockInfo.phase = PaintPhaseSelfOutline;
496         RenderBlock::paint(blockInfo, LayoutPoint());
497     }
498 }
499
500 FloatRect RenderSVGText::strokeBoundingBox() const
501 {
502     FloatRect strokeBoundaries = objectBoundingBox();
503     const SVGRenderStyle& svgStyle = style().svgStyle();
504     if (!svgStyle.hasStroke())
505         return strokeBoundaries;
506
507     SVGLengthContext lengthContext(&textElement());
508     strokeBoundaries.inflate(lengthContext.valueForLength(style().strokeWidth()));
509     return strokeBoundaries;
510 }
511
512 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
513 {
514     FloatRect repaintRect = strokeBoundingBox();
515     SVGRenderSupport::intersectRepaintRectWithResources(*this, repaintRect);
516
517     if (const ShadowData* textShadow = style().textShadow())
518         textShadow->adjustRectForShadow(repaintRect);
519
520     return repaintRect;
521 }
522
523 void RenderSVGText::addChild(RenderTreeBuilder& builder, RenderPtr<RenderObject> newChild, RenderObject* beforeChild)
524 {
525     auto& child = *newChild;
526     RenderSVGBlock::addChild(builder, WTFMove(newChild), beforeChild);
527
528     SVGResourcesCache::clientWasAddedToTree(child);
529     subtreeChildWasAdded(&child);
530 }
531
532 RenderPtr<RenderObject> RenderSVGText::takeChild(RenderObject& child)
533 {
534     SVGResourcesCache::clientWillBeRemovedFromTree(child);
535
536     Vector<SVGTextLayoutAttributes*, 2> affectedAttributes;
537     subtreeChildWillBeRemoved(&child, affectedAttributes);
538     auto takenChild = RenderSVGBlock::takeChild(child);
539     subtreeChildWasRemoved(affectedAttributes);
540     return takenChild;
541 }
542
543 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
544 // in a SVG text element context.
545 RenderBlock* RenderSVGText::firstLineBlock() const
546 {
547     return 0;
548 }
549
550 }