Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / editing / TextIterator.cpp
index b3dbed1..1cc9a9d 100644 (file)
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
 #include "Document.h"
 #include "ExceptionCodePlaceholder.h"
-#include "Font.h"
+#include "FontCascade.h"
 #include "Frame.h"
 #include "HTMLElement.h"
 #include "HTMLNames.h"
+#include "HTMLParagraphElement.h"
 #include "HTMLTextFormControlElement.h"
 #include "InlineTextBox.h"
 #include "NodeTraversal.h"
@@ -43,6 +44,8 @@
 #include "RenderTextControl.h"
 #include "RenderTextFragment.h"
 #include "ShadowRoot.h"
+#include "SimpleLineLayout.h"
+#include "SimpleLineLayoutResolver.h"
 #include "TextBoundaries.h"
 #include "TextBreakIterator.h"
 #include "TextControlInnerElements.h"
@@ -50,6 +53,7 @@
 #include "VisibleUnits.h"
 #include "htmlediting.h"
 #include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
 #include <wtf/unicode/CharacterNames.h>
 
 #if !UCONFIG_NO_COLLATION
@@ -91,8 +95,10 @@ public:
 private:
     bool isBadMatch(const UChar*, size_t length) const;
     bool isWordStartMatch(size_t start, size_t length) const;
+    bool isWordEndMatch(size_t start, size_t length) const;
 
-    String m_target;
+    const String m_target;
+    const StringView::UpconvertedCharacters m_targetCharacters;
     FindOptions m_options;
 
     Vector<UChar> m_buffer;
@@ -101,7 +107,7 @@ private:
     bool m_atBreak;
     bool m_needsMoreContext;
 
-    bool m_targetRequiresKanaWorkaround;
+    const bool m_targetRequiresKanaWorkaround;
     Vector<UChar> m_normalizedTarget;
     mutable Vector<UChar> m_normalizedMatch;
 
@@ -190,7 +196,7 @@ static unsigned depthCrossingShadowBoundaries(Node& node)
 static Node* nextInPreOrderCrossingShadowBoundaries(Node& rangeEndContainer, int rangeEndOffset)
 {
     if (rangeEndOffset >= 0 && !rangeEndContainer.offsetInCharacters()) {
-        if (Node* next = rangeEndContainer.childNode(rangeEndOffset))
+        if (Node* next = rangeEndContainer.traverseToChildAt(rangeEndOffset))
             return next;
     }
     for (Node* node = &rangeEndContainer; node; node = node->parentOrShadowHostNode()) {
@@ -203,9 +209,9 @@ static Node* nextInPreOrderCrossingShadowBoundaries(Node& rangeEndContainer, int
 static inline bool fullyClipsContents(Node& node)
 {
     auto* renderer = node.renderer();
-    if (!renderer || !renderer->isBox() || !renderer->hasOverflowClip())
+    if (!is<RenderBox>(renderer) || !renderer->hasOverflowClip())
         return false;
-    return toRenderBox(renderer)->size().isEmpty();
+    return downcast<RenderBox>(*renderer).size().isEmpty();
 }
 
 static inline bool ignoresContainerClip(Node& node)
@@ -241,30 +247,19 @@ static void setUpFullyClippedStack(BitStack& stack, Node& node)
     ASSERT(stack.size() == 1 + depthCrossingShadowBoundaries(node));
 }
 
+// FIXME: editingIgnoresContent and isRendererReplacedElement try to do the same job.
+// It's not good to have both of them.
 bool isRendererReplacedElement(RenderObject* renderer)
 {
     if (!renderer)
         return false;
     
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-    if (renderer->isImage() || renderer->isMedia())
-        return true;
-    if (renderer->isWidget()) {
-        if (renderer->node() && renderer->node()->isElementNode()) {
-            Element* element = toElement(renderer->node());
-            if (element->hasTagName(videoTag) || !element->hasTagName(audioTag))
-                return false; // See <rdar://problem/6893793>.
-        }
-        return true;
-    }
-#else
     if (renderer->isImage() || renderer->isWidget() || renderer->isMedia())
         return true;
-#endif
 
-    if (renderer->node() && renderer->node()->isElementNode()) {
-        Element& element = toElement(*renderer->node());
-        if (element.isFormControlElement() || element.hasTagName(legendTag) || element.hasTagName(meterTag) || element.hasTagName(progressTag))
+    if (is<Element>(renderer->node())) {
+        Element& element = downcast<Element>(*renderer->node());
+        if (is<HTMLFormControlElement>(element) || is<HTMLLegendElement>(element) || is<HTMLMeterElement>(element) || is<HTMLProgressElement>(element))
             return true;
         if (equalIgnoringCase(element.fastGetAttribute(roleAttr), "img"))
             return true;
@@ -286,7 +281,7 @@ inline void TextIteratorCopyableText::reset()
 inline void TextIteratorCopyableText::set(String&& string)
 {
     m_singleCharacter = 0;
-    m_string = std::move(string);
+    m_string = WTFMove(string);
     m_offset = 0;
     m_length = m_string.length();
 }
@@ -298,7 +293,7 @@ inline void TextIteratorCopyableText::set(String&& string, unsigned offset, unsi
     ASSERT(length <= string.length() - offset);
 
     m_singleCharacter = 0;
-    m_string = std::move(string);
+    m_string = WTFMove(string);
     m_offset = offset;
     m_length = length;
 }
@@ -347,17 +342,14 @@ TextIterator::TextIterator(const Range* range, TextIteratorBehavior behavior)
 
     range->ownerDocument().updateLayoutIgnorePendingStylesheets();
 
-    m_startContainer = range->startContainer();
-    if (!m_startContainer)
-        return;
-    ASSERT(range->endContainer());
+    m_startContainer = &range->startContainer();
 
     // Callers should be handing us well-formed ranges. If we discover that this isn't
     // the case, we could consider changing this assertion to an early return.
     ASSERT(range->boundaryPointsValid());
 
     m_startOffset = range->startOffset();
-    m_endContainer = range->endContainer();
+    m_endContainer = &range->endContainer();
     m_endOffset = range->endOffset();
 
     // Set up the current node for processing.
@@ -456,7 +448,7 @@ void TextIterator::advance()
         if (!next) {
             next = m_node->nextSibling();
             if (!next) {
-                bool pastEnd = NodeTraversal::next(m_node) == m_pastEndNode;
+                bool pastEnd = NodeTraversal::next(*m_node) == m_pastEndNode;
                 Node* parentNode = m_node->parentOrShadowHostNode();
                 while (!next && parentNode) {
                     if ((pastEnd && parentNode == m_endContainer) || m_endContainer->isDescendantOf(parentNode))
@@ -497,8 +489,8 @@ static bool hasVisibleTextNode(RenderText& renderer)
 {
     if (renderer.style().visibility() == VISIBLE)
         return true;
-    if (renderer.isTextFragment()) {
-        if (auto firstLetter = toRenderTextFragment(renderer).firstLetter()) {
+    if (is<RenderTextFragment>(renderer)) {
+        if (auto firstLetter = downcast<RenderTextFragment>(renderer).firstLetter()) {
             if (firstLetter->style().visibility() == VISIBLE)
                 return true;
         }
@@ -506,15 +498,33 @@ static bool hasVisibleTextNode(RenderText& renderer)
     return false;
 }
 
+static unsigned textNodeOffsetInFlow(const Text& firstTextNodeInRange)
+{
+    // Calculate the text offset for simple lines.
+    RenderObject* renderer = firstTextNodeInRange.renderer();
+    if (!renderer)
+        return 0;
+    unsigned textOffset = 0;
+    for (renderer = renderer->previousSibling(); renderer; renderer = renderer->previousSibling()) {
+        if (is<RenderText>(renderer))
+            textOffset += downcast<RenderText>(renderer)->textLength();
+    }
+    return textOffset;
+}
+
+static bool isNewLineOrTabCharacter(UChar character)
+{
+    return character == '\n' || character == '\t';
+}
+
 bool TextIterator::handleTextNode()
 {
-    Text& textNode = toText(*m_node);
+    Text& textNode = downcast<Text>(*m_node);
 
     if (m_fullyClippedStack.top() && !(m_behavior & TextIteratorIgnoresStyleVisibility))
         return false;
 
     auto& renderer = *textNode.renderer();
-        
     m_lastTextNode = &textNode;
     String rendererText = renderer.text();
 
@@ -525,8 +535,8 @@ bool TextIterator::handleTextNode()
             emitCharacter(' ', textNode, nullptr, runStart, runStart);
             return false;
         }
-        if (!m_handledFirstLetter && renderer.isTextFragment() && !m_offset) {
-            handleTextNodeFirstLetter(toRenderTextFragment(renderer));
+        if (!m_handledFirstLetter && is<RenderTextFragment>(renderer) && !m_offset) {
+            handleTextNodeFirstLetter(downcast<RenderTextFragment>(renderer));
             if (m_firstLetterText) {
                 String firstLetter = m_firstLetterText->text();
                 emitText(textNode, *m_firstLetterText, m_offset, m_offset + firstLetter.length());
@@ -548,43 +558,89 @@ bool TextIterator::handleTextNode()
         return true;
     }
 
-    if (renderer.simpleLineLayout()) {
+    if (const auto* layout = renderer.simpleLineLayout()) {
         if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
             return true;
-        // This code aims to produce same results as handleTextBox() below so test results don't change. It does not make much logical sense.
-        unsigned end = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
-        unsigned runEnd = m_offset;
-        unsigned runStart = m_offset;
-        while (runEnd < end && (deprecatedIsCollapsibleWhitespace(rendererText[runEnd]) || rendererText[runEnd] == '\t'))
-            ++runEnd;
-        bool addSpaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && m_lastCharacter && !deprecatedIsCollapsibleWhitespace(m_lastCharacter);
-        if (runEnd > runStart || addSpaceForPrevious) {
-            if (runEnd == rendererText.length()) {
-                m_lastTextNodeEndedWithCollapsedSpace = true;
-                return true;
+        ASSERT(renderer.parent());
+        ASSERT(is<RenderBlockFlow>(*renderer.parent()));
+        const auto& blockFlow = downcast<RenderBlockFlow>(*renderer.parent());
+        // Use the simple layout runs to iterate over the text content.
+        bool isNewTextNode = m_previousSimpleTextNodeInFlow && m_previousSimpleTextNodeInFlow != &textNode;
+        // Simple line layout run positions are all absolute to the parent flow.
+        // Offsetting is required when multiple renderers are present.
+        m_accumulatedSimpleTextLengthInFlow += isNewTextNode ? m_previousSimpleTextNodeInFlow->renderer()->text()->length() : 0;
+        m_previousSimpleTextNodeInFlow = &textNode;
+
+        unsigned endPosition = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
+        if (!m_flowRunResolverCache || &m_flowRunResolverCache->flow() != &blockFlow) {
+            m_accumulatedSimpleTextLengthInFlow = m_flowRunResolverCache ? 0 : textNodeOffsetInFlow(textNode);
+            m_flowRunResolverCache = std::make_unique<SimpleLineLayout::RunResolver>(blockFlow, *layout);
+        }
+        // Skip to m_offset position.
+        auto range = m_flowRunResolverCache->rangeForRenderer(renderer);
+        auto it = range.begin();
+        auto end = range.end();
+        while (it != end && (*it).end() <= (static_cast<unsigned>(m_offset) + m_accumulatedSimpleTextLengthInFlow))
+            ++it;
+        if (m_nextRunNeedsWhitespace && rendererText[m_offset - 1] == '\n') {
+            emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+            return it == end;
+        }
+        if (it == end) {
+            // Collapsed trailing whitespace.
+            m_offset = endPosition;
+            m_lastTextNodeEndedWithCollapsedSpace = true;
+            return true;
+        }
+        if (m_nextRunNeedsWhitespace) {
+            emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+            return false;
+        }
+        const auto run = *it;
+        ASSERT(run.end() - run.start() <= rendererText.length());
+        // contentStart skips leading whitespace.
+        unsigned contentStart = std::max<unsigned>(m_offset, run.start() - m_accumulatedSimpleTextLengthInFlow);
+        unsigned contentEnd = std::min(endPosition, run.end() - m_accumulatedSimpleTextLengthInFlow);
+        ASSERT_WITH_SECURITY_IMPLICATION(contentStart <= contentEnd);
+        // Check if whitespace adjustment is needed when crossing renderer boundary.
+        if (isNewTextNode) {
+            bool lastCharacterIsNotWhitespace = m_lastCharacter && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter);
+            bool addTrailingWhitespaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && lastCharacterIsNotWhitespace;
+            bool leadingWhitespaceIsNeededForCurrent = contentStart > static_cast<unsigned>(m_offset) && lastCharacterIsNotWhitespace;
+            if (addTrailingWhitespaceForPrevious || leadingWhitespaceIsNeededForCurrent) {
+                emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+                return false;
             }
-            bool addSpaceForCurrent = runStart || (m_lastCharacter && !deprecatedIsCollapsibleWhitespace(m_lastCharacter));
-            if (addSpaceForCurrent || addSpaceForPrevious) {
-                emitCharacter(' ', textNode, nullptr, runStart, runEnd);
-                m_offset = runEnd;
+        }
+        // \n \t single whitespace characters need replacing so that the new line/tab characters don't show up.
+        unsigned stopPosition = contentStart;
+        while (stopPosition < contentEnd && !isNewLineOrTabCharacter(rendererText[stopPosition]))
+            ++stopPosition;
+        // Emit the text up to the new line/tab character.
+        if (stopPosition < contentEnd) {
+            if (stopPosition == contentStart) {
+                emitCharacter(' ', textNode, nullptr, contentStart, contentStart + 1);
+                m_offset = contentStart + 1;
                 return false;
             }
-            runStart = runEnd;
+            emitText(textNode, renderer, contentStart, stopPosition);
+            m_offset = stopPosition + 1;
+            m_nextRunNeedsWhitespace = true;
+            return false;
         }
-        while (runEnd < end && !deprecatedIsCollapsibleWhitespace(rendererText[runEnd]))
-            ++runEnd;
-        if (runStart < end)
-            emitText(textNode, renderer, runStart, runEnd);
-        m_offset = runEnd;
-        return runEnd == end;
+        emitText(textNode, renderer, contentStart, contentEnd);
+        // When line ending with collapsed whitespace is present, we need to carry over one whitespace: foo(end of line)bar -> foo bar (otherwise we would end up with foobar).
+        m_nextRunNeedsWhitespace = run.isEndOfLine() && contentEnd < endPosition && renderer.style().isCollapsibleWhiteSpace(rendererText[contentEnd]);
+        m_offset = contentEnd;
+        return static_cast<unsigned>(m_offset) == endPosition;
     }
 
     if (renderer.firstTextBox())
         m_textBox = renderer.firstTextBox();
 
-    bool shouldHandleFirstLetter = !m_handledFirstLetter && renderer.isTextFragment() && !m_offset;
+    bool shouldHandleFirstLetter = !m_handledFirstLetter && is<RenderTextFragment>(renderer) && !m_offset;
     if (shouldHandleFirstLetter)
-        handleTextNodeFirstLetter(toRenderTextFragment(renderer));
+        handleTextNodeFirstLetter(downcast<RenderTextFragment>(renderer));
 
     if (!renderer.firstTextBox() && rendererText.length() && !shouldHandleFirstLetter) {
         if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
@@ -610,7 +666,7 @@ bool TextIterator::handleTextNode()
 
 void TextIterator::handleTextBox()
 {    
-    Text& textNode = toText(*m_node);
+    Text& textNode = downcast<Text>(*m_node);
 
     auto& renderer = m_firstLetterText ? *m_firstLetterText : *textNode.renderer();
     if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility)) {
@@ -627,7 +683,7 @@ void TextIterator::handleTextBox()
         // Check for collapsed space at the start of this run.
         InlineTextBox* firstTextBox = renderer.containsReversedText() ? (m_sortedTextBoxes.isEmpty() ? nullptr : m_sortedTextBoxes[0]) : renderer.firstTextBox();
         bool needSpace = m_lastTextNodeEndedWithCollapsedSpace || (m_textBox == firstTextBox && textBoxStart == runStart && runStart);
-        if (needSpace && !deprecatedIsCollapsibleWhitespace(m_lastCharacter) && m_lastCharacter) {
+        if (needSpace && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter) && m_lastCharacter) {
             if (m_lastTextNode == &textNode && runStart && rendererText[runStart - 1] == ' ') {
                 unsigned spaceRunStart = runStart - 1;
                 while (spaceRunStart && rendererText[spaceRunStart - 1] == ' ')
@@ -735,8 +791,8 @@ bool TextIterator::handleReplacedElement()
         return false;
     }
 
-    if ((m_behavior & TextIteratorEntersTextControls) && renderer.isTextControl()) {
-        if (TextControlInnerTextElement* innerTextElement = toRenderTextControl(renderer).textFormControlElement().innerTextElement()) {
+    if ((m_behavior & TextIteratorEntersTextControls) && is<RenderTextControl>(renderer)) {
+        if (TextControlInnerTextElement* innerTextElement = downcast<RenderTextControl>(renderer).textFormControlElement().innerTextElement()) {
             m_node = innerTextElement->containingShadowRoot();
             pushFullyClippedState(m_fullyClippedStack, *m_node);
             m_offset = 0;
@@ -767,11 +823,11 @@ bool TextIterator::handleReplacedElement()
     m_positionStartOffset = 0;
     m_positionEndOffset = 1;
 
-    if ((m_behavior & TextIteratorEmitsImageAltText) && renderer.isRenderImage()) {
-        String altText = toRenderImage(renderer).altText();
+    if ((m_behavior & TextIteratorEmitsImageAltText) && is<RenderImage>(renderer)) {
+        String altText = downcast<RenderImage>(renderer).altText();
         if (unsigned length = altText.length()) {
             m_lastCharacter = altText[length - 1];
-            m_copyableText.set(std::move(altText));
+            m_copyableText.set(WTFMove(altText));
             m_text = m_copyableText.text();
             return true;
         }
@@ -792,7 +848,7 @@ static bool shouldEmitTabBeforeNode(Node& node)
         return false;
     
     // Want a tab before every cell other than the first one.
-    RenderTableCell& cell = toRenderTableCell(*renderer);
+    RenderTableCell& cell = downcast<RenderTableCell>(*renderer);
     RenderTable* table = cell.table();
     return table && (table->cellBefore(&cell) || table->cellAbove(&cell));
 }
@@ -802,17 +858,17 @@ static bool shouldEmitNewlineForNode(Node* node, bool emitsOriginalText)
     auto* renderer = node->renderer();
     if (!(renderer ? renderer->isBR() : node->hasTagName(brTag)))
         return false;
-    return emitsOriginalText || !(node->isInShadowTree() && node->shadowHost()->toInputElement());
+    return emitsOriginalText || !(node->isInShadowTree() && is<HTMLInputElement>(*node->shadowHost()));
 }
 
 static bool hasHeaderTag(HTMLElement& element)
 {
-    return element.hasLocalName(h1Tag)
-        || element.hasLocalName(h2Tag)
-        || element.hasLocalName(h3Tag)
-        || element.hasLocalName(h4Tag)
-        || element.hasLocalName(h5Tag)
-        || element.hasLocalName(h6Tag);
+    return element.hasTagName(h1Tag)
+        || element.hasTagName(h2Tag)
+        || element.hasTagName(h3Tag)
+        || element.hasTagName(h4Tag)
+        || element.hasTagName(h5Tag)
+        || element.hasTagName(h6Tag);
 }
 
 static bool shouldEmitNewlinesBeforeAndAfterNode(Node& node)
@@ -821,23 +877,23 @@ static bool shouldEmitNewlinesBeforeAndAfterNode(Node& node)
     // a newline both before and after the element.
     auto* renderer = node.renderer();
     if (!renderer) {
-        if (!node.isHTMLElement())
+        if (!is<HTMLElement>(node))
             return false;
-        auto& element = toHTMLElement(node);
+        auto& element = downcast<HTMLElement>(node);
         return hasHeaderTag(element)
-            || element.hasLocalName(blockquoteTag)
-            || element.hasLocalName(ddTag)
-            || element.hasLocalName(divTag)
-            || element.hasLocalName(dlTag)
-            || element.hasLocalName(dtTag)
-            || element.hasLocalName(hrTag)
-            || element.hasLocalName(liTag)
-            || element.hasLocalName(listingTag)
-            || element.hasLocalName(olTag)
-            || element.hasLocalName(pTag)
-            || element.hasLocalName(preTag)
-            || element.hasLocalName(trTag)
-            || element.hasLocalName(ulTag);
+            || element.hasTagName(blockquoteTag)
+            || element.hasTagName(ddTag)
+            || element.hasTagName(divTag)
+            || element.hasTagName(dlTag)
+            || element.hasTagName(dtTag)
+            || element.hasTagName(hrTag)
+            || element.hasTagName(liTag)
+            || element.hasTagName(listingTag)
+            || element.hasTagName(olTag)
+            || element.hasTagName(pTag)
+            || element.hasTagName(preTag)
+            || element.hasTagName(trTag)
+            || element.hasTagName(ulTag);
     }
     
     // Need to make an exception for table cells, because they are blocks, but we
@@ -847,14 +903,14 @@ static bool shouldEmitNewlinesBeforeAndAfterNode(Node& node)
     
     // Need to make an exception for table row elements, because they are neither
     // "inline" or "RenderBlock", but we want newlines for them.
-    if (renderer->isTableRow()) {
-        RenderTable* table = toRenderTableRow(*renderer).table();
+    if (is<RenderTableRow>(*renderer)) {
+        RenderTable* table = downcast<RenderTableRow>(*renderer).table();
         if (table && !table->isInline())
             return true;
     }
     
     return !renderer->isInline()
-        && renderer->isRenderBlock()
+        && is<RenderBlock>(*renderer)
         && !renderer->isFloatingOrOutOfFlowPositioned()
         && !renderer->isBody()
         && !renderer->isRubyText();
@@ -868,7 +924,7 @@ static bool shouldEmitNewlineAfterNode(Node& node)
     // Check if this is the very last renderer in the document.
     // If so, then we should not emit a newline.
     Node* subsequentNode = &node;
-    while ((subsequentNode = NodeTraversal::nextSkippingChildren(subsequentNode))) {
+    while ((subsequentNode = NodeTraversal::nextSkippingChildren(*subsequentNode))) {
         if (subsequentNode->renderer())
             return true;
     }
@@ -888,17 +944,19 @@ static bool shouldEmitExtraNewlineForNode(Node& node)
     // will work right even if both the <div> and the <p> have bottom margins.
 
     auto* renderer = node.renderer();
-    if (!renderer || !renderer->isBox())
+    if (!is<RenderBox>(renderer))
         return false;
 
     // NOTE: We only do this for a select set of nodes, and WinIE appears not to do this at all.
-    if (!node.isHTMLElement())
+    if (!is<HTMLElement>(node))
         return false;
-    if (!(hasHeaderTag(toHTMLElement(node)) || toHTMLElement(node).hasLocalName(pTag)))
+
+    HTMLElement& element = downcast<HTMLElement>(node);
+    if (!hasHeaderTag(element) && !is<HTMLParagraphElement>(element))
         return false;
 
-    int bottomMargin = toRenderBox(renderer)->collapsedMarginAfter();
-    int fontSize = toRenderBox(renderer)->style().fontDescription().computedPixelSize();
+    int bottomMargin = downcast<RenderBox>(*renderer).collapsedMarginAfter();
+    int fontSize = downcast<RenderBox>(*renderer).style().fontDescription().computedPixelSize();
     return bottomMargin * 2 >= fontSize;
 }
 
@@ -917,8 +975,8 @@ static int maxOffsetIncludingCollapsedSpaces(Node& node)
 {
     int offset = caretMaxOffset(&node);
     if (auto* renderer = node.renderer()) {
-        if (renderer->isText())
-            offset += collapsedSpaceLength(toRenderText(*renderer), offset);
+        if (is<RenderText>(*renderer))
+            offset += collapsedSpaceLength(downcast<RenderText>(*renderer), offset);
     }
     return offset;
 }
@@ -968,7 +1026,7 @@ bool TextIterator::shouldRepresentNodeOffsetZero()
     // Additionally, if the range we are iterating over contains huge sections of unrendered content, 
     // we would create VisiblePositions on every call to this function without this check.
     if (!m_node->renderer() || m_node->renderer()->style().visibility() != VISIBLE
-        || (m_node->renderer()->isRenderBlockFlow() && !toRenderBlock(m_node->renderer())->height() && !m_node->hasTagName(bodyTag)))
+        || (is<RenderBlockFlow>(*m_node->renderer()) && !downcast<RenderBlockFlow>(*m_node->renderer()).height() && !is<HTMLBodyElement>(*m_node)))
         return false;
 
     // The startPos.isNotNull() check is needed because the start could be before the body,
@@ -1070,6 +1128,7 @@ void TextIterator::emitCharacter(UChar character, Node& characterNode, Node* off
     m_text = m_copyableText.text();
     m_lastCharacter = character;
     m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_nextRunNeedsWhitespace = false;
 }
 
 void TextIterator::emitText(Text& textNode, RenderText& renderer, int textStartOffset, int textEndOffset)
@@ -1090,20 +1149,21 @@ void TextIterator::emitText(Text& textNode, RenderText& renderer, int textStartO
     m_positionEndOffset = textEndOffset;
 
     m_lastCharacter = string[textEndOffset - 1];
-    m_copyableText.set(std::move(string), textStartOffset, textEndOffset - textStartOffset);
+    m_copyableText.set(WTFMove(string), textStartOffset, textEndOffset - textStartOffset);
     m_text = m_copyableText.text();
 
     m_lastTextNodeEndedWithCollapsedSpace = false;
+    m_nextRunNeedsWhitespace = false;
     m_hasEmitted = true;
 }
 
-PassRefPtr<Range> TextIterator::range() const
+Ref<Range> TextIterator::range() const
 {
     ASSERT(!atEnd());
 
     // use the current run information, if we have it
     if (m_positionOffsetBaseNode) {
-        int index = m_positionOffsetBaseNode->nodeIndex();
+        unsigned index = m_positionOffsetBaseNode->computeNodeIndex();
         m_positionStartOffset += index;
         m_positionEndOffset += index;
         m_positionOffsetBaseNode = nullptr;
@@ -1113,13 +1173,13 @@ PassRefPtr<Range> TextIterator::range() const
     
 Node* TextIterator::node() const
 {
-    RefPtr<Range> textRange = range();
+    Ref<Range> textRange = range();
 
-    Node* node = textRange->startContainer();
-    if (node->offsetInCharacters())
-        return node;
+    Node& node = textRange->startContainer();
+    if (node.offsetInCharacters())
+        return &node;
     
-    return node->childNode(textRange->startOffset());
+    return node.traverseToChildAt(textRange->startOffset());
 }
 
 // --------
@@ -1146,22 +1206,20 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range& ra
 
     range.ownerDocument().updateLayoutIgnorePendingStylesheets();
 
-    Node* startNode = range.startContainer();
-    if (!startNode)
-        return;
-    Node* endNode = range.endContainer();
+    Node* startNode = &range.startContainer();
+    Node* endNode = &range.endContainer();
     int startOffset = range.startOffset();
     int endOffset = range.endOffset();
 
     if (!startNode->offsetInCharacters()) {
-        if (startOffset >= 0 && startOffset < static_cast<int>(startNode->childNodeCount())) {
-            startNode = startNode->childNode(startOffset);
+        if (startOffset >= 0 && startOffset < static_cast<int>(startNode->countChildNodes())) {
+            startNode = startNode->traverseToChildAt(startOffset);
             startOffset = 0;
         }
     }
     if (!endNode->offsetInCharacters()) {
-        if (endOffset > 0 && endOffset <= static_cast<int>(endNode->childNodeCount())) {
-            endNode = endNode->childNode(endOffset - 1);
+        if (endOffset > 0 && endOffset <= static_cast<int>(endNode->countChildNodes())) {
+            endNode = endNode->traverseToChildAt(endOffset - 1);
             endOffset = lastOffsetInNode(endNode);
         }
     }
@@ -1268,7 +1326,7 @@ void SimplifiedBackwardsTextIterator::advance()
 
 bool SimplifiedBackwardsTextIterator::handleTextNode()
 {
-    Text& textNode = toText(*m_node);
+    Text& textNode = downcast<Text>(*m_node);
 
     m_lastTextNode = &textNode;
 
@@ -1298,7 +1356,7 @@ bool SimplifiedBackwardsTextIterator::handleTextNode()
     ASSERT(m_positionEndOffset - offsetInNode <= static_cast<int>(text.length()));
 
     m_lastCharacter = text[m_positionEndOffset - offsetInNode - 1];
-    m_copyableText.set(std::move(text), m_positionStartOffset - offsetInNode, m_positionEndOffset - m_positionStartOffset);
+    m_copyableText.set(WTFMove(text), m_positionStartOffset - offsetInNode, m_positionEndOffset - m_positionStartOffset);
     m_text = m_copyableText.text();
 
     return !m_shouldHandleFirstLetter;
@@ -1306,36 +1364,36 @@ bool SimplifiedBackwardsTextIterator::handleTextNode()
 
 RenderText* SimplifiedBackwardsTextIterator::handleFirstLetter(int& startOffset, int& offsetInNode)
 {
-    RenderText* renderer = toRenderText(m_node->renderer());
+    RenderText& renderer = downcast<RenderText>(*m_node->renderer());
     startOffset = (m_node == m_startContainer) ? m_startOffset : 0;
 
-    if (!renderer->isTextFragment()) {
+    if (!is<RenderTextFragment>(renderer)) {
         offsetInNode = 0;
-        return renderer;
+        return &renderer;
     }
 
-    RenderTextFragment* fragment = toRenderTextFragment(renderer);
-    int offsetAfterFirstLetter = fragment->start();
+    RenderTextFragment& fragment = downcast<RenderTextFragment>(renderer);
+    int offsetAfterFirstLetter = fragment.start();
     if (startOffset >= offsetAfterFirstLetter) {
         ASSERT(!m_shouldHandleFirstLetter);
         offsetInNode = offsetAfterFirstLetter;
-        return renderer;
+        return &renderer;
     }
 
     if (!m_shouldHandleFirstLetter && startOffset + offsetAfterFirstLetter < m_offset) {
         m_shouldHandleFirstLetter = true;
         offsetInNode = offsetAfterFirstLetter;
-        return renderer;
+        return &renderer;
     }
 
     m_shouldHandleFirstLetter = false;
     offsetInNode = 0;
-    return firstRenderTextInFirstLetter(fragment->firstLetter());
+    return firstRenderTextInFirstLetter(fragment.firstLetter());
 }
 
 bool SimplifiedBackwardsTextIterator::handleReplacedElement()
 {
-    unsigned index = m_node->nodeIndex();
+    unsigned index = m_node->computeNodeIndex();
     // We want replaced elements to behave like punctuation for boundary 
     // finding, and to simply take up space for the selection preservation 
     // code in moveParagraphs, so we use a comma. Unconditionally emit
@@ -1349,7 +1407,7 @@ bool SimplifiedBackwardsTextIterator::handleNonTextNode()
     // We can use a linefeed in place of a tab because this simple iterator is only used to
     // find boundaries, not actual content. A linefeed breaks words, sentences, and paragraphs.
     if (shouldEmitNewlineForNode(m_node, m_behavior & TextIteratorEmitsOriginalText) || shouldEmitNewlineAfterNode(*m_node) || shouldEmitTabBeforeNode(*m_node)) {
-        unsigned index = m_node->nodeIndex();
+        unsigned index = m_node->computeNodeIndex();
         // The start of this emitted range is wrong. Ensuring correctness would require
         // VisiblePositions and so would be slow. previousBoundary expects this.
         emitCharacter('\n', *m_node->parentNode(), index + 1, index + 1);
@@ -1387,7 +1445,7 @@ bool SimplifiedBackwardsTextIterator::advanceRespectingRange(Node* next)
     return true;
 }
 
-PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const
+Ref<Range> SimplifiedBackwardsTextIterator::range() const
 {
     ASSERT(!atEnd());
 
@@ -1406,21 +1464,21 @@ CharacterIterator::CharacterIterator(const Range& range, TextIteratorBehavior be
         m_underlyingIterator.advance();
 }
 
-PassRefPtr<Range> CharacterIterator::range() const
+Ref<Range> CharacterIterator::range() const
 {
-    RefPtr<Range> r = m_underlyingIterator.range();
+    Ref<Range> range = m_underlyingIterator.range();
     if (!m_underlyingIterator.atEnd()) {
         if (m_underlyingIterator.text().length() <= 1) {
             ASSERT(m_runOffset == 0);
         } else {
-            Node* n = r->startContainer();
-            ASSERT(n == r->endContainer());
-            int offset = r->startOffset() + m_runOffset;
-            r->setStart(n, offset);
-            r->setEnd(n, offset + 1);
+            Node& node = range->startContainer();
+            ASSERT(&node == &range->endContainer());
+            int offset = range->startOffset() + m_runOffset;
+            range->setStart(&node, offset);
+            range->setEnd(&node, offset + 1);
         }
     }
-    return r.release();
+    return range;
 }
 
 void CharacterIterator::advance(int count)
@@ -1468,27 +1526,22 @@ void CharacterIterator::advance(int count)
     m_runOffset = 0;
 }
 
-static void append(Vector<UChar>& buffer, StringView string)
-{
-    unsigned oldSize = buffer.size();
-    unsigned length = string.length();
-    buffer.grow(oldSize + length);
-    for (unsigned i = 0; i < length; ++i)
-        buffer[oldSize + i] = string[i];
-}
-
-static PassRefPtr<Range> characterSubrange(CharacterIterator& it, int offset, int length)
+static Ref<Range> characterSubrange(Document& document, CharacterIterator& it, int offset, int length)
 {
     it.advance(offset);
-    RefPtr<Range> start = it.range();
+    if (it.atEnd())
+        return Range::create(document);
+
+    Ref<Range> start = it.range();
 
     if (length > 1)
         it.advance(length - 1);
-    RefPtr<Range> end = it.range();
+    if (it.atEnd())
+        return Range::create(document);
+
+    Ref<Range> end = it.range();
 
-    return Range::create(start->startContainer()->document(),
-        start->startContainer(), start->startOffset(), 
-        end->endContainer(), end->endOffset());
+    return Range::create(document, &start->startContainer(), start->startOffset(), &end->endContainer(), end->endOffset());
 }
 
 BackwardsCharacterIterator::BackwardsCharacterIterator(const Range& range)
@@ -1501,21 +1554,21 @@ BackwardsCharacterIterator::BackwardsCharacterIterator(const Range& range)
         m_underlyingIterator.advance();
 }
 
-PassRefPtr<Range> BackwardsCharacterIterator::range() const
+Ref<Range> BackwardsCharacterIterator::range() const
 {
-    RefPtr<Range> r = m_underlyingIterator.range();
+    Ref<Range> r = m_underlyingIterator.range();
     if (!m_underlyingIterator.atEnd()) {
         if (m_underlyingIterator.text().length() <= 1)
             ASSERT(m_runOffset == 0);
         else {
-            Node* n = r->startContainer();
-            ASSERT(n == r->endContainer());
+            Node& node = r->startContainer();
+            ASSERT(&node == &r->endContainer());
             int offset = r->endOffset() - m_runOffset;
-            r->setStart(n, offset - 1);
-            r->setEnd(n, offset);
+            r->setStart(&node, offset - 1);
+            r->setEnd(&node, offset);
         }
     }
-    return r.release();
+    return r;
 }
 
 void BackwardsCharacterIterator::advance(int count)
@@ -1629,7 +1682,7 @@ StringView WordAwareIterator::text() const
 
 // --------
 
-static inline UChar foldQuoteMarkOrSoftHyphen(UChar c)
+static inline UChar foldQuoteMark(UChar c)
 {
     switch (c) {
         case hebrewPunctuationGershayim:
@@ -1640,26 +1693,24 @@ static inline UChar foldQuoteMarkOrSoftHyphen(UChar c)
         case leftSingleQuotationMark:
         case rightSingleQuotationMark:
             return '\'';
-        case softHyphen:
-            // Replace soft hyphen with an ignorable character so that their presence or absence will
-            // not affect string comparison.
-            return 0;
         default:
             return c;
     }
 }
 
-static inline void foldQuoteMarksAndSoftHyphens(String& s)
+// FIXME: We'd like to tailor the searcher to fold quote marks for us instead
+// of doing it in a separate replacement pass here, but ICU doesn't offer a way
+// to add tailoring on top of the locale-specific tailoring as of this writing.
+static inline String foldQuoteMarks(String string)
 {
-    s.replace(hebrewPunctuationGeresh, '\'');
-    s.replace(hebrewPunctuationGershayim, '"');
-    s.replace(leftDoubleQuotationMark, '"');
-    s.replace(leftSingleQuotationMark, '\'');
-    s.replace(rightDoubleQuotationMark, '"');
-    s.replace(rightSingleQuotationMark, '\'');
-    // Replace soft hyphen with an ignorable character so that their presence or absence will
-    // not affect string comparison.
-    s.replace(softHyphen, 0);
+    string.replace(hebrewPunctuationGeresh, '\'');
+    string.replace(hebrewPunctuationGershayim, '"');
+    string.replace(leftDoubleQuotationMark, '"');
+    string.replace(leftSingleQuotationMark, '\'');
+    string.replace(rightDoubleQuotationMark, '"');
+    string.replace(rightSingleQuotationMark, '\'');
+
+    return string;
 }
 
 #if !UCONFIG_NO_COLLATION
@@ -1943,7 +1994,8 @@ static inline bool isSeparator(UChar32 character)
 }
 
 inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
-    : m_target(target)
+    : m_target(foldQuoteMarks(target))
+    , m_targetCharacters(StringView(m_target).upconvertedCharacters())
     , m_options(options)
     , m_prefixLength(0)
     , m_atBreak(true)
@@ -1952,11 +2004,6 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
 {
     ASSERT(!m_target.isEmpty());
 
-    // FIXME: We'd like to tailor the searcher to fold quote marks for us instead
-    // of doing it in a separate replacement pass here, but ICU doesn't offer a way
-    // to add tailoring on top of the locale-specific tailoring as of this writing.
-    foldQuoteMarksAndSoftHyphens(m_target);
-
     size_t targetLength = m_target.length();
     m_buffer.reserveInitialCapacity(std::max(targetLength * 8, minimumSearchBufferSize));
     m_overlap = m_buffer.capacity() / 4;
@@ -2000,12 +2047,12 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
     usearch_setAttribute(searcher, USEARCH_ELEMENT_COMPARISON, comparator, &status);
     ASSERT(status == U_ZERO_ERROR);
 
-    usearch_setPattern(searcher, m_target.deprecatedCharacters(), targetLength, &status);
+    usearch_setPattern(searcher, m_targetCharacters, targetLength, &status);
     ASSERT(status == U_ZERO_ERROR);
 
     // The kana workaround requires a normalized copy of the target string.
     if (m_targetRequiresKanaWorkaround)
-        normalizeCharacters(m_target.deprecatedCharacters(), m_target.length(), m_normalizedTarget);
+        normalizeCharacters(m_targetCharacters, targetLength, m_normalizedTarget);
 }
 
 inline SearchBuffer::~SearchBuffer()
@@ -2014,6 +2061,8 @@ inline SearchBuffer::~SearchBuffer()
     UErrorCode status = U_ZERO_ERROR;
     usearch_setPattern(WebCore::searcher(), &newlineCharacter, 1, &status);
     ASSERT(status == U_ZERO_ERROR);
+    usearch_setText(WebCore::searcher(), &newlineCharacter, 1, &status);
+    ASSERT(status == U_ZERO_ERROR);
 
     unlockSearcher();
 }
@@ -2037,7 +2086,7 @@ inline size_t SearchBuffer::append(StringView text)
     ASSERT(usableLength);
     m_buffer.grow(oldLength + usableLength);
     for (unsigned i = 0; i < usableLength; ++i)
-        m_buffer[oldLength + i] = foldQuoteMarkOrSoftHyphen(text[i]);
+        m_buffer[oldLength + i] = foldQuoteMark(text[i]);
     return usableLength;
 }
 
@@ -2063,7 +2112,7 @@ inline void SearchBuffer::prependContext(StringView text)
     }
 
     size_t usableLength = std::min(m_buffer.capacity() - m_prefixLength, text.length() - wordBoundaryContextStart);
-    WebCore::append(m_buffer, text.substring(text.length() - usableLength, usableLength));
+    WTF::append(m_buffer, text.substring(text.length() - usableLength, usableLength));
     m_prefixLength += usableLength;
 
     if (wordBoundaryContextStart || m_prefixLength == m_buffer.capacity())
@@ -2139,6 +2188,17 @@ inline bool SearchBuffer::isBadMatch(const UChar* match, size_t matchLength) con
         }
     }
 }
+    
+inline bool SearchBuffer::isWordEndMatch(size_t start, size_t length) const
+{
+    ASSERT(length);
+    ASSERT(m_options & AtWordEnds);
+
+    int endWord;
+    // Start searching at the end of matched search, so that multiple word matches succeed.
+    findEndWordBoundary(StringView(m_buffer.data(), m_buffer.size()), start + length - 1, &endWord);
+    return static_cast<size_t>(endWord) == (start + length);
+}
 
 inline bool SearchBuffer::isWordStartMatch(size_t start, size_t length) const
 {
@@ -2186,7 +2246,7 @@ inline bool SearchBuffer::isWordStartMatch(size_t start, size_t length) const
 
     // Chinese and Japanese lack word boundary marks, and there is no clear agreement on what constitutes
     // a word, so treat the position before any CJK character as a word start.
-    if (Font::isCJKIdeographOrSymbol(firstCharacter))
+    if (FontCascade::isCJKIdeographOrSymbol(firstCharacter))
         return true;
 
     size_t wordBreakSearchStart = start + length;
@@ -2247,7 +2307,9 @@ nextMatch:
     ASSERT_WITH_SECURITY_IMPLICATION(matchStart + matchedLength <= size);
 
     // If this match is "bad", move on to the next match.
-    if (isBadMatch(m_buffer.data() + matchStart, matchedLength) || ((m_options & AtWordStarts) && !isWordStartMatch(matchStart, matchedLength))) {
+    if (isBadMatch(m_buffer.data() + matchStart, matchedLength)
+        || ((m_options & AtWordStarts) && !isWordStartMatch(matchStart, matchedLength))
+        || ((m_options & AtWordEnds) && !isWordEndMatch(matchStart, matchedLength))) {
         matchStart = usearch_next(searcher, &status);
         ASSERT(status == U_ZERO_ERROR);
         goto nextMatch;
@@ -2274,7 +2336,7 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
 {
     ASSERT(!m_target.isEmpty());
     m_target.replace(noBreakSpace, ' ');
-    foldQuoteMarksAndSoftHyphens(m_target);
+    foldQuoteMarks(m_target);
 }
 
 inline SearchBuffer::~SearchBuffer()
@@ -2294,7 +2356,7 @@ inline bool SearchBuffer::atBreak() const
 
 inline void SearchBuffer::append(UChar c, bool isStart)
 {
-    m_buffer[m_cursor] = c == noBreakSpace ? ' ' : foldQuoteMarkOrSoftHyphen(c);
+    m_buffer[m_cursor] = c == noBreakSpace ? ' ' : foldQuoteMark(c);
     m_isCharacterStartBuffer[m_cursor] = isStart;
     if (++m_cursor == m_target.length()) {
         m_cursor = 0;
@@ -2384,10 +2446,10 @@ int TextIterator::rangeLength(const Range* range, bool forSelectionPreservation)
     return length;
 }
 
-PassRefPtr<Range> TextIterator::subrange(Range* entireRange, int characterOffset, int characterCount)
+Ref<Range> TextIterator::subrange(Range* entireRange, int characterOffset, int characterCount)
 {
     CharacterIterator entireRangeIterator(*entireRange);
-    return characterSubrange(entireRangeIterator, characterOffset, characterCount);
+    return characterSubrange(entireRange->ownerDocument(), entireRangeIterator, characterOffset, characterCount);
 }
 
 static inline bool isInsideReplacedElement(TextIterator& iterator)
@@ -2398,23 +2460,23 @@ static inline bool isInsideReplacedElement(TextIterator& iterator)
     return node && isRendererReplacedElement(node->renderer());
 }
 
-PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
+RefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
 {
-    RefPtr<Range> resultRange = scope->document().createRange();
+    Ref<Range> resultRange = scope->document().createRange();
 
     int docTextPosition = 0;
     int rangeEnd = rangeLocation + rangeLength;
     bool startRangeFound = false;
 
-    RefPtr<Range> textRunRange = rangeOfContents(*scope);
+    Ref<Range> textRunRange = rangeOfContents(*scope);
 
-    TextIterator it(textRunRange.get(), forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior);
+    TextIterator it(textRunRange.ptr(), forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior);
     
     // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
     if (!rangeLocation && !rangeLength && it.atEnd()) {
-        resultRange->setStart(textRunRange->startContainer(), 0);
-        resultRange->setEnd(textRunRange->startContainer(), 0);
-        return resultRange.release();
+        resultRange->setStart(&textRunRange->startContainer(), 0);
+        resultRange->setEnd(&textRunRange->startContainer(), 0);
+        return WTFMove(resultRange);
     }
 
     for (; !it.atEnd(); it.advance()) {
@@ -2430,8 +2492,8 @@ PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope,
             if (length == 1 && (it.text()[0] == '\n' || isInsideReplacedElement(it))) {
                 it.advance();
                 if (!it.atEnd()) {
-                    RefPtr<Range> range = it.range();
-                    textRunRange->setEnd(range->startContainer(), range->startOffset());
+                    Ref<Range> range = it.range();
+                    textRunRange->setEnd(&range->startContainer(), range->startOffset());
                 } else {
                     Position runStart = textRunRange->startPosition();
                     Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
@@ -2443,26 +2505,26 @@ PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope,
 
         if (foundStart) {
             startRangeFound = true;
-            if (textRunRange->startContainer()->isTextNode()) {
+            if (textRunRange->startContainer().isTextNode()) {
                 int offset = rangeLocation - docTextPosition;
-                resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset());
+                resultRange->setStart(&textRunRange->startContainer(), offset + textRunRange->startOffset());
             } else {
                 if (rangeLocation == docTextPosition)
-                    resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset());
+                    resultRange->setStart(&textRunRange->startContainer(), textRunRange->startOffset());
                 else
-                    resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset());
+                    resultRange->setStart(&textRunRange->endContainer(), textRunRange->endOffset());
             }
         }
 
         if (foundEnd) {
-            if (textRunRange->startContainer()->isTextNode()) {
+            if (textRunRange->startContainer().isTextNode()) {
                 int offset = rangeEnd - docTextPosition;
-                resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset());
+                resultRange->setEnd(&textRunRange->startContainer(), offset + textRunRange->startOffset());
             } else {
                 if (rangeEnd == docTextPosition)
-                    resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset());
+                    resultRange->setEnd(&textRunRange->startContainer(), textRunRange->startOffset());
                 else
-                    resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset());
+                    resultRange->setEnd(&textRunRange->endContainer(), textRunRange->endOffset());
             }
             docTextPosition += length;
             break;
@@ -2475,9 +2537,9 @@ PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope,
         return nullptr;
     
     if (rangeLength && rangeEnd > docTextPosition) // rangeEnd is out of bounds
-        resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset());
+        resultRange->setEnd(&textRunRange->endContainer(), textRunRange->endOffset());
     
-    return resultRange.release();
+    return WTFMove(resultRange);
 }
 
 bool TextIterator::getLocationAndLengthFromRange(Node* scope, const Range* range, size_t& location, size_t& length)
@@ -2485,26 +2547,23 @@ bool TextIterator::getLocationAndLengthFromRange(Node* scope, const Range* range
     location = notFound;
     length = 0;
 
-    if (!range->startContainer())
-        return false;
-
     // The critical assumption is that this only gets called with ranges that
     // concentrate on a given area containing the selection root. This is done
     // because of text fields and textareas. The DOM for those is not
     // directly in the document DOM, so ensure that the range does not cross a
     // boundary of one of those.
-    if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
+    if (&range->startContainer() != scope && !range->startContainer().isDescendantOf(scope))
         return false;
-    if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
+    if (&range->endContainer() != scope && !range->endContainer().isDescendantOf(scope))
         return false;
 
-    RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
-    ASSERT(testRange->startContainer() == scope);
-    location = TextIterator::rangeLength(testRange.get());
+    Ref<Range> testRange = Range::create(scope->document(), scope, 0, &range->startContainer(), range->startOffset());
+    ASSERT(&testRange->startContainer() == scope);
+    location = TextIterator::rangeLength(testRange.ptr());
 
-    testRange->setEnd(range->endContainer(), range->endOffset(), IGNORE_EXCEPTION);
-    ASSERT(testRange->startContainer() == scope);
-    length = TextIterator::rangeLength(testRange.get()) - location;
+    testRange->setEnd(&range->endContainer(), range->endOffset(), IGNORE_EXCEPTION);
+    ASSERT(&testRange->startContainer() == scope);
+    length = TextIterator::rangeLength(testRange.ptr()) - location;
     return true;
 }
 
@@ -2538,11 +2597,16 @@ String plainText(const Range* r, TextIteratorBehavior defaultBehavior, bool isDi
     return result;
 }
 
-static PassRefPtr<Range> collapsedToBoundary(const Range& range, bool forward)
+String plainTextReplacingNoBreakSpace(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
+{
+    return plainText(range, defaultBehavior, isDisplayString).replace(noBreakSpace, ' ');
+}
+
+static Ref<Range> collapsedToBoundary(const Range& range, bool forward)
 {
-    RefPtr<Range> result = range.cloneRange(ASSERT_NO_EXCEPTION);
-    result->collapse(!forward, ASSERT_NO_EXCEPTION);
-    return result.release();
+    Ref<Range> result = range.cloneRange();
+    result->collapse(!forward);
+    return result;
 }
 
 static size_t findPlainText(const Range& range, const String& target, FindOptions options, size_t& matchStart)
@@ -2553,9 +2617,9 @@ static size_t findPlainText(const Range& range, const String& target, FindOption
     SearchBuffer buffer(target, options);
 
     if (buffer.needsMoreContext()) {
-        RefPtr<Range> beforeStartRange = range.ownerDocument().createRange();
-        beforeStartRange->setEnd(range.startContainer(), range.startOffset());
-        for (SimplifiedBackwardsTextIterator backwardsIterator(*beforeStartRange); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
+        Ref<Range> beforeStartRange = range.ownerDocument().createRange();
+        beforeStartRange->setEnd(&range.startContainer(), range.startOffset());
+        for (SimplifiedBackwardsTextIterator backwardsIterator(beforeStartRange.get()); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
             buffer.prependContext(backwardsIterator.text());
             if (!buffer.needsMoreContext())
                 break;
@@ -2589,7 +2653,7 @@ tryAgain:
     return matchLength;
 }
 
-PassRefPtr<Range> findPlainText(const Range& range, const String& target, FindOptions options)
+Ref<Range> findPlainText(const Range& range, const String& target, FindOptions options)
 {
     // First, find the text.
     size_t matchStart;
@@ -2602,7 +2666,7 @@ PassRefPtr<Range> findPlainText(const Range& range, const String& target, FindOp
 
     // Then, find the document position of the start and the end of the text.
     CharacterIterator computeRangeIterator(range, TextIteratorEntersTextControls);
-    return characterSubrange(computeRangeIterator, matchStart, matchLength);
+    return characterSubrange(range.ownerDocument(), computeRangeIterator, matchStart, matchLength);
 }
 
 }