• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
InlineIterator()43     InlineIterator()
44         : m_root(0)
45         , m_obj(0)
46         , m_pos(0)
47         , m_nextBreakablePosition(-1)
48     {
49     }
50 
InlineIterator(RenderObject * root,RenderObject * o,unsigned p)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 
clear()59     void clear() { moveTo(0, 0); }
60 
moveToStartOf(RenderObject * object)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 
object()73     RenderObject* object() const { return m_obj; }
setObject(RenderObject * object)74     void setObject(RenderObject* object) { m_obj = object; }
75 
offset()76     unsigned offset() const { return m_pos; }
root()77     RenderObject* root() const { return m_root; }
78 
79     void fastIncrementInTextNode();
80     void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode);
81     bool atEnd() const;
82 
atTextParagraphSeparator()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 
atParagraphSeparator()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 
embedCharFromDirection(TextDirection dir,EUnicodeBidi unicodeBidi)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>
notifyObserverEnteredObject(Observer * observer,RenderObject * object)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>
notifyObserverWillExitObject(Observer * observer,RenderObject * object)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 
isIteratorTarget(RenderObject * object)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 
isEmptyInline(RenderObject * object)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>
bidiNextSkippingEmptyInlines(RenderObject * root,RenderObject * current,Observer * 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.
bidiNextSkippingEmptyInlines(RenderObject * root,RenderObject * current)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.
bidiFirstIncludingEmptyInlines(RenderObject * root)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 
fastIncrementInTextNode()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:
InlineWalker(RenderObject * root)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 
root()344     RenderObject* root() { return m_root; }
current()345     RenderObject* current() { return m_current; }
346 
atEndOfInline()347     bool atEndOfInline() { return m_atEndOfInline; }
atEnd()348     bool atEnd() const { return !m_current; }
349 
advance()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 
endOfLineHasIsolatedObjectAncestor(const InlineIterator & isolatedIterator,const InlineIterator & ancestorItertor)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 
increment(InlineBidiResolver * resolver,IncrementRule rule)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 
atEnd()397 inline bool InlineIterator::atEnd() const
398 {
399     return !m_obj;
400 }
401 
characterAt(unsigned index)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 
current()410 inline UChar InlineIterator::current() const
411 {
412     return characterAt(m_pos);
413 }
414 
previousInSameNode()415 inline UChar InlineIterator::previousInSameNode() const
416 {
417     if (!m_pos)
418         return 0;
419 
420     return characterAt(m_pos - 1);
421 }
422 
direction()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<>
increment()435 inline void InlineBidiResolver::increment()
436 {
437     m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer);
438 }
439 
440 template <>
isEndOfLine(const InlineIterator & end)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 
isIsolatedInline(RenderObject * object)452 static inline bool isIsolatedInline(RenderObject* object)
453 {
454     ASSERT(object);
455     return object->isRenderInline() && isIsolated(object->style()->unicodeBidi());
456 }
457 
highestContainingIsolateWithinRoot(RenderObject * object,RenderObject * root)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 
numberOfIsolateAncestors(const InlineIterator & iter)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.
addPlaceholderRunForIsolatedInline(InlineBidiResolver & resolver,RenderObject * obj,unsigned pos)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 
createRun(int start,int end,RenderObject * obj,InlineBidiResolver & resolver)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:
IsolateTracker(unsigned nestedIsolateCount)510     explicit IsolateTracker(unsigned nestedIsolateCount)
511         : m_nestedIsolateCount(nestedIsolateCount)
512         , m_haveAddedFakeRunForRootIsolate(false)
513     {
514     }
515 
setMidpointStateForRootIsolate(const LineMidpointState & midpointState)516     void setMidpointStateForRootIsolate(const LineMidpointState& midpointState)
517     {
518         m_midpointStateForRootIsolate = midpointState;
519     }
520 
enterIsolate()521     void enterIsolate() { m_nestedIsolateCount++; }
exitIsolate()522     void exitIsolate()
523     {
524         ASSERT(m_nestedIsolateCount >= 1);
525         m_nestedIsolateCount--;
526         if (!inIsolate())
527             m_haveAddedFakeRunForRootIsolate = false;
528     }
inIsolate()529     bool inIsolate() const { return m_nestedIsolateCount; }
530 
531     // We don't care if we encounter bidi directional overrides.
embed(WTF::Unicode::Direction,BidiEmbeddingSource)532     void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { }
commitExplicitEmbedding()533     void commitExplicitEmbedding() { }
534 
addFakeRunIfNecessary(RenderObject * obj,unsigned pos,unsigned end,InlineBidiResolver & resolver)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 
appendRunObjectIfNecessary(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,AppendRunBehavior behavior,IsolateTracker & tracker)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 
adjustMidpointsAndAppendRunsForObjectIfNeeded(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,AppendRunBehavior behavior,IsolateTracker & tracker)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 
addFakeRunIfNecessary(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,IsolateTracker & tracker)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 <>
appendRun()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