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