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