From 71d39c7e6f933144c0b39ec03566a24e4c8f66e2 Mon Sep 17 00:00:00 2001 From: Iuri de Silvio Date: Mon, 18 May 2026 09:34:06 +0200 Subject: [PATCH] Retain source Image/Canvas in CanvasPattern so the cairo surface stays valid Pattern stores a cairo_pattern_t* created from a source Image or Canvas via cairo_pattern_create_for_surface(). The pattern references the cairo surface internally but does not retain the JS source object. If the source is garbage-collected, Canvas::destroySurface() calls cairo_surface_finish(_surface) followed by cairo_surface_destroy(_surface). A finished surface is no longer a valid live backing store for pattern rendering, even if cairo still holds a reference to it via the pattern. The bug is reliably reproducible when finalizers run promptly (e.g. with NAPI_EXPERIMENTAL enabled and node_api_post_finalizer in use); without that path, the source typically survives long enough by accident. Fix: hold a Napi::Reference to the source JS object in Pattern and reset it from Pattern::Finalize(). Fixes #2579 --- src/CanvasPattern.cc | 5 +++++ src/CanvasPattern.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/CanvasPattern.cc b/src/CanvasPattern.cc index 1645d6db8..ce2c88502 100644 --- a/src/CanvasPattern.cc +++ b/src/CanvasPattern.cc @@ -64,6 +64,7 @@ Pattern::Pattern(const Napi::CallbackInfo& info) : ObjectWrap(info), en return; } _pattern = cairo_pattern_create_for_surface(surface); + _source = Napi::Persistent(obj); if (info[1].IsString()) { if ("no-repeat" == info[1].As().Utf8Value()) { @@ -127,3 +128,7 @@ repeat_type_t Pattern::get_repeat_type_for_cairo_pattern(cairo_pattern_t *patter Pattern::~Pattern() { if (_pattern) cairo_pattern_destroy(_pattern); } + +void Pattern::Finalize(Napi::Env env) { + _source.Reset(); +} diff --git a/src/CanvasPattern.h b/src/CanvasPattern.h index 1f768e03b..55a0119b9 100644 --- a/src/CanvasPattern.h +++ b/src/CanvasPattern.h @@ -26,8 +26,10 @@ class Pattern : public Napi::ObjectWrap { static repeat_type_t get_repeat_type_for_cairo_pattern(cairo_pattern_t *pattern); inline cairo_pattern_t *pattern(){ return _pattern; } ~Pattern(); + void Finalize(Napi::Env env); Napi::Env env; private: cairo_pattern_t *_pattern; + Napi::Reference _source; repeat_type_t _repeat = REPEAT; };