Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
      4  * All right reserved.
      5  * Copyright (C) 2010 Google Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #ifndef BidiRunForLine_h
     25 #define BidiRunForLine_h
     26 
     27 #include "config.h"
     28 #include "core/rendering/BidiRunForLine.h"
     29 
     30 #include "core/rendering/InlineIterator.h"
     31 
     32 namespace blink {
     33 
     34 using namespace WTF::Unicode;
     35 
     36 static RenderObject* firstRenderObjectForDirectionalityDetermination(
     37     RenderObject* root, RenderObject* current = 0)
     38 {
     39     RenderObject* next = current;
     40     while (current) {
     41         if (isIsolated(current->style()->unicodeBidi())
     42             && (current->isRenderInline() || current->isRenderBlock())) {
     43             if (current != root)
     44                 current = 0;
     45             else
     46                 current = next;
     47             break;
     48         }
     49         current = current->parent();
     50     }
     51 
     52     if (!current)
     53         current = root->slowFirstChild();
     54 
     55     while (current) {
     56         next = 0;
     57         if (isIteratorTarget(current) && !(current->isText()
     58             && toRenderText(current)->isAllCollapsibleWhitespace()))
     59             break;
     60 
     61         if (!isIteratorTarget(current)
     62             && !isIsolated(current->style()->unicodeBidi()))
     63             next = current->slowFirstChild();
     64 
     65         if (!next) {
     66             while (current && current != root) {
     67                 next = current->nextSibling();
     68                 if (next)
     69                     break;
     70                 current = current->parent();
     71             }
     72         }
     73 
     74         if (!next)
     75             break;
     76 
     77         current = next;
     78     }
     79 
     80     return current;
     81 }
     82 
     83 TextDirection determinePlaintextDirectionality(RenderObject* root,
     84     RenderObject* current = 0, unsigned pos = 0)
     85 {
     86     InlineIterator iter(root,
     87         firstRenderObjectForDirectionalityDetermination(root, current), pos);
     88     InlineBidiResolver observer;
     89     observer.setStatus(BidiStatus(root->style()->direction(),
     90         isOverride(root->style()->unicodeBidi())));
     91     observer.setPositionIgnoringNestedIsolates(iter);
     92     return observer.determineParagraphDirectionality();
     93 }
     94 
     95 // FIXME: This should be a BidiStatus constructor or create method.
     96 static inline BidiStatus statusWithDirection(TextDirection textDirection,
     97     bool isOverride)
     98 {
     99     WTF::Unicode::Direction direction = textDirection == LTR
    100         ? LeftToRight
    101         : RightToLeft;
    102     RefPtr<BidiContext> context = BidiContext::create(
    103         textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
    104 
    105     // This copies BidiStatus and may churn the ref on BidiContext.
    106     // I doubt it matters.
    107     return BidiStatus(direction, direction, direction, context.release());
    108 }
    109 
    110 static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver,
    111     RenderObject* root, RenderObject* startObject)
    112 {
    113     if (root != startObject) {
    114         RenderObject* parent = startObject->parent();
    115         setupResolverToResumeInIsolate(resolver, root, parent);
    116         notifyObserverEnteredObject(&resolver, startObject);
    117     }
    118 }
    119 
    120 static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver,
    121     InlineBidiResolver& isolatedResolver)
    122 {
    123     while (!isolatedResolver.isolatedRuns().isEmpty()) {
    124         BidiRun* run = isolatedResolver.isolatedRuns().last();
    125         isolatedResolver.isolatedRuns().removeLast();
    126         topResolver.setMidpointStateForIsolatedRun(run,
    127             isolatedResolver.midpointStateForIsolatedRun(run));
    128     }
    129 }
    130 
    131 void constructBidiRunsForLine(InlineBidiResolver& topResolver,
    132     BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine,
    133     VisualDirectionOverride override, bool previousLineBrokeCleanly,
    134     bool isNewUBAParagraph)
    135 {
    136     // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead
    137     // of the resolver owning the runs.
    138     ASSERT(&topResolver.runs() == &bidiRuns);
    139     ASSERT(topResolver.position() != endOfLine);
    140     RenderObject* currentRoot = topResolver.position().root();
    141     topResolver.createBidiRunsForLine(endOfLine, override,
    142         previousLineBrokeCleanly);
    143 
    144     while (!topResolver.isolatedRuns().isEmpty()) {
    145         // It does not matter which order we resolve the runs as long as we
    146         // resolve them all.
    147         BidiRun* isolatedRun = topResolver.isolatedRuns().last();
    148         topResolver.isolatedRuns().removeLast();
    149 
    150         RenderObject* startObj = isolatedRun->object();
    151 
    152         // Only inlines make sense with unicode-bidi: isolate (blocks are
    153         // already isolated).
    154         // FIXME: Because enterIsolate is not passed a RenderObject, we have to
    155         // crawl up the tree to see which parent inline is the isolate. We could
    156         // change enterIsolate to take a RenderObject and do this logic there,
    157         // but that would be a layering violation for BidiResolver (which knows
    158         // nothing about RenderObject).
    159         RenderInline* isolatedInline = toRenderInline(
    160             highestContainingIsolateWithinRoot(startObj, currentRoot));
    161         ASSERT(isolatedInline);
    162 
    163         InlineBidiResolver isolatedResolver;
    164         LineMidpointState& isolatedLineMidpointState =
    165             isolatedResolver.midpointState();
    166         isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun(
    167             isolatedRun);
    168         EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
    169         TextDirection direction;
    170         if (unicodeBidi == Plaintext) {
    171             direction = determinePlaintextDirectionality(isolatedInline,
    172                 isNewUBAParagraph ? startObj : 0);
    173         } else {
    174             ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
    175             direction = isolatedInline->style()->direction();
    176         }
    177         isolatedResolver.setStatus(statusWithDirection(direction,
    178             isOverride(unicodeBidi)));
    179 
    180         setupResolverToResumeInIsolate(isolatedResolver, isolatedInline,
    181             startObj);
    182 
    183         // The starting position is the beginning of the first run within the
    184         // isolate that was identified during the earlier call to
    185         // createBidiRunsForLine. This can be but is not necessarily the first
    186         // run within the isolate.
    187         InlineIterator iter = InlineIterator(isolatedInline, startObj,
    188             isolatedRun->m_start);
    189         isolatedResolver.setPositionIgnoringNestedIsolates(iter);
    190         // We stop at the next end of line; we may re-enter this isolate in the
    191         // next call to constructBidiRuns().
    192         // FIXME: What should end and previousLineBrokeCleanly be?
    193         // rniwa says previousLineBrokeCleanly is just a WinIE hack and could
    194         // always be false here?
    195         isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride,
    196             previousLineBrokeCleanly);
    197 
    198         ASSERT(isolatedResolver.runs().runCount());
    199         if (isolatedResolver.runs().runCount())
    200             bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
    201 
    202         // If we encountered any nested isolate runs, just move them
    203         // to the top resolver's list for later processing.
    204         if (!isolatedResolver.isolatedRuns().isEmpty()) {
    205             topResolver.isolatedRuns().appendVector(
    206                 isolatedResolver.isolatedRuns());
    207             currentRoot = isolatedInline;
    208             restoreIsolatedMidpointStates(topResolver, isolatedResolver);
    209         }
    210     }
    211 }
    212 
    213 } // namespace blink
    214 
    215 #endif // BidiRunForLine_h
    216