1/*
2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
4 * All right reserved.
5 * Copyright (C) 2010 Google Inc. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#ifndef BidiRunForLine_h
25#define BidiRunForLine_h
26
27#include "config.h"
28#include "core/rendering/BidiRunForLine.h"
29
30#include "core/rendering/InlineIterator.h"
31
32namespace blink {
33
34using namespace WTF::Unicode;
35
36static RenderObject* firstRenderObjectForDirectionalityDetermination(
37    RenderObject* root, RenderObject* current = 0)
38{
39    RenderObject* next = current;
40    while (current) {
41        if (isIsolated(current->style()->unicodeBidi())
42            && (current->isRenderInline() || current->isRenderBlock())) {
43            if (current != root)
44                current = 0;
45            else
46                current = next;
47            break;
48        }
49        current = current->parent();
50    }
51
52    if (!current)
53        current = root->slowFirstChild();
54
55    while (current) {
56        next = 0;
57        if (isIteratorTarget(current) && !(current->isText()
58            && toRenderText(current)->isAllCollapsibleWhitespace()))
59            break;
60
61        if (!isIteratorTarget(current)
62            && !isIsolated(current->style()->unicodeBidi()))
63            next = current->slowFirstChild();
64
65        if (!next) {
66            while (current && current != root) {
67                next = current->nextSibling();
68                if (next)
69                    break;
70                current = current->parent();
71            }
72        }
73
74        if (!next)
75            break;
76
77        current = next;
78    }
79
80    return current;
81}
82
83TextDirection determinePlaintextDirectionality(RenderObject* root,
84    RenderObject* current = 0, unsigned pos = 0)
85{
86    InlineIterator iter(root,
87        firstRenderObjectForDirectionalityDetermination(root, current), pos);
88    InlineBidiResolver observer;
89    observer.setStatus(BidiStatus(root->style()->direction(),
90        isOverride(root->style()->unicodeBidi())));
91    observer.setPositionIgnoringNestedIsolates(iter);
92    return observer.determineParagraphDirectionality();
93}
94
95// FIXME: This should be a BidiStatus constructor or create method.
96static inline BidiStatus statusWithDirection(TextDirection textDirection,
97    bool isOverride)
98{
99    WTF::Unicode::Direction direction = textDirection == LTR
100        ? LeftToRight
101        : RightToLeft;
102    RefPtr<BidiContext> context = BidiContext::create(
103        textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
104
105    // This copies BidiStatus and may churn the ref on BidiContext.
106    // I doubt it matters.
107    return BidiStatus(direction, direction, direction, context.release());
108}
109
110static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver,
111    RenderObject* root, RenderObject* startObject)
112{
113    if (root != startObject) {
114        RenderObject* parent = startObject->parent();
115        setupResolverToResumeInIsolate(resolver, root, parent);
116        notifyObserverEnteredObject(&resolver, startObject);
117    }
118}
119
120static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver,
121    InlineBidiResolver& isolatedResolver)
122{
123    while (!isolatedResolver.isolatedRuns().isEmpty()) {
124        BidiRun* run = isolatedResolver.isolatedRuns().last();
125        isolatedResolver.isolatedRuns().removeLast();
126        topResolver.setMidpointStateForIsolatedRun(run,
127            isolatedResolver.midpointStateForIsolatedRun(run));
128    }
129}
130
131void constructBidiRunsForLine(InlineBidiResolver& topResolver,
132    BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine,
133    VisualDirectionOverride override, bool previousLineBrokeCleanly,
134    bool isNewUBAParagraph)
135{
136    // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead
137    // of the resolver owning the runs.
138    ASSERT(&topResolver.runs() == &bidiRuns);
139    ASSERT(topResolver.position() != endOfLine);
140    RenderObject* currentRoot = topResolver.position().root();
141    topResolver.createBidiRunsForLine(endOfLine, override,
142        previousLineBrokeCleanly);
143
144    while (!topResolver.isolatedRuns().isEmpty()) {
145        // It does not matter which order we resolve the runs as long as we
146        // resolve them all.
147        BidiRun* isolatedRun = topResolver.isolatedRuns().last();
148        topResolver.isolatedRuns().removeLast();
149
150        RenderObject* startObj = isolatedRun->object();
151
152        // Only inlines make sense with unicode-bidi: isolate (blocks are
153        // already isolated).
154        // FIXME: Because enterIsolate is not passed a RenderObject, we have to
155        // crawl up the tree to see which parent inline is the isolate. We could
156        // change enterIsolate to take a RenderObject and do this logic there,
157        // but that would be a layering violation for BidiResolver (which knows
158        // nothing about RenderObject).
159        RenderInline* isolatedInline = toRenderInline(
160            highestContainingIsolateWithinRoot(startObj, currentRoot));
161        ASSERT(isolatedInline);
162
163        InlineBidiResolver isolatedResolver;
164        LineMidpointState& isolatedLineMidpointState =
165            isolatedResolver.midpointState();
166        isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun(
167            isolatedRun);
168        EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
169        TextDirection direction;
170        if (unicodeBidi == Plaintext) {
171            direction = determinePlaintextDirectionality(isolatedInline,
172                isNewUBAParagraph ? startObj : 0);
173        } else {
174            ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
175            direction = isolatedInline->style()->direction();
176        }
177        isolatedResolver.setStatus(statusWithDirection(direction,
178            isOverride(unicodeBidi)));
179
180        setupResolverToResumeInIsolate(isolatedResolver, isolatedInline,
181            startObj);
182
183        // The starting position is the beginning of the first run within the
184        // isolate that was identified during the earlier call to
185        // createBidiRunsForLine. This can be but is not necessarily the first
186        // run within the isolate.
187        InlineIterator iter = InlineIterator(isolatedInline, startObj,
188            isolatedRun->m_start);
189        isolatedResolver.setPositionIgnoringNestedIsolates(iter);
190        // We stop at the next end of line; we may re-enter this isolate in the
191        // next call to constructBidiRuns().
192        // FIXME: What should end and previousLineBrokeCleanly be?
193        // rniwa says previousLineBrokeCleanly is just a WinIE hack and could
194        // always be false here?
195        isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride,
196            previousLineBrokeCleanly);
197
198        ASSERT(isolatedResolver.runs().runCount());
199        if (isolatedResolver.runs().runCount())
200            bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
201
202        // If we encountered any nested isolate runs, just move them
203        // to the top resolver's list for later processing.
204        if (!isolatedResolver.isolatedRuns().isEmpty()) {
205            topResolver.isolatedRuns().appendVector(
206                isolatedResolver.isolatedRuns());
207            currentRoot = isolatedInline;
208            restoreIsolatedMidpointStates(topResolver, isolatedResolver);
209        }
210    }
211}
212
213} // namespace blink
214
215#endif // BidiRunForLine_h
216