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
32namespace WebCore {
33
34class InlineIterator {
35public:
36    InlineIterator()
37        : m_root(0)
38        , m_obj(0)
39        , m_pos(0)
40        , m_nextBreakablePosition(-1)
41    {
42    }
43
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
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    RenderObject* root() const { return m_root; }
67
68    void increment(InlineBidiResolver* = 0);
69    bool atEnd() const;
70
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
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
85private:
86    RenderObject* m_root;
87
88    // FIXME: These should be private.
89public:
90    RenderObject* m_obj;
91    unsigned m_pos;
92    int m_nextBreakablePosition;
93};
94
95inline 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
100inline 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
105static 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
113static 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
125static 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).
137static 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
191static 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
217inline 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
230inline bool InlineIterator::atEnd() const
231{
232    return !m_obj;
233}
234
235inline 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
247ALWAYS_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
258template<>
259inline void InlineBidiResolver::increment()
260{
261    m_current.increment(this);
262}
263
264template <>
265inline 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