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