return null on getContext('webgl') and getContext('experimental-webgl')

Currently, we return a dummy `WebGLRenderingContext`. But this type implements
virtually nothing. From MDN: "If the context identifier is not supported null is
returned."

Now, normally we like to move things along as much as we can. Returning
`WebGLRenderingContext` lets us know the next thing the code needs. But in this
case, it seems to be problematic. At least some code is defensive around a null
value from getContext('webgl'), but once we return an instance, it expects it
to work correctly. This is what causes https://github.com/features/copilot to
enter an endless JS loop.
This commit is contained in:
Karl Seguin
2026-04-27 14:56:55 +08:00
parent a578f4d6ad
commit a205bbbf7d
2 changed files with 15 additions and 6 deletions

View File

@@ -204,7 +204,8 @@ pub const JsApi = struct {
pub const getSupportedExtensions = bridge.function(WebGLRenderingContext.getSupportedExtensions, .{});
};
const testing = @import("../../../testing.zig");
test "WebApi: WebGLRenderingContext" {
try testing.htmlRunner("canvas/webgl_rendering_context.html", .{});
}
// getContext('web-gl') currently returns null, so this cannot be tested
// const testing = @import("../../../testing.zig");
// test "WebApi: WebGLRenderingContext" {
// try testing.htmlRunner("canvas/webgl_rendering_context.html", .{});
// }

View File

@@ -85,9 +85,17 @@ pub fn getContext(self: *Canvas, context_type: []const u8, frame: *Frame) !?Draw
break :blk .{ .@"2d" = ctx };
}
// We only stub a tiny slice of the WebGL API (getParameter,
// getExtension, getSupportedExtensions). Real WebGL consumers like
// Three.js immediately call createTexture/createBuffer/etc. and
// throw `TypeError: e.createTexture is not a function`. Pretending
// WebGL works until the first non-stubbed call is the worst of both
// worlds: pages that have an error boundary above the WebGL widget
// catch the throw, reset, re-render, and loop forever.
// Spec-correct signal for "no WebGL" is null, so apps that check
// (Three.js does) can degrade gracefully.
if (std.mem.eql(u8, context_type, "webgl") or std.mem.eql(u8, context_type, "experimental-webgl")) {
const ctx = try frame._factory.create(WebGLRenderingContext{});
break :blk .{ .webgl = ctx };
return null;
}
return null;
};