Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved.
      4  * Copyright (C) 2010 Google Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  *
     21  */
     22 
     23 #ifndef InlineIterator_h
     24 #define InlineIterator_h
     25 
     26 #include "core/rendering/BidiRun.h"
     27 #include "core/rendering/RenderBlockFlow.h"
     28 #include "core/rendering/RenderText.h"
     29 #include "wtf/StdLibExtras.h"
     30 
     31 namespace WebCore {
     32 
     33 // This class is used to RenderInline subtrees, stepping by character within the
     34 // text children. InlineIterator will use bidiNext to find the next RenderText
     35 // optionally notifying a BidiResolver every time it steps into/out of a RenderInline.
     36 class InlineIterator {
     37 public:
     38     enum IncrementRule {
     39         FastIncrementInIsolatedRenderer,
     40         FastIncrementInTextNode
     41     };
     42 
     43     InlineIterator()
     44         : m_root(0)
     45         , m_obj(0)
     46         , m_pos(0)
     47         , m_nextBreakablePosition(-1)
     48     {
     49     }
     50 
     51     InlineIterator(RenderObject* root, RenderObject* o, unsigned p)
     52         : m_root(root)
     53         , m_obj(o)
     54         , m_pos(p)
     55         , m_nextBreakablePosition(-1)
     56     {
     57     }
     58 
     59     void clear() { moveTo(0, 0); }
     60 
     61     void moveToStartOf(RenderObject* object)
     62     {
     63         moveTo(object, 0);
     64     }
     65 
     66     void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1)
     67     {
     68         m_obj = object;
     69         m_pos = offset;
     70         m_nextBreakablePosition = nextBreak;
     71     }
     72 
     73     RenderObject* object() const { return m_obj; }
     74     void setObject(RenderObject* object) { m_obj = object; }
     75 
     76     unsigned offset() const { return m_pos; }
     77     RenderObject* root() const { return m_root; }
     78 
     79     void fastIncrementInTextNode();
     80     void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode);
     81     bool atEnd() const;
     82 
     83     inline bool atTextParagraphSeparator() const
     84     {
     85         return m_obj && m_obj->preservesNewline() && m_obj->isText() && toRenderText(m_obj)->textLength()
     86             && !toRenderText(m_obj)->isWordBreak() && toRenderText(m_obj)->characterAt(m_pos) == '\n';
     87     }
     88 
     89     inline bool atParagraphSeparator() const
     90     {
     91         return (m_obj && m_obj->isBR()) || atTextParagraphSeparator();
     92     }
     93 
     94     UChar characterAt(unsigned) const;
     95     UChar current() const;
     96     UChar previousInSameNode() const;
     97     ALWAYS_INLINE WTF::Unicode::Direction direction() const;
     98 
     99 private:
    100     RenderObject* m_root;
    101     RenderObject* m_obj;
    102 
    103 // FIXME: These should be private.
    104 public:
    105     unsigned m_pos;
    106     int m_nextBreakablePosition;
    107 };
    108 
    109 inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
    110 {
    111     return it1.m_pos == it2.m_pos && it1.object() == it2.object();
    112 }
    113 
    114 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
    115 {
    116     return it1.m_pos != it2.m_pos || it1.object() != it2.object();
    117 }
    118 
    119 static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi)
    120 {
    121     using namespace WTF::Unicode;
    122     if (unicodeBidi == Embed)
    123         return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding;
    124     return dir == RTL ? RightToLeftOverride : LeftToRightOverride;
    125 }
    126 
    127 template <class Observer>
    128 static inline void notifyObserverEnteredObject(Observer* observer, RenderObject* object)
    129 {
    130     if (!observer || !object || !object->isRenderInline())
    131         return;
    132 
    133     RenderStyle* style = object->style();
    134     EUnicodeBidi unicodeBidi = style->unicodeBidi();
    135     if (unicodeBidi == UBNormal) {
    136         // http://dev.w3.org/csswg/css3-writing-modes/#unicode-bidi
    137         // "The element does not open an additional level of embedding with respect to the bidirectional algorithm."
    138         // Thus we ignore any possible dir= attribute on the span.
    139         return;
    140     }
    141     if (isIsolated(unicodeBidi)) {
    142         // Make sure that explicit embeddings are committed before we enter the isolated content.
    143         observer->commitExplicitEmbedding();
    144         observer->enterIsolate();
    145         // Embedding/Override characters implied by dir= will be handled when
    146         // we process the isolated span, not when laying out the "parent" run.
    147         return;
    148     }
    149 
    150     if (!observer->inIsolate())
    151         observer->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM);
    152 }
    153 
    154 template <class Observer>
    155 static inline void notifyObserverWillExitObject(Observer* observer, RenderObject* object)
    156 {
    157     if (!observer || !object || !object->isRenderInline())
    158         return;
    159 
    160     EUnicodeBidi unicodeBidi = object->style()->unicodeBidi();
    161     if (unicodeBidi == UBNormal)
    162         return; // Nothing to do for unicode-bidi: normal
    163     if (isIsolated(unicodeBidi)) {
    164         observer->exitIsolate();
    165         return;
    166     }
    167 
    168     // Otherwise we pop any embed/override character we added when we opened this tag.
    169     if (!observer->inIsolate())
    170         observer->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM);
    171 }
    172 
    173 static inline bool isIteratorTarget(RenderObject* object)
    174 {
    175     ASSERT(object); // The iterator will of course return 0, but its not an expected argument to this function.
    176     return object->isText() || object->isFloating() || object->isOutOfFlowPositioned() || object->isReplaced();
    177 }
    178 
    179 // This enum is only used for bidiNextShared()
    180 enum EmptyInlineBehavior {
    181     SkipEmptyInlines,
    182     IncludeEmptyInlines,
    183 };
    184 
    185 static bool isEmptyInline(RenderObject* object)
    186 {
    187     if (!object->isRenderInline())
    188         return false;
    189 
    190     for (RenderObject* curr = object->firstChild(); curr; curr = curr->nextSibling()) {
    191         if (curr->isFloatingOrOutOfFlowPositioned())
    192             continue;
    193         if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace())
    194             continue;
    195 
    196         if (!isEmptyInline(curr))
    197             return false;
    198     }
    199     return true;
    200 }
    201 
    202 // FIXME: This function is misleadingly named. It has little to do with bidi.
    203 // This function will iterate over inlines within a block, optionally notifying
    204 // a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels).
    205 template <class Observer>
    206 static inline RenderObject* bidiNextShared(RenderObject* root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0)
    207 {
    208     RenderObject* next = 0;
    209     // oldEndOfInline denotes if when we last stopped iterating if we were at the end of an inline.
    210     bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
    211     bool endOfInline = false;
    212 
    213     while (current) {
    214         next = 0;
    215         if (!oldEndOfInline && !isIteratorTarget(current)) {
    216             next = current->firstChild();
    217             notifyObserverEnteredObject(observer, next);
    218         }
    219 
    220         // We hit this when either current has no children, or when current is not a renderer we care about.
    221         if (!next) {
    222             // If it is a renderer we care about, and we're doing our inline-walk, return it.
    223             if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && current->isRenderInline()) {
    224                 next = current;
    225                 endOfInline = true;
    226                 break;
    227             }
    228 
    229             while (current && current != root) {
    230                 notifyObserverWillExitObject(observer, current);
    231 
    232                 next = current->nextSibling();
    233                 if (next) {
    234                     notifyObserverEnteredObject(observer, next);
    235                     break;
    236                 }
    237 
    238                 current = current->parent();
    239                 if (emptyInlineBehavior == IncludeEmptyInlines && current && current != root && current->isRenderInline()) {
    240                     next = current;
    241                     endOfInline = true;
    242                     break;
    243                 }
    244             }
    245         }
    246 
    247         if (!next)
    248             break;
    249 
    250         if (isIteratorTarget(next)
    251             || ((emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(next)) // Always return EMPTY inlines.
    252                 && next->isRenderInline()))
    253             break;
    254         current = next;
    255     }
    256 
    257     if (endOfInlinePtr)
    258         *endOfInlinePtr = endOfInline;
    259 
    260     return next;
    261 }
    262 
    263 template <class Observer>
    264 static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current, Observer* observer)
    265 {
    266     // The SkipEmptyInlines callers never care about endOfInlinePtr.
    267     return bidiNextShared(root, current, observer, SkipEmptyInlines);
    268 }
    269 
    270 // This makes callers cleaner as they don't have to specify a type for the observer when not providing one.
    271 static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current)
    272 {
    273     InlineBidiResolver* observer = 0;
    274     return bidiNextSkippingEmptyInlines(root, current, observer);
    275 }
    276 
    277 static inline RenderObject* bidiNextIncludingEmptyInlines(RenderObject* root, RenderObject* current, bool* endOfInlinePtr = 0)
    278 {
    279     InlineBidiResolver* observer = 0; // Callers who include empty inlines, never use an observer.
    280     return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr);
    281 }
    282 
    283 static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderObject* root, InlineBidiResolver* resolver = 0)
    284 {
    285     RenderObject* o = root->firstChild();
    286     if (!o)
    287         return 0;
    288 
    289     if (o->isRenderInline()) {
    290         notifyObserverEnteredObject(resolver, o);
    291         if (!isEmptyInline(o))
    292             o = bidiNextSkippingEmptyInlines(root, o, resolver);
    293         else {
    294             // Never skip empty inlines.
    295             if (resolver)
    296                 resolver->commitExplicitEmbedding();
    297             return o;
    298         }
    299     }
    300 
    301     // FIXME: Unify this with the bidiNext call above.
    302     if (o && !isIteratorTarget(o))
    303         o = bidiNextSkippingEmptyInlines(root, o, resolver);
    304 
    305     if (resolver)
    306         resolver->commitExplicitEmbedding();
    307     return o;
    308 }
    309 
    310 // FIXME: This method needs to be renamed when bidiNext finds a good name.
    311 static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderObject* root)
    312 {
    313     RenderObject* o = root->firstChild();
    314     // If either there are no children to walk, or the first one is correct
    315     // then just return it.
    316     if (!o || o->isRenderInline() || isIteratorTarget(o))
    317         return o;
    318 
    319     return bidiNextIncludingEmptyInlines(root, o);
    320 }
    321 
    322 inline void InlineIterator::fastIncrementInTextNode()
    323 {
    324     ASSERT(m_obj);
    325     ASSERT(m_obj->isText());
    326     ASSERT(m_pos <= toRenderText(m_obj)->textLength());
    327     if (m_pos < INT_MAX)
    328         m_pos++;
    329 }
    330 
    331 // FIXME: This is used by RenderBlockFlow for simplified layout, and has nothing to do with bidi
    332 // it shouldn't use functions called bidiFirst and bidiNext.
    333 class InlineWalker {
    334 public:
    335     InlineWalker(RenderObject* root)
    336         : m_root(root)
    337         , m_current(0)
    338         , m_atEndOfInline(false)
    339     {
    340         // FIXME: This class should be taught how to do the SkipEmptyInlines codepath as well.
    341         m_current = bidiFirstIncludingEmptyInlines(m_root);
    342     }
    343 
    344     RenderObject* root() { return m_root; }
    345     RenderObject* current() { return m_current; }
    346 
    347     bool atEndOfInline() { return m_atEndOfInline; }
    348     bool atEnd() const { return !m_current; }
    349 
    350     RenderObject* advance()
    351     {
    352         // FIXME: Support SkipEmptyInlines and observer parameters.
    353         m_current = bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline);
    354         return m_current;
    355     }
    356 private:
    357     RenderObject* m_root;
    358     RenderObject* m_current;
    359     bool m_atEndOfInline;
    360 };
    361 
    362 static inline bool endOfLineHasIsolatedObjectAncestor(const InlineIterator& isolatedIterator, const InlineIterator& ancestorItertor)
    363 {
    364     if (!isolatedIterator.object() || !isIsolated(isolatedIterator.object()->style()->unicodeBidi()))
    365         return false;
    366 
    367     RenderObject* innerIsolatedObject = isolatedIterator.object();
    368     while (innerIsolatedObject && innerIsolatedObject != isolatedIterator.root()) {
    369         if (innerIsolatedObject == ancestorItertor.object())
    370             return true;
    371         innerIsolatedObject = innerIsolatedObject->parent();
    372     }
    373     return false;
    374 }
    375 
    376 inline void InlineIterator::increment(InlineBidiResolver* resolver, IncrementRule rule)
    377 {
    378     if (!m_obj)
    379         return;
    380 
    381     if (rule == FastIncrementInIsolatedRenderer
    382         && resolver && resolver->inIsolate()
    383         && !endOfLineHasIsolatedObjectAncestor(resolver->endOfLine(), resolver->position())) {
    384         moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0);
    385         return;
    386     }
    387 
    388     if (m_obj->isText()) {
    389         fastIncrementInTextNode();
    390         if (m_pos < toRenderText(m_obj)->textLength())
    391             return;
    392     }
    393     // bidiNext can return 0, so use moveTo instead of moveToStartOf
    394     moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0);
    395 }
    396 
    397 inline bool InlineIterator::atEnd() const
    398 {
    399     return !m_obj;
    400 }
    401 
    402 inline UChar InlineIterator::characterAt(unsigned index) const
    403 {
    404     if (!m_obj || !m_obj->isText())
    405         return 0;
    406 
    407     return toRenderText(m_obj)->characterAt(index);
    408 }
    409 
    410 inline UChar InlineIterator::current() const
    411 {
    412     return characterAt(m_pos);
    413 }
    414 
    415 inline UChar InlineIterator::previousInSameNode() const
    416 {
    417     if (!m_pos)
    418         return 0;
    419 
    420     return characterAt(m_pos - 1);
    421 }
    422 
    423 ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
    424 {
    425     if (UChar c = current())
    426         return WTF::Unicode::direction(c);
    427 
    428     if (m_obj && m_obj->isListMarker())
    429         return m_obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
    430 
    431     return WTF::Unicode::OtherNeutral;
    432 }
    433 
    434 template<>
    435 inline void InlineBidiResolver::increment()
    436 {
    437     m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer);
    438 }
    439 
    440 template <>
    441 inline bool InlineBidiResolver::isEndOfLine(const InlineIterator& end)
    442 {
    443     bool inEndOfLine = m_current == end || m_current.atEnd() || (inIsolate() && m_current.object() == end.object());
    444     if (inIsolate() && inEndOfLine) {
    445         m_current.moveTo(m_current.object(), end.m_pos, m_current.m_nextBreakablePosition);
    446         m_last = m_current;
    447         updateStatusLastFromCurrentDirection(WTF::Unicode::OtherNeutral);
    448     }
    449     return inEndOfLine;
    450 }
    451 
    452 static inline bool isIsolatedInline(RenderObject* object)
    453 {
    454     ASSERT(object);
    455     return object->isRenderInline() && isIsolated(object->style()->unicodeBidi());
    456 }
    457 
    458 static inline RenderObject* highestContainingIsolateWithinRoot(RenderObject* object, RenderObject* root)
    459 {
    460     ASSERT(object);
    461     RenderObject* containingIsolateObj = 0;
    462     while (object && object != root) {
    463         if (isIsolatedInline(object))
    464             containingIsolateObj = object;
    465 
    466         object = object->parent();
    467     }
    468     return containingIsolateObj;
    469 }
    470 
    471 static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter)
    472 {
    473     RenderObject* object = iter.object();
    474     if (!object)
    475         return 0;
    476     unsigned count = 0;
    477     while (object && object != iter.root()) {
    478         if (isIsolatedInline(object))
    479             count++;
    480         object = object->parent();
    481     }
    482     return count;
    483 }
    484 
    485 // FIXME: This belongs on InlineBidiResolver, except it's a template specialization
    486 // of BidiResolver which knows nothing about RenderObjects.
    487 static inline BidiRun* addPlaceholderRunForIsolatedInline(InlineBidiResolver& resolver, RenderObject* obj, unsigned pos)
    488 {
    489     ASSERT(obj);
    490     BidiRun* isolatedRun = new BidiRun(pos, pos, obj, resolver.context(), resolver.dir());
    491     resolver.runs().addRun(isolatedRun);
    492     // FIXME: isolatedRuns() could be a hash of object->run and then we could cheaply
    493     // ASSERT here that we didn't create multiple objects for the same inline.
    494     resolver.isolatedRuns().append(isolatedRun);
    495     return isolatedRun;
    496 }
    497 
    498 static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
    499 {
    500     return new BidiRun(start, end, obj, resolver.context(), resolver.dir());
    501 }
    502 
    503 enum AppendRunBehavior {
    504     AppendingFakeRun,
    505     AppendingRunsForObject
    506 };
    507 
    508 class IsolateTracker {
    509 public:
    510     explicit IsolateTracker(unsigned nestedIsolateCount)
    511         : m_nestedIsolateCount(nestedIsolateCount)
    512         , m_haveAddedFakeRunForRootIsolate(false)
    513     {
    514     }
    515 
    516     void setMidpointStateForRootIsolate(const LineMidpointState& midpointState)
    517     {
    518         m_midpointStateForRootIsolate = midpointState;
    519     }
    520 
    521     void enterIsolate() { m_nestedIsolateCount++; }
    522     void exitIsolate()
    523     {
    524         ASSERT(m_nestedIsolateCount >= 1);
    525         m_nestedIsolateCount--;
    526         if (!inIsolate())
    527             m_haveAddedFakeRunForRootIsolate = false;
    528     }
    529     bool inIsolate() const { return m_nestedIsolateCount; }
    530 
    531     // We don't care if we encounter bidi directional overrides.
    532     void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { }
    533     void commitExplicitEmbedding() { }
    534 
    535     void addFakeRunIfNecessary(RenderObject* obj, unsigned pos, unsigned end, InlineBidiResolver& resolver)
    536     {
    537         // We only need to add a fake run for a given isolated span once during each call to createBidiRunsForLine.
    538         // We'll be called for every span inside the isolated span so we just ignore subsequent calls.
    539         // We also avoid creating a fake run until we hit a child that warrants one, e.g. we skip floats.
    540         if (RenderBlockFlow::shouldSkipCreatingRunsForObject(obj))
    541             return;
    542         if (!m_haveAddedFakeRunForRootIsolate) {
    543             BidiRun* run = addPlaceholderRunForIsolatedInline(resolver, obj, pos);
    544             resolver.setMidpointStateForIsolatedRun(run, m_midpointStateForRootIsolate);
    545             m_haveAddedFakeRunForRootIsolate = true;
    546         }
    547         // obj and pos together denote a single position in the inline, from which the parsing of the isolate will start.
    548         // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the
    549         // isolate, when we call createBidiRunsForLine it will stop at whichever comes first.
    550     }
    551 
    552 private:
    553     unsigned m_nestedIsolateCount;
    554     bool m_haveAddedFakeRunForRootIsolate;
    555     LineMidpointState m_midpointStateForRootIsolate;
    556 };
    557 
    558 static void inline appendRunObjectIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker)
    559 {
    560     if (behavior == AppendingFakeRun)
    561         tracker.addFakeRunIfNecessary(obj, start, end, resolver);
    562     else
    563         resolver.runs().addRun(createRun(start, end, obj, resolver));
    564 }
    565 
    566 static void adjustMidpointsAndAppendRunsForObjectIfNeeded(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker)
    567 {
    568     if (start > end || RenderBlockFlow::shouldSkipCreatingRunsForObject(obj))
    569         return;
    570 
    571     LineMidpointState& lineMidpointState = resolver.midpointState();
    572     bool haveNextMidpoint = (lineMidpointState.currentMidpoint < lineMidpointState.numMidpoints);
    573     InlineIterator nextMidpoint;
    574     if (haveNextMidpoint)
    575         nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint];
    576     if (lineMidpointState.betweenMidpoints) {
    577         if (!(haveNextMidpoint && nextMidpoint.object() == obj))
    578             return;
    579         // This is a new start point. Stop ignoring objects and
    580         // adjust our start.
    581         lineMidpointState.betweenMidpoints = false;
    582         start = nextMidpoint.m_pos;
    583         lineMidpointState.currentMidpoint++;
    584         if (start < end)
    585             return adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, resolver, behavior, tracker);
    586     } else {
    587         if (!haveNextMidpoint || (obj != nextMidpoint.object())) {
    588             appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker);
    589             return;
    590         }
    591 
    592         // An end midpoint has been encountered within our object. We
    593         // need to go ahead and append a run with our endpoint.
    594         if (nextMidpoint.m_pos + 1 <= end) {
    595             lineMidpointState.betweenMidpoints = true;
    596             lineMidpointState.currentMidpoint++;
    597             if (nextMidpoint.m_pos != UINT_MAX) { // UINT_MAX means stop at the object and don't nclude any of it.
    598                 if (nextMidpoint.m_pos + 1 > start)
    599                     appendRunObjectIfNecessary(obj, start, nextMidpoint.m_pos + 1, resolver, behavior, tracker);
    600                 return adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, nextMidpoint.m_pos + 1, end, resolver, behavior, tracker);
    601             }
    602         } else {
    603             appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker);
    604         }
    605     }
    606 }
    607 
    608 static inline void addFakeRunIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, IsolateTracker& tracker)
    609 {
    610     tracker.setMidpointStateForRootIsolate(resolver.midpointState());
    611     adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), resolver, AppendingFakeRun, tracker);
    612 }
    613 
    614 template <>
    615 inline void InlineBidiResolver::appendRun()
    616 {
    617     if (!m_emptyRun && !m_eor.atEnd() && !m_reachedEndOfLine) {
    618         // Keep track of when we enter/leave "unicode-bidi: isolate" inlines.
    619         // Initialize our state depending on if we're starting in the middle of such an inline.
    620         // FIXME: Could this initialize from this->inIsolate() instead of walking up the render tree?
    621         IsolateTracker isolateTracker(numberOfIsolateAncestors(m_sor));
    622         int start = m_sor.m_pos;
    623         RenderObject* obj = m_sor.object();
    624         while (obj && obj != m_eor.object() && obj != m_endOfRunAtEndOfLine.object()) {
    625             if (isolateTracker.inIsolate())
    626                 addFakeRunIfNecessary(obj, start, obj->length(), *this, isolateTracker);
    627             else
    628                 adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), *this, AppendingRunsForObject, isolateTracker);
    629             // FIXME: start/obj should be an InlineIterator instead of two separate variables.
    630             start = 0;
    631             obj = bidiNextSkippingEmptyInlines(m_sor.root(), obj, &isolateTracker);
    632         }
    633         bool isEndOfLine = obj == m_endOfLine.object() && !m_endOfLine.m_pos;
    634         if (obj && !isEndOfLine) {
    635             unsigned pos = obj == m_eor.object() ? m_eor.m_pos : INT_MAX;
    636             if (obj == m_endOfRunAtEndOfLine.object() && m_endOfRunAtEndOfLine.m_pos <= pos) {
    637                 m_reachedEndOfLine = true;
    638                 pos = m_endOfRunAtEndOfLine.m_pos;
    639             }
    640             // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
    641             int end = obj->length() ? pos + 1 : 0;
    642             if (isolateTracker.inIsolate())
    643                 addFakeRunIfNecessary(obj, start, end, *this, isolateTracker);
    644             else
    645                 adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, *this, AppendingRunsForObject, isolateTracker);
    646         }
    647 
    648         if (isEndOfLine)
    649             m_reachedEndOfLine = true;
    650         // If isolateTrack is inIsolate, the next |start of run| can not be the current isolated renderer.
    651         if (isolateTracker.inIsolate())
    652             m_eor.moveTo(bidiNextSkippingEmptyInlines(m_eor.root(), m_eor.object()), 0);
    653         else
    654             m_eor.increment();
    655         m_sor = m_eor;
    656     }
    657 
    658     m_direction = WTF::Unicode::OtherNeutral;
    659     m_status.eor = WTF::Unicode::OtherNeutral;
    660 }
    661 
    662 }
    663 
    664 #endif // InlineIterator_h
    665