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