1 /* 2 * Copyright (C) 2000 Lars Knoll (knoll (at) 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: 36 InlineIterator() 37 : block(0) 38 , obj(0) 39 , pos(0) 40 , nextBreakablePosition(-1) 41 { 42 } 43 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 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 200 inline bool InlineIterator::atEnd() const 201 { 202 return !obj; 203 } 204 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 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<> 229 inline void InlineBidiResolver::increment() 230 { 231 current.increment(this); 232 } 233 234 template <> 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