2011-04-20 Eric Seidel <eric@webkit.org>
[WebKit-https.git] / Source / WebCore / rendering / InlineIterator.h
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 "BidiRun.h"
27 #include "RenderBlock.h"
28 #include "RenderText.h"
29 #include <wtf/AlwaysInline.h>
30 #include <wtf/StdLibExtras.h>
31
32 namespace WebCore {
33
34 class InlineIterator {
35 public:
36     InlineIterator()
37         : m_root(0)
38         , m_obj(0)
39         , m_pos(0)
40         , m_nextBreakablePosition(-1)
41     {
42     }
43
44     InlineIterator(RenderObject* root, RenderObject* o, unsigned p)
45         : m_root(root)
46         , m_obj(o)
47         , m_pos(p)
48         , m_nextBreakablePosition(-1)
49     {
50     }
51
52     void clear() { moveTo(0, 0); }
53
54     void moveToStartOf(RenderObject* object)
55     {
56         moveTo(object, 0);
57     }
58
59     void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1)
60     {
61         m_obj = object;
62         m_pos = offset;
63         m_nextBreakablePosition = nextBreak;
64     }
65
66     RenderObject* root() const { return m_root; }
67
68     void increment(InlineBidiResolver* = 0);
69     bool atEnd() const;
70
71     inline bool atTextParagraphSeparator()
72     {
73         return m_obj && m_obj->preservesNewline() && m_obj->isText() && toRenderText(m_obj)->textLength()
74             && !toRenderText(m_obj)->isWordBreak() && toRenderText(m_obj)->characters()[m_pos] == '\n';
75     }
76     
77     inline bool atParagraphSeparator()
78     {
79         return (m_obj && m_obj->isBR()) || atTextParagraphSeparator();
80     }
81
82     UChar current() const;
83     ALWAYS_INLINE WTF::Unicode::Direction direction() const;
84
85 private:
86     RenderObject* m_root;
87
88     // FIXME: These should be private.
89 public:
90     RenderObject* m_obj;
91     unsigned m_pos;
92     int m_nextBreakablePosition;
93 };
94
95 inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
96 {
97     return it1.m_pos == it2.m_pos && it1.m_obj == it2.m_obj;
98 }
99
100 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
101 {
102     return it1.m_pos != it2.m_pos || it1.m_obj != it2.m_obj;
103 }
104
105 static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi)
106 {
107     using namespace WTF::Unicode;
108     if (unicodeBidi == Embed)
109         return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding;
110     return dir == RTL ? RightToLeftOverride : LeftToRightOverride;
111 }
112
113 static inline void notifyResolverEnteredObject(InlineBidiResolver* resolver, RenderObject* object)
114 {
115     if (!resolver || !object || !object->isRenderInline())
116         return;
117
118     RenderStyle* style = object->style();
119     EUnicodeBidi unicodeBidi = style->unicodeBidi();
120     if (unicodeBidi == UBNormal)
121         return;
122     resolver->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM);
123 }
124
125 static inline void notifyResolverWillExitObject(InlineBidiResolver* resolver, RenderObject* object)
126 {
127     if (!resolver || !object || !object->isRenderInline())
128         return;
129     if (object->style()->unicodeBidi() == UBNormal)
130         return;
131     resolver->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM);
132 }
133
134 static inline bool isIteratorTarget(RenderObject* object)
135 {
136     ASSERT(object); // The iterator will of course return 0, but its not an expected argument to this function.
137     return object->isText() || object->isFloating() || object->isPositioned() || object->isReplaced();
138 }
139
140 // FIXME: This function is misleadingly named. It has little to do with bidi.
141 // This function will iterate over inlines within a block, optionally notifying
142 // a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels).
143 static inline RenderObject* bidiNext(RenderObject* root, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
144 {
145     RenderObject* next = 0;
146     // oldEndOfInline denotes if when we last stopped iterating if we were at the end of an inline.
147     bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
148     bool endOfInline = false;
149
150     while (current) {
151         next = 0;
152         if (!oldEndOfInline && !isIteratorTarget(current)) {
153             next = current->firstChild();
154             notifyResolverEnteredObject(resolver, next);
155         }
156
157         // We hit this when either current has no children, or when current is not a renderer we care about.
158         if (!next) {
159             // If it is a renderer we care about, and we're doing our inline-walk, return it.
160             if (!skipInlines && !oldEndOfInline && current->isRenderInline()) {
161                 next = current;
162                 endOfInline = true;
163                 break;
164             }
165
166             while (current && current != root) {
167                 notifyResolverWillExitObject(resolver, current);
168
169                 next = current->nextSibling();
170                 if (next) {
171                     notifyResolverEnteredObject(resolver, next);
172                     break;
173                 }
174
175                 current = current->parent();
176                 if (!skipInlines && current && current != root && current->isRenderInline()) {
177                     next = current;
178                     endOfInline = true;
179                     break;
180                 }
181             }
182         }
183
184         if (!next)
185             break;
186
187         if (isIteratorTarget(next)
188             || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
189                 && next->isRenderInline()))
190             break;
191         current = next;
192     }
193
194     if (endOfInlinePtr)
195         *endOfInlinePtr = endOfInline;
196
197     return next;
198 }
199
200 static inline RenderObject* bidiFirstSkippingInlines(RenderObject* root, InlineBidiResolver* resolver)
201 {
202     ASSERT(resolver);
203     RenderObject* o = root->firstChild();
204     if (!o)
205         return 0;
206
207     if (o->isRenderInline()) {
208         notifyResolverEnteredObject(resolver, o);
209         if (o->firstChild())
210             o = bidiNext(root, o, resolver, true);
211         else {
212             // Never skip empty inlines.
213             if (resolver)
214                 resolver->commitExplicitEmbedding();
215             return o; 
216         }
217     }
218
219     // FIXME: Unify this with the bidiNext call above.
220     if (o && !isIteratorTarget(o))
221         o = bidiNext(root, o, resolver, true);
222
223     resolver->commitExplicitEmbedding();
224     return o;
225 }
226
227 // FIXME: This method needs to be renamed when bidiNext finds a good name.
228 static inline RenderObject* bidiFirstNotSkippingInlines(RenderObject* root)
229 {
230     RenderObject* o = root->firstChild();
231     // If either there are no children to walk, or the first one is correct
232     // then just return it.
233     if (!o || o->isRenderInline() || isIteratorTarget(o))
234         return o;
235
236     return bidiNext(root, o, 0, false);
237 }
238
239 inline void InlineIterator::increment(InlineBidiResolver* resolver)
240 {
241     if (!m_obj)
242         return;
243     if (m_obj->isText()) {
244         m_pos++;
245         if (m_pos < toRenderText(m_obj)->textLength())
246             return;
247     }
248     // bidiNext can return 0, so use moveTo instead of moveToStartOf
249     moveTo(bidiNext(m_root, m_obj, resolver), 0);
250 }
251
252 inline bool InlineIterator::atEnd() const
253 {
254     return !m_obj;
255 }
256
257 inline UChar InlineIterator::current() const
258 {
259     if (!m_obj || !m_obj->isText())
260         return 0;
261
262     RenderText* text = toRenderText(m_obj);
263     if (m_pos >= text->textLength())
264         return 0;
265
266     return text->characters()[m_pos];
267 }
268
269 ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
270 {
271     if (UChar c = current())
272         return WTF::Unicode::direction(c);
273
274     if (m_obj && m_obj->isListMarker())
275         return m_obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
276
277     return WTF::Unicode::OtherNeutral;
278 }
279
280 template<>
281 inline void InlineBidiResolver::increment()
282 {
283     m_current.increment(this);
284 }
285
286 template <>
287 inline void InlineBidiResolver::appendRun()
288 {
289     if (!m_emptyRun && !m_eor.atEnd()) {
290         int start = m_sor.m_pos;
291         RenderObject* obj = m_sor.m_obj;
292         while (obj && obj != m_eor.m_obj && obj != endOfLine.m_obj) {
293             RenderBlock::appendRunsForObject(m_runs, start, obj->length(), obj, *this);
294             start = 0;
295             obj = bidiNext(m_sor.root(), obj);
296         }
297         if (obj) {
298             unsigned pos = obj == m_eor.m_obj ? m_eor.m_pos : UINT_MAX;
299             if (obj == endOfLine.m_obj && endOfLine.m_pos <= pos) {
300                 m_reachedEndOfLine = true;
301                 pos = endOfLine.m_pos;
302             }
303             // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
304             int end = obj->length() ? pos + 1 : 0;
305             RenderBlock::appendRunsForObject(m_runs, start, end, obj, *this);
306         }
307
308         m_eor.increment();
309         m_sor = m_eor;
310     }
311
312     m_direction = WTF::Unicode::OtherNeutral;
313     m_status.eor = WTF::Unicode::OtherNeutral;
314 }
315
316 }
317
318 #endif // InlineIterator_h