Remove all uses of live ranges from TextIterator
https://bugs.webkit.org/show_bug.cgi?id=209723

Reviewed by Antti Koivisto.

Source/WebCore:

- Replaced TextIterator::getLocationAndLengthFromRange with a function named
  characterRange that computes a CharacterRange given a scope and a range.
- Removed the overload of plainText that takes a pointer to a live range.
- Update the many callers of plainText that pass a pointer to a live range
  to pass a reference instead, adding null checks as needed to preserve behavior.
- Rewrote some call sites to not use live ranges at all, or use them minimally.

* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::nextSentenceEndPosition const): Streamlined
the logic in this function, using a smaller number of null checks since the
functions we are calling also do null checks, simpler variable names and
fewer local variables. Pass a reference to a live range rather than a
pointer to the plainText function.
(WebCore::AccessibilityObject::previousSentenceStartPosition const): Ditto.
(WebCore::AccessibilityObject::nextParagraphEndPosition const): Ditto.
(WebCore::AccessibilityObject::previousParagraphStartPosition const): Ditto.
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::textUnderElement const): Ditto.
(WebCore::boundsForRects): Converted this to a file-local function and changed
it to take a SimpleRange instead of a live range.
(WebCore::AccessibilityRenderObject::boundsForVisiblePositionRange const): Pass
a reference to a live range instead of a pointer.
(WebCore::AccessibilityRenderObject::boundsForRange const): Ditto.
* accessibility/AccessibilityRenderObject.h: Removed boundsForRects.

* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper _convertToNSRange:]): Rewrote to use
characterRange instead of TextIterator::getLocationAndLengthFromRange.

* dom/BoundaryPoint.h:
(WebCore::makeBoundaryPointBeforeNodeContents): Added.

* dom/Element.cpp:
(WebCore::Element::innerText): Pass a SimpleRange instead of a live range
to the plainText function.

* dom/Range.cpp:
(WebCore::Range::text const): Pass a reference to a live range instead of a
pointer to the plainText function.

* dom/SimpleRange.cpp:
(WebCore::makeBoundaryPointAfterNodeContents): Added.
(WebCore::makeRangeSelectingNodeContents): Use makeBoundaryPointBeforeNodeContents
and makeBoundaryPointAfterNodeContents

* editing/AlternativeTextController.cpp:
(WebCore::AlternativeTextController::applyPendingCorrection): Pass a reference
to a live range to plainText.
(WebCore::AlternativeTextController::show): Ditto.
(WebCore::AlternativeTextController::timerFired): Ditto.
(WebCore::AlternativeTextController::handleAlternativeTextUIResult): Ditto.
(WebCore::AlternativeTextController::recordAutocorrectionResponse): Ditto. Also
take a SimpleRange argument.
(WebCore::AlternativeTextController::markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand):
Ditto.
(WebCore::AlternativeTextController::respondToMarkerAtEndOfWord):Ditto.
* editing/AlternativeTextController.h: Update for the above changes.

* editing/Editor.cpp:
(WebCore::Editor::markMisspellingsAfterTypingToWord): Pass a reference
to a live range to plainText.
(WebCore::correctSpellcheckingPreservingTextCheckingParagraph): Ditto.
Also refactor for simplicity and clarity.
(WebCore::Editor::markAndReplaceFor): Ditto.
(WebCore::Editor::changeBackToReplacedString): Ditto.
(WebCore::Editor::transpose): Ditto.
(WebCore::Editor::addRangeToKillRing): Ditto.
(WebCore::Editor::stringForCandidateRequest const): Ditto.
* editing/ReplaceRangeWithTextCommand.cpp:
(WebCore::ReplaceRangeWithTextCommand::doApply): Ditto.
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplacementFragment::ReplacementFragment): Ditto.
* editing/SpellingCorrectionCommand.cpp:
(WebCore::SpellingCorrectionCommand::doApply): Ditto.
* editing/TextCheckingHelper.cpp:
(WebCore::TextCheckingParagraph::text const): Ditto.
(WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar): Ditto.

* editing/TextIterator.cpp: Use more constexpr.
(WebCore::characterSubrange): Deleted. Moved the logic from this function
into the one place using it, the rangeForMatch function.
(WebCore::resolveCharacterLocation): Deleted. Moved to the header file.
(WebCore::TextIterator::getLocationAndLengthFromRange): Deleted.
(WebCore::plainText): Deleted the overload that takes a live range pointer.
(WebCore::plainTextUsingBackwardsTextIteratorForTesting): Deleted. Moved
the implementation to Internals. There's nothing special about the algorithm,
it uses SimplifiedBackwardsTextIterator in a simple way.
(WebCore::collapsedToBoundary): Deleted. Moved the code to the one place
it's used, the rangeForMatch function.
(WebCore::forEachMatch): Renamed from findPlainTextMatches and changed to
work without any use of live ranges and to use CharacterRange.
(WebCore::rangeForMatch): Rewrote to include more of the logic, removing
the collapsedToBoundary and characterSubrange functions, and to not use
any live ranges.
(WebCore::findClosestPlainText): Rewrote to tighten up the algorithm a
bit, break ties based on the search direction, and have less repetitive code.
(WebCore::findPlainText): Rewrote for clarity.

* editing/TextIterator.h: Removed the forward declaration of Range.
Removed the overload of plainText that takes a live range pointer. Moved
the functions that work with character ranges up to the top of the file,
grouped the other functions more logically. Deleted the
TextIterator::getLocationAndLengthFromRange function. Put some inline
function definitions here.

* editing/TypingCommand.cpp:
(WebCore::TypingCommand::markMisspellingsAfterTyping): Pass a reference
to a live range to plainText.
* editing/VisibleUnits.cpp:
(WebCore::charactersAroundPosition): Pass a SimpleRange to plainText
rather than a live range.
* editing/cocoa/DataDetection.mm:
(WebCore::detectItemAtPositionWithRange): Pass a reference to a live
range to plainText.
* editing/cocoa/DictionaryLookup.mm:
(WebCore::DictionaryLookup::rangeForSelection): Ditto.

* editing/cocoa/HTMLConverter.h: Tweaked #if a bit.

* editing/mac/DictionaryLookupLegacy.mm:
(WebCore::DictionaryLookup::rangeForSelection): Pass a reference to a
live range to plainText. Also rewrote logic to use mostly SimpleRange.
(WebCore::DictionaryLookup::rangeAtHitTestResult): Ditto.
* editing/markup.cpp:
(WebCore::StyledMarkupAccumulator::renderedTextRespectingRange): Ditto.
* html/HTMLTextAreaElement.cpp:
(WebCore::HTMLTextAreaElement::handleBeforeTextInsertedEvent const): Ditto.
* page/ContextMenuController.cpp:
(WebCore::ContextMenuController::contextMenuItemSelected): Ditto.
* page/DOMSelection.cpp:
(WebCore::DOMSelection::toString): Ditto.

* page/Page.cpp:
(WebCore::Page::replaceRangesWithText): Rewrote to replaces use of
TextIterator::getLocationAndLengthFromRange with characterRange.

* page/ios/FrameIOS.mm:
(WebCore::Frame::interpretationsForCurrentRoot const): Pass a
reference to a live range to plainText.

* testing/Internals.cpp:
(WebCore::Internals::locationFromRange): Use characterRange.
(WebCore::Internals::lengthFromRange): Ditto.
(WebCore::Internals::rangeAsTextUsingBackwardsTextIterator):
USe SimplifiedBackwardsTextIterator directly since we no longer have
the function plainTextUsingBackwardsTextIteratorForTesting.

Source/WebKit:

* Shared/EditingRange.cpp:
(WebKit::EditingRange::fromRange): Use characterRange.

* Shared/mac/AttributedString.h: Added a constructor that takes rvalue
references so we can initialize this slightly more efficiently.

* Shared/mac/AttributedString.mm:
(IPC::ArgumentCoder<WebKit::AttributedString>::decode): Pass rvalue
references when creating an AttributedString.

* UIProcess/mac/TextCheckerMac.mm:
(WebKit::TextChecker::updateSpellingUIWithGrammarString): Simplify the
code to remove some local variables that weren't helpful.

* WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h: Made the
annotatedSubstringBetweenPositions a static member function. Also used
const& argument types to cut down on reference count churn a bit.

* WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm:
(WebKit::TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection):
Streamlined and made this use characterCount instead of
TextIterator::getLocationAndLengthFromRange.
(WebKit::TextCheckingControllerProxy::replaceRelativeToSelection): Tweaked
the argument type.
(WebKit::TextCheckingControllerProxy::removeAnnotationRelativeToSelection):
Ditto. Also removed some unnecessary use of NSString.
(WebKit::TextCheckingControllerProxy::annotatedSubstringBetweenPositions):
Rewrote to no longer use live ranges.

* WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
(WebKit::WebPage::getContentsAsAttributedString): Use construction and
rvalue references to tigten things up a bit.

* WebProcess/WebPage/WebFrame.cpp:
(WebKit::WebFrame::contentsAsString const): Use a SimpleRange instead of
a live range to pass to plainText.
* WebProcess/WebPage/glib/WebPageGLib.cpp:
(WebKit::WebPage::getPlatformEditorState const): Pass references to
live ranges to plainText.
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::updateSelectionWithDelta): Rewrote to minimize use of
live ranges.
(WebKit::WebPage::requestDocumentEditingContext): Ditto.
* WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::getPlatformEditorState const): Pass reference to
a live range to plainText.

Source/WebKitLegacy/mac:

* WebCoreSupport/WebEditorClient.mm:
(WebEditorClient::updateSpellingUIWithGrammarString): Remove some unnecessary
use of local variabels for NSString.
(WebEditorClient::requestCandidatesForSelection): Pass a reference to a live
range to plainText.
* WebView/WebFrame.mm:
(-[WebFrame _stringForRange:]): Ditto.
(-[WebFrame _convertToNSRange:]): Use characterRange instead of
TextIterator::getLocationAndLengthFromRange.

Source/WebKitLegacy/win:

* WebFrame.cpp:
(WebFrame::string): Use a SimpleRange instead of a live range
to call plainText.
* WebView.cpp:
(WebView::selectedRangeForTesting): Use a SimpleRange instead
of a live range to call characterRange instead of
TextIterator::getLocationAndLengthFromRange.

LayoutTests:

* editing/text-iterator/find-string-on-flat-tree-expected.txt:
* editing/text-iterator/find-string-on-flat-tree.html:
Updated test for a progress where we properly find strings across a shadow boundary.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@259401 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 794f1f3..c8ecf24 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2020-04-01  Darin Adler  <darin@apple.com>
+
+        Remove all uses of live ranges from TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=209723
+
+        Reviewed by Antti Koivisto.
+
+        * editing/text-iterator/find-string-on-flat-tree-expected.txt:
+        * editing/text-iterator/find-string-on-flat-tree.html:
+        Updated test for a progress where we properly find strings across a shadow boundary.
+
 2020-04-02  David Kilzer  <ddkilzer@apple.com>
 
         API::PageConfiguration may have conflicting preference values between WebPreferences and WebPreferencesStore::ValueMap instance variables
diff --git a/LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt b/LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt
index 598d0d4..6a36821 100644
--- a/LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt
+++ b/LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt
@@ -76,7 +76,7 @@
 PASS internals.countFindMatches('in-', ['DoNotTraverseFlatTree']) is 2
 PASS internals.countFindMatches('in-', []) is 4
 PASS internals.countFindMatches('in-shadow in-document', ['DoNotTraverseFlatTree']) is 0
-PASS internals.countFindMatches('in-shadow in-document', []) is 0
+PASS internals.countFindMatches('in-shadow in-document', []) is 1
 PASS rangeText(internals.rangeOfString('inside-display-none', null, [])) is null
 PASS successfullyParsed is true
 
diff --git a/LayoutTests/editing/text-iterator/find-string-on-flat-tree.html b/LayoutTests/editing/text-iterator/find-string-on-flat-tree.html
index 1905253..3659ff2 100644
--- a/LayoutTests/editing/text-iterator/find-string-on-flat-tree.html
+++ b/LayoutTests/editing/text-iterator/find-string-on-flat-tree.html
@@ -160,7 +160,7 @@
     shouldBe("internals.countFindMatches('in-', [])", "4");
 
     shouldBe("internals.countFindMatches('in-shadow in-document', ['DoNotTraverseFlatTree'])", "0");
-    shouldBe("internals.countFindMatches('in-shadow in-document', [])", "0");
+    shouldBe("internals.countFindMatches('in-shadow in-document', [])", "1");
 
     shouldBe("rangeText(internals.rangeOfString('inside-display-none', null, []))", "null");
 
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 15c584a..7d8f2b9 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,157 @@
+2020-04-01  Darin Adler  <darin@apple.com>
+
+        Remove all uses of live ranges from TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=209723
+
+        Reviewed by Antti Koivisto.
+
+        - Replaced TextIterator::getLocationAndLengthFromRange with a function named
+          characterRange that computes a CharacterRange given a scope and a range.
+        - Removed the overload of plainText that takes a pointer to a live range.
+        - Update the many callers of plainText that pass a pointer to a live range
+          to pass a reference instead, adding null checks as needed to preserve behavior.
+        - Rewrote some call sites to not use live ranges at all, or use them minimally.
+
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::nextSentenceEndPosition const): Streamlined
+        the logic in this function, using a smaller number of null checks since the
+        functions we are calling also do null checks, simpler variable names and
+        fewer local variables. Pass a reference to a live range rather than a
+        pointer to the plainText function.
+        (WebCore::AccessibilityObject::previousSentenceStartPosition const): Ditto.
+        (WebCore::AccessibilityObject::nextParagraphEndPosition const): Ditto.
+        (WebCore::AccessibilityObject::previousParagraphStartPosition const): Ditto.
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::textUnderElement const): Ditto.
+        (WebCore::boundsForRects): Converted this to a file-local function and changed
+        it to take a SimpleRange instead of a live range.
+        (WebCore::AccessibilityRenderObject::boundsForVisiblePositionRange const): Pass
+        a reference to a live range instead of a pointer.
+        (WebCore::AccessibilityRenderObject::boundsForRange const): Ditto.
+        * accessibility/AccessibilityRenderObject.h: Removed boundsForRects.
+
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (-[WebAccessibilityObjectWrapper _convertToNSRange:]): Rewrote to use
+        characterRange instead of TextIterator::getLocationAndLengthFromRange.
+
+        * dom/BoundaryPoint.h:
+        (WebCore::makeBoundaryPointBeforeNodeContents): Added.
+
+        * dom/Element.cpp:
+        (WebCore::Element::innerText): Pass a SimpleRange instead of a live range
+        to the plainText function.
+
+        * dom/Range.cpp:
+        (WebCore::Range::text const): Pass a reference to a live range instead of a
+        pointer to the plainText function.
+
+        * dom/SimpleRange.cpp:
+        (WebCore::makeBoundaryPointAfterNodeContents): Added.
+        (WebCore::makeRangeSelectingNodeContents): Use makeBoundaryPointBeforeNodeContents
+        and makeBoundaryPointAfterNodeContents
+
+        * editing/AlternativeTextController.cpp:
+        (WebCore::AlternativeTextController::applyPendingCorrection): Pass a reference
+        to a live range to plainText.
+        (WebCore::AlternativeTextController::show): Ditto.
+        (WebCore::AlternativeTextController::timerFired): Ditto.
+        (WebCore::AlternativeTextController::handleAlternativeTextUIResult): Ditto.
+        (WebCore::AlternativeTextController::recordAutocorrectionResponse): Ditto. Also
+        take a SimpleRange argument.
+        (WebCore::AlternativeTextController::markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand):
+        Ditto.
+        (WebCore::AlternativeTextController::respondToMarkerAtEndOfWord):Ditto.
+        * editing/AlternativeTextController.h: Update for the above changes.
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::markMisspellingsAfterTypingToWord): Pass a reference
+        to a live range to plainText.
+        (WebCore::correctSpellcheckingPreservingTextCheckingParagraph): Ditto.
+        Also refactor for simplicity and clarity.
+        (WebCore::Editor::markAndReplaceFor): Ditto.
+        (WebCore::Editor::changeBackToReplacedString): Ditto.
+        (WebCore::Editor::transpose): Ditto.
+        (WebCore::Editor::addRangeToKillRing): Ditto.
+        (WebCore::Editor::stringForCandidateRequest const): Ditto.
+        * editing/ReplaceRangeWithTextCommand.cpp:
+        (WebCore::ReplaceRangeWithTextCommand::doApply): Ditto.
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::ReplacementFragment::ReplacementFragment): Ditto.
+        * editing/SpellingCorrectionCommand.cpp:
+        (WebCore::SpellingCorrectionCommand::doApply): Ditto.
+        * editing/TextCheckingHelper.cpp:
+        (WebCore::TextCheckingParagraph::text const): Ditto.
+        (WebCore::TextCheckingHelper::findFirstMisspellingOrBadGrammar): Ditto.
+
+        * editing/TextIterator.cpp: Use more constexpr.
+        (WebCore::characterSubrange): Deleted. Moved the logic from this function
+        into the one place using it, the rangeForMatch function.
+        (WebCore::resolveCharacterLocation): Deleted. Moved to the header file.
+        (WebCore::TextIterator::getLocationAndLengthFromRange): Deleted.
+        (WebCore::plainText): Deleted the overload that takes a live range pointer.
+        (WebCore::plainTextUsingBackwardsTextIteratorForTesting): Deleted. Moved
+        the implementation to Internals. There's nothing special about the algorithm,
+        it uses SimplifiedBackwardsTextIterator in a simple way.
+        (WebCore::collapsedToBoundary): Deleted. Moved the code to the one place
+        it's used, the rangeForMatch function.
+        (WebCore::forEachMatch): Renamed from findPlainTextMatches and changed to
+        work without any use of live ranges and to use CharacterRange.
+        (WebCore::rangeForMatch): Rewrote to include more of the logic, removing
+        the collapsedToBoundary and characterSubrange functions, and to not use
+        any live ranges.
+        (WebCore::findClosestPlainText): Rewrote to tighten up the algorithm a
+        bit, break ties based on the search direction, and have less repetitive code.
+        (WebCore::findPlainText): Rewrote for clarity.
+
+        * editing/TextIterator.h: Removed the forward declaration of Range.
+        Removed the overload of plainText that takes a live range pointer. Moved
+        the functions that work with character ranges up to the top of the file,
+        grouped the other functions more logically. Deleted the
+        TextIterator::getLocationAndLengthFromRange function. Put some inline
+        function definitions here.
+
+        * editing/TypingCommand.cpp:
+        (WebCore::TypingCommand::markMisspellingsAfterTyping): Pass a reference
+        to a live range to plainText.
+        * editing/VisibleUnits.cpp:
+        (WebCore::charactersAroundPosition): Pass a SimpleRange to plainText
+        rather than a live range.
+        * editing/cocoa/DataDetection.mm:
+        (WebCore::detectItemAtPositionWithRange): Pass a reference to a live
+        range to plainText.
+        * editing/cocoa/DictionaryLookup.mm:
+        (WebCore::DictionaryLookup::rangeForSelection): Ditto.
+
+        * editing/cocoa/HTMLConverter.h: Tweaked #if a bit.
+
+        * editing/mac/DictionaryLookupLegacy.mm:
+        (WebCore::DictionaryLookup::rangeForSelection): Pass a reference to a
+        live range to plainText. Also rewrote logic to use mostly SimpleRange.
+        (WebCore::DictionaryLookup::rangeAtHitTestResult): Ditto.
+        * editing/markup.cpp:
+        (WebCore::StyledMarkupAccumulator::renderedTextRespectingRange): Ditto.
+        * html/HTMLTextAreaElement.cpp:
+        (WebCore::HTMLTextAreaElement::handleBeforeTextInsertedEvent const): Ditto.
+        * page/ContextMenuController.cpp:
+        (WebCore::ContextMenuController::contextMenuItemSelected): Ditto.
+        * page/DOMSelection.cpp:
+        (WebCore::DOMSelection::toString): Ditto.
+
+        * page/Page.cpp:
+        (WebCore::Page::replaceRangesWithText): Rewrote to replaces use of
+        TextIterator::getLocationAndLengthFromRange with characterRange.
+
+        * page/ios/FrameIOS.mm:
+        (WebCore::Frame::interpretationsForCurrentRoot const): Pass a
+        reference to a live range to plainText.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::locationFromRange): Use characterRange.
+        (WebCore::Internals::lengthFromRange): Ditto.
+        (WebCore::Internals::rangeAsTextUsingBackwardsTextIterator):
+        USe SimplifiedBackwardsTextIterator directly since we no longer have
+        the function plainTextUsingBackwardsTextIteratorForTesting.
+
 2020-04-02  Ryosuke Niwa  <rniwa@webkit.org>
 
         Remove a superflous blank line added in the previous commit as pointed out during the code review.
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
index 51ca4cf..e887524 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityObject.cpp
@@ -1443,79 +1443,47 @@
     return startPosition;
 }
 
-VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
+VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& position) const
 {
     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
     // Related? <rdar://problem/3927736> Text selection broken in 8A336
-    if (visiblePos.isNull())
-        return VisiblePosition();
 
-    // make sure we move off of a sentence end
-    VisiblePosition nextVisiblePos = visiblePos.next();
-    if (nextVisiblePos.isNull())
-        return VisiblePosition();
+    // Make sure we move off of a sentence end.
+    auto nextPosition = position.next();
+    auto lineRange = makeRange(startOfLine(nextPosition), endOfLine(nextPosition));
+    if (!lineRange)
+        return { };
 
-    // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
-    // see this empty line.  Instead, return the end position of the empty line.
-    VisiblePosition endPosition;
-    
-    String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
-    if (lineString.isEmpty())
-        endPosition = nextVisiblePos;
-    else
-        endPosition = endOfSentence(nextVisiblePos);
-
-    return endPosition;
+    // An empty line is considered a sentence. If it's skipped, then the sentence parser will not
+    // see this empty line. Instead, return the end position of the empty line.
+    // FIXME: It would be more efficient to use hasAnyPlainText instead of plainText.isEmpty.
+    return plainText(*lineRange).isEmpty() ? nextPosition : endOfSentence(nextPosition);
 }
 
-VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
+VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& position) const
 {
     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
     // Related? <rdar://problem/3927736> Text selection broken in 8A336
-    if (visiblePos.isNull())
-        return VisiblePosition();
 
-    // make sure we move off of a sentence start
-    VisiblePosition previousVisiblePos = visiblePos.previous();
-    if (previousVisiblePos.isNull())
-        return VisiblePosition();
+    // Make sure we move off of a sentence start.
+    auto previousPosition = position.previous();
+    auto lineRange = makeRange(startOfLine(previousPosition), endOfLine(previousPosition));
+    if (!lineRange)
+        return { };
 
-    // treat empty line as a separate sentence.
-    VisiblePosition startPosition;
-    
-    String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
-    if (lineString.isEmpty())
-        startPosition = previousVisiblePos;
-    else
-        startPosition = startOfSentence(previousVisiblePos);
-
-    return startPosition;
+    // Treat empty line as a separate sentence.
+    // FIXME: It would be more efficient to use hasAnyPlainText instead of plainText.isEmpty.
+    return plainText(*lineRange).isEmpty() ? previousPosition : startOfSentence(previousPosition);
 }
 
-VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
+VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& position) const
 {
-    if (visiblePos.isNull())
-        return VisiblePosition();
-
-    // make sure we move off of a paragraph end
-    VisiblePosition nextPos = visiblePos.next();
-    if (nextPos.isNull())
-        return VisiblePosition();
-
-    return endOfParagraph(nextPos);
+    return endOfParagraph(position.next());
 }
 
-VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
+VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& position) const
 {
-    if (visiblePos.isNull())
-        return VisiblePosition();
-
-    // make sure we move off of a paragraph start
-    VisiblePosition previousPos = visiblePos.previous();
-    if (previousPos.isNull())
-        return VisiblePosition();
-
-    return startOfParagraph(previousPos);
+    return startOfParagraph(position.previous());
 }
 
 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
index e616ea9..35a66be 100644
--- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
@@ -664,7 +664,7 @@
                 // style update/layout here. See also AXObjectCache::deferTextChangedIfNeeded().
                 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->childNeedsStyleRecalc());
                 ASSERT_WITH_SECURITY_IMPLICATION(!nodeDocument->view()->layoutContext().isInRenderTreeLayout());
-                return plainText(textRange.get(), textIteratorBehaviorForTextRange());
+                return plainText(*textRange, textIteratorBehaviorForTextRange());
             }
         }
     
@@ -2081,16 +2081,15 @@
     return false;
 }
 
-IntRect AccessibilityRenderObject::boundsForRects(LayoutRect const& rect1, LayoutRect const& rect2, RefPtr<Range> const& dataRange)
+static IntRect boundsForRects(const LayoutRect& rect1, const LayoutRect& rect2, const SimpleRange& dataRange)
 {
     LayoutRect ourRect = rect1;
     ourRect.unite(rect2);
-    
-    // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
+
+    // If the rectangle spans lines and contains multiple text characters, use the range's bounding box intead.
     if (rect1.maxY() != rect2.maxY()) {
-        LayoutRect boundingBox = dataRange->absoluteBoundingBox();
-        String rangeString = plainText(dataRange.get());
-        if (rangeString.length() > 1 && !boundingBox.isEmpty())
+        LayoutRect boundingBox = createLiveRange(dataRange)->absoluteBoundingBox();
+        if (characterCount(dataRange) > 1 && !boundingBox.isEmpty())
             ourRect = boundingBox;
     }
 
@@ -2120,8 +2119,7 @@
         }
     }
     
-    RefPtr<Range> dataRange = makeRange(range.start, range.end);
-    return boundsForRects(rect1, rect2, dataRange);
+    return boundsForRects(rect1, rect2, *makeRange(range.start, range.end));
 }
 
 IntRect AccessibilityRenderObject::boundsForRange(const RefPtr<Range> range) const
@@ -2152,7 +2150,7 @@
         }
     }
     
-    return boundsForRects(rect1, rect2, range);
+    return boundsForRects(rect1, rect2, *range);
 }
 
 bool AccessibilityRenderObject::isVisiblePositionRangeInDifferentDocument(const VisiblePositionRange& range) const
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.h b/Source/WebCore/accessibility/AccessibilityRenderObject.h
index 776b48f..7ecf04a 100644
--- a/Source/WebCore/accessibility/AccessibilityRenderObject.h
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.h
@@ -293,8 +293,6 @@
     bool canHavePlainText() const;
     // Special handling of click point for links.
     IntPoint linkClickPoint();
-    // Rects utilities.
-    static IntRect boundsForRects(LayoutRect const&, LayoutRect const&, RefPtr<Range> const&);
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
index f6a7231..88f8948 100644
--- a/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
+++ b/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
@@ -3636,21 +3636,18 @@
 {
     ASSERT(isMainThread());
     
-    NSRange result = NSMakeRange(NSNotFound, 0);
     if (!range)
-        return result;
+        return NSMakeRange(NSNotFound, 0);
     
-    Document* document = self.axBackingObject->document();
+    auto document = self.axBackingObject->document();
     if (!document)
-        return result;
-    
-    size_t location;
-    size_t length;
-    TextIterator::getLocationAndLengthFromRange(document->documentElement(), range, location, length);
-    result.location = location;
-    result.length = length;
-    
-    return result;
+        return NSMakeRange(NSNotFound, 0);
+
+    auto documentElement = document->documentElement();
+    if (!documentElement)
+        return NSMakeRange(NSNotFound, 0);
+
+    return characterRange(makeBoundaryPointBeforeNodeContents(*documentElement), *range);
 }
 
 - (NSInteger)_indexForTextMarker:(id)marker
diff --git a/Source/WebCore/dom/BoundaryPoint.h b/Source/WebCore/dom/BoundaryPoint.h
index a5d8e3e..5cbbfdc 100644
--- a/Source/WebCore/dom/BoundaryPoint.h
+++ b/Source/WebCore/dom/BoundaryPoint.h
@@ -46,6 +46,8 @@
 
 bool operator==(const BoundaryPoint&, const BoundaryPoint&);
 
+BoundaryPoint makeBoundaryPointBeforeNodeContents(Node&);
+
 inline BoundaryPoint::BoundaryPoint(Ref<Node>&& container, unsigned offset)
     : container(WTFMove(container))
     , offset(offset)
@@ -75,4 +77,9 @@
     return a.container.ptr() == b.container.ptr() && a.offset == b.offset;
 }
 
+inline BoundaryPoint makeBoundaryPointBeforeNodeContents(Node& node)
+{
+    return { node, 0 };
+}
+
 }
diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp
index 6002c3d..c9dbd5e 100644
--- a/Source/WebCore/dom/Element.cpp
+++ b/Source/WebCore/dom/Element.cpp
@@ -3197,7 +3197,7 @@
     if (!renderer())
         return textContent(true);
 
-    return plainText(rangeOfContents(*this).ptr());
+    return plainText(makeRangeSelectingNodeContents(*this));
 }
 
 String Element::outerText()
diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp
index 45c1bcf..be4eb69 100644
--- a/Source/WebCore/dom/Range.cpp
+++ b/Source/WebCore/dom/Range.cpp
@@ -951,7 +951,7 @@
     // FIXME: As with innerText, we'd like this to work even if there are no render objects.
     startContainer().document().updateLayout();
 
-    return plainText(this);
+    return plainText(*this);
 }
 
 // https://w3c.github.io/DOM-Parsing/#widl-Range-createContextualFragment-DocumentFragment-DOMString-fragment
diff --git a/Source/WebCore/dom/SimpleRange.cpp b/Source/WebCore/dom/SimpleRange.cpp
index a7d91cd2..8eead06 100644
--- a/Source/WebCore/dom/SimpleRange.cpp
+++ b/Source/WebCore/dom/SimpleRange.cpp
@@ -68,9 +68,14 @@
     return node.countChildNodes();
 }
 
+static BoundaryPoint makeBoundaryPointAfterNodeContents(Node& node)
+{
+    return { node, length(node) };
+}
+
 SimpleRange makeRangeSelectingNodeContents(Node& node)
 {
-    return { { node, 0 }, { node, length(node) } };
+    return { makeBoundaryPointBeforeNodeContents(node), makeBoundaryPointAfterNodeContents(node) };
 }
 
 }
diff --git a/Source/WebCore/editing/AlternativeTextController.cpp b/Source/WebCore/editing/AlternativeTextController.cpp
index f70da58..f1128b9 100644
--- a/Source/WebCore/editing/AlternativeTextController.cpp
+++ b/Source/WebCore/editing/AlternativeTextController.cpp
@@ -130,9 +130,11 @@
     VisiblePosition startOfSelection = selectionAfterTyping.visibleStart();
     VisibleSelection currentWord = VisibleSelection(startOfWord(startOfSelection, LeftWordIfOnBoundary), endOfWord(startOfSelection, RightWordIfOnBoundary));
     if (currentWord.visibleEnd() == startOfSelection) {
-        String wordText = plainText(currentWord.toNormalizedRange().get());
-        if (wordText.length() > 0 && isAmbiguousBoundaryCharacter(wordText[wordText.length() - 1]))
-            doApplyCorrection = false;
+        if (auto wordRange = currentWord.firstRange()) {
+            String wordText = plainText(*wordRange);
+            if (!wordText.isEmpty() && isAmbiguousBoundaryCharacter(wordText[wordText.length() - 1]))
+                doApplyCorrection = false;
+        }
     }
     if (doApplyCorrection)
         handleAlternativeTextUIResult(dismissSoon(ReasonForDismissingAlternativeTextAccepted)); 
@@ -155,7 +157,7 @@
     FloatRect boundingBox = rootViewRectForRange(&rangeToReplace);
     if (boundingBox.isEmpty())
         return;
-    m_originalText = plainText(&rangeToReplace);
+    m_originalText = plainText(rangeToReplace);
     m_rangeWithAlternative = &rangeToReplace;
     m_details = replacement;
     m_isActive = true;
@@ -249,7 +251,7 @@
         if (replacementString.isEmpty())
             break;
         m_isActive = true;
-        m_originalText = plainText(m_rangeWithAlternative.get());
+        m_originalText = plainText(*m_rangeWithAlternative);
         FloatRect boundingBox = rootViewRectForRange(m_rangeWithAlternative.get());
         if (!boundingBox.isEmpty()) {
             if (AlternativeTextClient* client = alternativeTextClient())
@@ -258,9 +260,9 @@
     }
         break;
     case AlternativeTextTypeSpellingSuggestions: {
-        if (!m_rangeWithAlternative || plainText(m_rangeWithAlternative.get()) != m_originalText)
+        if (!m_rangeWithAlternative || plainText(*m_rangeWithAlternative) != m_originalText)
             break;
-        String paragraphText = plainText(&TextCheckingParagraph(*m_rangeWithAlternative).paragraphRange());
+        String paragraphText = plainText(TextCheckingParagraph(*m_rangeWithAlternative).paragraphRange());
         Vector<String> suggestions;
         textChecker()->getGuessesForWord(m_originalText, paragraphText, m_frame.selection().selection(), suggestions);
         if (suggestions.isEmpty()) {
@@ -303,7 +305,7 @@
     if (!rangeWithAlternative || m_frame.document() != &rangeWithAlternative->ownerDocument())
         return;
 
-    String currentWord = plainText(rangeWithAlternative);
+    String currentWord = plainText(*rangeWithAlternative);
     // Check to see if the word we are about to correct has been changed between timer firing and callback being triggered.
     if (currentWord != m_originalText)
         return;
@@ -416,7 +418,7 @@
     return nullptr;
 }
 
-void AlternativeTextController::recordAutocorrectionResponse(AutocorrectionResponse response, const String& replacedString, Range* replacementRange)
+void AlternativeTextController::recordAutocorrectionResponse(AutocorrectionResponse response, const String& replacedString, const SimpleRange& replacementRange)
 {
     if (auto client = alternativeTextClient())
         client->recordAutocorrectionResponse(response, replacedString, plainText(replacementRange));
@@ -475,7 +477,7 @@
         return;
 
     auto precedingCharacterRange = Range::create(*m_frame.document(), precedingCharacterPosition, endOfSelection);
-    String string = plainText(precedingCharacterRange.ptr());
+    String string = plainText(precedingCharacterRange);
     if (string.isEmpty() || !deprecatedIsEditingWhitespace(string[string.length() - 1]))
         return;
 
@@ -524,7 +526,7 @@
         return false;
     Node* node = endOfWordPosition.containerNode();
     auto wordRange = Range::create(*m_frame.document(), node, marker.startOffset(), node, marker.endOffset());
-    String currentWord = plainText(wordRange.ptr());
+    String currentWord = plainText(wordRange);
     if (!currentWord.length())
         return false;
     m_originalText = currentWord;
diff --git a/Source/WebCore/editing/AlternativeTextController.h b/Source/WebCore/editing/AlternativeTextController.h
index 27021ec..981d270 100644
--- a/Source/WebCore/editing/AlternativeTextController.h
+++ b/Source/WebCore/editing/AlternativeTextController.h
@@ -45,6 +45,7 @@
 class VisibleSelection;
 
 struct DictationAlternative;
+struct SimpleRange;
 struct TextCheckingResult;
 
 #if USE(AUTOCORRECTION_PANEL)
@@ -88,7 +89,7 @@
     bool isAutomaticSpellingCorrectionEnabled() UNLESS_ENABLED({ return false; })
     bool shouldRemoveMarkersUponEditing();
 
-    void recordAutocorrectionResponse(AutocorrectionResponse, const String& replacedString, Range* replacementRange) UNLESS_ENABLED({ UNUSED_PARAM(replacedString); UNUSED_PARAM(replacementRange); })
+    void recordAutocorrectionResponse(AutocorrectionResponse, const String& replacedString, const SimpleRange& replacementRange) UNLESS_ENABLED({ UNUSED_PARAM(replacedString); UNUSED_PARAM(replacementRange); })
     void markReversed(Range& changedRange) UNLESS_ENABLED({ UNUSED_PARAM(changedRange); })
     void markCorrection(Range& replacedRange, const String& replacedString) UNLESS_ENABLED({ UNUSED_PARAM(replacedRange); UNUSED_PARAM(replacedString); })
 
diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp
index 093b12b..a4e0602 100644
--- a/Source/WebCore/editing/Editor.cpp
+++ b/Source/WebCore/editing/Editor.cpp
@@ -2592,8 +2592,7 @@
         return;
     
     // Get the misspelled word.
-    const String misspelledWord = plainText(misspellingRange.get());
-    String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(misspelledWord);
+    String autocorrectedString = textChecker()->getAutoCorrectSuggestionForMisspelledWord(plainText(*misspellingRange));
 
     // If autocorrected word is non empty, replace the misspelled word by this word.
     if (!autocorrectedString.isEmpty()) {
@@ -2773,21 +2772,19 @@
     SpellingCorrectionCommand::create(rangeToReplace, replacement)->apply();
 }
 
-static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, Range& rangeToReplace, const String& replacement, CharacterRange resultRange)
+static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, Range& rangeToReplace, const String& replacement, CharacterRange resultCharacterRange)
 {
-    auto& scope = downcast<ContainerNode>(paragraph.paragraphRange().startContainer().rootNode());
-
-    size_t paragraphLocation;
-    size_t paragraphLength;
-    TextIterator::getLocationAndLengthFromRange(&scope, &paragraph.paragraphRange(), paragraphLocation, paragraphLength);
+    auto scopeNode = makeRef(downcast<ContainerNode>(paragraph.paragraphRange().startContainer().rootNode()));
+    auto paragraphCharacterRange = characterRange(makeBoundaryPointBeforeNodeContents(scopeNode), paragraph.paragraphRange());
 
     SpellingCorrectionCommand::create(rangeToReplace, replacement)->apply();
 
     // TextCheckingParagraph may be orphaned after SpellingCorrectionCommand mutated DOM.
     // See <rdar://10305315>, http://webkit.org/b/89526.
 
-    auto newParagraphRange = resolveCharacterRange(makeRangeSelectingNodeContents(scope), { paragraphLocation, paragraphLength + replacement.length() - resultRange.length });
-    auto spellCheckingRange = resolveCharacterRange(newParagraphRange, { resultRange.location, replacement.length() });
+    paragraphCharacterRange.length += replacement.length() - resultCharacterRange.length;
+    auto newParagraphRange = resolveCharacterRange(makeRangeSelectingNodeContents(scopeNode), paragraphCharacterRange);
+    auto spellCheckingRange = resolveCharacterRange(newParagraphRange, { resultCharacterRange.location, replacement.length() });
     paragraph = TextCheckingParagraph(createLiveRange(spellCheckingRange), createLiveRange(spellCheckingRange), createLiveRange(newParagraphRange));
 }
 
@@ -2885,7 +2882,7 @@
             if (!(shouldPerformReplacement || shouldCheckForCorrection || shouldMarkLink) || !doReplacement)
                 continue;
 
-            String replacedString = plainText(rangeToReplace.ptr());
+            String replacedString = plainText(rangeToReplace);
             const bool existingMarkersPermitReplacement = m_alternativeTextController->processMarkersOnTextToBeReplacedByResult(results[i], rangeToReplace, replacedString);
             if (!existingMarkersPermitReplacement)
                 continue;
@@ -2931,7 +2928,7 @@
 
                 if (resultType == TextCheckingType::Correction) {
                     auto replacementRange = paragraph.subrange({ resultLocation, replacement.length() });
-                    m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Accepted, replacedString, replacementRange.ptr());
+                    m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Accepted, replacedString, replacementRange.get());
 
                     // Add a marker so that corrections can easily be undone and won't be re-corrected.
                     m_alternativeTextController->markCorrection(replacementRange, replacedString);
@@ -2966,10 +2963,10 @@
         return;
 
     RefPtr<Range> selection = selectedRange();
-    if (!shouldInsertText(replacedString, selection.get(), EditorInsertAction::Pasted))
+    if (!selection || !shouldInsertText(replacedString, selection.get(), EditorInsertAction::Pasted))
         return;
     
-    m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Reverted, replacedString, selection.get());
+    m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Reverted, replacedString, *selection);
     TextCheckingParagraph paragraph(*selection);
     replaceSelectionWithText(replacedString, SelectReplacement::No, SmartReplace::No, EditAction::Insert);
     auto changedRange = paragraph.subrange(CharacterRange(paragraph.checkingStart(), replacedString.length()));
@@ -3201,7 +3198,7 @@
     VisibleSelection newSelection(*range, DOWNSTREAM);
 
     // Transpose the two characters.
-    String text = plainText(range.get());
+    String text = plainText(*range);
     if (text.length() != 2)
         return;
     String transposed = text.right(1) + text.left(1);
@@ -3221,7 +3218,7 @@
 
 void Editor::addRangeToKillRing(const Range& range, KillRingInsertionMode mode)
 {
-    addTextToKillRing(plainText(&range), mode);
+    addTextToKillRing(plainText(range), mode);
 }
 
 void Editor::addTextToKillRing(const String& text, KillRingInsertionMode mode)
@@ -3975,7 +3972,7 @@
     const VisibleSelection& selection = m_frame.selection().selection();
     RefPtr<Range> rangeForCurrentlyTypedString = candidateRangeForSelection(m_frame);
     if (rangeForCurrentlyTypedString && candidateWouldReplaceText(selection))
-        return plainText(rangeForCurrentlyTypedString.get());
+        return plainText(*rangeForCurrentlyTypedString);
 
     return String();
 }
diff --git a/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp b/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp
index ad78232..5460b75 100644
--- a/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp
+++ b/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp
@@ -63,8 +63,7 @@
     if (!frame().selection().shouldChangeSelection(selection))
         return;
 
-    String previousText = plainText(m_rangeToBeReplaced.get());
-    if (!previousText.length())
+    if (!characterCount(*m_rangeToBeReplaced))
         return;
 
     applyCommandToComposite(SetSelectionCommand::create(selection, FrameSelection::defaultSetSelectionOptions()));
diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.cpp b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
index d866e85..5278db9 100644
--- a/Source/WebCore/editing/ReplaceSelectionCommand.cpp
+++ b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
@@ -191,7 +191,7 @@
     }
     
     RefPtr<Range> range = VisibleSelection::selectionFromContentsOfNode(holder.get()).toNormalizedRange();
-    String text = plainText(range.get(), static_cast<TextIteratorBehavior>(TextIteratorEmitsOriginalText | TextIteratorIgnoresStyleVisibility));
+    String text = range ? plainText(*range, static_cast<TextIteratorBehavior>(TextIteratorEmitsOriginalText | TextIteratorIgnoresStyleVisibility)) : emptyString();
 
     removeInterchangeNodes(holder.get());
     removeUnrenderedNodes(holder.get());
diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.cpp b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
index 5133145..99e8f15 100644
--- a/Source/WebCore/editing/SpellingCorrectionCommand.cpp
+++ b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
@@ -99,7 +99,7 @@
 
 void SpellingCorrectionCommand::doApply()
 {
-    m_corrected = plainText(m_rangeToBeCorrected.ptr());
+    m_corrected = plainText(m_rangeToBeCorrected);
     if (!m_corrected.length())
         return;
 
diff --git a/Source/WebCore/editing/TextCheckingHelper.cpp b/Source/WebCore/editing/TextCheckingHelper.cpp
index 99b8e31..d72e07f 100644
--- a/Source/WebCore/editing/TextCheckingHelper.cpp
+++ b/Source/WebCore/editing/TextCheckingHelper.cpp
@@ -187,7 +187,7 @@
 StringView TextCheckingParagraph::text() const
 {
     if (m_text.isNull())
-        m_text = plainText(&paragraphRange());
+        m_text = plainText(paragraphRange());
     return m_text; 
 }
 
@@ -337,7 +337,7 @@
             lastIteration = true;
         }
         if (currentStartOffset < currentEndOffset) {
-            String paragraphString = plainText(paragraphRange.ptr());
+            String paragraphString = plainText(paragraphRange);
             if (paragraphString.length() > 0) {
                 bool foundGrammar = false;
                 uint64_t spellingLocation = 0;
diff --git a/Source/WebCore/editing/TextIterator.cpp b/Source/WebCore/editing/TextIterator.cpp
index 21864c8..172e2644 100644
--- a/Source/WebCore/editing/TextIterator.cpp
+++ b/Source/WebCore/editing/TextIterator.cpp
@@ -46,7 +46,6 @@
 #include "HTMLTextFormControlElement.h"
 #include "InlineTextBox.h"
 #include "NodeTraversal.h"
-#include "Range.h"
 #include "RenderImage.h"
 #include "RenderIterator.h"
 #include "RenderTableCell.h"
@@ -141,8 +140,8 @@
 
 // --------
 
-static constexpr unsigned bitsInWord = sizeof(unsigned) * 8;
-static constexpr unsigned bitInWordMask = bitsInWord - 1;
+constexpr unsigned bitsInWord = sizeof(unsigned) * 8;
+constexpr unsigned bitInWordMask = bitsInWord - 1;
 
 void BitStack::push(bool bit)
 {
@@ -1435,24 +1434,6 @@
     m_runOffset = 0;
 }
 
-static Ref<Range> characterSubrange(Document& document, CharacterIterator& it, int offset, int length)
-{
-    it.advance(offset);
-    if (it.atEnd())
-        return Range::create(document);
-
-    auto start = it.range().start;
-
-    if (length > 1)
-        it.advance(length - 1);
-    if (it.atEnd())
-        return Range::create(document);
-
-    auto end = it.range().end;
-
-    return createLiveRange(SimpleRange { start, end });
-}
-
 BackwardsCharacterIterator::BackwardsCharacterIterator(const SimpleRange& range)
     : m_underlyingIterator(range)
 {
@@ -1623,7 +1604,7 @@
 
 #if !UCONFIG_NO_COLLATION
 
-const size_t minimumSearchBufferSize = 8192;
+constexpr size_t minimumSearchBufferSize = 8192;
 
 #ifndef NDEBUG
 static bool searcherInUse;
@@ -1876,7 +1857,7 @@
 
 static inline bool isSeparator(UChar32 character)
 {
-    static const bool latin1SeparatorTable[256] = {
+    static constexpr bool latin1SeparatorTable[256] = {
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // space ! " # $ % & ' ( ) * + , - . /
@@ -2276,7 +2257,7 @@
         append(characters[0], true);
         return 1;
     }
-    const int maxFoldedCharacters = 16; // sensible maximum is 3, this should be more than enough
+    constexpr int maxFoldedCharacters = 16; // sensible maximum is 3, this should be more than enough
     UChar foldedCharacters[maxFoldedCharacters];
     UErrorCode status = U_ZERO_ERROR;
     int numFoldedCharacters = u_strFoldCase(foldedCharacters, maxFoldedCharacters, characters, 1, U_FOLD_CASE_DEFAULT, &status);
@@ -2415,31 +2396,6 @@
     return resultRange;
 }
 
-BoundaryPoint resolveCharacterLocation(const SimpleRange& scope, uint64_t location, TextIteratorBehavior behavior)
-{
-    return resolveCharacterRange(scope, { location, 0 }, behavior).start;
-}
-
-bool TextIterator::getLocationAndLengthFromRange(Node* scope, const Range* range, size_t& location, size_t& length)
-{
-    location = notFound;
-    length = 0;
-
-    // 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))
-        return false;
-    if (&range->endContainer() != scope && !range->endContainer().isDescendantOf(scope))
-        return false;
-
-    location = characterCount({ { *scope, 0 }, { range->startContainer(), range->startOffset() } });
-    length = characterCount({ { *scope, 0 }, { range->endContainer(), range->endOffset() } }) - location;
-    return true;
-}
-
 // --------
 
 bool hasAnyPlainText(const SimpleRange& range, TextIteratorBehavior behavior)
@@ -2454,7 +2410,7 @@
 String plainText(const SimpleRange& range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
 {
     // The initial buffer size can be critical for performance: https://bugs.webkit.org/show_bug.cgi?id=81192
-    static const unsigned initialCapacity = 1 << 15;
+    constexpr unsigned initialCapacity = 1 << 15;
 
     auto document = makeRef(range.start.document());
 
@@ -2481,32 +2437,11 @@
     return result;
 }
 
-String plainText(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
-{
-    if (!range)
-        return emptyString();
-    return plainText(*range, defaultBehavior, isDisplayString);
-}
-
-String plainTextUsingBackwardsTextIteratorForTesting(const SimpleRange& range)
-{
-    String result;
-    for (SimplifiedBackwardsTextIterator backwardsIterator(createLiveRange(range)); !backwardsIterator.atEnd(); backwardsIterator.advance())
-        result.insert(backwardsIterator.text().toString(), 0);
-    return result;
-}
-
 String plainTextReplacingNoBreakSpace(const SimpleRange& range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
 {
     return plainText(range, defaultBehavior, isDisplayString).replace(noBreakSpace, ' ');
 }
 
-static SimpleRange collapsedToBoundary(const SimpleRange& range, bool forward)
-{
-    auto& boundary = forward ? range.end : range.start;
-    return { boundary, boundary };
-}
-
 static TextIteratorBehavior findIteratorOptions(FindOptions options)
 {
     TextIteratorBehavior iteratorOptions = TextIteratorEntersTextControls | TextIteratorClipsToFrameAncestors;
@@ -2515,20 +2450,19 @@
     return iteratorOptions;
 }
 
-static void findPlainTextMatches(const SimpleRange& range, const String& target, FindOptions options, const WTF::Function<bool(size_t, size_t)>& match)
+static void forEachMatch(const SimpleRange& range, const String& target, FindOptions options, const Function<bool(CharacterRange)>& match)
 {
     SearchBuffer buffer(target, options);
     if (buffer.needsMoreContext()) {
-        Ref<Range> beforeStartRange = range.start.document().createRange();
-        beforeStartRange->setEnd(range.start.container.copyRef(), range.start.offset);
-        for (SimplifiedBackwardsTextIterator backwardsIterator(beforeStartRange.get()); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
+        auto beforeStartRange = SimpleRange { makeBoundaryPointBeforeNodeContents(range.start.document()), range.start };
+        for (SimplifiedBackwardsTextIterator backwardsIterator(beforeStartRange); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
             buffer.prependContext(backwardsIterator.text());
             if (!buffer.needsMoreContext())
                 break;
         }
     }
 
-    CharacterIterator findIterator(createLiveRange(range), findIteratorOptions(options));
+    CharacterIterator findIterator(range, findIteratorOptions(options));
     while (!findIterator.atEnd()) {
         findIterator.advance(buffer.append(findIterator.text()));
         while (1) {
@@ -2543,53 +2477,69 @@
             }
             size_t lastCharacterInBufferOffset = findIterator.characterOffset();
             ASSERT(lastCharacterInBufferOffset >= matchStartOffset);
-            if (match(lastCharacterInBufferOffset - matchStartOffset, newMatchLength))
+            if (match(CharacterRange(lastCharacterInBufferOffset - matchStartOffset, newMatchLength)))
                 return;
         }
     }
 }
 
-static SimpleRange rangeForMatch(const SimpleRange& range, FindOptions options, size_t matchStart, size_t matchLength, bool searchForward)
+static SimpleRange rangeForMatch(const SimpleRange& range, FindOptions options, CharacterRange match)
 {
-    if (!matchLength)
-        return collapsedToBoundary(range, searchForward);
-    CharacterIterator rangeComputeIterator(createLiveRange(range), findIteratorOptions(options));
-    return characterSubrange(range.start.document(), rangeComputeIterator, matchStart, matchLength);
-}
-
-SimpleRange findClosestPlainText(const SimpleRange& range, const String& target, FindOptions options, unsigned targetOffset)
-{
-    size_t matchStart = 0;
-    size_t matchLength = 0;
-    size_t distance = std::numeric_limits<size_t>::max();
-    auto match = [targetOffset, &distance, &matchStart, &matchLength] (size_t start, size_t length) {
-        size_t newDistance = std::min(abs(static_cast<signed>(start - targetOffset)), abs(static_cast<signed>(start + length - targetOffset)));
-        if (newDistance < distance) {
-            matchStart = start;
-            matchLength = length;
-            distance = newDistance;
-        }
-        return false;
+    auto noMatchResult = [&] () {
+        auto& boundary = options.contains(Backwards) ? range.start : range.end;
+        return SimpleRange { boundary, boundary };
     };
 
-    findPlainTextMatches(range, target, options, WTFMove(match));
-    return rangeForMatch(range, options, matchStart, matchLength, !options.contains(Backwards));
+    if (!match.length)
+        return noMatchResult();
+
+    CharacterIterator it(range, findIteratorOptions(options));
+
+    it.advance(match.location);
+    if (it.atEnd())
+        return noMatchResult();
+    auto start = it.range().start;
+
+    it.advance(match.length - 1);
+    if (it.atEnd())
+        return noMatchResult();
+
+    return { WTFMove(start), it.range().end };
+}
+
+SimpleRange findClosestPlainText(const SimpleRange& range, const String& target, FindOptions options, uint64_t targetOffset)
+{
+    CharacterRange closestMatch;
+    uint64_t closestMatchDistance = std::numeric_limits<uint64_t>::max();
+    forEachMatch(range, target, options, [&] (CharacterRange match) {
+        auto distance = [] (uint64_t a, uint64_t b) -> uint64_t {
+            return std::abs(static_cast<int64_t>(a - b));
+        };
+        auto matchDistance = std::min(distance(match.location, targetOffset), distance(match.location + match.length, targetOffset));
+        if (matchDistance > closestMatchDistance)
+            return false;
+        if (matchDistance == closestMatchDistance && !options.contains(Backwards))
+            return false;
+        closestMatch = match;
+        if (!matchDistance && !options.contains(Backwards))
+            return true;
+        closestMatchDistance = matchDistance;
+        return false;
+    });
+    return rangeForMatch(range, options, closestMatch);
 }
 
 SimpleRange findPlainText(const SimpleRange& range, const String& target, FindOptions options)
 {
-    bool searchForward = !options.contains(Backwards);
-    size_t matchStart = 0;
-    size_t matchLength = 0;
-    auto match = [searchForward, &matchStart, &matchLength] (size_t start, size_t length) {
-        matchStart = start;
-        matchLength = length;
-        // Look for the last match when searching backwards instead.
-        return searchForward;
-    };
-
-    findPlainTextMatches(range, target, options, WTFMove(match));
-    return rangeForMatch(range, options, matchStart, matchLength, searchForward);
+    // When searching forward stop since we want the first match.
+    // When searching backward keep going since we want the last match.
+    bool stopAfterFindingMatch = !options.contains(Backwards);
+    CharacterRange lastMatchFound;
+    forEachMatch(range, target, options, [&] (CharacterRange match) {
+        lastMatchFound = match;
+        return stopAfterFindingMatch;
+    });
+    return rangeForMatch(range, options, lastMatchFound);
 }
 
 bool containsPlainText(const String& document, const String& target, FindOptions options)
diff --git a/Source/WebCore/editing/TextIterator.h b/Source/WebCore/editing/TextIterator.h
index e160287..9dea733 100644
--- a/Source/WebCore/editing/TextIterator.h
+++ b/Source/WebCore/editing/TextIterator.h
@@ -34,23 +34,27 @@
 
 namespace WebCore {
 
-class Range;
 class RenderTextFragment;
 
-// FIXME: Delete this overload after moving all the callers to the SimpleRange version.
-WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
+// Character ranges based on characters from the text iterator.
+WEBCORE_EXPORT uint64_t characterCount(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior);
+CharacterRange characterRange(const BoundaryPoint& start, const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior);
+CharacterRange characterRange(const SimpleRange& scope, const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior);
+BoundaryPoint resolveCharacterLocation(const SimpleRange& scope, uint64_t, TextIteratorBehavior = TextIteratorDefaultBehavior);
+WEBCORE_EXPORT SimpleRange resolveCharacterRange(const SimpleRange& scope, CharacterRange, TextIteratorBehavior = TextIteratorDefaultBehavior);
 
+// Text from the text iterator.
 WEBCORE_EXPORT String plainText(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
-WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
-WEBCORE_EXPORT String plainTextUsingBackwardsTextIteratorForTesting(const SimpleRange&);
-
-SimpleRange findPlainText(const SimpleRange&, const String&, FindOptions);
-WEBCORE_EXPORT SimpleRange findClosestPlainText(const SimpleRange&, const String&, FindOptions, unsigned);
 WEBCORE_EXPORT bool hasAnyPlainText(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior);
+WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
+
+// Find within the document, based on the text from the text iterator.
+SimpleRange findPlainText(const SimpleRange&, const String&, FindOptions);
+WEBCORE_EXPORT SimpleRange findClosestPlainText(const SimpleRange&, const String&, FindOptions, uint64_t targetCharacterOffset);
 bool containsPlainText(const String& document, const String&, FindOptions); // Lets us use the search algorithm on a string.
 WEBCORE_EXPORT String foldQuoteMarks(const String&);
 
-// FIXME: Move this somewhere else in the editing directory. It doesn't belong here.
+// FIXME: Move this somewhere else in the editing directory. It doesn't belong in the header with TextIterator.
 bool isRendererReplacedElement(RenderObject*);
 
 // FIXME: Move each iterator class into a separate header file.
@@ -103,9 +107,6 @@
     const TextIteratorCopyableText& copyableText() const { ASSERT(!atEnd()); return m_copyableText; }
     void appendTextToStringBuilder(StringBuilder& builder) const { copyableText().appendToStringBuilder(builder); }
 
-    // FIXME: Move this to SimpleRange and CharacterRange and move out of this class to the top level.
-    WEBCORE_EXPORT static bool getLocationAndLengthFromRange(Node* scope, const Range*, size_t& location, size_t& length);
-
 private:
     void init();
     void exitNode(Node*);
@@ -173,10 +174,10 @@
 // chunks so as to optimize for performance of the iteration.
 class SimplifiedBackwardsTextIterator {
 public:
-    explicit SimplifiedBackwardsTextIterator(const SimpleRange&);
+    WEBCORE_EXPORT explicit SimplifiedBackwardsTextIterator(const SimpleRange&);
 
     bool atEnd() const { return !m_positionNode; }
-    void advance();
+    WEBCORE_EXPORT void advance();
 
     StringView text() const { ASSERT(!atEnd()); return m_text; }
     WEBCORE_EXPORT SimpleRange range() const;
@@ -288,9 +289,19 @@
     bool m_didLookAhead { true };
 };
 
+inline CharacterRange characterRange(const BoundaryPoint& start, const SimpleRange& range, TextIteratorBehavior behavior)
+{
+    return { characterCount({ start, range.start }, behavior), characterCount(range, behavior) };
+}
 
-WEBCORE_EXPORT uint64_t characterCount(const SimpleRange&, TextIteratorBehavior = TextIteratorDefaultBehavior);
-WEBCORE_EXPORT BoundaryPoint resolveCharacterLocation(const SimpleRange&, uint64_t, TextIteratorBehavior = TextIteratorDefaultBehavior);
-WEBCORE_EXPORT SimpleRange resolveCharacterRange(const SimpleRange&, CharacterRange, TextIteratorBehavior = TextIteratorDefaultBehavior);
+inline CharacterRange characterRange(const SimpleRange& scope, const SimpleRange& range, TextIteratorBehavior behavior)
+{
+    return characterRange(scope.start, range, behavior);
+}
+
+inline BoundaryPoint resolveCharacterLocation(const SimpleRange& scope, uint64_t location, TextIteratorBehavior behavior)
+{
+    return resolveCharacterRange(scope, { location, 0 }, behavior).start;
+}
 
 } // namespace WebCore
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index c4d80fb..5a6c53c 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -465,7 +465,7 @@
             RefPtr<Range> range = makeRange(p1, p2);
             String strippedPreviousWord;
             if (range && (commandType == TypingCommand::InsertText || commandType == TypingCommand::InsertLineBreak || commandType == TypingCommand::InsertParagraphSeparator || commandType == TypingCommand::InsertParagraphSeparatorInQuotedContent))
-                strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
+                strippedPreviousWord = plainText(*range).stripWhiteSpace();
             frame.editor().markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
         } else if (commandType == TypingCommand::InsertText)
             frame.editor().startAlternativeTextUITimer();
diff --git a/Source/WebCore/editing/VisibleUnits.cpp b/Source/WebCore/editing/VisibleUnits.cpp
index d6f7178..3253b6f 100644
--- a/Source/WebCore/editing/VisibleUnits.cpp
+++ b/Source/WebCore/editing/VisibleUnits.cpp
@@ -1934,7 +1934,7 @@
     }
 
     if (startPosition != endPosition) {
-        String characterString = plainText(Range::create(position.deepEquivalent().anchorNode()->document(), startPosition, endPosition).ptr()).replace(noBreakSpace, ' ');
+        String characterString = plainText({ *makeBoundaryPoint(startPosition), *makeBoundaryPoint(endPosition) }).replace(noBreakSpace, ' ');
         for (int i = characterString.length() - 1, index = 0; i >= 0 && index < maxCharacters; --i) {
             if (!index && nextPosition.isNull())
                 index++;
diff --git a/Source/WebCore/editing/cocoa/DataDetection.mm b/Source/WebCore/editing/cocoa/DataDetection.mm
index 99e6159..557bfec 100644
--- a/Source/WebCore/editing/cocoa/DataDetection.mm
+++ b/Source/WebCore/editing/cocoa/DataDetection.mm
@@ -70,10 +70,10 @@
 
 static RetainPtr<DDActionContext> detectItemAtPositionWithRange(VisiblePosition position, RefPtr<Range> contextRange, FloatRect& detectedDataBoundingBox, RefPtr<Range>& detectedDataRange)
 {
-    String fullPlainTextString = plainText(contextRange.get());
     auto start = contextRange->startPosition();
     if (start.isNull() || position.isNull())
         return nil;
+    String fullPlainTextString = plainText(*contextRange);
     CFIndex hitLocation = characterCount({ *makeBoundaryPoint(start), *makeBoundaryPoint(position) });
 
     auto scanner = adoptCF(DDScannerCreate(DDScannerTypeStandard, 0, nullptr));
diff --git a/Source/WebCore/editing/cocoa/DictionaryLookup.mm b/Source/WebCore/editing/cocoa/DictionaryLookup.mm
index 47ad47a..a23dc40 100644
--- a/Source/WebCore/editing/cocoa/DictionaryLookup.mm
+++ b/Source/WebCore/editing/cocoa/DictionaryLookup.mm
@@ -286,7 +286,7 @@
     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, selectionCharacterCount);
 
     RefPtr<Range> fullCharacterRange = makeRange(paragraphStart, paragraphEnd);
-    String itemString = plainText(fullCharacterRange.get());
+    String itemString = plainText(*fullCharacterRange);
     NSRange highlightRange = adoptNS([allocRVItemInstance() initWithText:itemString selectedRange:rangeToPass]).get().highlightRange;
 
     return { createLiveRange(resolveCharacterRange(*fullCharacterRange, highlightRange)), nil };
diff --git a/Source/WebCore/editing/cocoa/HTMLConverter.h b/Source/WebCore/editing/cocoa/HTMLConverter.h
index aab49c4..097078d 100644
--- a/Source/WebCore/editing/cocoa/HTMLConverter.h
+++ b/Source/WebCore/editing/cocoa/HTMLConverter.h
@@ -36,12 +36,10 @@
 enum class IncludeImagesInAttributedString { Yes, No };
 
 NSAttributedString *attributedStringFromSelection(const VisibleSelection&, NSDictionary** documentAttributes = nullptr);
-
-// For testing purpose only
 WEBCORE_EXPORT NSAttributedString *attributedStringBetweenStartAndEnd(const Position&, const Position&, NSDictionary** documentAttributes = nullptr);
-
 WEBCORE_EXPORT NSAttributedString *attributedStringFromRange(Range&, NSDictionary** documentAttributes = nullptr);
-#if !PLATFORM(IOS_FAMILY)
+
+#if PLATFORM(MAC)
 WEBCORE_EXPORT NSAttributedString *editingAttributedStringFromRange(Range&, IncludeImagesInAttributedString = IncludeImagesInAttributedString::Yes);
 #endif
 
diff --git a/Source/WebCore/editing/mac/DictionaryLookupLegacy.mm b/Source/WebCore/editing/mac/DictionaryLookupLegacy.mm
index c0e3f55..aecbcd1 100644
--- a/Source/WebCore/editing/mac/DictionaryLookupLegacy.mm
+++ b/Source/WebCore/editing/mac/DictionaryLookupLegacy.mm
@@ -87,17 +87,16 @@
     auto selectionEnd = selection.visibleEnd();
 
     // As context, we are going to use the surrounding paragraphs of text.
-    auto paragraphStart = startOfParagraph(selectionStart);
-    auto paragraphEnd = endOfParagraph(selectionEnd);
-    if (paragraphStart.isNull() || paragraphEnd.isNull())
+    auto paragraphStart = makeBoundaryPoint(startOfParagraph(selectionStart));
+    auto paragraphEnd = makeBoundaryPoint(endOfParagraph(selectionEnd));
+    if (!paragraphStart || !paragraphEnd)
         return { nullptr, nil };
 
-    auto lengthToSelectionStart = characterCount({ *makeBoundaryPoint(paragraphStart), *makeBoundaryPoint(selectionStart) });
-    auto selectionCharacterCount = characterCount({ *makeBoundaryPoint(selectionStart), *makeBoundaryPoint(selectionEnd) });
-    NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, selectionCharacterCount);
+    auto selectionRange = SimpleRange { *makeBoundaryPoint(selectionStart), *makeBoundaryPoint(selectionEnd) };
+    auto paragraphRange = SimpleRange { *paragraphStart, *paragraphEnd };
 
     NSDictionary *options = nil;
-    tokenRange(plainText(makeRange(paragraphStart, paragraphEnd).get()), rangeToPass, &options);
+    tokenRange(plainText(paragraphRange), characterRange(paragraphRange, selectionRange), &options);
 
     return { selectedRange, options };
 }
@@ -142,7 +141,7 @@
 
     NSRange rangeToPass = NSMakeRange(characterCount({ *fullCharacterStart, *positionBoundary }), 0);
     NSDictionary *options = nil;
-    auto extractedRange = tokenRange(plainText(fullCharacterRange.get()), rangeToPass, &options);
+    auto extractedRange = tokenRange(plainText(*fullCharacterRange), rangeToPass, &options);
 
     // tokenRange sometimes returns {NSNotFound, 0} if it was unable to determine a good string.
     // FIXME (159063): We shouldn't need to check for zero length here.
diff --git a/Source/WebCore/editing/markup.cpp b/Source/WebCore/editing/markup.cpp
index 42f9bca..cbf6288 100644
--- a/Source/WebCore/editing/markup.cpp
+++ b/Source/WebCore/editing/markup.cpp
@@ -435,7 +435,12 @@
             behavior = TextIteratorBehavesAsIfNodesFollowing;
     }
 
-    return plainText(Range::create(text.document(), start, end).ptr(), behavior);
+    auto startBoundary = makeBoundaryPoint(start);
+    auto endBoundary = makeBoundaryPoint(end);
+    if (!startBoundary || !endBoundary)
+        return emptyString();
+
+    return plainText({ WTFMove(*startBoundary), WTFMove(*endBoundary) }, behavior);
 }
 
 String StyledMarkupAccumulator::textContentRespectingRange(const Text& text)
diff --git a/Source/WebCore/html/HTMLTextAreaElement.cpp b/Source/WebCore/html/HTMLTextAreaElement.cpp
index 681ca78..5c297ca 100644
--- a/Source/WebCore/html/HTMLTextAreaElement.cpp
+++ b/Source/WebCore/html/HTMLTextAreaElement.cpp
@@ -318,7 +318,8 @@
     // If the text field has no focus, we don't need to take account of the
     // selection length. The selection is the source of text drag-and-drop in
     // that case, and nothing in the text field will be removed.
-    unsigned selectionLength = focused() ? computeLengthForSubmission(plainText(document().frame()->selection().selection().toNormalizedRange().get())) : 0;
+    auto selectionRange = focused() ? document().frame()->selection().selection().toNormalizedRange() : nullptr;
+    unsigned selectionLength = selectionRange ? computeLengthForSubmission(plainText(*selectionRange)) : 0;
     ASSERT(currentLength >= selectionLength);
     unsigned baseLength = currentLength - selectionLength;
     unsigned appendableLength = unsignedMaxLength > baseLength ? unsignedMaxLength - baseLength : 0;
diff --git a/Source/WebCore/page/ContextMenuController.cpp b/Source/WebCore/page/ContextMenuController.cpp
index 72b1306..2c32b2f 100644
--- a/Source/WebCore/page/ContextMenuController.cpp
+++ b/Source/WebCore/page/ContextMenuController.cpp
@@ -431,7 +431,7 @@
             if (auto* element = document.documentElement())
                 selectedRange->selectNode(*element);
         }
-        m_client.speak(plainText(selectedRange.get()));
+        m_client.speak(plainText(*selectedRange));
         break;
     }
     case ContextMenuItemTagStopSpeaking:
diff --git a/Source/WebCore/page/DOMSelection.cpp b/Source/WebCore/page/DOMSelection.cpp
index 475e64b..dd7f8e8 100644
--- a/Source/WebCore/page/DOMSelection.cpp
+++ b/Source/WebCore/page/DOMSelection.cpp
@@ -448,7 +448,8 @@
     auto* frame = this->frame();
     if (!frame)
         return String();
-    return plainText(frame->selection().selection().toNormalizedRange().get());
+    auto range = frame->selection().selection().toNormalizedRange();
+    return range ? plainText(*range) : emptyString();
 }
 
 Node* DOMSelection::shadowAdjustedNode(const Position& position) const
diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp
index 90e75d5..b3712e4 100644
--- a/Source/WebCore/page/Page.cpp
+++ b/Source/WebCore/page/Page.cpp
@@ -884,22 +884,10 @@
 
     for (auto& range : rangesToReplace) {
         auto highestRoot = makeRefPtr(highestEditableRoot(range->startPosition()));
-        if (!highestRoot || highestRoot != highestEditableRoot(range->endPosition()))
+        if (!highestRoot || highestRoot != highestEditableRoot(range->endPosition()) || !highestRoot->document().frame())
             continue;
-
-        auto frame = makeRefPtr(highestRoot->document().frame());
-        if (!frame)
-            continue;
-
-        size_t replacementLocation = notFound;
-        size_t replacementLength = 0;
-        if (!TextIterator::getLocationAndLengthFromRange(highestRoot.get(), range.ptr(), replacementLocation, replacementLength))
-            continue;
-
-        if (replacementLocation == notFound || !replacementLength)
-            continue;
-
-        replacementRanges.append({ WTFMove(highestRoot), { replacementLocation, replacementLength } });
+        auto scope = makeRangeSelectingNodeContents(*highestRoot);
+        replacementRanges.append({ WTFMove(highestRoot), characterRange(scope, range) });
     }
 
     replaceRanges(*this, replacementRanges, replacementText);
diff --git a/Source/WebCore/page/ios/FrameIOS.mm b/Source/WebCore/page/ios/FrameIOS.mm
index 6ee4fb4..06285a5 100644
--- a/Source/WebCore/page/ios/FrameIOS.mm
+++ b/Source/WebCore/page/ios/FrameIOS.mm
@@ -730,7 +730,7 @@
 
     // There are no phrases with alternatives, so there is just one interpretation.
     if (markersInRoot.isEmpty())
-        return [NSArray arrayWithObject:plainText(rangeOfRootContents.ptr())];
+        return [NSArray arrayWithObject:plainText(rangeOfRootContents)];
 
     // The number of interpretations will be i1 * i2 * ... * iN, where iX is the number of interpretations for the Xth phrase with alternatives.
     size_t interpretationsCount = 1;
@@ -752,7 +752,7 @@
             // First, add text that precede the marker.
             if (precedingTextStartPosition != createLegacyEditingPosition(node, marker->startOffset())) {
                 auto precedingTextRange = Range::create(*document(), precedingTextStartPosition, createLegacyEditingPosition(node, marker->startOffset()));
-                String precedingText = plainText(precedingTextRange.ptr());
+                String precedingText = plainText(precedingTextRange);
                 if (!precedingText.isEmpty()) {
                     for (auto& interpretation : interpretations)
                         append(interpretation, precedingText);
diff --git a/Source/WebCore/testing/Internals.cpp b/Source/WebCore/testing/Internals.cpp
index b2af0bc..034bc32 100644
--- a/Source/WebCore/testing/Internals.cpp
+++ b/Source/WebCore/testing/Internals.cpp
@@ -2051,18 +2051,12 @@
 
 unsigned Internals::locationFromRange(Element& scope, const Range& range)
 {
-    size_t location = 0;
-    size_t unusedLength = 0;
-    TextIterator::getLocationAndLengthFromRange(&scope, &range, location, unusedLength);
-    return location;
+    return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), range).location);
 }
 
 unsigned Internals::lengthFromRange(Element& scope, const Range& range)
 {
-    size_t unusedLocation = 0;
-    size_t length = 0;
-    TextIterator::getLocationAndLengthFromRange(&scope, &range, unusedLocation, length);
-    return length;
+    return clampTo<unsigned>(characterRange(makeBoundaryPointBeforeNodeContents(scope), range).length);
 }
 
 String Internals::rangeAsText(const Range& range)
@@ -2072,7 +2066,10 @@
 
 String Internals::rangeAsTextUsingBackwardsTextIterator(const Range& range)
 {
-    return plainTextUsingBackwardsTextIteratorForTesting(range);
+    String result;
+    for (SimplifiedBackwardsTextIterator backwardsIterator(range); !backwardsIterator.atEnd(); backwardsIterator.advance())
+        result.insert(backwardsIterator.text().toString(), 0);
+    return result;
 }
 
 Ref<Range> Internals::subrange(Range& range, unsigned rangeLocation, unsigned rangeLength)
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 550725b..46f1b96 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,57 @@
+2020-04-01  Darin Adler  <darin@apple.com>
+
+        Remove all uses of live ranges from TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=209723
+
+        Reviewed by Antti Koivisto.
+
+        * Shared/EditingRange.cpp:
+        (WebKit::EditingRange::fromRange): Use characterRange.
+
+        * Shared/mac/AttributedString.h: Added a constructor that takes rvalue
+        references so we can initialize this slightly more efficiently.
+
+        * Shared/mac/AttributedString.mm:
+        (IPC::ArgumentCoder<WebKit::AttributedString>::decode): Pass rvalue
+        references when creating an AttributedString.
+
+        * UIProcess/mac/TextCheckerMac.mm:
+        (WebKit::TextChecker::updateSpellingUIWithGrammarString): Simplify the
+        code to remove some local variables that weren't helpful.
+
+        * WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h: Made the
+        annotatedSubstringBetweenPositions a static member function. Also used
+        const& argument types to cut down on reference count churn a bit.
+
+        * WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm:
+        (WebKit::TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection):
+        Streamlined and made this use characterCount instead of
+        TextIterator::getLocationAndLengthFromRange.
+        (WebKit::TextCheckingControllerProxy::replaceRelativeToSelection): Tweaked
+        the argument type.
+        (WebKit::TextCheckingControllerProxy::removeAnnotationRelativeToSelection):
+        Ditto. Also removed some unnecessary use of NSString.
+        (WebKit::TextCheckingControllerProxy::annotatedSubstringBetweenPositions):
+        Rewrote to no longer use live ranges.
+
+        * WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
+        (WebKit::WebPage::getContentsAsAttributedString): Use construction and
+        rvalue references to tigten things up a bit.
+
+        * WebProcess/WebPage/WebFrame.cpp:
+        (WebKit::WebFrame::contentsAsString const): Use a SimpleRange instead of
+        a live range to pass to plainText.
+        * WebProcess/WebPage/glib/WebPageGLib.cpp:
+        (WebKit::WebPage::getPlatformEditorState const): Pass references to
+        live ranges to plainText.
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::updateSelectionWithDelta): Rewrote to minimize use of
+        live ranges.
+        (WebKit::WebPage::requestDocumentEditingContext): Ditto.
+        * WebProcess/WebPage/mac/WebPageMac.mm:
+        (WebKit::WebPage::getPlatformEditorState const): Pass reference to
+        a live range to plainText.
+
 2020-04-02  David Kilzer  <ddkilzer@apple.com>
 
         API::PageConfiguration may have conflicting preference values between WebPreferences and WebPreferencesStore::ValueMap instance variables
diff --git a/Source/WebKit/Shared/EditingRange.cpp b/Source/WebKit/Shared/EditingRange.cpp
index 76ef472..86ac553 100644
--- a/Source/WebKit/Shared/EditingRange.cpp
+++ b/Source/WebKit/Shared/EditingRange.cpp
@@ -65,16 +65,15 @@
 {
     ASSERT(editingRangeIsRelativeTo == EditingRangeIsRelativeTo::EditableRoot);
 
-    size_t location = 0;
-    size_t length = 0;
-    if (!range || !WebCore::TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range, location, length))
+    if (!range)
         return { };
 
-    EditingRange editingRange(location, length);
-    if (!editingRange.isValid())
+    auto* element = frame.selection().rootEditableElementOrDocumentElement();
+    if (!element)
         return { };
 
-    return editingRange;
+    auto relativeRange = characterRange(makeBoundaryPointBeforeNodeContents(*element), *range);
+    return EditingRange(relativeRange.location, relativeRange.length);
 }
 
 } // namespace WebKit
diff --git a/Source/WebKit/Shared/mac/AttributedString.h b/Source/WebKit/Shared/mac/AttributedString.h
index 8530f81..02d6b01 100644
--- a/Source/WebKit/Shared/mac/AttributedString.h
+++ b/Source/WebKit/Shared/mac/AttributedString.h
@@ -33,9 +33,10 @@
 namespace WebKit {
 
 struct AttributedString {
-    AttributedString()
-    {
-    }
+    RetainPtr<NSAttributedString> string;
+    RetainPtr<NSDictionary> documentAttributes;
+
+    AttributedString() = default;
 
 #if defined(__OBJC__)
     AttributedString(NSAttributedString *attributedString, NSDictionary *documentAttributes = nil)
@@ -44,22 +45,26 @@
     {
     }
 
+    AttributedString(RetainPtr<NSAttributedString>&& attributedString, RetainPtr<NSDictionary>&& documentAttributes = { })
+        : string(WTFMove(attributedString))
+        , documentAttributes(WTFMove(documentAttributes))
+    {
+    }
+
     operator NSAttributedString *() const
     {
         return string.get();
     }
 #endif
-
-    RetainPtr<NSAttributedString> string;
-    RetainPtr<NSDictionary> documentAttributes;
 };
 
 }
 
 namespace IPC {
+
 template<> struct ArgumentCoder<WebKit::AttributedString> {
     static void encode(Encoder&, const WebKit::AttributedString&);
     static Optional<WebKit::AttributedString> decode(Decoder&);
 };
-}
 
+}
diff --git a/Source/WebKit/Shared/mac/AttributedString.mm b/Source/WebKit/Shared/mac/AttributedString.mm
index 3c47005..0f2e92d 100644
--- a/Source/WebKit/Shared/mac/AttributedString.mm
+++ b/Source/WebKit/Shared/mac/AttributedString.mm
@@ -45,7 +45,7 @@
     RetainPtr<NSDictionary> documentAttributes;
     if (!IPC::decode(decoder, documentAttributes))
         return WTF::nullopt;
-    return WebKit::AttributedString { attributedString.get(), documentAttributes.get() };
+    return { { WTFMove(attributedString), WTFMove(documentAttributes) } };
 }
 
 }
diff --git a/Source/WebKit/UIProcess/mac/TextCheckerMac.mm b/Source/WebKit/UIProcess/mac/TextCheckerMac.mm
index 0d19e4b..d786f11 100644
--- a/Source/WebKit/UIProcess/mac/TextCheckerMac.mm
+++ b/Source/WebKit/UIProcess/mac/TextCheckerMac.mm
@@ -436,18 +436,13 @@
 void TextChecker::updateSpellingUIWithGrammarString(SpellDocumentTag, const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
 {
     auto corrections = adoptNS([[NSMutableArray alloc] init]);
-    for (auto& guess : grammarDetail.guesses) {
-        NSString *guessNSString = guess;
-        [corrections addObject:guessNSString];
-    }
-
-    NSString *descriptionNSString = grammarDetail.userDescription;
+    for (auto& guess : grammarDetail.guesses)
+        [corrections addObject:guess];
     NSDictionary *detail = @{
         NSGrammarRange : [NSValue valueWithRange:grammarDetail.range],
-        NSGrammarUserDescription : descriptionNSString,
+        NSGrammarUserDescription : grammarDetail.userDescription,
         NSGrammarCorrections : corrections.get(),
     };
-
     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:detail];
 }
 
diff --git a/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h b/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h
index 07413cf..f48f0b6 100644
--- a/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h
+++ b/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.h
@@ -52,7 +52,7 @@
     TextCheckingControllerProxy(WebPage&);
     ~TextCheckingControllerProxy();
 
-    AttributedString annotatedSubstringBetweenPositions(const WebCore::VisiblePosition&, const WebCore::VisiblePosition&);
+    static AttributedString annotatedSubstringBetweenPositions(const WebCore::VisiblePosition&, const WebCore::VisiblePosition&);
 
 private:
     // IPC::MessageReceiver
@@ -65,8 +65,8 @@
     Optional<RangeAndOffset> rangeAndOffsetRelativeToSelection(int64_t offset, uint64_t length);
 
     // Message handlers.
-    void replaceRelativeToSelection(AttributedString, int64_t selectionOffset, uint64_t length, uint64_t relativeReplacementLocation, uint64_t relativeReplacementLength);
-    void removeAnnotationRelativeToSelection(String annotationName, int64_t selectionOffset, uint64_t length);
+    void replaceRelativeToSelection(const AttributedString&, int64_t selectionOffset, uint64_t length, uint64_t relativeReplacementLocation, uint64_t relativeReplacementLength);
+    void removeAnnotationRelativeToSelection(const String& annotationName, int64_t selectionOffset, uint64_t length);
 
     WebPage& m_page;
 };
diff --git a/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm b/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm
index 659d5af..e449bfd 100644
--- a/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm
+++ b/Source/WebKit/WebProcess/WebPage/Cocoa/TextCheckingControllerProxy.mm
@@ -72,28 +72,28 @@
 
 Optional<TextCheckingControllerProxy::RangeAndOffset> TextCheckingControllerProxy::rangeAndOffsetRelativeToSelection(int64_t offset, uint64_t length)
 {
-    Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
-    auto& frameSelection = frame.selection();
+    auto& frameSelection = m_page.corePage()->focusController().focusedOrMainFrame().selection();
     auto& selection = frameSelection.selection();
-    if (selection.isNone())
-        return WTF::nullopt;
 
     auto root = frameSelection.rootEditableElementOrDocumentElement();
     if (!root)
         return WTF::nullopt;
 
-    auto range = selection.toNormalizedRange();
-    range->collapse(true);
+    auto selectionLiveRange = selection.toNormalizedRange();
+    if (!selectionLiveRange)
+        return WTF::nullopt;
+    auto selectionRange = SimpleRange { *selectionLiveRange };
 
-    size_t selectionLocation;
-    size_t selectionLength;
-    TextIterator::getLocationAndLengthFromRange(root, range.get(), selectionLocation, selectionLength);
-    selectionLocation += offset;
+    auto scope = makeRangeSelectingNodeContents(*root);
+    int64_t adjustedStartLocation = characterCount({ scope.start, selectionRange.start }) + offset;
+    if (adjustedStartLocation < 0)
+        return WTF::nullopt;
+    auto adjustedSelectionCharacterRange = CharacterRange { static_cast<uint64_t>(adjustedStartLocation), length };
 
-    return { { createLiveRange(resolveCharacterRange(makeRangeSelectingNodeContents(*root), { selectionLocation, length })), selectionLocation } };
+    return { { createLiveRange(resolveCharacterRange(scope, adjustedSelectionCharacterRange)), adjustedSelectionCharacterRange.location } };
 }
 
-void TextCheckingControllerProxy::replaceRelativeToSelection(AttributedString annotatedString, int64_t selectionOffset, uint64_t length, uint64_t relativeReplacementLocation, uint64_t relativeReplacementLength)
+void TextCheckingControllerProxy::replaceRelativeToSelection(const AttributedString& annotatedString, int64_t selectionOffset, uint64_t length, uint64_t relativeReplacementLocation, uint64_t relativeReplacementLength)
 {
     Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
     FrameSelection& frameSelection = frame.selection();
@@ -154,7 +154,7 @@
     }];
 }
 
-void TextCheckingControllerProxy::removeAnnotationRelativeToSelection(String annotation, int64_t selectionOffset, uint64_t length)
+void TextCheckingControllerProxy::removeAnnotationRelativeToSelection(const String& annotation, int64_t selectionOffset, uint64_t length)
 {
     Frame& frame = m_page.corePage()->focusController().focusedOrMainFrame();
     auto rangeAndOffset = rangeAndOffsetRelativeToSelection(selectionOffset, length);
@@ -164,7 +164,7 @@
     if (!range)
         return;
 
-    bool removeCoreSpellingMarkers = (annotation == String(@"NSSpellingState"));
+    bool removeCoreSpellingMarkers = annotation == "NSSpellingState";
     frame.document()->markers().filterMarkers(*range, [&] (DocumentMarker* marker) {
         if (!WTF::holds_alternative<DocumentMarker::PlatformTextCheckingData>(marker->data()))
             return false;
@@ -175,51 +175,30 @@
 
 AttributedString TextCheckingControllerProxy::annotatedSubstringBetweenPositions(const WebCore::VisiblePosition& start, const WebCore::VisiblePosition& end)
 {
-    RetainPtr<NSMutableAttributedString> string = adoptNS([[NSMutableAttributedString alloc] init]);
-    NSUInteger stringLength = 0;
-
-    RefPtr<Document> document = start.deepEquivalent().document();
-    if (!document)
+    auto startBoundary = makeBoundaryPoint(start);
+    auto endBoundary = makeBoundaryPoint(end);
+    if (!startBoundary || !endBoundary)
         return { };
+    auto entireRange = SimpleRange { *startBoundary, *endBoundary };
 
-    auto entireRange = makeRange(start, end);
-    if (!entireRange)
-        return { };
+    auto string = adoptNS([[NSMutableAttributedString alloc] init]);
 
-    RefPtr<Node> commonAncestor = entireRange->commonAncestorContainer();
-    size_t entireRangeLocation;
-    size_t entireRangeLength;
-    TextIterator::getLocationAndLengthFromRange(commonAncestor.get(), entireRange.get(), entireRangeLocation, entireRangeLength);
-
-    for (TextIterator it({ *makeBoundaryPoint(start.deepEquivalent()), *makeBoundaryPoint(end.deepEquivalent()) }); !it.atEnd(); it.advance()) {
-        int currentTextLength = it.text().length();
-        if (!currentTextLength)
+    for (TextIterator it(entireRange); !it.atEnd(); it.advance()) {
+        if (!it.text().length())
             continue;
-
-        [string appendAttributedString:[[[NSAttributedString alloc] initWithString:it.text().createNSStringWithoutCopying().get()] autorelease]];
-
-        SimpleRange currentTextRange = it.range();
-        auto markers = document->markers().markersInRange(createLiveRange(currentTextRange), DocumentMarker::PlatformTextChecking);
+        [string appendAttributedString:adoptNS([[NSAttributedString alloc] initWithString:it.text().createNSStringWithoutCopying().get()]).get()];
+        auto range = it.range();
+        auto markers = range.start.document().markers().markersInRange(createLiveRange(range), DocumentMarker::PlatformTextChecking);
         for (const auto* marker : markers) {
             if (!WTF::holds_alternative<DocumentMarker::PlatformTextCheckingData>(marker->data()))
                 continue;
-
-            auto& textCheckingData = WTF::get<DocumentMarker::PlatformTextCheckingData>(marker->data());
-            auto subrange = resolveCharacterRange(currentTextRange, { marker->startOffset(), marker->endOffset() - marker->startOffset() });
-
-            size_t subrangeLocation;
-            size_t subrangeLength;
-            TextIterator::getLocationAndLengthFromRange(commonAncestor.get(), createLiveRange(subrange).ptr(), subrangeLocation, subrangeLength);
-
-            ASSERT(subrangeLocation > entireRangeLocation);
-            ASSERT(subrangeLocation + subrangeLength < entireRangeLength);
-            [string addAttribute:textCheckingData.key value:textCheckingData.value range:NSMakeRange(subrangeLocation - entireRangeLocation, subrangeLength)];
+            auto& data = WTF::get<DocumentMarker::PlatformTextCheckingData>(marker->data());
+            auto subrange = resolveCharacterRange(range, { marker->startOffset(), marker->endOffset() - marker->startOffset() });
+            [string addAttribute:data.key value:data.value range:characterRange(entireRange, subrange)];
         }
-
-        stringLength += currentTextLength;
     }
 
-    return string.autorelease();
+    return { { WTFMove(string) } };
 }
 
 } // namespace WebKit
diff --git a/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm b/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
index 0c1720f..33ecd7b 100644
--- a/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
+++ b/Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
@@ -239,12 +239,9 @@
     }
 
     NSDictionary* documentAttributes = nil;
+    auto attributedString = attributedStringFromRange(rangeOfContents(*documentElement), &documentAttributes);
 
-    AttributedString result;
-    result.string = attributedStringFromRange(rangeOfContents(*documentElement), &documentAttributes);
-    result.documentAttributes = documentAttributes;
-
-    completionHandler({ result });
+    completionHandler({ WTFMove(attributedString), WTFMove(documentAttributes) });
 }
 
 void WebPage::setRemoteObjectRegistry(WebRemoteObjectRegistry* registry)
diff --git a/Source/WebKit/WebProcess/WebPage/WebFrame.cpp b/Source/WebKit/WebProcess/WebPage/WebFrame.cpp
index 4a513b6..468c359 100644
--- a/Source/WebKit/WebProcess/WebPage/WebFrame.cpp
+++ b/Source/WebKit/WebProcess/WebPage/WebFrame.cpp
@@ -362,20 +362,15 @@
         return builder.toString();
     }
 
-    Document* document = m_coreFrame->document();
+    auto document = m_coreFrame->document();
     if (!document)
         return String();
 
-    RefPtr<Element> documentElement = document->documentElement();
+    auto documentElement = document->documentElement();
     if (!documentElement)
         return String();
 
-    RefPtr<Range> range = document->createRange();
-
-    if (range->selectNode(*documentElement).hasException())
-        return String();
-
-    return plainText(range.get());
+    return plainText(makeRangeSelectingNodeContents(*documentElement));
 }
 
 String WebFrame::selectionAsString() const 
diff --git a/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp b/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
index 4e86f05..16b538c 100644
--- a/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
+++ b/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
@@ -113,11 +113,11 @@
             auto clonedRange = surroundingRange->cloneRange();
             surroundingRange->setEnd(compositionRange->startPosition());
             clonedRange->setStart(compositionRange->endPosition());
-            postLayoutData.surroundingContext = plainText(surroundingRange.get()) + plainText(clonedRange.ptr());
+            postLayoutData.surroundingContext = plainText(*surroundingRange) + plainText(clonedRange);
             postLayoutData.surroundingContextCursorPosition = characterCount(*surroundingRange);
             postLayoutData.surroundingContextSelectionPosition = postLayoutData.surroundingContextCursorPosition;
         } else {
-            postLayoutData.surroundingContext = plainText(surroundingRange.get());
+            postLayoutData.surroundingContext = plainText(*surroundingRange);
             if (surroundingStart.isNull() || selectionStart.isNull())
                 postLayoutData.surroundingContextCursorPosition = 0;
             else
diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
index 961e2c1..70e5285 100644
--- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
+++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -4041,38 +4041,28 @@
 
 void WebPage::updateSelectionWithDelta(int64_t locationDelta, int64_t lengthDelta, CompletionHandler<void()>&& completionHandler)
 {
-    Ref<Frame> frame = corePage()->focusController().focusedOrMainFrame();
-    VisibleSelection selection = frame->selection().selection();
-    if (selection.isNone()) {
+    auto frame = makeRef(corePage()->focusController().focusedOrMainFrame());
+    auto root = makeRefPtr(frame->selection().rootEditableElementOrDocumentElement());
+    auto selectionRange = frame->selection().selection().toNormalizedRange();
+    if (!root || !selectionRange) {
         completionHandler();
         return;
     }
 
-    auto root = frame->selection().rootEditableElementOrDocumentElement();
-    auto range = selection.toNormalizedRange();
-    if (!root || !range) {
-        completionHandler();
-        return;
-    }
-
-    size_t selectionLocation;
-    size_t selectionLength;
-    TextIterator::getLocationAndLengthFromRange(root, range.get(), selectionLocation, selectionLength);
-
-    CheckedInt64 newSelectionLocation { selectionLocation };
-    CheckedInt64 newSelectionLength { selectionLength };
+    auto scope = makeRangeSelectingNodeContents(*root);
+    auto selectionCharacterRange = characterRange(scope, *selectionRange);
+    CheckedInt64 newSelectionLocation { selectionCharacterRange.location };
+    CheckedInt64 newSelectionLength { selectionCharacterRange.length };
     newSelectionLocation += locationDelta;
     newSelectionLength += lengthDelta;
-
     if (newSelectionLocation.hasOverflowed() || newSelectionLength.hasOverflowed()) {
         completionHandler();
         return;
     }
 
     auto newSelectionRange = CharacterRange(newSelectionLocation.unsafeGet(), newSelectionLength.unsafeGet());
-    auto resolvedRange = resolveCharacterRange(makeRangeSelectingNodeContents(*root), newSelectionRange);
-    frame->selection().setSelectedRange(createLiveRange(resolvedRange).ptr(), DOWNSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
-
+    auto updatedSelectionRange = resolveCharacterRange(makeRangeSelectingNodeContents(*root), newSelectionRange);
+    frame->selection().setSelectedRange(createLiveRange(updatedSelectionRange).ptr(), DOWNSTREAM, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
     completionHandler();
 }
 
@@ -4175,25 +4165,25 @@
         startOfRangeOfInterestInSelection = rangeOfInterestStart > selectionStart ? rangeOfInterestStart : selectionStart;
         endOfRangeOfInterestInSelection = rangeOfInterestEnd < selectionEnd ? rangeOfInterestEnd : selectionEnd;
     } else {
-        size_t rangeOfInterestLocation;
-        size_t rangeOfInterestLength;
-        RefPtr<Node> rootNode = rangeOfInterest->commonAncestorContainer();
+        auto rootNode = makeRefPtr(rangeOfInterest->commonAncestorContainer());
         if (!rootNode) {
             completionHandler({ });
             return;
         }
+        auto rootContainerNode = rootNode->isContainerNode() ? downcast<ContainerNode>(rootNode.get()) : rootNode->parentNode();
+        if (!rootContainerNode) {
+            completionHandler({ });
+            return;
+        }
+        auto scope = makeRangeSelectingNodeContents(*rootContainerNode);
 
-        RefPtr<ContainerNode> rootContainerNode = rootNode->isContainerNode() ? downcast<ContainerNode>(rootNode.get()) : rootNode->parentNode();
-        TextIterator::getLocationAndLengthFromRange(rootContainerNode.get(), rangeOfInterest.get(), rangeOfInterestLocation, rangeOfInterestLength);
-
-        Checked<uint64_t, RecordOverflow> midpointLocation { rangeOfInterestLocation };
-        midpointLocation += rangeOfInterestLength / 2;
+        auto characterRangeOfInterest = characterRange(scope, *rangeOfInterest);
+        auto midpointLocation = checkedSum<uint64_t>(characterRangeOfInterest.location, characterRangeOfInterest.length / 2);
         if (midpointLocation.hasOverflowed()) {
             completionHandler({ });
             return;
         }
-
-        auto midpoint = createLegacyEditingPosition(resolveCharacterLocation(makeRangeSelectingNodeContents(*rootContainerNode), midpointLocation.unsafeGet()));
+        auto midpoint = createLegacyEditingPosition(resolveCharacterLocation(scope, midpointLocation.unsafeGet()));
 
         startOfRangeOfInterestInSelection = startOfWord(midpoint);
         if (startOfRangeOfInterestInSelection < rangeOfInterestStart) {
@@ -4201,7 +4191,6 @@
             if (startOfRangeOfInterestInSelection > rangeOfInterestEnd)
                 startOfRangeOfInterestInSelection = midpoint;
         }
-
         endOfRangeOfInterestInSelection = startOfRangeOfInterestInSelection;
     }
 
diff --git a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
index 96281ab..da1132c 100644
--- a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
+++ b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
@@ -154,9 +154,11 @@
     if (!selectionStartBoundary || !selectionEnd || !paragraphStart)
         return;
 
+    auto contextRangeForCandidateRequest = frame.editor().contextRangeForCandidateRequest();
+
     postLayoutData.candidateRequestStartPosition = characterCount({ *paragraphStart, *selectionStartBoundary });
     postLayoutData.selectedTextLength = characterCount({ *selectionStartBoundary, *selectionEnd });
-    postLayoutData.paragraphContextForCandidateRequest = plainText(frame.editor().contextRangeForCandidateRequest().get());
+    postLayoutData.paragraphContextForCandidateRequest = contextRangeForCandidateRequest ? plainText(*contextRangeForCandidateRequest) : String();
     postLayoutData.stringForCandidateRequest = frame.editor().stringForCandidateRequest();
 
     IntRect rectForSelectionCandidates;
diff --git a/Source/WebKitLegacy/mac/ChangeLog b/Source/WebKitLegacy/mac/ChangeLog
index 800ca13..a140b96 100644
--- a/Source/WebKitLegacy/mac/ChangeLog
+++ b/Source/WebKitLegacy/mac/ChangeLog
@@ -1,3 +1,20 @@
+2020-04-01  Darin Adler  <darin@apple.com>
+
+        Remove all uses of live ranges from TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=209723
+
+        Reviewed by Antti Koivisto.
+
+        * WebCoreSupport/WebEditorClient.mm:
+        (WebEditorClient::updateSpellingUIWithGrammarString): Remove some unnecessary
+        use of local variabels for NSString.
+        (WebEditorClient::requestCandidatesForSelection): Pass a reference to a live
+        range to plainText.
+        * WebView/WebFrame.mm:
+        (-[WebFrame _stringForRange:]): Ditto.
+        (-[WebFrame _convertToNSRange:]): Use characterRange instead of
+        TextIterator::getLocationAndLengthFromRange.
+
 2020-04-01  Ryosuke Niwa  <rniwa@webkit.org>
 
         HTMLFormElement should use WeakPtr to keep track of its associated elements
diff --git a/Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.mm b/Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.mm
index 692ee07..3c30d57 100644
--- a/Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.mm
+++ b/Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.mm
@@ -1039,14 +1039,11 @@
 void WebEditorClient::updateSpellingUIWithGrammarString(const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
 {
     NSMutableArray *corrections = [NSMutableArray array];
-    for (auto& guess : grammarDetail.guesses) {
-        NSString *guessNSString = guess;
-        [corrections addObject:guessNSString];
-    }
-    NSString *descriptionNSString = grammarDetail.userDescription;
+    for (auto& guess : grammarDetail.guesses)
+        [corrections addObject:guess];
     NSDictionary *dictionary = @{
         NSGrammarRange : [NSValue valueWithRange:grammarDetail.range],
-        NSGrammarUserDescription : descriptionNSString,
+        NSGrammarUserDescription : grammarDetail.userDescription,
         NSGrammarCorrections : corrections,
     };
     [[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:dictionary];
@@ -1122,9 +1119,11 @@
     auto selectionStart = selection.visibleStart();
     auto selectionStartOffsetInParagraph = characterCount({ *makeBoundaryPoint(startOfParagraph(selectionStart)), *makeBoundaryPoint(selectionStart) });
     auto selectionLength = characterCount({ *makeBoundaryPoint(selectionStart), *makeBoundaryPoint(selection.visibleEnd()) });
+    auto contextRangeForCandidateRequest = frame->editor().contextRangeForCandidateRequest();
+    String contextForCandidateReqeuest = contextRangeForCandidateRequest ? plainText(*contextRangeForCandidateRequest) : String();
 
     m_rangeForCandidates = NSMakeRange(selectionStartOffsetInParagraph, selectionLength);
-    m_paragraphContextForCandidateRequest = plainText(frame->editor().contextRangeForCandidateRequest().get());
+    m_paragraphContextForCandidateRequest = contextForCandidateReqeuest;
 
     NSTextCheckingTypes checkingTypes = NSTextCheckingTypeSpelling | NSTextCheckingTypeReplacement | NSTextCheckingTypeCorrection;
     auto weakEditor = makeWeakPtr(*this);
diff --git a/Source/WebKitLegacy/mac/WebView/WebFrame.mm b/Source/WebKitLegacy/mac/WebView/WebFrame.mm
index fe66d1b..e55c309 100644
--- a/Source/WebKitLegacy/mac/WebView/WebFrame.mm
+++ b/Source/WebKitLegacy/mac/WebView/WebFrame.mm
@@ -587,7 +587,9 @@
 
 - (NSString *)_stringForRange:(DOMRange *)range
 {
-    return plainText(core(range), WebCore::TextIteratorDefaultBehavior, true);
+    if (!range)
+        return @"";
+    return plainText(*core(range), WebCore::TextIteratorDefaultBehavior, true);
 }
 
 - (OptionSet<WebCore::PaintBehavior>)_paintBehaviorForDestinationContext:(CGContextRef)context
@@ -806,12 +808,15 @@
     if (!range)
         return NSMakeRange(NSNotFound, 0);
 
-    size_t location = 0;
-    size_t length = 0;
-    if (!WebCore::TextIterator::getLocationAndLengthFromRange(_private->coreFrame->selection().rootEditableElementOrDocumentElement(), range, location, length))
+    auto frame = _private->coreFrame;
+    if (!frame)
         return NSMakeRange(NSNotFound, 0);
 
-    return NSMakeRange(location, length);
+    auto* element = frame->selection().rootEditableElementOrDocumentElement();
+    if (!element)
+        return NSMakeRange(NSNotFound, 0);
+
+    return characterRange(makeBoundaryPointBeforeNodeContents(*element), *range);
 }
 
 - (RefPtr<WebCore::Range>)_convertToDOMRange:(NSRange)nsrange
diff --git a/Source/WebKitLegacy/win/ChangeLog b/Source/WebKitLegacy/win/ChangeLog
index aa2e30b..13d4f42 100644
--- a/Source/WebKitLegacy/win/ChangeLog
+++ b/Source/WebKitLegacy/win/ChangeLog
@@ -1,3 +1,18 @@
+2020-04-01  Darin Adler  <darin@apple.com>
+
+        Remove all uses of live ranges from TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=209723
+
+        Reviewed by Antti Koivisto.
+
+        * WebFrame.cpp:
+        (WebFrame::string): Use a SimpleRange instead of a live range
+        to call plainText.
+        * WebView.cpp:
+        (WebView::selectedRangeForTesting): Use a SimpleRange instead
+        of a live range to call characterRange instead of
+        TextIterator::getLocationAndLengthFromRange.
+
 2020-03-30  Simon Fraser  <simon.fraser@apple.com>
 
         scrollIntoView() erroneously scrolls non-containing block scrollers
diff --git a/Source/WebKitLegacy/win/WebFrame.cpp b/Source/WebKitLegacy/win/WebFrame.cpp
index 368d7ce..d42e451 100644
--- a/Source/WebKitLegacy/win/WebFrame.cpp
+++ b/Source/WebKitLegacy/win/WebFrame.cpp
@@ -1923,13 +1923,15 @@
 
     *result = nullptr;
 
-    Frame* coreFrame = core(this);
-    if (!coreFrame)
+    auto* frame = core(this);
+    if (!frame)
         return E_UNEXPECTED;
 
-    RefPtr<Range> allRange(rangeOfContents(*coreFrame->document()));
-    String allString = plainText(allRange.get());
-    *result = BString(allString).release();
+    auto* document = frame->document();
+    if (!document)
+        return E_FAIL;
+
+    *result = BString(plainText(makeRangeSelectingNodeContents(*document))).release();
     return S_OK;
 }
 
diff --git a/Source/WebKitLegacy/win/WebView.cpp b/Source/WebKitLegacy/win/WebView.cpp
index 4da0aff..ad138e1 100644
--- a/Source/WebKitLegacy/win/WebView.cpp
+++ b/Source/WebKitLegacy/win/WebView.cpp
@@ -7801,14 +7801,16 @@
     Frame& frame = m_page->focusController().focusedOrMainFrame();
 
     RefPtr<Range> range = frame.editor().selectedRange();
+    if (!range)
+        return E_FAIL;
 
-    size_t locationSize;
-    size_t lengthSize;
-    if (range && TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize)) {
-        *location = static_cast<UINT>(locationSize);
-        *length = static_cast<UINT>(lengthSize);
-    }
+    auto* element = frame.selection().rootEditableElementOrDocumentElement();
+    if (!element)
+        return E_FAIL;
 
+    auto relativeRange = characterRange(makeBoundaryPointBeforeNodeContents(*element), *range);
+    *location = static_cast<UINT>(relativeRange.location);
+    *length = static_cast<UINT>(relativeRange.length);
     return S_OK;
 }