Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc.  All right reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  *
     20  */
     21 
     22 #ifndef BidiResolver_h
     23 #define BidiResolver_h
     24 
     25 #include "BidiContext.h"
     26 #include "BidiRunList.h"
     27 #include <wtf/Noncopyable.h>
     28 #include <wtf/PassRefPtr.h>
     29 #include <wtf/Vector.h>
     30 
     31 namespace WebCore {
     32 
     33 template <class Iterator> struct MidpointState {
     34     MidpointState()
     35     {
     36         reset();
     37     }
     38 
     39     void reset()
     40     {
     41         numMidpoints = 0;
     42         currentMidpoint = 0;
     43         betweenMidpoints = false;
     44     }
     45 
     46     // The goal is to reuse the line state across multiple
     47     // lines so we just keep an array around for midpoints and never clear it across multiple
     48     // lines.  We track the number of items and position using the two other variables.
     49     Vector<Iterator> midpoints;
     50     unsigned numMidpoints;
     51     unsigned currentMidpoint;
     52     bool betweenMidpoints;
     53 };
     54 
     55 // The BidiStatus at a given position (typically the end of a line) can
     56 // be cached and then used to restart bidi resolution at that position.
     57 struct BidiStatus {
     58     BidiStatus()
     59         : eor(WTF::Unicode::OtherNeutral)
     60         , lastStrong(WTF::Unicode::OtherNeutral)
     61         , last(WTF::Unicode::OtherNeutral)
     62     {
     63     }
     64 
     65     BidiStatus(WTF::Unicode::Direction eorDir, WTF::Unicode::Direction lastStrongDir, WTF::Unicode::Direction lastDir, PassRefPtr<BidiContext> bidiContext)
     66         : eor(eorDir)
     67         , lastStrong(lastStrongDir)
     68         , last(lastDir)
     69         , context(bidiContext)
     70     {
     71     }
     72 
     73     WTF::Unicode::Direction eor;
     74     WTF::Unicode::Direction lastStrong;
     75     WTF::Unicode::Direction last;
     76     RefPtr<BidiContext> context;
     77 };
     78 
     79 class BidiEmbedding {
     80 public:
     81     BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source)
     82     : m_direction(direction)
     83     , m_source(source)
     84     {
     85     }
     86 
     87     WTF::Unicode::Direction direction() const { return m_direction; }
     88     BidiEmbeddingSource source() const { return m_source; }
     89 private:
     90     WTF::Unicode::Direction m_direction;
     91     BidiEmbeddingSource m_source;
     92 };
     93 
     94 inline bool operator==(const BidiStatus& status1, const BidiStatus& status2)
     95 {
     96     return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong && *(status1.context) == *(status2.context);
     97 }
     98 
     99 inline bool operator!=(const BidiStatus& status1, const BidiStatus& status2)
    100 {
    101     return !(status1 == status2);
    102 }
    103 
    104 struct BidiCharacterRun {
    105     BidiCharacterRun(int start, int stop, BidiContext* context, WTF::Unicode::Direction dir)
    106         : m_start(start)
    107         , m_stop(stop)
    108         , m_override(context->override())
    109         , m_next(0)
    110     {
    111         if (dir == WTF::Unicode::OtherNeutral)
    112             dir = context->dir();
    113 
    114         m_level = context->level();
    115 
    116         // add level of run (cases I1 & I2)
    117         if (m_level % 2) {
    118             if (dir == WTF::Unicode::LeftToRight || dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
    119                 m_level++;
    120         } else {
    121             if (dir == WTF::Unicode::RightToLeft)
    122                 m_level++;
    123             else if (dir == WTF::Unicode::ArabicNumber || dir == WTF::Unicode::EuropeanNumber)
    124                 m_level += 2;
    125         }
    126     }
    127 
    128     void destroy() { delete this; }
    129 
    130     int start() const { return m_start; }
    131     int stop() const { return m_stop; }
    132     unsigned char level() const { return m_level; }
    133     bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
    134     bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
    135 
    136     BidiCharacterRun* next() const { return m_next; }
    137 
    138     unsigned char m_level;
    139     int m_start;
    140     int m_stop;
    141     bool m_override;
    142     BidiCharacterRun* m_next;
    143 };
    144 
    145 enum VisualDirectionOverride {
    146     NoVisualOverride,
    147     VisualLeftToRightOverride,
    148     VisualRightToLeftOverride
    149 };
    150 
    151 // BidiResolver is WebKit's implementation of the Unicode Bidi Algorithm
    152 // http://unicode.org/reports/tr9
    153 template <class Iterator, class Run> class BidiResolver {
    154     WTF_MAKE_NONCOPYABLE(BidiResolver);
    155 public:
    156     BidiResolver()
    157         : m_direction(WTF::Unicode::OtherNeutral)
    158         , m_reachedEndOfLine(false)
    159         , m_emptyRun(true)
    160     {
    161     }
    162 
    163     const Iterator& position() const { return m_current; }
    164     void setPosition(const Iterator& position) { m_current = position; }
    165 
    166     void increment() { m_current.increment(); }
    167 
    168     BidiContext* context() const { return m_status.context.get(); }
    169     void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
    170 
    171     void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
    172     void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
    173     void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
    174 
    175     WTF::Unicode::Direction dir() const { return m_direction; }
    176     void setDir(WTF::Unicode::Direction d) { m_direction = d; }
    177 
    178     const BidiStatus& status() const { return m_status; }
    179     void setStatus(const BidiStatus s) { m_status = s; }
    180 
    181     MidpointState<Iterator>& midpointState() { return m_midpointState; }
    182 
    183     void embed(WTF::Unicode::Direction, BidiEmbeddingSource);
    184     bool commitExplicitEmbedding();
    185 
    186     void createBidiRunsForLine(const Iterator& end, VisualDirectionOverride = NoVisualOverride, bool hardLineBreak = false);
    187 
    188     BidiRunList<Run>& runs() { return m_runs; }
    189 
    190     // FIXME: This used to be part of deleteRuns() but was a layering violation.
    191     // It's unclear if this is still needed.
    192     void markCurrentRunEmpty() { m_emptyRun = true; }
    193 
    194 protected:
    195     // FIXME: Instead of InlineBidiResolvers subclassing this method, we should
    196     // pass in some sort of Traits object which knows how to create runs for appending.
    197     void appendRun();
    198 
    199     Iterator m_current;
    200     // sor and eor are "start of run" and "end of run" respectively and correpond
    201     // to abreviations used in UBA spec: http://unicode.org/reports/tr9/#BD7
    202     Iterator m_sor;
    203     Iterator m_eor;
    204     Iterator m_last;
    205     BidiStatus m_status;
    206     WTF::Unicode::Direction m_direction;
    207     Iterator endOfLine;
    208     bool m_reachedEndOfLine;
    209     Iterator m_lastBeforeET; // Before a EuropeanNumberTerminator
    210     bool m_emptyRun;
    211 
    212     // FIXME: This should not belong to the resolver, but rather be passed
    213     // into createBidiRunsForLine by the caller.
    214     BidiRunList<Run> m_runs;
    215 
    216     MidpointState<Iterator> m_midpointState;
    217 
    218 private:
    219     void raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to);
    220     void lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from);
    221     void checkDirectionInLowerRaiseEmbeddingLevel();
    222 
    223     void updateStatusLastFromCurrentDirection(WTF::Unicode::Direction);
    224     void reorderRunsFromLevels();
    225 
    226     Vector<BidiEmbedding, 8> m_currentExplicitEmbeddingSequence;
    227 };
    228 
    229 template <class Iterator, class Run>
    230 void BidiResolver<Iterator, Run>::appendRun()
    231 {
    232     if (!m_emptyRun && !m_eor.atEnd()) {
    233         unsigned startOffset = m_sor.offset();
    234         unsigned endOffset = m_eor.offset();
    235 
    236         if (!endOfLine.atEnd() && endOffset >= endOfLine.offset()) {
    237             m_reachedEndOfLine = true;
    238             endOffset = endOfLine.offset();
    239         }
    240 
    241         if (endOffset >= startOffset)
    242             m_runs.addRun(new Run(startOffset, endOffset + 1, context(), m_direction));
    243 
    244         m_eor.increment();
    245         m_sor = m_eor;
    246     }
    247 
    248     m_direction = WTF::Unicode::OtherNeutral;
    249     m_status.eor = WTF::Unicode::OtherNeutral;
    250 }
    251 
    252 template <class Iterator, class Run>
    253 void BidiResolver<Iterator, Run>::embed(WTF::Unicode::Direction dir, BidiEmbeddingSource source)
    254 {
    255     using namespace WTF::Unicode;
    256 
    257     ASSERT(dir == PopDirectionalFormat || dir == LeftToRightEmbedding || dir == LeftToRightOverride || dir == RightToLeftEmbedding || dir == RightToLeftOverride);
    258     m_currentExplicitEmbeddingSequence.append(BidiEmbedding(dir, source));
    259 }
    260 
    261 template <class Iterator, class Run>
    262 void BidiResolver<Iterator, Run>::checkDirectionInLowerRaiseEmbeddingLevel()
    263 {
    264     using namespace WTF::Unicode;
    265 
    266     ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
    267     ASSERT(m_status.last != NonSpacingMark
    268         && m_status.last != BoundaryNeutral
    269         && m_status.last != RightToLeftEmbedding
    270         && m_status.last != LeftToRightEmbedding
    271         && m_status.last != RightToLeftOverride
    272         && m_status.last != LeftToRightOverride
    273         && m_status.last != PopDirectionalFormat);
    274     if (m_direction == OtherNeutral)
    275         m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
    276 }
    277 
    278 template <class Iterator, class Run>
    279 void BidiResolver<Iterator, Run>::lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from)
    280 {
    281     using namespace WTF::Unicode;
    282 
    283     if (!m_emptyRun && m_eor != m_last) {
    284         checkDirectionInLowerRaiseEmbeddingLevel();
    285         // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
    286         if (from == LeftToRight) {
    287             // bidi.sor ... bidi.eor ... bidi.last L
    288             if (m_status.eor == EuropeanNumber) {
    289                 if (m_status.lastStrong != LeftToRight) {
    290                     m_direction = EuropeanNumber;
    291                     appendRun();
    292                 }
    293             } else if (m_status.eor == ArabicNumber) {
    294                 m_direction = ArabicNumber;
    295                 appendRun();
    296             } else if (m_status.lastStrong != LeftToRight) {
    297                 appendRun();
    298                 m_direction = LeftToRight;
    299             }
    300         } else if (m_status.eor == EuropeanNumber || m_status.eor == ArabicNumber || m_status.lastStrong == LeftToRight) {
    301             appendRun();
    302             m_direction = RightToLeft;
    303         }
    304         m_eor = m_last;
    305     }
    306 
    307     appendRun();
    308     m_emptyRun = true;
    309 
    310     // sor for the new run is determined by the higher level (rule X10)
    311     setLastDir(from);
    312     setLastStrongDir(from);
    313     m_eor = Iterator();
    314 }
    315 
    316 template <class Iterator, class Run>
    317 void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from, WTF::Unicode::Direction to)
    318 {
    319     using namespace WTF::Unicode;
    320 
    321     if (!m_emptyRun && m_eor != m_last) {
    322         checkDirectionInLowerRaiseEmbeddingLevel();
    323         // bidi.sor ... bidi.eor ... bidi.last eor; need to append the bidi.sor-bidi.eor run or extend it through bidi.last
    324         if (to == LeftToRight) {
    325             // bidi.sor ... bidi.eor ... bidi.last L
    326             if (m_status.eor == EuropeanNumber) {
    327                 if (m_status.lastStrong != LeftToRight) {
    328                     m_direction = EuropeanNumber;
    329                     appendRun();
    330                 }
    331             } else if (m_status.eor == ArabicNumber) {
    332                 m_direction = ArabicNumber;
    333                 appendRun();
    334             } else if (m_status.lastStrong != LeftToRight && from == LeftToRight) {
    335                 appendRun();
    336                 m_direction = LeftToRight;
    337             }
    338         } else if (m_status.eor == ArabicNumber
    339             || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft))
    340             || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) {
    341             appendRun();
    342             m_direction = RightToLeft;
    343         }
    344         m_eor = m_last;
    345     }
    346 
    347     appendRun();
    348     m_emptyRun = true;
    349 
    350     setLastDir(to);
    351     setLastStrongDir(to);
    352     m_eor = Iterator();
    353 }
    354 
    355 template <class Iterator, class Run>
    356 bool BidiResolver<Iterator, Run>::commitExplicitEmbedding()
    357 {
    358     using namespace WTF::Unicode;
    359 
    360     unsigned char fromLevel = context()->level();
    361     RefPtr<BidiContext> toContext = context();
    362 
    363     for (size_t i = 0; i < m_currentExplicitEmbeddingSequence.size(); ++i) {
    364         BidiEmbedding embedding = m_currentExplicitEmbeddingSequence[i];
    365         if (embedding.direction() == PopDirectionalFormat) {
    366             if (BidiContext* parentContext = toContext->parent())
    367                 toContext = parentContext;
    368         } else {
    369             Direction direction = (embedding.direction() == RightToLeftEmbedding || embedding.direction() == RightToLeftOverride) ? RightToLeft : LeftToRight;
    370             bool override = embedding.direction() == LeftToRightOverride || embedding.direction() == RightToLeftOverride;
    371             unsigned char level = toContext->level();
    372             if (direction == RightToLeft)
    373                 level = nextGreaterOddLevel(level);
    374             else
    375                 level = nextGreaterEvenLevel(level);
    376             if (level < 61)
    377                 toContext = BidiContext::create(level, direction, override, embedding.source(), toContext.get());
    378         }
    379     }
    380 
    381     unsigned char toLevel = toContext->level();
    382 
    383     if (toLevel > fromLevel)
    384         raiseExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight, toLevel % 2 ? RightToLeft : LeftToRight);
    385     else if (toLevel < fromLevel)
    386         lowerExplicitEmbeddingLevel(fromLevel % 2 ? RightToLeft : LeftToRight);
    387 
    388     setContext(toContext);
    389 
    390     m_currentExplicitEmbeddingSequence.clear();
    391 
    392     return fromLevel != toLevel;
    393 }
    394 
    395 template <class Iterator, class Run>
    396 inline void BidiResolver<Iterator, Run>::updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent)
    397 {
    398     using namespace WTF::Unicode;
    399     switch (dirCurrent) {
    400     case EuropeanNumberTerminator:
    401         if (m_status.last != EuropeanNumber)
    402             m_status.last = EuropeanNumberTerminator;
    403         break;
    404     case EuropeanNumberSeparator:
    405     case CommonNumberSeparator:
    406     case SegmentSeparator:
    407     case WhiteSpaceNeutral:
    408     case OtherNeutral:
    409         switch (m_status.last) {
    410         case LeftToRight:
    411         case RightToLeft:
    412         case RightToLeftArabic:
    413         case EuropeanNumber:
    414         case ArabicNumber:
    415             m_status.last = dirCurrent;
    416             break;
    417         default:
    418             m_status.last = OtherNeutral;
    419         }
    420         break;
    421     case NonSpacingMark:
    422     case BoundaryNeutral:
    423     case RightToLeftEmbedding:
    424     case LeftToRightEmbedding:
    425     case RightToLeftOverride:
    426     case LeftToRightOverride:
    427     case PopDirectionalFormat:
    428         // ignore these
    429         break;
    430     case EuropeanNumber:
    431         // fall through
    432     default:
    433         m_status.last = dirCurrent;
    434     }
    435 }
    436 
    437 template <class Iterator, class Run>
    438 inline void BidiResolver<Iterator, Run>::reorderRunsFromLevels()
    439 {
    440     unsigned char levelLow = 128;
    441     unsigned char levelHigh = 0;
    442     for (Run* run = m_runs.firstRun(); run; run = run->next()) {
    443         levelHigh = std::max(run->level(), levelHigh);
    444         levelLow = std::min(run->level(), levelLow);
    445     }
    446 
    447     // This implements reordering of the line (L2 according to Bidi spec):
    448     // http://unicode.org/reports/tr9/#L2
    449     // L2. From the highest level found in the text to the lowest odd level on each line,
    450     // reverse any contiguous sequence of characters that are at that level or higher.
    451 
    452     // Reversing is only done up to the lowest odd level.
    453     if (!(levelLow % 2))
    454         levelLow++;
    455 
    456     unsigned count = m_runs.runCount() - 1;
    457 
    458     while (levelHigh >= levelLow) {
    459         unsigned i = 0;
    460         Run* run = m_runs.firstRun();
    461         while (i < count) {
    462             for (;i < count && run && run->level() < levelHigh; i++)
    463                 run = run->next();
    464             unsigned start = i;
    465             for (;i <= count && run && run->level() >= levelHigh; i++)
    466                 run = run->next();
    467             unsigned end = i - 1;
    468             m_runs.reverseRuns(start, end);
    469         }
    470         levelHigh--;
    471     }
    472 }
    473 
    474 template <class Iterator, class Run>
    475 void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, VisualDirectionOverride override, bool hardLineBreak)
    476 {
    477     using namespace WTF::Unicode;
    478 
    479     ASSERT(m_direction == OtherNeutral);
    480 
    481     if (override != NoVisualOverride) {
    482         m_emptyRun = false;
    483         m_sor = m_current;
    484         m_eor = Iterator();
    485         while (m_current != end && !m_current.atEnd()) {
    486             m_eor = m_current;
    487             increment();
    488         }
    489         m_direction = override == VisualLeftToRightOverride ? LeftToRight : RightToLeft;
    490         appendRun();
    491         m_runs.setLogicallyLastRun(m_runs.lastRun());
    492         if (override == VisualRightToLeftOverride)
    493             m_runs.reverseRuns(0, m_runs.runCount() - 1);
    494         return;
    495     }
    496 
    497     m_emptyRun = true;
    498 
    499     m_eor = Iterator();
    500 
    501     m_last = m_current;
    502     bool pastEnd = false;
    503     BidiResolver<Iterator, Run> stateAtEnd;
    504 
    505     while (true) {
    506         Direction dirCurrent;
    507         if (pastEnd && (hardLineBreak || m_current.atEnd())) {
    508             BidiContext* c = context();
    509             if (hardLineBreak) {
    510                 // A deviation from the Unicode Bidi Algorithm in order to match
    511                 // WinIE and user expectations: hard line breaks reset bidi state
    512                 // coming from unicode bidi control characters, but not those from
    513                 // DOM nodes with specified directionality
    514                 stateAtEnd.setContext(c->copyStackRemovingUnicodeEmbeddingContexts());
    515 
    516                 dirCurrent = stateAtEnd.context()->dir();
    517                 stateAtEnd.setEorDir(dirCurrent);
    518                 stateAtEnd.setLastDir(dirCurrent);
    519                 stateAtEnd.setLastStrongDir(dirCurrent);
    520             } else {
    521                 while (c->parent())
    522                     c = c->parent();
    523                 dirCurrent = c->dir();
    524             }
    525         } else {
    526             dirCurrent = m_current.direction();
    527             if (context()->override()
    528                     && dirCurrent != RightToLeftEmbedding
    529                     && dirCurrent != LeftToRightEmbedding
    530                     && dirCurrent != RightToLeftOverride
    531                     && dirCurrent != LeftToRightOverride
    532                     && dirCurrent != PopDirectionalFormat)
    533                 dirCurrent = context()->dir();
    534             else if (dirCurrent == NonSpacingMark)
    535                 dirCurrent = m_status.last;
    536         }
    537 
    538         ASSERT(m_status.eor != OtherNeutral || m_eor.atEnd());
    539         switch (dirCurrent) {
    540 
    541         // embedding and overrides (X1-X9 in the Bidi specs)
    542         case RightToLeftEmbedding:
    543         case LeftToRightEmbedding:
    544         case RightToLeftOverride:
    545         case LeftToRightOverride:
    546         case PopDirectionalFormat:
    547             embed(dirCurrent, FromUnicode);
    548             commitExplicitEmbedding();
    549             break;
    550 
    551         // strong types
    552         case LeftToRight:
    553             switch(m_status.last) {
    554                 case RightToLeft:
    555                 case RightToLeftArabic:
    556                 case EuropeanNumber:
    557                 case ArabicNumber:
    558                     if (m_status.last != EuropeanNumber || m_status.lastStrong != LeftToRight)
    559                         appendRun();
    560                     break;
    561                 case LeftToRight:
    562                     break;
    563                 case EuropeanNumberSeparator:
    564                 case EuropeanNumberTerminator:
    565                 case CommonNumberSeparator:
    566                 case BoundaryNeutral:
    567                 case BlockSeparator:
    568                 case SegmentSeparator:
    569                 case WhiteSpaceNeutral:
    570                 case OtherNeutral:
    571                     if (m_status.eor == EuropeanNumber) {
    572                         if (m_status.lastStrong != LeftToRight) {
    573                             // the numbers need to be on a higher embedding level, so let's close that run
    574                             m_direction = EuropeanNumber;
    575                             appendRun();
    576                             if (context()->dir() != LeftToRight) {
    577                                 // the neutrals take the embedding direction, which is R
    578                                 m_eor = m_last;
    579                                 m_direction = RightToLeft;
    580                                 appendRun();
    581                             }
    582                         }
    583                     } else if (m_status.eor == ArabicNumber) {
    584                         // Arabic numbers are always on a higher embedding level, so let's close that run
    585                         m_direction = ArabicNumber;
    586                         appendRun();
    587                         if (context()->dir() != LeftToRight) {
    588                             // the neutrals take the embedding direction, which is R
    589                             m_eor = m_last;
    590                             m_direction = RightToLeft;
    591                             appendRun();
    592                         }
    593                     } else if (m_status.lastStrong != LeftToRight) {
    594                         //last stuff takes embedding dir
    595                         if (context()->dir() == RightToLeft) {
    596                             m_eor = m_last;
    597                             m_direction = RightToLeft;
    598                         }
    599                         appendRun();
    600                     }
    601                 default:
    602                     break;
    603             }
    604             m_eor = m_current;
    605             m_status.eor = LeftToRight;
    606             m_status.lastStrong = LeftToRight;
    607             m_direction = LeftToRight;
    608             break;
    609         case RightToLeftArabic:
    610         case RightToLeft:
    611             switch (m_status.last) {
    612                 case LeftToRight:
    613                 case EuropeanNumber:
    614                 case ArabicNumber:
    615                     appendRun();
    616                 case RightToLeft:
    617                 case RightToLeftArabic:
    618                     break;
    619                 case EuropeanNumberSeparator:
    620                 case EuropeanNumberTerminator:
    621                 case CommonNumberSeparator:
    622                 case BoundaryNeutral:
    623                 case BlockSeparator:
    624                 case SegmentSeparator:
    625                 case WhiteSpaceNeutral:
    626                 case OtherNeutral:
    627                     if (m_status.eor == EuropeanNumber) {
    628                         if (m_status.lastStrong == LeftToRight && context()->dir() == LeftToRight)
    629                             m_eor = m_last;
    630                         appendRun();
    631                     } else if (m_status.eor == ArabicNumber)
    632                         appendRun();
    633                     else if (m_status.lastStrong == LeftToRight) {
    634                         if (context()->dir() == LeftToRight)
    635                             m_eor = m_last;
    636                         appendRun();
    637                     }
    638                 default:
    639                     break;
    640             }
    641             m_eor = m_current;
    642             m_status.eor = RightToLeft;
    643             m_status.lastStrong = dirCurrent;
    644             m_direction = RightToLeft;
    645             break;
    646 
    647             // weak types:
    648 
    649         case EuropeanNumber:
    650             if (m_status.lastStrong != RightToLeftArabic) {
    651                 // if last strong was AL change EN to AN
    652                 switch (m_status.last) {
    653                     case EuropeanNumber:
    654                     case LeftToRight:
    655                         break;
    656                     case RightToLeft:
    657                     case RightToLeftArabic:
    658                     case ArabicNumber:
    659                         m_eor = m_last;
    660                         appendRun();
    661                         m_direction = EuropeanNumber;
    662                         break;
    663                     case EuropeanNumberSeparator:
    664                     case CommonNumberSeparator:
    665                         if (m_status.eor == EuropeanNumber)
    666                             break;
    667                     case EuropeanNumberTerminator:
    668                     case BoundaryNeutral:
    669                     case BlockSeparator:
    670                     case SegmentSeparator:
    671                     case WhiteSpaceNeutral:
    672                     case OtherNeutral:
    673                         if (m_status.eor == EuropeanNumber) {
    674                             if (m_status.lastStrong == RightToLeft) {
    675                                 // ENs on both sides behave like Rs, so the neutrals should be R.
    676                                 // Terminate the EN run.
    677                                 appendRun();
    678                                 // Make an R run.
    679                                 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
    680                                 m_direction = RightToLeft;
    681                                 appendRun();
    682                                 // Begin a new EN run.
    683                                 m_direction = EuropeanNumber;
    684                             }
    685                         } else if (m_status.eor == ArabicNumber) {
    686                             // Terminate the AN run.
    687                             appendRun();
    688                             if (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) {
    689                                 // Make an R run.
    690                                 m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
    691                                 m_direction = RightToLeft;
    692                                 appendRun();
    693                                 // Begin a new EN run.
    694                                 m_direction = EuropeanNumber;
    695                             }
    696                         } else if (m_status.lastStrong == RightToLeft) {
    697                             // Extend the R run to include the neutrals.
    698                             m_eor = m_status.last == EuropeanNumberTerminator ? m_lastBeforeET : m_last;
    699                             m_direction = RightToLeft;
    700                             appendRun();
    701                             // Begin a new EN run.
    702                             m_direction = EuropeanNumber;
    703                         }
    704                     default:
    705                         break;
    706                 }
    707                 m_eor = m_current;
    708                 m_status.eor = EuropeanNumber;
    709                 if (m_direction == OtherNeutral)
    710                     m_direction = LeftToRight;
    711                 break;
    712             }
    713         case ArabicNumber:
    714             dirCurrent = ArabicNumber;
    715             switch (m_status.last) {
    716                 case LeftToRight:
    717                     if (context()->dir() == LeftToRight)
    718                         appendRun();
    719                     break;
    720                 case ArabicNumber:
    721                     break;
    722                 case RightToLeft:
    723                 case RightToLeftArabic:
    724                 case EuropeanNumber:
    725                     m_eor = m_last;
    726                     appendRun();
    727                     break;
    728                 case CommonNumberSeparator:
    729                     if (m_status.eor == ArabicNumber)
    730                         break;
    731                 case EuropeanNumberSeparator:
    732                 case EuropeanNumberTerminator:
    733                 case BoundaryNeutral:
    734                 case BlockSeparator:
    735                 case SegmentSeparator:
    736                 case WhiteSpaceNeutral:
    737                 case OtherNeutral:
    738                     if (m_status.eor == ArabicNumber
    739                         || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft))
    740                         || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) {
    741                         // Terminate the run before the neutrals.
    742                         appendRun();
    743                         // Begin an R run for the neutrals.
    744                         m_direction = RightToLeft;
    745                     } else if (m_direction == OtherNeutral)
    746                         m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : RightToLeft;
    747                     m_eor = m_last;
    748                     appendRun();
    749                 default:
    750                     break;
    751             }
    752             m_eor = m_current;
    753             m_status.eor = ArabicNumber;
    754             if (m_direction == OtherNeutral)
    755                 m_direction = ArabicNumber;
    756             break;
    757         case EuropeanNumberSeparator:
    758         case CommonNumberSeparator:
    759             break;
    760         case EuropeanNumberTerminator:
    761             if (m_status.last == EuropeanNumber) {
    762                 dirCurrent = EuropeanNumber;
    763                 m_eor = m_current;
    764                 m_status.eor = dirCurrent;
    765             } else if (m_status.last != EuropeanNumberTerminator)
    766                 m_lastBeforeET = m_emptyRun ? m_eor : m_last;
    767             break;
    768 
    769         // boundary neutrals should be ignored
    770         case BoundaryNeutral:
    771             if (m_eor == m_last)
    772                 m_eor = m_current;
    773             break;
    774             // neutrals
    775         case BlockSeparator:
    776             // ### what do we do with newline and paragraph seperators that come to here?
    777             break;
    778         case SegmentSeparator:
    779             // ### implement rule L1
    780             break;
    781         case WhiteSpaceNeutral:
    782             break;
    783         case OtherNeutral:
    784             break;
    785         default:
    786             break;
    787         }
    788 
    789         if (pastEnd && m_eor == m_current) {
    790             if (!m_reachedEndOfLine) {
    791                 m_eor = endOfLine;
    792                 switch (m_status.eor) {
    793                     case LeftToRight:
    794                     case RightToLeft:
    795                     case ArabicNumber:
    796                         m_direction = m_status.eor;
    797                         break;
    798                     case EuropeanNumber:
    799                         m_direction = m_status.lastStrong == LeftToRight ? LeftToRight : EuropeanNumber;
    800                         break;
    801                     default:
    802                         ASSERT(false);
    803                 }
    804                 appendRun();
    805             }
    806             m_current = end;
    807             m_status = stateAtEnd.m_status;
    808             m_sor = stateAtEnd.m_sor;
    809             m_eor = stateAtEnd.m_eor;
    810             m_last = stateAtEnd.m_last;
    811             m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
    812             m_lastBeforeET = stateAtEnd.m_lastBeforeET;
    813             m_emptyRun = stateAtEnd.m_emptyRun;
    814             m_direction = OtherNeutral;
    815             break;
    816         }
    817 
    818         updateStatusLastFromCurrentDirection(dirCurrent);
    819         m_last = m_current;
    820 
    821         if (m_emptyRun) {
    822             m_sor = m_current;
    823             m_emptyRun = false;
    824         }
    825 
    826         increment();
    827         if (!m_currentExplicitEmbeddingSequence.isEmpty()) {
    828             bool committed = commitExplicitEmbedding();
    829             if (committed && pastEnd) {
    830                 m_current = end;
    831                 m_status = stateAtEnd.m_status;
    832                 m_sor = stateAtEnd.m_sor;
    833                 m_eor = stateAtEnd.m_eor;
    834                 m_last = stateAtEnd.m_last;
    835                 m_reachedEndOfLine = stateAtEnd.m_reachedEndOfLine;
    836                 m_lastBeforeET = stateAtEnd.m_lastBeforeET;
    837                 m_emptyRun = stateAtEnd.m_emptyRun;
    838                 m_direction = OtherNeutral;
    839                 break;
    840             }
    841         }
    842 
    843         if (!pastEnd && (m_current == end || m_current.atEnd())) {
    844             if (m_emptyRun)
    845                 break;
    846             stateAtEnd.m_status = m_status;
    847             stateAtEnd.m_sor = m_sor;
    848             stateAtEnd.m_eor = m_eor;
    849             stateAtEnd.m_last = m_last;
    850             stateAtEnd.m_reachedEndOfLine = m_reachedEndOfLine;
    851             stateAtEnd.m_lastBeforeET = m_lastBeforeET;
    852             stateAtEnd.m_emptyRun = m_emptyRun;
    853             endOfLine = m_last;
    854             pastEnd = true;
    855         }
    856     }
    857 
    858     m_runs.setLogicallyLastRun(m_runs.lastRun());
    859     reorderRunsFromLevels();
    860     endOfLine = Iterator();
    861 }
    862 
    863 } // namespace WebCore
    864 
    865 #endif // BidiResolver_h
    866