NEW 196295
Changing a drop shadow filter does not render properly outside of element boundaries
https://bugs.webkit.org/show_bug.cgi?id=196295
Summary Changing a drop shadow filter does not render properly outside of element bou...
Tom Bigelajzen
Reported 2019-03-27 08:03:45 PDT
Created attachment 366070 [details] how the shadow looks on Safari 12.1 when values changed Use case: - Add an Element (not square preferably) - Apply a drop-shadow() filter to it - Change the drop shadow values with Javascript Actual: The drop shadow refreshes only in the boundaries of the Element but not outside of it Expected: Should repaint the entire shadow Link: https://codepen.io/tombigel/pen/QoXjEN I created this pen to play with drop shadow so it's a bit of an overkill but if you change any param you will see the issue (see screenshot) Tested on: Does not recreate on Chrome, Firefox, Safari 12.0.3 Recreates on Safari 12.1, Safari Technology Preview 78 Notes: I also encountered a similar bug only on Safari 12.1+ with animating -webkit-clip-path, hadn't had time to create a simple test case for it
Attachments
how the shadow looks on Safari 12.1 when values changed (43.47 KB, image/png)
2019-03-27 08:03 PDT, Tom Bigelajzen
no flags
Small testcase (889 bytes, text/html)
2021-10-21 20:41 PDT, Simon Fraser (smfr)
no flags
Radar WebKit Bug Importer
Comment 1 2019-03-28 11:31:41 PDT
Simon Fraser (smfr)
Comment 2 2019-03-28 12:00:47 PDT
Huh, I could have sworn this worked in the past.
Simon Fraser (smfr)
Comment 3 2019-03-28 21:10:33 PDT
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.
Tom Bigelajzen
Comment 4 2021-06-16 05:18:20 PDT
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.
Simon Fraser (smfr)
Comment 5 2021-10-21 20:41:45 PDT
Created attachment 442108 [details] Small testcase
Simon Fraser (smfr)
Comment 6 2021-10-21 21:07:35 PDT
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;
Simon Fraser (smfr)
Comment 7 2025-06-05 17:23:45 PDT
*** Bug 202472 has been marked as a duplicate of this bug. ***
Note You need to log in before you can comment on or make changes to this bug.