1 /* 2 * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org) 3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. 4 * Copyright (C) 2010 Google Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #ifndef InlineIterator_h 24 #define InlineIterator_h 25 26 #include "core/rendering/BidiRun.h" 27 #include "core/rendering/RenderBlockFlow.h" 28 #include "core/rendering/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 43 InlineIterator() 44 : m_root(0) 45 , m_obj(0) 46 , m_pos(0) 47 , m_nextBreakablePosition(-1) 48 { 49 } 50 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 59 void clear() { moveTo(0, 0); } 60 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 73 RenderObject* object() const { return m_obj; } 74 void setObject(RenderObject* object) { m_obj = object; } 75 76 unsigned offset() const { return m_pos; } 77 RenderObject* root() const { return m_root; } 78 79 void fastIncrementInTextNode(); 80 void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode); 81 bool atEnd() const; 82 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 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 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> 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> 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 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 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> 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. 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. 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 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: 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 344 RenderObject* root() { return m_root; } 345 RenderObject* current() { return m_current; } 346 347 bool atEndOfInline() { return m_atEndOfInline; } 348 bool atEnd() const { return !m_current; } 349 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 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 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 397 inline bool InlineIterator::atEnd() const 398 { 399 return !m_obj; 400 } 401 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 410 inline UChar InlineIterator::current() const 411 { 412 return characterAt(m_pos); 413 } 414 415 inline UChar InlineIterator::previousInSameNode() const 416 { 417 if (!m_pos) 418 return 0; 419 420 return characterAt(m_pos - 1); 421 } 422 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<> 435 inline void InlineBidiResolver::increment() 436 { 437 m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer); 438 } 439 440 template <> 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 452 static inline bool isIsolatedInline(RenderObject* object) 453 { 454 ASSERT(object); 455 return object->isRenderInline() && isIsolated(object->style()->unicodeBidi()); 456 } 457 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 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. 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 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: 510 explicit IsolateTracker(unsigned nestedIsolateCount) 511 : m_nestedIsolateCount(nestedIsolateCount) 512 , m_haveAddedFakeRunForRootIsolate(false) 513 { 514 } 515 516 void setMidpointStateForRootIsolate(const LineMidpointState& midpointState) 517 { 518 m_midpointStateForRootIsolate = midpointState; 519 } 520 521 void enterIsolate() { m_nestedIsolateCount++; } 522 void exitIsolate() 523 { 524 ASSERT(m_nestedIsolateCount >= 1); 525 m_nestedIsolateCount--; 526 if (!inIsolate()) 527 m_haveAddedFakeRunForRootIsolate = false; 528 } 529 bool inIsolate() const { return m_nestedIsolateCount; } 530 531 // We don't care if we encounter bidi directional overrides. 532 void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { } 533 void commitExplicitEmbedding() { } 534 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 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 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 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 <> 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