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