1/*
2 * Copyright (C) 2000 Lars Knoll (knoll@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
32namespace 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.
37class InlineIterator {
38public:
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
104private:
105    RenderObject* m_root;
106    RenderObject* m_obj;
107
108    int m_nextBreakablePosition;
109    unsigned m_pos;
110};
111
112inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
113{
114    return it1.offset() == it2.offset() && it1.object() == it2.object();
115}
116
117inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
118{
119    return it1.offset() != it2.offset() || it1.object() != it2.object();
120}
121
122static 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
130template <class Observer>
131static 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
157template <class Observer>
158static 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
176static 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()
183enum EmptyInlineBehavior {
184    SkipEmptyInlines,
185    IncludeEmptyInlines,
186};
187
188static 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).
208template <class Observer>
209static 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
266template <class Observer>
267static 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.
274static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current)
275{
276    InlineBidiResolver* observer = 0;
277    return bidiNextSkippingEmptyInlines(root, current, observer);
278}
279
280static 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
286static 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.
314static 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
325inline 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.
336class InlineWalker {
337public:
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    }
359private:
360    RenderBlock* m_root;
361    RenderObject* m_current;
362    bool m_atEndOfInline;
363};
364
365static 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
379inline 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
400inline bool InlineIterator::atEnd() const
401{
402    return !m_obj;
403}
404
405inline 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
413inline UChar InlineIterator::current() const
414{
415    return characterAt(m_pos);
416}
417
418inline UChar InlineIterator::previousInSameNode() const
419{
420    if (!m_pos)
421        return 0;
422
423    return characterAt(m_pos - 1);
424}
425
426ALWAYS_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
437template<>
438inline void InlineBidiResolver::increment()
439{
440    m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer);
441}
442
443template <>
444inline 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
455static 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
464template <typename CharacterType>
465static 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
478template <>
479inline 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
495template <>
496inline 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
507template <>
508inline 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
516static inline bool isIsolatedInline(RenderObject* object)
517{
518    ASSERT(object);
519    return object->isRenderInline() && isIsolated(object->style()->unicodeBidi());
520}
521
522static 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
535static 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.
551static 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
562static 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
567enum AppendRunBehavior {
568    AppendingFakeRun,
569    AppendingRunsForObject
570};
571
572class IsolateTracker {
573public:
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
618private:
619    unsigned m_nestedIsolateCount;
620    bool m_haveAddedFakeRunForRootIsolate;
621    LineMidpointState m_midpointStateForRootIsolate;
622    BidiRunList<BidiRun>& m_runs;
623};
624
625static 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
633static 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
675static 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
681template <>
682inline 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