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/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 44 InlineIterator() 45 : m_root(0) 46 , m_obj(0) 47 , m_nextBreakablePosition(-1) 48 , m_pos(0) 49 { 50 } 51 52 InlineIterator(RenderObject* root, RenderObject* o, unsigned p) 53 : m_root(root) 54 , m_obj(o) 55 , m_nextBreakablePosition(-1) 56 , m_pos(p) 57 { 58 } 59 60 void clear() { moveTo(0, 0); } 61 62 void moveToStartOf(RenderObject* object) 63 { 64 moveTo(object, 0); 65 } 66 67 void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1) 68 { 69 m_obj = object; 70 m_pos = offset; 71 m_nextBreakablePosition = nextBreak; 72 } 73 74 RenderObject* object() const { return m_obj; } 75 void setObject(RenderObject* object) { m_obj = object; } 76 77 int nextBreakablePosition() const { return m_nextBreakablePosition; } 78 void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; } 79 80 unsigned offset() const { return m_pos; } 81 void setOffset(unsigned position) { m_pos = position; } 82 RenderObject* root() const { return m_root; } 83 84 void fastIncrementInTextNode(); 85 void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode); 86 bool atEnd() const; 87 88 inline bool atTextParagraphSeparator() const 89 { 90 return m_obj && m_obj->preservesNewline() && m_obj->isText() && toRenderText(m_obj)->textLength() 91 && !toRenderText(m_obj)->isWordBreak() && toRenderText(m_obj)->characterAt(m_pos) == '\n'; 92 } 93 94 inline bool atParagraphSeparator() const 95 { 96 return (m_obj && m_obj->isBR()) || atTextParagraphSeparator(); 97 } 98 99 UChar characterAt(unsigned) const; 100 UChar current() const; 101 UChar previousInSameNode() const; 102 ALWAYS_INLINE WTF::Unicode::Direction direction() const; 103 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 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> 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> 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 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 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> 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. 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. 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 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: 338 InlineWalker(RenderBlock* root) 339 : m_root(root) 340 , m_current(0) 341 , m_atEndOfInline(false) 342 { 343 // FIXME: This class should be taught how to do the SkipEmptyInlines codepath as well. 344 m_current = bidiFirstIncludingEmptyInlines(m_root); 345 } 346 347 RenderBlock* root() { return m_root; } 348 RenderObject* current() { return m_current; } 349 350 bool atEndOfInline() { return m_atEndOfInline; } 351 bool atEnd() const { return !m_current; } 352 353 RenderObject* advance() 354 { 355 // FIXME: Support SkipEmptyInlines and observer parameters. 356 m_current = bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline); 357 return m_current; 358 } 359 private: 360 RenderBlock* m_root; 361 RenderObject* m_current; 362 bool m_atEndOfInline; 363 }; 364 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 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 400 inline bool InlineIterator::atEnd() const 401 { 402 return !m_obj; 403 } 404 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 413 inline UChar InlineIterator::current() const 414 { 415 return characterAt(m_pos); 416 } 417 418 inline UChar InlineIterator::previousInSameNode() const 419 { 420 if (!m_pos) 421 return 0; 422 423 return characterAt(m_pos - 1); 424 } 425 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<> 438 inline void InlineBidiResolver::increment() 439 { 440 m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer); 441 } 442 443 template <> 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 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> 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 <> 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 <> 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 <> 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 516 static inline bool isIsolatedInline(RenderObject* object) 517 { 518 ASSERT(object); 519 return object->isRenderInline() && isIsolated(object->style()->unicodeBidi()); 520 } 521 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 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. 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 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: 574 explicit IsolateTracker(BidiRunList<BidiRun>& runs, unsigned nestedIsolateCount) 575 : m_nestedIsolateCount(nestedIsolateCount) 576 , m_haveAddedFakeRunForRootIsolate(false) 577 , m_runs(runs) 578 { 579 } 580 581 void setMidpointStateForRootIsolate(const LineMidpointState& midpointState) 582 { 583 m_midpointStateForRootIsolate = midpointState; 584 } 585 586 void enterIsolate() { m_nestedIsolateCount++; } 587 void exitIsolate() 588 { 589 ASSERT(m_nestedIsolateCount >= 1); 590 m_nestedIsolateCount--; 591 if (!inIsolate()) 592 m_haveAddedFakeRunForRootIsolate = false; 593 } 594 bool inIsolate() const { return m_nestedIsolateCount; } 595 596 // We don't care if we encounter bidi directional overrides. 597 void embed(WTF::Unicode::Direction, BidiEmbeddingSource) { } 598 void commitExplicitEmbedding(BidiRunList<BidiRun>&) { } 599 BidiRunList<BidiRun>& runs() { return m_runs; } 600 601 void addFakeRunIfNecessary(RenderObject* obj, unsigned pos, unsigned end, InlineBidiResolver& resolver) 602 { 603 // We only need to add a fake run for a given isolated span once during each call to createBidiRunsForLine. 604 // We'll be called for every span inside the isolated span so we just ignore subsequent calls. 605 // We also avoid creating a fake run until we hit a child that warrants one, e.g. we skip floats. 606 if (RenderBlockFlow::shouldSkipCreatingRunsForObject(obj)) 607 return; 608 if (!m_haveAddedFakeRunForRootIsolate) { 609 BidiRun* run = addPlaceholderRunForIsolatedInline(resolver, obj, pos); 610 resolver.setMidpointStateForIsolatedRun(run, m_midpointStateForRootIsolate); 611 m_haveAddedFakeRunForRootIsolate = true; 612 } 613 // obj and pos together denote a single position in the inline, from which the parsing of the isolate will start. 614 // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the 615 // isolate, when we call createBidiRunsForLine it will stop at whichever comes first. 616 } 617 618 private: 619 unsigned m_nestedIsolateCount; 620 bool m_haveAddedFakeRunForRootIsolate; 621 LineMidpointState m_midpointStateForRootIsolate; 622 BidiRunList<BidiRun>& m_runs; 623 }; 624 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 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 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 <> 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