Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ T
(transform [1.50 0.00 0.00 0.00] [0.00 0.50 0.00 0.00] [0.00 0.00 1.00 0.00] [-565.25 7.75 0.00 1.00])
(children 1
(GraphicsLayer
(anchor 0.50 0.52)
(bounds 10.00 31.00)
(anchor 0.56 0.52)
(bounds 9.00 31.00)
(contentsOpaque 1)
(drawsContent 1)
(backingStoreAttached 1)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Tests that hovering a rotated option in base-select does not trigger a repaint of the select button.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS selectWasRepainted is false
PASS successfullyParsed is true

TEST COMPLETE

one
two
three

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<head>
<script>jsTestIsAsync = true;</script>
<script src="../../../../resources/js-test.js"></script>
<script src="../../../../resources/ui-helper.js"></script>
<style>
select, ::picker(select) {
appearance: base-select;
}
option {
transform-origin: right -50px;
rotate: -3deg;
}
</style>
</head>
<body>
<select id="select">
<option>one</option>
<option id="target">two</option>
<option>three</option>
</select>
<script>
description("Tests that hovering a rotated option in base-select does not trigger a repaint of the select button.");

(async () => {
const select = document.getElementById("select");
const target = document.getElementById("target");

// Open the picker via keyboard.
select.focus();
await UIHelper.keyDown(" ");
await UIHelper.renderingUpdate();

const selectRect = select.getBoundingClientRect();

// Start tracking repaints after the picker is fully open.
if (window.internals)
internals.startTrackingRepaints();

// Hover over the target option to trigger willResetComputedStyle.
if (window.eventSender) {
const targetRect = target.getBoundingClientRect();
eventSender.mouseMoveTo(targetRect.x + targetRect.width / 2, targetRect.y + targetRect.height / 2);
}

await UIHelper.renderingUpdate();

if (window.internals) {
const repaintRects = internals.repaintRectsAsText();
internals.stopTrackingRepaints();

// Check that no repaint rect overlaps the select button area.
// Without the fix, willResetComputedStyle calls select->renderer()->repaint()
// which unnecessarily repaints the select button.
const rectPattern = /\(rect ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+)\)/g;
let selectWasRepainted = false;
let match;
while ((match = rectPattern.exec(repaintRects)) !== null) {
const rx = parseFloat(match[1]);
const ry = parseFloat(match[2]);
const rw = parseFloat(match[3]);
const rh = parseFloat(match[4]);
if (rx < selectRect.right && rx + rw > selectRect.left &&
ry < selectRect.bottom && ry + rh > selectRect.top) {
selectWasRepainted = true;
}
}

window.selectWasRepainted = selectWasRepainted;
shouldBeFalse("selectWasRepainted");
}

finishJSTest();
})();
</script>
</body>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(repaint rects
(rect 0 0 50 50)
(rect 0 0 200 200)
(rect 0 0 200 200)
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(repaint rects
(rect 0 0 200 130)
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<script src="resources/text-based-repaint.js"></script>
<style>
#parent {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 100px;
background: blue;
overflow: visible;
}
#child {
width: 200px;
height: 50px;
background: green;
transform: translateY(80px);
}
</style>
<script>
function repaintTest()
{
document.getElementById("parent").style.background = "red";
}
</script>
</head>
<body onload="runRepaintTest();">
<!-- The child extends from y=80 to y=130, 30px beyond the parent's border box.
With overflow: visible, the parent's visualOverflowRect should include the child. -->
<div id="parent">
<div id="child"></div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ PASS if green boxes are aligned.
(repaint rects
(rect 8 26 20 20)
(rect 108 26 20 20)
(rect 8 26 20 20)
(rect 108 26 20 20)
(rect 8 26 20 40)
(rect 108 26 20 40)
(rect 8 46 20 20)
(rect 108 46 20 20)
(rect 8 66 20 20)
(rect 108 66 20 20)
(rect 8 66 20 40)
(rect 108 66 20 40)
(rect 8 86 20 20)
(rect 108 86 20 20)
(rect 8 106 20 20)
(rect 108 106 20 20)
(rect 8 106 20 40)
(rect 108 106 20 40)
(rect 8 126 20 20)
(rect 108 126 20 20)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(repaint rects
(rect 0 168 800 28)
(rect 40 50 208 118)
(rect 32 50 201 147)
)
Expand Down
2 changes: 2 additions & 0 deletions LayoutTests/fast/repaint/transform-table-layout-expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
(rect 8 108 2 2)
(rect 8 109 100 1)
(rect 8 108 100 2)
(rect 8 109 784 1)
(rect 8 9 784 1)
(rect 0 109 800 1)
(rect 0 17 800 1)
(rect 8 109 100 1)
)
Expand Down
6 changes: 4 additions & 2 deletions Source/WebCore/html/HTMLOptionElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,10 @@ void HTMLOptionElement::willResetComputedStyle()
// FIXME: This is nasty, we ask our owner select to repaint even if the new
// style is exactly the same.
if (RefPtr select = ownerSelectElement()) {
if (CheckedPtr renderer = select->renderer())
renderer->repaint();
if (CheckedPtr renderer = select->renderer()) {
if (renderer->style().usedAppearance() != StyleAppearance::Base)
renderer->repaint();
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/rendering/RenderBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4658,7 +4658,7 @@ void RenderBox::addOverflowWithRendererOffset(const RenderBox& renderer, LayoutS
} else {
// Update our visual overflow in case the child spills out the block, but only if we were going to paint
// the child block ourselves.
if (renderer.hasSelfPaintingLayer() && !hasFilter())
if (renderer.hasSelfPaintingLayer() && !hasFilter() && (!renderer.isTransformed() || renderer.layer()->has3DTransform()))
return;
}
if (!childVisualOverflowRect)
Expand Down
14 changes: 9 additions & 5 deletions Source/WebCore/rendering/RenderLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5564,8 +5564,12 @@ LayoutRect RenderLayer::localBoundingBox(OptionSet<CalculateLayerBoundsFlag> fla
if (!(flags & DontConstrainForMask) && box->hasMask()) {
result = box->maskClipRect(LayoutPoint());
box->flipForWritingMode(result); // The mask clip rect is in physical coordinates, so we have to flip, since localBoundingBox is not.
} else
result = box->visualOverflowRect();
} else {
if (flags & ExcludeSelfPaintingChildOverflow)
result = box->applyVisualEffectOverflow(box->borderBoxRect());
else
result = box->visualOverflowRect();
}

if (flags.contains(IncludeRootBackgroundPaintingArea) && renderer().isDocumentElementRenderer()) {
// If the root layer becomes composited (e.g. because some descendant with negative z-index is composited),
Expand Down Expand Up @@ -5623,7 +5627,7 @@ LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, const Layo
bool RenderLayer::getOverlapBoundsIncludingChildrenAccountingForTransformAnimations(LayoutRect& bounds, OptionSet<CalculateLayerBoundsFlag> additionalFlags) const
{
// The animation will override the display transform, so don't include it.
auto boundsFlags = additionalFlags | (defaultCalculateLayerBoundsFlags() - IncludeSelfTransform);
auto boundsFlags = additionalFlags | (defaultCalculateLayerBoundsFlags() - IncludeSelfTransform) | ExcludeSelfPaintingChildOverflow;

bounds = calculateLayerBounds(this, LayoutSize(), boundsFlags);

Expand Down Expand Up @@ -5653,9 +5657,9 @@ FloatRect RenderLayer::absoluteBoundingBoxForPainting() const
LayoutRect RenderLayer::overlapBounds() const
{
if (overlapBoundsIncludeChildren())
return calculateLayerBounds(this, { }, { UseLocalClipRectExcludingCompositingIfPossible, IncludeFilterOutsets, UseFragmentBoxesExcludingCompositing });
return calculateLayerBounds(this, { }, { UseLocalClipRectExcludingCompositingIfPossible, IncludeFilterOutsets, UseFragmentBoxesExcludingCompositing, ExcludeSelfPaintingChildOverflow });

return localBoundingBox();
return localBoundingBox({ ExcludeSelfPaintingChildOverflow });
}
LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, OptionSet<CalculateLayerBoundsFlag> flags) const
{
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/rendering/RenderLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ class RenderLayer final : public UniquelyOwned<RenderLayer> {
PreserveAncestorFlags = 1 << 10,
UseLocalClipRectExcludingCompositingIfPossible = 1 << 11,
ExcludeViewTransitionCapturedDescendants = 1 << 12,
ExcludeSelfPaintingChildOverflow = 1 << 13,
};
static constexpr OptionSet<CalculateLayerBoundsFlag> defaultCalculateLayerBoundsFlags() { return { IncludeSelfTransform, UseLocalClipRectIfPossible, IncludePaintedFilterOutsets, UseFragmentBoxesExcludingCompositing }; }

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/rendering/RenderLayerBacking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@ static bool NODELETE hasNonZeroTransformOrigin(const RenderLayerModelObject& ren

bool RenderLayerBacking::updateCompositedBounds()
{
LayoutRect layerBounds = m_owningLayer.calculateLayerBounds(&m_owningLayer, { }, RenderLayer::defaultCalculateLayerBoundsFlags() | RenderLayer::ExcludeHiddenDescendants | RenderLayer::DontConstrainForMask);
LayoutRect layerBounds = m_owningLayer.calculateLayerBounds(&m_owningLayer, { }, RenderLayer::defaultCalculateLayerBoundsFlags() | RenderLayer::ExcludeHiddenDescendants | RenderLayer::DontConstrainForMask | RenderLayer::ExcludeSelfPaintingChildOverflow);
// Clip to the size of the document or enclosing overflow-scroll layer.
// If this or an ancestor is transformed, we can't currently compute the correct rect to intersect with.
// We'd need RenderObject::convertContainerToLocalQuad(), which doesn't yet exist.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,6 @@ inline bool RenderStyle::preserveNewline() const
inline bool RenderStyle::affectsTransform() const
{
return !transform().isNone()
|| !offsetPath().isNone()
|| !offsetPath().isNone()
|| !rotate().isNone()
|| !scale().isNone()
Expand Down