[LFC][IFC] Rename Display::Run::TextContext to TextContent
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Feb 2020 15:53:36 +0000 (15:53 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Feb 2020 15:53:36 +0000 (15:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=207611
<rdar://problem/59374523>

Reviewed by Antti Koivisto.

* layout/Verification.cpp:
(WebCore::Layout::outputMismatchingSimpleLineInformationIfNeeded):
(WebCore::Layout::checkForMatchingTextRuns):
(WebCore::Layout::outputMismatchingComplexLineInformationIfNeeded):
* layout/displaytree/DisplayPainter.cpp:
(WebCore::Display::paintInlineContent):
* layout/displaytree/DisplayRun.h:
(WebCore::Display::Run::setTextContent):
(WebCore::Display::Run::textContent const):
(WebCore::Display::Run::textContent):
(WebCore::Display::Run::isLineBreak const):
(WebCore::Display::Run::Run):
(WebCore::Display::Run::TextContent::TextContent):
(WebCore::Display::Run::TextContext::start const): Deleted.
(WebCore::Display::Run::TextContext::end const): Deleted.
(WebCore::Display::Run::TextContext::length const): Deleted.
(WebCore::Display::Run::TextContext::content const): Deleted.
(WebCore::Display::Run::TextContext::needsHyphen const): Deleted.
(WebCore::Display::Run::TextContext::setNeedsHyphen): Deleted.
(WebCore::Display::Run::TextContext::expand): Deleted.
(WebCore::Display::Run::setTextContext): Deleted.
(WebCore::Display::Run::textContext const): Deleted.
(WebCore::Display::Run::textContext): Deleted.
(WebCore::Display::Run::TextContext::TextContext): Deleted.
* layout/inlineformatting/InlineFormattingContext.cpp:
(WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::m_textContent):
(WebCore::Layout::LineBuilder::Run::expand):
(WebCore::Layout::LineBuilder::Run::removeTrailingWhitespace):
(WebCore::Layout::m_textContext): Deleted.
* layout/inlineformatting/InlineLineBuilder.h:
(WebCore::Layout::LineBuilder::Run::textContent const):
(WebCore::Layout::LineBuilder::Run::textContext const): Deleted.
* layout/integration/LayoutIntegrationLineLayout.cpp:
(WebCore::LayoutIntegration::LineLayout::paint):
* layout/layouttree/LayoutTreeBuilder.cpp:
(WebCore::Layout::outputInlineRuns):
* rendering/line/LineLayoutTraversalDisplayRunPath.h:
(WebCore::LineLayoutTraversal::DisplayRunPath::hasHyphen const):
(WebCore::LineLayoutTraversal::DisplayRunPath::text const):
(WebCore::LineLayoutTraversal::DisplayRunPath::localStartOffset const):
(WebCore::LineLayoutTraversal::DisplayRunPath::localEndOffset const):
(WebCore::LineLayoutTraversal::DisplayRunPath::length const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@256437 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/layout/Verification.cpp
Source/WebCore/layout/displaytree/DisplayPainter.cpp
Source/WebCore/layout/displaytree/DisplayRun.h
Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp
Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp
Source/WebCore/layout/inlineformatting/InlineLineBuilder.h
Source/WebCore/layout/integration/LayoutIntegrationLineLayout.cpp
Source/WebCore/layout/layouttree/LayoutTreeBuilder.cpp
Source/WebCore/rendering/line/LineLayoutTraversalDisplayRunPath.h

index 9418cec..3e85311 100644 (file)
@@ -1,3 +1,56 @@
+2020-02-12  Zalan Bujtas  <zalan@apple.com>
+
+        [LFC][IFC] Rename Display::Run::TextContext to TextContent
+        https://bugs.webkit.org/show_bug.cgi?id=207611
+        <rdar://problem/59374523>
+
+        Reviewed by Antti Koivisto.
+
+        * layout/Verification.cpp:
+        (WebCore::Layout::outputMismatchingSimpleLineInformationIfNeeded):
+        (WebCore::Layout::checkForMatchingTextRuns):
+        (WebCore::Layout::outputMismatchingComplexLineInformationIfNeeded):
+        * layout/displaytree/DisplayPainter.cpp:
+        (WebCore::Display::paintInlineContent):
+        * layout/displaytree/DisplayRun.h:
+        (WebCore::Display::Run::setTextContent):
+        (WebCore::Display::Run::textContent const):
+        (WebCore::Display::Run::textContent):
+        (WebCore::Display::Run::isLineBreak const):
+        (WebCore::Display::Run::Run):
+        (WebCore::Display::Run::TextContent::TextContent):
+        (WebCore::Display::Run::TextContext::start const): Deleted.
+        (WebCore::Display::Run::TextContext::end const): Deleted.
+        (WebCore::Display::Run::TextContext::length const): Deleted.
+        (WebCore::Display::Run::TextContext::content const): Deleted.
+        (WebCore::Display::Run::TextContext::needsHyphen const): Deleted.
+        (WebCore::Display::Run::TextContext::setNeedsHyphen): Deleted.
+        (WebCore::Display::Run::TextContext::expand): Deleted.
+        (WebCore::Display::Run::setTextContext): Deleted.
+        (WebCore::Display::Run::textContext const): Deleted.
+        (WebCore::Display::Run::textContext): Deleted.
+        (WebCore::Display::Run::TextContext::TextContext): Deleted.
+        * layout/inlineformatting/InlineFormattingContext.cpp:
+        (WebCore::Layout::InlineFormattingContext::setDisplayBoxesForLine):
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::m_textContent):
+        (WebCore::Layout::LineBuilder::Run::expand):
+        (WebCore::Layout::LineBuilder::Run::removeTrailingWhitespace):
+        (WebCore::Layout::m_textContext): Deleted.
+        * layout/inlineformatting/InlineLineBuilder.h:
+        (WebCore::Layout::LineBuilder::Run::textContent const):
+        (WebCore::Layout::LineBuilder::Run::textContext const): Deleted.
+        * layout/integration/LayoutIntegrationLineLayout.cpp:
+        (WebCore::LayoutIntegration::LineLayout::paint):
+        * layout/layouttree/LayoutTreeBuilder.cpp:
+        (WebCore::Layout::outputInlineRuns):
+        * rendering/line/LineLayoutTraversalDisplayRunPath.h:
+        (WebCore::LineLayoutTraversal::DisplayRunPath::hasHyphen const):
+        (WebCore::LineLayoutTraversal::DisplayRunPath::text const):
+        (WebCore::LineLayoutTraversal::DisplayRunPath::localStartOffset const):
+        (WebCore::LineLayoutTraversal::DisplayRunPath::localEndOffset const):
+        (WebCore::LineLayoutTraversal::DisplayRunPath::length const):
+
 2020-02-12  Charles Turner  <cturner@igalia.com>
 
         [GStreamer][EME] Fix build with ENCRYPTED_MEDIA=OFF
index aaae8a7..9025579 100644 (file)
@@ -96,19 +96,19 @@ static bool outputMismatchingSimpleLineInformationIfNeeded(TextStream& stream, c
         auto& displayRun = displayRuns[i];
 
         auto matchingRuns = areEssentiallyEqual(simpleRun.logicalLeft, displayRun.left()) && areEssentiallyEqual(simpleRun.logicalRight, displayRun.right());
-        if (matchingRuns && displayRun.textContext()) {
-            matchingRuns = simpleRun.start == displayRun.textContext()->start() && simpleRun.end == displayRun.textContext()->end();
+        if (matchingRuns && displayRun.textContent()) {
+            matchingRuns = simpleRun.start == displayRun.textContent()->start() && simpleRun.end == displayRun.textContent()->end();
             // SLL handles strings in a more concatenated format <div>foo<br>bar</div> -> foo -> 0,3 bar -> 3,6 vs. 0,3 and 0,3
             if (!matchingRuns)
-                matchingRuns = (simpleRun.end - simpleRun.start) == (displayRun.textContext()->end() - displayRun.textContext()->start());
+                matchingRuns = (simpleRun.end - simpleRun.start) == (displayRun.textContent()->end() - displayRun.textContent()->start());
         }
         if (matchingRuns)
             continue;
 
         stream << "Mismatching: simple run(" << simpleRun.start << ", " << simpleRun.end << ") (" << simpleRun.logicalLeft << ", " << simpleRun.logicalRight << ")";
         stream << " inline run";
-        if (displayRun.textContext())
-            stream << " (" << displayRun.textContext()->start() << ", " << displayRun.textContext()->end() << ")";
+        if (displayRun.textContent())
+            stream << " (" << displayRun.textContent()->start() << ", " << displayRun.textContent()->end() << ")";
         stream << " (" << displayRun.left() << ", " << displayRun.top() << ") (" << displayRun.width() << "x" << displayRun.height() << ")";
         stream.nextLine();
         mismatched = true;
@@ -131,7 +131,7 @@ static bool checkForMatchingTextRuns(const Display::Run& inlineRun, const WebCor
         && areEssentiallyEqual(inlineTextBox.right(), inlineRun.right())
         && areEssentiallyEqual(inlineTextBox.top(), inlineRun.top())
         && areEssentiallyEqual(inlineTextBox.bottom(), inlineRun.bottom())
-        && (inlineTextBox.isLineBreak() || (inlineTextBox.start() == inlineRun.textContext()->start() && inlineTextBox.end() == inlineRun.textContext()->end()));
+        && (inlineTextBox.isLineBreak() || (inlineTextBox.start() == inlineRun.textContent()->start() && inlineTextBox.end() == inlineRun.textContent()->end()));
 }
 
 static void collectFlowBoxSubtree(const InlineFlowBox& flowbox, Vector<WebCore::InlineBox*>& inlineBoxes)
@@ -204,8 +204,8 @@ static bool outputMismatchingComplexLineInformationIfNeeded(TextStream& stream,
             stream << " (" << inlineBox->logicalLeft() << ", " << inlineBox->logicalTop() << ") (" << inlineBox->logicalWidth() << "x" << inlineBox->logicalHeight() << ")";
 
             stream << " inline run";
-            if (displayRun.textContext())
-                stream << " (" << displayRun.textContext()->start() << ", " << displayRun.textContext()->end() << ")";
+            if (displayRun.textContent())
+                stream << " (" << displayRun.textContent()->start() << ", " << displayRun.textContent()->end() << ")";
             stream << " (" << displayRun.left() << ", " << displayRun.top() << ") (" << displayRun.width() << "x" << displayRun.height() << ")";
             stream.nextLine();
             mismatched = true;
index dd67825..328e518 100644 (file)
@@ -121,7 +121,7 @@ static void paintInlineContent(GraphicsContext& context, LayoutPoint absoluteOff
         return;
 
     for (auto& run : displayRuns) {
-        if (auto& textContext = run.textContext()) {
+        if (auto& textContent = run.textContent()) {
             auto& style = run.style();
             context.setStrokeColor(style.color());
             context.setFillColor(style.color());
@@ -131,7 +131,7 @@ static void paintInlineContent(GraphicsContext& context, LayoutPoint absoluteOff
             auto& lineBox = displayInlineContent->lineBoxForRun(run);
             auto baselineOffset = absoluteOffset.y() + lineBox.top() + lineBox.baselineOffset();
             auto expansion = run.expansion();
-            auto textRun = TextRun { textContext->content(), run.left() - lineBox.left(), expansion.horizontalExpansion, expansion.behavior };
+            auto textRun = TextRun { textContent->content(), run.left() - lineBox.left(), expansion.horizontalExpansion, expansion.behavior };
             textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
             context.drawText(style.fontCascade(), textRun, { absoluteLeft, baselineOffset });
         } else if (auto* cachedImage = run.image()) {
index 26ed4d1..c8a29df 100644 (file)
@@ -40,11 +40,10 @@ namespace Display {
 
 struct Run {
     WTF_MAKE_STRUCT_FAST_ALLOCATED;
-    struct TextContext {
+    struct TextContent {
         WTF_MAKE_STRUCT_FAST_ALLOCATED;
     public:
-        struct ExpansionContext;
-        TextContext(unsigned position, unsigned length, const String&);
+        TextContent(unsigned position, unsigned length, const String&);
 
         unsigned start() const { return m_start; }
         unsigned end() const { return start() + length(); }
@@ -64,7 +63,7 @@ struct Run {
     };
 
     struct Expansion;
-    Run(size_t lineIndex, const Layout::Box&, const InlineRect&, const InlineRect& inkOverflow, Expansion, Optional<TextContext> = WTF::nullopt);
+    Run(size_t lineIndex, const Layout::Box&, const InlineRect&, const InlineRect& inkOverflow, Expansion, Optional<TextContent> = WTF::nullopt);
 
     size_t lineIndex() const { return m_lineIndex; }
 
@@ -88,9 +87,9 @@ struct Run {
     void expandVertically(InlineLayoutUnit delta) { m_rect.expandVertically(delta); }
     void expandHorizontally(InlineLayoutUnit delta) { m_rect.expandHorizontally(delta); }
 
-    void setTextContext(const TextContext&& textContext) { m_textContext.emplace(textContext); }
-    const Optional<TextContext>& textContext() const { return m_textContext; }
-    Optional<TextContext>& textContext() { return m_textContext; }
+    void setTextContent(const TextContent&& textContent) { m_textContent.emplace(textContent); }
+    const Optional<TextContent>& textContent() const { return m_textContent; }
+    Optional<TextContent>& textContent() { return m_textContent; }
 
     struct Expansion {
         ExpansionBehavior behavior { DefaultExpansion };
@@ -102,7 +101,8 @@ struct Run {
     void setImage(CachedImage& image) { m_cachedImage = &image; }
     CachedImage* image() const { return m_cachedImage; }
 
-    bool isLineBreak() const { return layoutBox().isLineBreakBox() || (textContext() && textContext()->content() == "\n" && style().preserveNewline()); }
+    // FIXME: This information should be preserved at Run construction time.
+    bool isLineBreak() const { return layoutBox().isLineBreakBox() || (textContent() && textContent()->content() == "\n" && style().preserveNewline()); }
 
     const Layout::Box& layoutBox() const { return *m_layoutBox; }
     const RenderStyle& style() const { return m_layoutBox->style(); }
@@ -115,20 +115,20 @@ private:
     InlineRect m_rect;
     InlineRect m_inkOverflow;
     Expansion m_expansion;
-    Optional<TextContext> m_textContext;
+    Optional<TextContent> m_textContent;
 };
 
-inline Run::Run(size_t lineIndex, const Layout::Box& layoutBox, const InlineRect& rect, const InlineRect& inkOverflow, Expansion expansion, Optional<TextContext> textContext)
+inline Run::Run(size_t lineIndex, const Layout::Box& layoutBox, const InlineRect& rect, const InlineRect& inkOverflow, Expansion expansion, Optional<TextContent> textContent)
     : m_lineIndex(lineIndex)
     , m_layoutBox(makeWeakPtr(layoutBox))
     , m_rect(rect)
     , m_inkOverflow(inkOverflow)
     , m_expansion(expansion)
-    , m_textContext(textContext)
+    , m_textContent(textContent)
 {
 }
 
-inline Run::TextContext::TextContext(unsigned start, unsigned length, const String& contentString)
+inline Run::TextContent::TextContent(unsigned start, unsigned length, const String& contentString)
     : m_start(start)
     , m_length(length)
     , m_contentString(contentString)
index 2ec576c..6ea3130 100644 (file)
@@ -485,7 +485,7 @@ void InlineFormattingContext::setDisplayBoxesForLine(const LineLayoutContext::Li
             };
             auto inkOverflow = computedInkOverflow();
             lineInkOverflow.expandToContain(inkOverflow);
-            inlineContent.runs.append({ lineIndex, lineRun.layoutBox(), logicalRect, inkOverflow, lineRun.expansion(), lineRun.textContext() });
+            inlineContent.runs.append({ lineIndex, lineRun.layoutBox(), logicalRect, inkOverflow, lineRun.expansion(), lineRun.textContent() });
         }
 
         if (lineRun.isLineBreak()) {
@@ -543,7 +543,7 @@ void InlineFormattingContext::setDisplayBoxesForLine(const LineLayoutContext::Li
     }
     // Make sure the trailing text run gets a hyphen when it needs one.
     if (lineContent.partialContent && lineContent.partialContent->trailingContentNeedsHyphen)
-        inlineContent.runs[*lastTextItemIndex].textContext()->setNeedsHyphen();
+        inlineContent.runs[*lastTextItemIndex].textContent()->setNeedsHyphen();
     // FIXME: This is where the logical to physical translate should happen.
     auto& baseline = lineBox.baseline();
     inlineContent.lineBoxes.append({ lineBox.logicalRect(), lineBox.scrollableOverflow(), lineInkOverflow, { baseline.ascent(), baseline.descent() }, lineBox.baselineOffset(), lineBox.isConsideredEmpty() });
index c6a647e..2a3b352 100644 (file)
@@ -750,7 +750,7 @@ LineBuilder::Run::Run(const InlineSoftLineBreakItem& softLineBreakItem, InlineLa
     : m_type(softLineBreakItem.type())
     , m_layoutBox(&softLineBreakItem.layoutBox())
     , m_logicalRect({ 0, logicalLeft, 0, 0 })
-    , m_textContext({ softLineBreakItem.position(), 1, softLineBreakItem.inlineTextBox().content() })
+    , m_textContent({ softLineBreakItem.position(), 1, softLineBreakItem.inlineTextBox().content() })
 {
 }
 
@@ -759,7 +759,7 @@ LineBuilder::Run::Run(const InlineTextItem& inlineTextItem, InlineLayoutUnit log
     , m_layoutBox(&inlineTextItem.layoutBox())
     , m_logicalRect({ 0, logicalLeft, logicalWidth, 0 })
     , m_trailingWhitespaceType(trailingWhitespaceType(inlineTextItem))
-    , m_textContext({ inlineTextItem.start(), m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length(), inlineTextItem.inlineTextBox().content() })
+    , m_textContent({ inlineTextItem.start(), m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length(), inlineTextItem.inlineTextBox().content() })
 {
     if (m_trailingWhitespaceType != TrailingWhitespace::None) {
         m_trailingWhitespaceWidth = logicalWidth;
@@ -781,14 +781,14 @@ void LineBuilder::Run::expand(const InlineTextItem& inlineTextItem, InlineLayout
     if (m_trailingWhitespaceType == TrailingWhitespace::None) {
         m_trailingWhitespaceWidth = { };
         setExpansionBehavior(AllowLeadingExpansion | AllowTrailingExpansion);
-        m_textContext->expand(inlineTextItem.length());
+        m_textContent->expand(inlineTextItem.length());
         return;
     }
     m_trailingWhitespaceWidth += logicalWidth;
     if (!isWhitespacePreserved(inlineTextItem.style()))
         ++m_expansionOpportunityCount;
     setExpansionBehavior(DefaultExpansion);
-    m_textContext->expand(m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length());
+    m_textContent->expand(m_trailingWhitespaceType == TrailingWhitespace::Collapsed ? 1 : inlineTextItem.length());
 }
 
 bool LineBuilder::Run::hasTrailingLetterSpacing() const
@@ -817,8 +817,8 @@ void LineBuilder::Run::removeTrailingWhitespace()
 {
     // According to https://www.w3.org/TR/css-text-3/#white-space-property matrix
     // Trimmable whitespace is always collapsable so the length of the trailing trimmable whitespace is always 1 (or non-existent).
-    ASSERT(m_textContext->length());
-    m_textContext->expand(-1);
+    ASSERT(m_textContent->length());
+    m_textContent->expand(-1);
     visuallyCollapseTrailingWhitespace();
 }
 
index 7f84618..af82601 100644 (file)
@@ -85,7 +85,7 @@ public:
         const RenderStyle& style() const { return m_layoutBox->style(); }
         const Display::InlineRect& logicalRect() const { return m_logicalRect; }
         Display::Run::Expansion expansion() const { return m_expansion; }
-        const Optional<Display::Run::TextContext>& textContext() const { return m_textContext; }
+        const Optional<Display::Run::TextContent>& textContent() const { return m_textContent; }
 
         Run(Run&&) = default;
         Run& operator=(Run&& other) = default;
@@ -137,7 +137,7 @@ public:
         Display::InlineRect m_logicalRect;
         TrailingWhitespace m_trailingWhitespaceType { TrailingWhitespace::None };
         InlineLayoutUnit m_trailingWhitespaceWidth { 0 };
-        Optional<Display::Run::TextContext> m_textContext;
+        Optional<Display::Run::TextContent> m_textContent;
         Display::Run::Expansion m_expansion;
         unsigned m_expansionOpportunityCount { 0 };
     };
index 527891e..190fc06 100644 (file)
@@ -267,11 +267,11 @@ void LineLayout::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
     paintRect.moveBy(-paintOffset);
 
     for (auto& run : inlineContent.runsForRect(paintRect)) {
-        if (!run.textContext())
+        if (!run.textContent())
             continue;
 
-        auto& textContext = *run.textContext();
-        if (!textContext.length())
+        auto& textContent = *run.textContent();
+        if (!textContent.length())
             continue;
 
         auto& style = run.style();
@@ -294,9 +294,9 @@ void LineLayout::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
         auto expansion = run.expansion();
 
         String textWithHyphen;
-        if (textContext.needsHyphen())
-            textWithHyphen = makeString(textContext.content(), style.hyphenString());
-        TextRun textRun { !textWithHyphen.isEmpty() ? textWithHyphen : textContext.content(), run.left() - lineBox.left(), expansion.horizontalExpansion, expansion.behavior };
+        if (textContent.needsHyphen())
+            textWithHyphen = makeString(textContent.content(), style.hyphenString());
+        TextRun textRun { !textWithHyphen.isEmpty() ? textWithHyphen : textContent.content(), run.left() - lineBox.left(), expansion.horizontalExpansion, expansion.behavior };
         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
         FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(baselineOffset, deviceScaleFactor) };
 
index 22f76d7..d5efe71 100644 (file)
@@ -341,13 +341,13 @@ static void outputInlineRuns(TextStream& stream, const LayoutState& layoutState,
         while (++printedCharacters <= depth * 2)
             stream << " ";
         stream << "  ";
-        if (displayRun.textContext())
+        if (displayRun.textContent())
             stream << "inline text box";
         else
             stream << "inline box";
         stream << " at (" << displayRun.left() << "," << displayRun.top() << ") size " << displayRun.width() << "x" << displayRun.height();
-        if (displayRun.textContext())
-            stream << " run(" << displayRun.textContext()->start() << ", " << displayRun.textContext()->end() << ")";
+        if (displayRun.textContent())
+            stream << " run(" << displayRun.textContent()->start() << ", " << displayRun.textContent()->end() << ")";
         stream.nextLine();
     }
 }
index 030a687..0c849bf 100644 (file)
@@ -67,11 +67,11 @@ public:
         return previous.lineIndex() == run().lineIndex();
     }
 
-    bool hasHyphen() const { return run().textContext()->needsHyphen(); }
-    StringView text() const { return run().textContext()->content(); }
-    unsigned localStartOffset() const { return run().textContext()->start(); }
-    unsigned localEndOffset() const { return run().textContext()->end(); }
-    unsigned length() const { return run().textContext()->length(); }
+    bool hasHyphen() const { return run().textContent()->needsHyphen(); }
+    StringView text() const { return run().textContent()->content(); }
+    unsigned localStartOffset() const { return run().textContent()->start(); }
+    unsigned localEndOffset() const { return run().textContent()->end(); }
+    unsigned length() const { return run().textContent()->length(); }
 
     bool isLastOnLine() const
     {