| Summary: | Changing a drop shadow filter does not render properly outside of element boundaries | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Product: | WebKit | Reporter: | Tom Bigelajzen <tombigel> | ||||||
| Component: | Layout and Rendering | Assignee: | Simon Fraser (smfr) <simon.fraser> | ||||||
| Status: | NEW --- | ||||||||
| Severity: | Normal | CC: | bfulgham, dino, graouts, simon.fraser, webkit-bug-importer, zalan | ||||||
| Priority: | P2 | Keywords: | InRadar | ||||||
| Version: | Safari 12 | ||||||||
| Hardware: | Mac | ||||||||
| OS: | macOS 10.14 | ||||||||
| Attachments: |
|
||||||||
|
Description
Tom Bigelajzen
2019-03-27 08:03:45 PDT
Huh, I could have sworn this worked in the past. I was thinking of box-shadow repainting. We don't have a code path that ensures correct repainting of layers with out-setting filters, for changes on the renderer with the filter, or descendants. We just get a RepaintLayer RenderStyle diff type, and call renderer.repaint(). Also we just do this in willChangeStyle, so we only ever repaint the "before" state of the filter when the style changes. What we'll need to do before and after the style change is to walk the ancestor RenderLayer chain and ensure that repaint rects get inflated for filters that move pixels. Update 2021: Still recreates in 14.x Found a viable workaround - using "will-change: filter" On Chrome it breaks the shadow in a weird way so it's a partial workaround because it requires browser detection. Created attachment 442108 [details]
Small testcase
Need something like this:
diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp
index d7990e430f10a0c962fe11483f9a55d67349431b..bf5946d3de53e610bc24ec3c9b6c848ef3f8b419 100644
--- a/Source/WebCore/rendering/RenderBox.cpp
+++ b/Source/WebCore/rendering/RenderBox.cpp
@@ -4860,7 +4860,8 @@ void RenderBox::addVisualEffectOverflow()
bool hasBoxShadow = style().boxShadow();
bool hasBorderImageOutsets = style().hasBorderImageOutsets();
bool hasOutline = outlineStyleForRepaint().hasOutlineInVisualOverflow();
- if (!hasBoxShadow && !hasBorderImageOutsets && !hasOutline)
+ bool hasFilter = style().hasFilter();
+ if (!hasBoxShadow && !hasBorderImageOutsets && !hasOutline && !hasFilter)
return;
addVisualOverflow(applyVisualEffectOverflow(borderBoxRect()));
@@ -4901,6 +4902,16 @@ LayoutRect RenderBox::applyVisualEffectOverflow(const LayoutRect& borderBox) con
overflowMinY = std::min(overflowMinY, borderBox.y() - ((!isFlipped || !isHorizontal) ? borderOutsets.top() : borderOutsets.bottom()));
overflowMaxY = std::max(overflowMaxY, borderBox.maxY() + ((!isFlipped || !isHorizontal) ? borderOutsets.bottom() : borderOutsets.top()));
}
+
+ if (style().hasFilter()) {
+ auto filterOutsets = style().filterOutsets();
+
+ // FIXME: For writing modes.
+ overflowMinX = std::min(overflowMinX, borderBox.x() - filterOutsets.left());
+ overflowMaxX = std::max(overflowMaxX, borderBox.maxX() + filterOutsets.right());
+ overflowMinY = std::min(overflowMinY, borderBox.y() - filterOutsets.top());
+ overflowMaxY = std::max(overflowMaxY, borderBox.maxY() + filterOutsets.bottom());
+ }
if (outlineStyleForRepaint().hasOutlineInVisualOverflow()) {
LayoutUnit outlineSize { outlineStyleForRepaint().outlineSize() };
diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp
index 606c99b83dfb71b2682e91412745a6d0c3f6b124..f559b34a8bf0daf73f5ab2a42ff259f3bb55ab42 100644
--- a/Source/WebCore/rendering/style/RenderStyle.cpp
+++ b/Source/WebCore/rendering/style/RenderStyle.cpp
@@ -643,6 +643,10 @@ inline bool RenderStyle::changeAffectsVisualOverflow(const RenderStyle& other) c
return visualOverflowForDecorations(*this, { }) != visualOverflowForDecorations(other, { });
}
+ // FIXME: Examine filters to determine if outsets changed.
+ if (filter() != other.filter())
+ return true;
+
if (hasOutlineInVisualOverflow() != other.hasOutlineInVisualOverflow())
return true;
return false;
|