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
57namespace WebCore {
58
59RenderSVGText::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
68RenderSVGText::~RenderSVGText()
69{
70    ASSERT(m_layoutAttributes.isEmpty());
71}
72
73bool RenderSVGText::isChildAllowed(RenderObject* child, RenderStyle*) const
74{
75    return child->isInline();
76}
77
78RenderSVGText* 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
88const 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
98LayoutRect RenderSVGText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
99{
100    return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
101}
102
103void 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
110void RenderSVGText::computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) const
111{
112    SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed);
113}
114
115void RenderSVGText::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const
116{
117    SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed);
118}
119
120const RenderObject* RenderSVGText::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
121{
122    return SVGRenderSupport::pushMappingToContainer(this, ancestorToStopAt, geometryMap);
123}
124
125static 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
133static 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
165inline 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
175void 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
230static 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
242void RenderSVGText::willBeDestroyed()
243{
244    m_layoutAttributes.clear();
245    m_layoutAttributesBuilder.clearTextPositioningElements();
246
247    RenderSVGBlock::willBeDestroyed();
248}
249
250void 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
282void 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
296void 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
313void 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
337static 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
349void 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*>(node());
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    forceLayoutInlineChildren();
418
419    if (m_needsReordering)
420        m_needsReordering = false;
421
422    if (!updateCachedBoundariesInParents)
423        updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
424
425    // Invalidate all resources of this client if our layout changed.
426    if (everHadLayout() && selfNeedsLayout())
427        SVGResourcesCache::clientLayoutChanged(this);
428
429    // If our bounds changed, notify the parents.
430    if (updateCachedBoundariesInParents)
431        RenderSVGBlock::setNeedsBoundariesUpdate();
432
433    repainter.repaintAfterLayout();
434    setNeedsLayout(false);
435}
436
437RootInlineBox* RenderSVGText::createRootInlineBox()
438{
439    RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
440    box->setHasVirtualLogicalHeight();
441    return box;
442}
443
444bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
445{
446    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
447    bool isVisible = (style()->visibility() == VISIBLE);
448    if (isVisible || !hitRules.requireVisible) {
449        if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
450            || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
451            FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
452
453            if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
454                return false;
455
456            HitTestLocation hitTestLocation(LayoutPoint(flooredIntPoint(localPoint)));
457            return RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), hitTestAction);
458        }
459    }
460
461    return false;
462}
463
464bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint&, HitTestAction)
465{
466    ASSERT_NOT_REACHED();
467    return false;
468}
469
470VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents)
471{
472    RootInlineBox* rootBox = firstRootBox();
473    if (!rootBox)
474        return createVisiblePosition(0, DOWNSTREAM);
475
476    ASSERT_WITH_SECURITY_IMPLICATION(rootBox->isSVGRootInlineBox());
477    ASSERT(!rootBox->nextRootBox());
478    ASSERT(childrenInline());
479
480    InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents);
481    if (!closestBox)
482        return createVisiblePosition(0, DOWNSTREAM);
483
484    return closestBox->renderer()->positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()));
485}
486
487void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
488{
489    quads.append(localToAbsoluteQuad(strokeBoundingBox(), 0 /* mode */, wasFixed));
490}
491
492void RenderSVGText::paint(PaintInfo& paintInfo, const LayoutPoint&)
493{
494    if (paintInfo.context->paintingDisabled())
495        return;
496
497    if (paintInfo.phase != PaintPhaseForeground
498     && paintInfo.phase != PaintPhaseSelfOutline
499     && paintInfo.phase != PaintPhaseSelection)
500         return;
501
502    PaintInfo blockInfo(paintInfo);
503    GraphicsContextStateSaver stateSaver(*blockInfo.context);
504    blockInfo.applyTransform(localToParentTransform());
505    RenderBlock::paint(blockInfo, LayoutPoint());
506}
507
508FloatRect RenderSVGText::strokeBoundingBox() const
509{
510    FloatRect strokeBoundaries = objectBoundingBox();
511    const SVGRenderStyle* svgStyle = style()->svgStyle();
512    if (!svgStyle->hasStroke())
513        return strokeBoundaries;
514
515    ASSERT(node());
516    ASSERT(node()->isSVGElement());
517    SVGLengthContext lengthContext(toSVGElement(node()));
518    strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext));
519    return strokeBoundaries;
520}
521
522FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
523{
524    FloatRect repaintRect = strokeBoundingBox();
525    SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
526
527    if (const ShadowData* textShadow = style()->textShadow())
528        textShadow->adjustRectForShadow(repaintRect);
529
530    return repaintRect;
531}
532
533void RenderSVGText::addChild(RenderObject* child, RenderObject* beforeChild)
534{
535    RenderSVGBlock::addChild(child, beforeChild);
536
537    SVGResourcesCache::clientWasAddedToTree(child, child->style());
538    subtreeChildWasAdded(child);
539}
540
541void RenderSVGText::removeChild(RenderObject* child)
542{
543    SVGResourcesCache::clientWillBeRemovedFromTree(child);
544
545    Vector<SVGTextLayoutAttributes*, 2> affectedAttributes;
546    FontCachePurgePreventer fontCachePurgePreventer;
547    subtreeChildWillBeRemoved(child, affectedAttributes);
548    RenderSVGBlock::removeChild(child);
549    subtreeChildWasRemoved(affectedAttributes);
550}
551
552// Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
553// in a SVG text element context.
554RenderBlock* RenderSVGText::firstLineBlock() const
555{
556    return 0;
557}
558
559// Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
560// in a SVG text element context.
561void RenderSVGText::updateFirstLetter()
562{
563}
564
565}
566
567#endif // ENABLE(SVG)
568