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