From a205bbbf7dbe95c1570a787a2657da9364fdfc50 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 27 Apr 2026 14:56:55 +0800 Subject: [PATCH] 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. --- src/browser/webapi/canvas/WebGLRenderingContext.zig | 9 +++++---- src/browser/webapi/element/html/Canvas.zig | 12 ++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/browser/webapi/canvas/WebGLRenderingContext.zig b/src/browser/webapi/canvas/WebGLRenderingContext.zig index ba4b6938..28dcce39 100644 --- a/src/browser/webapi/canvas/WebGLRenderingContext.zig +++ b/src/browser/webapi/canvas/WebGLRenderingContext.zig @@ -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", .{}); +// } diff --git a/src/browser/webapi/element/html/Canvas.zig b/src/browser/webapi/element/html/Canvas.zig index e1648f5d..da2bfb43 100644 --- a/src/browser/webapi/element/html/Canvas.zig +++ b/src/browser/webapi/element/html/Canvas.zig @@ -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; };