• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2000 Lars Knoll (knoll@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 {
MidpointStateMidpointState34     MidpointState()
35     {
36         reset();
37     }
38 
resetMidpointState39     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 {
BidiStatusBidiStatus58     BidiStatus()
59         : eor(WTF::Unicode::OtherNeutral)
60         , lastStrong(WTF::Unicode::OtherNeutral)
61         , last(WTF::Unicode::OtherNeutral)
62     {
63     }
64 
BidiStatusBidiStatus65     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:
BidiEmbedding(WTF::Unicode::Direction direction,BidiEmbeddingSource source)81     BidiEmbedding(WTF::Unicode::Direction direction, BidiEmbeddingSource source)
82     : m_direction(direction)
83     , m_source(source)
84     {
85     }
86 
direction()87     WTF::Unicode::Direction direction() const { return m_direction; }
source()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 {
BidiCharacterRunBidiCharacterRun105     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 
destroyBidiCharacterRun128     void destroy() { delete this; }
129 
startBidiCharacterRun130     int start() const { return m_start; }
stopBidiCharacterRun131     int stop() const { return m_stop; }
levelBidiCharacterRun132     unsigned char level() const { return m_level; }
reversedBidiCharacterRun133     bool reversed(bool visuallyOrdered) { return m_level % 2 && !visuallyOrdered; }
dirOverrideBidiCharacterRun134     bool dirOverride(bool visuallyOrdered) { return m_override || visuallyOrdered; }
135 
nextBidiCharacterRun136     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:
BidiResolver()156     BidiResolver()
157         : m_direction(WTF::Unicode::OtherNeutral)
158         , m_reachedEndOfLine(false)
159         , m_emptyRun(true)
160     {
161     }
162 
position()163     const Iterator& position() const { return m_current; }
setPosition(const Iterator & position)164     void setPosition(const Iterator& position) { m_current = position; }
165 
increment()166     void increment() { m_current.increment(); }
167 
context()168     BidiContext* context() const { return m_status.context.get(); }
setContext(PassRefPtr<BidiContext> c)169     void setContext(PassRefPtr<BidiContext> c) { m_status.context = c; }
170 
setLastDir(WTF::Unicode::Direction lastDir)171     void setLastDir(WTF::Unicode::Direction lastDir) { m_status.last = lastDir; }
setLastStrongDir(WTF::Unicode::Direction lastStrongDir)172     void setLastStrongDir(WTF::Unicode::Direction lastStrongDir) { m_status.lastStrong = lastStrongDir; }
setEorDir(WTF::Unicode::Direction eorDir)173     void setEorDir(WTF::Unicode::Direction eorDir) { m_status.eor = eorDir; }
174 
dir()175     WTF::Unicode::Direction dir() const { return m_direction; }
setDir(WTF::Unicode::Direction d)176     void setDir(WTF::Unicode::Direction d) { m_direction = d; }
177 
status()178     const BidiStatus& status() const { return m_status; }
setStatus(const BidiStatus s)179     void setStatus(const BidiStatus s) { m_status = s; }
180 
midpointState()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 
runs()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.
markCurrentRunEmpty()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>
appendRun()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>
embed(WTF::Unicode::Direction dir,BidiEmbeddingSource source)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>
checkDirectionInLowerRaiseEmbeddingLevel()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>
lowerExplicitEmbeddingLevel(WTF::Unicode::Direction from)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>
raiseExplicitEmbeddingLevel(WTF::Unicode::Direction from,WTF::Unicode::Direction to)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>
commitExplicitEmbedding()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>
updateStatusLastFromCurrentDirection(WTF::Unicode::Direction dirCurrent)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>
reorderRunsFromLevels()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>
createBidiRunsForLine(const Iterator & end,VisualDirectionOverride override,bool hardLineBreak)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