From 1f22462f13f7f37af888c057634c96bf6688edb5 Mon Sep 17 00:00:00 2001 From: evan108108 Date: Fri, 27 Mar 2026 21:06:09 -0400 Subject: [PATCH] fix: cache canvas 2D context and lock context type per spec Per the HTML spec, HTMLCanvasElement.getContext() should: 1. Return the same object on repeated calls with the same type 2. Return null if a different context type was already requested Previously, every getContext("2d") call created a new CanvasRenderingContext2D object. This caused issues with code that relies on identity checks (ctx === canvas.getContext("2d")) and wasted memory by allocating duplicate contexts. The fix caches the 2D context and tracks which context type was first requested, returning null for incompatible subsequent calls. --- src/browser/webapi/element/html/Canvas.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/browser/webapi/element/html/Canvas.zig b/src/browser/webapi/element/html/Canvas.zig index 13002f66..c01fb651 100644 --- a/src/browser/webapi/element/html/Canvas.zig +++ b/src/browser/webapi/element/html/Canvas.zig @@ -30,6 +30,13 @@ const OffscreenCanvas = @import("../../canvas/OffscreenCanvas.zig"); const Canvas = @This(); _proto: *HtmlElement, +/// Cached context type. Once set, requesting a different type returns null (per spec). +_context_type: ContextType = .none, +/// Cached 2D rendering context (same object returned on repeated getContext("2d") calls). +_ctx_2d: ?*CanvasRenderingContext2D = null, + +const ContextType = enum { none, @"2d", webgl }; + pub fn asElement(self: *Canvas) *Element { return self._proto._proto; } @@ -69,12 +76,23 @@ const DrawingContext = union(enum) { pub fn getContext(self: *Canvas, context_type: []const u8, page: *Page) !?DrawingContext { if (std.mem.eql(u8, context_type, "2d")) { + // Return cached context if available. + if (self._ctx_2d) |cached| return .{ .@"2d" = cached }; + // Per spec: return null if a different context type was already requested. + if (self._context_type != .none) return null; + const ctx = try page._factory.create(CanvasRenderingContext2D{ ._canvas = self }); + self._ctx_2d = ctx; + self._context_type = .@"2d"; return .{ .@"2d" = ctx }; } if (std.mem.eql(u8, context_type, "webgl") or std.mem.eql(u8, context_type, "experimental-webgl")) { + // Per spec: return null if a different context type was already requested. + if (self._context_type != .none and self._context_type != .webgl) return null; + const ctx = try page._factory.create(WebGLRenderingContext{}); + self._context_type = .webgl; return .{ .webgl = ctx }; }