diff --git a/src/browser/tests/element/html/script/supports.html b/src/browser/tests/element/html/script/supports.html new file mode 100644 index 000000000..4011b71b0 --- /dev/null +++ b/src/browser/tests/element/html/script/supports.html @@ -0,0 +1,12 @@ + + + + diff --git a/src/browser/tests/element/rel_list.html b/src/browser/tests/element/rel_list.html new file mode 100644 index 000000000..28b231753 --- /dev/null +++ b/src/browser/tests/element/rel_list.html @@ -0,0 +1,23 @@ + + + + +
+ + + + diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig index ee7a5f964..2c40b7a35 100644 --- a/src/browser/webapi/collections/DOMTokenList.zig +++ b/src/browser/webapi/collections/DOMTokenList.zig @@ -69,6 +69,22 @@ pub fn item(self: *const DOMTokenList, index: usize, frame: *Frame) !?[]const u8 return null; } +/// https://dom.spec.whatwg.org/#dom-domtokenlist-supports +/// Only `rel` defines supported tokens here; per spec every other backing +/// attribute throws. Loaders probe `relList.supports("modulepreload")` and +/// fall back to fetch()-based legacy loading when it fails. +pub fn supports(self: *const DOMTokenList, token: []const u8, frame: *Frame) !bool { + if (!std.ascii.eqlIgnoreCase(self._attribute_name.str(), "rel")) { + return error.TypeError; + } + const supported = [_][]const u8{ "stylesheet", "preload", "modulepreload" }; + const lower = try std.ascii.allocLowerString(frame.local_arena, token); + for (supported) |s| { + if (std.mem.eql(u8, lower, s)) return true; + } + return false; +} + pub fn contains(self: *const DOMTokenList, search: []const u8) !bool { var it = std.mem.tokenizeAny(u8, self.getValue(), WHITESPACE); while (it.next()) |token| { @@ -310,6 +326,7 @@ pub const JsApi = struct { } pub const contains = bridge.function(DOMTokenList.contains, .{ .dom_exception = true }); + pub const supports = bridge.function(DOMTokenList.supports, .{ .dom_exception = true }); pub const add = bridge.function(DOMTokenList.add, .{ .dom_exception = true, .ce_reactions = true }); pub const remove = bridge.function(DOMTokenList.remove, .{ .dom_exception = true, .ce_reactions = true }); pub const toggle = bridge.function(DOMTokenList.toggle, .{ .dom_exception = true, .ce_reactions = true }); diff --git a/src/browser/webapi/element/html/Script.zig b/src/browser/webapi/element/html/Script.zig index 064b706bd..8b569ceb5 100644 --- a/src/browser/webapi/element/html/Script.zig +++ b/src/browser/webapi/element/html/Script.zig @@ -115,6 +115,17 @@ pub fn setInnerText(self: *Script, text: []const u8, frame: *Frame) !void { try self.asNode().setTextContent(text, frame); } +/// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports +/// Only the types this engine actually loads; loaders feature-detect module +/// support here and fall back to fetch()-based legacy loading when it fails. +pub fn supports(value: []const u8) bool { + const supported = [_][]const u8{ "classic", "module", "importmap" }; + for (supported) |s| { + if (std.mem.eql(u8, value, s)) return true; + } + return false; +} + pub const JsApi = struct { pub const bridge = js.Bridge(Script); @@ -131,6 +142,7 @@ pub const JsApi = struct { pub const nonce = bridge.accessor(Script.getNonce, Script.setNonce, .{ .ce_reactions = true }); pub const charset = bridge.accessor(Script.getCharset, Script.setCharset, .{ .ce_reactions = true }); pub const noModule = bridge.accessor(Script.getNoModule, null, .{}); + pub const supports = bridge.function(Script.supports, .{ .static = true }); pub const innerText = bridge.accessor(_innerText, Script.setInnerText, .{ .ce_reactions = true }); fn _innerText(self: *Script, frame: *const Frame) ![]const u8 { var buf = std.Io.Writer.Allocating.init(frame.call_arena);