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:
InlineIterator()36 InlineIterator()
37 : block(0)
38 , obj(0)
39 , pos(0)
40 , nextBreakablePosition(-1)
41 {
42 }
43
InlineIterator(RenderBlock * b,RenderObject * o,unsigned p)44 InlineIterator(RenderBlock* b, RenderObject* o, unsigned p)
45 : block(b)
46 , 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 WTF::Unicode::Direction direction() const;
57
58 RenderBlock* block;
59 RenderObject* 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.obj == it2.obj;
67 }
68
69 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
70 {
71 return it1.pos != it2.pos || it1.obj != it2.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
increment(InlineBidiResolver * resolver)182 inline void InlineIterator::increment(InlineBidiResolver* resolver)
183 {
184 if (!obj)
185 return;
186 if (obj->isText()) {
187 pos++;
188 if (pos >= toRenderText(obj)->textLength()) {
189 obj = bidiNext(block, obj, resolver);
190 pos = 0;
191 nextBreakablePosition = -1;
192 }
193 } else {
194 obj = bidiNext(block, obj, resolver);
195 pos = 0;
196 nextBreakablePosition = -1;
197 }
198 }
199
atEnd()200 inline bool InlineIterator::atEnd() const
201 {
202 return !obj;
203 }
204
current()205 inline UChar InlineIterator::current() const
206 {
207 if (!obj || !obj->isText())
208 return 0;
209
210 RenderText* text = toRenderText(obj);
211 if (pos >= text->textLength())
212 return 0;
213
214 return text->characters()[pos];
215 }
216
direction()217 ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
218 {
219 if (UChar c = current())
220 return WTF::Unicode::direction(c);
221
222 if (obj && obj->isListMarker())
223 return obj->style()->direction() == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
224
225 return WTF::Unicode::OtherNeutral;
226 }
227
228 template<>
increment()229 inline void InlineBidiResolver::increment()
230 {
231 current.increment(this);
232 }
233
234 template <>
appendRun()235 inline void InlineBidiResolver::appendRun()
236 {
237 if (!emptyRun && !eor.atEnd()) {
238 int start = sor.pos;
239 RenderObject *obj = sor.obj;
240 while (obj && obj != eor.obj && obj != endOfLine.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.obj ? eor.pos : UINT_MAX;
247 if (obj == endOfLine.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
267