• 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/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 
InlineIterator()44     InlineIterator()
45         : m_root(0)
46         , m_obj(0)
47         , m_nextBreakablePosition(-1)
48         , m_pos(0)
49     {
50     }
51 
InlineIterator(RenderObject * root,RenderObject * o,unsigned p)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 
clear()60     void clear() { moveTo(0, 0); }
61 
moveToStartOf(RenderObject * object)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 
object()74     RenderObject* object() const { return m_obj; }
setObject(RenderObject * object)75     void setObject(RenderObject* object) { m_obj = object; }
76 
nextBreakablePosition()77     int nextBreakablePosition() const { return m_nextBreakablePosition; }
setNextBreakablePosition(int position)78     void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; }
79 
offset()80     unsigned offset() const { return m_pos; }
setOffset(unsigned position)81     void setOffset(unsigned position) { m_pos = position; }
root()82     RenderObject* root() const { return m_root; }
83 
84     void fastIncrementInTextNode();
85     void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode);
86     bool atEnd() const;
87 
atTextParagraphSeparator()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 
atParagraphSeparator()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 
embedCharFromDirection(TextDirection dir,EUnicodeBidi unicodeBidi)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>
notifyObserverEnteredObject(Observer * observer,RenderObject * object)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>
notifyObserverWillExitObject(Observer * observer,RenderObject * object)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 
isIteratorTarget(RenderObject * object)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 
isEmptyInline(RenderObject * object)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>
bidiNextSkippingEmptyInlines(RenderObject * root,RenderObject * current,Observer * 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.
bidiNextSkippingEmptyInlines(RenderObject * root,RenderObject * current)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.
bidiFirstIncludingEmptyInlines(RenderBlock * root)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 
fastIncrementInTextNode()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:
InlineWalker(RenderBlock * root)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 
root()347     RenderBlock* root() { return m_root; }
current()348     RenderObject* current() { return m_current; }
349 
atEndOfInline()350     bool atEndOfInline() { return m_atEndOfInline; }
atEnd()351     bool atEnd() const { return !m_current; }
352 
advance()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 
endOfLineHasIsolatedObjectAncestor(const InlineIterator & isolatedIterator,const InlineIterator & ancestorItertor)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 
increment(InlineBidiResolver * resolver,IncrementRule rule)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 
atEnd()400 inline bool InlineIterator::atEnd() const
401 {
402     return !m_obj;
403 }
404 
characterAt(unsigned index)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 
current()413 inline UChar InlineIterator::current() const
414 {
415     return characterAt(m_pos);
416 }
417 
previousInSameNode()418 inline UChar InlineIterator::previousInSameNode() const
419 {
420     if (!m_pos)
421         return 0;
422 
423     return characterAt(m_pos - 1);
424 }
425 
direction()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<>
increment()438 inline void InlineBidiResolver::increment()
439 {
440     m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer);
441 }
442 
443 template <>
isEndOfLine(const InlineIterator & end)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 
isCollapsibleSpace(UChar character,RenderText * renderer)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>
findFirstTrailingSpace(RenderText * lastText,const CharacterType * characters,int start,int stop)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 <>
findFirstTrailingSpaceAtRun(BidiRun * run)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 <>
addTrailingRun(BidiRunList<BidiRun> & runs,int start,int stop,BidiRun * run,BidiContext * context,TextDirection direction)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 <>
needsToApplyL1Rule(BidiRunList<BidiRun> & runs)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 
isIsolatedInline(RenderObject * object)516 static inline bool isIsolatedInline(RenderObject* object)
517 {
518     ASSERT(object);
519     return object->isRenderInline() && isIsolated(object->style()->unicodeBidi());
520 }
521 
highestContainingIsolateWithinRoot(RenderObject * object,RenderObject * root)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 
numberOfIsolateAncestors(const InlineIterator & iter)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.
addPlaceholderRunForIsolatedInline(InlineBidiResolver & resolver,RenderObject * obj,unsigned pos)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 
createRun(int start,int end,RenderObject * obj,InlineBidiResolver & resolver)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:
IsolateTracker(BidiRunList<BidiRun> & runs,unsigned nestedIsolateCount)574     explicit IsolateTracker(BidiRunList<BidiRun>& runs, unsigned nestedIsolateCount)
575         : m_nestedIsolateCount(nestedIsolateCount)
576         , m_haveAddedFakeRunForRootIsolate(false)
577         , m_runs(runs)
578     {
579     }
580 
setMidpointStateForRootIsolate(const LineMidpointState & midpointState)581     void setMidpointStateForRootIsolate(const LineMidpointState& midpointState)
582     {
583         m_midpointStateForRootIsolate = midpointState;
584     }
585 
enterIsolate()586     void enterIsolate() { m_nestedIsolateCount++; }
exitIsolate()587     void exitIsolate()
588     {
589         ASSERT(m_nestedIsolateCount >= 1);
590         m_nestedIsolateCount--;
591         if (!inIsolate())
592             m_haveAddedFakeRunForRootIsolate = false;
593     }
inIsolate()594     bool inIsolate() const { return m_nestedIsolateCount; }
595 
596     // We don't care if we encounter bidi directional overrides.
embed(WTF::Unicode::Direction,BidiEmbeddingSource)597     void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { }
commitExplicitEmbedding(BidiRunList<BidiRun> &)598     void commitExplicitEmbedding(BidiRunList<BidiRun>&) { }
runs()599     BidiRunList<BidiRun>& runs() { return m_runs; }
600 
addFakeRunIfNecessary(RenderObject * obj,unsigned pos,unsigned end,InlineBidiResolver & resolver)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 
appendRunObjectIfNecessary(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,AppendRunBehavior behavior,IsolateTracker & tracker)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 
adjustMidpointsAndAppendRunsForObjectIfNeeded(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,AppendRunBehavior behavior,IsolateTracker & tracker)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 
addFakeRunIfNecessary(RenderObject * obj,unsigned start,unsigned end,InlineBidiResolver & resolver,IsolateTracker & tracker)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 <>
appendRun(BidiRunList<BidiRun> & runs)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