Home | History | Annotate | Download | only in rendering
      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