Changeset 117225 in webkit


Ignore:
Timestamp:
May 16, 2012 12:21:07 AM (12 years ago)
Author:
Nikolas Zimmermann
Message:

REGRESSION(r105057): Infinite loop inside SVGTextLayoutEngine::currentLogicalCharacterMetrics
https://bugs.webkit.org/show_bug.cgi?id=83405

Reviewed by Darin Adler.

Source/WebCore:

Dynamically adding tspans carrying position information in the x/y/dx/dy/rotate lists is broken.
To avoid mistakes like this in future, simplify the calling code in RenderSVGInlineText and centralize
the management of all caches (text positioning element cache / metrics map / layout attributes) in
RenderSVGText. This avoids the hack in SVGRootInlineBox::computePerCharacterLayoutInformation() which
called textRoot->rebuildLayoutAttributes(), which was used to fix previous security issues with this code.
Instead correctly handle destruction of RenderSVGInlineText in RenderSVGText, keeping the m_layoutAttributes
synchronized with the current state of the render tree. Fixes highcharts problems.

Tests: svg/text/add-tspan-position-bug.html

svg/text/highcharts-assertion.html
svg/text/modify-tspan-position-bug.html

  • rendering/svg/RenderSVGBlock.h:

(RenderSVGBlock):

  • rendering/svg/RenderSVGInline.cpp:

(WebCore::RenderSVGInline::addChild):
(WebCore::RenderSVGInline::removeChild):

  • rendering/svg/RenderSVGInline.h:

(RenderSVGInline):

  • rendering/svg/RenderSVGInlineText.cpp:

(WebCore::RenderSVGInlineText::setTextInternal):
(WebCore::RenderSVGInlineText::styleDidChange):

  • rendering/svg/RenderSVGInlineText.h:
  • rendering/svg/RenderSVGText.cpp:

(WebCore::RenderSVGText::RenderSVGText):
(WebCore::RenderSVGText::~RenderSVGText):
(WebCore::collectLayoutAttributes):
(WebCore::RenderSVGText::subtreeChildWasAdded):
(WebCore::findPreviousAndNextAttributes):
(WebCore::checkLayoutAttributesConsistency):
(WebCore::RenderSVGText::willBeDestroyed):
(WebCore::RenderSVGText::subtreeChildWillBeRemoved):
(WebCore::RenderSVGText::subtreeChildWasRemoved):
(WebCore::RenderSVGText::subtreeStyleDidChange):
(WebCore::RenderSVGText::subtreeTextDidChange):
(WebCore::updateFontInAllDescendants):
(WebCore::RenderSVGText::layout):
(WebCore::RenderSVGText::addChild):
(WebCore::RenderSVGText::removeChild):

  • rendering/svg/RenderSVGText.h:

(RenderSVGText):

  • rendering/svg/SVGRenderSupport.cpp:

(WebCore::SVGRenderSupport::layoutChildren):

  • rendering/svg/SVGRootInlineBox.cpp:

(WebCore::SVGRootInlineBox::computePerCharacterLayoutInformation):

  • rendering/svg/SVGTextLayoutAttributesBuilder.cpp:

(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer):
(WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree):
(WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer):
(WebCore::SVGTextLayoutAttributesBuilder::buildCharacterDataMap):

  • rendering/svg/SVGTextLayoutAttributesBuilder.h:

(SVGTextLayoutAttributesBuilder):
(WebCore::SVGTextLayoutAttributesBuilder::numberOfTextPositioningElements):

  • svg/SVGAElement.cpp:
  • svg/SVGAElement.h:

(SVGAElement):

  • svg/SVGTextContentElement.cpp:
  • svg/SVGTextContentElement.h:

(SVGTextContentElement):

LayoutTests:

Add three new testcases covering the problem.

  • svg/text/add-tspan-position-bug-expected.html: Added.
  • svg/text/add-tspan-position-bug.html: Added.
  • svg/text/highcharts-assertion-expected.txt: Added.
  • svg/text/highcharts-assertion.html: Added.
  • svg/text/modify-tspan-position-bug-expected.html: Added.
  • svg/text/modify-tspan-position-bug.html: Added.
Location:
trunk
Files:
6 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r117224 r117225  
     12012-05-16  Nikolas Zimmermann  <nzimmermann@rim.com>
     2
     3        REGRESSION(r105057): Infinite loop inside SVGTextLayoutEngine::currentLogicalCharacterMetrics
     4        https://bugs.webkit.org/show_bug.cgi?id=83405
     5
     6        Reviewed by Darin Adler.
     7
     8        Add three new testcases covering the problem.
     9
     10        * svg/text/add-tspan-position-bug-expected.html: Added.
     11        * svg/text/add-tspan-position-bug.html: Added.
     12        * svg/text/highcharts-assertion-expected.txt: Added.
     13        * svg/text/highcharts-assertion.html: Added.
     14        * svg/text/modify-tspan-position-bug-expected.html: Added.
     15        * svg/text/modify-tspan-position-bug.html: Added.
     16
    1172012-05-15  Abhishek Arya  <inferno@chromium.org>
    218
  • trunk/Source/WebCore/ChangeLog

    r117224 r117225  
     12012-05-16  Nikolas Zimmermann  <nzimmermann@rim.com>
     2
     3        REGRESSION(r105057): Infinite loop inside SVGTextLayoutEngine::currentLogicalCharacterMetrics
     4        https://bugs.webkit.org/show_bug.cgi?id=83405
     5
     6        Reviewed by Darin Adler.
     7
     8        Dynamically adding tspans carrying position information in the x/y/dx/dy/rotate lists is broken.
     9        To avoid mistakes like this in future, simplify the calling code in RenderSVGInlineText and centralize
     10        the management of all caches (text positioning element cache / metrics map / layout attributes) in
     11        RenderSVGText. This avoids the hack in SVGRootInlineBox::computePerCharacterLayoutInformation() which
     12        called textRoot->rebuildLayoutAttributes(), which was used to fix previous security issues with this code.
     13        Instead correctly handle destruction of RenderSVGInlineText in RenderSVGText, keeping the m_layoutAttributes
     14        synchronized with the current state of the render tree. Fixes highcharts problems.
     15
     16        Tests: svg/text/add-tspan-position-bug.html
     17               svg/text/highcharts-assertion.html
     18               svg/text/modify-tspan-position-bug.html
     19
     20        * rendering/svg/RenderSVGBlock.h:
     21        (RenderSVGBlock):
     22        * rendering/svg/RenderSVGInline.cpp:
     23        (WebCore::RenderSVGInline::addChild):
     24        (WebCore::RenderSVGInline::removeChild):
     25        * rendering/svg/RenderSVGInline.h:
     26        (RenderSVGInline):
     27        * rendering/svg/RenderSVGInlineText.cpp:
     28        (WebCore::RenderSVGInlineText::setTextInternal):
     29        (WebCore::RenderSVGInlineText::styleDidChange):
     30        * rendering/svg/RenderSVGInlineText.h:
     31        * rendering/svg/RenderSVGText.cpp:
     32        (WebCore::RenderSVGText::RenderSVGText):
     33        (WebCore::RenderSVGText::~RenderSVGText):
     34        (WebCore::collectLayoutAttributes):
     35        (WebCore::RenderSVGText::subtreeChildWasAdded):
     36        (WebCore::findPreviousAndNextAttributes):
     37        (WebCore::checkLayoutAttributesConsistency):
     38        (WebCore::RenderSVGText::willBeDestroyed):
     39        (WebCore::RenderSVGText::subtreeChildWillBeRemoved):
     40        (WebCore::RenderSVGText::subtreeChildWasRemoved):
     41        (WebCore::RenderSVGText::subtreeStyleDidChange):
     42        (WebCore::RenderSVGText::subtreeTextDidChange):
     43        (WebCore::updateFontInAllDescendants):
     44        (WebCore::RenderSVGText::layout):
     45        (WebCore::RenderSVGText::addChild):
     46        (WebCore::RenderSVGText::removeChild):
     47        * rendering/svg/RenderSVGText.h:
     48        (RenderSVGText):
     49        * rendering/svg/SVGRenderSupport.cpp:
     50        (WebCore::SVGRenderSupport::layoutChildren):
     51        * rendering/svg/SVGRootInlineBox.cpp:
     52        (WebCore::SVGRootInlineBox::computePerCharacterLayoutInformation):
     53        * rendering/svg/SVGTextLayoutAttributesBuilder.cpp:
     54        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextRenderer):
     55        (WebCore::SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree):
     56        (WebCore::SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer):
     57        (WebCore::SVGTextLayoutAttributesBuilder::buildCharacterDataMap):
     58        * rendering/svg/SVGTextLayoutAttributesBuilder.h:
     59        (SVGTextLayoutAttributesBuilder):
     60        (WebCore::SVGTextLayoutAttributesBuilder::numberOfTextPositioningElements):
     61        * svg/SVGAElement.cpp:
     62        * svg/SVGAElement.h:
     63        (SVGAElement):
     64        * svg/SVGTextContentElement.cpp:
     65        * svg/SVGTextContentElement.h:
     66        (SVGTextContentElement):
     67
    1682012-05-15  Abhishek Arya  <inferno@chromium.org>
    269
  • trunk/Source/WebCore/rendering/svg/RenderSVGBlock.h

    r110224 r117225  
    3535    virtual LayoutRect visualOverflowRect() const;
    3636
     37protected:
     38    virtual void willBeDestroyed() OVERRIDE;
     39
    3740private:
    3841    virtual void setStyle(PassRefPtr<RenderStyle>);
     
    4144    virtual void absoluteRects(Vector<IntRect>&, const LayoutPoint& accumulatedOffset) const;
    4245
    43     virtual void willBeDestroyed();
    4446    virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
    4547    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
  • trunk/Source/WebCore/rendering/svg/RenderSVGInline.cpp

    r116801 r117225  
    125125    RenderInline::addChild(child, beforeChild);
    126126    if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this))
    127         textRenderer->layoutAttributesChanged(child);
     127        textRenderer->subtreeChildWasAdded(child);
     128}
     129
     130void RenderSVGInline::removeChild(RenderObject* child)
     131{
     132    RenderSVGText* textRenderer = child->isSVGInlineText() ? RenderSVGText::locateRenderSVGTextAncestor(this) : 0;
     133    if (!textRenderer) {
     134        RenderInline::removeChild(child);
     135        return;
     136    }
     137
     138    RenderSVGInlineText* text = toRenderSVGInlineText(child);
     139    Vector<SVGTextLayoutAttributes*, 2> affectedAttributes;
     140    textRenderer->subtreeChildWillBeRemoved(text, affectedAttributes);
     141    RenderInline::removeChild(child);
     142    textRenderer->subtreeChildWasRemoved(affectedAttributes);
    128143}
    129144
  • trunk/Source/WebCore/rendering/svg/RenderSVGInline.h

    r115981 r117225  
    5858    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
    5959    virtual void updateFromElement();
     60
    6061    virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
     62    virtual void removeChild(RenderObject*) OVERRIDE;
    6163};
    6264
  • trunk/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp

    r116801 r117225  
    7373}
    7474
    75 void RenderSVGInlineText::willBeDestroyed()
    76 {
    77     RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this);
    78     if (!textRenderer) {
    79         RenderText::willBeDestroyed();
    80         return;
    81     }
    82 
    83     Vector<SVGTextLayoutAttributes*> affectedAttributes;
    84     textRenderer->layoutAttributesWillBeDestroyed(this, affectedAttributes);
    85 
    86     RenderText::willBeDestroyed();
    87     if (affectedAttributes.isEmpty())
    88         return;
    89 
    90     if (!documentBeingDestroyed())
    91         textRenderer->rebuildLayoutAttributes(affectedAttributes);
    92 }
    93 
    9475void RenderSVGInlineText::setTextInternal(PassRefPtr<StringImpl> text)
    9576{
    9677    RenderText::setTextInternal(text);
    97 
    98     // When the underlying text content changes, call both textDOMChanged() & layoutAttributesChanged()
    99     // The former will clear the SVGTextPositioningElement cache, which depends on the textLength() of
    100     // the RenderSVGInlineText objects, and thus needs to be rebuild. The latter will assure that the
    101     // SVGTextLayoutAttributes associated with the RenderSVGInlineText will be updated.
    102     if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this)) {
    103         textRenderer->invalidateTextPositioningElements();
    104         textRenderer->layoutAttributesChanged(this);
    105     }
     78    if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this))
     79        textRenderer->subtreeTextDidChange(this);
    10680}
    10781
     
    128102    // The text metrics may be influenced by style changes.
    129103    if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this))
    130         textRenderer->layoutAttributesChanged(this);
     104        textRenderer->subtreeStyleDidChange(this);
    131105}
    132106
  • trunk/Source/WebCore/rendering/svg/RenderSVGInlineText.h

    r112237 r117225  
    4949    virtual const char* renderName() const { return "RenderSVGInlineText"; }
    5050
    51     virtual void willBeDestroyed();
    5251    virtual void setTextInternal(PassRefPtr<StringImpl>);
    5352    virtual void styleDidChange(StyleDifference, const RenderStyle*);
  • trunk/Source/WebCore/rendering/svg/RenderSVGText.cpp

    r116801 r117225  
    5858    : RenderSVGBlock(node)
    5959    , m_needsReordering(false)
    60     , m_needsPositioningValuesUpdate(true)
     60    , m_needsPositioningValuesUpdate(false)
    6161    , m_needsTransformUpdate(true)
    62     , m_needsTextMetricsUpdate(true)
    63 {
     62    , m_needsTextMetricsUpdate(false)
     63{
     64}
     65
     66RenderSVGText::~RenderSVGText()
     67{
     68    ASSERT(m_layoutAttributes.isEmpty());
    6469}
    6570
     
    111116}
    112117
    113 static inline void recursiveUpdateLayoutAttributes(RenderObject* start, SVGTextLayoutAttributesBuilder& builder)
    114 {
    115     if (start->isSVGInlineText()) {
    116         builder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(start));
    117         return;
    118     }
    119 
    120     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
    121         recursiveUpdateLayoutAttributes(child, builder);
    122 }
    123 
    124 void RenderSVGText::layoutAttributesChanged(RenderObject* child)
    125 {
    126     ASSERT(child);
    127     if (m_needsPositioningValuesUpdate)
    128         return;
    129     FontCachePurgePreventer fontCachePurgePreventer;
    130     recursiveUpdateLayoutAttributes(child, m_layoutAttributesBuilder);
    131     rebuildLayoutAttributes();
     118static inline void collectLayoutAttributes(RenderObject* text, Vector<SVGTextLayoutAttributes*>& attributes)
     119{
     120    for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
     121        if (descendant->isSVGInlineText())
     122            attributes.append(toRenderSVGInlineText(descendant)->layoutAttributes());
     123    }
    132124}
    133125
     
    136128    ASSERT(start);
    137129    ASSERT(locateElement);
     130    // FIXME: Make this iterative.
    138131    for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    139132        if (child->isSVGInlineText()) {
     
    163156}
    164157
    165 void RenderSVGText::layoutAttributesWillBeDestroyed(RenderSVGInlineText* text, Vector<SVGTextLayoutAttributes*>& affectedAttributes)
     158inline bool RenderSVGText::shouldHandleSubtreeMutations() const
     159{
     160    if (beingDestroyed() || !everHadLayout()) {
     161        ASSERT(m_layoutAttributes.isEmpty());
     162        ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements());
     163        return false;
     164    }
     165    return true;
     166}
     167
     168void RenderSVGText::subtreeChildWasAdded(RenderObject* child)
     169{
     170    ASSERT(child);
     171    if (!shouldHandleSubtreeMutations() || documentBeingDestroyed())
     172        return;
     173
     174    // The positioning elements cache doesn't include the new 'child' yet. Clear the
     175    // cache, as the next buildLayoutAttributesForTextRenderer() call rebuilds it.
     176    m_layoutAttributesBuilder.clearTextPositioningElements();
     177
     178    // Detect changes in layout attributes and only measure those text parts that have changed!
     179    Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
     180    collectLayoutAttributes(this, newLayoutAttributes);
     181    if (newLayoutAttributes.isEmpty()) {
     182        ASSERT(m_layoutAttributes.isEmpty());
     183        return;
     184    }
     185
     186    // Compare m_layoutAttributes with newLayoutAttributes to figure out which attribute got added.
     187    size_t size = newLayoutAttributes.size();
     188    SVGTextLayoutAttributes* attributes = 0;
     189    for (size_t i = 0; i < size; ++i) {
     190        attributes = newLayoutAttributes[i];
     191        if (m_layoutAttributes.find(attributes) == notFound) {
     192            // Every time this is invoked, there's only a single new entry in the newLayoutAttributes list, compared to the old in m_layoutAttributes.
     193            bool stopAfterNext = false;
     194            SVGTextLayoutAttributes* previous = 0;
     195            SVGTextLayoutAttributes* next = 0;
     196            ASSERT_UNUSED(child, attributes->context() == child);
     197            findPreviousAndNextAttributes(this, attributes->context(), stopAfterNext, previous, next);
     198
     199            if (previous)
     200                m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(previous->context());
     201            m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(attributes->context());
     202            if (next)
     203                m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(next->context());
     204            break;
     205        }
     206    }
     207
     208#ifndef NDEBUG
     209    // Verify that m_layoutAttributes only differs by a maximum of one entry.
     210    for (size_t i = 0; i < size; ++i)
     211        ASSERT(m_layoutAttributes.find(newLayoutAttributes[i]) != notFound || newLayoutAttributes[i] == attributes);
     212#endif
     213
     214    m_layoutAttributes = newLayoutAttributes;
     215}
     216
     217static inline void checkLayoutAttributesConsistency(RenderSVGText* text, Vector<SVGTextLayoutAttributes*>& expectedLayoutAttributes)
     218{
     219#ifndef NDEBUG
     220    Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
     221    collectLayoutAttributes(text, newLayoutAttributes);
     222    ASSERT(newLayoutAttributes == expectedLayoutAttributes);
     223#else
     224    UNUSED_PARAM(text);
     225    UNUSED_PARAM(expectedLayoutAttributes);
     226#endif
     227}
     228
     229void RenderSVGText::willBeDestroyed()
     230{
     231    m_layoutAttributes.clear();
     232    m_layoutAttributesBuilder.clearTextPositioningElements();
     233
     234    RenderSVGBlock::willBeDestroyed();
     235}
     236
     237void RenderSVGText::subtreeChildWillBeRemoved(RenderSVGInlineText* text, Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes)
    166238{
    167239    ASSERT(text);
    168     if (m_needsPositioningValuesUpdate)
    169         return;
    170 
     240    if (!shouldHandleSubtreeMutations())
     241        return;
     242
     243    checkLayoutAttributesConsistency(this, m_layoutAttributes);
     244
     245    // The positioning elements cache depends on the size of each text renderer in the
     246    // subtree. If this changes, clear the cache. It's going to be rebuilt below.
     247    m_layoutAttributesBuilder.clearTextPositioningElements();
     248    if (m_layoutAttributes.isEmpty())
     249        return;
     250
     251    // This logic requires that the 'text' child is still inserted in the tree.
    171252    bool stopAfterNext = false;
    172253    SVGTextLayoutAttributes* previous = 0;
    173254    SVGTextLayoutAttributes* next = 0;
    174     findPreviousAndNextAttributes(this, text, stopAfterNext, previous, next);
     255    if (!documentBeingDestroyed())
     256        findPreviousAndNextAttributes(this, text, stopAfterNext, previous, next);
     257
    175258    if (previous)
    176259        affectedAttributes.append(previous);
    177260    if (next)
    178261        affectedAttributes.append(next);
    179 }
    180 
    181 void RenderSVGText::invalidateTextPositioningElements()
    182 {
    183     // Clear the text positioning elements. This should be called when either the children
    184     // of a DOM text element have changed, or the length of the text in any child element
    185     // has changed. Failure to clear may leave us with invalid elements, as other code paths
    186     // do not always cause the position elements to be marked invalid before use.
     262
     263    size_t position = m_layoutAttributes.find(text->layoutAttributes());
     264    ASSERT(position != notFound);
     265    m_layoutAttributes.remove(position);
     266}
     267
     268void RenderSVGText::subtreeChildWasRemoved(const Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes)
     269{
     270    if (!shouldHandleSubtreeMutations() || documentBeingDestroyed()) {
     271        ASSERT(affectedAttributes.isEmpty());
     272        return;
     273    }
     274
     275    // This is called immediately after subtreeChildWillBeDestroyed, once the RenderSVGInlineText::willBeDestroyed() method
     276    // passes on to the base class, which removes us from the render tree. At this point we can update the layout attributes.
     277    unsigned size = affectedAttributes.size();
     278    for (unsigned i = 0; i < size; ++i)
     279        m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(affectedAttributes[i]->context());
     280}
     281
     282void RenderSVGText::subtreeStyleDidChange(RenderSVGInlineText* text)
     283{
     284    ASSERT(text);
     285    if (!shouldHandleSubtreeMutations() || documentBeingDestroyed())
     286        return;
     287
     288    checkLayoutAttributesConsistency(this, m_layoutAttributes);
     289
     290    // Only update the metrics cache, but not the text positioning element cache
     291    // nor the layout attributes cached in the leaf #text renderers.
     292    FontCachePurgePreventer fontCachePurgePreventer;
     293    for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
     294        if (descendant->isSVGInlineText())
     295            m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(toRenderSVGInlineText(descendant));
     296    }
     297}
     298
     299void RenderSVGText::subtreeTextDidChange(RenderSVGInlineText* text)
     300{
     301    ASSERT(text);
     302    ASSERT(!beingDestroyed());
     303    if (!everHadLayout()) {
     304        ASSERT(m_layoutAttributes.isEmpty());
     305        ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements());
     306        return;
     307    }
     308
     309    // The positioning elements cache depends on the size of each text renderer in the
     310    // subtree. If this changes, clear the cache. It's going to be rebuilt below.
    187311    m_layoutAttributesBuilder.clearTextPositioningElements();
    188 }
    189 
    190 static inline void recursiveUpdateScaledFont(RenderObject* start)
    191 {
    192     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    193         if (child->isSVGInlineText()) {
    194             toRenderSVGInlineText(child)->updateScaledFont();
     312
     313    checkLayoutAttributesConsistency(this, m_layoutAttributes);
     314    for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) {
     315        if (descendant->isSVGInlineText())
     316            m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(descendant));
     317    }
     318}
     319
     320static inline void updateFontInAllDescendants(RenderObject* start, SVGTextLayoutAttributesBuilder* builder = 0)
     321{
     322    for (RenderObject* descendant = start; descendant; descendant = descendant->nextInPreOrder(start)) {
     323        if (!descendant->isSVGInlineText())
    195324            continue;
    196         }
    197 
    198         recursiveUpdateScaledFont(child);
     325        RenderSVGInlineText* text = toRenderSVGInlineText(descendant);
     326        text->updateScaledFont();
     327        if (builder)
     328            builder->rebuildMetricsForTextRenderer(text);
    199329    }
    200330}
     
    213343    }
    214344
    215     // If the root layout size changed (eg. window size changes) or the positioning values change
    216     // or the transform to the root context has changed then recompute the on-screen font size.
    217     if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) {
    218         recursiveUpdateScaledFont(this);
    219         rebuildLayoutAttributes(true);
     345    if (!everHadLayout()) {
     346        // When laying out initially, collect all layout attributes, build the character data map,
     347        // and propogate resulting SVGLayoutAttributes to all RenderSVGInlineText children in the subtree.
     348        ASSERT(m_layoutAttributes.isEmpty());
     349        collectLayoutAttributes(this, m_layoutAttributes);
     350        updateFontInAllDescendants(this);
     351        m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(this);
     352
     353        m_needsReordering = true;
     354        m_needsTextMetricsUpdate = false;
     355        m_needsPositioningValuesUpdate = false;
    220356        updateCachedBoundariesInParents = true;
    221         m_needsTextMetricsUpdate = false;
    222     }
    223 
    224     if (m_needsPositioningValuesUpdate) {
    225         // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
    226         m_layoutAttributesBuilder.buildLayoutAttributesForWholeTree(this);
     357    } else if (m_needsPositioningValuesUpdate) {
     358        // When the x/y/dx/dy/rotate lists change, recompute the layout attributes, and eventually
     359        // update the on-screen font objects as well in all descendants.
     360        if (m_needsTextMetricsUpdate) {
     361            updateFontInAllDescendants(this);
     362            m_needsTextMetricsUpdate = false;
     363        }
     364
     365        m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(this);
    227366        m_needsReordering = true;
    228367        m_needsPositioningValuesUpdate = false;
    229368        updateCachedBoundariesInParents = true;
    230     }
     369    } else if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) {
     370        // If the root layout size changed (eg. window size changes) or the transform to the root
     371        // context has changed then recompute the on-screen font size.
     372        updateFontInAllDescendants(this, &m_layoutAttributesBuilder);
     373
     374        ASSERT(!m_needsReordering);
     375        ASSERT(!m_needsPositioningValuesUpdate);
     376        m_needsTextMetricsUpdate = false;
     377        updateCachedBoundariesInParents = true;
     378    }
     379
     380    checkLayoutAttributesConsistency(this, m_layoutAttributes);
    231381
    232382    // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
     
    365515{
    366516    RenderSVGBlock::addChild(child, beforeChild);
    367     layoutAttributesChanged(child);
     517    subtreeChildWasAdded(child);
     518}
     519
     520void RenderSVGText::removeChild(RenderObject* child)
     521{
     522    if (!child->isSVGInlineText()) {
     523        RenderSVGBlock::removeChild(child);
     524        return;
     525    }
     526
     527    RenderSVGInlineText* text = toRenderSVGInlineText(child);
     528    Vector<SVGTextLayoutAttributes*, 2> affectedAttributes;
     529    subtreeChildWillBeRemoved(text, affectedAttributes);
     530    RenderSVGBlock::removeChild(child);
     531    subtreeChildWasRemoved(affectedAttributes);
    368532}
    369533
     
    381545}
    382546
    383 static inline void recursiveCollectLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes*>& attributes)
    384 {
    385     for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
    386         if (child->isSVGInlineText()) {
    387             attributes.append(toRenderSVGInlineText(child)->layoutAttributes());
    388             continue;
    389         }
    390 
    391         recursiveCollectLayoutAttributes(child, attributes);
    392     }
    393 }
    394 
    395 void RenderSVGText::rebuildLayoutAttributes(bool performFullRebuild)
    396 {
    397     if (performFullRebuild)
    398         m_layoutAttributes.clear();
    399 
    400     if (m_layoutAttributes.isEmpty()) {
    401         recursiveCollectLayoutAttributes(this, m_layoutAttributes);
    402         if (m_layoutAttributes.isEmpty() || !performFullRebuild)
    403             return;
    404 
    405         m_layoutAttributesBuilder.rebuildMetricsForWholeTree(this);
    406         return;
    407     }
    408 
    409     Vector<SVGTextLayoutAttributes*> affectedAttributes;
    410     rebuildLayoutAttributes(affectedAttributes);
    411 }
    412 
    413 void RenderSVGText::rebuildLayoutAttributes(Vector<SVGTextLayoutAttributes*>& affectedAttributes)
    414 {
    415     // Detect changes in layout attributes and only measure those text parts that have changed!
    416     Vector<SVGTextLayoutAttributes*> newLayoutAttributes;
    417     recursiveCollectLayoutAttributes(this, newLayoutAttributes);
    418     if (newLayoutAttributes.isEmpty()) {
    419         m_layoutAttributes.clear();
    420         return;
    421     }
    422 
    423     // Compare m_layoutAttributes with newLayoutAttributes to figure out which attributes got added/removed.
    424     size_t size = newLayoutAttributes.size();
    425     for (size_t i = 0; i < size; ++i) {
    426         SVGTextLayoutAttributes* attributes = newLayoutAttributes[i];
    427         if (m_layoutAttributes.find(attributes) == notFound)
    428             m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(attributes->context());
    429     }
    430 
    431     size = affectedAttributes.size();
    432     for (size_t i = 0; i < size; ++i)
    433         m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(affectedAttributes[i]->context());
    434 
    435     m_layoutAttributes = newLayoutAttributes;
    436 }
    437 
    438547}
    439548
  • trunk/Source/WebCore/rendering/svg/RenderSVGText.h

    r116801 r117225  
    3737public:
    3838    RenderSVGText(SVGTextElement*);
     39    virtual ~RenderSVGText();
    3940
    4041    virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
     
    4950
    5051    bool needsReordering() const { return m_needsReordering; }
     52    Vector<SVGTextLayoutAttributes*>& layoutAttributes() { return m_layoutAttributes; }
    5153
    52     // Call this method when either the children of a DOM text element have changed, or the length of
    53     // the text in any child element has changed.
    54     void invalidateTextPositioningElements();
    55 
    56     void layoutAttributesChanged(RenderObject*);
    57     void layoutAttributesWillBeDestroyed(RenderSVGInlineText*, Vector<SVGTextLayoutAttributes*>& affectedAttributes);
    58     void rebuildLayoutAttributes(bool performFullRebuild = false);
    59     void rebuildLayoutAttributes(Vector<SVGTextLayoutAttributes*>& affectedAttributes);
    60 
    61     Vector<SVGTextLayoutAttributes*>& layoutAttributes() { return m_layoutAttributes; }
     54    void subtreeChildWasAdded(RenderObject*);
     55    void subtreeChildWillBeRemoved(RenderSVGInlineText*, Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes);
     56    void subtreeChildWasRemoved(const Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes);
     57    void subtreeStyleDidChange(RenderSVGInlineText*);
     58    void subtreeTextDidChange(RenderSVGInlineText*);
    6259
    6360private:
     
    8178    virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&, ApplyContainerFlipOrNot = ApplyContainerFlip, bool* wasFixed = 0) const;
    8279    virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
     80    virtual void removeChild(RenderObject*) OVERRIDE;
     81    virtual void willBeDestroyed() OVERRIDE;
    8382
    8483    virtual FloatRect objectBoundingBox() const { return frameRect(); }
     
    9190    virtual RenderBlock* firstLineBlock() const;
    9291    virtual void updateFirstLetter();
     92
     93    bool shouldHandleSubtreeMutations() const;
    9394
    9495    bool m_needsReordering : 1;
  • trunk/Source/WebCore/rendering/svg/SVGRenderSupport.cpp

    r115981 r117225  
    198198                    if (child->isSVGShape())
    199199                        toRenderSVGShape(child)->setNeedsShapeUpdate();
    200                     else if (child->isSVGText())
     200                    else if (child->isSVGText()) {
     201                        toRenderSVGText(child)->setNeedsTextMetricsUpdate();
    201202                        toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
     203                    }
    202204
    203205                    needsLayout = true;
  • trunk/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp

    r116801 r117225  
    7474    ASSERT(textRoot);
    7575
    76     textRoot->rebuildLayoutAttributes();
    7776    Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot->layoutAttributes();
    7877    if (layoutAttributes.isEmpty())
  • trunk/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp

    r116801 r117225  
    2727#include "SVGTextPositioningElement.h"
    2828
    29 // Set to a value > 0 to dump the text layout attributes
    30 #define DUMP_TEXT_LAYOUT_ATTRIBUTES 0
    31 
    3229namespace WebCore {
    3330
     
    4542        return;
    4643
    47     if (!buildLayoutAttributesIfNeeded(textRoot))
    48         return;
     44    if (m_textPositions.isEmpty()) {
     45        m_characterDataMap.clear();
     46
     47        m_textLength = 0;
     48        const UChar* lastCharacter = 0;
     49        collectTextPositioningElements(textRoot, lastCharacter);
     50
     51        if (!m_textLength)
     52            return;
     53
     54        buildCharacterDataMap(textRoot);
     55    }
    4956
    5057    m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, text, m_characterDataMap);
    5158}
    5259
    53 void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForWholeTree(RenderSVGText* textRoot)
     60bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesForForSubtree(RenderSVGText* textRoot)
    5461{
    5562    ASSERT(textRoot);
    5663
    57     if (!buildLayoutAttributesIfNeeded(textRoot))
    58         return;
    59 
    60     m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
    61 }
    62 
    63 void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
    64 {
    65     ASSERT(text);
    66     m_metricsBuilder.measureTextRenderer(text);
    67 }
    68 
    69 void SVGTextLayoutAttributesBuilder::rebuildMetricsForWholeTree(RenderSVGText* textRoot)
    70 {
    71     ASSERT(textRoot);
    72     Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot->layoutAttributes();
    73 
    74     size_t layoutAttributesSize = layoutAttributes.size();
    75     for (size_t i = 0; i < layoutAttributesSize; ++i)
    76         m_metricsBuilder.measureTextRenderer(layoutAttributes[i]->context());
    77 }
    78 
    79 bool SVGTextLayoutAttributesBuilder::buildLayoutAttributesIfNeeded(RenderSVGText* textRoot)
    80 {
    81     ASSERT(textRoot);
     64    m_characterDataMap.clear();
    8265
    8366    if (m_textPositions.isEmpty()) {
     
    8770    }
    8871
    89     m_characterDataMap.clear();
    9072    if (!m_textLength)
    9173        return false;
    9274
    93     buildLayoutAttributes(textRoot);
     75    buildCharacterDataMap(textRoot);
     76    m_metricsBuilder.buildMetricsAndLayoutAttributes(textRoot, 0, m_characterDataMap);
    9477    return true;
     78}
     79
     80void SVGTextLayoutAttributesBuilder::rebuildMetricsForTextRenderer(RenderSVGInlineText* text)
     81{
     82    ASSERT(text);
     83    m_metricsBuilder.measureTextRenderer(text);
    9584}
    9685
     
    144133}
    145134
    146 void SVGTextLayoutAttributesBuilder::buildLayoutAttributes(RenderSVGText* textRoot)
    147 {
    148     ASSERT(m_textLength);
    149 
     135void SVGTextLayoutAttributesBuilder::buildCharacterDataMap(RenderSVGText* textRoot)
     136{
    150137    SVGTextPositioningElement* outermostTextElement = SVGTextPositioningElement::elementFromRenderer(textRoot);
    151138    ASSERT(outermostTextElement);
     
    174161    for (unsigned i = 0; i < size; ++i)
    175162        fillCharacterDataMap(m_textPositions[i]);
    176 
    177 #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
    178     fprintf(stderr, "\nDumping ALL layout attributes for RenderSVGText, renderer=%p, node=%p (m_textLength: %i)\n", textRoot, textRoot->node(), m_textLength);
    179     m_characterDataMap.dump();
    180 #endif
    181163}
    182164
  • trunk/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h

    r105057 r117225  
    4444public:
    4545    SVGTextLayoutAttributesBuilder();
    46     void buildLayoutAttributesForWholeTree(RenderSVGText*);
     46    bool buildLayoutAttributesForForSubtree(RenderSVGText*);
    4747    void buildLayoutAttributesForTextRenderer(RenderSVGInlineText*);
    4848
    49     void rebuildMetricsForWholeTree(RenderSVGText*);
    5049    void rebuildMetricsForTextRenderer(RenderSVGInlineText*);
    5150
    5251    // Invoked whenever the underlying DOM tree changes, so that m_textPositions is rebuild.
    5352    void clearTextPositioningElements() { m_textPositions.clear(); }
     53    unsigned numberOfTextPositioningElements() const { return m_textPositions.size(); }
    5454
    5555private:
     
    6767    };
    6868
    69     bool buildLayoutAttributesIfNeeded(RenderSVGText*);
     69    void buildCharacterDataMap(RenderSVGText*);
    7070    void collectTextPositioningElements(RenderObject*, const UChar*& lastCharacter);
    71     void buildLayoutAttributes(RenderSVGText*);
    7271    void fillCharacterDataMap(const TextPosition&);
    7372
  • trunk/Source/WebCore/svg/SVGAElement.cpp

    r117195 r117225  
    239239}
    240240
    241 void SVGAElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    242 {
    243     SVGStyledTransformableElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    244 
    245     if (changedByParser || !renderer())
    246         return;
    247 
    248     // Invalidate the TextPosition cache in SVGTextLayoutAttributesBuilder as it may now point
    249     // to no-longer existing SVGTextPositioningElements and thus needs to be rebuilt.
    250     if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(renderer()))
    251         textRenderer->invalidateTextPositioningElements();
    252 }
    253 
    254241} // namespace WebCore
    255242
  • trunk/Source/WebCore/svg/SVGAElement.h

    r117195 r117225  
    4141    static PassRefPtr<SVGAElement> create(const QualifiedName&, Document*);
    4242
    43 protected:
    44     virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0) OVERRIDE;
    45 
    4643private:
    4744    SVGAElement(const QualifiedName&, Document*);
  • trunk/Source/WebCore/svg/SVGTextContentElement.cpp

    r117195 r117225  
    322322}
    323323
    324 void SVGTextContentElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    325 {
    326     SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    327 
    328     if (changedByParser || !renderer())
    329         return;
    330 
    331     // Invalidate the TextPosition cache in SVGTextLayoutAttributesBuilder as it may now point
    332     // to no-longer existing SVGTextPositioningElements and thus needs to be rebuilt.
    333     if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(renderer()))
    334         textRenderer->invalidateTextPositioningElements();
    335 }
    336 
    337324}
    338325
  • trunk/Source/WebCore/svg/SVGTextContentElement.h

    r117195 r117225  
    110110
    111111    virtual bool selfHasRelativeLengths() const;
    112     virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0);
    113112
    114113private:
Note: See TracChangeset for help on using the changeset viewer.