#ifndef InlineIterator_h
#define InlineIterator_h
#include "BidiRun.h"
#include "RenderBlockFlow.h"
#include "RenderInline.h"
#include "RenderText.h"
#include <wtf/StdLibExtras.h>
namespace WebCore {
class InlineIterator {
public:
InlineIterator()
: m_root(0)
, m_renderer(0)
, m_nextBreakablePosition(-1)
, m_pos(0)
, m_refersToEndOfPreviousNode(false)
{
}
InlineIterator(RenderElement* root, RenderObject* o, unsigned p)
: m_root(root)
, m_renderer(o)
, m_nextBreakablePosition(-1)
, m_pos(p)
, m_refersToEndOfPreviousNode(false)
{
}
void clear() { moveTo(0, 0); }
void moveToStartOf(RenderObject* object)
{
moveTo(object, 0);
}
void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1)
{
setRenderer(object);
setOffset(offset);
setNextBreakablePosition(nextBreak);
}
RenderObject* renderer() const { return m_renderer; }
void setRenderer(RenderObject* renderer) { m_renderer = renderer; }
unsigned offset() const { return m_pos; }
void setOffset(unsigned position);
RenderElement* root() const { return m_root; }
int nextBreakablePosition() const { return m_nextBreakablePosition; }
void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; }
bool refersToEndOfPreviousNode() const { return m_refersToEndOfPreviousNode; }
void setRefersToEndOfPreviousNode();
void fastIncrementInTextNode();
void increment(InlineBidiResolver* = 0);
void fastDecrement();
bool atEnd() const;
inline bool atTextParagraphSeparator()
{
return m_renderer && m_renderer->preservesNewline() && m_renderer->isText() && toRenderText(m_renderer)->textLength()
&& toRenderText(m_renderer)->characterAt(m_pos) == '\n';
}
inline bool atParagraphSeparator()
{
return (m_renderer && m_renderer->isBR()) || atTextParagraphSeparator();
}
UChar characterAt(unsigned) const;
UChar current() const;
UChar previousInSameNode() const;
ALWAYS_INLINE UCharDirection direction() const;
private:
RenderElement* m_root;
RenderObject* m_renderer;
int m_nextBreakablePosition;
unsigned m_pos;
bool m_refersToEndOfPreviousNode;
};
inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
{
return it1.offset() == it2.offset() && it1.renderer() == it2.renderer();
}
inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
{
return it1.offset() != it2.offset() || it1.renderer() != it2.renderer();
}
static inline UCharDirection embedCharFromDirection(TextDirection direction, EUnicodeBidi unicodeBidi)
{
using namespace WTF::Unicode;
if (unicodeBidi == Embed)
return direction == RTL ? U_RIGHT_TO_LEFT_EMBEDDING : U_LEFT_TO_RIGHT_EMBEDDING;
return direction == RTL ? U_RIGHT_TO_LEFT_OVERRIDE : U_LEFT_TO_RIGHT_OVERRIDE;
}
template <class Observer>
static inline void notifyObserverEnteredObject(Observer* observer, RenderObject* object)
{
if (!observer || !object || !object->isRenderInline())
return;
const RenderStyle& style = object->style();
EUnicodeBidi unicodeBidi = style.unicodeBidi();
if (unicodeBidi == UBNormal) {
return;
}
if (isIsolated(unicodeBidi)) {
observer->commitExplicitEmbedding();
observer->enterIsolate();
return;
}
if (!observer->inIsolate())
observer->embed(embedCharFromDirection(style.direction(), unicodeBidi), FromStyleOrDOM);
}
template <class Observer>
static inline void notifyObserverWillExitObject(Observer* observer, RenderObject* object)
{
if (!observer || !object || !object->isRenderInline())
return;
EUnicodeBidi unicodeBidi = object->style().unicodeBidi();
if (unicodeBidi == UBNormal)
return; if (isIsolated(unicodeBidi)) {
observer->exitIsolate();
return;
}
if (!observer->inIsolate())
observer->embed(U_POP_DIRECTIONAL_FORMAT, FromStyleOrDOM);
}
static inline bool isIteratorTarget(RenderObject* object)
{
ASSERT(object); return object->isTextOrLineBreak() || object->isFloating() || object->isOutOfFlowPositioned() || object->isReplaced();
}
enum EmptyInlineBehavior {
SkipEmptyInlines,
IncludeEmptyInlines,
};
static bool isEmptyInline(const RenderInline& renderer)
{
for (RenderObject* curr = renderer.firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isFloatingOrOutOfFlowPositioned())
continue;
if (curr->isText()) {
if (!toRenderText(curr)->isAllCollapsibleWhitespace())
return false;
continue;
}
if (!curr->isRenderInline() || !isEmptyInline(toRenderInline(*curr)))
return false;
}
return true;
}
template <class Observer>
static inline RenderObject* bidiNextShared(RenderElement& root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0)
{
RenderObject* next = 0;
bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
bool endOfInline = false;
while (current) {
next = 0;
if (!oldEndOfInline && !isIteratorTarget(current)) {
next = toRenderElement(current)->firstChild();
notifyObserverEnteredObject(observer, next);
}
if (!next) {
if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && current->isRenderInline()) {
next = current;
endOfInline = true;
break;
}
while (current && current != &root) {
notifyObserverWillExitObject(observer, current);
next = current->nextSibling();
if (next) {
notifyObserverEnteredObject(observer, next);
break;
}
current = current->parent();
if (emptyInlineBehavior == IncludeEmptyInlines && current && current != &root && current->isRenderInline()) {
next = current;
endOfInline = true;
break;
}
}
}
if (!next)
break;
if (isIteratorTarget(next)
|| (next->isRenderInline() && (emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(toRenderInline(*next)))))
break;
current = next;
}
if (endOfInlinePtr)
*endOfInlinePtr = endOfInline;
return next;
}
template <class Observer>
static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement& root, RenderObject* current, Observer* observer)
{
return bidiNextShared(root, current, observer, SkipEmptyInlines);
}
static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement& root, RenderObject* current)
{
InlineBidiResolver* observer = 0;
return bidiNextSkippingEmptyInlines(root, current, observer);
}
static inline RenderObject* bidiNextIncludingEmptyInlines(RenderElement& root, RenderObject* current, bool* endOfInlinePtr = 0)
{
InlineBidiResolver* observer = 0; return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr);
}
static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderElement& root, InlineBidiResolver* resolver = 0)
{
RenderObject* o = root.firstChild();
if (!o)
return nullptr;
if (o->isRenderInline()) {
notifyObserverEnteredObject(resolver, o);
if (!isEmptyInline(toRenderInline(*o)))
o = bidiNextSkippingEmptyInlines(root, o, resolver);
else {
if (resolver)
resolver->commitExplicitEmbedding();
return o;
}
}
if (o && !isIteratorTarget(o))
o = bidiNextSkippingEmptyInlines(root, o, resolver);
if (resolver)
resolver->commitExplicitEmbedding();
return o;
}
static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderElement& root)
{
RenderObject* o = root.firstChild();
if (!o || o->isRenderInline() || isIteratorTarget(o))
return o;
return bidiNextIncludingEmptyInlines(root, o);
}
inline void InlineIterator::fastIncrementInTextNode()
{
ASSERT(m_renderer);
ASSERT(m_renderer->isText());
ASSERT(m_pos <= toRenderText(m_renderer)->textLength());
m_pos++;
}
inline void InlineIterator::setOffset(unsigned position)
{
ASSERT(position <= UINT_MAX - 10); m_pos = position;
}
inline void InlineIterator::setRefersToEndOfPreviousNode()
{
ASSERT(!m_pos);
ASSERT(!m_refersToEndOfPreviousNode);
m_refersToEndOfPreviousNode = true;
}
class InlineWalker {
public:
InlineWalker(RenderElement& root)
: m_root(root)
, m_current(0)
, m_atEndOfInline(false)
{
m_current = bidiFirstIncludingEmptyInlines(m_root);
}
RenderElement& root() { return m_root; }
RenderObject* current() { return m_current; }
bool atEndOfInline() { return m_atEndOfInline; }
bool atEnd() const { return !m_current; }
RenderObject* advance()
{
m_current = bidiNextIncludingEmptyInlines(m_root, m_current, &m_atEndOfInline);
return m_current;
}
private:
RenderElement& m_root;
RenderObject* m_current;
bool m_atEndOfInline;
};
inline void InlineIterator::increment(InlineBidiResolver* resolver)
{
if (!m_renderer)
return;
if (m_renderer->isText()) {
fastIncrementInTextNode();
if (m_pos < toRenderText(m_renderer)->textLength())
return;
}
moveTo(bidiNextSkippingEmptyInlines(*m_root, m_renderer, resolver), 0);
}
inline void InlineIterator::fastDecrement()
{
ASSERT(!refersToEndOfPreviousNode());
if (m_pos)
setOffset(m_pos - 1);
else
setRefersToEndOfPreviousNode();
}
inline bool InlineIterator::atEnd() const
{
return !m_renderer;
}
inline UChar InlineIterator::characterAt(unsigned index) const
{
if (!m_renderer || !m_renderer->isText())
return 0;
return toRenderText(m_renderer)->characterAt(index);
}
inline UChar InlineIterator::current() const
{
return characterAt(m_pos);
}
inline UChar InlineIterator::previousInSameNode() const
{
if (!m_pos)
return 0;
return characterAt(m_pos - 1);
}
ALWAYS_INLINE UCharDirection InlineIterator::direction() const
{
if (UChar character = current())
return u_charDirection(character);
if (m_renderer && m_renderer->isListMarker())
return m_renderer->style().isLeftToRightDirection() ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
return U_OTHER_NEUTRAL;
}
template<>
inline void InlineBidiResolver::increment()
{
m_current.increment(this);
}
static inline bool isIsolatedInline(RenderObject& object)
{
return object.isRenderInline() && isIsolated(object.style().unicodeBidi());
}
static inline RenderObject* highestContainingIsolateWithinRoot(RenderObject& initialObject, RenderObject* root)
{
RenderObject* containingIsolateObject = nullptr;
for (RenderObject* object = &initialObject; object && object != root; object = object->parent()) {
if (isIsolatedInline(*object))
containingIsolateObject = object;
}
return containingIsolateObject;
}
static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter)
{
unsigned count = 0;
typedef RenderObject* RenderObjectPtr;
for (RenderObjectPtr object = iter.renderer(), root = iter.root(); object && object != root; object = object->parent()) {
if (isIsolatedInline(*object))
count++;
}
return count;
}
static inline void addPlaceholderRunForIsolatedInline(InlineBidiResolver& resolver, RenderObject& obj, unsigned pos)
{
BidiRun* isolatedRun = new BidiRun(pos, 0, obj, resolver.context(), resolver.dir());
resolver.runs().addRun(isolatedRun);
resolver.isolatedRuns().append(isolatedRun);
}
class IsolateTracker {
public:
explicit IsolateTracker(unsigned nestedIsolateCount)
: m_nestedIsolateCount(nestedIsolateCount)
, m_haveAddedFakeRunForRootIsolate(false)
{
}
void enterIsolate() { m_nestedIsolateCount++; }
void exitIsolate()
{
ASSERT(m_nestedIsolateCount >= 1);
m_nestedIsolateCount--;
if (!inIsolate())
m_haveAddedFakeRunForRootIsolate = false;
}
bool inIsolate() const { return m_nestedIsolateCount; }
void embed(UCharDirection, BidiEmbeddingSource) { }
void commitExplicitEmbedding() { }
void addFakeRunIfNecessary(RenderObject& obj, unsigned pos, InlineBidiResolver& resolver)
{
if (m_haveAddedFakeRunForRootIsolate || RenderBlock::shouldSkipCreatingRunsForObject(&obj))
return;
m_haveAddedFakeRunForRootIsolate = true;
addPlaceholderRunForIsolatedInline(resolver, obj, pos);
MidpointState<InlineIterator>& midpointState = resolver.midpointState();
if (midpointState.betweenMidpoints() && midpointState.midpoints()[midpointState.currentMidpoint()].renderer() == &obj) {
midpointState.setBetweenMidpoints(false);
midpointState.incrementCurrentMidpoint();
}
}
private:
unsigned m_nestedIsolateCount;
bool m_haveAddedFakeRunForRootIsolate;
};
template <>
inline void InlineBidiResolver::appendRun()
{
if (!m_emptyRun && !m_eor.atEnd() && !m_reachedEndOfLine) {
IsolateTracker isolateTracker(numberOfIsolateAncestors(m_sor));
int start = m_sor.offset();
RenderObject* obj = m_sor.renderer();
while (obj && obj != m_eor.renderer() && obj != endOfLine.renderer()) {
if (isolateTracker.inIsolate())
isolateTracker.addFakeRunIfNecessary(*obj, start, *this);
else
RenderBlockFlow::appendRunsForObject(m_runs, start, obj->length(), obj, *this);
start = 0;
obj = bidiNextSkippingEmptyInlines(*m_sor.root(), obj, &isolateTracker);
}
if (obj) {
unsigned pos = obj == m_eor.renderer() ? m_eor.offset() : UINT_MAX;
if (obj == endOfLine.renderer() && endOfLine.offset() <= pos) {
m_reachedEndOfLine = true;
pos = endOfLine.offset();
}
int end = obj->length() ? pos + 1 : 0;
if (isolateTracker.inIsolate())
isolateTracker.addFakeRunIfNecessary(*obj, start, *this);
else
RenderBlockFlow::appendRunsForObject(m_runs, start, end, obj, *this);
}
m_eor.increment();
m_sor = m_eor;
}
m_direction = U_OTHER_NEUTRAL;
m_status.eor = U_OTHER_NEUTRAL;
}
}
#endif // InlineIterator_h