blob: 0c614f9ef6fa2b5d2d2fea412eb918a99dbba018 [file] [log] [blame]
/*
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 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.
*
*/
#ifndef InlineIterator_h
#define InlineIterator_h
#include "BidiRun.h"
#include "RenderBlock.h"
#include "RenderText.h"
#include <wtf/AlwaysInline.h>
#include <wtf/StdLibExtras.h>
namespace WebCore {
class InlineIterator {
public:
InlineIterator()
: block(0)
, obj(0)
, pos(0)
, nextBreakablePosition(-1)
{
}
InlineIterator(RenderBlock* b, RenderObject* o, unsigned p)
: block(b)
, obj(o)
, pos(p)
, nextBreakablePosition(-1)
{
}
void increment(InlineBidiResolver* resolver = 0);
bool atEnd() const;
UChar current() const;
WTF::Unicode::Direction direction() const;
RenderBlock* block;
RenderObject* obj;
unsigned pos;
int nextBreakablePosition;
};
inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
{
return it1.pos == it2.pos && it1.obj == it2.obj;
}
inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
{
return it1.pos != it2.pos || it1.obj != it2.obj;
}
static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
{
RenderObject* next = 0;
bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
bool endOfInline = false;
while (current) {
next = 0;
if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) {
next = current->firstChild();
if (next && resolver && next->isRenderInline()) {
EUnicodeBidi ub = next->style()->unicodeBidi();
if (ub != UBNormal) {
TextDirection dir = next->style()->direction();
WTF::Unicode::Direction d = (ub == Embed
? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding)
: (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride));
resolver->embed(d);
}
}
}
if (!next) {
if (!skipInlines && !oldEndOfInline && current->isRenderInline()) {
next = current;
endOfInline = true;
break;
}
while (current && current != block) {
if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal)
resolver->embed(WTF::Unicode::PopDirectionalFormat);
next = current->nextSibling();
if (next) {
if (resolver && next->isRenderInline()) {
EUnicodeBidi ub = next->style()->unicodeBidi();
if (ub != UBNormal) {
TextDirection dir = next->style()->direction();
WTF::Unicode::Direction d = (ub == Embed
? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding: WTF::Unicode::LeftToRightEmbedding)
: (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride));
resolver->embed(d);
}
}
break;
}
current = current->parent();
if (!skipInlines && current && current != block && current->isRenderInline()) {
next = current;
endOfInline = true;
break;
}
}
}
if (!next)
break;
if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned()
|| ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
&& next->isRenderInline()))
break;
current = next;
}
if (endOfInlinePtr)
*endOfInlinePtr = endOfInline;
return next;
}
static inline RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true)
{
if (!block->firstChild())
return 0;
RenderObject* o = block->firstChild();
if (o->isRenderInline()) {
if (resolver) {
EUnicodeBidi ub = o->style()->unicodeBidi();
if (ub != UBNormal) {
TextDirection dir = o->style()->direction();
WTF::Unicode::Direction d = (ub == Embed
? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding)
: (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride));
resolver->embed(d);
}
}
if (skipInlines && o->firstChild())
o = bidiNext(block, o, resolver, skipInlines);
else {
// Never skip empty inlines.
if (resolver)
resolver->commitExplicitEmbedding();
return o;
}
}
if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
o = bidiNext(block, o, resolver, skipInlines);
if (resolver)
resolver->commitExplicitEmbedding();
return o;
}
inline void InlineIterator::increment(InlineBidiResolver* resolver)
{
if (!obj)
return;
if (obj->isText()) {
pos++;
if (pos >= toRenderText(obj)->textLength()) {
obj = bidiNext(block, obj, resolver);
pos = 0;
nextBreakablePosition = -1;
}
} else {
obj = bidiNext(block, obj, resolver);
pos = 0;
nextBreakablePosition = -1;
}
}
inline bool InlineIterator::atEnd() const
{
return !obj;
}
inline UChar InlineIterator::current() const
{
if (!obj || !obj->isText())
return 0;
RenderText* text = toRenderText(obj);
if (pos >= text->textLength())
return 0;
return text->characters()[pos];
}
ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const
{
if (UChar c = current())
return WTF::Unicode::direction(c);
if (obj && obj->isListMarker())
return obj->style()->direction() == LTR ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
return WTF::Unicode::OtherNeutral;
}
template<>
inline void InlineBidiResolver::increment()
{
current.increment(this);
}
template <>
inline void InlineBidiResolver::appendRun()
{
if (!emptyRun && !eor.atEnd()) {
int start = sor.pos;
RenderObject *obj = sor.obj;
while (obj && obj != eor.obj && obj != endOfLine.obj) {
RenderBlock::appendRunsForObject(start, obj->length(), obj, *this);
start = 0;
obj = bidiNext(sor.block, obj);
}
if (obj) {
unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX;
if (obj == endOfLine.obj && endOfLine.pos <= pos) {
reachedEndOfLine = true;
pos = endOfLine.pos;
}
// It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
int end = obj->length() ? pos+1 : 0;
RenderBlock::appendRunsForObject(start, end, obj, *this);
}
eor.increment();
sor = eor;
}
m_direction = WTF::Unicode::OtherNeutral;
m_status.eor = WTF::Unicode::OtherNeutral;
}
}
#endif // InlineIterator_h