2011-04-03 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     UChar current() const;
72     ALWAYS_INLINE WTF::Unicode::Direction direction() const;
73
74 private:
75     RenderObject* m_root;
76
77     // FIXME: These should be private.
78 public:
79     RenderObject* m_obj;
80     unsigned m_pos;
81     int m_nextBreakablePosition;
82 };
83
84 inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
85 {
86     return it1.m_pos == it2.m_pos && it1.m_obj == it2.m_obj;
87 }
88
89 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
90 {
91     return it1.m_pos != it2.m_pos || it1.m_obj != it2.m_obj;
92 }
93
94 static inline WTF::Unicode::Direction embedCharFromDirection(TextDirection dir, EUnicodeBidi unicodeBidi)
95 {
96     using namespace WTF::Unicode;
97     if (unicodeBidi == Embed)
98         return dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding;
99     return dir == RTL ? RightToLeftOverride : LeftToRightOverride;
100 }
101
102 static inline void notifyResolverEnteredObject(InlineBidiResolver* resolver, RenderObject* object)
103 {
104     if (!resolver || !object || !object->isRenderInline())
105         return;
106
107     RenderStyle* style = object->style();
108     EUnicodeBidi unicodeBidi = style->unicodeBidi();
109     if (unicodeBidi == UBNormal)
110         return;
111     resolver->embed(embedCharFromDirection(style->direction(), unicodeBidi), FromStyleOrDOM);
112 }
113
114 static inline void notifyResolverWillExitObject(InlineBidiResolver* resolver, RenderObject* object)
115 {
116     if (!resolver || !object || !object->isRenderInline())
117         return;
118     if (object->style()->unicodeBidi() == UBNormal)
119         return;
120     resolver->embed(WTF::Unicode::PopDirectionalFormat, FromStyleOrDOM);
121 }
122
123 // FIXME: This function is misleadingly named. It has little to do with bidi.
124 // This function will iterate over inlines within a block, optionally notifying
125 // a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels).
126 static inline RenderObject* bidiNext(RenderObject* root, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
127 {
128     RenderObject* next = 0;
129     bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
130     bool endOfInline = false;
131
132     while (current) {
133         next = 0;
134         if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) {
135             next = current->firstChild();
136             notifyResolverEnteredObject(resolver, next);
137         }
138
139         if (!next) {
140             if (!skipInlines && !oldEndOfInline && current->isRenderInline()) {
141                 next = current;
142                 endOfInline = true;
143                 break;
144             }
145
146             while (current && current != root) {
147                 notifyResolverWillExitObject(resolver, current);
148
149                 next = current->nextSibling();
150                 if (next) {
151                     notifyResolverEnteredObject(resolver, next);
152                     break;
153                 }
154
155                 current = current->parent();
156                 if (!skipInlines && current && current != root && current->isRenderInline()) {
157                     next = current;
158                     endOfInline = true;
159                     break;
160                 }
161             }
162         }
163
164         if (!next)
165             break;
166
167         if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned()
168             || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
169                 && next->isRenderInline()))
170             break;
171         current = next;
172     }
173
174     if (endOfInlinePtr)
175         *endOfInlinePtr = endOfInline;
176
177     return next;
178 }
179
180 static inline RenderObject* bidiFirst(RenderObject* root, InlineBidiResolver* resolver, bool skipInlines = true)
181 {
182     if (!root->firstChild())
183         return 0;
184
185     RenderObject* o = root->firstChild();
186     if (o->isRenderInline()) {
187         notifyResolverEnteredObject(resolver, o);
188         if (skipInlines && o->firstChild())
189             o = bidiNext(root, o, resolver, skipInlines);
190         else {
191             // Never skip empty inlines.
192             if (resolver)
193                 resolver->commitExplicitEmbedding();
194             return o; 
195         }
196     }
197
198     if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
199         o = bidiNext(root, o, resolver, skipInlines);
200
201     if (resolver)
202         resolver->commitExplicitEmbedding();
203     return o;
204 }
205
206 inline void InlineIterator::increment(InlineBidiResolver* resolver)
207 {
208     if (!m_obj)
209         return;
210     if (m_obj->isText()) {
211         m_pos++;
212         if (m_pos < toRenderText(m_obj)->textLength())
213             return;
214     }
215     // bidiNext can return 0, so use moveTo instead of moveToStartOf
216     moveTo(bidiNext(m_root, m_obj, resolver), 0);
217 }
218
219 inline bool InlineIterator::atEnd() const
220 {
221     return !m_obj;
222 }
223
224 inline UChar InlineIterator::current() const
225 {
226     if (!m_obj || !m_obj->isText())
227         return 0;
228
229     RenderText* text = toRenderText(m_obj);
230     if (m_pos >= text->textLength())
231         return 0;
232
233     return text->characters()[m_pos];
234 }
235
236 ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
237 {
238     if (UChar c = current())
239         return WTF::Unicode::direction(c);
240
241     if (m_obj && m_obj->isListMarker())
242         return m_obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
243
244     return WTF::Unicode::OtherNeutral;
245 }
246
247 template<>
248 inline void InlineBidiResolver::increment()
249 {
250     m_current.increment(this);
251 }
252
253 template <>
254 inline void InlineBidiResolver::appendRun()
255 {
256     if (!m_emptyRun && !m_eor.atEnd()) {
257         int start = m_sor.m_pos;
258         RenderObject* obj = m_sor.m_obj;
259         while (obj && obj != m_eor.m_obj && obj != endOfLine.m_obj) {
260             RenderBlock::appendRunsForObject(start, obj->length(), obj, *this);        
261             start = 0;
262             obj = bidiNext(m_sor.root(), obj);
263         }
264         if (obj) {
265             unsigned pos = obj == m_eor.m_obj ? m_eor.m_pos : UINT_MAX;
266             if (obj == endOfLine.m_obj && endOfLine.m_pos <= pos) {
267                 m_reachedEndOfLine = true;
268                 pos = endOfLine.m_pos;
269             }
270             // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
271             int end = obj->length() ? pos + 1 : 0;
272             RenderBlock::appendRunsForObject(start, end, obj, *this);
273         }
274         
275         m_eor.increment();
276         m_sor = m_eor;
277     }
278
279     m_direction = WTF::Unicode::OtherNeutral;
280     m_status.eor = WTF::Unicode::OtherNeutral;
281 }
282
283 }
284
285 #endif // InlineIterator_h