LibWeb: Don't require layout node when setting canvas 2D filter
Setting the filter property on a CanvasRenderingContext2D would crash with a null pointer dereference if the canvas element had no layout node (e.g. a detached canvas not in the document). Instead of forcing a full layout update and requiring a layout node, we now only update style if needed and resolve lengths via the element's computed properties when available, falling back to document-level defaults otherwise. This matches the pattern used by CanvasTextDrawingStyles.
This commit is contained in:
parent
ef6368924e
commit
cbf8b70d42
4 changed files with 32 additions and 11 deletions
|
|
@ -66,7 +66,6 @@ enum class InvalidateLayoutTreeReason {
|
|||
|
||||
#define ENUMERATE_UPDATE_LAYOUT_REASONS(X) \
|
||||
X(AutoScrollSelection) \
|
||||
X(CanvasRenderingContext2DSetFilter) \
|
||||
X(CanvasRenderingContext2DSetFillStyle) \
|
||||
X(CanvasRenderingContext2DSetShadowColor) \
|
||||
X(CanvasRenderingContext2DSetStrokeStyle) \
|
||||
|
|
|
|||
|
|
@ -1218,14 +1218,17 @@ void CanvasRenderingContext2D::set_filter(String filter)
|
|||
auto style_value = parser.parse_as_css_value(CSS::PropertyID::Filter);
|
||||
|
||||
if (style_value && style_value->is_filter_value_list()) {
|
||||
// Note: The layout must be updated to make sure the canvas's layout node isn't null.
|
||||
canvas_element().document().update_layout(DOM::UpdateLayoutReason::CanvasRenderingContext2DSetFilter);
|
||||
auto layout_node = canvas_element().layout_node();
|
||||
auto& document = canvas_element().document();
|
||||
DOM::AbstractElement abstract_element { canvas_element() };
|
||||
document.update_style_if_needed_for_element(abstract_element);
|
||||
|
||||
auto length_resolution_context = canvas_element().computed_properties()
|
||||
? CSS::Length::ResolutionContext::for_element(abstract_element)
|
||||
: CSS::Length::ResolutionContext::for_document(document);
|
||||
|
||||
CSS::ComputationContext computation_context {
|
||||
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*layout_node),
|
||||
.abstract_element = DOM::AbstractElement { canvas_element() },
|
||||
.color_scheme = layout_node->computed_values().color_scheme(),
|
||||
.length_resolution_context = length_resolution_context,
|
||||
.abstract_element = abstract_element,
|
||||
};
|
||||
auto filter_value_list = style_value->absolutized(computation_context)->as_filter_value_list().filter_value_list();
|
||||
|
||||
|
|
@ -1266,10 +1269,11 @@ void CanvasRenderingContext2D::set_filter(String filter)
|
|||
radius = static_cast<float>(CSS::Length::from_style_value(*drop_shadow.radius, {}).absolute_length_to_px());
|
||||
};
|
||||
|
||||
auto color_context = CSS::ColorResolutionContext::for_layout_node_with_style(*layout_node);
|
||||
auto color = drop_shadow.color
|
||||
? drop_shadow.color->to_color(color_context).value_or(Gfx::Color::Black)
|
||||
: Gfx::Color::Black;
|
||||
Gfx::Color color = Gfx::Color::Black;
|
||||
if (drop_shadow.color && canvas_element().computed_properties()) {
|
||||
auto color_context = CSS::ColorResolutionContext::for_element(abstract_element);
|
||||
color = drop_shadow.color->to_color(color_context).value_or(Gfx::Color::Black);
|
||||
}
|
||||
|
||||
auto new_filter = Gfx::Filter::drop_shadow(offset_x, offset_y, radius, color);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
blur(5px)
|
||||
sepia(0.5)
|
||||
none
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
// Setting filter on a detached canvas (no layout node) should not crash.
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.filter = "blur(5px)";
|
||||
println(ctx.filter);
|
||||
ctx.filter = "sepia(0.5)";
|
||||
println(ctx.filter);
|
||||
ctx.filter = "none";
|
||||
println(ctx.filter);
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue