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