Title: [223699] trunk/Source/WebCore
Revision
223699
Author
[email protected]
Date
2017-10-19 11:55:16 -0700 (Thu, 19 Oct 2017)

Log Message

Share logic in InlineTextBox to compute selection rect
https://bugs.webkit.org/show_bug.cgi?id=178232
<rdar://problem/34963452>

Reviewed by Zalan Bujtas.

Currently each paint routine in InlineTextBox duplicates similar code to compute the selection
rect it will paint. This change consolidates all the duplication into localSelectionRectWithClampedPositions()
and writes all of the paint operations, except for paintCompositionUnderline(), in terms of it.
We will write paintCompositionUnderline() in terms of localSelectionRectWithClampedPositions()
in a subsequent patch.

We also write localSelectionRect() in terms of localSelectionRectWithClampedPositions(). Ideally
we would have one way to compute the selection rect. However, localSelectionRect() and paintDocumentMarker()
currently expect the enclosing integral rectangle of the selection rectangle. The function
paintDocumentMarker() needs the enclosing integral rectangle to avoid truncating the dot pattern
drawn under marked words (e.g. a spelling error) on Cocoa platforms. With regards to localSelectionRect()
we should look to have it return the actual selection rectangle. See <https://bugs.webkit.org/show_bug.cgi?id=138913>
for more details.

* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::localSelectionRect const): Move logic in common with paintSelection() into
localSelectionRectWithClampedPositions() and modified code to use it.
(WebCore::InlineTextBox::localSelectionRectWithClampedPositions const): Added.
(WebCore::InlineTextBox::paint): Store the local paint offset as a LayoutPoint as it is the canonical
data type for representing an offset when painting. Pass the local paint offset instead of the analagous boxOrigin value.
(WebCore::InlineTextBox::paintSelection): Write in terms of localSelectionRectWithClampedPositions().
(WebCore::InlineTextBox::paintTextSubrangeBackground): Ditto.
(WebCore::InlineTextBox::paintCompositionBackground): Ditto.
(WebCore::InlineTextBox::paintTextMatchMarker): Ditto.
(WebCore::InlineTextBox::paintDocumentMarker): Ditto.
(WebCore::InlineTextBox::paintDocumentMarkers): Pass paint offset instead of the analogous boxOrigin value.
* rendering/InlineTextBox.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (223698 => 223699)


--- trunk/Source/WebCore/ChangeLog	2017-10-19 18:54:47 UTC (rev 223698)
+++ trunk/Source/WebCore/ChangeLog	2017-10-19 18:55:16 UTC (rev 223699)
@@ -1,5 +1,41 @@
 2017-10-19  Daniel Bates  <[email protected]>
 
+        Share logic in InlineTextBox to compute selection rect
+        https://bugs.webkit.org/show_bug.cgi?id=178232
+        <rdar://problem/34963452>
+
+        Reviewed by Zalan Bujtas.
+
+        Currently each paint routine in InlineTextBox duplicates similar code to compute the selection
+        rect it will paint. This change consolidates all the duplication into localSelectionRectWithClampedPositions()
+        and writes all of the paint operations, except for paintCompositionUnderline(), in terms of it.
+        We will write paintCompositionUnderline() in terms of localSelectionRectWithClampedPositions()
+        in a subsequent patch.
+
+        We also write localSelectionRect() in terms of localSelectionRectWithClampedPositions(). Ideally
+        we would have one way to compute the selection rect. However, localSelectionRect() and paintDocumentMarker()
+        currently expect the enclosing integral rectangle of the selection rectangle. The function
+        paintDocumentMarker() needs the enclosing integral rectangle to avoid truncating the dot pattern
+        drawn under marked words (e.g. a spelling error) on Cocoa platforms. With regards to localSelectionRect()
+        we should look to have it return the actual selection rectangle. See <https://bugs.webkit.org/show_bug.cgi?id=138913>
+        for more details.
+
+        * rendering/InlineTextBox.cpp:
+        (WebCore::InlineTextBox::localSelectionRect const): Move logic in common with paintSelection() into
+        localSelectionRectWithClampedPositions() and modified code to use it.
+        (WebCore::InlineTextBox::localSelectionRectWithClampedPositions const): Added.
+        (WebCore::InlineTextBox::paint): Store the local paint offset as a LayoutPoint as it is the canonical
+        data type for representing an offset when painting. Pass the local paint offset instead of the analagous boxOrigin value.
+        (WebCore::InlineTextBox::paintSelection): Write in terms of localSelectionRectWithClampedPositions().
+        (WebCore::InlineTextBox::paintTextSubrangeBackground): Ditto.
+        (WebCore::InlineTextBox::paintCompositionBackground): Ditto.
+        (WebCore::InlineTextBox::paintTextMatchMarker): Ditto.
+        (WebCore::InlineTextBox::paintDocumentMarker): Ditto.
+        (WebCore::InlineTextBox::paintDocumentMarkers): Pass paint offset instead of the analogous boxOrigin value.
+        * rendering/InlineTextBox.h:
+
+2017-10-19  Daniel Bates  <[email protected]>
+
         Referrer policy should be inherited from creator
         https://bugs.webkit.org/show_bug.cgi?id=178403
         <rdar://problem/31546136>

Modified: trunk/Source/WebCore/rendering/InlineTextBox.cpp (223698 => 223699)


--- trunk/Source/WebCore/rendering/InlineTextBox.cpp	2017-10-19 18:54:47 UTC (rev 223698)
+++ trunk/Source/WebCore/rendering/InlineTextBox.cpp	2017-10-19 18:55:16 UTC (rev 223699)
@@ -185,39 +185,63 @@
     return combinedText() ? combinedText()->textCombineFont() : lineStyle().fontCascade();
 }
 
-// FIXME: Share more code with paintSelection().
-LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
+// FIXME: This function should return the same rectangle as localSelectionRectWithClampedPositions(..., ..., SelectionRectRounding::None),
+// which represents the rectange of the selected text on the page. We need to update all callers to expect this change.
+// See <https://bugs.webkit.org/show_bug.cgi?id=138913> for more details.
+LayoutRect InlineTextBox::localSelectionRect(unsigned startPosition, unsigned endPosition) const
 {
-    unsigned sPos = clampedOffset(startPos);
-    unsigned ePos = clampedOffset(endPos);
+    if (startPosition > endPosition || endPosition < m_start || startPosition > end() + 1)
+        return { };
+    return localSelectionRectWithClampedPositions(clampedOffset(startPosition), clampedOffset(endPosition), SelectionRectRounding::EnclosingIntRect);
+}
 
-    if (sPos >= ePos && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
+LayoutRect InlineTextBox::localSelectionRectWithClampedPositions(unsigned clampedStartPosition, unsigned clampedEndPosition, SelectionRectRounding roundingStrategy) const
+{
+    if (clampedStartPosition > clampedEndPosition)
         return { };
 
-    LayoutUnit selectionTop = this->selectionTop();
-    LayoutUnit selectionHeight = this->selectionHeight();
+    auto selectionTop = root().selectionTopAdjustedForPrecedingBlock();
+    auto selectionBottom = this->selectionBottom();
 
     auto text = this->text();
     TextRun textRun = createTextRun(text);
 
-    LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
+    // Use same y positioning and height as used for selected text on the page, so that when some or all of this
+    // subrange is selected on the page there are no pieces sticking out.
+    LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
+    auto selectionHeight = root().selectionHeightAdjustedForPrecedingBlock();
+    LayoutRect selectionRect { LayoutPoint { logicalLeft(), logicalTop() - deltaY }, LayoutSize { m_logicalWidth, selectionHeight } };
+
     // Avoid computing the font width when the entire line box is selected as an optimization.
-    if (sPos || ePos != textRun.length())
-        lineFont().adjustSelectionRectForText(textRun, selectionRect, sPos, ePos);
-    // FIXME: The computation of the snapped selection rect differs from the computation of this rect
-    // in paintSelection(). See <https://bugs.webkit.org/show_bug.cgi?id=138913>.
-    IntRect snappedSelectionRect = enclosingIntRect(selectionRect);
-    LayoutUnit logicalWidth = snappedSelectionRect.width();
-    if (snappedSelectionRect.x() > logicalRight())
-        logicalWidth  = 0;
-    else if (snappedSelectionRect.maxX() > logicalRight())
-        logicalWidth = logicalRight() - snappedSelectionRect.x();
+    if (clampedStartPosition || clampedEndPosition != textRun.length())
+        lineFont().adjustSelectionRectForText(textRun, selectionRect, clampedStartPosition, clampedEndPosition);
 
-    LayoutPoint topPoint = isHorizontal() ? LayoutPoint(snappedSelectionRect.x(), selectionTop) : LayoutPoint(selectionTop, snappedSelectionRect.x());
-    LayoutUnit width = isHorizontal() ? logicalWidth : selectionHeight;
-    LayoutUnit height = isHorizontal() ? selectionHeight : logicalWidth;
+    LayoutUnit selectionLeft;
+    LayoutUnit selectionWidth;
+    if (roundingStrategy == SelectionRectRounding::EnclosingIntRect) {
+        auto snappedSelectionRect = enclosingIntRect(selectionRect);
+        selectionLeft = snappedSelectionRect.x();
+        selectionWidth = snappedSelectionRect.width();
+        if (snappedSelectionRect.x() > logicalRight())
+            selectionWidth = 0;
+        else if (snappedSelectionRect.maxX() > logicalRight())
+            selectionWidth = logicalRight() - snappedSelectionRect.x();
+    } else {
+        selectionLeft = selectionRect.x();
+        selectionWidth = selectionRect.width();
+    }
 
-    return LayoutRect(topPoint, LayoutSize(width, height));
+    LayoutPoint location;
+    LayoutSize size;
+    if (isHorizontal()) {
+        location = { selectionLeft, selectionTop };
+        size = { selectionWidth, selectionHeight };
+    } else {
+        location = { selectionTop, selectionLeft };
+        size = { selectionHeight, selectionWidth };
+    }
+
+    return { location, size };
 }
 
 void InlineTextBox::deleteLine()
@@ -405,7 +429,7 @@
     LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
     LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
     
-    FloatPoint localPaintOffset(paintOffset);
+    LayoutPoint localPaintOffset { paintOffset };
     
     if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
         return;
@@ -472,12 +496,12 @@
     // and composition underlines.
     if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
         if (containsComposition && !useCustomUnderlines)
-            paintCompositionBackground(context, boxOrigin);
+            paintCompositionBackground(context, localPaintOffset);
 
-        paintDocumentMarkers(context, boxOrigin, true);
+        paintDocumentMarkers(context, localPaintOffset, true);
 
         if (haveSelection && !useCustomUnderlines)
-            paintSelection(context, boxOrigin, selectionPaintStyle.fillColor);
+            paintSelection(context, localPaintOffset, selectionPaintStyle.fillColor);
     }
 
     // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
@@ -579,7 +603,7 @@
     }
 
     if (paintInfo.phase == PaintPhaseForeground) {
-        paintDocumentMarkers(context, boxOrigin, false);
+        paintDocumentMarkers(context, localPaintOffset, false);
 
         if (useCustomUnderlines)
             paintCompositionUnderlines(context, boxOrigin);
@@ -623,7 +647,7 @@
     return { clampedOffset(start), clampedOffset(end) };
 }
 
-void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& textColor)
+void InlineTextBox::paintSelection(GraphicsContext& context, const LayoutPoint& paintOffset, const Color& textColor)
 {
 #if ENABLE(TEXT_SELECTION)
     if (context.paintingDisabled())
@@ -650,29 +674,19 @@
 
     // Note that if the text is truncated, we let the thing being painted in the truncation
     // draw its own highlight.
-    auto text = this->text();
-    TextRun textRun = createTextRun(text);
+    auto selectionRect = localSelectionRectWithClampedPositions(selectionStart, selectionEnd);
+    selectionRect.moveBy(paintOffset);
 
-    const RootInlineBox& rootBox = root();
-    LayoutUnit selectionBottom = rootBox.selectionBottom();
-    LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
-
-    LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
-    LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
-
-    LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
-    lineFont().adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
-
     // FIXME: Support painting combined text.
-    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c);
+    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), isLeftToRightDirection()), c);
 #else
     UNUSED_PARAM(context);
-    UNUSED_PARAM(boxOrigin);
+    UNUSED_PARAM(paintOffset);
     UNUSED_PARAM(textColor);
 #endif
 }
 
-inline void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const Color& color, unsigned startOffset, unsigned endOffset)
+inline void InlineTextBox::paintTextSubrangeBackground(GraphicsContext& context, const LayoutPoint& paintOffset, const Color& color, unsigned startOffset, unsigned endOffset)
 {
     startOffset = clampedOffset(startOffset);
     endOffset = clampedOffset(endOffset);
@@ -682,30 +696,24 @@
     GraphicsContextStateSaver stateSaver { context };
     updateGraphicsContext(context, TextPaintStyle { color }); // Don't draw text at all!
 
-    // Use same y positioning and height as for selection, so that when the selection and this subrange are on
-    // the same word there are no pieces sticking out.
-    LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
-    LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight());
+    auto selectionRect = localSelectionRectWithClampedPositions(startOffset, endOffset);
+    selectionRect.moveBy(paintOffset);
 
-    auto text = this->text();
-    TextRun textRun = createTextRun(text);
-    lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
-
     // FIXME: Support painting combined text.
-    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), color);
+    context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), isLeftToRightDirection()), color);
 }
 
-void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin)
+void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const LayoutPoint& paintOffset)
 {
-    paintTextSubrangeBackground(context, boxOrigin, renderer().frame().editor().compositionStart(), renderer().frame().editor().compositionEnd(), Color::compositionFill);
+    paintTextSubrangeBackground(context, paintOffset, renderer().frame().editor().compositionStart(), renderer().frame().editor().compositionEnd(), Color::compositionFill);
 }
 
-void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange, bool isActiveMatch)
+void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const LayoutPoint& paintOffset, const MarkerSubrange& subrange, bool isActiveMatch)
 {
     if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
         return;
     auto highlightColor = isActiveMatch ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
-    paintTextSubrangeBackground(context, boxOrigin, highlightColor, subrange.startOffset, subrange.endOffset);
+    paintTextSubrangeBackground(context, paintOffset, highlightColor, subrange.startOffset, subrange.endOffset);
 }
 
 static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
@@ -758,7 +766,7 @@
         context.concatCTM(rotation(boxRect, Counterclockwise));
 }
 
-void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, const MarkerSubrange& subrange)
+void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const LayoutPoint& paintOffset, const MarkerSubrange& subrange)
 {
     // Never print spelling/grammar markers (5327887)
     if (renderer().document().printing())
@@ -767,9 +775,6 @@
     if (m_truncation == cFullTruncation)
         return;
 
-    float start = 0; // start of line to draw, relative to tx
-    float width = m_logicalWidth; // how much line to draw
-
     // Determine whether we need to measure text
     bool markerSpansWholeBox = true;
     if (m_start <= subrange.startOffset)
@@ -779,24 +784,23 @@
     if (m_truncation != cNoTruncation)
         markerSpansWholeBox = false;
 
-    if (!markerSpansWholeBox) {
+    FloatPoint location;
+    float width;
+    if (markerSpansWholeBox) {
+        location = locationIncludingFlipping();
+        width = m_logicalWidth;
+    } else {
         unsigned startPosition = clampedOffset(subrange.startOffset);
         unsigned endPosition = clampedOffset(subrange.endOffset);
 
-        // Calculate start & width
-        int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
-        int selHeight = selectionHeight();
-        FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
-        auto text = this->text();
-        TextRun run = createTextRun(text);
+        // We round to the nearest enclosing integral rect to avoid truncating the dot decoration on Cocoa platforms.
+        // FIXME: Find a better place to ensure that the Cocoa dots are not truncated.
+        auto selectionRect = localSelectionRectWithClampedPositions(startPosition, endPosition, SelectionRectRounding::EnclosingIntRect);
+        location = selectionRect.location();
+        width = selectionRect.width();
+    }
+    location.moveBy(paintOffset);
 
-        LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
-        lineFont().adjustSelectionRectForText(run, selectionRect, startPosition, endPosition);
-        IntRect markerRect = enclosingIntRect(selectionRect);
-        start = markerRect.x() - startPoint.x();
-        width = markerRect.width();
-    }
-    
     auto lineStyleForSubrangeType = [] (MarkerSubrange::Type type) {
         switch (type) {
         case MarkerSubrange::SpellingError:
@@ -836,10 +840,11 @@
         underlineOffset = baseline + 2;
     }
     // FIXME: Support painting combined text.
-    context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForSubrangeType(subrange.type));
+    location.move(0, underlineOffset);
+    context.drawLineForDocumentMarker(location, width, lineStyleForSubrangeType(subrange.type));
 }
 
-void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, bool background)
+void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const LayoutPoint& paintOffset, bool background)
 {
     if (!renderer().textNode())
         return;
@@ -933,9 +938,9 @@
 
     for (auto& subrange : subdivide(subranges, OverlapStrategy::Frontmost)) {
         if (subrange.type == MarkerSubrange::TextMatch)
-            paintTextMatchMarker(context, boxOrigin, subrange, subrange.marker->isActiveMatch());
+            paintTextMatchMarker(context, paintOffset, subrange, subrange.marker->isActiveMatch());
         else
-            paintDocumentMarker(context, boxOrigin, subrange);
+            paintDocumentMarker(context, paintOffset, subrange);
     }
 }
 

Modified: trunk/Source/WebCore/rendering/InlineTextBox.h (223698 => 223699)


--- trunk/Source/WebCore/rendering/InlineTextBox.h	2017-10-19 18:54:47 UTC (rev 223698)
+++ trunk/Source/WebCore/rendering/InlineTextBox.h	2017-10-19 18:55:16 UTC (rev 223699)
@@ -110,7 +110,7 @@
 public:
     FloatRect calculateBoundaries() const override { return FloatRect(x(), y(), width(), height()); }
 
-    virtual LayoutRect localSelectionRect(unsigned startPos, unsigned endPos) const;
+    virtual LayoutRect localSelectionRect(unsigned startPosition, unsigned endPosition) const;
     bool isSelected(unsigned startPosition, unsigned endPosition) const;
     std::pair<unsigned, unsigned> selectionStartEnd() const;
 
@@ -152,18 +152,21 @@
 private:
     void paintDecoration(GraphicsContext&, const TextRun&, const FloatPoint& textOrigin, const FloatRect& boxRect,
         TextDecoration, TextPaintStyle, const ShadowData*, const FloatRect& clipOutRect);
-    void paintSelection(GraphicsContext&, const FloatPoint& boxOrigin, const Color&);
+    void paintSelection(GraphicsContext&, const LayoutPoint& paintOffset, const Color&);
 
-    void paintDocumentMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&);
-    void paintDocumentMarkers(GraphicsContext&, const FloatPoint& boxOrigin, bool background);
-    void paintTextMatchMarker(GraphicsContext&, const FloatPoint& boxOrigin, const MarkerSubrange&, bool isActiveMatch);
+    void paintDocumentMarker(GraphicsContext&, const LayoutPoint& paintOffset, const MarkerSubrange&);
+    void paintDocumentMarkers(GraphicsContext&, const LayoutPoint& paintOffset, bool background);
+    void paintTextMatchMarker(GraphicsContext&, const LayoutPoint& paintOffset, const MarkerSubrange&, bool isActiveMatch);
 
-    void paintCompositionBackground(GraphicsContext&, const FloatPoint& boxOrigin);
+    void paintCompositionBackground(GraphicsContext&, const LayoutPoint& paintOffset);
     void paintCompositionUnderlines(GraphicsContext&, const FloatPoint& boxOrigin) const;
     void paintCompositionUnderline(GraphicsContext&, const FloatPoint& boxOrigin, const CompositionUnderline&) const;
 
-    void paintTextSubrangeBackground(GraphicsContext&, const FloatPoint& boxOrigin, const Color&, unsigned startOffset, unsigned endOffset);
+    void paintTextSubrangeBackground(GraphicsContext&, const LayoutPoint& paintOffset, const Color&, unsigned startOffset, unsigned endOffset);
 
+    enum class SelectionRectRounding { None, EnclosingIntRect };
+    LayoutRect localSelectionRectWithClampedPositions(unsigned clampedStartPosition, unsigned clampedEndPosition, SelectionRectRounding = SelectionRectRounding::None) const;
+
     const RenderCombineText* combinedText() const;
     const FontCascade& lineFont() const;
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to