Skip to content

Commit

Permalink
Assertion may fail when repainting the RenderView of an SVGImage
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=273803
rdar://127102474

Reviewed by Chris Dumez.

Sometimes repainting the RenderView of an SVGImage is associated with changing
the SVGImage source. This would delete the SVGImage and all its render tree which
includes the repainted RenderView itself.

RenderLayer::recursiveUpdateLayerPositions() should not hold a CheckedPtr to the
repainted renderer because the renderer might be physically deleted while calling
RenderElement::repaintAfterLayoutIfNeeded() which happens before deleting the
CheckedPtr itself.

The fix is make RenderObject::repaintUsingContainer() take a WeakPtr to the
repainted renderer. So this WeakPtr pointer is nullified once it is deleted.
It also can be checked before it is referenced.

* Source/WebCore/rendering/RenderElement.cpp:
(WebCore::RenderElement::repaintAfterLayoutIfNeeded):
* Source/WebCore/rendering/RenderElement.h:
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::recursiveUpdateLayerPositions):
* Source/WebCore/rendering/RenderObject.cpp:
(WebCore::RenderObject::repaintUsingContainer const):
* Source/WebCore/rendering/RenderObject.h:

Canonical link: https://commits.webkit.org/278734@main
  • Loading branch information
shallawa authored and Said Abou-Hallawa committed May 14, 2024
1 parent bff7571 commit 9e2447a
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 22 deletions.
30 changes: 15 additions & 15 deletions Source/WebCore/rendering/RenderElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,7 @@ static bool mustRepaintFillLayers(const RenderElement& renderer, const FillLayer
return false;
}

bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repaintContainer, RequiresFullRepaint requiresFullRepaint, const RepaintRects& oldRects, const RepaintRects& newRects)
bool RenderElement::repaintAfterLayoutIfNeeded(SingleThreadWeakPtr<const RenderLayerModelObject>&& repaintContainer, RequiresFullRepaint requiresFullRepaint, const RepaintRects& oldRects, const RepaintRects& newRects)
{
if (view().printing())
return false; // Don't repaint if we're printing.
Expand Down Expand Up @@ -1344,12 +1344,12 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep

if (fullRepaint) {
if (newClippedOverflowRect.contains(oldClippedOverflowRect))
repaintUsingContainer(repaintContainer, newClippedOverflowRect);
repaintUsingContainer(WeakPtr { repaintContainer }, newClippedOverflowRect);
else if (oldClippedOverflowRect.contains(newClippedOverflowRect))
repaintUsingContainer(repaintContainer, oldClippedOverflowRect);
repaintUsingContainer(WeakPtr { repaintContainer }, oldClippedOverflowRect);
else {
repaintUsingContainer(repaintContainer, oldClippedOverflowRect);
repaintUsingContainer(repaintContainer, newClippedOverflowRect);
repaintUsingContainer(WeakPtr { repaintContainer }, oldClippedOverflowRect);
repaintUsingContainer(WeakPtr { repaintContainer }, newClippedOverflowRect);
}
return true;
}
Expand All @@ -1359,27 +1359,27 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep

LayoutUnit deltaLeft = newClippedOverflowRect.x() - oldClippedOverflowRect.x();
if (deltaLeft > 0)
repaintUsingContainer(repaintContainer, LayoutRect(oldClippedOverflowRect.x(), oldClippedOverflowRect.y(), deltaLeft, oldClippedOverflowRect.height()));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(oldClippedOverflowRect.x(), oldClippedOverflowRect.y(), deltaLeft, oldClippedOverflowRect.height()));
else if (deltaLeft < 0)
repaintUsingContainer(repaintContainer, LayoutRect(newClippedOverflowRect.x(), newClippedOverflowRect.y(), -deltaLeft, newClippedOverflowRect.height()));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(newClippedOverflowRect.x(), newClippedOverflowRect.y(), -deltaLeft, newClippedOverflowRect.height()));

LayoutUnit deltaRight = newClippedOverflowRect.maxX() - oldClippedOverflowRect.maxX();
if (deltaRight > 0)
repaintUsingContainer(repaintContainer, LayoutRect(oldClippedOverflowRect.maxX(), newClippedOverflowRect.y(), deltaRight, newClippedOverflowRect.height()));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(oldClippedOverflowRect.maxX(), newClippedOverflowRect.y(), deltaRight, newClippedOverflowRect.height()));
else if (deltaRight < 0)
repaintUsingContainer(repaintContainer, LayoutRect(newClippedOverflowRect.maxX(), oldClippedOverflowRect.y(), -deltaRight, oldClippedOverflowRect.height()));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(newClippedOverflowRect.maxX(), oldClippedOverflowRect.y(), -deltaRight, oldClippedOverflowRect.height()));

LayoutUnit deltaTop = newClippedOverflowRect.y() - oldClippedOverflowRect.y();
if (deltaTop > 0)
repaintUsingContainer(repaintContainer, LayoutRect(oldClippedOverflowRect.x(), oldClippedOverflowRect.y(), oldClippedOverflowRect.width(), deltaTop));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(oldClippedOverflowRect.x(), oldClippedOverflowRect.y(), oldClippedOverflowRect.width(), deltaTop));
else if (deltaTop < 0)
repaintUsingContainer(repaintContainer, LayoutRect(newClippedOverflowRect.x(), newClippedOverflowRect.y(), newClippedOverflowRect.width(), -deltaTop));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(newClippedOverflowRect.x(), newClippedOverflowRect.y(), newClippedOverflowRect.width(), -deltaTop));

LayoutUnit deltaBottom = newClippedOverflowRect.maxY() - oldClippedOverflowRect.maxY();
if (deltaBottom > 0)
repaintUsingContainer(repaintContainer, LayoutRect(newClippedOverflowRect.x(), oldClippedOverflowRect.maxY(), newClippedOverflowRect.width(), deltaBottom));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(newClippedOverflowRect.x(), oldClippedOverflowRect.maxY(), newClippedOverflowRect.width(), deltaBottom));
else if (deltaBottom < 0)
repaintUsingContainer(repaintContainer, LayoutRect(oldClippedOverflowRect.x(), newClippedOverflowRect.maxY(), oldClippedOverflowRect.width(), -deltaBottom));
repaintUsingContainer(WeakPtr { repaintContainer }, LayoutRect(oldClippedOverflowRect.x(), newClippedOverflowRect.maxY(), oldClippedOverflowRect.width(), -deltaBottom));

if (!haveOutlinesBoundsRects || *oldRects.outlineBoundsRect == *newRects.outlineBoundsRect)
return false;
Expand Down Expand Up @@ -1436,7 +1436,7 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
if (damageExtentWithinClippedOverflow > 0) {
damageExtentWithinClippedOverflow = std::min(sizeDelta.width() + decorationRightExtent, damageExtentWithinClippedOverflow);
auto damagedRect = LayoutRect { decorationLeft, newOutlineBoundsRect.y(), damageExtentWithinClippedOverflow, std::max(newOutlineBoundsRect.height(), oldOutlineBoundsRect.height()) };
repaintUsingContainer(repaintContainer, damagedRect);
repaintUsingContainer(WeakPtr { repaintContainer }, damagedRect);
}
}
if (sizeDelta.height()) {
Expand Down Expand Up @@ -1478,7 +1478,7 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
if (damageExtentWithinClippedOverflow > 0) {
damageExtentWithinClippedOverflow = std::min(sizeDelta.height() + decorationBottomExtent, damageExtentWithinClippedOverflow);
auto damagedRect = LayoutRect { newOutlineBoundsRect.x(), decorationTop, std::max(newOutlineBoundsRect.width(), oldOutlineBoundsRect.width()), damageExtentWithinClippedOverflow };
repaintUsingContainer(repaintContainer, damagedRect);
repaintUsingContainer(WeakPtr { repaintContainer }, damagedRect);
}
}
return false;
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/rendering/RenderElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class RenderElement : public RenderObject {
void setStyleInternal(RenderStyle&& style) { m_style = WTFMove(style); }

// Repaint only if our old bounds and new bounds are different. The caller may pass in newBounds and newOutlineBox if they are known.
bool repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repaintContainer, RequiresFullRepaint, const RepaintRects& oldRects, const RepaintRects& newRects);
bool repaintAfterLayoutIfNeeded(SingleThreadWeakPtr<const RenderLayerModelObject>&& repaintContainer, RequiresFullRepaint, const RepaintRects& oldRects, const RepaintRects& newRects);

void repaintClientsOfReferencedSVGResources() const;
void repaintRendererOrClientsOfReferencedSVGResources() const;
Expand Down
6 changes: 3 additions & 3 deletions Source/WebCore/rendering/RenderLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1024,16 +1024,16 @@ void RenderLayer::recursiveUpdateLayerPositions(OptionSet<UpdateLayerPositionsFl
// LayoutState outside the layout() phase and use it here.
ASSERT(!renderer().view().frameView().layoutContext().isPaintOffsetCacheEnabled());

CheckedPtr repaintContainer = renderer().containerForRepaint().renderer;
WeakPtr repaintContainer = renderer().containerForRepaint().renderer.get();

auto oldRects = repaintRects();
computeRepaintRects(repaintContainer.get());
auto newRects = repaintRects();

if (checkForRepaint && shouldRepaintAfterLayout() && newRects) {
auto needsFullRepaint = m_repaintStatus == RepaintStatus::NeedsFullRepaint ? RequiresFullRepaint::Yes : RequiresFullRepaint::No;
auto resolvedOldRects = valueOrDefault(oldRects);
renderer().repaintAfterLayoutIfNeeded(repaintContainer.get(), needsFullRepaint, resolvedOldRects, *newRects);
renderer().repaintAfterLayoutIfNeeded(WTFMove(repaintContainer), needsFullRepaint, resolvedOldRects, *newRects);
}
};

Expand Down
5 changes: 4 additions & 1 deletion Source/WebCore/rendering/RenderObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ void RenderObject::propagateRepaintToParentWithOutlineAutoIfNeeded(const RenderL
ASSERT_NOT_REACHED();
}

void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const LayoutRect& r, bool shouldClipToLayer) const
void RenderObject::repaintUsingContainer(SingleThreadWeakPtr<const RenderLayerModelObject>&& repaintContainer, const LayoutRect& r, bool shouldClipToLayer) const
{
if (r.isEmpty())
return;
Expand All @@ -987,6 +987,9 @@ void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintCo
return;
}

if (!repaintContainer)
return;

propagateRepaintToParentWithOutlineAutoIfNeeded(*repaintContainer, r);

if (repaintContainer->hasFilter() && repaintContainer->layer() && repaintContainer->layer()->requiresFullLayerImageForFilters()) {
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/rendering/RenderObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -891,8 +891,8 @@ class RenderObject : public CachedImageClient, public CanMakeCheckedPtr<RenderOb
RepaintContainerStatus containerForRepaint() const;
// Actually do the repaint of rect r for this object which has been computed in the coordinate space
// of repaintContainer. If repaintContainer is nullptr, repaint via the view.
void repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const LayoutRect&, bool shouldClipToLayer = true) const;
void repaintUsingContainer(SingleThreadWeakPtr<const RenderLayerModelObject>&& repaintContainer, const LayoutRect&, bool shouldClipToLayer = true) const;

// Repaint the entire object. Called when, e.g., the color of a border changes, or when a border
// style changes.
enum class ForceRepaint : bool { No, Yes };
Expand Down

0 comments on commit 9e2447a

Please sign in to comment.