diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/InlineIterator.h | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/InlineIterator.h')
-rw-r--r-- | Source/WebCore/rendering/InlineIterator.h | 244 |
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 |