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.
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.
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.
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.
23 #ifndef InlineIterator_h
24 #define InlineIterator_h
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"
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 {
40 FastIncrementInIsolatedRenderer,
41 FastIncrementInTextNode
47 , m_nextBreakablePosition(-1)
52 InlineIterator(RenderObject* root, RenderObject* o, unsigned p)
55 , m_nextBreakablePosition(-1)
60 void clear() { moveTo(0, 0); }
62 void moveToStartOf(RenderObject* object)
67 void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1)
71 m_nextBreakablePosition = nextBreak;
74 RenderObject* object() const { return m_obj; }
75 void setObject(RenderObject* object) { m_obj = object; }
77 int nextBreakablePosition() const { return m_nextBreakablePosition; }
78 void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; }
80 unsigned offset() const { return m_pos; }
81 void setOffset(unsigned position) { m_pos = position; }
82 RenderObject* root() const { return m_root; }
84 void fastIncrementInTextNode();
85 void increment(InlineBidiResolver* = 0, IncrementRule = FastIncrementInTextNode);
88 inline bool atTextParagraphSeparator() const
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';
94 inline bool atParagraphSeparator() const
96 return (m_obj && m_obj->isBR()) || atTextParagraphSeparator();
99 UChar characterAt(unsigned) const;
100 UChar current() const;
101 UChar previousInSameNode() const;
102 ALWAYS_INLINE WTF::Unicode::Direction direction() const;
105 RenderObject* m_root;
108 int m_nextBreakablePosition;
112 inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
114 return it1.offset() == it2.offset() && it1.object() == it2.object();
117 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
119 return it1.offset() != it2.offset() || it1.object() != it2.object();
122 static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi)
124 using namespace WTF::Unicode;
125 if (unicodeBidi == Embed)
126 return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding;
127 return dir == RTL ? RightToLeftOverride : LeftToRightOverride;
130 template <class Observer>
131 static inline void notifyObserverEnteredObject(Observer* observer, RenderObject* object)
133 if (!observer || !object || !object->isRenderInline())
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.
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.
153 if (!observer->inIsolate())
154 observer->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM);
157 template <class Observer>
158 static inline void notifyObserverWillExitObject(Observer* observer, RenderObject* object)
160 if (!observer || !object || !object->isRenderInline())
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();
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);
176 static inline bool isIteratorTarget(RenderObject* object)
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();
182 // This enum is only used for bidiNextShared()
183 enum EmptyInlineBehavior {
188 static bool isEmptyInline(RenderObject* object)
190 if (!object->isRenderInline())
193 for (RenderObject* curr = toRenderInline(object)->firstChild(); curr; curr = curr->nextSibling()) {
194 if (curr->isFloatingOrOutOfFlowPositioned())
196 if (curr->isText() && toRenderText(curr)->isAllCollapsibleWhitespace())
199 if (!isEmptyInline(curr))
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)
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;
218 if (!oldEndOfInline && !isIteratorTarget(current)) {
219 next = current->slowFirstChild();
220 notifyObserverEnteredObject(observer, next);
223 // We hit this when either current has no children, or when current is not a renderer we care about.
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()) {
232 while (current && current != root) {
233 notifyObserverWillExitObject(observer, current);
235 next = current->nextSibling();
237 notifyObserverEnteredObject(observer, next);
241 current = current->parent();
242 if (emptyInlineBehavior == IncludeEmptyInlines && current && current != root && current->isRenderInline()) {
253 if (isIteratorTarget(next)
254 || ((emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(next)) // Always return EMPTY inlines.
255 && next->isRenderInline()))
261 *endOfInlinePtr = endOfInline;
266 template <class Observer>
267 static inline RenderObject* bidiNextSkippingEmptyInlines(RenderObject* root, RenderObject* current, Observer* observer)
269 // The SkipEmptyInlines callers never care about endOfInlinePtr.
270 return bidiNextShared(root, current, observer, SkipEmptyInlines);
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)
276 InlineBidiResolver* observer = 0;
277 return bidiNextSkippingEmptyInlines(root, current, observer);
280 static inline RenderObject* bidiNextIncludingEmptyInlines(RenderObject* root, RenderObject* current, bool* endOfInlinePtr = 0)
282 InlineBidiResolver* observer = 0; // Callers who include empty inlines, never use an observer.
283 return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr);
286 static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderBlockFlow* root, BidiRunList<BidiRun>& runs, InlineBidiResolver* resolver = 0)
288 RenderObject* o = root->firstChild();
292 if (o->isRenderInline()) {
293 notifyObserverEnteredObject(resolver, o);
294 if (!isEmptyInline(o))
295 o = bidiNextSkippingEmptyInlines(root, o, resolver);
297 // Never skip empty inlines.
299 resolver->commitExplicitEmbedding(runs);
304 // FIXME: Unify this with the bidiNext call above.
305 if (o && !isIteratorTarget(o))
306 o = bidiNextSkippingEmptyInlines(root, o, resolver);
309 resolver->commitExplicitEmbedding(runs);
313 // FIXME: This method needs to be renamed when bidiNext finds a good name.
314 static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderBlock* root)
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))
322 return bidiNextIncludingEmptyInlines(root, o);
325 inline void InlineIterator::fastIncrementInTextNode()
328 ASSERT(m_obj->isText());
329 ASSERT(m_pos <= toRenderText(m_obj)->textLength());
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.
338 InlineWalker(RenderBlock* root)
341 , m_atEndOfInline(false)
343 // FIXME: This class should be taught how to do the SkipEmptyInlines codepath as well.
344 m_current = bidiFirstIncludingEmptyInlines(m_root);
347 RenderBlock* root() { return m_root; }
348 RenderObject* current() { return m_current; }
350 bool atEndOfInline() { return m_atEndOfInline; }
351 bool atEnd() const { return !m_current; }
353 RenderObject* advance()
355 // FIXME: Support SkipEmptyInlines and observer parameters.
356 m_current = bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline);
361 RenderObject* m_current;
362 bool m_atEndOfInline;
365 static inline bool endOfLineHasIsolatedObjectAncestor(const InlineIterator& isolatedIterator, const InlineIterator& ancestorItertor)
367 if (!isolatedIterator.object() || !isIsolated(isolatedIterator.object()->style()->unicodeBidi()))
370 RenderObject* innerIsolatedObject = isolatedIterator.object();
371 while (innerIsolatedObject && innerIsolatedObject != isolatedIterator.root()) {
372 if (innerIsolatedObject == ancestorItertor.object())
374 innerIsolatedObject = innerIsolatedObject->parent();
379 inline void InlineIterator::increment(InlineBidiResolver* resolver, IncrementRule rule)
384 if (rule == FastIncrementInIsolatedRenderer
385 && resolver && resolver->inIsolate()
386 && !endOfLineHasIsolatedObjectAncestor(resolver->endOfLine(), resolver->position())) {
387 moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0);
391 if (m_obj->isText()) {
392 fastIncrementInTextNode();
393 if (m_pos < toRenderText(m_obj)->textLength())
396 // bidiNext can return 0, so use moveTo instead of moveToStartOf
397 moveTo(bidiNextSkippingEmptyInlines(m_root, m_obj, resolver), 0);
400 inline bool InlineIterator::atEnd() const
405 inline UChar InlineIterator::characterAt(unsigned index) const
407 if (!m_obj || !m_obj->isText())
410 return toRenderText(m_obj)->characterAt(index);
413 inline UChar InlineIterator::current() const
415 return characterAt(m_pos);
418 inline UChar InlineIterator::previousInSameNode() const
423 return characterAt(m_pos - 1);
426 ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
428 if (UChar c = current())
429 return WTF::Unicode::direction(c);
431 if (m_obj && m_obj->isListMarker())
432 return m_obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
434 return WTF::Unicode::OtherNeutral;
438 inline void InlineBidiResolver::increment()
440 m_current.increment(this, InlineIterator::FastIncrementInIsolatedRenderer);
444 inline bool InlineBidiResolver::isEndOfLine(const InlineIterator& end)
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());
450 updateStatusLastFromCurrentDirection(WTF::Unicode::OtherNeutral);
455 static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
457 if (character == ' ' || character == '\t' || character == softHyphen)
459 if (character == '\n')
460 return !renderer->style()->preserveNewline();
464 template <typename CharacterType>
465 static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterType* characters, int start, int stop)
467 int firstSpace = stop;
468 while (firstSpace > start) {
469 UChar current = characters[firstSpace - 1];
470 if (!isCollapsibleSpace(current, lastText))
479 inline int InlineBidiResolver::findFirstTrailingSpaceAtRun(BidiRun* run)
482 RenderObject* lastObject = run->m_object;
483 if (!lastObject->isText())
486 RenderText* lastText = toRenderText(lastObject);
488 if (lastText->is8Bit())
489 firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), run->start(), run->stop());
491 firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), run->start(), run->stop());
496 inline BidiRun* InlineBidiResolver::addTrailingRun(BidiRunList<BidiRun>& runs, int start, int stop, BidiRun* run, BidiContext* context, TextDirection direction) const
498 BidiRun* newTrailingRun = new BidiRun(start, stop, run->m_object, context, WTF::Unicode::OtherNeutral);
499 if (direction == LTR)
500 runs.addRun(newTrailingRun);
502 runs.prependRun(newTrailingRun);
504 return newTrailingRun;
508 inline bool InlineBidiResolver::needsToApplyL1Rule(BidiRunList<BidiRun>& runs)
510 if (!runs.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()
511 || !runs.logicallyLastRun()->m_object->style()->autoWrap())
516 static inline bool isIsolatedInline(RenderObject* object)
519 return object->isRenderInline() && isIsolated(object->style()->unicodeBidi());
522 static inline RenderObject* highestContainingIsolateWithinRoot(RenderObject* object, RenderObject* root)
525 RenderObject* containingIsolateObj = 0;
526 while (object && object != root) {
527 if (isIsolatedInline(object))
528 containingIsolateObj = object;
530 object = object->parent();
532 return containingIsolateObj;
535 static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter)
537 RenderObject* object = iter.object();
541 while (object && object != iter.root()) {
542 if (isIsolatedInline(object))
544 object = object->parent();
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)
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);
562 static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
564 return new BidiRun(start, end, obj, resolver.context(), resolver.dir());
567 enum AppendRunBehavior {
569 AppendingRunsForObject
572 class IsolateTracker {
574 explicit IsolateTracker(BidiRunList<BidiRun>& runs, unsigned nestedIsolateCount)
575 : m_nestedIsolateCount(nestedIsolateCount)
576 , m_haveAddedFakeRunForRootIsolate(false)
581 void setMidpointStateForRootIsolate(const LineMidpointState& midpointState)
583 m_midpointStateForRootIsolate = midpointState;
586 void enterIsolate() { m_nestedIsolateCount++; }
589 ASSERT(m_nestedIsolateCount >= 1);
590 m_nestedIsolateCount--;
592 m_haveAddedFakeRunForRootIsolate = false;
594 bool inIsolate() const { return m_nestedIsolateCount; }
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; }
601 void addFakeRunIfNecessary(RenderObject* obj, unsigned pos, unsigned end, InlineBidiResolver& resolver)
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))
608 if (!m_haveAddedFakeRunForRootIsolate) {
609 BidiRun* run = addPlaceholderRunForIsolatedInline(resolver, obj, pos);
610 resolver.setMidpointStateForIsolatedRun(run, m_midpointStateForRootIsolate);
611 m_haveAddedFakeRunForRootIsolate = true;
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.
619 unsigned m_nestedIsolateCount;
620 bool m_haveAddedFakeRunForRootIsolate;
621 LineMidpointState m_midpointStateForRootIsolate;
622 BidiRunList<BidiRun>& m_runs;
625 static void inline appendRunObjectIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker)
627 if (behavior == AppendingFakeRun)
628 tracker.addFakeRunIfNecessary(obj, start, end, resolver);
630 resolver.runs().addRun(createRun(start, end, obj, resolver));
633 static void adjustMidpointsAndAppendRunsForObjectIfNeeded(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, AppendRunBehavior behavior, IsolateTracker& tracker)
635 if (start > end || RenderBlockFlow::shouldSkipCreatingRunsForObject(obj))
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))
646 // This is a new start point. Stop ignoring objects and
648 lineMidpointState.setBetweenMidpoints(false);
649 start = nextMidpoint.offset();
650 lineMidpointState.incrementCurrentMidpoint();
652 return adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, resolver, behavior, tracker);
654 if (!haveNextMidpoint || (obj != nextMidpoint.object())) {
655 appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker);
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);
670 appendRunObjectIfNecessary(obj, start, end, resolver, behavior, tracker);
675 static inline void addFakeRunIfNecessary(RenderObject* obj, unsigned start, unsigned end, InlineBidiResolver& resolver, IsolateTracker& tracker)
677 tracker.setMidpointStateForRootIsolate(resolver.midpointState());
678 adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), resolver, AppendingFakeRun, tracker);
682 inline void InlineBidiResolver::appendRun(BidiRunList<BidiRun>& runs)
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);
695 adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, obj->length(), *this, AppendingRunsForObject, isolateTracker);
696 // FIXME: start/obj should be an InlineIterator instead of two separate variables.
698 obj = bidiNextSkippingEmptyInlines(m_sor.root(), obj, &isolateTracker);
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();
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);
712 adjustMidpointsAndAppendRunsForObjectIfNeeded(obj, start, end, *this, AppendingRunsForObject, isolateTracker);
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);
725 m_direction = WTF::Unicode::OtherNeutral;
726 m_status.eor = WTF::Unicode::OtherNeutral;
731 #endif // InlineIterator_h