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