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