Diff
Modified: trunk/Source/WebCore/ChangeLog (282310 => 282311)
--- trunk/Source/WebCore/ChangeLog 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/ChangeLog 2021-09-11 16:10:13 UTC (rev 282311)
@@ -1,3 +1,48 @@
+2021-09-11 Antti Koivisto <[email protected]>
+
+ Move rest of the LegacyInlineTextBox painting code to TextBoxPainter
+ https://bugs.webkit.org/show_bug.cgi?id=230188
+
+ Reviewed by Alan Bujtas.
+
+ Move the remaining painting code.
+
+ * rendering/LegacyEllipsisBox.h:
+ * rendering/LegacyInlineBox.cpp:
+ (WebCore::LegacyInlineBox::selectionState const):
+ (WebCore::LegacyInlineBox::selectionState): Deleted.
+ * rendering/LegacyInlineBox.h:
+ * rendering/LegacyInlineFlowBox.cpp:
+ (WebCore::LegacyInlineFlowBox::selectionState const):
+ (WebCore::LegacyInlineFlowBox::selectionState): Deleted.
+ * rendering/LegacyInlineFlowBox.h:
+ * rendering/LegacyInlineTextBox.cpp:
+ (WebCore::LegacyInlineTextBox::selectionState const):
+ (WebCore::LegacyInlineTextBox::paint):
+ (WebCore::LegacyInlineTextBox::selectionState): Deleted.
+ (WebCore::createMarkedTextFromSelectionInBox): Deleted.
+ * rendering/LegacyInlineTextBox.h:
+ * rendering/LegacyRootInlineBox.cpp:
+ (WebCore::LegacyRootInlineBox::selectionState const):
+ (WebCore::LegacyRootInlineBox::selectionState): Deleted.
+ * rendering/LegacyRootInlineBox.h:
+ * rendering/MarkedText.cpp:
+ (WebCore::MarkedText::collectForHighlights):
+ * rendering/MarkedText.h:
+ * rendering/TextBoxPainter.cpp:
+ (WebCore::TextBoxPainter::TextBoxPainter):
+ (WebCore::TextBoxPainter::~TextBoxPainter):
+ (WebCore::TextBoxPainter::paint):
+ (WebCore::createMarkedTextFromSelectionInBox):
+ (WebCore::TextBoxPainter::paintBackground):
+ (WebCore::TextBoxPainter::paintForegroundAndDecorations):
+ (WebCore::TextBoxPainter::paintForeground):
+ (WebCore::TextBoxPainter::paintDecoration):
+ (WebCore::TextBoxPainter::paintCompositionUnderline):
+ (WebCore::TextBoxPainter::paintPlatformDocumentMarker):
+ (WebCore::TextBoxPainter::computePaintRect):
+ * rendering/TextBoxPainter.h:
+
2021-09-11 Philippe Normand <[email protected]>
[GLIB] MediaSession is not enabled
Modified: trunk/Source/WebCore/rendering/LegacyEllipsisBox.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyEllipsisBox.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyEllipsisBox.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -41,7 +41,7 @@
private:
void paintMarkupBox(PaintInfo&, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, const RenderStyle&);
int height() const { return m_height; }
- RenderObject::HighlightState selectionState() override { return m_selectionState; }
+ RenderObject::HighlightState selectionState() const override { return m_selectionState; }
void paintSelection(GraphicsContext&, const LayoutPoint&, const RenderStyle&, const FontCascade&);
LegacyInlineBox* markupBox() const;
Modified: trunk/Source/WebCore/rendering/LegacyInlineBox.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineBox.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineBox.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -245,7 +245,7 @@
return leaf;
}
-RenderObject::HighlightState LegacyInlineBox::selectionState()
+RenderObject::HighlightState LegacyInlineBox::selectionState() const
{
return renderer().selectionState();
}
Modified: trunk/Source/WebCore/rendering/LegacyInlineBox.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineBox.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineBox.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -219,7 +219,7 @@
WEBCORE_EXPORT virtual void dirtyLineBoxes();
- WEBCORE_EXPORT virtual RenderObject::HighlightState selectionState();
+ WEBCORE_EXPORT virtual RenderObject::HighlightState selectionState() const;
WEBCORE_EXPORT virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const;
// visibleLeftEdge, visibleRightEdge are in the parent's coordinate system.
Modified: trunk/Source/WebCore/rendering/LegacyInlineFlowBox.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineFlowBox.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineFlowBox.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -1456,7 +1456,7 @@
return leaf;
}
-RenderObject::HighlightState LegacyInlineFlowBox::selectionState()
+RenderObject::HighlightState LegacyInlineFlowBox::selectionState() const
{
return RenderObject::HighlightState::None;
}
Modified: trunk/Source/WebCore/rendering/LegacyInlineFlowBox.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineFlowBox.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineFlowBox.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -203,7 +203,7 @@
void removeChild(LegacyInlineBox* child);
- RenderObject::HighlightState selectionState() override;
+ RenderObject::HighlightState selectionState() const override;
bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const final;
float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool&) override;
Modified: trunk/Source/WebCore/rendering/LegacyInlineTextBox.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineTextBox.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineTextBox.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -159,7 +159,7 @@
return selectableRange().intersects(startPosition, endPosition);
}
-RenderObject::HighlightState LegacyInlineTextBox::selectionState()
+RenderObject::HighlightState LegacyInlineTextBox::selectionState() const
{
auto state = renderer().view().selection().highlightStateForTextBox(renderer(), selectableRange());
@@ -404,14 +404,6 @@
return isAbove;
}
-static MarkedText createMarkedTextFromSelectionInBox(const LegacyInlineTextBox& box)
-{
- auto [selectionStart, selectionEnd] = box.selectionStartEnd();
- if (selectionStart < selectionEnd)
- return { selectionStart, selectionEnd, MarkedText::Selection };
- return { };
-}
-
void LegacyInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /*lineTop*/, LayoutUnit /*lineBottom*/)
{
if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != Visibility::Visible
@@ -427,187 +419,12 @@
LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
-
- FloatPoint localPaintOffset(paintOffset);
-
+
if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
return;
- bool isPrinting = renderer().document().printing();
-
- // Determine whether or not we're selected.
- bool haveSelection = !isPrinting && paintInfo.phase != PaintPhase::TextClip && selectionState() != RenderObject::HighlightState::None;
- if (!haveSelection && paintInfo.phase == PaintPhase::Selection) {
- // When only painting the selection, don't bother to paint if there is none.
- return;
- }
-
- if (m_truncation != cNoTruncation) {
- if (renderer().containingBlock()->style().isLeftToRightDirection() != isLeftToRightDirection()) {
- // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
- // at which we start drawing text.
- // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
- // |Hello|CBA| -> |...He|CBA|
- // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
- // farther to the right.
- // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
- // truncated string i.e. |Hello|CBA| -> |...lo|CBA|
- LayoutUnit widthOfVisibleText { renderer().width(m_start, m_truncation, textPos(), isFirstLine()) };
- LayoutUnit widthOfHiddenText { logicalWidth() - widthOfVisibleText };
- LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0_lu);
- localPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
- }
- }
-
- const RenderStyle& lineStyle = this->lineStyle();
-
- localPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
-
- FloatPoint boxOrigin = locationIncludingFlipping();
- boxOrigin.moveBy(localPaintOffset);
- FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
-
- if (paintInfo.phase == PaintPhase::EventRegion) {
- if (visibleToHitTesting())
- paintInfo.eventRegionContext->unite(enclosingIntRect(boxRect), renderer().style());
- return;
- }
-
- TextBoxPainter textBoxPainter(*this, paintInfo, boxRect);
-
- // Determine whether or not we have composition underlines to draw.
- bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
- bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
-
- // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
- // and composition underlines.
- if (paintInfo.phase != PaintPhase::Selection && paintInfo.phase != PaintPhase::TextClip && !isPrinting) {
- if (containsComposition && !useCustomUnderlines)
- textBoxPainter.paintCompositionBackground();
-
- auto selectableRange = this->selectableRange();
-
- Vector<MarkedText> markedTexts;
- markedTexts.appendVector(MarkedText::collectForDocumentMarkers(renderer(), selectableRange, MarkedText::PaintPhase::Background));
- markedTexts.appendVector(MarkedText::collectForHighlights(renderer(), parent()->renderer(), selectableRange, MarkedText::PaintPhase::Background));
-
-#if ENABLE(TEXT_SELECTION)
- GraphicsContext& context = paintInfo.context();
- if (haveSelection && !useCustomUnderlines && !context.paintingDisabled()) {
- auto selectionMarkedText = createMarkedTextFromSelectionInBox(*this);
- if (!selectionMarkedText.isEmpty())
- markedTexts.append(WTFMove(selectionMarkedText));
- }
-#endif
- auto styledMarkedTexts = StyledMarkedText::subdivideAndResolve(markedTexts, renderer(), isFirstLine(), paintInfo);
-
- // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
- auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualBackground(styledMarkedTexts);
-
- for (auto& markedText : coalescedStyledMarkedTexts)
- textBoxPainter.paintBackground(markedText);
- }
-
- // FIXME: Right now, LegacyInlineTextBoxes never call addRelevantUnpaintedObject() even though they might
- // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
- // when we do, we will have to account for the fact the LegacyInlineTextBoxes do not always have unique
- // renderers and Page currently relies on each unpainted object having a unique renderer.
- if (paintInfo.phase == PaintPhase::Foreground)
- renderer().page().addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
-
- if (paintInfo.phase == PaintPhase::Foreground)
- textBoxPainter.paintPlatformDocumentMarkers();
-
- // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
- bool shouldPaintSelectionForeground = haveSelection && !useCustomUnderlines;
- Vector<MarkedText> markedTexts;
- if (paintInfo.phase != PaintPhase::Selection) {
- auto selectableRange = this->selectableRange();
-
- // The marked texts for the gaps between document markers and selection are implicitly created by subdividing the entire line.
- markedTexts.append({ selectableRange.clamp(m_start), selectableRange.clamp(end()), MarkedText::Unmarked });
-
- if (!isPrinting) {
- markedTexts.appendVector(MarkedText::collectForDocumentMarkers(renderer(), selectableRange, MarkedText::PaintPhase::Foreground));
- markedTexts.appendVector(MarkedText::collectForHighlights(renderer(), parent()->renderer(), selectableRange, MarkedText::PaintPhase::Foreground));
-
- bool shouldPaintDraggedContent = !(paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection));
- if (shouldPaintDraggedContent) {
- auto markedTextsForDraggedContent = MarkedText::collectForDraggedContent(renderer(), selectableRange);
- if (!markedTextsForDraggedContent.isEmpty()) {
- shouldPaintSelectionForeground = false;
- markedTexts.appendVector(markedTextsForDraggedContent);
- }
- }
- }
- }
- // The selection marked text acts as a placeholder when computing the marked texts for the gaps...
- if (shouldPaintSelectionForeground) {
- ASSERT(!isPrinting);
- auto selectionMarkedText = createMarkedTextFromSelectionInBox(*this);
- if (!selectionMarkedText.isEmpty())
- markedTexts.append(WTFMove(selectionMarkedText));
- }
-
- auto styledMarkedTexts = StyledMarkedText::subdivideAndResolve(markedTexts, renderer(), isFirstLine(), paintInfo);
-
- // ... now remove the selection marked text if we are excluding selection.
- if (!isPrinting && paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection)) {
- styledMarkedTexts.removeAllMatching([] (const StyledMarkedText& markedText) {
- return markedText.type == MarkedText::Selection;
- });
- }
-
- // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
- auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualForeground(styledMarkedTexts);
-
- for (auto& markedText : coalescedStyledMarkedTexts)
- textBoxPainter.paintForeground(markedText);
-
- // Paint decorations
- auto textDecorations = lineStyle.textDecorationsInEffect();
- bool highlightDecorations = !MarkedText::collectForHighlights(renderer(), parent()->renderer(), selectableRange(), MarkedText::PaintPhase::Decoration).isEmpty();
- bool lineDecorations = !textDecorations.isEmpty();
- if ((lineDecorations || highlightDecorations) && paintInfo.phase != PaintPhase::Selection) {
- TextRun textRun = createTextRun();
- unsigned length = textRun.length();
- if (m_truncation != cNoTruncation)
- length = m_truncation;
- unsigned selectionStart = 0;
- unsigned selectionEnd = 0;
- if (haveSelection)
- std::tie(selectionStart, selectionEnd) = selectionStartEnd();
-
- FloatRect textDecorationSelectionClipOutRect;
- if ((paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection)) && selectionStart < selectionEnd && selectionEnd <= length) {
- textDecorationSelectionClipOutRect = logicalOverflowRect();
- textDecorationSelectionClipOutRect.moveBy(localPaintOffset);
- float logicalWidthBeforeRange;
- float logicalWidthAfterRange;
- float logicalSelectionWidth = lineFont().widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
- // FIXME: Do we need to handle vertical bottom to top text?
- if (!isHorizontal()) {
- textDecorationSelectionClipOutRect.move(0, logicalWidthBeforeRange);
- textDecorationSelectionClipOutRect.setHeight(logicalSelectionWidth);
- } else if (direction() == TextDirection::RTL) {
- textDecorationSelectionClipOutRect.move(logicalWidthAfterRange, 0);
- textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
- } else {
- textDecorationSelectionClipOutRect.move(logicalWidthBeforeRange, 0);
- textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
- }
- }
-
- // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
- auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualDecorations(styledMarkedTexts);
-
- for (auto& markedText : coalescedStyledMarkedTexts)
- textBoxPainter.paintDecoration(markedText, textDecorationSelectionClipOutRect);
- }
-
- // 3. Paint fancy decorations, including composition underlines and platform-specific underlines for spelling errors, grammar errors, et cetera.
- if (paintInfo.phase == PaintPhase::Foreground && useCustomUnderlines)
- textBoxPainter.paintCompositionUnderlines();
+ TextBoxPainter textBoxPainter(*this, paintInfo, paintOffset);
+ textBoxPainter.paint();
}
TextBoxSelectableRange LegacyInlineTextBox::selectableRange() const
Modified: trunk/Source/WebCore/rendering/LegacyInlineTextBox.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyInlineTextBox.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyInlineTextBox.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -128,7 +128,7 @@
void attachLine() final;
public:
- RenderObject::HighlightState selectionState() final;
+ RenderObject::HighlightState selectionState() const final;
private:
void clearTruncation() final { m_truncation = cNoTruncation; }
Modified: trunk/Source/WebCore/rendering/LegacyRootInlineBox.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyRootInlineBox.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyRootInlineBox.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -482,7 +482,7 @@
return result;
}
-RenderObject::HighlightState LegacyRootInlineBox::selectionState()
+RenderObject::HighlightState LegacyRootInlineBox::selectionState() const
{
// Walk over all of the selected boxes.
RenderObject::HighlightState state = RenderObject::HighlightState::None;
Modified: trunk/Source/WebCore/rendering/LegacyRootInlineBox.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/LegacyRootInlineBox.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/LegacyRootInlineBox.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -126,7 +126,7 @@
void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom) override;
bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction) override;
- RenderObject::HighlightState selectionState() final;
+ RenderObject::HighlightState selectionState() const final;
LegacyInlineBox* firstSelectedBox();
LegacyInlineBox* lastSelectedBox();
Modified: trunk/Source/WebCore/rendering/MarkedText.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/MarkedText.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/MarkedText.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -104,11 +104,12 @@
return result;
}
-Vector<MarkedText> MarkedText::collectForHighlights(RenderText& renderer, RenderBoxModelObject& parentRenderer, const TextBoxSelectableRange& selectableRange, PaintPhase phase)
+Vector<MarkedText> MarkedText::collectForHighlights(const RenderText& renderer, const TextBoxSelectableRange& selectableRange, PaintPhase phase)
{
Vector<MarkedText> markedTexts;
HighlightData highlightData;
if (RuntimeEnabledFeatures::sharedFeatures().highlightAPIEnabled()) {
+ auto& parentRenderer = *renderer.parent();
auto& parentStyle = parentRenderer.style();
if (auto highlightRegister = renderer.document().highlightRegisterIfExists()) {
for (auto& highlight : highlightRegister->map()) {
@@ -147,7 +148,7 @@
return markedTexts;
}
-Vector<MarkedText> MarkedText::collectForDocumentMarkers(RenderText& renderer, const TextBoxSelectableRange& selectableRange, PaintPhase phase)
+Vector<MarkedText> MarkedText::collectForDocumentMarkers(const RenderText& renderer, const TextBoxSelectableRange& selectableRange, PaintPhase phase)
{
if (!renderer.textNode())
return { };
@@ -252,7 +253,7 @@
return markedTexts;
}
-Vector<MarkedText> MarkedText::collectForDraggedContent(RenderText& renderer, const TextBoxSelectableRange& selectableRange)
+Vector<MarkedText> MarkedText::collectForDraggedContent(const RenderText& renderer, const TextBoxSelectableRange& selectableRange)
{
auto draggedContentRanges = renderer.draggedContentRangesBetweenOffsets(selectableRange.start, selectableRange.start + selectableRange.length);
Modified: trunk/Source/WebCore/rendering/MarkedText.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/MarkedText.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/MarkedText.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -82,9 +82,9 @@
WEBCORE_EXPORT static Vector<MarkedText> subdivide(const Vector<MarkedText>&, OverlapStrategy = OverlapStrategy::None);
- static Vector<MarkedText> collectForDocumentMarkers(RenderText&, const TextBoxSelectableRange&, PaintPhase);
- static Vector<MarkedText> collectForHighlights(RenderText&, RenderBoxModelObject& parentRenderer, const TextBoxSelectableRange&, PaintPhase);
- static Vector<MarkedText> collectForDraggedContent(RenderText&, const TextBoxSelectableRange&);
+ static Vector<MarkedText> collectForDocumentMarkers(const RenderText&, const TextBoxSelectableRange&, PaintPhase);
+ static Vector<MarkedText> collectForHighlights(const RenderText&, const TextBoxSelectableRange&, PaintPhase);
+ static Vector<MarkedText> collectForDraggedContent(const RenderText&, const TextBoxSelectableRange&);
};
}
Modified: trunk/Source/WebCore/rendering/TextBoxPainter.cpp (282310 => 282311)
--- trunk/Source/WebCore/rendering/TextBoxPainter.cpp 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/TextBoxPainter.cpp 2021-09-11 16:10:13 UTC (rev 282311)
@@ -30,6 +30,7 @@
#include "GraphicsContext.h"
#include "LegacyInlineTextBox.h"
#include "PaintInfo.h"
+#include "RenderText.h"
#include "StyledMarkedText.h"
#include "TextPaintStyle.h"
#include "TextPainter.h"
@@ -36,28 +37,186 @@
namespace WebCore {
-TextBoxPainter::TextBoxPainter(const LegacyInlineTextBox& textBox, PaintInfo& paintInfo, const FloatRect& boxRect)
+TextBoxPainter::TextBoxPainter(const LegacyInlineTextBox& textBox, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
: m_textBox(textBox)
+ , m_renderer(textBox.renderer())
+ , m_document(m_renderer.document())
, m_paintInfo(paintInfo)
- , m_boxRect(boxRect)
+ , m_paintRect(computePaintRect(textBox, paintOffset))
+ , m_isPrinting(m_document.printing())
+ , m_haveSelection(!m_isPrinting && m_paintInfo.phase != PaintPhase::TextClip && m_textBox.selectionState() != RenderObject::HighlightState::None)
+ , m_containsComposition(m_renderer.textNode() && m_renderer.frame().editor().compositionNode() == m_renderer.textNode())
+ , m_useCustomUnderlines(m_containsComposition && m_renderer.frame().editor().compositionUsesCustomUnderlines())
{
- bool shouldRotate = !m_textBox.isHorizontal() && !m_textBox.combinedText();
- if (shouldRotate)
- m_paintInfo.context().concatCTM(rotation(m_boxRect, Clockwise));
+ ASSERT(paintInfo.phase == PaintPhase::Foreground || paintInfo.phase == PaintPhase::Selection || paintInfo.phase == PaintPhase::TextClip || paintInfo.phase == PaintPhase::EventRegion);
}
TextBoxPainter::~TextBoxPainter()
{
+}
+
+void TextBoxPainter::paint()
+{
+ if (m_paintInfo.phase == PaintPhase::Selection && !m_haveSelection)
+ return;
+
+ if (m_paintInfo.phase == PaintPhase::EventRegion) {
+ if (m_textBox.visibleToHitTesting())
+ m_paintInfo.eventRegionContext->unite(enclosingIntRect(m_paintRect), m_renderer.style());
+ return;
+ }
+
bool shouldRotate = !m_textBox.isHorizontal() && !m_textBox.combinedText();
if (shouldRotate)
- m_paintInfo.context().concatCTM(rotation(m_boxRect, Counterclockwise));
+ m_paintInfo.context().concatCTM(rotation(m_paintRect, Clockwise));
+
+ if (m_paintInfo.phase == PaintPhase::Foreground) {
+ if (!m_isPrinting)
+ paintBackground();
+
+ paintPlatformDocumentMarkers();
+ }
+
+ paintForegroundAndDecorations();
+
+ if (m_paintInfo.phase == PaintPhase::Foreground) {
+ if (m_useCustomUnderlines)
+ paintCompositionUnderlines();
+
+ m_renderer.page().addRelevantRepaintedObject(const_cast<RenderText*>(&m_renderer), enclosingLayoutRect(m_paintRect));
+ }
+
+ if (shouldRotate)
+ m_paintInfo.context().concatCTM(rotation(m_paintRect, Counterclockwise));
}
+static MarkedText createMarkedTextFromSelectionInBox(const LegacyInlineTextBox& box)
+{
+ auto [selectionStart, selectionEnd] = box.selectionStartEnd();
+ if (selectionStart < selectionEnd)
+ return { selectionStart, selectionEnd, MarkedText::Selection };
+ return { };
+}
+
+void TextBoxPainter::paintBackground()
+{
+ if (m_containsComposition && !m_useCustomUnderlines)
+ paintCompositionBackground();
+
+ auto selectableRange = m_textBox.selectableRange();
+
+ Vector<MarkedText> markedTexts;
+ markedTexts.appendVector(MarkedText::collectForDocumentMarkers(m_renderer, selectableRange, MarkedText::PaintPhase::Background));
+ markedTexts.appendVector(MarkedText::collectForHighlights(m_renderer, selectableRange, MarkedText::PaintPhase::Background));
+
+#if ENABLE(TEXT_SELECTION)
+ if (m_haveSelection && !m_useCustomUnderlines && !m_paintInfo.context().paintingDisabled()) {
+ auto selectionMarkedText = createMarkedTextFromSelectionInBox(m_textBox);
+ if (!selectionMarkedText.isEmpty())
+ markedTexts.append(WTFMove(selectionMarkedText));
+ }
+#endif
+ auto styledMarkedTexts = StyledMarkedText::subdivideAndResolve(markedTexts, m_renderer, m_textBox.isFirstLine(), m_paintInfo);
+
+ // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
+ auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualBackground(styledMarkedTexts);
+
+ for (auto& markedText : coalescedStyledMarkedTexts)
+ paintBackground(markedText);
+}
+
+void TextBoxPainter::paintForegroundAndDecorations()
+{
+ bool shouldPaintSelectionForeground = m_haveSelection && !m_useCustomUnderlines;
+ Vector<MarkedText> markedTexts;
+ if (m_paintInfo.phase != PaintPhase::Selection) {
+ auto selectableRange = m_textBox.selectableRange();
+
+ // The marked texts for the gaps between document markers and selection are implicitly created by subdividing the entire line.
+ markedTexts.append({ selectableRange.clamp(m_textBox.start()), selectableRange.clamp(m_textBox.end()), MarkedText::Unmarked });
+
+ if (!m_isPrinting) {
+ markedTexts.appendVector(MarkedText::collectForDocumentMarkers(m_renderer, selectableRange, MarkedText::PaintPhase::Foreground));
+ markedTexts.appendVector(MarkedText::collectForHighlights(m_renderer, selectableRange, MarkedText::PaintPhase::Foreground));
+
+ bool shouldPaintDraggedContent = !(m_paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection));
+ if (shouldPaintDraggedContent) {
+ auto markedTextsForDraggedContent = MarkedText::collectForDraggedContent(m_renderer, selectableRange);
+ if (!markedTextsForDraggedContent.isEmpty()) {
+ shouldPaintSelectionForeground = false;
+ markedTexts.appendVector(markedTextsForDraggedContent);
+ }
+ }
+ }
+ }
+ // The selection marked text acts as a placeholder when computing the marked texts for the gaps...
+ if (shouldPaintSelectionForeground) {
+ ASSERT(!m_isPrinting);
+ auto selectionMarkedText = createMarkedTextFromSelectionInBox(m_textBox);
+ if (!selectionMarkedText.isEmpty())
+ markedTexts.append(WTFMove(selectionMarkedText));
+ }
+
+ auto styledMarkedTexts = StyledMarkedText::subdivideAndResolve(markedTexts, m_renderer, m_textBox.isFirstLine(), m_paintInfo);
+
+ // ... now remove the selection marked text if we are excluding selection.
+ if (!m_isPrinting && m_paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection)) {
+ styledMarkedTexts.removeAllMatching([] (const StyledMarkedText& markedText) {
+ return markedText.type == MarkedText::Selection;
+ });
+ }
+
+ // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
+ auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualForeground(styledMarkedTexts);
+
+ for (auto& markedText : coalescedStyledMarkedTexts)
+ paintForeground(markedText);
+
+ auto textDecorations = m_textBox.lineStyle().textDecorationsInEffect();
+ bool highlightDecorations = !MarkedText::collectForHighlights(m_renderer, m_textBox.selectableRange(), MarkedText::PaintPhase::Decoration).isEmpty();
+ bool lineDecorations = !textDecorations.isEmpty();
+ if ((lineDecorations || highlightDecorations) && m_paintInfo.phase != PaintPhase::Selection) {
+ TextRun textRun = m_textBox.createTextRun();
+ unsigned length = textRun.length();
+ if (m_textBox.truncation() != cNoTruncation)
+ length = m_textBox.truncation();
+ unsigned selectionStart = 0;
+ unsigned selectionEnd = 0;
+ if (m_haveSelection)
+ std::tie(selectionStart, selectionEnd) = m_textBox.selectionStartEnd();
+
+ FloatRect textDecorationSelectionClipOutRect;
+ if ((m_paintInfo.paintBehavior.contains(PaintBehavior::ExcludeSelection)) && selectionStart < selectionEnd && selectionEnd <= length) {
+ textDecorationSelectionClipOutRect = m_paintRect;
+ float logicalWidthBeforeRange;
+ float logicalWidthAfterRange;
+ float logicalSelectionWidth = m_textBox.lineFont().widthOfTextRange(textRun, selectionStart, selectionEnd, nullptr, &logicalWidthBeforeRange, &logicalWidthAfterRange);
+ // FIXME: Do we need to handle vertical bottom to top text?
+ if (!m_textBox.isHorizontal()) {
+ textDecorationSelectionClipOutRect.move(0, logicalWidthBeforeRange);
+ textDecorationSelectionClipOutRect.setHeight(logicalSelectionWidth);
+ } else if (m_textBox.direction() == TextDirection::RTL) {
+ textDecorationSelectionClipOutRect.move(logicalWidthAfterRange, 0);
+ textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
+ } else {
+ textDecorationSelectionClipOutRect.move(logicalWidthBeforeRange, 0);
+ textDecorationSelectionClipOutRect.setWidth(logicalSelectionWidth);
+ }
+ }
+
+ // Coalesce styles of adjacent marked texts to minimize the number of drawing commands.
+ auto coalescedStyledMarkedTexts = StyledMarkedText::coalesceAdjacentWithEqualDecorations(styledMarkedTexts);
+
+ for (auto& markedText : coalescedStyledMarkedTexts)
+ paintDecoration(markedText, textDecorationSelectionClipOutRect);
+ }
+}
+
void TextBoxPainter::paintCompositionBackground()
{
auto selectableRange = m_textBox.selectableRange();
- auto& editor = m_textBox.renderer().frame().editor();
+ auto& editor = m_renderer.frame().editor();
if (!editor.compositionUsesCustomHighlights()) {
auto [clampedStart, clampedEnd] = selectableRange.clamp(editor.compositionStart(), editor.compositionEnd());
@@ -106,14 +265,14 @@
// 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 { m_textBox.renderer().style().isFlippedLinesWritingMode() ? selectionBottom - m_textBox.logicalBottom() : m_textBox.logicalTop() - selectionTop };
+ LayoutUnit deltaY { m_renderer.style().isFlippedLinesWritingMode() ? selectionBottom - m_textBox.logicalBottom() : m_textBox.logicalTop() - selectionTop };
LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
- LayoutRect selectionRect { LayoutUnit(m_boxRect.x()), LayoutUnit(m_boxRect.y() - deltaY), LayoutUnit(m_textBox.logicalWidth()), selectionHeight };
+ LayoutRect selectionRect { LayoutUnit(m_paintRect.x()), LayoutUnit(m_paintRect.y() - deltaY), LayoutUnit(m_textBox.logicalWidth()), selectionHeight };
m_textBox.lineFont().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
// FIXME: Support painting combined text. See <https://bugs.webkit.org/show_bug.cgi?id=180993>.
- auto backgroundRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_textBox.renderer().document().deviceScaleFactor(), textRun.ltr());
+ auto backgroundRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_document.deviceScaleFactor(), textRun.ltr());
if (backgroundStyle == BackgroundStyle::Rounded) {
backgroundRect.expand(-1, -1);
backgroundRect.move(0.5, 0.5);
@@ -161,7 +320,7 @@
context.setAlpha(markedText.style.alpha);
}
// TextPainter wants the box rectangle and text origin of the entire line box.
- textPainter.paintRange(textRun, m_boxRect, m_textBox.textOriginFromBoxRect(m_boxRect), markedText.startOffset, markedText.endOffset);
+ textPainter.paintRange(textRun, m_paintRect, m_textBox.textOriginFromBoxRect(m_paintRect), markedText.startOffset, markedText.endOffset);
}
void TextBoxPainter::paintDecoration(const StyledMarkedText& markedText, const FloatRect& clipOutRect)
@@ -177,7 +336,7 @@
bool isCombinedText = m_textBox.combinedText();
if (isCombinedText)
- context.concatCTM(rotation(m_boxRect, Clockwise));
+ context.concatCTM(rotation(m_paintRect, Clockwise));
// 1. Compute text selection
unsigned startOffset = markedText.startOffset;
@@ -190,17 +349,17 @@
TextRun textRun = m_textBox.createTextRun();
// Avoid measuring the text when the entire line box is selected as an optimization.
- FloatRect snappedSelectionRect = m_boxRect;
+ FloatRect snappedSelectionRect = m_paintRect;
if (startOffset || endOffset != textRun.length()) {
- LayoutRect selectionRect = { m_boxRect.x(), m_boxRect.y(), m_boxRect.width(), m_boxRect.height() };
+ LayoutRect selectionRect = { m_paintRect.x(), m_paintRect.y(), m_paintRect.width(), m_paintRect.height() };
font.adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
- snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_textBox.renderer().document().deviceScaleFactor(), textRun.ltr());
+ snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, m_document.deviceScaleFactor(), textRun.ltr());
}
// 2. Paint
auto textDecorations = lineStyle.textDecorationsInEffect();
textDecorations.add(TextDecorationPainter::textDecorationsInEffectForStyle(markedText.style.textDecorationStyles));
- TextDecorationPainter decorationPainter { context, textDecorations, m_textBox.renderer(), m_textBox.isFirstLine(), font, markedText.style.textDecorationStyles };
+ TextDecorationPainter decorationPainter { context, textDecorations, m_renderer, m_textBox.isFirstLine(), font, markedText.style.textDecorationStyles };
decorationPainter.setTextRunIterator(LayoutIntegration::textRunFor(&m_textBox));
decorationPainter.setWidth(snappedSelectionRect.width());
decorationPainter.setIsHorizontal(m_textBox.isHorizontal());
@@ -224,7 +383,7 @@
}
if (isCombinedText)
- context.concatCTM(rotation(m_boxRect, Counterclockwise));
+ context.concatCTM(rotation(m_paintRect, Counterclockwise));
}
void TextBoxPainter::paintCompositionUnderlines()
@@ -232,7 +391,7 @@
if (m_textBox.truncation() == cFullTruncation)
return;
- for (auto& underline : m_textBox.renderer().frame().editor().customCompositionUnderlines()) {
+ for (auto& underline : m_renderer.frame().editor().customCompositionUnderlines()) {
if (underline.endOffset <= m_textBox.start()) {
// Underline is completely before this run. This might be an underline that sits
// before the first run we draw, or underlines that were within runs we skipped
@@ -260,8 +419,6 @@
void TextBoxPainter::paintCompositionUnderline(const CompositionUnderline& underline)
{
- auto& renderer = m_textBox.renderer();
-
float start = 0; // start of line to draw, relative to tx
float width = m_textBox.logicalWidth(); // how much line to draw
bool useWholeWidth = true;
@@ -270,7 +427,7 @@
if (paintStart <= underline.startOffset) {
paintStart = underline.startOffset;
useWholeWidth = false;
- start = renderer.width(m_textBox.start(), paintStart - m_textBox.start(), m_textBox.textPos(), m_textBox.isFirstLine());
+ start = m_renderer.width(m_textBox.start(), paintStart - m_textBox.start(), m_textBox.textPos(), m_textBox.isFirstLine());
}
if (paintEnd != underline.endOffset) {
paintEnd = std::min(paintEnd, (unsigned)underline.endOffset);
@@ -281,7 +438,7 @@
useWholeWidth = false;
}
if (!useWholeWidth) {
- width = renderer.width(paintStart, paintEnd - paintStart, m_textBox.textPos() + start, m_textBox.isFirstLine());
+ width = m_renderer.width(paintStart, paintEnd - paintStart, m_textBox.textPos() + start, m_textBox.isFirstLine());
mirrorRTLSegment(m_textBox.logicalWidth(), m_textBox.direction(), start, width);
}
@@ -298,16 +455,18 @@
start += 1;
width -= 2;
+ auto& style = m_renderer.style();
+ Color underlineColor = underline.compositionUnderlineColor == CompositionUnderlineColor::TextColor ? style.visitedDependentColorWithColorFilter(CSSPropertyWebkitTextFillColor) : style.colorByApplyingColorFilter(underline.color);
+
GraphicsContext& context = m_paintInfo.context();
- Color underlineColor = underline.compositionUnderlineColor == CompositionUnderlineColor::TextColor ? renderer.style().visitedDependentColorWithColorFilter(CSSPropertyWebkitTextFillColor) : renderer.style().colorByApplyingColorFilter(underline.color);
context.setStrokeColor(underlineColor);
context.setStrokeThickness(lineThickness);
- context.drawLineForText(FloatRect(m_boxRect.x() + start, m_boxRect.y() + m_textBox.logicalHeight() - lineThickness, width, lineThickness), renderer.document().printing());
+ context.drawLineForText(FloatRect(m_paintRect.x() + start, m_paintRect.y() + m_textBox.logicalHeight() - lineThickness, width, lineThickness), m_document.printing());
}
void TextBoxPainter::paintPlatformDocumentMarkers()
{
- auto markedTexts = MarkedText::collectForDocumentMarkers(m_textBox.renderer(), m_textBox.selectableRange(), MarkedText::PaintPhase::Decoration);
+ auto markedTexts = MarkedText::collectForDocumentMarkers(m_renderer, m_textBox.selectableRange(), MarkedText::PaintPhase::Decoration);
for (auto& markedText : MarkedText::subdivide(markedTexts, MarkedText::OverlapStrategy::Frontmost))
paintPlatformDocumentMarker(markedText);
}
@@ -325,7 +484,7 @@
void TextBoxPainter::paintPlatformDocumentMarker(const MarkedText& markedText)
{
// Never print spelling/grammar markers (5327887)
- if (m_textBox.renderer().document().printing())
+ if (m_document.printing())
return;
if (m_textBox.truncation() == cFullTruncation)
@@ -334,7 +493,7 @@
auto bounds = calculateDocumentMarkerBounds(m_textBox, markedText);
auto lineStyleForMarkedTextType = [&]() -> DocumentMarkerLineStyle {
- bool shouldUseDarkAppearance = m_textBox.renderer().useDarkAppearance();
+ bool shouldUseDarkAppearance = m_renderer.useDarkAppearance();
switch (markedText.type) {
case MarkedText::SpellingError:
return { DocumentMarkerLineStyle::Mode::Spelling, shouldUseDarkAppearance };
@@ -355,10 +514,39 @@
}
};
- bounds.moveBy(m_boxRect.location());
+ bounds.moveBy(m_paintRect.location());
m_paintInfo.context().drawDotsForDocumentMarker(bounds, lineStyleForMarkedTextType());
}
+FloatRect TextBoxPainter::computePaintRect(const LegacyInlineTextBox& textBox, const LayoutPoint& paintOffset)
+{
+ FloatPoint localPaintOffset(paintOffset);
+
+ if (textBox.truncation() != cNoTruncation) {
+ auto& renderer = textBox.renderer();
+ if (renderer.containingBlock()->style().isLeftToRightDirection() != textBox.isLeftToRightDirection()) {
+ // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin
+ // at which we start drawing text.
+ // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is:
+ // |Hello|CBA| -> |...He|CBA|
+ // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing
+ // farther to the right.
+ // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the
+ // truncated string i.e. |Hello|CBA| -> |...lo|CBA|
+ LayoutUnit widthOfVisibleText { renderer.width(textBox.start(), textBox.truncation(), textBox.textPos(), textBox.isFirstLine()) };
+ LayoutUnit widthOfHiddenText { textBox.logicalWidth() - widthOfVisibleText };
+ LayoutSize truncationOffset(textBox.isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0_lu);
+ localPaintOffset.move(textBox.isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
+ }
+ }
+
+ localPaintOffset.move(0, textBox.lineStyle().isHorizontalWritingMode() ? 0 : -textBox.logicalHeight());
+
+ FloatPoint boxOrigin = textBox.locationIncludingFlipping();
+ boxOrigin.moveBy(localPaintOffset);
+ return { boxOrigin, FloatSize(textBox.logicalWidth(), textBox.logicalHeight()) };
+}
+
FloatRect TextBoxPainter::calculateDocumentMarkerBounds(const LegacyInlineTextBox& textBox, const MarkedText& markedText)
{
auto& font = textBox.lineFont();
Modified: trunk/Source/WebCore/rendering/TextBoxPainter.h (282310 => 282311)
--- trunk/Source/WebCore/rendering/TextBoxPainter.h 2021-09-11 14:59:45 UTC (rev 282310)
+++ trunk/Source/WebCore/rendering/TextBoxPainter.h 2021-09-11 16:10:13 UTC (rev 282311)
@@ -29,7 +29,9 @@
namespace WebCore {
class Color;
+class Document;
class LegacyInlineTextBox;
+class RenderText;
struct CompositionUnderline;
struct MarkedText;
struct PaintInfo;
@@ -37,29 +39,40 @@
class TextBoxPainter {
public:
- TextBoxPainter(const LegacyInlineTextBox&, PaintInfo&, const FloatRect& boxRect);
+ TextBoxPainter(const LegacyInlineTextBox&, PaintInfo&, const LayoutPoint& paintOffset);
~TextBoxPainter();
+ void paint();
+
+ static FloatRect calculateUnionOfAllDocumentMarkerBounds(const LegacyInlineTextBox&);
+
+private:
+ void paintBackground();
+ void paintForegroundAndDecorations();
void paintCompositionBackground();
- void paintBackground(const StyledMarkedText&);
- void paintForeground(const StyledMarkedText&);
- void paintDecoration(const StyledMarkedText&, const FloatRect& clipOutRect);
void paintCompositionUnderlines();
void paintPlatformDocumentMarkers();
- static FloatRect calculateUnionOfAllDocumentMarkerBounds(const LegacyInlineTextBox&);
-
-private:
enum class BackgroundStyle { Normal, Rounded };
void paintBackground(unsigned startOffset, unsigned endOffset, const Color&, BackgroundStyle = BackgroundStyle::Normal);
+ void paintBackground(const StyledMarkedText&);
+ void paintForeground(const StyledMarkedText&);
+ void paintDecoration(const StyledMarkedText&, const FloatRect& clipOutRect);
void paintCompositionUnderline(const CompositionUnderline&);
void paintPlatformDocumentMarker(const MarkedText&);
-
+
+ static FloatRect computePaintRect(const LegacyInlineTextBox&, const LayoutPoint& paintOffset);
static FloatRect calculateDocumentMarkerBounds(const LegacyInlineTextBox&, const MarkedText&);
const LegacyInlineTextBox& m_textBox;
+ const RenderText& m_renderer;
+ const Document& m_document;
PaintInfo& m_paintInfo;
- const FloatRect m_boxRect;
+ const FloatRect m_paintRect;
+ const bool m_isPrinting;
+ const bool m_haveSelection;
+ const bool m_containsComposition;
+ const bool m_useCustomUnderlines;
};
}