/* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. * All right reserved. * Copyright (C) 2010 Google Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "sky/engine/config.h" #include "sky/engine/core/rendering/BidiRunForLine.h" #include "sky/engine/core/rendering/InlineIterator.h" namespace blink { using namespace WTF::Unicode; static RenderObject* firstRenderObjectForDirectionalityDetermination( RenderObject* root, RenderObject* current = 0) { RenderObject* next = current; while (current) { if (isIsolated(current->style()->unicodeBidi()) && (current->isRenderInline() || current->isRenderBlock())) { if (current != root) current = 0; else current = next; break; } current = current->parent(); } if (!current) current = root->slowFirstChild(); while (current) { next = 0; if (isIteratorTarget(current) && !(current->isText() && toRenderText(current)->isAllCollapsibleWhitespace())) break; if (!isIteratorTarget(current) && !isIsolated(current->style()->unicodeBidi())) next = current->slowFirstChild(); if (!next) { while (current && current != root) { next = current->nextSibling(); if (next) break; current = current->parent(); } } if (!next) break; current = next; } return current; } TextDirection determinePlaintextDirectionality(RenderObject* root, RenderObject* current, unsigned pos) { InlineIterator iter(root, firstRenderObjectForDirectionalityDetermination(root, current), pos); InlineBidiResolver observer; observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi()))); observer.setPositionIgnoringNestedIsolates(iter); return observer.determineParagraphDirectionality(); } // FIXME: This should be a BidiStatus constructor or create method. static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride) { WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft; RefPtr context = BidiContext::create( textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM); // This copies BidiStatus and may churn the ref on BidiContext. // I doubt it matters. return BidiStatus(direction, direction, direction, context.release()); } static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject) { if (root != startObject) { RenderObject* parent = startObject->parent(); setupResolverToResumeInIsolate(resolver, root, parent); notifyObserverEnteredObject(&resolver, startObject); } } static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver, InlineBidiResolver& isolatedResolver) { while (!isolatedResolver.isolatedRuns().isEmpty()) { BidiRun* run = isolatedResolver.isolatedRuns().last(); isolatedResolver.isolatedRuns().removeLast(); topResolver.setMidpointStateForIsolatedRun(run, isolatedResolver.midpointStateForIsolatedRun(run)); } } void constructBidiRunsForLine(InlineBidiResolver& topResolver, BidiRunList& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly, bool isNewUBAParagraph) { // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead // of the resolver owning the runs. ASSERT(&topResolver.runs() == &bidiRuns); ASSERT(topResolver.position() != endOfLine); RenderObject* currentRoot = topResolver.position().root(); topResolver.createBidiRunsForLine(endOfLine, override, previousLineBrokeCleanly); while (!topResolver.isolatedRuns().isEmpty()) { // It does not matter which order we resolve the runs as long as we // resolve them all. BidiRun* isolatedRun = topResolver.isolatedRuns().last(); topResolver.isolatedRuns().removeLast(); RenderObject* startObj = isolatedRun->object(); // Only inlines make sense with unicode-bidi: isolate (blocks are // already isolated). // FIXME: Because enterIsolate is not passed a RenderObject, we have to // crawl up the tree to see which parent inline is the isolate. We could // change enterIsolate to take a RenderObject and do this logic there, // but that would be a layering violation for BidiResolver (which knows // nothing about RenderObject). RenderInline* isolatedInline = toRenderInline( highestContainingIsolateWithinRoot(startObj, currentRoot)); ASSERT(isolatedInline); InlineBidiResolver isolatedResolver; LineMidpointState& isolatedLineMidpointState = isolatedResolver.midpointState(); isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun( isolatedRun); EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi(); TextDirection direction; if (unicodeBidi == Plaintext) { direction = determinePlaintextDirectionality(isolatedInline, isNewUBAParagraph ? startObj : 0); } else { ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); direction = isolatedInline->style()->direction(); } isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi))); setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj); // The starting position is the beginning of the first run within the // isolate that was identified during the earlier call to // createBidiRunsForLine. This can be but is not necessarily the first // run within the isolate. InlineIterator iter = InlineIterator(isolatedInline, startObj, isolatedRun->m_start); isolatedResolver.setPositionIgnoringNestedIsolates(iter); // We stop at the next end of line; we may re-enter this isolate in the // next call to constructBidiRuns(). // FIXME: What should end and previousLineBrokeCleanly be? // rniwa says previousLineBrokeCleanly is just a WinIE hack and could // always be false here? isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, previousLineBrokeCleanly); ASSERT(isolatedResolver.runs().runCount()); if (isolatedResolver.runs().runCount()) bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); // If we encountered any nested isolate runs, just move them // to the top resolver's list for later processing. if (!isolatedResolver.isolatedRuns().isEmpty()) { topResolver.isolatedRuns().appendVector( isolatedResolver.isolatedRuns()); currentRoot = isolatedInline; restoreIsolatedMidpointStates(topResolver, isolatedResolver); } } } } // namespace blink