Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebCore / rendering / InlineIterator.h
index 2082dae..fb9e5af 100644 (file)
  *
  */
 
-#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_obj(0)
-        , m_pos(0)
-        , m_nextBreakablePosition(-1)
     {
     }
 
     InlineIterator(RenderElement* root, RenderObject* o, unsigned p)
         : m_root(root)
-        , m_obj(o)
+        , m_renderer(o)
         , m_pos(p)
-        , m_nextBreakablePosition(-1)
+        , 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_obj = object;
-        m_pos = offset;
-        m_nextBreakablePosition = nextBreak;
+        setRenderer(&object);
+        setOffset(offset);
+        setNextBreakablePosition(nextBreak);
     }
 
-    RenderObject* object() const { return m_obj; }
+    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; }
+    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_obj && m_obj->preservesNewline() && m_obj->isText() && toRenderText(m_obj)->textLength()
-            && toRenderText(m_obj)->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_obj && m_obj->isBR()) || atTextParagraphSeparator();
+        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;
+    UChar characterAt(unsigned) const;
 
-    // FIXME: These should be private.
-public:
-    RenderObject* m_obj;
-    unsigned m_pos;
-    int m_nextBreakablePosition;
+    UCharDirection surrogateTextDirection(UChar currentCodeUnit) const;
+
+    RenderElement* m_root { nullptr };
+    RenderObject* m_renderer { nullptr };
+
+    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)
 {
-    return it1.m_pos == it2.m_pos && it1.m_obj == it2.m_obj;
+    return it1.offset() == it2.offset() && it1.renderer() == it2.renderer();
 }
 
 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
 {
-    return it1.m_pos != it2.m_pos || it1.m_obj != it2.m_obj;
+    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;
+        return direction == TextDirection::RTL ? U_RIGHT_TO_LEFT_EMBEDDING : U_LEFT_TO_RIGHT_EMBEDDING;
+    return direction == TextDirection::RTL ? U_RIGHT_TO_LEFT_OVERRIDE : U_LEFT_TO_RIGHT_OVERRIDE;
 }
 
 template <class Observer>
@@ -178,15 +206,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;
@@ -196,24 +224,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;
@@ -229,7 +257,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;
@@ -241,7 +269,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;
     }
@@ -262,41 +290,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.
@@ -313,10 +341,22 @@ static inline RenderObject* bidiFirstIncludingEmptyInlines(RenderElement& root)
 
 inline void InlineIterator::fastIncrementInTextNode()
 {
-    ASSERT(m_obj);
-    ASSERT(m_obj->isText());
-    ASSERT(m_pos <= toRenderText(m_obj)->textLength());
-    m_pos++;
+    ASSERT(m_renderer);
+    ASSERT(m_pos <= downcast<RenderText>(*m_renderer).text().length());
+    ++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
@@ -325,7 +365,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.
@@ -352,32 +392,41 @@ private:
 
 inline void InlineIterator::increment(InlineBidiResolver* resolver)
 {
-    if (!m_obj)
+    if (!m_renderer)
         return;
-    if (m_obj->isText()) {
+    if (is<RenderText>(*m_renderer)) {
         fastIncrementInTextNode();
-        if (m_pos < toRenderText(m_obj)->textLength())
+        if (m_pos < downcast<RenderText>(*m_renderer).text().length())
             return;
     }
-    // bidiNext can return 0, so use moveTo instead of moveToStartOf
-    moveTo(bidiNextSkippingEmptyInlines(*m_root, m_obj, 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
 {
-    return !m_obj;
+    return !m_renderer;
 }
 
 inline UChar InlineIterator::characterAt(unsigned index) const
 {
-    if (!m_obj || !m_obj->isText())
+    if (!is<RenderText>(m_renderer))
         return 0;
 
-    RenderText* text = toRenderText(m_obj);
-    if (index >= text->textLength())
-        return 0;
-
-    return text->characterAt(index);
+    return downcast<RenderText>(*m_renderer).characterAt(index);
 }
 
 inline UChar InlineIterator::current() const
@@ -387,75 +436,69 @@ 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_obj && m_obj->isListMarker())
-        return m_obj->style().isLeftToRightDirection() ? U_LEFT_TO_RIGHT : U_RIGHT_TO_LEFT;
+    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);
 }
 
-static inline bool isIsolatedInline(RenderObject* object)
+static inline bool isIsolatedInline(RenderObject& object)
 {
-    ASSERT(object);
-    return object->isRenderInline() && isIsolated(object->style().unicodeBidi());
+    return object.isRenderInline() && isIsolated(object.style().unicodeBidi());
 }
 
-static inline RenderObject* containingIsolate(RenderObject* object, RenderObject* root)
+static inline RenderObject* highestContainingIsolateWithinRoot(RenderObject& initialObject, RenderObject* root)
 {
-    ASSERT(object);
-    RenderObject* containingIsolateObject = 0;
-    while (object && object != root) {
-        if (containingIsolateObject && !isIsolatedInline(object))
-            break;
-
-        if (isIsolatedInline(object))
+    RenderObject* containingIsolateObject = nullptr;
+    for (RenderObject* object = &initialObject; object && object != root; object = object->parent()) {
+        if (isIsolatedInline(*object))
             containingIsolateObject = object;
-
-        object = object->parent();
     }
     return containingIsolateObject;
 }
 
 static inline unsigned numberOfIsolateAncestors(const InlineIterator& iter)
 {
-    RenderObject* object = iter.object();
-    if (!object)
-        return 0;
     unsigned count = 0;
-    while (object && object != iter.root()) {
-        if (isIsolatedInline(object))
+    typedef RenderObject* RenderObjectPtr;
+    for (RenderObjectPtr object = iter.renderer(), root = iter.root(); object && object != root; object = object->parent()) {
+        if (isIsolatedInline(*object))
             count++;
-        object = object->parent();
     }
     return count;
 }
 
 // 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)
 {
-    ASSERT(obj);
-    BidiRun* isolatedRun = new (obj->renderArena()) 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 {
@@ -480,26 +523,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].object() == obj) {
-            midpointState.betweenMidpoints = false;
-            ++midpointState.currentMidpoint;
+        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:
@@ -507,37 +545,37 @@ 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.
         // Initialize our state depending on if we're starting in the middle of such an inline.
         // FIXME: Could this initialize from this->inIsolate() instead of walking up the render tree?
         IsolateTracker isolateTracker(numberOfIsolateAncestors(m_sor));
-        int start = m_sor.m_pos;
-        RenderObject* obj = m_sor.m_obj;
-        while (obj && obj != m_eor.m_obj && obj != endOfLine.m_obj) {
+        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);
+                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);
         }
         if (obj) {
-            unsigned pos = obj == m_eor.m_obj ? m_eor.m_pos : UINT_MAX;
-            if (obj == endOfLine.m_obj && endOfLine.m_pos <= pos) {
+            unsigned pos = obj == m_eor.renderer() ? m_eor.offset() : UINT_MAX;
+            if (obj == endOfLine.renderer() && endOfLine.offset() <= pos) {
                 m_reachedEndOfLine = true;
-                pos = endOfLine.m_pos;
+                pos = endOfLine.offset();
             }
             // 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();
@@ -548,6 +586,11 @@ inline void InlineBidiResolver::appendRun()
     m_status.eor = U_OTHER_NEUTRAL;
 }
 
+template<>
+inline bool InlineBidiResolver::needsContinuePastEndInternal() const
+{
+    // We don't collect runs beyond the endOfLine renderer. Stop traversing when the iterator moves to the next renderer to prevent O(n^2).
+    return m_current.renderer() == endOfLine.renderer();
 }
 
-#endif // InlineIterator_h
+} // namespace WebCore