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 : 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 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 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 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 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 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 230 inline bool InlineIterator::atEnd() const 231 { 232 return !m_obj; 233 } 234 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 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<> 259 inline void InlineBidiResolver::increment() 260 { 261 m_current.increment(this); 262 } 263 264 template <> 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