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