⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 226138 in webkit


Ignore:
Timestamp:
Dec 19, 2017, 1:48:19 PM (9 years ago)
Author:
dbates@webkit.org
Message:

Implement InlineTextBox painting using marker subranges
https://bugs.webkit.org/show_bug.cgi?id=180984
<rdar://problem/36139364>

Reviewed by David Hyatt.

Source/WebCore:

As a step towards implementing CSS Pseudo-Elements Module Level 4, explicitly decompose a
text line into subrange objects that may be styled (say, to give the appearance of selected
text) and painted.

No functionality changed. So, no new tests.

  • rendering/InlineTextBox.cpp:

(WebCore::InlineTextBox::localSelectionRect const): Update comments.

(WebCore::InlineTextBox::MarkerSubrangeStyle::operator== const):
(WebCore::InlineTextBox::MarkerSubrangeStyle::operator!= const):
(WebCore::InlineTextBox::StyledMarkerSubrange::StyledMarkerSubrange):
Define a subclass to represent a marker subrange that has associated style information.
We will make use of the style information when painting the subrange.

(WebCore::createMarkerSubrangeFromSelectionInBox): Convenience function to instantiate a
MarkerSubrange instance from the current selection of a specified text box.

(WebCore::InlineTextBox::paint): Write in terms of MarkerSubrange. We keep composition backgrounds
and composition underlines as special cases for now. We represent all other subranges of the line
as MarkerSubrange instances, including unmarked text (i.e. non-selected text without an associated
document marker). The subranges for the gaps between document markers and selection are implicitly
created by subdividing the entire line. Support for PaintBehaviorExcludeSelection is implemented
by subdividing the entire line with the selection subrange and then removing the selection subrange
from the resulting list of subdivisions before painting with the resulting list.

(WebCore::InlineTextBox::paintPlatformDocumentMarkers): Renamed; formerly named paintDocumentMarkers.
(WebCore::InlineTextBox::paintPlatformDocumentMarker): Renamed; formerly named paintDocumentMarker.

(WebCore::InlineTextBox::computeStyleForUnmarkedMarkerSubrange const): Convenience function to
compute the style for unmarked text.

(WebCore::InlineTextBox::resolveStyleForSubrange): Added

(WebCore::InlineTextBox::subdivideAndResolveStyle): Subdivides the list of marker subranges and
coalesces subranges of the same type (e.g. selection) or with the same style. The latter strategy
preserves the optimization of drawing the text of the entire line in one draw command when we know
that the selected text looks identical to non-selected text. This optimization was performed in TextPainter::paint().

(WebCore::InlineTextBox::collectSubrangesForDraggedContent): Added.
(WebCore::InlineTextBox::collectSubrangesForDocumentMarkers): Added.
(WebCore::InlineTextBox::textOriginFromBoxRect const): Added.

(WebCore::InlineTextBox::paintMarkerSubranges):
(WebCore::InlineTextBox::paintTextSubrangeBackground):
(WebCore::InlineTextBox::paintTextSubrangeForeground):
(WebCore::InlineTextBox::paintTextSubrangeDecoration):
Paint the marker subrange.

(WebCore::InlineTextBox::paintCompositionBackground): Moved function to group it with the logic to
paint composition underlines. Modified to pass clamped offsets to paintTextSubrangeBackground() as
paintTextSubrangeBackground() now expects them.

(WebCore::mirrorRTLSegment): Moved function to be above paintCompositionUnderline() as it is the only
function that makes use of it.

(WebCore::InlineTextBox::paintSelection): Deleted.
(WebCore::InlineTextBox::paintTextMatchMarker): Deleted.
(WebCore::InlineTextBox::paintDecoration): Deleted.
(WebCore::InlineTextBox::paintDocumentMarker): Deleted; renamed to paintPlatformDocumentMarker.
(WebCore::InlineTextBox::paintDocumentMarkers): Deleted; renamed to paintPlatformDocumentMarkers.

  • rendering/InlineTextBox.h:

(WebCore::InlineTextBox::paintMarkerSubranges): Added.

  • rendering/MarkerSubrange.h:

(WebCore::MarkerSubrange::isEmpty const): Added.
(WebCore::MarkerSubrange::operator!= const): Added.
(WebCore::MarkerSubrange::operator== const): Added.

  • rendering/SimpleLineLayoutFunctions.cpp:

(WebCore::SimpleLineLayout::paintFlow): Updated code as we no longer need to pass the text run length
to TextPainter::paint(). Also modernize the code while I am here.

  • rendering/TextDecorationPainter.cpp:

(WebCore::TextDecorationPainter::Styles::operator== const): Added.
(WebCore::TextDecorationPainter::TextDecorationPainter): Modified to take an optional TextDecorationPainter::Styles
instance as an argument and removed the unused parameter for PseudoId.

  • rendering/TextDecorationPainter.h:

(WebCore::TextDecorationPainter::Styles::operator!= const): Added.

  • rendering/TextPaintStyle.cpp:

(WebCore::TextPaintStyle::operator== const): Added.
(WebCore::computeTextSelectionPaintStyle): Removed the now unused out-arguments paintSelectedTextOnly, paintSelectedTextSeparately,
and paintNonSelectedTextOnly. The values stored in these out-arguments were intended to be used by the caller to
minimize the number of drawing commands. The value of paintSelectedTextSeparately indicated whether selected text
would look identical to non-selected text so that a caller could use one paint command to draw the entire line
instead of issuing two or more drawing commands to paint non-selected and selected text separately. We now
accomplish the same optimization by coalescing subrange styles in InlineTextBox::subdivideAndResolveStyle().
The value of paintNonSelectedTextOnly, as its name states, indicated whether a caller should only paint non-selected
text and hence skip painting of selected text. This value was only used when painting dragged content (i.e. DocumentMarker::DraggedContent
markers) and its effect is now more directly achieved by the dragged content logic in InlineTextBox::paint().

  • rendering/TextPaintStyle.h:

(WebCore::TextPaintStyle::operator!= const): Added.

  • rendering/TextPainter.cpp:

(WebCore::TextPainter::paint): Implemented in terms TextPainter::paintRange() now that InlineTextBox::paint() optimizes
for the number of drawing calls. See remarks for WebCore::computeTextSelectionPaintStyle() for more details.

  • rendering/TextPainter.h: Group together related member functions.

(WebCore::TextPainter::setSelectionStyle): Deleted.
(WebCore::TextPainter::setSelectionShadow): Deleted.

Tools:

Update unit tests now that we use subranges for the painting of dragged content.

  • TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp:

(WebCore::operator<<):
(WebCore::operator==): Deleted; moved to class MarkerSubrange.

Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r226137 r226138  
     12017-12-19  Daniel Bates  <dabates@apple.com>
     2
     3        Implement InlineTextBox painting using marker subranges
     4        https://bugs.webkit.org/show_bug.cgi?id=180984
     5        <rdar://problem/36139364>
     6
     7        Reviewed by David Hyatt.
     8
     9        As a step towards implementing CSS Pseudo-Elements Module Level 4, explicitly decompose a
     10        text line into subrange objects that may be styled (say, to give the appearance of selected
     11        text) and painted.
     12
     13        No functionality changed. So, no new tests.
     14
     15        * rendering/InlineTextBox.cpp:
     16        (WebCore::InlineTextBox::localSelectionRect const): Update comments.
     17
     18        (WebCore::InlineTextBox::MarkerSubrangeStyle::operator== const):
     19        (WebCore::InlineTextBox::MarkerSubrangeStyle::operator!= const):
     20        (WebCore::InlineTextBox::StyledMarkerSubrange::StyledMarkerSubrange):
     21        Define a subclass to represent a marker subrange that has associated style information.
     22        We will make use of the style information when painting the subrange.
     23
     24        (WebCore::createMarkerSubrangeFromSelectionInBox): Convenience function to instantiate a
     25        MarkerSubrange instance from the current selection of a specified text box.
     26
     27        (WebCore::InlineTextBox::paint): Write in terms of MarkerSubrange. We keep composition backgrounds
     28        and composition underlines as special cases for now. We represent all other subranges of the line
     29        as MarkerSubrange instances, including unmarked text (i.e. non-selected text without an associated
     30        document marker). The subranges for the gaps between document markers and selection are implicitly
     31        created by subdividing the entire line. Support for PaintBehaviorExcludeSelection is implemented
     32        by subdividing the entire line with the selection subrange and then removing the selection subrange
     33        from the resulting list of subdivisions before painting with the resulting list.
     34
     35        (WebCore::InlineTextBox::paintPlatformDocumentMarkers): Renamed; formerly named paintDocumentMarkers.
     36        (WebCore::InlineTextBox::paintPlatformDocumentMarker): Renamed; formerly named paintDocumentMarker.
     37
     38        (WebCore::InlineTextBox::computeStyleForUnmarkedMarkerSubrange const): Convenience function to
     39        compute the style for unmarked text.
     40
     41        (WebCore::InlineTextBox::resolveStyleForSubrange): Added
     42
     43        (WebCore::InlineTextBox::subdivideAndResolveStyle): Subdivides the list of marker subranges and
     44        coalesces subranges of the same type (e.g. selection) or with the same style. The latter strategy
     45        preserves the optimization of drawing the text of the entire line in one draw command when we know
     46        that the selected text looks identical to non-selected text. This optimization was performed in TextPainter::paint().
     47
     48        (WebCore::InlineTextBox::collectSubrangesForDraggedContent): Added.
     49        (WebCore::InlineTextBox::collectSubrangesForDocumentMarkers): Added.
     50        (WebCore::InlineTextBox::textOriginFromBoxRect const): Added.
     51
     52        (WebCore::InlineTextBox::paintMarkerSubranges):
     53        (WebCore::InlineTextBox::paintTextSubrangeBackground):
     54        (WebCore::InlineTextBox::paintTextSubrangeForeground):
     55        (WebCore::InlineTextBox::paintTextSubrangeDecoration):
     56        Paint the marker subrange.
     57
     58        (WebCore::InlineTextBox::paintCompositionBackground): Moved function to group it with the logic to
     59        paint composition underlines. Modified to pass clamped offsets to paintTextSubrangeBackground() as
     60        paintTextSubrangeBackground() now expects them.
     61
     62        (WebCore::mirrorRTLSegment): Moved function to be above paintCompositionUnderline() as it is the only
     63        function that makes use of it.
     64
     65        (WebCore::InlineTextBox::paintSelection): Deleted.
     66        (WebCore::InlineTextBox::paintTextMatchMarker): Deleted.
     67        (WebCore::InlineTextBox::paintDecoration): Deleted.
     68        (WebCore::InlineTextBox::paintDocumentMarker): Deleted; renamed to paintPlatformDocumentMarker.
     69        (WebCore::InlineTextBox::paintDocumentMarkers): Deleted; renamed to paintPlatformDocumentMarkers.
     70        * rendering/InlineTextBox.h:
     71        (WebCore::InlineTextBox::paintMarkerSubranges): Added.
     72        * rendering/MarkerSubrange.h:
     73        (WebCore::MarkerSubrange::isEmpty const): Added.
     74        (WebCore::MarkerSubrange::operator!= const): Added.
     75        (WebCore::MarkerSubrange::operator== const): Added.
     76        * rendering/SimpleLineLayoutFunctions.cpp:
     77        (WebCore::SimpleLineLayout::paintFlow): Updated code as we no longer need to pass the text run length
     78        to TextPainter::paint(). Also modernize the code while I am here.
     79        * rendering/TextDecorationPainter.cpp:
     80        (WebCore::TextDecorationPainter::Styles::operator== const): Added.
     81        (WebCore::TextDecorationPainter::TextDecorationPainter): Modified to take an optional TextDecorationPainter::Styles
     82        instance as an argument and removed the unused parameter for PseudoId.
     83        * rendering/TextDecorationPainter.h:
     84        (WebCore::TextDecorationPainter::Styles::operator!= const): Added.
     85        * rendering/TextPaintStyle.cpp:
     86        (WebCore::TextPaintStyle::operator== const): Added.
     87        (WebCore::computeTextSelectionPaintStyle): Removed the now unused out-arguments paintSelectedTextOnly, paintSelectedTextSeparately,
     88        and paintNonSelectedTextOnly. The values stored in these out-arguments were intended to be used by the caller to
     89        minimize the number of drawing commands. The value of paintSelectedTextSeparately indicated whether selected text
     90        would look identical to non-selected text so that a caller could use one paint command to draw the entire line
     91        instead of issuing two or more drawing commands to paint non-selected and selected text separately. We now
     92        accomplish the same optimization by coalescing subrange styles in InlineTextBox::subdivideAndResolveStyle().
     93        The value of paintNonSelectedTextOnly, as its name states, indicated whether a caller should only paint non-selected
     94        text and hence skip painting of selected text. This value was only used when painting dragged content (i.e. DocumentMarker::DraggedContent
     95        markers) and its effect is now more directly achieved by the dragged content logic in InlineTextBox::paint().
     96        * rendering/TextPaintStyle.h:
     97        (WebCore::TextPaintStyle::operator!= const): Added.
     98        * rendering/TextPainter.cpp:
     99        (WebCore::TextPainter::paint): Implemented in terms TextPainter::paintRange() now that InlineTextBox::paint() optimizes
     100        for the number of drawing calls. See remarks for WebCore::computeTextSelectionPaintStyle() for more details.
     101        * rendering/TextPainter.h: Group together related member functions.
     102        (WebCore::TextPainter::setSelectionStyle): Deleted.
     103        (WebCore::TextPainter::setSelectionShadow): Deleted.
     104
    11052017-12-19  Daniel Bates  <dabates@apple.com>
    2106
  • trunk/Source/WebCore/rendering/InlineTextBox.cpp

    r225712 r226138  
    189189}
    190190
    191 // FIXME: Share more code with paintSelection().
     191// FIXME: Share more code with paintTextSubrangeBackground().
    192192LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
    193193{
     
    205205
    206206    LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
    207     // Avoid computing the font width when the entire line box is selected as an optimization.
     207    // Avoid measuring the text when the entire line box is selected as an optimization.
    208208    if (sPos || ePos != textRun.length())
    209209        lineFont().adjustSelectionRectForText(textRun, selectionRect, sPos, ePos);
    210210    // FIXME: The computation of the snapped selection rect differs from the computation of this rect
    211     // in paintSelection(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.
     211    // in paintTextSubrangeBackground(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.
    212212    IntRect snappedSelectionRect = enclosingIntRect(selectionRect);
    213213    LayoutUnit logicalWidth = snappedSelectionRect.width();
     
    393393}
    394394
     395struct InlineTextBox::MarkerSubrangeStyle {
     396    bool operator==(const MarkerSubrangeStyle& other) const
     397    {
     398        return backgroundColor == other.backgroundColor && textStyles == other.textStyles
     399            && textDecorationStyles == other.textDecorationStyles && textShadow == other.textShadow
     400            && alpha == other.alpha;
     401    }
     402    bool operator!=(const MarkerSubrangeStyle& other) const { return !(*this == other); }
     403
     404    Color backgroundColor;
     405    TextPaintStyle textStyles;
     406    TextDecorationPainter::Styles textDecorationStyles;
     407    const ShadowData* textShadow;
     408    float alpha;
     409};
     410
     411struct InlineTextBox::StyledMarkerSubrange : MarkerSubrange {
     412    StyledMarkerSubrange(const MarkerSubrange& marker)
     413        : MarkerSubrange { marker }
     414    {
     415    }
     416
     417    MarkerSubrangeStyle style;
     418};
     419
     420static MarkerSubrange createMarkerSubrangeFromSelectionInBox(const InlineTextBox& box)
     421{
     422    unsigned selectionStart;
     423    unsigned selectionEnd;
     424    std::tie(selectionStart, selectionEnd) = box.selectionStartEnd();
     425    if (selectionStart < selectionEnd)
     426        return { selectionStart, selectionEnd, MarkerSubrange::Selection };
     427    return { };
     428}
     429
    395430void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
    396431{
     
    459494    bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
    460495
    461     // Determine the text colors and selection colors.
    462     TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
    463 
    464     bool paintSelectedTextOnly = false;
    465     bool paintSelectedTextSeparately = false;
    466     bool paintNonSelectedTextOnly = false;
    467     const ShadowData* selectionShadow = nullptr;
    468    
    469     // Text with custom underlines does not have selection background painted, so selection paint style is not appropriate for it.
    470     TextPaintStyle selectionPaintStyle = haveSelection && !useCustomUnderlines ? computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly, selectionShadow) : textPaintStyle;
    471 
    472     // Set our font.
    473     const FontCascade& font = lineFont();
     496    MarkerSubrangeStyle unmarkedStyle = computeStyleForUnmarkedMarkerSubrange(paintInfo);
     497
    474498    // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
    475499    // and composition underlines.
     
    478502            paintCompositionBackground(context, boxOrigin);
    479503
    480         paintDocumentMarkers(context, boxOrigin, true);
    481 
    482         if (haveSelection && !useCustomUnderlines)
    483             paintSelection(context, boxOrigin, selectionPaintStyle.fillColor);
     504        Vector<MarkerSubrange> subranges = collectSubrangesForDocumentMarkers(TextPaintPhase::Background);
     505#if ENABLE(TEXT_SELECTION)
     506        if (haveSelection && !useCustomUnderlines && !context.paintingDisabled()) {
     507            auto selectionSubrange = createMarkerSubrangeFromSelectionInBox(*this);
     508            if (!selectionSubrange.isEmpty())
     509                subranges.append(WTFMove(selectionSubrange));
     510        }
     511#endif
     512        auto styledSubranges = subdivideAndResolveStyle(subranges, unmarkedStyle, paintInfo);
     513        paintMarkerSubranges(context, TextPaintPhase::Background, boxRect, styledSubranges);
    484514    }
    485515
     
    492522
    493523    // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
    494     auto text = this->text();
    495     TextRun textRun = createTextRun(text);
    496     unsigned length = textRun.length();
    497     if (m_truncation != cNoTruncation)
    498         length = m_truncation;
    499 
    500     unsigned selectionStart = 0;
    501     unsigned selectionEnd = 0;
    502     if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately))
    503         std::tie(selectionStart, selectionEnd) = selectionStartEnd();
    504 
    505     float emphasisMarkOffset = 0;
    506     bool emphasisMarkAbove;
    507     bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
    508     const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom();
    509     if (!emphasisMark.isEmpty())
    510         emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
    511 
    512     const ShadowData* textShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow();
    513 
    514     FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
    515     if (combinedText) {
    516         if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
    517             textOrigin = newOrigin.value();
    518     }
    519 
    520     if (isHorizontal())
    521         textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), renderer().document().deviceScaleFactor()));
    522     else
    523         textOrigin.setX(roundToDevicePixel(LayoutUnit(textOrigin.x()), renderer().document().deviceScaleFactor()));
    524 
    525     TextPainter textPainter(context);
    526     textPainter.setFont(font);
    527     textPainter.setStyle(textPaintStyle);
    528     textPainter.setSelectionStyle(selectionPaintStyle);
    529     textPainter.setIsHorizontal(isHorizontal());
    530     textPainter.setShadow(textShadow);
    531     textPainter.setSelectionShadow(selectionShadow);
    532     textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText);
    533 
    534     auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len);
    535     if (!draggedContentRanges.isEmpty() && !paintSelectedTextOnly && !paintNonSelectedTextOnly) {
    536         // FIXME: Painting with text effects ranges currently only works if we're not also painting the selection.
    537         // In the future, we may want to support this capability, but in the meantime, this isn't required by anything.
    538         unsigned currentEnd = 0;
    539         for (size_t index = 0; index < draggedContentRanges.size(); ++index) {
    540             unsigned previousEnd = index ? std::min(draggedContentRanges[index - 1].second, length) : 0;
    541             unsigned currentStart = draggedContentRanges[index].first - m_start;
    542             currentEnd = std::min(draggedContentRanges[index].second - m_start, length);
    543 
    544             if (previousEnd < currentStart)
    545                 textPainter.paintRange(textRun, boxRect, textOrigin, previousEnd, currentStart);
    546 
    547             if (currentStart < currentEnd) {
    548                 context.save();
    549                 context.setAlpha(0.25);
    550                 textPainter.paintRange(textRun, boxRect, textOrigin, currentStart, currentEnd);
    551                 context.restore();
     524    bool shouldPaintSelectionForeground = haveSelection && !useCustomUnderlines;
     525    Vector<MarkerSubrange> subranges;
     526    if (paintInfo.phase != PaintPhaseSelection) {
     527        // The subranges for the gaps between document markers and selection are implicitly created by subdividing the entire line.
     528        subranges.append({ clampedOffset(m_start), clampedOffset(end() + 1), MarkerSubrange::Unmarked });
     529        if (!isPrinting) {
     530            subranges.appendVector(collectSubrangesForDocumentMarkers(TextPaintPhase::Foreground));
     531
     532            bool shouldPaintDraggedContent = !(paintInfo.paintBehavior & PaintBehaviorExcludeSelection);
     533            if (shouldPaintDraggedContent) {
     534                auto subrangesForDraggedContent = collectSubrangesForDraggedContent();
     535                if (!subrangesForDraggedContent.isEmpty()) {
     536                    shouldPaintSelectionForeground = false;
     537                    subranges.appendVector(subrangesForDraggedContent);
     538                }
    552539            }
    553540        }
    554         if (currentEnd < length)
    555             textPainter.paintRange(textRun, boxRect, textOrigin, currentEnd, length);
    556     } else
    557         textPainter.paint(textRun, length, boxRect, textOrigin, selectionStart, selectionEnd, paintSelectedTextOnly, paintSelectedTextSeparately, paintNonSelectedTextOnly);
     541    }
     542    // The selection subrange acts as a placeholder when computing the subranges for the gaps...
     543    if (shouldPaintSelectionForeground) {
     544        ASSERT(!isPrinting);
     545        auto selectionSubrange = createMarkerSubrangeFromSelectionInBox(*this);
     546        if (!selectionSubrange.isEmpty())
     547            subranges.append(WTFMove(selectionSubrange));
     548    }
     549
     550    auto styledSubranges = subdivideAndResolveStyle(subranges, unmarkedStyle, paintInfo);
     551
     552    // ... now remove the selection subrange if we are excluding selection.
     553    if (!isPrinting && paintInfo.paintBehavior & PaintBehaviorExcludeSelection)
     554        styledSubranges.removeAllMatching([] (const StyledMarkerSubrange& subrange) { return subrange.type == MarkerSubrange::Selection; });
     555
     556    paintMarkerSubranges(context, TextPaintPhase::Foreground, boxRect, styledSubranges);
    558557
    559558    // Paint decorations
    560559    TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
    561560    if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) {
     561        auto text = this->text();
     562        TextRun textRun = createTextRun(text);
     563        unsigned length = textRun.length();
     564        if (m_truncation != cNoTruncation)
     565            length = m_truncation;
     566        unsigned selectionStart = 0;
     567        unsigned selectionEnd = 0;
     568        if (haveSelection)
     569            std::tie(selectionStart, selectionEnd) = selectionStartEnd();
     570
    562571        FloatRect textDecorationSelectionClipOutRect;
    563572        if ((paintInfo.paintBehavior & PaintBehaviorExcludeSelection) && selectionStart < selectionEnd && selectionEnd <= length) {
     
    566575            float logicalWidthBeforeRange;
    567576            float logicalWidthAfterRange;
    568             float logicalSelectionWidth = font.widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
     577            float logicalSelectionWidth = lineFont().widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
    569578            // FIXME: Do we need to handle vertical bottom to top text?
    570579            if (!isHorizontal()) {
     
    579588            }
    580589        }
    581         paintDecoration(context, textRun, textOrigin, boxRect, textDecorations, textPaintStyle, textShadow, textDecorationSelectionClipOutRect);
    582     }
    583 
     590        paintMarkerSubranges(context, TextPaintPhase::Decoration, boxRect, styledSubranges, textDecorationSelectionClipOutRect);
     591    }
     592
     593    // 3. Paint fancy decorations, including composition underlines and platform-specific underlines for spelling errors, grammar errors, et cetera.
    584594    if (paintInfo.phase == PaintPhaseForeground) {
    585         paintDocumentMarkers(context, boxOrigin, false);
     595        paintPlatformDocumentMarkers(context, boxOrigin);
    586596
    587597        if (useCustomUnderlines)
     
    627637}
    628638
    629 void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& textColor)
    630 {
    631 #if ENABLE(TEXT_SELECTION)
    632     if (context.paintingDisabled())
    633         return;
    634 
    635     // See if we have a selection to paint at all.
    636     unsigned selectionStart;
    637     unsigned selectionEnd;
    638     std::tie(selectionStart, selectionEnd) = selectionStartEnd();
    639     if (selectionStart >= selectionEnd)
    640         return;
    641 
    642     Color c = renderer().selectionBackgroundColor();
    643     if (!c.isValid() || c.alpha() == 0)
    644         return;
    645 
    646     // If the text color ends up being the same as the selection background, invert the selection
    647     // background.
    648     if (textColor == c)
    649         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
    650 
    651     GraphicsContextStateSaver stateSaver(context);
    652     updateGraphicsContext(context, TextPaintStyle(c)); // Don't draw text at all!
    653 
    654     // Note that if the text is truncated, we let the thing being painted in the truncation
    655     // draw its own highlight.
    656     auto text = this->text();
    657     TextRun textRun = createTextRun(text);
    658 
    659     const RootInlineBox& rootBox = root();
    660     LayoutUnit selectionBottom = rootBox.selectionBottom();
    661     LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
    662 
    663     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
    664     LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
    665 
    666     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
    667     lineFont().adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
    668 
    669     // FIXME: Support painting combined text.
    670     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c);
    671 #else
    672     UNUSED_PARAM(context);
    673     UNUSED_PARAM(boxOrigin);
    674     UNUSED_PARAM(textColor);
    675 #endif
    676 }
    677 
    678 inline void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned startOffset, unsigned endOffset)
    679 {
    680     startOffset = clampedOffset(startOffset);
    681     endOffset = clampedOffset(endOffset);
    682     if (startOffset >= endOffset)
    683         return;
    684 
    685     GraphicsContextStateSaver stateSaver { context };
    686     updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
    687 
    688     // Use same y positioning and height as for selection, so that when the selection and this subrange are on
    689     // the same word there are no pieces sticking out.
    690     LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
    691     LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight());
    692 
    693     auto text = this->text();
    694     TextRun textRun = createTextRun(text);
    695     lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
    696 
    697     // FIXME: Support painting combined text.
    698     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
    699 }
    700 
    701 void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin)
    702 {
    703     paintTextSubrangeBackground(context, boxOrigin, renderer().frame().editor().compositionStart(), renderer().frame().editor().compositionEnd(), Color::compositionFill);
    704 }
    705 
    706 void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange, bool isActiveMatch)
    707 {
    708     if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
    709         return;
    710     auto highlightColor = isActiveMatch ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
    711     paintTextSubrangeBackground(context, boxOrigin, highlightColor, subrange.startOffset, subrange.endOffset);
    712 }
    713 
    714 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
    715 {
    716     if (direction == LTR)
    717         return;
    718     start = logicalWidth - width - start;
    719 }
    720 
    721 void InlineTextBox::paintDecoration(GraphicsContext& context, const TextRun& textRun, const FloatPoint& textOrigin,
    722     const FloatRect& boxRect, TextDecoration decoration, TextPaintStyle textPaintStyle, const ShadowData* shadow, const FloatRect& clipOutRect)
    723 {
    724     if (m_truncation == cFullTruncation)
    725         return;
    726 
    727     updateGraphicsContext(context, textPaintStyle);
    728 
    729     bool isCombinedText = combinedText();
    730     if (isCombinedText)
    731         context.concatCTM(rotation(boxRect, Clockwise));
    732 
    733     float start = 0;
    734     float width = m_logicalWidth;
    735     if (m_truncation != cNoTruncation) {
    736         width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
    737         mirrorRTLSegment(m_logicalWidth, direction(), start, width);
    738     }
    739 
    740     TextDecorationPainter decorationPainter(context, decoration, renderer(), isFirstLine());
    741     decorationPainter.setInlineTextBox(this);
    742     decorationPainter.setFont(lineFont());
    743     decorationPainter.setWidth(width);
    744     decorationPainter.setBaseline(lineStyle().fontMetrics().ascent());
    745     decorationPainter.setIsHorizontal(isHorizontal());
    746     decorationPainter.addTextShadow(shadow);
    747 
    748     FloatPoint localOrigin = boxRect.location();
    749     localOrigin.move(start, 0);
    750 
    751     {
    752         GraphicsContextStateSaver stateSaver { context, false };
    753         if (!clipOutRect.isEmpty()) {
    754             stateSaver.save();
    755             context.clipOut(clipOutRect);
    756         }
    757         decorationPainter.paintTextDecoration(textRun, textOrigin, localOrigin);
    758     }
    759 
    760     if (isCombinedText)
    761         context.concatCTM(rotation(boxRect, Counterclockwise));
    762 }
    763 
    764 void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange)
     639void InlineTextBox::paintPlatformDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin)
     640{
     641    for (auto& subrange : subdivide(collectSubrangesForDocumentMarkers(TextPaintPhase::Foreground), OverlapStrategy::Frontmost))
     642        paintPlatformDocumentMarker(context, boxOrigin, subrange);
     643}
     644
     645void InlineTextBox::paintPlatformDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange)
    765646{
    766647    // Never print spelling/grammar markers (5327887)
     
    774655    float width = m_logicalWidth; // how much line to draw
    775656
    776     // Determine whether we need to measure text
    777     bool markerSpansWholeBox = true;
    778     if (m_start <= subrange.startOffset)
    779         markerSpansWholeBox = false;
    780     if ((end() + 1) != subrange.endOffset) // End points at the last char, not past it
    781         markerSpansWholeBox = false;
    782     if (m_truncation != cNoTruncation)
    783         markerSpansWholeBox = false;
    784 
    785     if (!markerSpansWholeBox) {
    786         unsigned startPosition = clampedOffset(subrange.startOffset);
    787         unsigned endPosition = clampedOffset(subrange.endOffset);
    788 
     657    // Avoid measuring the text when the entire line box is selected as an optimization.
     658    if (subrange.startOffset || subrange.endOffset != clampedOffset(end() + 1)) {
    789659        // Calculate start & width
    790660        int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
     
    795665
    796666        LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
    797         lineFont().adjustSelectionRectForText(run, selectionRect, startPosition, endPosition);
     667        lineFont().adjustSelectionRectForText(run, selectionRect, subrange.startOffset, subrange.endOffset);
    798668        IntRect markerRect = enclosingIntRect(selectionRect);
    799669        start = markerRect.x() - startPoint.x();
    800670        width = markerRect.width();
    801671    }
    802    
     672
    803673    auto lineStyleForSubrangeType = [] (MarkerSubrange::Type type) {
    804674        switch (type) {
     
    839709        underlineOffset = baseline + 2;
    840710    }
    841     // FIXME: Support painting combined text.
    842711    context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForSubrangeType(subrange.type));
    843712}
    844713
    845 void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, bool background)
    846 {
     714auto InlineTextBox::computeStyleForUnmarkedMarkerSubrange(const PaintInfo& paintInfo) const -> MarkerSubrangeStyle
     715{
     716    auto& lineStyle = this->lineStyle();
     717
     718    MarkerSubrangeStyle style;
     719    style.textDecorationStyles = TextDecorationPainter::stylesForRenderer(renderer(), lineStyle.textDecorationsInEffect(), isFirstLine());
     720    style.textStyles = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
     721    style.textShadow = paintInfo.forceTextColor() ? nullptr : lineStyle.textShadow();
     722    style.alpha = 1;
     723    return style;
     724}
     725
     726auto InlineTextBox::resolveStyleForSubrange(const MarkerSubrange& subrange, const MarkerSubrangeStyle& baseStyle, const PaintInfo& paintInfo) -> StyledMarkerSubrange
     727{
     728    MarkerSubrangeStyle style = baseStyle;
     729    switch (subrange.type) {
     730    case MarkerSubrange::Correction:
     731    case MarkerSubrange::DictationAlternatives:
     732#if PLATFORM(IOS)
     733    // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
     734    case MarkerSubrange::DictationPhraseWithAlternatives:
     735#endif
     736    case MarkerSubrange::GrammarError:
     737    case MarkerSubrange::SpellingError:
     738    case MarkerSubrange::Unmarked:
     739        break;
     740    case MarkerSubrange::DraggedContent:
     741        style.alpha = 0.25;
     742        break;
     743    case MarkerSubrange::Selection: {
     744        const ShadowData* selectionShadow = nullptr;
     745        style.textStyles = computeTextSelectionPaintStyle(style.textStyles, renderer(), lineStyle(), paintInfo, selectionShadow);
     746        style.textShadow = selectionShadow;
     747
     748        Color selectionBackgroundColor = renderer().selectionBackgroundColor();
     749        style.backgroundColor = selectionBackgroundColor;
     750        if (selectionBackgroundColor.isValid() && selectionBackgroundColor.alpha() && style.textStyles.fillColor == selectionBackgroundColor)
     751            style.backgroundColor = { 0xff - selectionBackgroundColor.red(), 0xff - selectionBackgroundColor.green(), 0xff - selectionBackgroundColor.blue() };
     752        break;
     753    }
     754    case MarkerSubrange::TextMatch:
     755        style.backgroundColor = subrange.marker->isActiveMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
     756        break;
     757    }
     758    StyledMarkerSubrange styledSubrange = subrange;
     759    styledSubrange.style = WTFMove(style);
     760    return styledSubrange;
     761}
     762
     763auto InlineTextBox::subdivideAndResolveStyle(const Vector<MarkerSubrange>& subrangesToSubdivide, const MarkerSubrangeStyle& baseStyle, const PaintInfo& paintInfo) -> Vector<StyledMarkerSubrange>
     764{
     765    if (subrangesToSubdivide.isEmpty())
     766        return { };
     767
     768    auto areAdjacentSubrangesWithSameStyle = [] (const StyledMarkerSubrange& a, const StyledMarkerSubrange& b) {
     769        return a.endOffset == b.startOffset && a.style == b.style;
     770    };
     771
     772    auto subranges = subdivide(subrangesToSubdivide, OverlapStrategy::FrontmostWithLongestEffectiveRange);
     773
     774    // Coallesce styles of adjacent subranges to minimize the number of drawing commands.
     775    Vector<StyledMarkerSubrange> styledSubranges;
     776    styledSubranges.reserveInitialCapacity(subranges.size());
     777    styledSubranges.uncheckedAppend(resolveStyleForSubrange(subranges[0], baseStyle, paintInfo));
     778    for (auto it = subranges.begin() + 1, end = subranges.end(); it != end; ++it) {
     779        StyledMarkerSubrange& previousStyledSubrange = styledSubranges.last();
     780        auto currentStyledSubrange = resolveStyleForSubrange(*it, baseStyle, paintInfo);
     781        if (areAdjacentSubrangesWithSameStyle(previousStyledSubrange, currentStyledSubrange)) {
     782            previousStyledSubrange.endOffset = currentStyledSubrange.endOffset;
     783            continue;
     784        }
     785        styledSubranges.uncheckedAppend(currentStyledSubrange);
     786    }
     787    return styledSubranges;
     788}
     789
     790Vector<MarkerSubrange> InlineTextBox::collectSubrangesForDraggedContent()
     791{
     792    using DraggendContentRange = std::pair<unsigned, unsigned>;
     793    auto draggedContentRanges = renderer().draggedContentRangesBetweenOffsets(m_start, m_start + m_len);
     794    Vector<MarkerSubrange> result = draggedContentRanges.map([this] (const DraggendContentRange& range) -> MarkerSubrange {
     795        return { clampedOffset(range.first), clampedOffset(range.second), MarkerSubrange::DraggedContent };
     796    });
     797    return result;
     798}
     799
     800Vector<MarkerSubrange> InlineTextBox::collectSubrangesForDocumentMarkers(TextPaintPhase phase)
     801{
     802    ASSERT(phase == TextPaintPhase::Background || phase == TextPaintPhase::Foreground);
    847803    if (!renderer().textNode())
    848         return;
     804        return { };
    849805
    850806    Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
     
    877833    // Note end() points at the last char, not one past it like endOffset and ranges do.
    878834    for (auto* marker : markers) {
    879         // Paint either the background markers or the foreground markers, but not both
     835        // Collect either the background markers or the foreground markers, but not both
    880836        switch (marker->type()) {
    881             case DocumentMarker::Grammar:
    882             case DocumentMarker::Spelling:
    883             case DocumentMarker::CorrectionIndicator:
    884             case DocumentMarker::Replacement:
    885             case DocumentMarker::DictationAlternatives:
     837        case DocumentMarker::Grammar:
     838        case DocumentMarker::Spelling:
     839        case DocumentMarker::CorrectionIndicator:
     840        case DocumentMarker::Replacement:
     841        case DocumentMarker::DictationAlternatives:
    886842#if PLATFORM(IOS)
    887             // FIXME: Remove the PLATFORM(IOS)-guard.
    888             case DocumentMarker::DictationPhraseWithAlternatives:
     843        // FIXME: Remove the PLATFORM(IOS)-guard.
     844        case DocumentMarker::DictationPhraseWithAlternatives:
    889845#endif
    890                 if (background)
    891                     continue;
    892                 break;
    893             case DocumentMarker::TextMatch:
     846            if (phase == TextPaintPhase::Background)
     847                continue;
     848            break;
     849        case DocumentMarker::TextMatch:
     850            if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
     851                continue;
     852            FALLTHROUGH;
    894853#if ENABLE(TELEPHONE_NUMBER_DETECTION)
    895             case DocumentMarker::TelephoneNumber:
     854        case DocumentMarker::TelephoneNumber:
    896855#endif
    897                 if (!background)
    898                     continue;
    899                 break;
    900             default:
     856            if (phase == TextPaintPhase::Foreground)
    901857                continue;
    902         }
    903 
    904         if (marker->endOffset() <= start())
    905             // marker is completely before this run.  This might be a marker that sits before the
     858            break;
     859        default:
     860            continue;
     861        }
     862
     863        if (marker->endOffset() <= start()) {
     864            // Marker is completely before this run. This might be a marker that sits before the
    906865            // first run we draw, or markers that were within runs we skipped due to truncation.
    907866            continue;
    908        
    909         if (marker->startOffset() > end())
    910             // marker is completely after this run, bail.  A later run will paint it.
     867        }
     868
     869        if (marker->startOffset() > end()) {
     870            // Marker is completely after this run, bail. A later run will paint it.
    911871            break;
    912        
    913         // marker intersects this run.  Paint it.
     872        }
     873
     874        // Marker intersects this run. Collect it.
    914875        switch (marker->type()) {
    915             case DocumentMarker::Spelling:
    916             case DocumentMarker::CorrectionIndicator:
    917             case DocumentMarker::DictationAlternatives:
    918             case DocumentMarker::Grammar:
     876        case DocumentMarker::Spelling:
     877        case DocumentMarker::CorrectionIndicator:
     878        case DocumentMarker::DictationAlternatives:
     879        case DocumentMarker::Grammar:
    919880#if PLATFORM(IOS)
    920             // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
    921             case DocumentMarker::DictationPhraseWithAlternatives:
     881        // FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
     882        case DocumentMarker::DictationPhraseWithAlternatives:
    922883#endif
    923             case DocumentMarker::TextMatch:
    924                 subranges.uncheckedAppend({ marker->startOffset(), marker->endOffset(), markerTypeForSubrangeType(marker->type()), marker });
    925                 break;
    926             case DocumentMarker::Replacement:
    927                 break;
     884        case DocumentMarker::TextMatch:
     885            subranges.uncheckedAppend({ clampedOffset(marker->startOffset()), clampedOffset(marker->endOffset()), markerTypeForSubrangeType(marker->type()), marker });
     886            break;
     887        case DocumentMarker::Replacement:
     888            break;
    928889#if ENABLE(TELEPHONE_NUMBER_DETECTION)
    929             case DocumentMarker::TelephoneNumber:
    930                 break;
     890        case DocumentMarker::TelephoneNumber:
     891            break;
    931892#endif
    932             default:
    933                 ASSERT_NOT_REACHED();
    934         }
    935     }
    936 
    937     for (auto& subrange : subdivide(subranges, OverlapStrategy::Frontmost)) {
    938         if (subrange.type == MarkerSubrange::TextMatch)
    939             paintTextMatchMarker(context, boxOrigin, subrange, subrange.marker->isActiveMatch());
    940         else
    941             paintDocumentMarker(context, boxOrigin, subrange);
    942     }
     893        default:
     894            ASSERT_NOT_REACHED();
     895        }
     896    }
     897    return subranges;
     898}
     899
     900FloatPoint InlineTextBox::textOriginFromBoxRect(const FloatRect& boxRect) const
     901{
     902    FloatPoint textOrigin { boxRect.x(), boxRect.y() + lineFont().fontMetrics().ascent() };
     903    if (auto* combinedText = this->combinedText()) {
     904        if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
     905            textOrigin = newOrigin.value();
     906    }
     907    if (isHorizontal())
     908        textOrigin.setY(roundToDevicePixel(LayoutUnit { textOrigin.y() }, renderer().document().deviceScaleFactor()));
     909    else
     910        textOrigin.setX(roundToDevicePixel(LayoutUnit { textOrigin.x() }, renderer().document().deviceScaleFactor()));
     911    return textOrigin;
     912}
     913
     914void InlineTextBox::paintMarkerSubranges(GraphicsContext& context, TextPaintPhase phase, const FloatRect& boxRect, const Vector<StyledMarkerSubrange>& subranges, const FloatRect& decorationClipOutRect)
     915{
     916    switch (phase) {
     917    case TextPaintPhase::Background:
     918        for (auto& subrange : subranges)
     919            paintTextSubrangeBackground(context, boxRect.location(), subrange.style.backgroundColor, subrange.startOffset, subrange.endOffset);
     920        return;
     921    case TextPaintPhase::Foreground:
     922        for (auto& subrange : subranges)
     923            paintTextSubrangeForeground(context, boxRect, subrange);
     924        return;
     925    case TextPaintPhase::Decoration:
     926        for (auto& subrange : subranges)
     927            paintTextSubrangeDecoration(context, boxRect, decorationClipOutRect, subrange);
     928        return;
     929    }
     930}
     931
     932void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned clampedStartOffset, unsigned clampedEndOffset)
     933{
     934    if (clampedStartOffset >= clampedEndOffset)
     935        return;
     936
     937    GraphicsContextStateSaver stateSaver { context };
     938    updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
     939
     940    // Note that if the text is truncated, we let the thing being painted in the truncation
     941    // draw its own highlight.
     942    auto text = this->text();
     943    TextRun textRun = createTextRun(text);
     944
     945    const RootInlineBox& rootBox = root();
     946    LayoutUnit selectionBottom = rootBox.selectionBottom();
     947    LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
     948
     949    // Use same y positioning and height as for selection, so that when the selection and this subrange are on
     950    // the same word there are no pieces sticking out.
     951    LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
     952    LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
     953
     954    LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
     955    lineFont().adjustSelectionRectForText(textRun, selectionRect, clampedStartOffset, clampedEndOffset);
     956
     957    // FIXME: Support painting combined text. See <https://bugs.webkit.org/show_bug.cgi?id=180993>.
     958    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
     959}
     960
     961void InlineTextBox::paintTextSubrangeForeground(GraphicsContext& context, const FloatRect& boxRect, const StyledMarkerSubrange& subrange)
     962{
     963    if (subrange.startOffset >= subrange.endOffset)
     964        return;
     965
     966    const FontCascade& font = lineFont();
     967    const RenderStyle& lineStyle = this->lineStyle();
     968
     969    float emphasisMarkOffset = 0;
     970    bool emphasisMarkAbove;
     971    bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
     972    const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom();
     973    if (!emphasisMark.isEmpty())
     974        emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
     975
     976    TextPainter textPainter { context };
     977    textPainter.setFont(font);
     978    textPainter.setStyle(subrange.style.textStyles);
     979    textPainter.setIsHorizontal(isHorizontal());
     980    textPainter.setShadow(subrange.style.textShadow);
     981    textPainter.setEmphasisMark(emphasisMark, emphasisMarkOffset, combinedText());
     982
     983    GraphicsContextStateSaver stateSaver { context, false };
     984    if (subrange.type == MarkerSubrange::DraggedContent) {
     985        stateSaver.save();
     986        context.setAlpha(subrange.style.alpha);
     987    }
     988    // TextPainter wants the box rectangle and text origin of the entire line box.
     989    auto text = this->text();
     990    textPainter.paintRange(createTextRun(text), boxRect, textOriginFromBoxRect(boxRect), subrange.startOffset, subrange.endOffset);
     991}
     992
     993void InlineTextBox::paintTextSubrangeDecoration(GraphicsContext& context, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkerSubrange& subrange)
     994{
     995    if (m_truncation == cFullTruncation)
     996        return;
     997
     998    updateGraphicsContext(context, subrange.style.textStyles);
     999
     1000    bool isCombinedText = combinedText();
     1001    if (isCombinedText)
     1002        context.concatCTM(rotation(boxRect, Clockwise));
     1003
     1004    // 1. Compute text selection
     1005    unsigned startOffset = subrange.startOffset;
     1006    unsigned endOffset = subrange.endOffset;
     1007    if (startOffset >= endOffset)
     1008        return;
     1009
     1010    // Note that if the text is truncated, we let the thing being painted in the truncation
     1011    // draw its own decoration.
     1012    auto text = this->text();
     1013    TextRun textRun = createTextRun(text);
     1014
     1015    // Avoid measuring the text when the entire line box is selected as an optimization.
     1016    FloatRect snappedSelectionRect = boxRect;
     1017    if (startOffset || endOffset != textRun.length()) {
     1018        LayoutRect selectionRect = { boxRect.x(), boxRect.y(), boxRect.width(), boxRect.height() };
     1019        lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
     1020        snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr());
     1021    }
     1022
     1023    // 2. Paint
     1024    TextDecorationPainter decorationPainter { context, static_cast<unsigned>(lineStyle().textDecorationsInEffect()), renderer(), isFirstLine(), subrange.style.textDecorationStyles };
     1025    decorationPainter.setInlineTextBox(this);
     1026    decorationPainter.setFont(lineFont());
     1027    decorationPainter.setWidth(snappedSelectionRect.width());
     1028    decorationPainter.setBaseline(lineStyle().fontMetrics().ascent());
     1029    decorationPainter.setIsHorizontal(isHorizontal());
     1030    decorationPainter.addTextShadow(subrange.style.textShadow);
     1031
     1032    {
     1033        GraphicsContextStateSaver stateSaver { context, false };
     1034        if (!clipOutRect.isEmpty()) {
     1035            stateSaver.save();
     1036            context.clipOut(clipOutRect);
     1037        }
     1038        decorationPainter.paintTextDecoration(textRun.subRun(startOffset, endOffset - startOffset), textOriginFromBoxRect(snappedSelectionRect), snappedSelectionRect.location());
     1039    }
     1040
     1041    if (isCombinedText)
     1042        context.concatCTM(rotation(boxRect, Counterclockwise));
     1043}
     1044
     1045void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin)
     1046{
     1047    paintTextSubrangeBackground(context, boxOrigin, clampedOffset(renderer().frame().editor().compositionStart()), clampedOffset(renderer().frame().editor().compositionEnd()), Color::compositionFill);
    9431048}
    9441049
     
    9651070            break; // Underline also runs into the next run. Bail now, no more marker advancement.
    9661071    }
     1072}
     1073
     1074static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
     1075{
     1076    if (direction == LTR)
     1077        return;
     1078    start = logicalWidth - width - start;
    9671079}
    9681080
  • trunk/Source/WebCore/rendering/InlineTextBox.h

    r225712 r226138  
    152152
    153153private:
    154     void paintDecoration(GraphicsContext&, const TextRun&, const FloatPoint& textOrigin, const FloatRect& boxRect,
    155         TextDecoration, TextPaintStyle, const ShadowData*, const FloatRect& clipOutRect);
    156     void paintSelection(GraphicsContext&, const FloatPoint& boxOrigin, const Color&);
    157 
    158     void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&);
    159     void paintDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin, bool background);
    160     void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&, bool isActiveMatch);
     154    struct MarkerSubrangeStyle;
     155    struct StyledMarkerSubrange;
     156
     157    enum class TextPaintPhase { Background, Foreground, Decoration };
     158
     159    Vector<MarkerSubrange> collectSubrangesForDraggedContent();
     160    Vector<MarkerSubrange> collectSubrangesForDocumentMarkers(TextPaintPhase);
     161
     162    MarkerSubrangeStyle computeStyleForUnmarkedMarkerSubrange(const PaintInfo&) const;
     163    StyledMarkerSubrange resolveStyleForSubrange(const MarkerSubrange&, const MarkerSubrangeStyle& baseStyle, const PaintInfo&);
     164    Vector<StyledMarkerSubrange> subdivideAndResolveStyle(const Vector<MarkerSubrange>&, const MarkerSubrangeStyle& baseStyle, const PaintInfo&);
     165
     166    FloatPoint textOriginFromBoxRect(const FloatRect&) const;
     167
     168    void paintMarkerSubranges(GraphicsContext&, TextPaintPhase, const FloatRect& boxRect, const Vector<StyledMarkerSubrange>&, const FloatRect& decorationClipOutRect = { });
     169
     170    void paintPlatformDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&);
     171    void paintPlatformDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin);
    161172
    162173    void paintCompositionBackground(GraphicsContext&, const FloatPoint& boxOrigin);
     
    164175    void paintCompositionUnderline(GraphicsContext&, const FloatPoint& boxOrigin, const CompositionUnderline&) const;
    165176
    166     void paintTextSubrangeBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned startOffset, unsigned endOffset);
     177    void paintTextSubrangeBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned clampedStartOffset, unsigned clampedEndOffset);
     178    void paintTextSubrangeForeground(GraphicsContext&, const FloatRect& boxRect, const StyledMarkerSubrange&);
     179    void paintTextSubrangeDecoration(GraphicsContext&, const FloatRect& boxRect, const FloatRect& clipOutRect, const StyledMarkerSubrange&);
    167180
    168181    const RenderCombineText* combinedText() const;
  • trunk/Source/WebCore/rendering/MarkerSubrange.h

    r226137 r226138  
    4646#endif
    4747        Selection,
     48        DraggedContent,
    4849    };
    4950#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
     
    6162    Type type;
    6263    const RenderedDocumentMarker* marker { nullptr };
     64
     65    bool isEmpty() const { return endOffset <= startOffset; }
     66    bool operator!=(const MarkerSubrange& other) const { return !(*this == other); }
     67    bool operator==(const MarkerSubrange& other) const
     68    {
     69        return startOffset == other.startOffset && endOffset == other.endOffset && type == other.type && marker == other.marker;
     70    }
    6371};
    6472
  • trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp

    r225117 r226138  
    118118            textWithHyphen = run.textWithHyphen();
    119119        // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
    120         TextRun textRun(run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior());
     120        TextRun textRun { run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior() };
    121121        textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
    122         FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));
    123         textPainter.paint(textRun, textRun.length(), rect, textOrigin);
     122        FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor) };
     123        textPainter.paint(textRun, rect, textOrigin);
    124124        if (textDecorationPainter) {
    125125            textDecorationPainter->setWidth(rect.width());
  • trunk/Source/WebCore/rendering/TextDecorationPainter.cpp

    r224780 r226138  
    242242}
    243243
    244 TextDecorationPainter::TextDecorationPainter(GraphicsContext& context, unsigned decorations, const RenderText& renderer, bool isFirstLine, PseudoId pseudoId)
     244bool TextDecorationPainter::Styles::operator==(const Styles& other) const
     245{
     246    return underlineColor == other.underlineColor && overlineColor == other.overlineColor && linethroughColor == other.linethroughColor
     247        && underlineStyle == other.underlineStyle && overlineStyle == other.overlineStyle && linethroughStyle == other.linethroughStyle;
     248}
     249
     250TextDecorationPainter::TextDecorationPainter(GraphicsContext& context, unsigned decorations, const RenderText& renderer, bool isFirstLine, std::optional<Styles> styles)
    245251    : m_context { context }
    246252    , m_decorations { OptionSet<TextDecoration>::fromRaw(decorations) }
    247253    , m_wavyOffset { wavyOffsetFromDecoration() }
    248254    , m_isPrinting { renderer.document().printing() }
    249     , m_styles { stylesForRenderer(renderer, decorations, isFirstLine, pseudoId) }
     255    , m_styles { styles ? *WTFMove(styles) : stylesForRenderer(renderer, decorations, isFirstLine, NOPSEUDO) }
    250256    , m_lineStyle { isFirstLine ? renderer.firstLineStyle() : renderer.style() }
    251257{
  • trunk/Source/WebCore/rendering/TextDecorationPainter.h

    r222889 r226138  
    4343public:
    4444    // FIXME: Make decorations an OptionSet<TextDecoration>. See <https://bugs.webkit.org/show_bug.cgi?id=176844>.
    45     TextDecorationPainter(GraphicsContext&, unsigned decorations, const RenderText&, bool isFirstLine, PseudoId = NOPSEUDO);
     45    struct Styles;
     46    TextDecorationPainter(GraphicsContext&, unsigned decorations, const RenderText&, bool isFirstLine, std::optional<Styles> = std::nullopt);
    4647   
    4748    void setInlineTextBox(const InlineTextBox* inlineTextBox) { m_inlineTextBox = inlineTextBox; }
     
    5556
    5657    struct Styles {
     58        bool operator==(const Styles&) const;
     59        bool operator!=(const Styles& other) const { return !(*this == other); }
     60
    5761        Color underlineColor;
    5862        Color overlineColor;
     
    6468    // FIXME: Make requestedDecorations an OptionSet<TextDecoration>. See <https://bugs.webkit.org/show_bug.cgi?id=176844>.
    6569    static Styles stylesForRenderer(const RenderObject&, unsigned requestedDecorations, bool firstLineStyle = false, PseudoId = NOPSEUDO);
    66        
     70
    6771private:
    6872    GraphicsContext& m_context;
  • trunk/Source/WebCore/rendering/TextPaintStyle.cpp

    r224780 r226138  
    4646}
    4747
     48bool TextPaintStyle::operator==(const TextPaintStyle& other) const
     49{
     50    return fillColor == other.fillColor && strokeColor == other.strokeColor && emphasisMarkColor == other.emphasisMarkColor
     51        && strokeWidth == other.strokeWidth && paintOrder == other.paintOrder && lineJoin == other.lineJoin
     52#if ENABLE(LETTERPRESS)
     53        && useLetterpressEffect == other.useLetterpressEffect
     54#endif
     55        && lineCap == other.lineCap && miterLimit == other.miterLimit;
     56}
     57
    4858bool textColorIsLegibleAgainstBackgroundColor(const Color& textColor, const Color& backgroundColor)
    4959{
     
    123133}
    124134
    125 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle& textPaintStyle, const RenderText& renderer, const RenderStyle& lineStyle, const PaintInfo& paintInfo, bool& paintSelectedTextOnly, bool& paintSelectedTextSeparately, bool& paintNonSelectedTextOnly, const ShadowData*& selectionShadow)
    126 {
    127     paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection);
    128     paintSelectedTextSeparately = paintInfo.paintBehavior & PaintBehaviorExcludeSelection;
    129     paintNonSelectedTextOnly = paintInfo.paintBehavior & PaintBehaviorExcludeSelection;
     135TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle& textPaintStyle, const RenderText& renderer, const RenderStyle& lineStyle, const PaintInfo& paintInfo, const ShadowData*& selectionShadow)
     136{
    130137    selectionShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow();
    131138
     
    134141#if ENABLE(TEXT_SELECTION)
    135142    Color foreground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionForegroundColor();
    136     if (foreground.isValid() && foreground != selectionPaintStyle.fillColor) {
    137         if (!paintSelectedTextOnly)
    138             paintSelectedTextSeparately = true;
     143    if (foreground.isValid() && foreground != selectionPaintStyle.fillColor)
    139144        selectionPaintStyle.fillColor = foreground;
    140     }
    141145
    142146    Color emphasisMarkForeground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : renderer.selectionEmphasisMarkColor();
    143     if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionPaintStyle.emphasisMarkColor) {
    144         if (!paintSelectedTextOnly)
    145             paintSelectedTextSeparately = true;
     147    if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionPaintStyle.emphasisMarkColor)
    146148        selectionPaintStyle.emphasisMarkColor = emphasisMarkForeground;
    147     }
    148149
    149150    if (auto* pseudoStyle = renderer.getCachedPseudoStyle(SELECTION)) {
    150151        const ShadowData* shadow = paintInfo.forceTextColor() ? nullptr : pseudoStyle->textShadow();
    151         if (shadow != selectionShadow) {
    152             if (!paintSelectedTextOnly)
    153                 paintSelectedTextSeparately = true;
     152        if (shadow != selectionShadow)
    154153            selectionShadow = shadow;
    155         }
    156154
    157155        auto viewportSize = renderer.frame().view() ? renderer.frame().view()->size() : IntSize();
    158156        float strokeWidth = pseudoStyle->computedStrokeWidth(viewportSize);
    159         if (strokeWidth != selectionPaintStyle.strokeWidth) {
    160             if (!paintSelectedTextOnly)
    161                 paintSelectedTextSeparately = true;
     157        if (strokeWidth != selectionPaintStyle.strokeWidth)
    162158            selectionPaintStyle.strokeWidth = strokeWidth;
    163         }
    164159
    165160        Color stroke = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : pseudoStyle->computedStrokeColor();
    166         if (stroke != selectionPaintStyle.strokeColor) {
    167             if (!paintSelectedTextOnly)
    168                 paintSelectedTextSeparately = true;
     161        if (stroke != selectionPaintStyle.strokeColor)
    169162            selectionPaintStyle.strokeColor = stroke;
    170         }
    171163    }
    172164#else
  • trunk/Source/WebCore/rendering/TextPaintStyle.h

    r219033 r226138  
    4343    TextPaintStyle(const Color&);
    4444
     45    bool operator==(const TextPaintStyle&) const;
     46    bool operator!=(const TextPaintStyle& other) const { return !(*this == other); }
     47
    4548    Color fillColor;
    4649    Color strokeColor;
     
    5861bool textColorIsLegibleAgainstBackgroundColor(const Color& textColor, const Color& backgroundColor);
    5962TextPaintStyle computeTextPaintStyle(const Frame&, const RenderStyle&, const PaintInfo&);
    60 TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle&, const RenderText&, const RenderStyle&, const PaintInfo&, bool& paintSelectedTextOnly, bool& paintSelectedTextSeparately, bool& paintNonSelectedTextOnly, const ShadowData*& selectionShadow);
     63TextPaintStyle computeTextSelectionPaintStyle(const TextPaintStyle&, const RenderText&, const RenderStyle&, const PaintInfo&, const ShadowData*& selectionShadow);
    6164
    6265enum FillColorType { UseNormalFillColor, UseEmphasisMarkColor };
  • trunk/Source/WebCore/rendering/TextPainter.cpp

    r223552 r226138  
    181181}
    182182
     183void TextPainter::paint(const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin)
     184{
     185    paintRange(textRun, boxRect, textOrigin, 0, textRun.length());
     186}
     187
    183188void TextPainter::paintRange(const TextRun& textRun, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned start, unsigned end)
    184189{
     
    190195    paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, start, end, m_style, m_shadow);
    191196}
    192    
    193 void TextPainter::paint(const TextRun& textRun, unsigned length, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned selectionStart, unsigned selectionEnd,
    194     bool paintSelectedTextOnly, bool paintSelectedTextSeparately, bool paintNonSelectedTextOnly)
    195 {
    196     ASSERT(m_font);
    197     ASSERT(length <= textRun.length());
    198     if (!paintSelectedTextOnly) {
    199         // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side
    200         // effect, so only when we know we're stroking, do a save/restore.
    201         GraphicsContextStateSaver stateSaver(m_context, m_style.strokeWidth > 0);
    202         updateGraphicsContext(m_context, m_style);
    203         bool fullPaint = !paintSelectedTextSeparately || selectionEnd <= selectionStart;
    204         if (fullPaint)
    205             paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, length, m_style, m_shadow);
    206         else {
    207             // Paint the before and after selection parts.
    208             if (selectionStart > 0)
    209                 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, 0, selectionStart, m_style, m_shadow);
    210             if (selectionEnd < length)
    211                 paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionEnd, length, m_style, m_shadow);
    212         }
    213     }
    214 
    215     if (paintNonSelectedTextOnly)
    216         return;
    217 
    218     // Paint only the text that is selected.
    219     if ((paintSelectedTextOnly || paintSelectedTextSeparately) && selectionStart < selectionEnd) {
    220         GraphicsContextStateSaver stateSaver(m_context, m_selectionStyle.strokeWidth > 0);
    221         updateGraphicsContext(m_context, m_selectionStyle);
    222         paintTextAndEmphasisMarksIfNeeded(textRun, boxRect, textOrigin, selectionStart, selectionEnd, m_selectionStyle, m_selectionShadow);
    223     }
    224 }
    225197
    226198} // namespace WebCore
  • trunk/Source/WebCore/rendering/TextPainter.h

    r223552 r226138  
    4949public:
    5050    TextPainter(GraphicsContext&);
    51    
     51
    5252    void setStyle(const TextPaintStyle& textPaintStyle) { m_style = textPaintStyle; }
    53     void setSelectionStyle(const TextPaintStyle& selectionPaintStyle) { m_selectionStyle = selectionPaintStyle; }
    54 
    5553    void setShadow(const ShadowData* shadow) { m_shadow = shadow; }
    56     void setSelectionShadow(const ShadowData* selectionShadow) { m_selectionShadow = selectionShadow; }
    57 
    5854    void setFont(const FontCascade& font) { m_font = &font; }
    59 
    6055    void setIsHorizontal(bool isHorizontal) { m_textBoxIsHorizontal = isHorizontal; }
    61 
    6256    void setEmphasisMark(const AtomicString& mark, float offset, const RenderCombineText*);
    6357
     58    void paint(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin);
    6459    void paintRange(const TextRun&, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned start, unsigned end);
    65     void paint(const TextRun&, unsigned length, const FloatRect& boxRect, const FloatPoint& textOrigin, unsigned selectionStart = 0, unsigned selectionEnd = 0, bool paintSelectedTextOnly = false, bool paintSelectedTextSeparately = false, bool paintNonSelectedTextOnly = false);
    6660
    6761private:
     
    7670    const FontCascade* m_font { nullptr };
    7771    TextPaintStyle m_style;
    78     TextPaintStyle m_selectionStyle;
    7972    const ShadowData* m_shadow { nullptr };
    80     const ShadowData* m_selectionShadow { nullptr };
    8173    AtomicString m_emphasisMark;
    8274    const RenderCombineText* m_combinedText { nullptr };
  • trunk/Tools/ChangeLog

    r226137 r226138  
     12017-12-19  Daniel Bates  <dabates@apple.com>
     2
     3        Implement InlineTextBox painting using marker subranges
     4        https://bugs.webkit.org/show_bug.cgi?id=180984
     5        <rdar://problem/36139364>
     6
     7        Reviewed by David Hyatt.
     8
     9        Update unit tests now that we use subranges for the painting of dragged content.
     10
     11        * TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp:
     12        (WebCore::operator<<):
     13        (WebCore::operator==): Deleted; moved to class MarkerSubrange.
     14
    1152017-12-19  Daniel Bates  <dabates@apple.com>
    216
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/MarkerSubrange.cpp

    r226137 r226138  
    4646        return os << "DictationPhraseWithAlternatives";
    4747#endif
     48    case MarkerSubrange::DraggedContent:
     49        return os << "DraggedContent";
    4850    case MarkerSubrange::GrammarError:
    4951        return os << "GrammarError";
     
    6769}
    6870
    69 bool operator==(const MarkerSubrange& a, const MarkerSubrange& b)
    70 {
    71     return a.startOffset == b.startOffset && a.endOffset == b.endOffset && a.type == b.type && a.marker == b.marker;
    72 }
    73 
    7471}
    7572
Note: See TracChangeset for help on using the changeset viewer.