summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/InlineIterator.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/InlineIterator.h')
-rw-r--r--Source/WebCore/rendering/InlineIterator.h244
1 files changed, 147 insertions, 97 deletions
diff --git a/Source/WebCore/rendering/InlineIterator.h b/Source/WebCore/rendering/InlineIterator.h
index 3c6d726f0..f366b3cd3 100644
--- a/Source/WebCore/rendering/InlineIterator.h
+++ b/Source/WebCore/rendering/InlineIterator.h
@@ -20,86 +20,113 @@
*
*/
-#ifndef InlineIterator_h
-#define InlineIterator_h
+#pragma once
#include "BidiRun.h"
#include "RenderBlockFlow.h"
+#include "RenderChildIterator.h"
#include "RenderInline.h"
#include "RenderText.h"
#include <wtf/StdLibExtras.h>
namespace WebCore {
+struct BidiIsolatedRun {
+ BidiIsolatedRun(RenderObject& object, unsigned position, RenderElement& root, BidiRun& runToReplace)
+ : object(object)
+ , root(root)
+ , runToReplace(runToReplace)
+ , position(position)
+ {
+ }
+
+ RenderObject& object;
+ RenderElement& root;
+ BidiRun& runToReplace;
+ unsigned position;
+};
+
// This class is used to RenderInline subtrees, stepping by character within the
// text children. InlineIterator will use bidiNext to find the next RenderText
// optionally notifying a BidiResolver every time it steps into/out of a RenderInline.
class InlineIterator {
public:
InlineIterator()
- : m_root(0)
- , m_renderer(0)
- , m_nextBreakablePosition(-1)
- , m_pos(0)
{
}
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)
+ void clear()
+ {
+ setRenderer(nullptr);
+ setOffset(0);
+ setNextBreakablePosition(std::numeric_limits<unsigned>::max());
+ }
+ void moveToStartOf(RenderObject& object)
{
moveTo(object, 0);
}
- void moveTo(RenderObject* object, unsigned offset, int nextBreak = -1)
+ void moveTo(RenderObject& object, unsigned offset, std::optional<unsigned> nextBreak = std::optional<unsigned>())
{
- m_renderer = object;
- m_pos = offset;
- m_nextBreakablePosition = nextBreak;
+ 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) { m_pos = position; }
+ void setOffset(unsigned position);
RenderElement* root() const { return m_root; }
- int nextBreakablePosition() const { return m_nextBreakablePosition; }
- void setNextBreakablePosition(int position) { m_nextBreakablePosition = position; }
+ std::optional<unsigned> nextBreakablePosition() const { return m_nextBreakablePosition; }
+ void setNextBreakablePosition(std::optional<unsigned> position) { m_nextBreakablePosition = position; }
+ bool refersToEndOfPreviousNode() const { return m_refersToEndOfPreviousNode; }
+ void setRefersToEndOfPreviousNode();
void fastIncrementInTextNode();
- void increment(InlineBidiResolver* = 0);
+ void increment(InlineBidiResolver* = nullptr);
+ void fastDecrement();
bool atEnd() const;
- inline bool atTextParagraphSeparator()
+ bool atTextParagraphSeparator() const
{
- return m_renderer && m_renderer->preservesNewline() && m_renderer->isText() && toRenderText(m_renderer)->textLength()
- && toRenderText(m_renderer)->characterAt(m_pos) == '\n';
+ return is<RenderText>(m_renderer) && m_renderer->preservesNewline() && downcast<RenderText>(*m_renderer).characterAt(m_pos) == '\n';
}
- inline bool atParagraphSeparator()
+ bool atParagraphSeparator() const
{
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;
+ UChar characterAt(unsigned) const;
+
+ UCharDirection surrogateTextDirection(UChar currentCodeUnit) const;
+
+ RenderElement* m_root { nullptr };
+ RenderObject* m_renderer { nullptr };
- int m_nextBreakablePosition;
- unsigned m_pos;
+ std::optional<unsigned> m_nextBreakablePosition;
+ unsigned m_pos { 0 };
+
+ // There are a couple places where we want to decrement an InlineIterator.
+ // Usually this take the form of decrementing m_pos; however, m_pos might be 0.
+ // However, we shouldn't ever need to decrement an InlineIterator more than
+ // once, so rather than implementing a decrement() function which traverses
+ // nodes, we can simply keep track of this state and handle it.
+ bool m_refersToEndOfPreviousNode { false };
};
inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
@@ -180,15 +207,15 @@ enum EmptyInlineBehavior {
static bool isEmptyInline(const RenderInline& renderer)
{
- for (RenderObject* curr = renderer.firstChild(); curr; curr = curr->nextSibling()) {
- if (curr->isFloatingOrOutOfFlowPositioned())
+ for (auto& current : childrenOfType<RenderObject>(renderer)) {
+ if (current.isFloatingOrOutOfFlowPositioned())
continue;
- if (curr->isText()) {
- if (!toRenderText(curr)->isAllCollapsibleWhitespace())
+ if (is<RenderText>(current)) {
+ if (!downcast<RenderText>(current).isAllCollapsibleWhitespace())
return false;
continue;
}
- if (!curr->isRenderInline() || !isEmptyInline(toRenderInline(*curr)))
+ if (!is<RenderInline>(current) || !isEmptyInline(downcast<RenderInline>(current)))
return false;
}
return true;
@@ -198,24 +225,24 @@ static bool isEmptyInline(const RenderInline& renderer)
// This function will iterate over inlines within a block, optionally notifying
// a bidi resolver as it enters/exits inlines (so it can push/pop embedding levels).
template <class Observer>
-static inline RenderObject* bidiNextShared(RenderElement& root, RenderObject* current, Observer* observer = 0, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = 0)
+static inline RenderObject* bidiNextShared(RenderElement& root, RenderObject* current, Observer* observer = nullptr, EmptyInlineBehavior emptyInlineBehavior = SkipEmptyInlines, bool* endOfInlinePtr = nullptr)
{
- RenderObject* next = 0;
+ RenderObject* next = nullptr;
// oldEndOfInline denotes if when we last stopped iterating if we were at the end of an inline.
bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
bool endOfInline = false;
while (current) {
- next = 0;
+ next = nullptr;
if (!oldEndOfInline && !isIteratorTarget(current)) {
- next = toRenderElement(current)->firstChild();
+ next = downcast<RenderElement>(*current).firstChild();
notifyObserverEnteredObject(observer, next);
}
// We hit this when either current has no children, or when current is not a renderer we care about.
if (!next) {
// If it is a renderer we care about, and we're doing our inline-walk, return it.
- if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && current->isRenderInline()) {
+ if (emptyInlineBehavior == IncludeEmptyInlines && !oldEndOfInline && is<RenderInline>(*current)) {
next = current;
endOfInline = true;
break;
@@ -231,7 +258,7 @@ static inline RenderObject* bidiNextShared(RenderElement& root, RenderObject* cu
}
current = current->parent();
- if (emptyInlineBehavior == IncludeEmptyInlines && current && current != &root && current->isRenderInline()) {
+ if (emptyInlineBehavior == IncludeEmptyInlines && current && current != &root && is<RenderInline>(*current)) {
next = current;
endOfInline = true;
break;
@@ -243,7 +270,7 @@ static inline RenderObject* bidiNextShared(RenderElement& root, RenderObject* cu
break;
if (isIteratorTarget(next)
- || (next->isRenderInline() && (emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(toRenderInline(*next)))))
+ || (is<RenderInline>(*next) && (emptyInlineBehavior == IncludeEmptyInlines || isEmptyInline(downcast<RenderInline>(*next)))))
break;
current = next;
}
@@ -264,41 +291,41 @@ static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement& root, Re
// This makes callers cleaner as they don't have to specify a type for the observer when not providing one.
static inline RenderObject* bidiNextSkippingEmptyInlines(RenderElement& root, RenderObject* current)
{
- InlineBidiResolver* observer = 0;
+ InlineBidiResolver* observer = nullptr;
return bidiNextSkippingEmptyInlines(root, current, observer);
}
-static inline RenderObject* bidiNextIncludingEmptyInlines(RenderElement& root, RenderObject* current, bool* endOfInlinePtr = 0)
+static inline RenderObject* bidiNextIncludingEmptyInlines(RenderElement& root, RenderObject* current, bool* endOfInlinePtr = nullptr)
{
- InlineBidiResolver* observer = 0; // Callers who include empty inlines, never use an observer.
+ InlineBidiResolver* observer = nullptr; // Callers who include empty inlines, never use an observer.
return bidiNextShared(root, current, observer, IncludeEmptyInlines, endOfInlinePtr);
}
-static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderElement& root, InlineBidiResolver* resolver = 0)
+static inline RenderObject* bidiFirstSkippingEmptyInlines(RenderElement& root, InlineBidiResolver* resolver = nullptr)
{
- RenderObject* o = root.firstChild();
- if (!o)
+ RenderObject* renderer = root.firstChild();
+ if (!renderer)
return nullptr;
- if (o->isRenderInline()) {
- notifyObserverEnteredObject(resolver, o);
- if (!isEmptyInline(toRenderInline(*o)))
- o = bidiNextSkippingEmptyInlines(root, o, resolver);
+ if (is<RenderInline>(*renderer)) {
+ notifyObserverEnteredObject(resolver, renderer);
+ if (!isEmptyInline(downcast<RenderInline>(*renderer)))
+ renderer = bidiNextSkippingEmptyInlines(root, renderer, resolver);
else {
// Never skip empty inlines.
if (resolver)
resolver->commitExplicitEmbedding();
- return o;
+ return renderer;
}
}
// FIXME: Unify this with the bidiNext call above.
- if (o && !isIteratorTarget(o))
- o = bidiNextSkippingEmptyInlines(root, o, resolver);
+ if (renderer && !isIteratorTarget(renderer))
+ renderer = bidiNextSkippingEmptyInlines(root, renderer, resolver);
if (resolver)
resolver->commitExplicitEmbedding();
- return o;
+ return renderer;
}
// FIXME: This method needs to be renamed when bidiNext finds a good name.
@@ -316,9 +343,21 @@ static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderElement& root)
inline void InlineIterator::fastIncrementInTextNode()
{
ASSERT(m_renderer);
- ASSERT(m_renderer->isText());
- ASSERT(m_pos <= toRenderText(m_renderer)->textLength());
- m_pos++;
+ ASSERT(m_pos <= downcast<RenderText>(*m_renderer).textLength());
+ ++m_pos;
+}
+
+inline void InlineIterator::setOffset(unsigned position)
+{
+ ASSERT(position <= UINT_MAX - 10); // Sanity check
+ m_pos = position;
+}
+
+inline void InlineIterator::setRefersToEndOfPreviousNode()
+{
+ ASSERT(!m_pos);
+ ASSERT(!m_refersToEndOfPreviousNode);
+ m_refersToEndOfPreviousNode = true;
}
// FIXME: This is used by RenderBlock for simplified layout, and has nothing to do with bidi
@@ -327,7 +366,7 @@ class InlineWalker {
public:
InlineWalker(RenderElement& root)
: m_root(root)
- , m_current(0)
+ , m_current(nullptr)
, m_atEndOfInline(false)
{
// FIXME: This class should be taught how to do the SkipEmptyInlines codepath as well.
@@ -356,13 +395,26 @@ inline void InlineIterator::increment(InlineBidiResolver* resolver)
{
if (!m_renderer)
return;
- if (m_renderer->isText()) {
+ if (is<RenderText>(*m_renderer)) {
fastIncrementInTextNode();
- if (m_pos < toRenderText(m_renderer)->textLength())
+ if (m_pos < downcast<RenderText>(*m_renderer).textLength())
return;
}
- // bidiNext can return 0, so use moveTo instead of moveToStartOf
- moveTo(bidiNextSkippingEmptyInlines(*m_root, m_renderer, resolver), 0);
+ // bidiNext can return nullptr
+ RenderObject* bidiNext = bidiNextSkippingEmptyInlines(*m_root, m_renderer, resolver);
+ if (bidiNext)
+ moveToStartOf(*bidiNext);
+ else
+ clear();
+}
+
+inline void InlineIterator::fastDecrement()
+{
+ ASSERT(!refersToEndOfPreviousNode());
+ if (m_pos)
+ setOffset(m_pos - 1);
+ else
+ setRefersToEndOfPreviousNode();
}
inline bool InlineIterator::atEnd() const
@@ -372,10 +424,10 @@ inline bool InlineIterator::atEnd() const
inline UChar InlineIterator::characterAt(unsigned index) const
{
- if (!m_renderer || !m_renderer->isText())
+ if (!is<RenderText>(m_renderer))
return 0;
- return toRenderText(m_renderer)->characterAt(index);
+ return downcast<RenderText>(*m_renderer).characterAt(index);
}
inline UChar InlineIterator::current() const
@@ -385,25 +437,29 @@ inline UChar InlineIterator::current() const
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 (UNLIKELY(!m_renderer))
+ return U_OTHER_NEUTRAL;
+
+ if (LIKELY(is<RenderText>(*m_renderer))) {
+ UChar codeUnit = downcast<RenderText>(*m_renderer).characterAt(m_pos);
+ if (LIKELY(U16_IS_SINGLE(codeUnit)))
+ return u_charDirection(codeUnit);
+ return surrogateTextDirection(codeUnit);
+ }
- if (m_renderer && m_renderer->isListMarker())
+ if (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()
+inline void InlineBidiResolver::incrementInternal()
{
m_current.increment(this);
}
@@ -436,13 +492,14 @@ static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter)
// FIXME: This belongs on InlineBidiResolver, except it's a template specialization
// of BidiResolver which knows nothing about RenderObjects.
-static inline void addPlaceholderRunForIsolatedInline(InlineBidiResolver& resolver, RenderObject& obj, unsigned pos)
+static inline void addPlaceholderRunForIsolatedInline(InlineBidiResolver& resolver, RenderObject& obj, unsigned pos, RenderElement& root)
{
- BidiRun* isolatedRun = new BidiRun(pos, 0, obj, resolver.context(), resolver.dir());
- resolver.runs().addRun(isolatedRun);
+ std::unique_ptr<BidiRun> isolatedRun = std::make_unique<BidiRun>(pos, pos, obj, resolver.context(), resolver.dir());
// FIXME: isolatedRuns() could be a hash of object->run and then we could cheaply
// ASSERT here that we didn't create multiple objects for the same inline.
- resolver.isolatedRuns().append(isolatedRun);
+ resolver.setWhitespaceCollapsingTransitionForIsolatedRun(*isolatedRun, resolver.whitespaceCollapsingState().currentTransition());
+ resolver.isolatedRuns().append(BidiIsolatedRun(obj, pos, root, *isolatedRun));
+ resolver.runs().appendRun(WTFMove(isolatedRun));
}
class IsolateTracker {
@@ -467,26 +524,21 @@ public:
void embed(UCharDirection, BidiEmbeddingSource) { }
void commitExplicitEmbedding() { }
- void addFakeRunIfNecessary(RenderObject& obj, unsigned pos, InlineBidiResolver& resolver)
+ void addFakeRunIfNecessary(RenderObject& obj, unsigned pos, unsigned end, RenderElement& root, InlineBidiResolver& resolver)
{
// We only need to add a fake run for a given isolated span once during each call to createBidiRunsForLine.
// We'll be called for every span inside the isolated span so we just ignore subsequent calls.
// We also avoid creating a fake run until we hit a child that warrants one, e.g. we skip floats.
- if (m_haveAddedFakeRunForRootIsolate || RenderBlock::shouldSkipCreatingRunsForObject(&obj))
+ if (RenderBlock::shouldSkipCreatingRunsForObject(obj))
return;
- m_haveAddedFakeRunForRootIsolate = true;
- // obj and pos together denote a single position in the inline, from which the parsing of the isolate will start.
- // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the
- // isolate, when we call createBidiRunsForLine it will stop at whichever comes first.
- addPlaceholderRunForIsolatedInline(resolver, obj, pos);
- // FIXME: Inline isolates don't work properly with collapsing whitespace, see webkit.org/b/109624
- // For now, if we enter an isolate between midpoints, we increment our current midpoint or else
- // we'll leave the isolate and ignore the content that follows.
- MidpointState<InlineIterator>& midpointState = resolver.midpointState();
- if (midpointState.betweenMidpoints() && midpointState.midpoints()[midpointState.currentMidpoint()].renderer() == &obj) {
- midpointState.setBetweenMidpoints(false);
- midpointState.incrementCurrentMidpoint();
+ if (!m_haveAddedFakeRunForRootIsolate) {
+ // obj and pos together denote a single position in the inline, from which the parsing of the isolate will start.
+ // We don't need to mark the end of the run because this is implicit: it is either endOfLine or the end of the
+ // isolate, when we call createBidiRunsForLine it will stop at whichever comes first.
+ addPlaceholderRunForIsolatedInline(resolver, obj, pos, root);
}
+ m_haveAddedFakeRunForRootIsolate = true;
+ RenderBlockFlow::appendRunsForObject(nullptr, pos, end, obj, resolver);
}
private:
@@ -494,8 +546,8 @@ private:
bool m_haveAddedFakeRunForRootIsolate;
};
-template <>
-inline void InlineBidiResolver::appendRun()
+template<>
+inline void InlineBidiResolver::appendRunInternal()
{
if (!m_emptyRun && !m_eor.atEnd() && !m_reachedEndOfLine) {
// Keep track of when we enter/leave "unicode-bidi: isolate" inlines.
@@ -506,9 +558,9 @@ inline void InlineBidiResolver::appendRun()
RenderObject* obj = m_sor.renderer();
while (obj && obj != m_eor.renderer() && obj != endOfLine.renderer()) {
if (isolateTracker.inIsolate())
- isolateTracker.addFakeRunIfNecessary(*obj, start, *this);
+ isolateTracker.addFakeRunIfNecessary(*obj, start, obj->length(), *m_sor.root(), *this);
else
- RenderBlockFlow::appendRunsForObject(m_runs, start, obj->length(), obj, *this);
+ RenderBlockFlow::appendRunsForObject(&m_runs, start, obj->length(), *obj, *this);
// FIXME: start/obj should be an InlineIterator instead of two separate variables.
start = 0;
obj = bidiNextSkippingEmptyInlines(*m_sor.root(), obj, &isolateTracker);
@@ -522,9 +574,9 @@ inline void InlineBidiResolver::appendRun()
// 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;
if (isolateTracker.inIsolate())
- isolateTracker.addFakeRunIfNecessary(*obj, start, *this);
+ isolateTracker.addFakeRunIfNecessary(*obj, start, obj->length(), *m_sor.root(), *this);
else
- RenderBlockFlow::appendRunsForObject(m_runs, start, end, obj, *this);
+ RenderBlockFlow::appendRunsForObject(&m_runs, start, end, *obj, *this);
}
m_eor.increment();
@@ -535,6 +587,4 @@ inline void InlineBidiResolver::appendRun()
m_status.eor = U_OTHER_NEUTRAL;
}
-}
-
-#endif // InlineIterator_h
+} // namespace WebCore