Merge pull request #1671 from lightpanda-io/custom_element_name
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled

Fix our custom element name validation
This commit is contained in:
Pierre Tachoire
2026-02-27 14:08:13 +01:00
committed by GitHub

View File

@@ -24,6 +24,7 @@ const Page = @import("../Page.zig");
const Node = @import("Node.zig"); const Node = @import("Node.zig");
const Element = @import("Element.zig"); const Element = @import("Element.zig");
const DOMException = @import("DOMException.zig");
const Custom = @import("element/html/Custom.zig"); const Custom = @import("element/html/Custom.zig");
const CustomElementDefinition = @import("CustomElementDefinition.zig"); const CustomElementDefinition = @import("CustomElementDefinition.zig");
@@ -124,6 +125,10 @@ pub fn whenDefined(self: *CustomElementRegistry, name: []const u8, page: *Page)
return local.resolvePromise(definition.constructor); return local.resolvePromise(definition.constructor);
} }
validateName(name) catch |err| {
return local.rejectPromise(DOMException.fromError(err) orelse unreachable);
};
const gop = try self._when_defined.getOrPut(page.arena, name); const gop = try self._when_defined.getOrPut(page.arena, name);
if (gop.found_existing) { if (gop.found_existing) {
return local.toLocal(gop.value_ptr.*).promise(); return local.toLocal(gop.value_ptr.*).promise();
@@ -200,15 +205,15 @@ pub fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinitio
fn validateName(name: []const u8) !void { fn validateName(name: []const u8) !void {
if (name.len == 0) { if (name.len == 0) {
return error.InvalidCustomElementName; return error.SyntaxError;
} }
if (std.mem.indexOf(u8, name, "-") == null) { if (std.mem.indexOf(u8, name, "-") == null) {
return error.InvalidCustomElementName; return error.SyntaxError;
} }
if (name[0] < 'a' or name[0] > 'z') { if (name[0] < 'a' or name[0] > 'z') {
return error.InvalidCustomElementName; return error.SyntaxError;
} }
const reserved_names = [_][]const u8{ const reserved_names = [_][]const u8{
@@ -224,16 +229,20 @@ fn validateName(name: []const u8) !void {
for (reserved_names) |reserved| { for (reserved_names) |reserved| {
if (std.mem.eql(u8, name, reserved)) { if (std.mem.eql(u8, name, reserved)) {
return error.InvalidCustomElementName; return error.SyntaxError;
} }
} }
for (name) |c| { for (name) |c| {
const valid = (c >= 'a' and c <= 'z') or if (c >= 'A' and c <= 'Z') {
(c >= '0' and c <= '9') or return error.SyntaxError;
c == '-'; }
if (!valid) {
return error.InvalidCustomElementName; // Reject control characters and specific invalid characters
// per elementLocalNameRegex: [^\0\t\n\f\r\u0020/>]*
switch (c) {
0, '\t', '\n', '\r', 0x0C, ' ', '/', '>' => return error.SyntaxError,
else => {},
} }
} }
} }
@@ -250,7 +259,7 @@ pub const JsApi = struct {
pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true }); pub const define = bridge.function(CustomElementRegistry.define, .{ .dom_exception = true });
pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true }); pub const get = bridge.function(CustomElementRegistry.get, .{ .null_as_undefined = true });
pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{}); pub const upgrade = bridge.function(CustomElementRegistry.upgrade, .{});
pub const whenDefined = bridge.function(CustomElementRegistry.whenDefined, .{}); pub const whenDefined = bridge.function(CustomElementRegistry.whenDefined, .{ .dom_exception = true });
}; };
const testing = @import("../../testing.zig"); const testing = @import("../../testing.zig");