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