<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[269601] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/269601">269601</a></dd>
<dt>Author</dt> <dd>simon.fraser@apple.com</dd>
<dt>Date</dt> <dd>2020-11-09 13:54:39 -0800 (Mon, 09 Nov 2020)</dd>
</dl>

<h3>Log Message</h3>
<pre>[LFC Display] Implement background bleed avoidance, and border-radius clipping of backgrounds
https://bugs.webkit.org/show_bug.cgi?id=218713

Reviewed by Zalan Bujtas.

Make BoxDecorationPainter into a class so it can hold references to the box, rounded rect
etc, and settle on a policy of passing PaintingContext as an argument to all the painting
functions.

Add code to compute the BackgroundBleedAvoidance policy, and implement the various policies
following rendering code.

* display/css/DisplayBoxDecorationData.cpp:
(WebCore::Display::BorderEdge::obscuresBackgroundEdge const):
(WebCore::Display::BoxDecorationData::hasBorder const):
(WebCore::Display::BoxDecorationData::hasBorderRadius const):
(WebCore::Display::BoxDecorationData::borderObscuresBackground const):
(WebCore::Display::BoxDecorationData::borderObscuresBackgroundEdge const):
* display/css/DisplayBoxDecorationData.h:
(WebCore::Display::borderWidths):
* display/css/DisplayBoxDecorationPainter.cpp:
(WebCore::Display::BorderPainter::BorderPainter):
(WebCore::Display::roundedRectWithIncludedRadii):
(WebCore::Display::roundedInsetBorderForRect):
(WebCore::Display::BorderPainter::drawBoxSideFromPath const):
(WebCore::Display::BorderPainter::clipBorderSidePolygon const):
(WebCore::Display::BorderPainter::drawLineForBoxSide const):
(WebCore::Display::BorderPainter::paintOneBorderSide const):
(WebCore::Display::BorderPainter::paintBorderSides const):
(WebCore::Display::BorderPainter::paintTranslucentBorderSides const):
(WebCore::Display::shrinkRectByOneDevicePixel):
(WebCore::Display::BorderPainter::borderInnerRectAdjustedForBleedAvoidance const):
(WebCore::Display::BorderPainter::paintBorders const):
(WebCore::Display::BoxDecorationPainter::paintBorders const):
(WebCore::Display::BoxDecorationPainter::paintFillLayer const):
(WebCore::Display::BoxDecorationPainter::BoxDecorationPainter):
(WebCore::Display::BoxDecorationPainter::computeBorderRect):
(WebCore::Display::BoxDecorationPainter::paintBackgroundImages const):
(WebCore::Display::BoxDecorationPainter::backgroundRoundedRectAdjustedForBleedAvoidance const):
(WebCore::Display::BoxDecorationPainter::paintBackground const):
(WebCore::Display::BoxDecorationPainter::determineBackgroundBleedAvoidance):
(WebCore::Display::BoxDecorationPainter::paintBackgroundAndBorders const):
(): Deleted.
(WebCore::Display::BorderPainter::roundedBorderForRect const): Deleted.
(WebCore::Display::BorderPainter::roundedInsetBorderForRect const): Deleted.
(WebCore::Display::BoxDecorationPainter::paintBorders): Deleted.
(WebCore::Display::paintFillLayer): Deleted.
(WebCore::Display::BoxDecorationPainter::paintBackgroundImages): Deleted.
(WebCore::Display::BoxDecorationPainter::paintBackground): Deleted.
(WebCore::Display::BoxDecorationPainter::paintBackgroundAndBorders): Deleted.
* display/css/DisplayBoxDecorationPainter.h:
(WebCore::Display::BoxDecorationPainter::borderRoundedRect const):
(WebCore::Display::BoxDecorationPainter::includeLeftEdge const):
(WebCore::Display::BoxDecorationPainter::includeRightEdge const):
* display/css/DisplayBoxPainter.cpp:
(WebCore::Display::BoxPainter::paintBoxDecorations):
* display/css/DisplayStyle.cpp:
(WebCore::Display::Style::backgroundHasOpaqueTopLayer const):
* display/css/DisplayStyle.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayBoxDecorationDatacpp">trunk/Source/WebCore/display/css/DisplayBoxDecorationData.cpp</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayBoxDecorationDatah">trunk/Source/WebCore/display/css/DisplayBoxDecorationData.h</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayBoxDecorationPaintercpp">trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.cpp</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayBoxDecorationPainterh">trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.h</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayBoxPaintercpp">trunk/Source/WebCore/display/css/DisplayBoxPainter.cpp</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayStylecpp">trunk/Source/WebCore/display/css/DisplayStyle.cpp</a></li>
<li><a href="#trunkSourceWebCoredisplaycssDisplayStyleh">trunk/Source/WebCore/display/css/DisplayStyle.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/ChangeLog      2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -1,3 +1,65 @@
</span><ins>+2020-11-09  Simon Fraser  <simon.fraser@apple.com>
+
+        [LFC Display] Implement background bleed avoidance, and border-radius clipping of backgrounds
+        https://bugs.webkit.org/show_bug.cgi?id=218713
+
+        Reviewed by Zalan Bujtas.
+
+        Make BoxDecorationPainter into a class so it can hold references to the box, rounded rect
+        etc, and settle on a policy of passing PaintingContext as an argument to all the painting
+        functions.
+        
+        Add code to compute the BackgroundBleedAvoidance policy, and implement the various policies
+        following rendering code.
+
+        * display/css/DisplayBoxDecorationData.cpp:
+        (WebCore::Display::BorderEdge::obscuresBackgroundEdge const):
+        (WebCore::Display::BoxDecorationData::hasBorder const):
+        (WebCore::Display::BoxDecorationData::hasBorderRadius const):
+        (WebCore::Display::BoxDecorationData::borderObscuresBackground const):
+        (WebCore::Display::BoxDecorationData::borderObscuresBackgroundEdge const):
+        * display/css/DisplayBoxDecorationData.h:
+        (WebCore::Display::borderWidths):
+        * display/css/DisplayBoxDecorationPainter.cpp:
+        (WebCore::Display::BorderPainter::BorderPainter):
+        (WebCore::Display::roundedRectWithIncludedRadii):
+        (WebCore::Display::roundedInsetBorderForRect):
+        (WebCore::Display::BorderPainter::drawBoxSideFromPath const):
+        (WebCore::Display::BorderPainter::clipBorderSidePolygon const):
+        (WebCore::Display::BorderPainter::drawLineForBoxSide const):
+        (WebCore::Display::BorderPainter::paintOneBorderSide const):
+        (WebCore::Display::BorderPainter::paintBorderSides const):
+        (WebCore::Display::BorderPainter::paintTranslucentBorderSides const):
+        (WebCore::Display::shrinkRectByOneDevicePixel):
+        (WebCore::Display::BorderPainter::borderInnerRectAdjustedForBleedAvoidance const):
+        (WebCore::Display::BorderPainter::paintBorders const):
+        (WebCore::Display::BoxDecorationPainter::paintBorders const):
+        (WebCore::Display::BoxDecorationPainter::paintFillLayer const):
+        (WebCore::Display::BoxDecorationPainter::BoxDecorationPainter):
+        (WebCore::Display::BoxDecorationPainter::computeBorderRect):
+        (WebCore::Display::BoxDecorationPainter::paintBackgroundImages const):
+        (WebCore::Display::BoxDecorationPainter::backgroundRoundedRectAdjustedForBleedAvoidance const):
+        (WebCore::Display::BoxDecorationPainter::paintBackground const):
+        (WebCore::Display::BoxDecorationPainter::determineBackgroundBleedAvoidance):
+        (WebCore::Display::BoxDecorationPainter::paintBackgroundAndBorders const):
+        (): Deleted.
+        (WebCore::Display::BorderPainter::roundedBorderForRect const): Deleted.
+        (WebCore::Display::BorderPainter::roundedInsetBorderForRect const): Deleted.
+        (WebCore::Display::BoxDecorationPainter::paintBorders): Deleted.
+        (WebCore::Display::paintFillLayer): Deleted.
+        (WebCore::Display::BoxDecorationPainter::paintBackgroundImages): Deleted.
+        (WebCore::Display::BoxDecorationPainter::paintBackground): Deleted.
+        (WebCore::Display::BoxDecorationPainter::paintBackgroundAndBorders): Deleted.
+        * display/css/DisplayBoxDecorationPainter.h:
+        (WebCore::Display::BoxDecorationPainter::borderRoundedRect const):
+        (WebCore::Display::BoxDecorationPainter::includeLeftEdge const):
+        (WebCore::Display::BoxDecorationPainter::includeRightEdge const):
+        * display/css/DisplayBoxPainter.cpp:
+        (WebCore::Display::BoxPainter::paintBoxDecorations):
+        * display/css/DisplayStyle.cpp:
+        (WebCore::Display::Style::backgroundHasOpaqueTopLayer const):
+        * display/css/DisplayStyle.h:
+
</ins><span class="cx"> 2020-11-09  Zalan Bujtas  <zalan@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [LFC][Integration] Enable inline image support
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayBoxDecorationDatacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayBoxDecorationData.cpp (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayBoxDecorationData.cpp    2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayBoxDecorationData.cpp       2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -55,6 +55,20 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool BorderEdge::obscuresBackgroundEdge(float scale) const
+{
+    if (!m_isPresent || m_isTransparent || (m_width * scale) < floorToDevicePixel(2, scale) || !m_color.isOpaque() || m_style == BorderStyle::Hidden)
+        return false;
+
+    if (m_style == BorderStyle::Dotted || m_style == BorderStyle::Dashed)
+        return false;
+
+    if (m_style == BorderStyle::Double)
+        return m_width >= scale * floorToDevicePixel(5, scale); // The outer band needs to be >= 2px wide at unit scale.
+
+    return true;
+}
+
</ins><span class="cx"> RectEdges<BorderEdge> calculateBorderEdges(const RenderStyle& style, float pixelSnappingFactor, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
</span><span class="cx"> {
</span><span class="cx">     bool horizontal = style.isHorizontalWritingMode();
</span><span class="lines">@@ -97,7 +111,54 @@
</span><span class="cx"> 
</span><span class="cx"> BoxDecorationData::BoxDecorationData() = default;
</span><span class="cx"> 
</span><ins>+bool BoxDecorationData::hasBorder() const
+{
+    // FIXME: There's a tricky interaction between borders and border-image here: webkit.org/b/217900.
+    if (hasBorderImage())
+        return true;
</ins><span class="cx"> 
</span><ins>+    for (auto side : allBoxSides) {
+        auto& edge = m_borderEdges[side];
+        if (edge.style() != BorderStyle::None && edge.width())
+            return true;
+    }
+    return false;
+}
+
+bool BoxDecorationData::hasBorderRadius() const
+{
+    return m_borderRadii && !m_borderRadii->isZero();
+}
+
+bool BoxDecorationData::borderObscuresBackground() const
+{
+    if (!hasBorder())
+        return false;
+
+    // Bail if we have any border-image for now. We could look at the image alpha to improve this.
+    // FIXME: Check border-image.
+
+    for (auto side : allBoxSides) {
+        if (!m_borderEdges.at(side).obscuresBackground())
+            return false;
+    }
+
+    return true;
+}
+
+bool BoxDecorationData::borderObscuresBackgroundEdge(const FloatSize& contextScale) const
+{
+    for (auto side : allBoxSides) {
+        auto& currEdge = m_borderEdges.at(side);
+        // FIXME: for vertical text
+        float axisScale = (side == BoxSide::Top || side == BoxSide::Bottom) ? contextScale.height() : contextScale.width();
+        if (!currEdge.obscuresBackgroundEdge(axisScale))
+            return false;
+    }
+
+    return true;
+}
+
</ins><span class="cx"> } // namespace Display
</span><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayBoxDecorationDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayBoxDecorationData.h (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayBoxDecorationData.h      2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayBoxDecorationData.h 2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -65,6 +65,7 @@
</span><span class="cx">     float outerWidth() const { return m_outerWidth; }
</span><span class="cx"> 
</span><span class="cx">     bool obscuresBackground() const;
</span><ins>+    bool obscuresBackgroundEdge(float scale) const;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     Color m_color;
</span><span class="lines">@@ -80,6 +81,16 @@
</span><span class="cx"> 
</span><span class="cx"> std::pair<BoxSide, BoxSide> adjacentSidesForSide(BoxSide);
</span><span class="cx"> 
</span><ins>+inline RectEdges<float> borderWidths(const RectEdges<BorderEdge>& edges)
+{
+    return {
+        edges.top().width(),
+        edges.right().width(),
+        edges.bottom().width(),
+        edges.left().width()
+    };
+}
+
</ins><span class="cx"> // Per-box data with pixel-snapped geometry for background images and border-radius.
</span><span class="cx"> class BoxDecorationData {
</span><span class="cx">     WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(BoxDecorationData);
</span><span class="lines">@@ -96,7 +107,12 @@
</span><span class="cx">     void setBorderRadii(std::unique_ptr<FloatRoundedRect::Radii>&& radii) { m_borderRadii = WTFMove(radii); }
</span><span class="cx"> 
</span><span class="cx">     bool hasBorderImage() const { return false; } // FIXME: Implement border-image.
</span><ins>+    bool hasBorder() const;
+    bool hasBorderRadius() const;
</ins><span class="cx"> 
</span><ins>+    bool borderObscuresBackground() const;
+    bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const;
+
</ins><span class="cx"> private:
</span><span class="cx">     Vector<FillLayerImageGeometry, 1> m_backgroundImageGeometry;
</span><span class="cx">     RectEdges<BorderEdge> m_borderEdges;
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayBoxDecorationPaintercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.cpp (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.cpp 2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.cpp    2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -46,16 +46,16 @@
</span><span class="cx"> 
</span><span class="cx"> class BorderPainter {
</span><span class="cx"> public:
</span><del>-    BorderPainter(const RectEdges<BorderEdge>& edges, PaintingContext& context, const FloatRoundedRect& borderRect, bool includeLeftEdge, bool includeRightEdge)
</del><ins>+    BorderPainter(const RectEdges<BorderEdge>& edges, const FloatRoundedRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, bool includeLeftEdge, bool includeRightEdge)
</ins><span class="cx">         : m_edges(edges)
</span><del>-        , m_paintingContext(context)
</del><span class="cx">         , m_borderRect(borderRect)
</span><ins>+        , m_bleedAvoidance(bleedAvoidance)
</ins><span class="cx">         , m_includeLeftEdge(includeLeftEdge)
</span><span class="cx">         , m_includeRightEdge(includeRightEdge)
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    void paintBorders() const;
</del><ins>+    void paintBorders(PaintingContext&) const;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     static bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge)
</span><span class="lines">@@ -93,33 +93,61 @@
</span><span class="cx">     static FloatRoundedRect calculateAdjustedInnerBorder(const FloatRoundedRect& innerBorder, BoxSide);
</span><span class="cx">     static void calculateBorderStyleColor(BorderStyle, BoxSide, Color&);
</span><span class="cx"> 
</span><ins>+    FloatRect borderInnerRectAdjustedForBleedAvoidance(const PaintingContext&) const;
+
</ins><span class="cx">     bool colorsMatchAtCorner(BoxSide, BoxSide adjacentSide) const;
</span><span class="cx">     bool colorNeedsAntiAliasAtCorner(BoxSide, BoxSide adjacentSide) const;
</span><span class="cx">     bool willBeOverdrawn(BoxSide, BoxSide adjacentSide) const;
</span><span class="cx">     bool joinRequiresMitre(BoxSide, BoxSide adjacentSide, bool allowOverdraw) const;
</span><span class="cx"> 
</span><del>-    FloatRoundedRect roundedBorderForRect(const FloatRect&) const;
-    FloatRoundedRect roundedInsetBorderForRect(const FloatRect&, const RectEdges<float>& borderWidth) const;
</del><ins>+    void clipBorderSidePolygon(PaintingContext&, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSide, bool firstEdgeMatches, bool secondEdgeMatches) const;
</ins><span class="cx"> 
</span><del>-    void clipBorderSidePolygon(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSide, bool firstEdgeMatches, bool secondEdgeMatches) const;
</del><ins>+    void drawLineForBoxSide(PaintingContext&, const FloatRect&, BoxSide, Color, BorderStyle, float adjacentWidth1, float adjacentWidth2, bool antialias) const;
+    void drawBoxSideFromPath(PaintingContext&, const FloatRect& borderRect, const Path& borderPath, float thickness, float drawThickness, BoxSide, Color, BorderStyle) const;
</ins><span class="cx"> 
</span><del>-    void drawLineForBoxSide(const FloatRect&, BoxSide, Color, BorderStyle, float adjacentWidth1, float adjacentWidth2, bool antialias) const;
-    void drawBoxSideFromPath(const FloatRect& borderRect, const Path& borderPath, float thickness, float drawThickness, BoxSide, Color, BorderStyle) const;
</del><ins>+    void paintOneBorderSide(PaintingContext&, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, const FloatRect& sideRect, BoxSide, const Path*, bool antialias, const Color* overrideColor) const;
+    void paintBorderSides(PaintingContext&, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, FloatSize innerBorderBleedAdjustment, BoxSideSet, bool antialias, const Color* overrideColor = nullptr) const;
+    void paintTranslucentBorderSides(PaintingContext&, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, FloatSize innerBorderBleedAdjustment, BoxSideSet, bool antialias) const;
</ins><span class="cx"> 
</span><del>-    void paintOneBorderSide(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, const FloatRect& sideRect, BoxSide, const Path*, bool antialias, const Color* overrideColor) const;
-    void paintBorderSides(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSideSet, bool antialias, const Color* overrideColor = nullptr) const;
-    void paintTranslucentBorderSides(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSideSet, bool antialias) const;
-
</del><span class="cx">     const RectEdges<BorderEdge>& m_edges;
</span><del>-    PaintingContext& m_paintingContext;
-    
-    FloatRoundedRect m_borderRect;
-    
-    bool m_includeLeftEdge { true };
-    bool m_includeRightEdge { true };
</del><ins>+    const FloatRoundedRect m_borderRect;
+    const BackgroundBleedAvoidance m_bleedAvoidance;
+    const bool m_includeLeftEdge;
+    const bool m_includeRightEdge;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><ins>+static FloatRoundedRect roundedRectWithIncludedRadii(const FloatRect& rect, const FloatRoundedRect::Radii& radii, bool includeLeftEdge, bool includeRightEdge)
+{
+    FloatRoundedRect::Radii resultRadii;
</ins><span class="cx"> 
</span><ins>+    if (includeLeftEdge) {
+        resultRadii.setTopLeft(radii.topLeft());
+        resultRadii.setBottomLeft(radii.bottomLeft());
+    }
+
+    if (includeRightEdge) {
+        resultRadii.setTopRight(radii.topRight());
+        resultRadii.setBottomRight(radii.bottomRight());
+    }
+
+    resultRadii.scale(calcBorderRadiiConstraintScaleFor(rect, resultRadii));
+    return FloatRoundedRect { rect, resultRadii };
+}
+
+static FloatRoundedRect roundedInsetBorderForRect(const FloatRect& borderRect, const FloatRoundedRect::Radii& radii, const RectEdges<float>& borderWidth, bool includeLeftEdge, bool includeRightEdge)
+{
+    auto insetRect = FloatRect { borderRect.x() + borderWidth.left(), borderRect.y() + borderWidth.top(),
+        borderRect.width() - borderWidth.left() - borderWidth.right(), borderRect.height() - borderWidth.top() - borderWidth.bottom() };
+
+    if (!radii.isZero()) {
+        auto adjustedRadii = radii;
+        adjustedRadii.shrink(borderWidth.top(), borderWidth.bottom(), borderWidth.left(), borderWidth.right());
+        return roundedRectWithIncludedRadii(insetRect, adjustedRadii, includeLeftEdge, includeRightEdge);
+    }
+
+    return FloatRoundedRect { insetRect, { } };
+}
+
</ins><span class="cx"> // BorderStyle::Outset darkens the bottom and right (and maybe lightens the top and left)
</span><span class="cx"> // BorderStyle::Inset darkens the top and left (and maybe lightens the bottom and right)
</span><span class="cx"> bool BorderPainter::borderStyleHasUnmatchedColorsAtCorner(BorderStyle style, BoxSide side, BoxSide adjacentSide)
</span><span class="lines">@@ -330,49 +358,8 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static FloatRoundedRect roundedRectWithIncludedRadii(const FloatRect& rect, const FloatRoundedRect::Radii& radii, bool includeLeftEdge, bool includeRightEdge)
</del><ins>+void BorderPainter::drawBoxSideFromPath(PaintingContext& paintingContext, const FloatRect& borderRect, const Path& borderPath, float thickness, float drawThickness, BoxSide side, Color color, BorderStyle borderStyle) const
</ins><span class="cx"> {
</span><del>-    FloatRoundedRect::Radii resultRadii;
-
-    if (includeLeftEdge) {
-        resultRadii.setTopLeft(radii.topLeft());
-        resultRadii.setBottomLeft(radii.bottomLeft());
-    }
-
-    if (includeRightEdge) {
-        resultRadii.setTopRight(radii.topRight());
-        resultRadii.setBottomRight(radii.bottomRight());
-    }
-
-    resultRadii.scale(calcBorderRadiiConstraintScaleFor(rect, resultRadii));
-    return FloatRoundedRect { rect, resultRadii };
-}
-
-FloatRoundedRect BorderPainter::roundedBorderForRect(const FloatRect& borderRect) const
-{
-    if (m_borderRect.isRounded()) {
-        auto radii = m_borderRect.radii();
-        return roundedRectWithIncludedRadii(borderRect, radii, m_includeLeftEdge, m_includeRightEdge);
-    }
-    return FloatRoundedRect { borderRect };
-}
-
-FloatRoundedRect BorderPainter::roundedInsetBorderForRect(const FloatRect& borderRect, const RectEdges<float>& borderWidth) const
-{
-    auto insetRect = FloatRect { borderRect.x() + borderWidth.left(), borderRect.y() + borderWidth.top(),
-        borderRect.width() - borderWidth.left() - borderWidth.right(), borderRect.height() - borderWidth.top() - borderWidth.bottom() };
-
-    if (m_borderRect.isRounded()) {
-        auto radii = m_borderRect.radii();
-        radii.shrink(borderWidth.top(), borderWidth.bottom(), borderWidth.left(), borderWidth.right());
-        return roundedRectWithIncludedRadii(insetRect, radii, m_includeLeftEdge, m_includeRightEdge);
-    }
-
-    return FloatRoundedRect { insetRect, { } };
-}
-
-void BorderPainter::drawBoxSideFromPath(const FloatRect& borderRect, const Path& borderPath, float thickness, float drawThickness, BoxSide side, Color color, BorderStyle borderStyle) const
-{
</del><span class="cx">     if (thickness <= 0)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -385,14 +372,14 @@
</span><span class="cx">         return;
</span><span class="cx">     case BorderStyle::Dotted:
</span><span class="cx">     case BorderStyle::Dashed: {
</span><del>-        m_paintingContext.context.setStrokeColor(color);
</del><ins>+        paintingContext.context.setStrokeColor(color);
</ins><span class="cx"> 
</span><span class="cx">         // The stroke is doubled here because the provided path is the
</span><span class="cx">         // outside edge of the border so half the stroke is clipped off.
</span><span class="cx">         // The extra multiplier is so that the clipping mask can antialias
</span><span class="cx">         // the edges to prevent jaggies.
</span><del>-        m_paintingContext.context.setStrokeThickness(drawThickness * 2 * 1.1f);
-        m_paintingContext.context.setStrokeStyle(borderStyle == BorderStyle::Dashed ? DashedStroke : DottedStroke);
</del><ins>+        paintingContext.context.setStrokeThickness(drawThickness * 2 * 1.1f);
+        paintingContext.context.setStrokeStyle(borderStyle == BorderStyle::Dashed ? DashedStroke : DottedStroke);
</ins><span class="cx"> 
</span><span class="cx">         // If the number of dashes that fit in the path is odd and non-integral then we
</span><span class="cx">         // will have an awkwardly-sized dash at the end of the path. To try to avoid that
</span><span class="lines">@@ -415,13 +402,13 @@
</span><span class="cx">             DashArray lineDash;
</span><span class="cx">             lineDash.append(dashLength);
</span><span class="cx">             lineDash.append(gapLength);
</span><del>-            m_paintingContext.context.setLineDash(lineDash, dashLength);
</del><ins>+            paintingContext.context.setLineDash(lineDash, dashLength);
</ins><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         // FIXME: stroking the border path causes issues with tight corners:
</span><span class="cx">         // https://bugs.webkit.org/show_bug.cgi?id=58711
</span><span class="cx">         // Also, to get the best appearance we should stroke a path between the two borders.
</span><del>-        m_paintingContext.context.strokePath(borderPath);
</del><ins>+        paintingContext.context.strokePath(borderPath);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     case BorderStyle::Double: {
</span><span class="lines">@@ -441,19 +428,28 @@
</span><span class="cx"> 
</span><span class="cx">         // Draw inner border line
</span><span class="cx">         {
</span><del>-            GraphicsContextStateSaver stateSaver(m_paintingContext.context);
-            auto innerClip = roundedInsetBorderForRect(borderRect, { innerBorderTopWidth, innerBorderRightWidth, innerBorderBottomWidth, innerBorderLeftWidth });
</del><ins>+            GraphicsContextStateSaver stateSaver(paintingContext.context);
+            auto innerClip = roundedInsetBorderForRect(borderRect, m_borderRect.radii(), { innerBorderTopWidth, innerBorderRightWidth, innerBorderBottomWidth, innerBorderLeftWidth }, m_includeLeftEdge, m_includeRightEdge);
</ins><span class="cx">             
</span><del>-            m_paintingContext.context.clipRoundedRect(innerClip);
-            drawBoxSideFromPath(borderRect, borderPath, thickness, drawThickness, side, color, BorderStyle::Solid);
</del><ins>+            paintingContext.context.clipRoundedRect(innerClip);
+            drawBoxSideFromPath(paintingContext, borderRect, borderPath, thickness, drawThickness, side, color, BorderStyle::Solid);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         // Draw outer border line
</span><span class="cx">         {
</span><del>-            GraphicsContextStateSaver stateSaver(m_paintingContext.context);
-            auto outerClip = roundedInsetBorderForRect(borderRect, { outerBorderTopWidth, outerBorderRightWidth, outerBorderBottomWidth, outerBorderLeftWidth });
-            m_paintingContext.context.clipOutRoundedRect(outerClip);
-            drawBoxSideFromPath(borderRect, borderPath, thickness, drawThickness, side, color, BorderStyle::Solid);
</del><ins>+            GraphicsContextStateSaver stateSaver(paintingContext.context);
+            auto outerRect = borderRect;
+            if (m_bleedAvoidance == BackgroundBleedAvoidance::UseTransparencyLayer) {
+                outerRect.inflate(1);
+                ++outerBorderTopWidth;
+                ++outerBorderBottomWidth;
+                ++outerBorderLeftWidth;
+                ++outerBorderRightWidth;
+            }
+
+            auto outerClip = roundedInsetBorderForRect(outerRect, m_borderRect.radii(), { outerBorderTopWidth, outerBorderRightWidth, outerBorderBottomWidth, outerBorderLeftWidth }, m_includeLeftEdge, m_includeRightEdge);
+            paintingContext.context.clipOutRoundedRect(outerClip);
+            drawBoxSideFromPath(paintingContext, borderRect, borderPath, thickness, drawThickness, side, color, BorderStyle::Solid);
</ins><span class="cx">         }
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -471,10 +467,10 @@
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         // Paint full border
</span><del>-        drawBoxSideFromPath(borderRect, borderPath, thickness, drawThickness, side, color, s1);
</del><ins>+        drawBoxSideFromPath(paintingContext, borderRect, borderPath, thickness, drawThickness, side, color, s1);
</ins><span class="cx"> 
</span><span class="cx">         // Paint inner only
</span><del>-        GraphicsContextStateSaver stateSaver(m_paintingContext.context);
</del><ins>+        GraphicsContextStateSaver stateSaver(paintingContext.context);
</ins><span class="cx">         // FIXME: Should have pixel snapped these at display tree generation time.
</span><span class="cx">         // FIXME: RectEdges
</span><span class="cx">         float topWidth { m_edges.top().widthForPainting() / 2 };
</span><span class="lines">@@ -482,10 +478,10 @@
</span><span class="cx">         float leftWidth { m_edges.left().widthForPainting() / 2 };
</span><span class="cx">         float rightWidth { m_edges.right().widthForPainting() / 2 };
</span><span class="cx"> 
</span><del>-        auto clipRect = roundedInsetBorderForRect(borderRect, { topWidth, rightWidth, bottomWidth, leftWidth });
</del><ins>+        auto clipRect = roundedInsetBorderForRect(borderRect, m_borderRect.radii(), { topWidth, rightWidth, bottomWidth, leftWidth }, m_includeLeftEdge, m_includeRightEdge);
</ins><span class="cx"> 
</span><del>-        m_paintingContext.context.clipRoundedRect(clipRect);
-        drawBoxSideFromPath(borderRect, borderPath, thickness, drawThickness, side, color, s2);
</del><ins>+        paintingContext.context.clipRoundedRect(clipRect);
+        drawBoxSideFromPath(paintingContext, borderRect, borderPath, thickness, drawThickness, side, color, s2);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     case BorderStyle::Inset:
</span><span class="lines">@@ -496,12 +492,12 @@
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    m_paintingContext.context.setStrokeStyle(NoStroke);
-    m_paintingContext.context.setFillColor(color);
-    m_paintingContext.context.drawRect(borderRect);
</del><ins>+    paintingContext.context.setStrokeStyle(NoStroke);
+    paintingContext.context.setFillColor(color);
+    paintingContext.context.drawRect(borderRect);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::clipBorderSidePolygon(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches) const
</del><ins>+void BorderPainter::clipBorderSidePolygon(PaintingContext& paintingContext, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches) const
</ins><span class="cx"> {
</span><span class="cx">     const FloatRect& outerRect = outerBorder.rect();
</span><span class="cx">     const FloatRect& innerRect = innerBorder.rect();
</span><span class="lines">@@ -579,10 +575,10 @@
</span><span class="cx">     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
</span><span class="cx">     // if neither side matches, anti-alias the clip.
</span><span class="cx">     if (firstEdgeMatches == secondEdgeMatches) {
</span><del>-        bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-        m_paintingContext.context.setShouldAntialias(!firstEdgeMatches);
-        m_paintingContext.context.clipPath(Path::polygonPathFromPoints(quad), WindRule::NonZero);
-        m_paintingContext.context.setShouldAntialias(wasAntialiased);
</del><ins>+        bool wasAntialiased = paintingContext.context.shouldAntialias();
+        paintingContext.context.setShouldAntialias(!firstEdgeMatches);
+        paintingContext.context.clipPath(Path::polygonPathFromPoints(quad), WindRule::NonZero);
+        paintingContext.context.setShouldAntialias(wasAntialiased);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -594,9 +590,9 @@
</span><span class="cx">         side == BoxSide::Top || side == BoxSide::Bottom ? FloatPoint(quad[3].x(), quad[2].y()) : FloatPoint(quad[2].x(), quad[3].y()),
</span><span class="cx">         quad[3]
</span><span class="cx">     };
</span><del>-    bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-    m_paintingContext.context.setShouldAntialias(!firstEdgeMatches);
-    m_paintingContext.context.clipPath(Path::polygonPathFromPoints(firstQuad), WindRule::NonZero);
</del><ins>+    bool wasAntialiased = paintingContext.context.shouldAntialias();
+    paintingContext.context.setShouldAntialias(!firstEdgeMatches);
+    paintingContext.context.clipPath(Path::polygonPathFromPoints(firstQuad), WindRule::NonZero);
</ins><span class="cx"> 
</span><span class="cx">     Vector<FloatPoint> secondQuad = {
</span><span class="cx">         quad[0],
</span><span class="lines">@@ -606,29 +602,29 @@
</span><span class="cx">         quad[3]
</span><span class="cx">     };
</span><span class="cx">     // Antialiasing affects the second side.
</span><del>-    m_paintingContext.context.setShouldAntialias(!secondEdgeMatches);
-    m_paintingContext.context.clipPath(Path::polygonPathFromPoints(secondQuad), WindRule::NonZero);
</del><ins>+    paintingContext.context.setShouldAntialias(!secondEdgeMatches);
+    paintingContext.context.clipPath(Path::polygonPathFromPoints(secondQuad), WindRule::NonZero);
</ins><span class="cx"> 
</span><del>-    m_paintingContext.context.setShouldAntialias(wasAntialiased);
</del><ins>+    paintingContext.context.setShouldAntialias(wasAntialiased);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::drawLineForBoxSide(const FloatRect& rect, BoxSide side, Color color, BorderStyle borderStyle, float adjacentWidth1, float adjacentWidth2, bool antialias) const
</del><ins>+void BorderPainter::drawLineForBoxSide(PaintingContext& paintingContext, const FloatRect& rect, BoxSide side, Color color, BorderStyle borderStyle, float adjacentWidth1, float adjacentWidth2, bool antialias) const
</ins><span class="cx"> {
</span><span class="cx">     auto drawBorderRect = [&](const FloatRect& rect) {
</span><span class="cx">         if (rect.isEmpty())
</span><span class="cx">             return;
</span><del>-        m_paintingContext.context.drawRect(rect);
</del><ins>+        paintingContext.context.drawRect(rect);
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     auto drawLineFor = [&](const FloatRect& rect, BoxSide side, BorderStyle borderStyle, const FloatSize& adjacent) {
</span><span class="cx">         if (rect.isEmpty())
</span><span class="cx">             return;
</span><del>-        drawLineForBoxSide(rect, side, color, borderStyle, adjacent.width(), adjacent.height(), antialias);
</del><ins>+        drawLineForBoxSide(paintingContext, rect, side, color, borderStyle, adjacent.width(), adjacent.height(), antialias);
</ins><span class="cx">     };
</span><span class="cx">     
</span><span class="cx">     auto& edge = m_edges.at(side);
</span><span class="cx"> 
</span><del>-    const float deviceScaleFactor = m_paintingContext.deviceScaleFactor;
</del><ins>+    const float deviceScaleFactor = paintingContext.deviceScaleFactor;
</ins><span class="cx">     float x1 = rect.x();
</span><span class="cx">     float x2 = rect.maxX();
</span><span class="cx">     float y1 = rect.y();
</span><span class="lines">@@ -653,26 +649,26 @@
</span><span class="cx">         return;
</span><span class="cx">     case BorderStyle::Dotted:
</span><span class="cx">     case BorderStyle::Dashed: {
</span><del>-        bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-        StrokeStyle oldStrokeStyle = m_paintingContext.context.strokeStyle();
-        m_paintingContext.context.setShouldAntialias(antialias);
-        m_paintingContext.context.setStrokeColor(color);
-        m_paintingContext.context.setStrokeThickness(thickness);
-        m_paintingContext.context.setStrokeStyle(borderStyle == BorderStyle::Dashed ? DashedStroke : DottedStroke);
-        m_paintingContext.context.drawLine({ x1, y1 }, { x2, y2 });
-        m_paintingContext.context.setShouldAntialias(wasAntialiased);
-        m_paintingContext.context.setStrokeStyle(oldStrokeStyle);
</del><ins>+        bool wasAntialiased = paintingContext.context.shouldAntialias();
+        StrokeStyle oldStrokeStyle = paintingContext.context.strokeStyle();
+        paintingContext.context.setShouldAntialias(antialias);
+        paintingContext.context.setStrokeColor(color);
+        paintingContext.context.setStrokeThickness(thickness);
+        paintingContext.context.setStrokeStyle(borderStyle == BorderStyle::Dashed ? DashedStroke : DottedStroke);
+        paintingContext.context.drawLine({ x1, y1 }, { x2, y2 });
+        paintingContext.context.setShouldAntialias(wasAntialiased);
+        paintingContext.context.setStrokeStyle(oldStrokeStyle);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case BorderStyle::Double: {
</span><span class="cx">         float thirdOfThickness = ceilToDevicePixel(thickness / 3, deviceScaleFactor);
</span><span class="cx">         if (!adjacentWidth1 && !adjacentWidth2) {
</span><del>-            StrokeStyle oldStrokeStyle = m_paintingContext.context.strokeStyle();
-            m_paintingContext.context.setStrokeStyle(NoStroke);
-            m_paintingContext.context.setFillColor(color);
</del><ins>+            StrokeStyle oldStrokeStyle = paintingContext.context.strokeStyle();
+            paintingContext.context.setStrokeStyle(NoStroke);
+            paintingContext.context.setFillColor(color);
</ins><span class="cx"> 
</span><del>-            bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-            m_paintingContext.context.setShouldAntialias(antialias);
</del><ins>+            bool wasAntialiased = paintingContext.context.shouldAntialias();
+            paintingContext.context.setShouldAntialias(antialias);
</ins><span class="cx"> 
</span><span class="cx">             switch (side) {
</span><span class="cx">             case BoxSide::Top:
</span><span class="lines">@@ -693,8 +689,8 @@
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            m_paintingContext.context.setShouldAntialias(wasAntialiased);
-            m_paintingContext.context.setStrokeStyle(oldStrokeStyle);
</del><ins>+            paintingContext.context.setShouldAntialias(wasAntialiased);
+            paintingContext.context.setStrokeStyle(oldStrokeStyle);
</ins><span class="cx">         } else {
</span><span class="cx">             float adjacent1BigThird = ceilToDevicePixel(adjacentWidth1 / 3, deviceScaleFactor);
</span><span class="cx">             float adjacent2BigThird = ceilToDevicePixel(adjacentWidth2 / 3, deviceScaleFactor);
</span><span class="lines">@@ -809,17 +805,17 @@
</span><span class="cx">         calculateBorderStyleColor(borderStyle, side, color);
</span><span class="cx">         FALLTHROUGH;
</span><span class="cx">     case BorderStyle::Solid: {
</span><del>-        StrokeStyle oldStrokeStyle = m_paintingContext.context.strokeStyle();
</del><ins>+        StrokeStyle oldStrokeStyle = paintingContext.context.strokeStyle();
</ins><span class="cx">         ASSERT(x2 >= x1);
</span><span class="cx">         ASSERT(y2 >= y1);
</span><span class="cx">         if (!adjacentWidth1 && !adjacentWidth2) {
</span><del>-            m_paintingContext.context.setStrokeStyle(NoStroke);
-            m_paintingContext.context.setFillColor(color);
-            bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-            m_paintingContext.context.setShouldAntialias(antialias);
</del><ins>+            paintingContext.context.setStrokeStyle(NoStroke);
+            paintingContext.context.setFillColor(color);
+            bool wasAntialiased = paintingContext.context.shouldAntialias();
+            paintingContext.context.setShouldAntialias(antialias);
</ins><span class="cx">             drawBorderRect(snapRectToDevicePixels(LayoutRect(x1, y1, x2 - x1, y2 - y1), deviceScaleFactor));
</span><del>-            m_paintingContext.context.setShouldAntialias(wasAntialiased);
-            m_paintingContext.context.setStrokeStyle(oldStrokeStyle);
</del><ins>+            paintingContext.context.setShouldAntialias(wasAntialiased);
+            paintingContext.context.setStrokeStyle(oldStrokeStyle);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -858,20 +854,20 @@
</span><span class="cx">             break;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        m_paintingContext.context.setStrokeStyle(NoStroke);
-        m_paintingContext.context.setFillColor(color);
-        bool wasAntialiased = m_paintingContext.context.shouldAntialias();
-        m_paintingContext.context.setShouldAntialias(antialias);
-        m_paintingContext.context.fillPath(Path::polygonPathFromPoints(quad));
-        m_paintingContext.context.setShouldAntialias(wasAntialiased);
</del><ins>+        paintingContext.context.setStrokeStyle(NoStroke);
+        paintingContext.context.setFillColor(color);
+        bool wasAntialiased = paintingContext.context.shouldAntialias();
+        paintingContext.context.setShouldAntialias(antialias);
+        paintingContext.context.fillPath(Path::polygonPathFromPoints(quad));
+        paintingContext.context.setShouldAntialias(wasAntialiased);
</ins><span class="cx"> 
</span><del>-        m_paintingContext.context.setStrokeStyle(oldStrokeStyle);
</del><ins>+        paintingContext.context.setStrokeStyle(oldStrokeStyle);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::paintOneBorderSide(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, const FloatRect& sideRect, BoxSide side, const Path* path, bool antialias, const Color* overrideColor) const
</del><ins>+void BorderPainter::paintOneBorderSide(PaintingContext& paintingContext, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, const FloatRect& sideRect, BoxSide side, const Path* path, bool antialias, const Color* overrideColor) const
</ins><span class="cx"> {
</span><span class="cx">     auto& edgeToRender = m_edges.at(side);
</span><span class="cx">     ASSERT(edgeToRender.widthForPainting());
</span><span class="lines">@@ -890,15 +886,15 @@
</span><span class="cx">     const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color();
</span><span class="cx"> 
</span><span class="cx">     if (path) {
</span><del>-        GraphicsContextStateSaver stateSaver(m_paintingContext.context);
</del><ins>+        GraphicsContextStateSaver stateSaver(paintingContext.context);
</ins><span class="cx"> 
</span><del>-        clipBorderSidePolygon(outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
</del><ins>+        clipBorderSidePolygon(paintingContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
</ins><span class="cx"> 
</span><span class="cx">         if (!innerBorder.isRenderable())
</span><del>-            m_paintingContext.context.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)));
</del><ins>+            paintingContext.context.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)));
</ins><span class="cx"> 
</span><span class="cx">         float thickness = std::max(std::max(edgeToRender.widthForPainting(), adjacentEdge1.widthForPainting()), adjacentEdge2.widthForPainting());
</span><del>-        drawBoxSideFromPath(outerBorder.rect(), *path, edgeToRender.widthForPainting(), thickness, side, colorToPaint, edgeToRender.style());
</del><ins>+        drawBoxSideFromPath(paintingContext, outerBorder.rect(), *path, edgeToRender.widthForPainting(), thickness, side, colorToPaint, edgeToRender.style());
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -907,19 +903,19 @@
</span><span class="cx">     bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSides.second) && mitreAdjacentSide2;
</span><span class="cx">     bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2;
</span><span class="cx">     
</span><del>-    GraphicsContextStateSaver clipStateSaver(m_paintingContext.context, shouldClip);
</del><ins>+    GraphicsContextStateSaver clipStateSaver(paintingContext.context, shouldClip);
</ins><span class="cx">     if (shouldClip) {
</span><span class="cx">         bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1);
</span><span class="cx">         bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2);
</span><del>-        clipBorderSidePolygon(outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2);
</del><ins>+        clipBorderSidePolygon(paintingContext, outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2);
</ins><span class="cx">         // Since we clipped, no need to draw with a mitre.
</span><span class="cx">         mitreAdjacentSide1 = false;
</span><span class="cx">         mitreAdjacentSide2 = false;
</span><span class="cx">     }
</span><del>-    drawLineForBoxSide(sideRect, side, colorToPaint, edgeToRender.style(), mitreAdjacentSide1 ? adjacentEdge1.widthForPainting() : 0, mitreAdjacentSide2 ? adjacentEdge2.widthForPainting() : 0, antialias);
</del><ins>+    drawLineForBoxSide(paintingContext, sideRect, side, colorToPaint, edgeToRender.style(), mitreAdjacentSide1 ? adjacentEdge1.widthForPainting() : 0, mitreAdjacentSide2 ? adjacentEdge2.widthForPainting() : 0, antialias);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::paintBorderSides(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSideSet edgeSet, bool antialias, const Color* overrideColor) const
</del><ins>+void BorderPainter::paintBorderSides(PaintingContext& paintingContext, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, FloatSize innerBorderBleedAdjustment, BoxSideSet edgeSet, bool antialias, const Color* overrideColor) const
</ins><span class="cx"> {
</span><span class="cx">     bool renderRadii = outerBorder.isRounded();
</span><span class="cx"> 
</span><span class="lines">@@ -938,22 +934,22 @@
</span><span class="cx"> 
</span><span class="cx">         switch (side) {
</span><span class="cx">         case BoxSide::Top:
</span><del>-            sideRect.setHeight(edge.widthForPainting());
</del><ins>+            sideRect.setHeight(edge.widthForPainting() + innerBorderBleedAdjustment.height());
</ins><span class="cx">             firstRadius = innerBorder.radii().topLeft();
</span><span class="cx">             secondRadius = innerBorder.radii().topRight();
</span><span class="cx">             break;
</span><span class="cx">         case BoxSide::Right:
</span><del>-            sideRect.shiftXEdgeTo(sideRect.maxX() - edge.widthForPainting());
</del><ins>+            sideRect.shiftXEdgeTo(sideRect.maxX() - edge.widthForPainting() - innerBorderBleedAdjustment.width());
</ins><span class="cx">             firstRadius = innerBorder.radii().bottomRight();
</span><span class="cx">             secondRadius = innerBorder.radii().topRight();
</span><span class="cx">             break;
</span><span class="cx">         case BoxSide::Bottom:
</span><del>-            sideRect.shiftYEdgeTo(sideRect.maxY() - edge.widthForPainting());
</del><ins>+            sideRect.shiftYEdgeTo(sideRect.maxY() - edge.widthForPainting() - innerBorderBleedAdjustment.height());
</ins><span class="cx">             firstRadius = innerBorder.radii().bottomLeft();
</span><span class="cx">             secondRadius = innerBorder.radii().bottomRight();
</span><span class="cx">             break;
</span><span class="cx">         case BoxSide::Left:
</span><del>-            sideRect.setWidth(edge.widthForPainting());
</del><ins>+            sideRect.setWidth(edge.widthForPainting() + innerBorderBleedAdjustment.width());
</ins><span class="cx">             firstRadius = innerBorder.radii().bottomLeft();
</span><span class="cx">             secondRadius = innerBorder.radii().topLeft();
</span><span class="cx">             break;
</span><span class="lines">@@ -960,7 +956,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         bool usePath = renderRadii && (borderStyleHasInnerDetail(edge.style()) || borderWillArcInnerEdge(firstRadius, secondRadius));
</span><del>-        paintOneBorderSide(outerBorder, innerBorder, sideRect, side, usePath ? &roundedPath : nullptr, antialias, overrideColor);
</del><ins>+        paintOneBorderSide(paintingContext, outerBorder, innerBorder, sideRect, side, usePath ? &roundedPath : nullptr, antialias, overrideColor);
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     paintOneSide(BoxSide::Top);
</span><span class="lines">@@ -969,7 +965,7 @@
</span><span class="cx">     paintOneSide(BoxSide::Right);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::paintTranslucentBorderSides(const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSideSet edgesToDraw, bool antialias) const
</del><ins>+void BorderPainter::paintTranslucentBorderSides(PaintingContext& paintingContext, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, FloatSize innerBorderBleedAdjustment, BoxSideSet edgesToDraw, bool antialias) const
</ins><span class="cx"> {
</span><span class="cx">     // willBeOverdrawn assumes that we draw in order: top, bottom, left, right.
</span><span class="cx">     // This is different from BoxSide enum order.
</span><span class="lines">@@ -998,21 +994,39 @@
</span><span class="cx"> 
</span><span class="cx">         bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && !commonColor.isOpaque();
</span><span class="cx">         if (useTransparencyLayer) {
</span><del>-            m_paintingContext.context.beginTransparencyLayer(commonColor.alphaAsFloat());
</del><ins>+            paintingContext.context.beginTransparencyLayer(commonColor.alphaAsFloat());
</ins><span class="cx">             commonColor = commonColor.opaqueColor();
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        paintBorderSides(outerBorder, innerBorder, commonColorEdgeSet, antialias, &commonColor);
</del><ins>+        paintBorderSides(paintingContext, outerBorder, innerBorder, innerBorderBleedAdjustment, commonColorEdgeSet, antialias, &commonColor);
</ins><span class="cx">             
</span><span class="cx">         if (useTransparencyLayer)
</span><del>-            m_paintingContext.context.endTransparencyLayer();
</del><ins>+            paintingContext.context.endTransparencyLayer();
</ins><span class="cx">         
</span><span class="cx">         edgesToDraw.remove(commonColorEdgeSet);
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BorderPainter::paintBorders() const
</del><ins>+static FloatRect shrinkRectByOneDevicePixel(const PaintingContext& paintingContext, const FloatRect& rect)
</ins><span class="cx"> {
</span><ins>+    auto shrunkRect = rect;
+    auto transform = paintingContext.context.getCTM();
+    shrunkRect.inflateX(-ceilToDevicePixel(1.0f / transform.xScale(), paintingContext.deviceScaleFactor));
+    shrunkRect.inflateY(-ceilToDevicePixel(1.0f / transform.yScale(), paintingContext.deviceScaleFactor));
+    return shrunkRect;
+}
+
+FloatRect BorderPainter::borderInnerRectAdjustedForBleedAvoidance(const PaintingContext& paintingContext) const
+{
+    if (m_bleedAvoidance != BackgroundBleedAvoidance::BackgroundOverBorder)
+        return m_borderRect.rect();
+
+    // We shrink the rectangle by one device pixel on each side to make it fully overlap the anti-aliased background border
+    return shrinkRectByOneDevicePixel(paintingContext, m_borderRect.rect());
+}
+
+void BorderPainter::paintBorders(PaintingContext& paintingContext) const
+{
</ins><span class="cx">     bool haveAlphaColor = false;
</span><span class="cx">     bool haveAllSolidEdges = true;
</span><span class="cx">     bool haveAllDoubleEdges = true;
</span><span class="lines">@@ -1053,7 +1067,7 @@
</span><span class="cx">             haveAllDoubleEdges = false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    FloatRoundedRect innerBorderRect = roundedInsetBorderForRect(m_borderRect.rect(), { m_edges.top().width(), m_edges.right().width(), m_edges.bottom().width(), m_edges.left().width() });
</del><ins>+    FloatRoundedRect innerBorderRect = roundedInsetBorderForRect(borderInnerRectAdjustedForBleedAvoidance(paintingContext), m_borderRect.radii(), borderWidths(m_edges), m_includeLeftEdge, m_includeRightEdge);
</ins><span class="cx"> 
</span><span class="cx">     // FIXME: If no corner intersects the clip region, we can pretend outerBorder is rectangular to improve performance.
</span><span class="cx">     
</span><span class="lines">@@ -1062,7 +1076,7 @@
</span><span class="cx">         if (numEdgesVisible == 4 && (m_borderRect.isRounded() || haveAlphaColor) && (haveAllSolidEdges || (!m_borderRect.isRounded() && !innerBorderRect.isRounded()))) {
</span><span class="cx">             Path path;
</span><span class="cx">             
</span><del>-            if (m_borderRect.isRounded())
</del><ins>+            if (m_borderRect.isRounded() && m_bleedAvoidance != BackgroundBleedAvoidance::UseTransparencyLayer)
</ins><span class="cx">                 path.addRoundedRect(m_borderRect);
</span><span class="cx">             else
</span><span class="cx">                 path.addRect(m_borderRect.rect());
</span><span class="lines">@@ -1098,7 +1112,7 @@
</span><span class="cx">                 auto outerThirdRoundedRect = m_borderRect;
</span><span class="cx">                 outerThirdRoundedRect.setRect(outerThirdRect);
</span><span class="cx"> 
</span><del>-                if (outerThirdRoundedRect.isRounded())
</del><ins>+                if (outerThirdRoundedRect.isRounded() && m_bleedAvoidance != BackgroundBleedAvoidance::UseTransparencyLayer)
</ins><span class="cx">                     path.addRoundedRect(outerThirdRoundedRect);
</span><span class="cx">                 else
</span><span class="cx">                     path.addRect(outerThirdRoundedRect.rect());
</span><span class="lines">@@ -1116,9 +1130,9 @@
</span><span class="cx">             else
</span><span class="cx">                 path.addRect(innerBorderRect.rect());
</span><span class="cx">             
</span><del>-            m_paintingContext.context.setFillRule(WindRule::EvenOdd);
-            m_paintingContext.context.setFillColor(m_edges.top().color());
-            m_paintingContext.context.fillPath(path);
</del><ins>+            paintingContext.context.setFillRule(WindRule::EvenOdd);
+            paintingContext.context.setFillColor(m_edges.top().color());
+            paintingContext.context.fillPath(path);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -1156,35 +1170,45 @@
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            m_paintingContext.context.setFillRule(WindRule::NonZero);
-            m_paintingContext.context.setFillColor(m_edges.at(*firstVisibleSide).color());
-            m_paintingContext.context.fillPath(path);
</del><ins>+            paintingContext.context.setFillRule(WindRule::NonZero);
+            paintingContext.context.setFillColor(m_edges.at(*firstVisibleSide).color());
+            paintingContext.context.fillPath(path);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     bool clipToOuterBorder = m_borderRect.isRounded();
</span><del>-    GraphicsContextStateSaver stateSaver(m_paintingContext.context, clipToOuterBorder);
</del><ins>+    GraphicsContextStateSaver stateSaver(paintingContext.context, clipToOuterBorder);
</ins><span class="cx">     if (clipToOuterBorder) {
</span><span class="cx">         // Clip to the inner and outer radii rects.
</span><del>-        m_paintingContext.context.clipRoundedRect(m_borderRect);
</del><ins>+        if (m_bleedAvoidance != BackgroundBleedAvoidance::UseTransparencyLayer)
+            paintingContext.context.clipRoundedRect(m_borderRect);
+
</ins><span class="cx">         if (innerBorderRect.isRenderable())
</span><del>-            m_paintingContext.context.clipOutRoundedRect(innerBorderRect);
</del><ins>+            paintingContext.context.clipOutRoundedRect(innerBorderRect);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    bool shouldAntialiasLines = !m_paintingContext.context.getCTM().isIdentityOrTranslationOrFlipped();
</del><ins>+    bool shouldAntialiasLines = !paintingContext.context.getCTM().isIdentityOrTranslationOrFlipped();
</ins><span class="cx"> 
</span><span class="cx">     // If only one edge visible antialiasing doesn't create seams
</span><span class="cx">     bool antialias = shouldAntialiasLines || numEdgesVisible == 1;
</span><ins>+
+    auto unadjustedInnerBorder = innerBorderRect;
+    FloatSize innerBorderBleedAdjustment;
+    if (m_bleedAvoidance == BackgroundBleedAvoidance::BackgroundOverBorder) {
+        unadjustedInnerBorder = roundedInsetBorderForRect(m_borderRect.rect(), m_borderRect.radii(), borderWidths(m_edges), m_includeLeftEdge, m_includeRightEdge);
+        innerBorderBleedAdjustment = innerBorderRect.rect().location() - unadjustedInnerBorder.rect().location();
+    }
+
</ins><span class="cx">     if (haveAlphaColor)
</span><del>-        paintTranslucentBorderSides(m_borderRect, innerBorderRect, edgesToDraw, antialias); // FIXME.
</del><ins>+        paintTranslucentBorderSides(paintingContext, m_borderRect, innerBorderRect, innerBorderBleedAdjustment, edgesToDraw, antialias); // FIXME.
</ins><span class="cx">     else
</span><del>-        paintBorderSides(m_borderRect, innerBorderRect, edgesToDraw, antialias);
</del><ins>+        paintBorderSides(paintingContext, m_borderRect, innerBorderRect, innerBorderBleedAdjustment, edgesToDraw, antialias);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BoxDecorationPainter::paintBorders(const BoxModelBox& box, PaintingContext& paintingContext, bool includeLeftEdge, bool includeRightEdge)
</del><ins>+void BoxDecorationPainter::paintBorders(PaintingContext& paintingContext) const
</ins><span class="cx"> {
</span><del>-    auto* boxDecorationData = box.boxDecorationData();
</del><ins>+    auto* boxDecorationData = m_box.boxDecorationData();
</ins><span class="cx">     if (!boxDecorationData)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -1194,15 +1218,12 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    auto outerBorder = FloatRoundedRect { box.absoluteBorderBoxRect(), { } };
-    if (auto borderRadii = boxDecorationData->borderRadii())
-        outerBorder.setRadii(*borderRadii);
-
-    BorderPainter painter(boxDecorationData->borderEdges(), paintingContext, outerBorder, includeLeftEdge, includeRightEdge);
-    painter.paintBorders();
</del><ins>+    auto outerBorder = borderRoundedRect();
+    BorderPainter painter(boxDecorationData->borderEdges(), outerBorder, m_bleedAvoidance, m_includeLeftEdge, m_includeRightEdge);
+    painter.paintBorders(paintingContext);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void paintFillLayer(const BoxModelBox& box, const FillLayer& layer, const FillLayerImageGeometry& geometry, PaintingContext& paintingContext)
</del><ins>+void BoxDecorationPainter::paintFillLayer(PaintingContext& paintingContext, const FillLayer& layer, const FillLayerImageGeometry& geometry) const
</ins><span class="cx"> {
</span><span class="cx">     GraphicsContextStateSaver stateSaver(paintingContext.context, false);
</span><span class="cx"> 
</span><span class="lines">@@ -1225,7 +1246,7 @@
</span><span class="cx">     case FillBox::Padding:
</span><span class="cx">     case FillBox::Content: {
</span><span class="cx">         stateSaver.save();
</span><del>-        paintingContext.context.clip(clipRectForLayer(box, layer));
</del><ins>+        paintingContext.context.clip(clipRectForLayer(m_box, layer));
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case FillBox::Text:
</span><span class="lines">@@ -1257,16 +1278,35 @@
</span><span class="cx">     paintingContext.context.drawTiledImage(*image, geometry.destRect(), toFloatPoint(geometry.relativePhase()), geometry.tileSize(), geometry.spaceSize(), options);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BoxDecorationPainter::paintBackgroundImages(const BoxModelBox& box, PaintingContext& paintingContext)
</del><ins>+BoxDecorationPainter::BoxDecorationPainter(const BoxModelBox& box, PaintingContext& paintingContext, bool includeLeftEdge, bool includeRightEdge)
+    : m_box(box)
+    , m_borderRect(computeBorderRect(box))
+    , m_bleedAvoidance(determineBackgroundBleedAvoidance(box, paintingContext))
+    , m_includeLeftEdge(includeLeftEdge)
+    , m_includeRightEdge(includeRightEdge)
</ins><span class="cx"> {
</span><del>-    const auto& style = box.style();
</del><ins>+}
</ins><span class="cx"> 
</span><ins>+FloatRoundedRect BoxDecorationPainter::computeBorderRect(const BoxModelBox& box)
+{
+    auto borderRect = FloatRoundedRect { box.absoluteBorderBoxRect(), { } };
+    auto* borderRadii = box.boxDecorationData() ? box.boxDecorationData()->borderRadii() : nullptr;
+    if (borderRadii)
+        borderRect.setRadii(*borderRadii);
+    
+    return borderRect;
+}
+
+void BoxDecorationPainter::paintBackgroundImages(PaintingContext& paintingContext) const
+{
+    const auto& style = m_box.style();
+
</ins><span class="cx">     Vector<const FillLayer*, 8> layers;
</span><span class="cx"> 
</span><span class="cx">     for (auto* layer = style.backgroundLayers(); layer; layer = layer->next())
</span><span class="cx">         layers.append(layer);
</span><span class="cx"> 
</span><del>-    auto* boxDecorationData = box.boxDecorationData();
</del><ins>+    auto* boxDecorationData = m_box.boxDecorationData();
</ins><span class="cx">     ASSERT(boxDecorationData);
</span><span class="cx"> 
</span><span class="cx">     auto& layerGeometryList = boxDecorationData->backgroundImageGeometry();
</span><span class="lines">@@ -1274,28 +1314,106 @@
</span><span class="cx">     for (int i = layers.size() - 1; i >=0; --i) {
</span><span class="cx">         const auto* layer = layers[i];
</span><span class="cx">         const auto& geometry = layerGeometryList[i];
</span><del>-        paintFillLayer(box, *layer, geometry, paintingContext);
</del><ins>+        paintFillLayer(paintingContext, *layer, geometry);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BoxDecorationPainter::paintBackground(const BoxModelBox& box, PaintingContext& paintingContext)
</del><ins>+FloatRoundedRect BoxDecorationPainter::backgroundRoundedRectAdjustedForBleedAvoidance(const PaintingContext& paintingContext) const
</ins><span class="cx"> {
</span><del>-    auto borderBoxRect = box.absoluteBorderBoxRect();
-    const auto& style = box.style();
</del><ins>+    if (m_bleedAvoidance == BackgroundBleedAvoidance::ShrinkBackground) {
+        // We shrink the rectangle by one device pixel on each side because the bleed is one pixel maximum.
+        return roundedRectWithIncludedRadii(shrinkRectByOneDevicePixel(paintingContext, m_borderRect.rect()), m_borderRect.radii(), m_includeLeftEdge, m_includeRightEdge);
+    }
</ins><span class="cx"> 
</span><ins>+    if (m_bleedAvoidance == BackgroundBleedAvoidance::BackgroundOverBorder) {
+        auto* boxDecorationData = m_box.boxDecorationData();
+        ASSERT(boxDecorationData);
+        return roundedInsetBorderForRect(m_borderRect.rect(), m_borderRect.radii(), borderWidths(boxDecorationData->borderEdges()), m_includeLeftEdge, m_includeRightEdge);
+    }
+
+    return roundedRectWithIncludedRadii(m_borderRect.rect(), m_borderRect.radii(), m_includeLeftEdge, m_includeRightEdge);
+}
+
+void BoxDecorationPainter::paintBackground(PaintingContext& paintingContext) const
+{
+    auto borderBoxRect = m_box.absoluteBorderBoxRect();
+    const auto& style = m_box.style();
+    auto* boxDecorationData = m_box.boxDecorationData();
+
</ins><span class="cx">     if (style.hasBackground()) {
</span><ins>+        GraphicsContextStateSaver stateSaver(paintingContext.context, false);
+        if (m_bleedAvoidance != BackgroundBleedAvoidance::UseTransparencyLayer && boxDecorationData && boxDecorationData->hasBorderRadius()) {
+            stateSaver.save();
+            auto outerBorder = backgroundRoundedRectAdjustedForBleedAvoidance(paintingContext);
+            paintingContext.context.clipRoundedRect(outerBorder);
+        }
+    
</ins><span class="cx">         paintingContext.context.fillRect(borderBoxRect, style.backgroundColor());
</span><ins>+
</ins><span class="cx">         if (style.hasBackgroundImage())
</span><del>-            paintBackgroundImages(box, paintingContext);
</del><ins>+            paintBackgroundImages(paintingContext);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BoxDecorationPainter::paintBackgroundAndBorders(const BoxModelBox& box, PaintingContext& paintingContext)
</del><ins>+BackgroundBleedAvoidance BoxDecorationPainter::determineBackgroundBleedAvoidance(const BoxModelBox& box, PaintingContext& paintingContext)
</ins><span class="cx"> {
</span><ins>+    if (paintingContext.context.paintingDisabled())
+        return BackgroundBleedAvoidance::None;
+
+    auto& style = box.style();
+    auto* boxDecorationData = box.boxDecorationData();
+    
+    auto hasBackgroundAndRoundedBorder = [&] {
+        if (!boxDecorationData)
+            return false;
+
+        // FIXME: Consult border-image.
+        return style.hasBackground() && boxDecorationData->hasBorder() && boxDecorationData->hasBorderRadius();
+    };
+
+    if (!hasBackgroundAndRoundedBorder())
+        return BackgroundBleedAvoidance::None;
+
+    auto ctm = paintingContext.context.getCTM();
+    auto contextScaling = FloatSize { static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale()) };
+    if (boxDecorationData->borderObscuresBackgroundEdge(contextScaling))
+        return BackgroundBleedAvoidance::ShrinkBackground;
+
+    if (boxDecorationData->borderObscuresBackground() && style.backgroundHasOpaqueTopLayer())
+        return BackgroundBleedAvoidance::BackgroundOverBorder;
+
+    return BackgroundBleedAvoidance::UseTransparencyLayer;
+}
+
+void BoxDecorationPainter::paintBackgroundAndBorders(PaintingContext& paintingContext) const
+{
</ins><span class="cx">     // FIXME: Table decoration painting is special.
</span><del>-    BoxDecorationPainter::paintBackground(box, paintingContext);
-    BoxDecorationPainter::paintBorders(box, paintingContext);
</del><ins>+    
+    switch (m_bleedAvoidance) {
+    case BackgroundBleedAvoidance::BackgroundOverBorder:
+        paintBorders(paintingContext);
+        paintBackground(paintingContext);
+        break;
</ins><span class="cx"> 
</span><ins>+    case BackgroundBleedAvoidance::UseTransparencyLayer: {
+        GraphicsContextStateSaver stateSaver(paintingContext.context);
+        auto outerBorder = borderRoundedRect();
+        paintingContext.context.clipRoundedRect(outerBorder);
+        paintingContext.context.beginTransparencyLayer(1);
+
+        paintBackground(paintingContext);
+        paintBorders(paintingContext);
+
+        paintingContext.context.endTransparencyLayer();
+        break;
+    }
+    
+    case BackgroundBleedAvoidance::ShrinkBackground:
+    case BackgroundBleedAvoidance::None:
+        paintBackground(paintingContext);
+        paintBorders(paintingContext);
+        break;
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace Display
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayBoxDecorationPainterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.h (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.h   2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayBoxDecorationPainter.h      2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -27,23 +27,58 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
</span><span class="cx"> 
</span><ins>+#include "FloatRoundedRect.h"
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class FillLayer;
</ins><span class="cx"> class GraphicsContext;
</span><span class="cx"> 
</span><span class="cx"> namespace Display {
</span><span class="cx"> 
</span><span class="cx"> class BoxModelBox;
</span><ins>+class FillLayerImageGeometry;
</ins><span class="cx"> struct PaintingContext;
</span><ins>+enum class BackgroundBleedAvoidance;
</ins><span class="cx"> 
</span><ins>+enum class BackgroundBleedAvoidance {
+    None,
+    ShrinkBackground,
+    UseTransparencyLayer,
+    BackgroundOverBorder
+};
+
</ins><span class="cx"> class BoxDecorationPainter {
</span><span class="cx"> public:
</span><del>-    static void paintBackgroundAndBorders(const BoxModelBox&, PaintingContext&);
</del><ins>+    BoxDecorationPainter(const BoxModelBox&, PaintingContext&, bool includeLeftEdge = true, bool includeRightEdge = true);
+    void paintBackgroundAndBorders(PaintingContext&) const;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    static void paintBorders(const BoxModelBox&, PaintingContext&, bool includeLeftEdge = true, bool includeRightEdge = true);
-    static void paintBackground(const BoxModelBox&, PaintingContext&);
-    static void paintBackgroundImages(const BoxModelBox&, PaintingContext&);
</del><ins>+    friend class BorderPainter;
+
+    static BackgroundBleedAvoidance determineBackgroundBleedAvoidance(const BoxModelBox&, PaintingContext&);
+    static FloatRoundedRect computeBorderRect(const BoxModelBox&);
+
+    void computeBorderRect();
+
+    void paintBorders(PaintingContext&) const;
+    void paintBackground(PaintingContext&) const;
+    void paintBackgroundImages(PaintingContext&) const;
+
+    void paintFillLayer(PaintingContext&, const FillLayer&, const FillLayerImageGeometry&) const;
+    
+    FloatRoundedRect borderRoundedRect() const { return m_borderRect; }
+    
+    FloatRoundedRect backgroundRoundedRectAdjustedForBleedAvoidance(const PaintingContext&) const;
+
+    bool includeLeftEdge() const { return m_includeLeftEdge; }
+    bool includeRightEdge() const { return m_includeRightEdge; }
+
+    const BoxModelBox& m_box;
+    const FloatRoundedRect m_borderRect;
+    const BackgroundBleedAvoidance m_bleedAvoidance { BackgroundBleedAvoidance::None };
+    bool m_includeLeftEdge { true };
+    bool m_includeRightEdge { true };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace Display
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayBoxPaintercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayBoxPainter.cpp (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayBoxPainter.cpp   2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayBoxPainter.cpp      2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -49,7 +49,8 @@
</span><span class="cx"> 
</span><span class="cx"> void BoxPainter::paintBoxDecorations(const BoxModelBox& box, PaintingContext& paintingContext)
</span><span class="cx"> {
</span><del>-    BoxDecorationPainter::paintBackgroundAndBorders(box, paintingContext);
</del><ins>+    BoxDecorationPainter painter(box, paintingContext);
+    painter.paintBackgroundAndBorders(paintingContext);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void BoxPainter::paintBoxContent(const Box& box, PaintingContext& paintingContext)
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayStylecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayStyle.cpp (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayStyle.cpp        2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayStyle.cpp   2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -84,6 +84,27 @@
</span><span class="cx">     return m_backgroundLayers && m_backgroundLayers->hasImage();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool Style::backgroundHasOpaqueTopLayer() const
+{
+    auto* fillLayer = backgroundLayers();
+    if (!fillLayer)
+        return false;
+
+    if (fillLayer->clip() != FillBox::Border)
+        return false;
+
+    // FIXME: Check for overflow clip and local attachment.
+    // FIXME: Check for repeated, opaque and renderable image.
+
+    // If there is only one layer and no image, check whether the background color is opaque.
+    if (!fillLayer->next() && !fillLayer->hasImage()) {
+        if (backgroundColor().isOpaque())
+            return true;
+    }
+
+    return false;
+}
+
</ins><span class="cx"> bool Style::autoWrap() const
</span><span class="cx"> {
</span><span class="cx">     return RenderStyle::autoWrap(whiteSpace());
</span></span></pre></div>
<a id="trunkSourceWebCoredisplaycssDisplayStyleh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/display/css/DisplayStyle.h (269600 => 269601)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/display/css/DisplayStyle.h  2020-11-09 21:41:43 UTC (rev 269600)
+++ trunk/Source/WebCore/display/css/DisplayStyle.h     2020-11-09 21:54:39 UTC (rev 269601)
</span><span class="lines">@@ -66,6 +66,7 @@
</span><span class="cx">     bool hasBackgroundImage() const;
</span><span class="cx"> 
</span><span class="cx">     const FillLayer* backgroundLayers() const { return m_backgroundLayers.get(); }
</span><ins>+    bool backgroundHasOpaqueTopLayer() const;
</ins><span class="cx"> 
</span><span class="cx">     Optional<int> zIndex() const { return m_zIndex; }
</span><span class="cx">     bool isStackingContext() const { return m_zIndex.hasValue(); }
</span></span></pre>
</div>
</div>

</body>
</html>