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