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         : 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