f0f0b9c32ea112f95bceea2b29a600860a2b5500
[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 "LayoutRepainter.h"
38 #include "PointerEventsHitRules.h"
39 #include "RenderSVGInlineText.h"
40 #include "RenderSVGResource.h"
41 #include "RenderSVGRoot.h"
42 #include "SVGLengthList.h"
43 #include "SVGRenderSupport.h"
44 #include "SVGResourcesCache.h"
45 #include "SVGRootInlineBox.h"
46 #include "SVGTextElement.h"
47 #include "SVGTextLayoutAttributesBuilder.h"
48 #include "SVGTextRunRenderingContext.h"
49 #include "SVGTransformList.h"
50 #include "SVGURIReference.h"
51 #include "SimpleFontData.h"
52 #include "TransformState.h"
53 #include "VisiblePosition.h"
54
55 namespace WebCore {
56
57 RenderSVGText::RenderSVGText(SVGTextElement* node) 
58     : RenderSVGBlock(node)
59     , m_needsReordering(false)
60     , m_needsPositioningValuesUpdate(true)
61     , m_needsTransformUpdate(true)
62     , m_needsTextMetricsUpdate(true)
63 {
64 }
65
66 bool RenderSVGText::isChildAllowed(RenderObject* child, RenderStyle*) const
67 {
68     return child->isInline();
69 }
70
71 RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject* start)
72 {
73     ASSERT(start);
74     while (start && !start->isSVGText())
75         start = start->parent();
76     if (!start || !start->isSVGText())
77         return 0;
78     return toRenderSVGText(start);
79 }
80
81 const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject* start)
82 {
83     ASSERT(start);
84     while (start && !start->isSVGText())
85         start = start->parent();
86     if (!start || !start->isSVGText())
87         return 0;
88     return toRenderSVGText(start);
89 }
90
91 LayoutRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) const
92 {
93     return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
94 }
95
96 void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
97 {
98     FloatRect repaintRect = rect;
99     computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
100     rect = enclosingLayoutRect(repaintRect);
101 }
102
103 void RenderSVGText::computeFloatRectForRepaint(RenderBoxModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) const
104 {
105     SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed);
106 }
107
108 void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool /* fixed */, bool /* useTransforms */, TransformState& transformState, ApplyContainerFlipOrNot, bool* wasFixed) const
109 {
110     SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed);
111 }
112
113 void RenderSVGText::subtreeChildAdded(RenderObject* child)
114 {
115     ASSERT(child);
116     if (m_needsPositioningValuesUpdate)
117         return;
118
119     // The positioning elements cache doesn't include the new 'child' yet. Clear the
120     // cache, as the next buildLayoutAttributesForTextRenderer() call rebuilds it.
121     invalidateTextPositioningElements();
122
123     FontCachePurgePreventer fontCachePurgePreventer;
124     for (RenderObject* descendant = child; descendant; descendant = descendant->nextInPreOrder(child)) {
125         if (descendant->isSVGInlineText())
126             m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(descendant));
127     }
128
129     rebuildLayoutAttributes();
130 }
131
132 static inline bool findPreviousAndNextAttributes(RenderObject* start, RenderSVGInlineText* locateElement, bool& stopAfterNext, SVGTextLayoutAttributes*& previous, SVGTextLayoutAttributes*& next)
133 {
134     ASSERT(start);
135     ASSERT(locateElement);
136     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
137         if (child->isSVGInlineText()) {
138             RenderSVGInlineText* text = toRenderSVGInlineText(child);
139             if (locateElement != text) {
140                 if (stopAfterNext) {
141                     next = text->layoutAttributes();
142                     return true;
143                 }
144
145                 previous = text->layoutAttributes();
146                 continue;
147             }
148
149             stopAfterNext = true;
150             continue;
151         }
152
153         if (!child->isSVGInline())
154             continue;
155
156         if (findPreviousAndNextAttributes(child, locateElement, stopAfterNext, previous, next))
157             return true;
158     }
159
160     return false;
161 }
162
163 void RenderSVGText::subtreeChildWillBeDestroyed(RenderSVGInlineText* text, Vector<SVGTextLayoutAttributes*>& affectedAttributes)
164 {
165     ASSERT(text);
166
167     // The positioning elements cache depends on the size of each text renderer in the
168     // subtree. If this changes, clear the cache. It's going to be rebuilt below.
169     invalidateTextPositioningElements();
170
171     if (m_needsPositioningValuesUpdate)
172         return;
173
174     // This logic requires that the 'text' child is still inserted in the tree.
175     bool stopAfterNext = false;
176     SVGTextLayoutAttributes* previous = 0;
177     SVGTextLayoutAttributes* next = 0;
178     findPreviousAndNextAttributes(this, text, stopAfterNext, previous, next);
179     if (previous)
180         affectedAttributes.append(previous);
181     if (next)
182         affectedAttributes.append(next);
183
184     SVGTextLayoutAttributes* currentLayoutAttributes = text->layoutAttributes();
185
186     size_t position = m_layoutAttributes.find(currentLayoutAttributes);
187     ASSERT(position != notFound);
188     m_layoutAttributes.remove(position);
189
190     ASSERT(!m_layoutAttributes.contains(currentLayoutAttributes));
191 }
192
193 static inline void recursiveCollectLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes*>& attributes)
194 {
195     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
196         if (child->isSVGInlineText()) {
197             attributes.append(toRenderSVGInlineText(child)->layoutAttributes());
198             continue;
199         }
200
201         recursiveCollectLayoutAttributes(child, attributes);
202     }
203 }
204
205 static inline void checkLayoutAttributesConsistency(RenderSVGText* text, Vector<SVGTextLayoutAttributes*>& expectedLayoutAttributes)
206 {
207 #ifndef NDEBUG
208     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
209     recursiveCollectLayoutAttributes(text, newLayoutAttributes);
210     ASSERT(newLayoutAttributes == expectedLayoutAttributes);
211 #else
212     UNUSED_PARAM(text);
213     UNUSED_PARAM(expectedLayoutAttributes);
214 #endif
215 }
216
217 void RenderSVGText::subtreeChildWasDestroyed(RenderSVGInlineText*, Vector<SVGTextLayoutAttributes*>& affectedAttributes)
218 {
219     if (documentBeingDestroyed() || affectedAttributes.isEmpty())
220         return;
221
222     checkLayoutAttributesConsistency(this, m_layoutAttributes);
223
224     size_t size = affectedAttributes.size();
225     for (size_t i = 0; i < size; ++i)
226         m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(affectedAttributes[i]->context());
227 }
228
229 void RenderSVGText::subtreeStyleChanged(RenderSVGInlineText* text)
230 {
231     ASSERT(text);
232     if (m_needsPositioningValuesUpdate)
233         return;
234
235     // Only update the metrics cache, but not the text positioning element cache
236     // nor the layout attributes cached in the leaf #text renderers.
237     FontCachePurgePreventer fontCachePurgePreventer;
238     for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
239         if (descendant->isSVGInlineText())
240             m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(toRenderSVGInlineText(descendant));
241     }
242 }
243
244 void RenderSVGText::subtreeTextChanged(RenderSVGInlineText* text)
245 {
246     ASSERT(text);
247
248     // The positioning elements cache depends on the size of each text renderer in the
249     // subtree. If this changes, clear the cache. It's going to be rebuilt below.
250     invalidateTextPositioningElements();
251
252     if (m_needsPositioningValuesUpdate)
253         return;
254
255     FontCachePurgePreventer fontCachePurgePreventer;
256     for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
257         if (descendant->isSVGInlineText())
258             m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(descendant));
259     }
260 }
261
262 void RenderSVGText::invalidateTextPositioningElements()
263 {
264     // Clear the text positioning elements. This should be called when either the children
265     // of a DOM text element have changed, or the length of the text in any child element
266     // has changed. Failure to clear may leave us with invalid elements, as other code paths
267     // do not always cause the position elements to be marked invalid before use.
268     m_layoutAttributesBuilder.clearTextPositioningElements();
269 }
270
271 void RenderSVGText::layout()
272 {
273     ASSERT(needsLayout());
274     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
275
276     bool updateCachedBoundariesInParents = false;
277     if (m_needsTransformUpdate) {
278         SVGTextElement* text = static_cast<SVGTextElement*>(node());
279         m_localTransform = text->animatedLocalTransform();
280         m_needsTransformUpdate = false;
281         updateCachedBoundariesInParents = true;
282     }
283
284     // If the root layout size changed (eg. window size changes) or the positioning values change
285     // or the transform to the root context has changed then recompute the on-screen font size.
286     if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) {
287         for (RenderObject* descendant = this; descendant; descendant = descendant->nextInPreOrder(this)) {
288             if (descendant->isSVGInlineText())
289                 toRenderSVGInlineText(descendant)->updateScaledFont();
290         }
291
292         rebuildAllLayoutAttributes();
293         updateCachedBoundariesInParents = true;
294         m_needsTextMetricsUpdate = false;
295     }
296
297     if (m_needsPositioningValuesUpdate) {
298         // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
299         m_layoutAttributesBuilder.buildLayoutAttributesForWholeTree(this);
300         m_needsReordering = true;
301         m_needsPositioningValuesUpdate = false;
302         updateCachedBoundariesInParents = true;
303     }
304
305     // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
306     // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions.
307     ASSERT(!isInline());
308     ASSERT(!simplifiedLayout());
309     ASSERT(!scrollsOverflow());
310     ASSERT(!hasControlClip());
311     ASSERT(!hasColumns());
312     ASSERT(!positionedObjects());
313     ASSERT(!m_overflow);
314     ASSERT(!isAnonymousBlock());
315
316     if (!firstChild())
317         setChildrenInline(true);
318
319     // FIXME: We need to find a way to only layout the child boxes, if needed.
320     FloatRect oldBoundaries = objectBoundingBox();
321     ASSERT(childrenInline());
322     forceLayoutInlineChildren();
323
324     if (m_needsReordering)
325         m_needsReordering = false;
326
327     if (!updateCachedBoundariesInParents)
328         updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
329
330     // Invalidate all resources of this client if our layout changed.
331     if (everHadLayout() && selfNeedsLayout())
332         SVGResourcesCache::clientLayoutChanged(this);
333
334     // If our bounds changed, notify the parents.
335     if (updateCachedBoundariesInParents)
336         RenderSVGBlock::setNeedsBoundariesUpdate();
337
338     repainter.repaintAfterLayout();
339     setNeedsLayout(false);
340 }
341
342 RootInlineBox* RenderSVGText::createRootInlineBox() 
343 {
344     RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
345     box->setHasVirtualLogicalHeight();
346     return box;
347 }
348
349 bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
350 {
351     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
352     bool isVisible = (style()->visibility() == VISIBLE);
353     if (isVisible || !hitRules.requireVisible) {
354         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
355             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
356             FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
357
358             if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
359                 return false;       
360
361             return RenderBlock::nodeAtPoint(request, result, flooredIntPoint(localPoint), IntPoint(), hitTestAction);
362         }
363     }
364
365     return false;
366 }
367
368 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, const LayoutPoint&, const LayoutPoint&, HitTestAction)
369 {
370     ASSERT_NOT_REACHED();
371     return false;
372 }
373
374 VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents)
375 {
376     RootInlineBox* rootBox = firstRootBox();
377     if (!rootBox)
378         return createVisiblePosition(0, DOWNSTREAM);
379
380     ASSERT(rootBox->isSVGRootInlineBox());
381     ASSERT(!rootBox->nextRootBox());
382     ASSERT(childrenInline());
383
384     InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents);
385     if (!closestBox)
386         return createVisiblePosition(0, DOWNSTREAM);
387
388     return closestBox->renderer()->positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()));
389 }
390
391 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
392 {
393     quads.append(localToAbsoluteQuad(strokeBoundingBox(), false, wasFixed));
394 }
395
396 void RenderSVGText::paint(PaintInfo& paintInfo, const LayoutPoint&)
397 {
398     if (paintInfo.context->paintingDisabled())
399         return;
400
401     if (paintInfo.phase != PaintPhaseForeground
402      && paintInfo.phase != PaintPhaseSelfOutline
403      && paintInfo.phase != PaintPhaseSelection)
404          return;
405
406     PaintInfo blockInfo(paintInfo);
407     GraphicsContextStateSaver stateSaver(*blockInfo.context);
408     blockInfo.applyTransform(localToParentTransform());
409     RenderBlock::paint(blockInfo, LayoutPoint());
410 }
411
412 FloatRect RenderSVGText::strokeBoundingBox() const
413 {
414     FloatRect strokeBoundaries = objectBoundingBox();
415     const SVGRenderStyle* svgStyle = style()->svgStyle();
416     if (!svgStyle->hasStroke())
417         return strokeBoundaries;
418
419     ASSERT(node());
420     ASSERT(node()->isSVGElement());
421     SVGLengthContext lengthContext(static_cast<SVGElement*>(node()));
422     strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext));
423     return strokeBoundaries;
424 }
425
426 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
427 {
428     FloatRect repaintRect = strokeBoundingBox();
429     SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
430
431     if (const ShadowData* textShadow = style()->textShadow())
432         textShadow->adjustRectForShadow(repaintRect);
433
434     return repaintRect;
435 }
436
437 void RenderSVGText::addChild(RenderObject* child, RenderObject* beforeChild)
438 {
439     RenderSVGBlock::addChild(child, beforeChild);
440     subtreeChildAdded(child);
441 }
442
443 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
444 // in a SVG text element context.
445 RenderBlock* RenderSVGText::firstLineBlock() const
446 {
447     return 0;
448 }
449
450 // Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
451 // in a SVG text element context.
452 void RenderSVGText::updateFirstLetter()
453 {
454 }
455
456 void RenderSVGText::rebuildAllLayoutAttributes()
457 {
458     m_layoutAttributes.clear();
459     recursiveCollectLayoutAttributes(this, m_layoutAttributes);
460     if (m_layoutAttributes.isEmpty())
461         return;
462
463     m_layoutAttributesBuilder.rebuildMetricsForWholeTree(this);
464 }
465
466 void RenderSVGText::rebuildLayoutAttributes()
467 {
468     if (m_layoutAttributes.isEmpty()) {
469         rebuildAllLayoutAttributes();
470         return;
471     }
472
473     // Detect changes in layout attributes and only measure those text parts that have changed!
474     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
475     recursiveCollectLayoutAttributes(this, newLayoutAttributes);
476     if (newLayoutAttributes.isEmpty()) {
477         m_layoutAttributes.clear();
478         return;
479     }
480
481     // Compare m_layoutAttributes with newLayoutAttributes to figure out which attributes got added.
482     size_t size = newLayoutAttributes.size();
483     for (size_t i = 0; i < size; ++i) {
484         SVGTextLayoutAttributes* attributes = newLayoutAttributes[i];
485         if (m_layoutAttributes.find(attributes) == notFound)
486             m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(attributes->context());
487     }
488
489     m_layoutAttributes = newLayoutAttributes;
490 }
491
492 }
493
494 #endif // ENABLE(SVG)