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.
This commit is contained in:
evan108108
2026-03-27 21:06:09 -04:00
parent 03ed45637a
commit 1f22462f13

View File

@@ -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 };
}