LibWeb: Ensure layout is up to date before resolving canvas colors

This commit is contained in:
Tim Ledbetter 2025-10-27 10:06:59 +00:00 committed by Jelle Raaijmakers
parent 4dbae64dce
commit 9da723b5c6
11 changed files with 210 additions and 56 deletions

View File

@ -71,56 +71,60 @@ enum class InvalidateLayoutTreeReason {
[[nodiscard]] StringView to_string(InvalidateLayoutTreeReason); [[nodiscard]] StringView to_string(InvalidateLayoutTreeReason);
#define ENUMERATE_UPDATE_LAYOUT_REASONS(X) \ #define ENUMERATE_UPDATE_LAYOUT_REASONS(X) \
X(CanvasRenderingContext2DSetFilter) \ X(CanvasRenderingContext2DSetFilter) \
X(CursorBlinkTimer) \ X(CanvasRenderingContext2DSetFillStyle) \
X(Debugging) \ X(CanvasRenderingContext2DSetShadowColor) \
X(DocumentElementFromPoint) \ X(CanvasRenderingContext2DSetStrokeStyle) \
X(DocumentElementsFromPoint) \ X(CanvasSetFillStyle) \
X(DocumentFindMatchingText) \ X(CursorBlinkTimer) \
X(DocumentSetDesignMode) \ X(Debugging) \
X(DumpDisplayList) \ X(DocumentElementFromPoint) \
X(ElementCheckVisibility) \ X(DocumentElementsFromPoint) \
X(ElementClientHeight) \ X(DocumentFindMatchingText) \
X(ElementClientLeft) \ X(DocumentSetDesignMode) \
X(ElementClientTop) \ X(DumpDisplayList) \
X(ElementClientWidth) \ X(ElementCheckVisibility) \
X(ElementGetClientRects) \ X(ElementClientHeight) \
X(ElementIsPotentiallyScrollable) \ X(ElementClientLeft) \
X(ElementScroll) \ X(ElementClientTop) \
X(ElementScrollHeight) \ X(ElementClientWidth) \
X(ElementScrollIntoView) \ X(ElementGetClientRects) \
X(ElementScrollLeft) \ X(ElementIsPotentiallyScrollable) \
X(ElementScrollTop) \ X(ElementScroll) \
X(ElementScrollWidth) \ X(ElementScrollHeight) \
X(ElementSetScrollLeft) \ X(ElementScrollIntoView) \
X(ElementSetScrollTop) \ X(ElementScrollLeft) \
X(EventHandlerHandleDoubleClick) \ X(ElementScrollTop) \
X(EventHandlerHandleDragAndDrop) \ X(ElementScrollWidth) \
X(EventHandlerHandleMouseDown) \ X(ElementSetScrollLeft) \
X(EventHandlerHandleMouseMove) \ X(ElementSetScrollTop) \
X(EventHandlerHandleMouseUp) \ X(EventHandlerHandleDoubleClick) \
X(EventHandlerHandleMouseWheel) \ X(EventHandlerHandleDragAndDrop) \
X(HTMLElementGetTheTextSteps) \ X(EventHandlerHandleMouseDown) \
X(HTMLElementOffsetHeight) \ X(EventHandlerHandleMouseMove) \
X(HTMLElementOffsetLeft) \ X(EventHandlerHandleMouseUp) \
X(HTMLElementOffsetParent) \ X(EventHandlerHandleMouseWheel) \
X(HTMLElementOffsetTop) \ X(HTMLElementGetTheTextSteps) \
X(HTMLElementOffsetWidth) \ X(HTMLElementOffsetHeight) \
X(HTMLElementScrollParent) \ X(HTMLElementOffsetLeft) \
X(HTMLEventLoopRenderingUpdate) \ X(HTMLElementOffsetParent) \
X(HTMLImageElementHeight) \ X(HTMLElementOffsetTop) \
X(HTMLImageElementWidth) \ X(HTMLElementOffsetWidth) \
X(HTMLInputElementHeight) \ X(HTMLElementScrollParent) \
X(HTMLInputElementWidth) \ X(HTMLEventLoopRenderingUpdate) \
X(InternalsHitTest) \ X(HTMLImageElementHeight) \
X(MediaQueryListMatches) \ X(HTMLImageElementWidth) \
X(NodeNameOrDescription) \ X(HTMLInputElementHeight) \
X(RangeGetClientRects) \ X(HTMLInputElementWidth) \
X(ResolvedCSSStyleDeclarationProperty) \ X(InternalsHitTest) \
X(SVGDecodedImageDataRender) \ X(MediaQueryListMatches) \
X(SVGGraphicsElementGetBBox) \ X(NodeNameOrDescription) \
X(SourceSetNormalizeSourceDensities) \ X(RangeGetClientRects) \
X(ResolvedCSSStyleDeclarationProperty) \
X(SVGDecodedImageDataRender) \
X(SVGGraphicsElementGetBBox) \
X(SourceSetNormalizeSourceDensities) \
X(WindowScroll) X(WindowScroll)
enum class UpdateLayoutReason { enum class UpdateLayoutReason {

View File

@ -43,8 +43,10 @@ void CanvasFillStrokeStyles<IncludingClass>::set_fill_style(FillOrStrokeStyleVar
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
CSS::ColorResolutionContext color_resolution_context {}; CSS::ColorResolutionContext color_resolution_context {};
if (context && context->layout_node()) { if (context) {
color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*context->layout_node()); context->document().update_layout(DOM::UpdateLayoutReason::CanvasRenderingContext2DSetFillStyle);
if (context->layout_node())
color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*context->layout_node());
} }
auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black); auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black);
@ -97,8 +99,10 @@ void CanvasFillStrokeStyles<IncludingClass>::set_stroke_style(FillOrStrokeStyleV
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
CSS::ColorResolutionContext color_resolution_context {}; CSS::ColorResolutionContext color_resolution_context {};
if (context && context->layout_node()) { if (context) {
color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*context->layout_node()); context->document().update_layout(DOM::UpdateLayoutReason::CanvasRenderingContext2DSetStrokeStyle);
if (context->layout_node())
color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*context->layout_node());
} }
auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black); auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black);

View File

@ -1053,9 +1053,9 @@ void CanvasRenderingContext2D::set_shadow_color(String color)
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color); auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
if (style_value && style_value->has_color()) { if (style_value && style_value->has_color()) {
CSS::ColorResolutionContext color_resolution_context {}; CSS::ColorResolutionContext color_resolution_context {};
context.document().update_layout(DOM::UpdateLayoutReason::CanvasRenderingContext2DSetShadowColor);
if (auto node = context.layout_node()) { if (auto node = context.layout_node()) {
color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*context.layout_node()); color_resolution_context = CSS::ColorResolutionContext::for_layout_node_with_style(*node);
} }
auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black); auto parsedValue = style_value->to_color(color_resolution_context).value_or(Color::Black);

View File

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass currentColor is computed from the canvas element

View File

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass currentColor is computed when the attribute is set, not when it is painted

View File

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass currentColor is computed from the canvas element

View File

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass currentColor is computed when the attribute is set, not when it is painted

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.fillStyle.parse.current.basic</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.fillStyle.parse.current.basic</h1>
<p class="desc">currentColor is computed from the canvas element</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("currentColor is computed from the canvas element");
_addTest(function(canvas, ctx) {
canvas.setAttribute('style', 'color: #0f0');
ctx.fillStyle = '#f00';
ctx.fillStyle = 'currentColor';
ctx.fillRect(0, 0, 100, 50);
_assertPixel(canvas, 50,25, 0,255,0,255);
});
</script>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.fillStyle.parse.current.changed</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.fillStyle.parse.current.changed</h1>
<p class="desc">currentColor is computed when the attribute is set, not when it is painted</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("currentColor is computed when the attribute is set, not when it is painted");
_addTest(function(canvas, ctx) {
canvas.setAttribute('style', 'color: #0f0');
ctx.fillStyle = '#f00';
ctx.fillStyle = 'currentColor';
canvas.setAttribute('style', 'color: #f00');
ctx.fillRect(0, 0, 100, 50);
_assertPixel(canvas, 50,25, 0,255,0,255);
});
</script>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.shadow.attributes.shadowColor.current.basic</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.shadow.attributes.shadowColor.current.basic</h1>
<p class="desc">currentColor is computed from the canvas element</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<ul id="d"></ul>
<script>
var t = async_test("currentColor is computed from the canvas element");
_addTest(function(canvas, ctx) {
canvas.style.color = '#0f0';
ctx.shadowColor = 'currentColor';
_assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
});
</script>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.shadow.attributes.shadowColor.current.changed</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.shadow.attributes.shadowColor.current.changed</h1>
<p class="desc">currentColor is computed when the attribute is set, not when it is painted</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<ul id="d"></ul>
<script>
var t = async_test("currentColor is computed when the attribute is set, not when it is painted");
_addTest(function(canvas, ctx) {
canvas.style.color = '#0f0';
ctx.shadowColor = 'currentColor';
canvas.style.color = '#f00';
_assertSame(ctx.shadowColor, '#00ff00', "ctx.shadowColor", "'#00ff00'");
});
</script>