diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 403c22b2..6fbd184a 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -1037,7 +1037,7 @@ pub fn scriptAddedCallback(self: *Page, comptime from_parser: bool, script: *Ele self._script_manager.addFromElement(from_parser, script, "parsing") catch |err| { log.err(.page, "page.scriptAddedCallback", .{ .err = err, - .src = script.asElement().getAttributeSafe("src"), + .src = script.asElement().getAttributeSafe(comptime .literal("src")), }); }; } @@ -1122,7 +1122,7 @@ pub fn getElementByIdFromNode(self: *Page, node: *Node, id: []const u8) ?*Elemen } var tw = @import("webapi/TreeWalker.zig").Full.Elements.init(node, .{}); while (tw.next()) |el| { - const element_id = el.getAttributeSafe("id") orelse continue; + const element_id = el.getAttributeSafe(comptime .literal("id")) orelse continue; if (std.mem.eql(u8, element_id, id)) { return el; } @@ -1696,7 +1696,7 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const // If page's base url is not already set, fill it with the base // tag. if (self.base_url == null) { - if (n.as(Element).getAttributeSafe("href")) |href| { + if (n.as(Element).getAttributeSafe(comptime .literal("href"))) |href| { self.base_url = try URL.resolve(self.arena, self.url, href, .{}); } } @@ -2307,7 +2307,7 @@ pub fn removeNode(self: *Page, parent: *Node, child: *Node, opts: RemoveNodeOpts if (parent.is(Element)) |parent_el| { if (self._element_shadow_roots.get(parent_el)) |shadow_root| { // Signal slot changes for any affected slots - const slot_name = el.getAttributeSafe("slot") orelse ""; + const slot_name = el.getAttributeSafe(comptime .literal("slot")) orelse ""; var tw = @import("webapi/TreeWalker.zig").Full.Elements.init(shadow_root.asNode(), .{}); while (tw.next()) |slot_el| { if (slot_el.is(Element.Html.Slot)) |slot| { @@ -2346,7 +2346,7 @@ pub fn removeNode(self: *Page, parent: *Node, child: *Node, opts: RemoveNodeOpts // the ID map and invoking disconnectedCallback for custom elements var tw = @import("webapi/TreeWalker.zig").Full.Elements.init(child, .{}); while (tw.next()) |el| { - if (el.getAttributeSafe("id")) |id| { + if (el.getAttributeSafe(comptime .literal("id"))) |id| { self.removeElementIdWithMaps(id_maps.?, id); } @@ -2480,7 +2480,7 @@ pub fn _insertNodeRelative(self: *Page, comptime from_parser: bool, parent: *Nod // For main document parsing, we know nodes are connected (fast path) // For fragment parsing (innerHTML), we need to check connectivity if (child.isConnected() or child.isInShadowTree()) { - if (el.getAttributeSafe("id")) |id| { + if (el.getAttributeSafe(comptime .literal("id"))) |id| { try self.addElementId(parent, el, id); } try Element.Html.Custom.invokeConnectedCallbackOnElement(true, el, self); @@ -2519,7 +2519,7 @@ pub fn _insertNodeRelative(self: *Page, comptime from_parser: bool, parent: *Nod var tw = @import("webapi/TreeWalker.zig").Full.Elements.init(child, .{}); while (tw.next()) |el| { - if (el.getAttributeSafe("id")) |id| { + if (el.getAttributeSafe(comptime .literal("id"))) |id| { try self.addElementId(el.asNode()._parent.?, el, id); } @@ -2622,7 +2622,7 @@ fn updateElementAssignedSlot(self: *Page, element: *Element) void { const parent_el = parent.is(Element) orelse return; const shadow_root = self._element_shadow_roots.get(parent_el) orelse return; - const slot_name = element.getAttributeSafe("slot") orelse ""; + const slot_name = element.getAttributeSafe(comptime .literal("slot")) orelse ""; // Recursively search through the shadow root for a matching slot if (findMatchingSlot(shadow_root.asNode(), slot_name)) |slot| { @@ -2919,7 +2919,7 @@ pub fn handleClick(self: *Page, target: *Node) !void { switch (html_element._type) { .anchor => |anchor| { - const href = element.getAttributeSafe("href") orelse return; + const href = element.getAttributeSafe(comptime .literal("href")) orelse return; if (href.len == 0) { return; } @@ -3015,7 +3015,7 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form const form = form_ orelse return; if (submitter_) |submitter| { - if (submitter.getAttributeSafe("disabled") != null) { + if (submitter.getAttributeSafe(comptime .literal("disabled")) != null) { return; } } @@ -3028,13 +3028,13 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form const transfer_arena = self._session.transfer_arena; - const encoding = form_element.getAttributeSafe("enctype"); + const encoding = form_element.getAttributeSafe(comptime .literal("enctype")); var buf = std.Io.Writer.Allocating.init(transfer_arena); try form_data.write(encoding, &buf.writer); - const method = form_element.getAttributeSafe("method") orelse ""; - var action = form_element.getAttributeSafe("action") orelse self.url; + const method = form_element.getAttributeSafe(comptime .literal("method")) orelse ""; + var action = form_element.getAttributeSafe(comptime .literal("action")) orelse self.url; var opts = NavigateOpts{ .reason = .form, diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index b2b64b86..6a86324d 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -152,14 +152,14 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e script_element._executed = true; const element = script_element.asElement(); - if (element.getAttributeSafe("nomodule") != null) { + if (element.getAttributeSafe(comptime .literal("nomodule")) != null) { // these scripts should only be loaded if we don't support modules // but since we do support modules, we can just skip them. return; } const kind: Script.Kind = blk: { - const script_type = element.getAttributeSafe("type") orelse break :blk .javascript; + const script_type = element.getAttributeSafe(comptime .literal("type")) orelse break :blk .javascript; if (script_type.len == 0) { break :blk .javascript; } @@ -186,7 +186,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e var source: Script.Source = undefined; var remote_url: ?[:0]const u8 = null; const base_url = page.base(); - if (element.getAttributeSafe("src")) |src| { + if (element.getAttributeSafe(comptime .literal("src"))) |src| { if (try parseDataURI(page.arena, src)) |data_uri| { source = .{ .@"inline" = data_uri }; } else { @@ -217,12 +217,12 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e break :blk if (kind == .module) .@"defer" else .normal; } - if (element.getAttributeSafe("async") != null) { + if (element.getAttributeSafe(comptime .literal("async")) != null) { break :blk .async; } // Check for defer or module (before checking dynamic script default) - if (kind == .module or element.getAttributeSafe("defer") != null) { + if (kind == .module or element.getAttributeSafe(comptime .literal("defer")) != null) { break :blk .@"defer"; } diff --git a/src/browser/dump.zig b/src/browser/dump.zig index ab863eef..3d852146 100644 --- a/src/browser/dump.zig +++ b/src/browser/dump.zig @@ -110,7 +110,7 @@ fn _deep(node: *Node, opts: Opts, comptime force_slot: bool, writer: *std.Io.Wri // to render that "active" content, so when we're trying to render // it, we don't want to skip it. if ((comptime force_slot == false) and opts.shadow == .rendered) { - if (el.getAttributeSafe("slot")) |_| { + if (el.getAttributeSafe(comptime .literal("slot"))) |_| { // Skip - will be rendered by the Slot if it's the active container return; } @@ -253,12 +253,12 @@ fn shouldStripElement(el: *const Node.Element, opts: Opts) bool { if (std.mem.eql(u8, tag_name, "noscript")) return true; if (std.mem.eql(u8, tag_name, "link")) { - if (el.getAttributeSafe("as")) |as| { + if (el.getAttributeSafe(comptime .literal("as"))) |as| { if (std.mem.eql(u8, as, "script")) return true; } - if (el.getAttributeSafe("rel")) |rel| { + if (el.getAttributeSafe(comptime .literal("rel"))) |rel| { if (std.mem.eql(u8, rel, "modulepreload") or std.mem.eql(u8, rel, "preload")) { - if (el.getAttributeSafe("as")) |as| { + if (el.getAttributeSafe(comptime .literal("as"))) |as| { if (std.mem.eql(u8, as, "script")) return true; } } @@ -270,7 +270,7 @@ fn shouldStripElement(el: *const Node.Element, opts: Opts) bool { if (std.mem.eql(u8, tag_name, "style")) return true; if (std.mem.eql(u8, tag_name, "link")) { - if (el.getAttributeSafe("rel")) |rel| { + if (el.getAttributeSafe(comptime .literal("rel"))) |rel| { if (std.mem.eql(u8, rel, "stylesheet")) return true; } } diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 4f294f17..ffb1876d 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -624,14 +624,14 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T { if (!js_val.isString()) { return null; } - return try self.valueToStringSSO(js_val, .{.allocator = self.ctx.call_arena}); + return try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.call_arena }); }, string.Global => { if (!js_val.isString()) { return null; } // Use arena for persistent strings - return .{.str = try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.arena }) }; + return .{ .str = try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.arena }) }; }, else => { if (!js_val.isObject()) { diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index fe61346f..e3b11987 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -199,7 +199,7 @@ pub fn getElementById(self: *Document, id: []const u8, page: *Page) ?*Element { if (self._removed_ids.remove(id)) { var tw = @import("TreeWalker.zig").Full.Elements.init(self.asNode(), .{}); while (tw.next()) |el| { - const element_id = el.getAttributeSafe("id") orelse continue; + const element_id = el.getAttributeSafe(comptime .literal("id")) orelse continue; if (std.mem.eql(u8, element_id, id)) { // we ignore this error to keep getElementById easy to call // if it really failed, then we're out of memory and nothing's diff --git a/src/browser/webapi/DocumentFragment.zig b/src/browser/webapi/DocumentFragment.zig index cbd77b4f..3f933fdc 100644 --- a/src/browser/webapi/DocumentFragment.zig +++ b/src/browser/webapi/DocumentFragment.zig @@ -74,7 +74,7 @@ pub fn getElementById(self: *DocumentFragment, id: []const u8) ?*Element { var tw = @import("TreeWalker.zig").Full.Elements.init(self.asNode(), .{}); while (tw.next()) |el| { - if (el.getAttributeSafe("id")) |element_id| { + if (el.getAttributeSafe(comptime .literal("id"))) |element_id| { if (std.mem.eql(u8, element_id, id)) { return el; } diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index d73b5ab5..456369cf 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -427,7 +427,7 @@ pub fn setInnerHTML(self: *Element, html: []const u8, page: *Page) !void { } pub fn getId(self: *const Element) []const u8 { - return self.getAttributeSafe("id") orelse ""; + return self.getAttributeSafe(comptime .literal("id")) orelse ""; } pub fn setId(self: *Element, value: []const u8, page: *Page) !void { @@ -435,7 +435,7 @@ pub fn setId(self: *Element, value: []const u8, page: *Page) !void { } pub fn getSlot(self: *const Element) []const u8 { - return self.getAttributeSafe("slot") orelse ""; + return self.getAttributeSafe(comptime .literal("slot")) orelse ""; } pub fn setSlot(self: *Element, value: []const u8, page: *Page) !void { @@ -443,7 +443,7 @@ pub fn setSlot(self: *Element, value: []const u8, page: *Page) !void { } pub fn getDir(self: *const Element) []const u8 { - return self.getAttributeSafe("dir") orelse ""; + return self.getAttributeSafe(comptime .literal("dir")) orelse ""; } pub fn setDir(self: *Element, value: []const u8, page: *Page) !void { @@ -451,7 +451,7 @@ pub fn setDir(self: *Element, value: []const u8, page: *Page) !void { } pub fn getClassName(self: *const Element) []const u8 { - return self.getAttributeSafe("class") orelse ""; + return self.getAttributeSafe(comptime .literal("class")) orelse ""; } pub fn setClassName(self: *Element, value: []const u8, page: *Page) !void { @@ -484,7 +484,7 @@ pub fn getAttributeNS( return self.getAttribute(local_name, page); } -pub fn getAttributeSafe(self: *const Element, name: []const u8) ?[]const u8 { +pub fn getAttributeSafe(self: *const Element, name: String) ?[]const u8 { const attributes = self._attributes orelse return null; return attributes.getSafe(name); } @@ -495,7 +495,7 @@ pub fn hasAttribute(self: *const Element, name: []const u8, page: *Page) !bool { return value != null; } -pub fn hasAttributeSafe(self: *const Element, name: []const u8) bool { +pub fn hasAttributeSafe(self: *const Element, name: String) bool { const attributes = self._attributes orelse return false; return attributes.hasSafe(name); } @@ -666,7 +666,7 @@ pub fn getClassList(self: *Element, page: *Page) !*collections.DOMTokenList { if (!gop.found_existing) { gop.value_ptr.* = try page._factory.create(collections.DOMTokenList{ ._element = self, - ._attribute_name = "class", + ._attribute_name = comptime .literal("class"), }); } return gop.value_ptr.*; @@ -677,7 +677,7 @@ pub fn getRelList(self: *Element, page: *Page) !*collections.DOMTokenList { if (!gop.found_existing) { gop.value_ptr.* = try page._factory.create(collections.DOMTokenList{ ._element = self, - ._attribute_name = "rel", + ._attribute_name = comptime .literal("rel"), }); } return gop.value_ptr.*; @@ -919,10 +919,10 @@ fn getElementDimensions(self: *Element, page: *Page) !struct { width: f64, heigh if (width == 5.0) width = 1920.0; if (height == 5.0) height = 100_000_000.0; } else if (tag == .img or tag == .iframe) { - if (self.getAttributeSafe("width")) |w| { + if (self.getAttributeSafe(comptime .literal("width"))) |w| { width = std.fmt.parseFloat(f64, w) catch width; } - if (self.getAttributeSafe("height")) |h| { + if (self.getAttributeSafe(comptime .literal("height"))) |h| { height = std.fmt.parseFloat(f64, h) catch height; } } diff --git a/src/browser/webapi/ShadowRoot.zig b/src/browser/webapi/ShadowRoot.zig index 796429c6..25048913 100644 --- a/src/browser/webapi/ShadowRoot.zig +++ b/src/browser/webapi/ShadowRoot.zig @@ -84,7 +84,7 @@ pub fn getElementById(self: *ShadowRoot, id: []const u8, page: *Page) ?*Element // Do a tree walk to find another element with this ID var tw = @import("TreeWalker.zig").Full.Elements.init(self.asNode(), .{}); while (tw.next()) |el| { - const element_id = el.getAttributeSafe("id") orelse continue; + const element_id = el.getAttributeSafe(comptime .literal("id")) orelse continue; if (std.mem.eql(u8, element_id, id)) { // we ignore this error to keep getElementById easy to call // if it really failed, then we're out of memory and nothing's diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig index b6d7b45f..8506175a 100644 --- a/src/browser/webapi/collections/DOMTokenList.zig +++ b/src/browser/webapi/collections/DOMTokenList.zig @@ -18,8 +18,9 @@ const std = @import("std"); const log = @import("../../../log.zig"); -const js = @import("../../js/js.zig"); +const String = @import("../../../string.zig").String; +const js = @import("../../js/js.zig"); const Page = @import("../../Page.zig"); const Element = @import("../Element.zig"); const GenericIterator = @import("iterator.zig").Entry; @@ -31,7 +32,7 @@ pub const DOMTokenList = @This(); // is that lists tend to be very short (often just 1 item). _element: *Element, -_attribute_name: []const u8, +_attribute_name: String, pub const KeyIterator = GenericIterator(Iterator, "0"); pub const ValueIterator = GenericIterator(Iterator, "1"); @@ -160,7 +161,7 @@ pub fn getValue(self: *const DOMTokenList) []const u8 { } pub fn setValue(self: *DOMTokenList, value: []const u8, page: *Page) !void { - try self._element.setAttribute(self._attribute_name, value, page); + try self._element.setAttribute(self._attribute_name.str(), value, page); } pub fn keys(self: *DOMTokenList, page: *Page) !*KeyIterator { @@ -226,7 +227,7 @@ fn validateToken(token: []const u8) !void { fn updateAttribute(self: *DOMTokenList, tokens: Lookup, page: *Page) !void { const joined = try std.mem.join(page.call_arena, " ", tokens.keys()); - try self._element.setAttribute(self._attribute_name, joined, page); + try self._element.setAttribute(self._attribute_name.str(), joined, page); } const Iterator = struct { diff --git a/src/browser/webapi/collections/HTMLAllCollection.zig b/src/browser/webapi/collections/HTMLAllCollection.zig index 8c362837..facb856a 100644 --- a/src/browser/webapi/collections/HTMLAllCollection.zig +++ b/src/browser/webapi/collections/HTMLAllCollection.zig @@ -111,7 +111,7 @@ pub fn getByName(self: *HTMLAllCollection, name: []const u8, page: *Page) ?*Elem while (tw.next()) |node| { if (node.is(Element)) |el| { - if (el.getAttributeSafe("name")) |attr_name| { + if (el.getAttributeSafe(comptime .literal("name"))) |attr_name| { if (std.mem.eql(u8, attr_name, name)) { return el; } diff --git a/src/browser/webapi/collections/HTMLFormControlsCollection.zig b/src/browser/webapi/collections/HTMLFormControlsCollection.zig index 600a1a69..e7f06a14 100644 --- a/src/browser/webapi/collections/HTMLFormControlsCollection.zig +++ b/src/browser/webapi/collections/HTMLFormControlsCollection.zig @@ -59,12 +59,12 @@ pub fn namedItem(self: *HTMLFormControlsCollection, name: []const u8, page: *Pag var it = try self.iterator(); while (it.next()) |element| { const is_match = blk: { - if (element.getAttributeSafe("id")) |id| { + if (element.getAttributeSafe(comptime .literal("id"))) |id| { if (std.mem.eql(u8, id, name)) { break :blk true; } } - if (element.getAttributeSafe("name")) |elem_name| { + if (element.getAttributeSafe(comptime .literal("name"))) |elem_name| { if (std.mem.eql(u8, elem_name, name)) { break :blk true; } diff --git a/src/browser/webapi/collections/RadioNodeList.zig b/src/browser/webapi/collections/RadioNodeList.zig index b126b2a1..fc5347c3 100644 --- a/src/browser/webapi/collections/RadioNodeList.zig +++ b/src/browser/webapi/collections/RadioNodeList.zig @@ -69,7 +69,7 @@ pub fn getValue(self: *RadioNodeList) ![]const u8 { if (!input.getChecked()) { continue; } - return element.getAttributeSafe("value") orelse "on"; + return element.getAttributeSafe(comptime .literal("value")) orelse "on"; } return ""; } @@ -82,7 +82,7 @@ pub fn setValue(self: *RadioNodeList, value: []const u8, page: *Page) !void { continue; } - const input_value = element.getAttributeSafe("value"); + const input_value = element.getAttributeSafe(comptime .literal("value")); const matches_value = blk: { if (std.mem.eql(u8, value, "on")) { break :blk input_value == null or (input_value != null and std.mem.eql(u8, input_value.?, "on")); @@ -99,12 +99,12 @@ pub fn setValue(self: *RadioNodeList, value: []const u8, page: *Page) !void { } fn matches(self: *const RadioNodeList, element: *Element) bool { - if (element.getAttributeSafe("id")) |id| { + if (element.getAttributeSafe(comptime .literal("id"))) |id| { if (std.mem.eql(u8, id, self._name)) { return true; } } - if (element.getAttributeSafe("name")) |elem_name| { + if (element.getAttributeSafe(comptime .literal("name"))) |elem_name| { if (std.mem.eql(u8, elem_name, self._name)) { return true; } diff --git a/src/browser/webapi/collections/node_live.zig b/src/browser/webapi/collections/node_live.zig index abb032cb..3d28ad07 100644 --- a/src/browser/webapi/collections/node_live.zig +++ b/src/browser/webapi/collections/node_live.zig @@ -187,7 +187,7 @@ pub fn NodeLive(comptime mode: Mode) type { // (like length or getAtIndex) var tw = self._tw.clone(); while (self.nextTw(&tw)) |element| { - const element_name = element.getAttributeSafe("name") orelse continue; + const element_name = element.getAttributeSafe(comptime .literal("name")) orelse continue; if (std.mem.eql(u8, element_name, name)) { return element; } @@ -228,7 +228,7 @@ pub fn NodeLive(comptime mode: Mode) type { } const el = node.is(Element) orelse return false; - const class_attr = el.getAttributeSafe("class") orelse return false; + const class_attr = el.getAttributeSafe(comptime .literal("class")) orelse return false; for (self._filter) |class_name| { if (!Selector.classAttributeContains(class_attr, class_name)) { return false; @@ -238,7 +238,7 @@ pub fn NodeLive(comptime mode: Mode) type { }, .name => { const el = node.is(Element) orelse return false; - const name_attr = el.getAttributeSafe("name") orelse return false; + const name_attr = el.getAttributeSafe(comptime .literal("name")) orelse return false; return std.mem.eql(u8, name_attr, self._filter); }, .all_elements => return node._type == .element, @@ -258,14 +258,14 @@ pub fn NodeLive(comptime mode: Mode) type { const el = node.is(Element) orelse return false; const Anchor = Element.Html.Anchor; if (el.is(Anchor) == null) return false; - return el.hasAttributeSafe("href"); + return el.hasAttributeSafe(comptime .literal("href")); }, .anchors => { // Anchors are elements with name attribute const el = node.is(Element) orelse return false; const Anchor = Element.Html.Anchor; if (el.is(Anchor) == null) return false; - return el.hasAttributeSafe("name"); + return el.hasAttributeSafe(comptime .literal("name")); }, .form => { const el = node.is(Element) orelse return false; @@ -273,8 +273,8 @@ pub fn NodeLive(comptime mode: Mode) type { return false; } - if (el.getAttributeSafe("form")) |form_attr| { - const form_id = self._filter.asElement().getAttributeSafe("id") orelse return false; + if (el.getAttributeSafe(comptime .literal("form"))) |form_attr| { + const form_id = self._filter.asElement().getAttributeSafe(comptime .literal("id")) orelse return false; return std.mem.eql(u8, form_attr, form_id); } diff --git a/src/browser/webapi/element/Attribute.zig b/src/browser/webapi/element/Attribute.zig index a1327ef2..a913457a 100644 --- a/src/browser/webapi/element/Attribute.zig +++ b/src/browser/webapi/element/Attribute.zig @@ -170,13 +170,13 @@ pub const List = struct { } // meant for internal usage, where the name is known to be properly cased - pub fn getSafe(self: *const List, name: []const u8) ?[]const u8 { + pub fn getSafe(self: *const List, name: String) ?[]const u8 { const entry = self.getEntryWithNormalizedName(name) orelse return null; return entry._value.str(); } // meant for internal usage, where the name is known to be properly cased - pub fn hasSafe(self: *const List, name: []const u8) bool { + pub fn hasSafe(self: *const List, name: String) bool { return self.getEntryWithNormalizedName(name) != null; } @@ -197,7 +197,7 @@ pub const List = struct { } pub fn putSafe(self: *List, name: []const u8, value: []const u8, element: *Element, page: *Page) !*Entry { - const entry = self.getEntryWithNormalizedName(name); + const entry = self.getEntryWithNormalizedNameOld(name); return self._put(.{ .entry = entry, .normalized = name }, value, element, page); } @@ -331,11 +331,24 @@ pub const List = struct { return .{ .normalized = normalized, - .entry = self.getEntryWithNormalizedName(normalized), + .entry = self.getEntryWithNormalizedNameOld(normalized), }; } - fn getEntryWithNormalizedName(self: *const List, name: []const u8) ?*Entry { + fn getEntryWithNormalizedName(self: *const List, name: String) ?*Entry { + var node = self._list.first; + while (node) |n| { + var e = Entry.fromNode(n); + if (e._name.eql(name)) { + return e; + } + node = n.next; + } + return null; + } + + // TODO remove when we're done making everything String-based + fn getEntryWithNormalizedNameOld(self: *const List, name: []const u8) ?*Entry { var node = self._list.first; while (node) |n| { var e = Entry.fromNode(n); diff --git a/src/browser/webapi/element/html/Anchor.zig b/src/browser/webapi/element/html/Anchor.zig index 1c427ecd..ec216c39 100644 --- a/src/browser/webapi/element/html/Anchor.zig +++ b/src/browser/webapi/element/html/Anchor.zig @@ -40,7 +40,7 @@ pub fn asNode(self: *Anchor) *Node { pub fn getHref(self: *Anchor, page: *Page) ![]const u8 { const element = self.asElement(); - const href = element.getAttributeSafe("href") orelse return ""; + const href = element.getAttributeSafe(comptime .literal("href")) orelse return ""; if (href.len == 0) { return ""; } @@ -52,7 +52,7 @@ pub fn setHref(self: *Anchor, value: []const u8, page: *Page) !void { } pub fn getTarget(self: *Anchor) []const u8 { - return self.asElement().getAttributeSafe("target") orelse ""; + return self.asElement().getAttributeSafe(comptime .literal("target")) orelse ""; } pub fn setTarget(self: *Anchor, value: []const u8, page: *Page) !void { @@ -167,7 +167,7 @@ pub fn setProtocol(self: *Anchor, value: []const u8, page: *Page) !void { } pub fn getType(self: *Anchor) []const u8 { - return self.asElement().getAttributeSafe("type") orelse ""; + return self.asElement().getAttributeSafe(comptime .literal("type")) orelse ""; } pub fn setType(self: *Anchor, value: []const u8, page: *Page) !void { @@ -175,7 +175,7 @@ pub fn setType(self: *Anchor, value: []const u8, page: *Page) !void { } pub fn getName(self: *const Anchor) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Anchor, value: []const u8, page: *Page) !void { @@ -191,7 +191,7 @@ pub fn setText(self: *Anchor, value: []const u8, page: *Page) !void { } fn getResolvedHref(self: *Anchor, page: *Page) !?[:0]const u8 { - const href = self.asElement().getAttributeSafe("href") orelse return null; + const href = self.asElement().getAttributeSafe(comptime .literal("href")) orelse return null; if (href.len == 0) { return null; } diff --git a/src/browser/webapi/element/html/Body.zig b/src/browser/webapi/element/html/Body.zig index 766ef1a3..f00b60d1 100644 --- a/src/browser/webapi/element/html/Body.zig +++ b/src/browser/webapi/element/html/Body.zig @@ -49,7 +49,7 @@ pub const JsApi = struct { pub const Build = struct { pub fn complete(node: *Node, page: *Page) !void { const el = node.as(Element); - const on_load = el.getAttributeSafe("onload") orelse return; + const on_load = el.getAttributeSafe(comptime .literal("onload")) orelse return; if (page.js.stringToPersistedFunction(on_load)) |func| { page.window._on_load = func; } else |err| { diff --git a/src/browser/webapi/element/html/Button.zig b/src/browser/webapi/element/html/Button.zig index 6b093640..9c3d342e 100644 --- a/src/browser/webapi/element/html/Button.zig +++ b/src/browser/webapi/element/html/Button.zig @@ -39,7 +39,7 @@ pub fn asNode(self: *Button) *Node { } pub fn getDisabled(self: *const Button) bool { - return self.asConstElement().getAttributeSafe("disabled") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null; } pub fn setDisabled(self: *Button, disabled: bool, page: *Page) !void { @@ -51,7 +51,7 @@ pub fn setDisabled(self: *Button, disabled: bool, page: *Page) !void { } pub fn getName(self: *const Button) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Button, name: []const u8, page: *Page) !void { @@ -59,7 +59,7 @@ pub fn setName(self: *Button, name: []const u8, page: *Page) !void { } pub fn getType(self: *const Button) []const u8 { - return self.asConstElement().getAttributeSafe("type") orelse "submit"; + return self.asConstElement().getAttributeSafe(comptime .literal("type")) orelse "submit"; } pub fn setType(self: *Button, typ: []const u8, page: *Page) !void { @@ -67,7 +67,7 @@ pub fn setType(self: *Button, typ: []const u8, page: *Page) !void { } pub fn getValue(self: *const Button) []const u8 { - return self.asConstElement().getAttributeSafe("value") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("value")) orelse ""; } pub fn setValue(self: *Button, value: []const u8, page: *Page) !void { @@ -75,7 +75,7 @@ pub fn setValue(self: *Button, value: []const u8, page: *Page) !void { } pub fn getRequired(self: *const Button) bool { - return self.asConstElement().getAttributeSafe("required") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("required")) != null; } pub fn setRequired(self: *Button, required: bool, page: *Page) !void { @@ -90,7 +90,7 @@ pub fn getForm(self: *Button, page: *Page) ?*Form { const element = self.asElement(); // If form attribute exists, ONLY use that (even if it references nothing) - if (element.getAttributeSafe("form")) |form_id| { + if (element.getAttributeSafe(comptime .literal("form"))) |form_id| { if (page.document.getElementById(form_id, page)) |form_element| { return form_element.is(Form); } diff --git a/src/browser/webapi/element/html/Canvas.zig b/src/browser/webapi/element/html/Canvas.zig index 8e973e09..1021eb35 100644 --- a/src/browser/webapi/element/html/Canvas.zig +++ b/src/browser/webapi/element/html/Canvas.zig @@ -40,7 +40,7 @@ pub fn asNode(self: *Canvas) *Node { } pub fn getWidth(self: *const Canvas) u32 { - const attr = self.asConstElement().getAttributeSafe("width") orelse return 300; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("width")) orelse return 300; return std.fmt.parseUnsigned(u32, attr, 10) catch 300; } @@ -50,7 +50,7 @@ pub fn setWidth(self: *Canvas, value: u32, page: *Page) !void { } pub fn getHeight(self: *const Canvas) u32 { - const attr = self.asConstElement().getAttributeSafe("height") orelse return 150; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("height")) orelse return 150; return std.fmt.parseUnsigned(u32, attr, 10) catch 150; } diff --git a/src/browser/webapi/element/html/Custom.zig b/src/browser/webapi/element/html/Custom.zig index adf23149..9965be17 100644 --- a/src/browser/webapi/element/html/Custom.zig +++ b/src/browser/webapi/element/html/Custom.zig @@ -174,7 +174,7 @@ fn invokeCallbackOnElement(element: *Element, definition: *CustomElementDefiniti // Check if element has "is" attribute and attach customized built-in definition pub fn checkAndAttachBuiltIn(element: *Element, page: *Page) !void { - const is_value = element.getAttributeSafe("is") orelse return; + const is_value = element.getAttributeSafe(comptime .literal("is")) orelse return; const custom_elements = page.window.getCustomElements(); const definition = custom_elements._definitions.get(is_value) orelse return; diff --git a/src/browser/webapi/element/html/Data.zig b/src/browser/webapi/element/html/Data.zig index 08e779f8..818042c2 100644 --- a/src/browser/webapi/element/html/Data.zig +++ b/src/browser/webapi/element/html/Data.zig @@ -36,7 +36,7 @@ pub fn asNode(self: *Data) *Node { } pub fn getValue(self: *Data) []const u8 { - return self.asElement().getAttributeSafe("value") orelse ""; + return self.asElement().getAttributeSafe(comptime .literal("value")) orelse ""; } pub fn setValue(self: *Data, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Dialog.zig b/src/browser/webapi/element/html/Dialog.zig index bc610b9a..89049961 100644 --- a/src/browser/webapi/element/html/Dialog.zig +++ b/src/browser/webapi/element/html/Dialog.zig @@ -20,7 +20,7 @@ pub fn asNode(self: *Dialog) *Node { } pub fn getOpen(self: *const Dialog) bool { - return self.asConstElement().getAttributeSafe("open") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("open")) != null; } pub fn setOpen(self: *Dialog, open: bool, page: *Page) !void { @@ -32,7 +32,7 @@ pub fn setOpen(self: *Dialog, open: bool, page: *Page) !void { } pub fn getReturnValue(self: *const Dialog) []const u8 { - return self.asConstElement().getAttributeSafe("returnvalue") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("returnvalue")) orelse ""; } pub fn setReturnValue(self: *Dialog, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Form.zig b/src/browser/webapi/element/html/Form.zig index d654d2ee..71a98ea4 100644 --- a/src/browser/webapi/element/html/Form.zig +++ b/src/browser/webapi/element/html/Form.zig @@ -43,7 +43,7 @@ pub fn asNode(self: *Form) *Node { } pub fn getName(self: *const Form) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Form, name: []const u8, page: *Page) !void { @@ -51,7 +51,7 @@ pub fn setName(self: *Form, name: []const u8, page: *Page) !void { } pub fn getMethod(self: *const Form) []const u8 { - const method = self.asConstElement().getAttributeSafe("method") orelse return "get"; + const method = self.asConstElement().getAttributeSafe(comptime .literal("method")) orelse return "get"; if (std.ascii.eqlIgnoreCase(method, "post")) { return "post"; @@ -68,7 +68,7 @@ pub fn setMethod(self: *Form, method: []const u8, page: *Page) !void { } pub fn getElements(self: *Form, page: *Page) !*collections.HTMLFormControlsCollection { - const form_id = self.asElement().getAttributeSafe("id"); + const form_id = self.asElement().getAttributeSafe(comptime .literal("id")); const root = if (form_id != null) self.asNode().getRootNode(null) // Has ID: walk entire document to find form=ID controls else diff --git a/src/browser/webapi/element/html/Image.zig b/src/browser/webapi/element/html/Image.zig index 8846d0c6..c159fa97 100644 --- a/src/browser/webapi/element/html/Image.zig +++ b/src/browser/webapi/element/html/Image.zig @@ -36,7 +36,7 @@ pub fn asNode(self: *Image) *Node { pub fn getSrc(self: *const Image, page: *Page) ![]const u8 { const element = self.asConstElement(); - const src = element.getAttributeSafe("src") orelse return ""; + const src = element.getAttributeSafe(comptime .literal("src")) orelse return ""; if (src.len == 0) { return ""; } @@ -50,7 +50,7 @@ pub fn setSrc(self: *Image, value: []const u8, page: *Page) !void { } pub fn getAlt(self: *const Image) []const u8 { - return self.asConstElement().getAttributeSafe("alt") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("alt")) orelse ""; } pub fn setAlt(self: *Image, value: []const u8, page: *Page) !void { @@ -58,7 +58,7 @@ pub fn setAlt(self: *Image, value: []const u8, page: *Page) !void { } pub fn getWidth(self: *const Image) u32 { - const attr = self.asConstElement().getAttributeSafe("width") orelse return 0; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("width")) orelse return 0; return std.fmt.parseUnsigned(u32, attr, 10) catch 0; } @@ -68,7 +68,7 @@ pub fn setWidth(self: *Image, value: u32, page: *Page) !void { } pub fn getHeight(self: *const Image) u32 { - const attr = self.asConstElement().getAttributeSafe("height") orelse return 0; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("height")) orelse return 0; return std.fmt.parseUnsigned(u32, attr, 10) catch 0; } @@ -78,7 +78,7 @@ pub fn setHeight(self: *Image, value: u32, page: *Page) !void { } pub fn getCrossOrigin(self: *const Image) ?[]const u8 { - return self.asConstElement().getAttributeSafe("crossorigin"); + return self.asConstElement().getAttributeSafe(comptime .literal("crossorigin")); } pub fn setCrossOrigin(self: *Image, value: ?[]const u8, page: *Page) !void { @@ -89,7 +89,7 @@ pub fn setCrossOrigin(self: *Image, value: ?[]const u8, page: *Page) !void { } pub fn getLoading(self: *const Image) []const u8 { - return self.asConstElement().getAttributeSafe("loading") orelse "eager"; + return self.asConstElement().getAttributeSafe(comptime .literal("loading")) orelse "eager"; } pub fn setLoading(self: *Image, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Input.zig b/src/browser/webapi/element/html/Input.zig index f3740073..15d8c1f8 100644 --- a/src/browser/webapi/element/html/Input.zig +++ b/src/browser/webapi/element/html/Input.zig @@ -156,7 +156,7 @@ pub fn setDefaultChecked(self: *Input, checked: bool, page: *Page) !void { pub fn getDisabled(self: *const Input) bool { // TODO: Also check for disabled fieldset ancestors // (but not if we're inside a of that fieldset) - return self.asConstElement().getAttributeSafe("disabled") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null; } pub fn setDisabled(self: *Input, disabled: bool, page: *Page) !void { @@ -168,7 +168,7 @@ pub fn setDisabled(self: *Input, disabled: bool, page: *Page) !void { } pub fn getName(self: *const Input) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Input, name: []const u8, page: *Page) !void { @@ -176,7 +176,7 @@ pub fn setName(self: *Input, name: []const u8, page: *Page) !void { } pub fn getAccept(self: *const Input) []const u8 { - return self.asConstElement().getAttributeSafe("accept") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("accept")) orelse ""; } pub fn setAccept(self: *Input, accept: []const u8, page: *Page) !void { @@ -184,7 +184,7 @@ pub fn setAccept(self: *Input, accept: []const u8, page: *Page) !void { } pub fn getAlt(self: *const Input) []const u8 { - return self.asConstElement().getAttributeSafe("alt") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("alt")) orelse ""; } pub fn setAlt(self: *Input, alt: []const u8, page: *Page) !void { @@ -192,7 +192,7 @@ pub fn setAlt(self: *Input, alt: []const u8, page: *Page) !void { } pub fn getMaxLength(self: *const Input) i32 { - const attr = self.asConstElement().getAttributeSafe("maxlength") orelse return -1; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("maxlength")) orelse return -1; return std.fmt.parseInt(i32, attr, 10) catch -1; } @@ -206,7 +206,7 @@ pub fn setMaxLength(self: *Input, max_length: i32, page: *Page) !void { } pub fn getSize(self: *const Input) i32 { - const attr = self.asConstElement().getAttributeSafe("size") orelse return 20; + const attr = self.asConstElement().getAttributeSafe(comptime .literal("size")) orelse return 20; const parsed = std.fmt.parseInt(i32, attr, 10) catch return 20; return if (parsed == 0) 20 else parsed; } @@ -225,7 +225,7 @@ pub fn setSize(self: *Input, size: i32, page: *Page) !void { } pub fn getSrc(self: *const Input, page: *Page) ![]const u8 { - const src = self.asConstElement().getAttributeSafe("src") orelse return ""; + const src = self.asConstElement().getAttributeSafe(comptime .literal("src")) orelse return ""; // If attribute is explicitly set (even if empty), resolve it against the base URL return @import("../../URL.zig").resolve(page.call_arena, page.base(), src, .{}); } @@ -236,7 +236,7 @@ pub fn setSrc(self: *Input, src: []const u8, page: *Page) !void { } pub fn getReadonly(self: *const Input) bool { - return self.asConstElement().getAttributeSafe("readonly") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("readonly")) != null; } pub fn setReadonly(self: *Input, readonly: bool, page: *Page) !void { @@ -248,7 +248,7 @@ pub fn setReadonly(self: *Input, readonly: bool, page: *Page) !void { } pub fn getRequired(self: *const Input) bool { - return self.asConstElement().getAttributeSafe("required") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("required")) != null; } pub fn setRequired(self: *Input, required: bool, page: *Page) !void { @@ -379,7 +379,7 @@ pub fn getForm(self: *Input, page: *Page) ?*Form { const element = self.asElement(); // If form attribute exists, ONLY use that (even if it references nothing) - if (element.getAttributeSafe("form")) |form_id| { + if (element.getAttributeSafe(comptime .literal("form"))) |form_id| { if (page.document.getElementById(form_id, page)) |form_element| { return form_element.is(Form); } @@ -402,7 +402,7 @@ pub fn getForm(self: *Input, page: *Page) ?*Form { fn uncheckRadioGroup(self: *Input, page: *Page) !void { const element = self.asElement(); - const name = element.getAttributeSafe("name") orelse return; + const name = element.getAttributeSafe(comptime .literal("name")) orelse return; if (name.len == 0) { return; } @@ -420,7 +420,7 @@ fn uncheckRadioGroup(self: *Input, page: *Page) !void { continue; } - const other_name = other_element.getAttributeSafe("name") orelse continue; + const other_name = other_element.getAttributeSafe(comptime .literal("name")) orelse continue; if (!std.mem.eql(u8, name, other_name)) { continue; } @@ -481,14 +481,14 @@ pub const Build = struct { const element = self.asElement(); // Store initial values from attributes - self._default_value = element.getAttributeSafe("value"); - self._default_checked = element.getAttributeSafe("checked") != null; + self._default_value = element.getAttributeSafe(comptime .literal("value")); + self._default_checked = element.getAttributeSafe(comptime .literal("checked")) != null; // Current state starts equal to default self._value = self._default_value; self._checked = self._default_checked; - self._input_type = if (element.getAttributeSafe("type")) |type_attr| + self._input_type = if (element.getAttributeSafe(comptime .literal("type"))) |type_attr| Type.fromString(type_attr) else .text; diff --git a/src/browser/webapi/element/html/Link.zig b/src/browser/webapi/element/html/Link.zig index 6c4ca29a..8595ce82 100644 --- a/src/browser/webapi/element/html/Link.zig +++ b/src/browser/webapi/element/html/Link.zig @@ -36,7 +36,7 @@ pub fn asNode(self: *Link) *Node { pub fn getHref(self: *Link, page: *Page) ![]const u8 { const element = self.asElement(); - const href = element.getAttributeSafe("href") orelse return ""; + const href = element.getAttributeSafe(comptime .literal("href")) orelse return ""; if (href.len == 0) { return ""; } @@ -50,7 +50,7 @@ pub fn setHref(self: *Link, value: []const u8, page: *Page) !void { } pub fn getRel(self: *Link) []const u8 { - return self.asElement().getAttributeSafe("rel") orelse return ""; + return self.asElement().getAttributeSafe(comptime .literal("rel")) orelse return ""; } pub fn setRel(self: *Link, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Media.zig b/src/browser/webapi/element/html/Media.zig index eca130d0..0d77506f 100644 --- a/src/browser/webapi/element/html/Media.zig +++ b/src/browser/webapi/element/html/Media.zig @@ -219,7 +219,7 @@ pub fn setCurrentTime(self: *Media, value: f64) void { pub fn getSrc(self: *const Media, page: *Page) ![]const u8 { const element = self.asConstElement(); - const src = element.getAttributeSafe("src") orelse return ""; + const src = element.getAttributeSafe(comptime .literal("src")) orelse return ""; if (src.len == 0) { return ""; } @@ -232,7 +232,7 @@ pub fn setSrc(self: *Media, value: []const u8, page: *Page) !void { } pub fn getAutoplay(self: *const Media) bool { - return self.asConstElement().getAttributeSafe("autoplay") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("autoplay")) != null; } pub fn setAutoplay(self: *Media, value: bool, page: *Page) !void { @@ -244,7 +244,7 @@ pub fn setAutoplay(self: *Media, value: bool, page: *Page) !void { } pub fn getControls(self: *const Media) bool { - return self.asConstElement().getAttributeSafe("controls") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("controls")) != null; } pub fn setControls(self: *Media, value: bool, page: *Page) !void { @@ -256,7 +256,7 @@ pub fn setControls(self: *Media, value: bool, page: *Page) !void { } pub fn getLoop(self: *const Media) bool { - return self.asConstElement().getAttributeSafe("loop") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("loop")) != null; } pub fn setLoop(self: *Media, value: bool, page: *Page) !void { @@ -268,7 +268,7 @@ pub fn setLoop(self: *Media, value: bool, page: *Page) !void { } pub fn getPreload(self: *const Media) []const u8 { - return self.asConstElement().getAttributeSafe("preload") orelse "auto"; + return self.asConstElement().getAttributeSafe(comptime .literal("preload")) orelse "auto"; } pub fn setPreload(self: *Media, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Meta.zig b/src/browser/webapi/element/html/Meta.zig index 9efddfbd..e4bb5eff 100644 --- a/src/browser/webapi/element/html/Meta.zig +++ b/src/browser/webapi/element/html/Meta.zig @@ -37,7 +37,7 @@ pub fn asNode(self: *Meta) *Node { } pub fn getName(self: *Meta) []const u8 { - return self.asElement().getAttributeSafe("name") orelse return ""; + return self.asElement().getAttributeSafe(comptime .literal("name")) orelse return ""; } pub fn setName(self: *Meta, value: []const u8, page: *Page) !void { @@ -45,7 +45,7 @@ pub fn setName(self: *Meta, value: []const u8, page: *Page) !void { } pub fn getHttpEquiv(self: *Meta) []const u8 { - return self.asElement().getAttributeSafe("http-equiv") orelse return ""; + return self.asElement().getAttributeSafe(comptime .literal("http-equiv")) orelse return ""; } pub fn setHttpEquiv(self: *Meta, value: []const u8, page: *Page) !void { @@ -53,7 +53,7 @@ pub fn setHttpEquiv(self: *Meta, value: []const u8, page: *Page) !void { } pub fn getContent(self: *Meta) []const u8 { - return self.asElement().getAttributeSafe("content") orelse return ""; + return self.asElement().getAttributeSafe(comptime .literal("content")) orelse return ""; } pub fn setContent(self: *Meta, value: []const u8, page: *Page) !void { @@ -61,7 +61,7 @@ pub fn setContent(self: *Meta, value: []const u8, page: *Page) !void { } pub fn getMedia(self: *Meta) []const u8 { - return self.asElement().getAttributeSafe("media") orelse return ""; + return self.asElement().getAttributeSafe(comptime .literal("media")) orelse return ""; } pub fn setMedia(self: *Meta, value: []const u8, page: *Page) !void { diff --git a/src/browser/webapi/element/html/Option.zig b/src/browser/webapi/element/html/Option.zig index 207f4aaf..bf4d3c8d 100644 --- a/src/browser/webapi/element/html/Option.zig +++ b/src/browser/webapi/element/html/Option.zig @@ -94,7 +94,7 @@ pub fn setDisabled(self: *Option, disabled: bool, page: *Page) !void { } pub fn getName(self: *const Option) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Option, name: []const u8, page: *Page) !void { @@ -124,14 +124,14 @@ pub const Build = struct { const element = self.asElement(); // Check for value attribute - self._value = element.getAttributeSafe("value"); + self._value = element.getAttributeSafe(comptime .literal("value")); // Check for selected attribute - self._default_selected = element.getAttributeSafe("selected") != null; + self._default_selected = element.getAttributeSafe(comptime .literal("selected")) != null; self._selected = self._default_selected; // Check for disabled attribute - self._disabled = element.getAttributeSafe("disabled") != null; + self._disabled = element.getAttributeSafe(comptime .literal("disabled")) != null; } pub fn attributeChange(element: *Element, name: []const u8, value: []const u8, _: *Page) !void { diff --git a/src/browser/webapi/element/html/Script.zig b/src/browser/webapi/element/html/Script.zig index fa467fd9..60ff75e4 100644 --- a/src/browser/webapi/element/html/Script.zig +++ b/src/browser/webapi/element/html/Script.zig @@ -54,14 +54,14 @@ pub fn getSrc(self: *const Script, page: *Page) ![]const u8 { pub fn setSrc(self: *Script, src: []const u8, page: *Page) !void { const element = self.asElement(); try element.setAttributeSafe("src", src, page); - self._src = element.getAttributeSafe("src") orelse unreachable; + self._src = element.getAttributeSafe(comptime .literal("src")) orelse unreachable; if (element.asNode().isConnected()) { try page.scriptAddedCallback(false, self); } } pub fn getType(self: *const Script) []const u8 { - return self.asConstElement().getAttributeSafe("type") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("type")) orelse ""; } pub fn setType(self: *Script, value: []const u8, page: *Page) !void { @@ -69,7 +69,7 @@ pub fn setType(self: *Script, value: []const u8, page: *Page) !void { } pub fn getNonce(self: *const Script) []const u8 { - return self.asConstElement().getAttributeSafe("nonce") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("nonce")) orelse ""; } pub fn setNonce(self: *Script, value: []const u8, page: *Page) !void { @@ -93,7 +93,7 @@ pub fn setOnError(self: *Script, cb: ?js.Function.Global) void { } pub fn getNoModule(self: *const Script) bool { - return self.asConstElement().getAttributeSafe("nomodule") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("nomodule")) != null; } pub fn setInnerText(self: *Script, text: []const u8, page: *Page) !void { @@ -127,9 +127,9 @@ pub const Build = struct { pub fn complete(node: *Node, page: *Page) !void { const self = node.as(Script); const element = self.asElement(); - self._src = element.getAttributeSafe("src") orelse ""; + self._src = element.getAttributeSafe(comptime .literal("src")) orelse ""; - if (element.getAttributeSafe("onload")) |on_load| { + if (element.getAttributeSafe(comptime .literal("onload"))) |on_load| { if (page.js.stringToPersistedFunction(on_load)) |func| { self._on_load = func; } else |err| { @@ -137,7 +137,7 @@ pub const Build = struct { } } - if (element.getAttributeSafe("onerror")) |on_error| { + if (element.getAttributeSafe(comptime .literal("onerror"))) |on_error| { if (page.js.stringToPersistedFunction(on_error)) |func| { self._on_error = func; } else |err| { diff --git a/src/browser/webapi/element/html/Select.zig b/src/browser/webapi/element/html/Select.zig index 293d53dc..fe84321c 100644 --- a/src/browser/webapi/element/html/Select.zig +++ b/src/browser/webapi/element/html/Select.zig @@ -121,7 +121,7 @@ pub fn setSelectedIndex(self: *Select, index: i32) !void { } pub fn getMultiple(self: *const Select) bool { - return self.asConstElement().getAttributeSafe("multiple") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("multiple")) != null; } pub fn setMultiple(self: *Select, multiple: bool, page: *Page) !void { @@ -133,7 +133,7 @@ pub fn setMultiple(self: *Select, multiple: bool, page: *Page) !void { } pub fn getDisabled(self: *const Select) bool { - return self.asConstElement().getAttributeSafe("disabled") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null; } pub fn setDisabled(self: *Select, disabled: bool, page: *Page) !void { @@ -145,7 +145,7 @@ pub fn setDisabled(self: *Select, disabled: bool, page: *Page) !void { } pub fn getName(self: *const Select) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Select, name: []const u8, page: *Page) !void { @@ -153,7 +153,7 @@ pub fn setName(self: *Select, name: []const u8, page: *Page) !void { } pub fn getSize(self: *const Select) u32 { - const s = self.asConstElement().getAttributeSafe("size") orelse return 0; + const s = self.asConstElement().getAttributeSafe(comptime .literal("size")) orelse return 0; const trimmed = std.mem.trimLeft(u8, s, &std.ascii.whitespace); @@ -176,7 +176,7 @@ pub fn setSize(self: *Select, size: u32, page: *Page) !void { } pub fn getRequired(self: *const Select) bool { - return self.asConstElement().getAttributeSafe("required") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("required")) != null; } pub fn setRequired(self: *Select, required: bool, page: *Page) !void { @@ -218,7 +218,7 @@ pub fn getForm(self: *Select, page: *Page) ?*Form { const element = self.asElement(); // If form attribute exists, ONLY use that (even if it references nothing) - if (element.getAttributeSafe("form")) |form_id| { + if (element.getAttributeSafe(comptime .literal("form"))) |form_id| { if (page.document.getElementById(form_id, page)) |form_element| { return form_element.is(Form); } diff --git a/src/browser/webapi/element/html/Slot.zig b/src/browser/webapi/element/html/Slot.zig index e3e59c6c..37d10a7a 100644 --- a/src/browser/webapi/element/html/Slot.zig +++ b/src/browser/webapi/element/html/Slot.zig @@ -25,7 +25,7 @@ pub fn asNode(self: *Slot) *Node { } pub fn getName(self: *const Slot) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *Slot, name: []const u8, page: *Page) !void { @@ -131,7 +131,7 @@ fn isAssignedToSlot(node: *Node, slot_name: []const u8) bool { // Check if a node should be assigned to a slot with the given name if (node.is(Element)) |element| { // Get the slot attribute from the element - const node_slot = element.getAttributeSafe("slot") orelse ""; + const node_slot = element.getAttributeSafe(comptime .literal("slot")) orelse ""; // Match if: // - Both are empty (default slot) diff --git a/src/browser/webapi/element/html/Style.zig b/src/browser/webapi/element/html/Style.zig index 7f385238..66489bea 100644 --- a/src/browser/webapi/element/html/Style.zig +++ b/src/browser/webapi/element/html/Style.zig @@ -39,7 +39,7 @@ pub fn asNode(self: *Style) *Node { // Attribute-backed properties pub fn getBlocking(self: *const Style) []const u8 { - return self.asConstElement().getAttributeSafe("blocking") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("blocking")) orelse ""; } pub fn setBlocking(self: *Style, value: []const u8, page: *Page) !void { @@ -47,7 +47,7 @@ pub fn setBlocking(self: *Style, value: []const u8, page: *Page) !void { } pub fn getMedia(self: *const Style) []const u8 { - return self.asConstElement().getAttributeSafe("media") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("media")) orelse ""; } pub fn setMedia(self: *Style, value: []const u8, page: *Page) !void { @@ -55,7 +55,7 @@ pub fn setMedia(self: *Style, value: []const u8, page: *Page) !void { } pub fn getType(self: *const Style) []const u8 { - return self.asConstElement().getAttributeSafe("type") orelse "text/css"; + return self.asConstElement().getAttributeSafe(comptime .literal("type")) orelse "text/css"; } pub fn setType(self: *Style, value: []const u8, page: *Page) !void { @@ -63,7 +63,7 @@ pub fn setType(self: *Style, value: []const u8, page: *Page) !void { } pub fn getDisabled(self: *const Style) bool { - return self.asConstElement().getAttributeSafe("disabled") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null; } pub fn setDisabled(self: *Style, disabled: bool, page: *Page) !void { diff --git a/src/browser/webapi/element/html/TextArea.zig b/src/browser/webapi/element/html/TextArea.zig index bcac5cb2..f0197901 100644 --- a/src/browser/webapi/element/html/TextArea.zig +++ b/src/browser/webapi/element/html/TextArea.zig @@ -84,7 +84,7 @@ pub fn setDefaultValue(self: *TextArea, value: []const u8, page: *Page) !void { } pub fn getDisabled(self: *const TextArea) bool { - return self.asConstElement().getAttributeSafe("disabled") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null; } pub fn setDisabled(self: *TextArea, disabled: bool, page: *Page) !void { @@ -96,7 +96,7 @@ pub fn setDisabled(self: *TextArea, disabled: bool, page: *Page) !void { } pub fn getName(self: *const TextArea) []const u8 { - return self.asConstElement().getAttributeSafe("name") orelse ""; + return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse ""; } pub fn setName(self: *TextArea, name: []const u8, page: *Page) !void { @@ -104,7 +104,7 @@ pub fn setName(self: *TextArea, name: []const u8, page: *Page) !void { } pub fn getRequired(self: *const TextArea) bool { - return self.asConstElement().getAttributeSafe("required") != null; + return self.asConstElement().getAttributeSafe(comptime .literal("required")) != null; } pub fn setRequired(self: *TextArea, required: bool, page: *Page) !void { @@ -221,7 +221,7 @@ pub fn getForm(self: *TextArea, page: *Page) ?*Form { const element = self.asElement(); // If form attribute exists, ONLY use that (even if it references nothing) - if (element.getAttributeSafe("form")) |form_id| { + if (element.getAttributeSafe(comptime .literal("form"))) |form_id| { if (page.document.getElementById(form_id, page)) |form_element| { return form_element.is(Form); } diff --git a/src/browser/webapi/element/html/Video.zig b/src/browser/webapi/element/html/Video.zig index dda22eb2..9774d1ca 100644 --- a/src/browser/webapi/element/html/Video.zig +++ b/src/browser/webapi/element/html/Video.zig @@ -53,7 +53,7 @@ pub fn getVideoHeight(_: *const Video) u32 { pub fn getPoster(self: *const Video, page: *Page) ![]const u8 { const element = self.asConstElement(); - const poster = element.getAttributeSafe("poster") orelse return ""; + const poster = element.getAttributeSafe(comptime .literal("poster")) orelse return ""; if (poster.len == 0) { return ""; } diff --git a/src/browser/webapi/net/FormData.zig b/src/browser/webapi/net/FormData.zig index edc4fd40..062e0af3 100644 --- a/src/browser/webapi/net/FormData.zig +++ b/src/browser/webapi/net/FormData.zig @@ -127,7 +127,7 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa var elements = try form.getElements(page); var it = try elements.iterator(); while (it.next()) |element| { - if (element.getAttributeSafe("disabled") != null) { + if (element.getAttributeSafe(comptime .literal("disabled")) != null) { continue; } @@ -139,7 +139,7 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa continue; } - const name = element.getAttributeSafe("name"); + const name = element.getAttributeSafe(comptime .literal("name")); const x_key = if (name) |n| try std.fmt.allocPrint(arena, "{s}.x", .{n}) else "x"; const y_key = if (name) |n| try std.fmt.allocPrint(arena, "{s}.y", .{n}) else "y"; try list.append(arena, x_key, "0"); @@ -148,7 +148,7 @@ fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Pa } } - const name = element.getAttributeSafe("name") orelse continue; + const name = element.getAttributeSafe(comptime .literal("name")) orelse continue; const value = blk: { if (element.is(Form.Input)) |input| { const input_type = input._input_type; diff --git a/src/browser/webapi/selector/List.zig b/src/browser/webapi/selector/List.zig index 2cb986a2..0f7d3c9c 100644 --- a/src/browser/webapi/selector/List.zig +++ b/src/browser/webapi/selector/List.zig @@ -412,11 +412,11 @@ fn matchesCompound(el: *Node.Element, compound: Selector.Compound, scope: *Node, fn matchesPart(el: *Node.Element, part: Part, scope: *Node, page: *Page) bool { switch (part) { .id => |id| { - const element_id = el.getAttributeSafe("id") orelse return false; + const element_id = el.getAttributeSafe(comptime .literal("id")) orelse return false; return std.mem.eql(u8, element_id, id); }, .class => |cls| { - const class_attr = el.getAttributeSafe("class") orelse return false; + const class_attr = el.getAttributeSafe(comptime .literal("class")) orelse return false; return Selector.classAttributeContains(class_attr, cls); }, .tag => |tag| { @@ -526,10 +526,10 @@ fn matchesPseudoClass(el: *Node.Element, pseudo: Selector.PseudoClass, scope: *N return input.getChecked(); }, .disabled => { - return el.getAttributeSafe("disabled") != null; + return el.getAttributeSafe(comptime .literal("disabled")) != null; }, .enabled => { - return el.getAttributeSafe("disabled") == null; + return el.getAttributeSafe(comptime .literal("disabled")) == null; }, .indeterminate => return false, @@ -537,19 +537,19 @@ fn matchesPseudoClass(el: *Node.Element, pseudo: Selector.PseudoClass, scope: *N .valid => return false, .invalid => return false, .required => { - return el.getAttributeSafe("required") != null; + return el.getAttributeSafe(comptime .literal("required")) != null; }, .optional => { - return el.getAttributeSafe("required") == null; + return el.getAttributeSafe(comptime .literal("required")) == null; }, .in_range => return false, .out_of_range => return false, .placeholder_shown => return false, .read_only => { - return el.getAttributeSafe("readonly") != null; + return el.getAttributeSafe(comptime .literal("readonly")) != null; }, .read_write => { - return el.getAttributeSafe("readonly") == null; + return el.getAttributeSafe(comptime .literal("readonly")) == null; }, .default => return false, @@ -571,10 +571,10 @@ fn matchesPseudoClass(el: *Node.Element, pseudo: Selector.PseudoClass, scope: *N .visited => return false, .any_link => { if (el.getTag() != .anchor) return false; - return el.getAttributeSafe("href") != null; + return el.getAttributeSafe(comptime .literal("href")) != null; }, .target => { - const element_id = el.getAttributeSafe("id") orelse return false; + const element_id = el.getAttributeSafe(comptime .literal("id")) orelse return false; const location = page.document._location orelse return false; const hash = location.getHash(); if (hash.len <= 1) return false; diff --git a/src/browser/webapi/selector/Parser.zig b/src/browser/webapi/selector/Parser.zig index 932d9adf..08281240 100644 --- a/src/browser/webapi/selector/Parser.zig +++ b/src/browser/webapi/selector/Parser.zig @@ -17,6 +17,7 @@ // along with this program. If not, see . const std = @import("std"); +const String = @import("../../../string.zig").String; const Page = @import("../../Page.zig"); @@ -46,6 +47,7 @@ const ParseError = error{ UnknownPseudoClass, InvalidTagSelector, InvalidSelector, + StringTooLarge, }; pub fn parseList(arena: Allocator, input: []const u8, page: *Page) ParseError![]const Selector.Selector { @@ -846,7 +848,7 @@ fn attribute(self: *Parser, arena: Allocator, page: *Page) !Selector.Attribute { const attr_name = try self.attributeName(); // Normalize the name to lowercase for fast matching (consistent with Attribute.normalizeNameForLookup) const normalized = try Attribute.normalizeNameForLookup(attr_name, page); - const name = try arena.dupe(u8, normalized); + const name = try String.init(arena, normalized, .{}); var case_insensitive = false; _ = self.skipSpaces(); diff --git a/src/browser/webapi/selector/Selector.zig b/src/browser/webapi/selector/Selector.zig index 1f1f1d53..0ac9793a 100644 --- a/src/browser/webapi/selector/Selector.zig +++ b/src/browser/webapi/selector/Selector.zig @@ -18,6 +18,8 @@ const std = @import("std"); +const String = @import("../../../string.zig").String; + const Parser = @import("Parser.zig"); const Node = @import("../Node.zig"); const Page = @import("../../Page.zig"); @@ -117,7 +119,7 @@ pub const Part = union(enum) { }; pub const Attribute = struct { - name: []const u8, + name: String, matcher: AttributeMatcher, case_insensitive: bool, }; diff --git a/src/cdp/AXNode.zig b/src/cdp/AXNode.zig index e887d75e..a96a5f57 100644 --- a/src/cdp/AXNode.zig +++ b/src/cdp/AXNode.zig @@ -289,7 +289,7 @@ pub const Writer = struct { }, .input => { const input = el.as(DOMNode.Element.Html.Input); - const is_disabled = el.hasAttributeSafe("disabled"); + const is_disabled = el.hasAttributeSafe(comptime .literal("disabled")); switch (input._input_type) { .text, .email, .tel, .url, .search, .password, .number => { @@ -305,8 +305,8 @@ pub const Writer = struct { try self.writeAXProperty(.{ .name = .settable, .value = .{ .booleanOrUndefined = true } }, w); } try self.writeAXProperty(.{ .name = .multiline, .value = .{ .boolean = false } }, w); - try self.writeAXProperty(.{ .name = .readonly, .value = .{ .boolean = el.hasAttributeSafe("readonly") } }, w); - try self.writeAXProperty(.{ .name = .required, .value = .{ .boolean = el.hasAttributeSafe("required") } }, w); + try self.writeAXProperty(.{ .name = .readonly, .value = .{ .boolean = el.hasAttributeSafe(comptime .literal("readonly")) } }, w); + try self.writeAXProperty(.{ .name = .required, .value = .{ .boolean = el.hasAttributeSafe(comptime .literal("required")) } }, w); }, .button, .submit, .reset, .image => { try self.writeAXProperty(.{ .name = .invalid, .value = .{ .token = "false" } }, w); @@ -319,14 +319,14 @@ pub const Writer = struct { if (!is_disabled) { try self.writeAXProperty(.{ .name = .focusable, .value = .{ .booleanOrUndefined = true } }, w); } - const is_checked = el.hasAttributeSafe("checked"); + const is_checked = el.hasAttributeSafe(comptime .literal("checked")); try self.writeAXProperty(.{ .name = .checked, .value = .{ .token = if (is_checked) "true" else "false" } }, w); }, else => {}, } }, .textarea => { - const is_disabled = el.hasAttributeSafe("disabled"); + const is_disabled = el.hasAttributeSafe(comptime .literal("disabled")); try self.writeAXProperty(.{ .name = .invalid, .value = .{ .token = "false" } }, w); if (!is_disabled) { @@ -337,11 +337,11 @@ pub const Writer = struct { try self.writeAXProperty(.{ .name = .settable, .value = .{ .booleanOrUndefined = true } }, w); } try self.writeAXProperty(.{ .name = .multiline, .value = .{ .boolean = true } }, w); - try self.writeAXProperty(.{ .name = .readonly, .value = .{ .boolean = el.hasAttributeSafe("readonly") } }, w); - try self.writeAXProperty(.{ .name = .required, .value = .{ .boolean = el.hasAttributeSafe("required") } }, w); + try self.writeAXProperty(.{ .name = .readonly, .value = .{ .boolean = el.hasAttributeSafe(comptime .literal("readonly")) } }, w); + try self.writeAXProperty(.{ .name = .required, .value = .{ .boolean = el.hasAttributeSafe(comptime .literal("required")) } }, w); }, .select => { - const is_disabled = el.hasAttributeSafe("disabled"); + const is_disabled = el.hasAttributeSafe(comptime .literal("disabled")); try self.writeAXProperty(.{ .name = .invalid, .value = .{ .token = "false" } }, w); if (!is_disabled) { @@ -385,7 +385,7 @@ pub const Writer = struct { } }, .button => { - const is_disabled = el.hasAttributeSafe("disabled"); + const is_disabled = el.hasAttributeSafe(comptime .literal("disabled")); try self.writeAXProperty(.{ .name = .invalid, .value = .{ .token = "false" } }, w); if (!is_disabled) { try self.writeAXProperty(.{ .name = .focusable, .value = .{ .booleanOrUndefined = true } }, w); @@ -629,10 +629,10 @@ pub const AXRole = enum(u8) { }, .textarea => .textbox, .select => { - if (el.getAttributeSafe("multiple") != null) { + if (el.getAttributeSafe(comptime .literal("multiple")) != null) { return .listbox; } - if (el.getAttributeSafe("size")) |size| { + if (el.getAttributeSafe(comptime .literal("size"))) |size| { if (!std.ascii.eqlIgnoreCase(size, "1")) { return .listbox; } @@ -649,7 +649,7 @@ pub const AXRole = enum(u8) { // Interactive Elements .anchor, .area => { - if (el.getAttributeSafe("href") == null) { + if (el.getAttributeSafe(comptime .literal("href")) == null) { return .none; } @@ -669,7 +669,7 @@ pub const AXRole = enum(u8) { .thead, .tbody, .tfoot => .rowgroup, .tr => .row, .th => { - if (el.getAttributeSafe("scope")) |scope| { + if (el.getAttributeSafe(comptime .literal("scope"))) |scope| { if (std.ascii.eqlIgnoreCase(scope, "row")) { return .rowheader; } @@ -722,7 +722,7 @@ pub fn fromNode(dom: *DOMNode) AXNode { break :blk null; } const elt = dom.as(DOMNode.Element); - break :blk elt.getAttributeSafe("role"); + break :blk elt.getAttributeSafe(comptime .literal("role")); }, }; } @@ -759,7 +759,7 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource { }, .element => |el| { // Handle aria-labelledby attribute (highest priority) - if (el.getAttributeSafe("aria-labelledby")) |labelledby| { + if (el.getAttributeSafe(.literal("aria-labelledby"))) |labelledby| { // Get the document to look up elements by ID const doc = node.ownerDocument(page) orelse return null; @@ -786,12 +786,12 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource { } } - if (el.getAttributeSafe("aria-label")) |aria_label| { + if (el.getAttributeSafe(comptime .literal("aria-label"))) |aria_label| { try w.write(aria_label); return .aria_label; } - if (el.getAttributeSafe("alt")) |alt| { + if (el.getAttributeSafe(comptime .literal("alt"))) |alt| { try w.write(alt); return .alt; } @@ -836,12 +836,12 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource { }, } - if (el.getAttributeSafe("title")) |title| { + if (el.getAttributeSafe(comptime .literal("title"))) |title| { try w.write(title); return .title; } - if (el.getAttributeSafe("placeholder")) |placeholder| { + if (el.getAttributeSafe(comptime .literal("placeholder"))) |placeholder| { try w.write(placeholder); return .placeholder; } @@ -857,17 +857,17 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource { } fn isHidden(elt: *DOMNode.Element) bool { - if (elt.getAttributeSafe("aria-hidden")) |value| { + if (elt.getAttributeSafe(comptime .literal("aria-hidden"))) |value| { if (std.mem.eql(u8, value, "true")) { return true; } } - if (elt.hasAttributeSafe("hidden")) { + if (elt.hasAttributeSafe(comptime .literal("hidden"))) { return true; } - if (elt.hasAttributeSafe("inert")) { + if (elt.hasAttributeSafe(comptime .literal("inert"))) { return true; } @@ -940,7 +940,7 @@ fn isIgnore(self: AXNode, page: *Page) bool { // zig fmt: on .img => { // Check for empty decorative images - const alt_ = elt.getAttributeSafe("alt"); + const alt_ = elt.getAttributeSafe(comptime .literal("alt")); if (alt_ == null or alt_.?.len == 0) { return true; } @@ -967,9 +967,9 @@ fn isIgnore(self: AXNode, page: *Page) bool { // Generic containers with no semantic value if (tag == .div or tag == .span) { - const has_role = elt.hasAttributeSafe("role"); - const has_aria_label = elt.hasAttributeSafe("aria-label"); - const has_aria_labelledby = elt.hasAttributeSafe("aria-labelledby"); + const has_role = elt.hasAttributeSafe(comptime .literal("role")); + const has_aria_label = elt.hasAttributeSafe(comptime .literal("aria-label")); + const has_aria_labelledby = elt.hasAttributeSafe(.literal("aria-labelledby")); if (!has_role and !has_aria_label and !has_aria_labelledby) { // Check if it has any non-ignored children diff --git a/src/string.zig b/src/string.zig index f2f5c3c5..234144f1 100644 --- a/src/string.zig +++ b/src/string.zig @@ -34,6 +34,44 @@ pub const String = packed struct { pub const empty = String{ .len = 0, .payload = .{ .content = @splat(0) } }; pub const deleted = String{ .len = tombstone, .payload = .{ .content = @splat(0) } }; + // Create a String from a string literal. For strings with len <= 12, the + // this can be done at comptime: comptime String.literal("id"); + // For strings with len > 12, this must be done at runtime. This is because, + // at comptime, we do not have a ptr for data and thus can't store it. + pub fn literal(input: anytype) String { + if (@inComptime()) { + const l = input.len; + if (l > 12) { + @compileError("Comptime string must be <= 12 bytes (SSO only): " ++ input); + } + + var content: [12]u8 = @splat(0); + @memcpy(content[0..l], input); + return .{ .len = @intCast(l), .payload = .{ .content = content } }; + } + + // Runtime path - handle both String and []const u8 + if (@TypeOf(input) == String) { + return input; + } + + const l = input.len; + + if (l <= 12) { + var content: [12]u8 = @splat(0); + @memcpy(content[0..l], input); + return .{ .len = @intCast(l), .payload = .{ .content = content } }; + } + + return .{ + .len = @intCast(l), + .payload = .{ .heap = .{ + .prefix = input[0..4].*, + .ptr = input.ptr, + } }, + }; + } + pub const InitOpts = struct { dupe: bool = true, }; @@ -47,13 +85,11 @@ pub const String = packed struct { @memcpy(content[0..l], input); return .{ .len = @intCast(l), .payload = .{ .content = content } }; } - var prefix: [4]u8 = @splat(0); - @memcpy(&prefix, input[0..4]); return .{ .len = @intCast(l), .payload = .{ .heap = .{ - .prefix = prefix, + .prefix = input[0..4].*, .ptr = (intern(input) orelse (if (opts.dupe) (try allocator.dupe(u8, input)) else input)).ptr, } }, };