Unreviewed, rolling out r116498.
[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 static inline void recursiveUpdateLayoutAttributes(RenderObject* start, SVGTextLayoutAttributesBuilder& builder)
114 {
115     if (start->isSVGInlineText()) {
116         builder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(start));
117         return;
118     }
119
120     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
121         recursiveUpdateLayoutAttributes(child, builder);
122 }
123
124 void RenderSVGText::layoutAttributesChanged(RenderObject* child)
125 {
126     ASSERT(child);
127     if (m_needsPositioningValuesUpdate)
128         return;
129     FontCachePurgePreventer fontCachePurgePreventer;
130     recursiveUpdateLayoutAttributes(child, m_layoutAttributesBuilder);
131     rebuildLayoutAttributes();
132 }
133
134 static inline bool findPreviousAndNextAttributes(RenderObject* start, RenderSVGInlineText* locateElement, bool& stopAfterNext, SVGTextLayoutAttributes*& previous, SVGTextLayoutAttributes*& next)
135 {
136     ASSERT(start);
137     ASSERT(locateElement);
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 void RenderSVGText::layoutAttributesWillBeDestroyed(RenderSVGInlineText* text, Vector<SVGTextLayoutAttributes*>& affectedAttributes)
166 {
167     ASSERT(text);
168     if (m_needsPositioningValuesUpdate)
169         return;
170
171     bool stopAfterNext = false;
172     SVGTextLayoutAttributes* previous = 0;
173     SVGTextLayoutAttributes* next = 0;
174     findPreviousAndNextAttributes(this, text, stopAfterNext, previous, next);
175     if (previous)
176         affectedAttributes.append(previous);
177     if (next)
178         affectedAttributes.append(next);
179 }
180
181 void RenderSVGText::invalidateTextPositioningElements()
182 {
183     // Clear the text positioning elements. This should be called when either the children
184     // of a DOM text element have changed, or the length of the text in any child element
185     // has changed. Failure to clear may leave us with invalid elements, as other code paths
186     // do not always cause the position elements to be marked invalid before use.
187     m_layoutAttributesBuilder.clearTextPositioningElements();
188 }
189
190 static inline void recursiveUpdateScaledFont(RenderObject* start)
191 {
192     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
193         if (child->isSVGInlineText()) {
194             toRenderSVGInlineText(child)->updateScaledFont();
195             continue;
196         }
197
198         recursiveUpdateScaledFont(child);
199     }
200 }
201
202 void RenderSVGText::layout()
203 {
204     ASSERT(needsLayout());
205     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
206
207     bool updateCachedBoundariesInParents = false;
208     if (m_needsTransformUpdate) {
209         SVGTextElement* text = static_cast<SVGTextElement*>(node());
210         m_localTransform = text->animatedLocalTransform();
211         m_needsTransformUpdate = false;
212         updateCachedBoundariesInParents = true;
213     }
214
215     // If the root layout size changed (eg. window size changes) or the positioning values change
216     // or the transform to the root context has changed then recompute the on-screen font size.
217     if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) {
218         recursiveUpdateScaledFont(this);
219         rebuildLayoutAttributes(true);
220         updateCachedBoundariesInParents = true;
221         m_needsTextMetricsUpdate = false;
222     }
223
224     if (m_needsPositioningValuesUpdate) {
225         // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
226         m_layoutAttributesBuilder.buildLayoutAttributesForWholeTree(this);
227         m_needsReordering = true;
228         m_needsPositioningValuesUpdate = false;
229         updateCachedBoundariesInParents = true;
230     }
231
232     // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
233     // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions.
234     ASSERT(!isInline());
235     ASSERT(!simplifiedLayout());
236     ASSERT(!scrollsOverflow());
237     ASSERT(!hasControlClip());
238     ASSERT(!hasColumns());
239     ASSERT(!positionedObjects());
240     ASSERT(!m_overflow);
241     ASSERT(!isAnonymousBlock());
242
243     if (!firstChild())
244         setChildrenInline(true);
245
246     // FIXME: We need to find a way to only layout the child boxes, if needed.
247     FloatRect oldBoundaries = objectBoundingBox();
248     ASSERT(childrenInline());
249     forceLayoutInlineChildren();
250
251     if (m_needsReordering)
252         m_needsReordering = false;
253
254     if (!updateCachedBoundariesInParents)
255         updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
256
257     // Invalidate all resources of this client if our layout changed.
258     if (everHadLayout() && selfNeedsLayout())
259         SVGResourcesCache::clientLayoutChanged(this);
260
261     // If our bounds changed, notify the parents.
262     if (updateCachedBoundariesInParents)
263         RenderSVGBlock::setNeedsBoundariesUpdate();
264
265     repainter.repaintAfterLayout();
266     setNeedsLayout(false);
267 }
268
269 RootInlineBox* RenderSVGText::createRootInlineBox() 
270 {
271     RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
272     box->setHasVirtualLogicalHeight();
273     return box;
274 }
275
276 bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
277 {
278     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
279     bool isVisible = (style()->visibility() == VISIBLE);
280     if (isVisible || !hitRules.requireVisible) {
281         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
282             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
283             FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
284
285             if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
286                 return false;       
287
288             return RenderBlock::nodeAtPoint(request, result, flooredIntPoint(localPoint), IntPoint(), hitTestAction);
289         }
290     }
291
292     return false;
293 }
294
295 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, const LayoutPoint&, const LayoutPoint&, HitTestAction)
296 {
297     ASSERT_NOT_REACHED();
298     return false;
299 }
300
301 VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents)
302 {
303     RootInlineBox* rootBox = firstRootBox();
304     if (!rootBox)
305         return createVisiblePosition(0, DOWNSTREAM);
306
307     ASSERT(rootBox->isSVGRootInlineBox());
308     ASSERT(!rootBox->nextRootBox());
309     ASSERT(childrenInline());
310
311     InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents);
312     if (!closestBox)
313         return createVisiblePosition(0, DOWNSTREAM);
314
315     return closestBox->renderer()->positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()));
316 }
317
318 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
319 {
320     quads.append(localToAbsoluteQuad(strokeBoundingBox(), false, wasFixed));
321 }
322
323 void RenderSVGText::paint(PaintInfo& paintInfo, const LayoutPoint&)
324 {
325     if (paintInfo.context->paintingDisabled())
326         return;
327
328     if (paintInfo.phase != PaintPhaseForeground
329      && paintInfo.phase != PaintPhaseSelfOutline
330      && paintInfo.phase != PaintPhaseSelection)
331          return;
332
333     PaintInfo blockInfo(paintInfo);
334     GraphicsContextStateSaver stateSaver(*blockInfo.context);
335     blockInfo.applyTransform(localToParentTransform());
336     RenderBlock::paint(blockInfo, LayoutPoint());
337 }
338
339 FloatRect RenderSVGText::strokeBoundingBox() const
340 {
341     FloatRect strokeBoundaries = objectBoundingBox();
342     const SVGRenderStyle* svgStyle = style()->svgStyle();
343     if (!svgStyle->hasStroke())
344         return strokeBoundaries;
345
346     ASSERT(node());
347     ASSERT(node()->isSVGElement());
348     SVGLengthContext lengthContext(static_cast<SVGElement*>(node()));
349     strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext));
350     return strokeBoundaries;
351 }
352
353 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
354 {
355     FloatRect repaintRect = strokeBoundingBox();
356     SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
357
358     if (const ShadowData* textShadow = style()->textShadow())
359         textShadow->adjustRectForShadow(repaintRect);
360
361     return repaintRect;
362 }
363
364 void RenderSVGText::addChild(RenderObject* child, RenderObject* beforeChild)
365 {
366     RenderSVGBlock::addChild(child, beforeChild);
367     layoutAttributesChanged(child);
368 }
369
370 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
371 // in a SVG text element context.
372 RenderBlock* RenderSVGText::firstLineBlock() const
373 {
374     return 0;
375 }
376
377 // Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
378 // in a SVG text element context.
379 void RenderSVGText::updateFirstLetter()
380 {
381 }
382
383 static inline void recursiveCollectLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes*>& attributes)
384 {
385     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
386         if (child->isSVGInlineText()) {
387             attributes.append(toRenderSVGInlineText(child)->layoutAttributes());
388             continue;
389         }
390
391         recursiveCollectLayoutAttributes(child, attributes);
392     }
393 }
394
395 void RenderSVGText::rebuildLayoutAttributes(bool performFullRebuild)
396 {
397     if (performFullRebuild)
398         m_layoutAttributes.clear();
399
400     if (m_layoutAttributes.isEmpty()) {
401         recursiveCollectLayoutAttributes(this, m_layoutAttributes);
402         if (m_layoutAttributes.isEmpty() || !performFullRebuild)
403             return;
404
405         m_layoutAttributesBuilder.rebuildMetricsForWholeTree(this);
406         return;
407     }
408
409     Vector<SVGTextLayoutAttributes*> affectedAttributes;
410     rebuildLayoutAttributes(affectedAttributes);
411 }
412
413 void RenderSVGText::rebuildLayoutAttributes(Vector<SVGTextLayoutAttributes*>& affectedAttributes)
414 {
415     // Detect changes in layout attributes and only measure those text parts that have changed!
416     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
417     recursiveCollectLayoutAttributes(this, newLayoutAttributes);
418     if (newLayoutAttributes.isEmpty()) {
419         m_layoutAttributes.clear();
420         return;
421     }
422
423     // Compare m_layoutAttributes with newLayoutAttributes to figure out which attributes got added/removed.
424     size_t size = newLayoutAttributes.size();
425     for (size_t i = 0; i < size; ++i) {
426         SVGTextLayoutAttributes* attributes = newLayoutAttributes[i];
427         if (m_layoutAttributes.find(attributes) == notFound)
428             m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(attributes->context());
429     }
430
431     size = affectedAttributes.size();
432     for (size_t i = 0; i < size; ++i)
433         m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(affectedAttributes[i]->context());
434
435     m_layoutAttributes = newLayoutAttributes;
436 }
437
438 }
439
440 #endif // ENABLE(SVG)