diff --git a/src/browser/Page.zig b/src/browser/Page.zig
index 6fbd184a..47c09c37 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(comptime .literal("src")),
+ .src = script.asElement().getAttributeSafe(comptime .wrap("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(comptime .literal("id")) orelse continue;
+ const element_id = el.getAttributeSafe(comptime .wrap("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(comptime .literal("href"))) |href| {
+ if (n.as(Element).getAttributeSafe(comptime .wrap("href"))) |href| {
self.base_url = try URL.resolve(self.arena, self.url, href, .{});
}
}
@@ -2074,9 +2074,9 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
while (it.next()) |attr| {
Element.Html.Custom.invokeAttributeChangedCallbackOnElement(
element,
- attr._name.str(),
+ attr._name,
null, // old_value is null for initial attributes
- attr._value.str(),
+ attr._value,
self,
);
}
@@ -2236,7 +2236,7 @@ pub fn createProcessingInstruction(self: *Page, target: []const u8, data: []cons
}
// Validate target follows XML name rules (similar to attribute name validation)
- try Element.Attribute.validateAttributeName(target);
+ try Element.Attribute.validateAttributeName(.wrap(target));
const owned_target = try self.dupeString(target);
const owned_data = try self.dupeString(data);
@@ -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(comptime .literal("slot")) orelse "";
+ const slot_name = el.getAttributeSafe(comptime .wrap("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(comptime .literal("id"))) |id| {
+ if (el.getAttributeSafe(comptime .wrap("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(comptime .literal("id"))) |id| {
+ if (el.getAttributeSafe(comptime .wrap("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(comptime .literal("id"))) |id| {
+ if (el.getAttributeSafe(comptime .wrap("id"))) |id| {
try self.addElementId(el.asNode()._parent.?, el, id);
}
@@ -2529,7 +2529,7 @@ pub fn _insertNodeRelative(self: *Page, comptime from_parser: bool, parent: *Nod
}
}
-pub fn attributeChange(self: *Page, element: *Element, name: []const u8, value: []const u8, old_value: ?[]const u8) void {
+pub fn attributeChange(self: *Page, element: *Element, name: String, value: String, old_value: ?String) void {
_ = Element.Build.call(element, "attributeChange", .{ element, name, value, self }) catch |err| {
log.err(.bug, "build.attributeChange", .{ .tag = element.getTag(), .name = name, .value = value, .err = err });
};
@@ -2545,9 +2545,9 @@ pub fn attributeChange(self: *Page, element: *Element, name: []const u8, value:
}
// Handle slot assignment changes
- if (std.mem.eql(u8, name, "slot")) {
+ if (name.eql(comptime .wrap("slot"))) {
self.updateSlotAssignments(element);
- } else if (std.mem.eql(u8, name, "name")) {
+ } else if (name.eql(comptime .wrap("name"))) {
// Check if this is a slot element
if (element.is(Element.Html.Slot)) |slot| {
self.signalSlotChange(slot);
@@ -2555,7 +2555,7 @@ pub fn attributeChange(self: *Page, element: *Element, name: []const u8, value:
}
}
-pub fn attributeRemove(self: *Page, element: *Element, name: []const u8, old_value: []const u8) void {
+pub fn attributeRemove(self: *Page, element: *Element, name: String, old_value: String) void {
_ = Element.Build.call(element, "attributeRemove", .{ element, name, self }) catch |err| {
log.err(.bug, "build.attributeRemove", .{ .tag = element.getTag(), .name = name, .err = err });
};
@@ -2571,9 +2571,9 @@ pub fn attributeRemove(self: *Page, element: *Element, name: []const u8, old_val
}
// Handle slot assignment changes
- if (std.mem.eql(u8, name, "slot")) {
+ if (name.eql(comptime .wrap("slot"))) {
self.updateSlotAssignments(element);
- } else if (std.mem.eql(u8, name, "name")) {
+ } else if (name.eql(comptime .wrap("name"))) {
// Check if this is a slot element
if (element.is(Element.Html.Slot)) |slot| {
self.signalSlotChange(slot);
@@ -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(comptime .literal("slot")) orelse "";
+ const slot_name = element.getAttributeSafe(comptime .wrap("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(comptime .literal("href")) orelse return;
+ const href = element.getAttributeSafe(comptime .wrap("href")) orelse return;
if (href.len == 0) {
return;
}
@@ -2935,7 +2935,7 @@ pub fn handleClick(self: *Page, target: *Node) !void {
return;
}
- if (try element.hasAttribute("download", self)) {
+ if (try element.hasAttribute(comptime .wrap("download"), self)) {
log.warn(.browser, "a.download", .{});
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(comptime .literal("disabled")) != null) {
+ if (submitter.getAttributeSafe(comptime .wrap("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(comptime .literal("enctype"));
+ const encoding = form_element.getAttributeSafe(comptime .wrap("enctype"));
var buf = std.Io.Writer.Allocating.init(transfer_arena);
try form_data.write(encoding, &buf.writer);
- const method = form_element.getAttributeSafe(comptime .literal("method")) orelse "";
- var action = form_element.getAttributeSafe(comptime .literal("action")) orelse self.url;
+ const method = form_element.getAttributeSafe(comptime .wrap("method")) orelse "";
+ var action = form_element.getAttributeSafe(comptime .wrap("action")) orelse self.url;
var opts = NavigateOpts{
.reason = .form,
diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig
index 6a86324d..53ffa10a 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(comptime .literal("nomodule")) != null) {
+ if (element.getAttributeSafe(comptime .wrap("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(comptime .literal("type")) orelse break :blk .javascript;
+ const script_type = element.getAttributeSafe(comptime .wrap("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(comptime .literal("src"))) |src| {
+ if (element.getAttributeSafe(comptime .wrap("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(comptime .literal("async")) != null) {
+ if (element.getAttributeSafe(comptime .wrap("async")) != null) {
break :blk .async;
}
// Check for defer or module (before checking dynamic script default)
- if (kind == .module or element.getAttributeSafe(comptime .literal("defer")) != null) {
+ if (kind == .module or element.getAttributeSafe(comptime .wrap("defer")) != null) {
break :blk .@"defer";
}
@@ -754,7 +754,7 @@ pub const Script = struct {
return;
}
- switch (self.mode) {
+ switch (self.mode) {
.import_async => |ia| ia.callback(ia.data, error.FailedToLoad),
.import => {
const entry = manager.imported_modules.getPtr(self.url).?;
diff --git a/src/browser/dump.zig b/src/browser/dump.zig
index 3d852146..a8b2bec6 100644
--- a/src/browser/dump.zig
+++ b/src/browser/dump.zig
@@ -66,7 +66,7 @@ pub fn root(doc: *Node.Document, opts: RootOpts, writer: *std.Io.Writer, page: *
if (opts.with_base) {
const parent = if (html_doc.getHead()) |head| head.asNode() else doc.asNode();
const base = try doc.createElement("base", null, page);
- try base.setAttributeSafe("base", page.base(), page);
+ try base.setAttributeSafe(comptime .wrap("base"), .wrap(page.base()), page);
_ = try parent.insertBefore(base.asNode(), parent.firstChild(), page);
}
}
@@ -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(comptime .literal("slot"))) |_| {
+ if (el.getAttributeSafe(comptime .wrap("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(comptime .literal("as"))) |as| {
+ if (el.getAttributeSafe(comptime .wrap("as"))) |as| {
if (std.mem.eql(u8, as, "script")) return true;
}
- if (el.getAttributeSafe(comptime .literal("rel"))) |rel| {
+ if (el.getAttributeSafe(comptime .wrap("rel"))) |rel| {
if (std.mem.eql(u8, rel, "modulepreload") or std.mem.eql(u8, rel, "preload")) {
- if (el.getAttributeSafe(comptime .literal("as"))) |as| {
+ if (el.getAttributeSafe(comptime .wrap("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(comptime .literal("rel"))) |rel| {
+ if (el.getAttributeSafe(comptime .wrap("rel"))) |rel| {
if (std.mem.eql(u8, rel, "stylesheet")) return true;
}
}
diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig
index c3a38ed1..1eb160ba 100644
--- a/src/browser/js/Caller.zig
+++ b/src/browser/js/Caller.zig
@@ -18,6 +18,8 @@
const std = @import("std");
const log = @import("../../log.zig");
+const string = @import("../../string.zig");
+
const Page = @import("../Page.zig");
const js = @import("js.zig");
@@ -219,7 +221,7 @@ fn _getNamedIndex(self: *Caller, comptime T: type, func: anytype, name: *const v
const F = @TypeOf(func);
var args = try self.getArgs(F, 2, info);
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
- @field(args, "1") = try self.nameToString(name);
+ @field(args, "1") = try self.nameToString(@TypeOf(args.@"1"), name);
const ret = @call(.auto, func, args);
return self.handleIndexedReturn(T, F, true, ret, info, opts);
}
@@ -241,7 +243,7 @@ fn _setNamedIndex(self: *Caller, comptime T: type, func: anytype, name: *const v
const F = @TypeOf(func);
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
- @field(args, "1") = try self.nameToString(name);
+ @field(args, "1") = try self.nameToString(@TypeOf(args.@"1"), name);
@field(args, "2") = try self.local.jsValueToZig(@TypeOf(@field(args, "2")), js_value);
if (@typeInfo(F).@"fn".params.len == 4) {
@field(args, "3") = self.local.ctx.page;
@@ -266,7 +268,7 @@ fn _deleteNamedIndex(self: *Caller, comptime T: type, func: anytype, name: *cons
const F = @TypeOf(func);
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
- @field(args, "1") = try self.nameToString(name);
+ @field(args, "1") = try self.nameToString(@TypeOf(args.@"1"), name);
if (@typeInfo(F).@"fn".params.len == 3) {
@field(args, "2") = self.local.ctx.page;
}
@@ -311,8 +313,15 @@ fn isInErrorSet(err: anyerror, comptime T: type) bool {
return false;
}
-fn nameToString(self: *const Caller, name: *const v8.Name) ![]const u8 {
- return self.local.valueHandleToString(@ptrCast(name), .{});
+fn nameToString(self: *const Caller, comptime T: type, name: *const v8.Name) !T {
+ const v8_string = @as(*const v8.String, @ptrCast(name));
+ if (T == string.String) {
+ return self.local.jsStringToStringSSO(v8_string, .{});
+ }
+ if (T == string.Global) {
+ return self.local.jsStringToStringSSO(v8_string, .{ .allocator = self.local.ctx.allocator });
+ }
+ return try self.local.valueHandleToString(v8_string, .{});
}
fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void {
diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig
index ffb1876d..c47a71a7 100644
--- a/src/browser/js/Local.zig
+++ b/src/browser/js/Local.zig
@@ -293,6 +293,10 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
return js_obj.toValue();
}
}
+ if (T == string.String or T == string.Global) {
+ // would have been handled by simpleZigValueToJs
+ unreachable;
+ }
// zig fmt: off
switch (T) {
@@ -1154,11 +1158,15 @@ pub fn jsStringToStringSSO(self: *const Local, str: anytype, opts: ToStringOpts)
const len: usize = @intCast(v8.v8__String__Utf8Length(handle, self.isolate.handle));
if (len <= 12) {
- var content: [12]u8 = @splat(0);
- const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, &content, len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
+ var content: [12]u8 = undefined;
+ const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, &content[0], content.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
if (comptime IS_DEBUG) {
std.debug.assert(n == len);
}
+ // Weird that we do this _after_, but we have to..I've seen weird issues
+ // in ReleaseMode where v8 won't write to content if it starts off zero
+ // initiated
+ @memset(content[len..], 0);
return .{ .len = @intCast(len), .payload = .{ .content = content } };
}
diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig
index e461a878..ee3c8009 100644
--- a/src/browser/js/js.zig
+++ b/src/browser/js/js.zig
@@ -20,6 +20,7 @@ const std = @import("std");
pub const v8 = @import("v8").c;
const log = @import("../../log.zig");
+const string = @import("../../string.zig");
pub const Env = @import("Env.zig");
pub const bridge = @import("bridge.zig");
@@ -130,6 +131,7 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool,
},
.@"struct" => {
switch (@TypeOf(value)) {
+ string.String => return isolate.initStringHandle(value.str()),
ArrayBuffer => {
const values = value.values;
const len = values.len;
diff --git a/src/browser/tests/element/html/input.html b/src/browser/tests/element/html/input.html
index b78c8185..2cc51a9f 100644
--- a/src/browser/tests/element/html/input.html
+++ b/src/browser/tests/element/html/input.html
@@ -183,7 +183,7 @@
}
-
+ -->
diff --git a/src/browser/webapi/CustomElementDefinition.zig b/src/browser/webapi/CustomElementDefinition.zig
index 41da64cb..de802f12 100644
--- a/src/browser/webapi/CustomElementDefinition.zig
+++ b/src/browser/webapi/CustomElementDefinition.zig
@@ -17,6 +17,8 @@
// along with this program. If not, see .
const std = @import("std");
+const String = @import("../../string.zig").String;
+
const js = @import("../js/js.zig");
const Page = @import("../Page.zig");
const Element = @import("Element.zig");
@@ -25,13 +27,16 @@ const CustomElementDefinition = @This();
name: []const u8,
constructor: js.Function.Global,
+
+// TODO: Make this a Map
observed_attributes: std.StringHashMapUnmanaged(void) = .{},
+
// For customized built-in elements, this is the element tag they extend (e.g., .button)
// For autonomous custom elements, this is null
extends: ?Element.Tag = null,
-pub fn isAttributeObserved(self: *const CustomElementDefinition, name: []const u8) bool {
- return self.observed_attributes.contains(name);
+pub fn isAttributeObserved(self: *const CustomElementDefinition, name: String) bool {
+ return self.observed_attributes.contains(name.str());
}
pub fn isAutonomous(self: *const CustomElementDefinition) bool {
diff --git a/src/browser/webapi/CustomElementRegistry.zig b/src/browser/webapi/CustomElementRegistry.zig
index d69ad542..461e7c6a 100644
--- a/src/browser/webapi/CustomElementRegistry.zig
+++ b/src/browser/webapi/CustomElementRegistry.zig
@@ -188,9 +188,9 @@ pub fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinitio
// Invoke attributeChangedCallback for existing observed attributes
var attr_it = custom.asElement().attributeIterator();
while (attr_it.next()) |attr| {
- const name = attr._name.str();
+ const name = attr._name;
if (definition.isAttributeObserved(name)) {
- custom.invokeAttributeChangedCallback(name, null, attr._value.str(), page);
+ custom.invokeAttributeChangedCallback(name, null, attr._value, page);
}
}
diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig
index e3b11987..e5c5f623 100644
--- a/src/browser/webapi/Document.zig
+++ b/src/browser/webapi/Document.zig
@@ -144,7 +144,7 @@ pub fn createElement(self: *Document, name: []const u8, options_: ?CreateElement
const options = options_ orelse return element;
if (options.is) |is_value| {
- try element.setAttribute("is", is_value, page);
+ try element.setAttribute(comptime .wrap("is"), .wrap(is_value), page);
try Element.Html.Custom.checkAndAttachBuiltIn(element, page);
}
@@ -162,26 +162,26 @@ pub fn createElementNS(self: *Document, namespace: ?[]const u8, name: []const u8
return node.as(Element);
}
-pub fn createAttribute(_: *const Document, name: []const u8, page: *Page) !?*Element.Attribute {
- try Element.Attribute.validateAttributeName(name);
+pub fn createAttribute(_: *const Document, name: String.Global, page: *Page) !?*Element.Attribute {
+ try Element.Attribute.validateAttributeName(name.str);
return page._factory.node(Element.Attribute{
._proto = undefined,
- ._name = try page.dupeString(name),
- ._value = "",
+ ._name = name.str,
+ ._value = String.empty,
._element = null,
});
}
-pub fn createAttributeNS(_: *const Document, namespace: []const u8, name: []const u8, page: *Page) !?*Element.Attribute {
+pub fn createAttributeNS(_: *const Document, namespace: []const u8, name: String.Global, page: *Page) !?*Element.Attribute {
if (std.mem.eql(u8, namespace, "http://www.w3.org/1999/xhtml") == false) {
log.warn(.not_implemented, "document.createAttributeNS", .{ .namespace = namespace });
}
- try Element.Attribute.validateAttributeName(name);
+ try Element.Attribute.validateAttributeName(name.str);
return page._factory.node(Element.Attribute{
._proto = undefined,
- ._name = try page.dupeString(name),
- ._value = "",
+ ._name = name.str,
+ ._value = String.empty,
._element = null,
});
}
@@ -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(comptime .literal("id")) orelse continue;
+ const element_id = el.getAttributeSafe(comptime .wrap("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 3f933fdc..74099104 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(comptime .literal("id"))) |element_id| {
+ if (el.getAttributeSafe(comptime .wrap("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 456369cf..f983b514 100644
--- a/src/browser/webapi/Element.zig
+++ b/src/browser/webapi/Element.zig
@@ -427,35 +427,35 @@ pub fn setInnerHTML(self: *Element, html: []const u8, page: *Page) !void {
}
pub fn getId(self: *const Element) []const u8 {
- return self.getAttributeSafe(comptime .literal("id")) orelse "";
+ return self.getAttributeSafe(comptime .wrap("id")) orelse "";
}
pub fn setId(self: *Element, value: []const u8, page: *Page) !void {
- return self.setAttributeSafe("id", value, page);
+ return self.setAttributeSafe(comptime .wrap("id"), .wrap(value), page);
}
pub fn getSlot(self: *const Element) []const u8 {
- return self.getAttributeSafe(comptime .literal("slot")) orelse "";
+ return self.getAttributeSafe(comptime .wrap("slot")) orelse "";
}
pub fn setSlot(self: *Element, value: []const u8, page: *Page) !void {
- return self.setAttributeSafe("slot", value, page);
+ return self.setAttributeSafe(comptime .wrap("slot"), .wrap(value), page);
}
pub fn getDir(self: *const Element) []const u8 {
- return self.getAttributeSafe(comptime .literal("dir")) orelse "";
+ return self.getAttributeSafe(comptime .wrap("dir")) orelse "";
}
pub fn setDir(self: *Element, value: []const u8, page: *Page) !void {
- return self.setAttributeSafe("dir", value, page);
+ return self.setAttributeSafe(comptime .wrap("dir"), .wrap(value), page);
}
pub fn getClassName(self: *const Element) []const u8 {
- return self.getAttributeSafe(comptime .literal("class")) orelse "";
+ return self.getAttributeSafe(comptime .wrap("class")) orelse "";
}
pub fn setClassName(self: *Element, value: []const u8, page: *Page) !void {
- return self.setAttributeSafe("class", value, page);
+ return self.setAttributeSafe(comptime .wrap("class"), .wrap(value), page);
}
pub fn attributeIterator(self: *Element) Attribute.InnerIterator {
@@ -463,7 +463,7 @@ pub fn attributeIterator(self: *Element) Attribute.InnerIterator {
return attributes.iterator();
}
-pub fn getAttribute(self: *const Element, name: []const u8, page: *Page) !?[]const u8 {
+pub fn getAttribute(self: *const Element, name: String, page: *Page) !?String {
const attributes = self._attributes orelse return null;
return attributes.get(name, page);
}
@@ -472,9 +472,9 @@ pub fn getAttribute(self: *const Element, name: []const u8, page: *Page) !?[]con
pub fn getAttributeNS(
self: *const Element,
maybe_namespace: ?[]const u8,
- local_name: []const u8,
+ local_name: String,
page: *Page,
-) !?[]const u8 {
+) !?String {
if (maybe_namespace) |namespace| {
if (!std.mem.eql(u8, namespace, "http://www.w3.org/1999/xhtml")) {
log.warn(.not_implemented, "Element.getAttributeNS", .{ .namespace = namespace });
@@ -489,7 +489,7 @@ pub fn getAttributeSafe(self: *const Element, name: String) ?[]const u8 {
return attributes.getSafe(name);
}
-pub fn hasAttribute(self: *const Element, name: []const u8, page: *Page) !bool {
+pub fn hasAttribute(self: *const Element, name: String, page: *Page) !bool {
const attributes = self._attributes orelse return false;
const value = try attributes.get(name, page);
return value != null;
@@ -505,12 +505,12 @@ pub fn hasAttributes(self: *const Element) bool {
return attributes.isEmpty() == false;
}
-pub fn getAttributeNode(self: *Element, name: []const u8, page: *Page) !?*Attribute {
+pub fn getAttributeNode(self: *Element, name: String, page: *Page) !?*Attribute {
const attributes = self._attributes orelse return null;
return attributes.getAttribute(name, self, page);
}
-pub fn setAttribute(self: *Element, name: []const u8, value: []const u8, page: *Page) !void {
+pub fn setAttribute(self: *Element, name: String, value: String, page: *Page) !void {
try Attribute.validateAttributeName(name);
const attributes = try self.getOrCreateAttributeList(page);
_ = try attributes.put(name, value, self, page);
@@ -533,10 +533,10 @@ pub fn setAttributeNS(
qualified_name[idx + 1 ..]
else
qualified_name;
- return self.setAttribute(local_name, value, page);
+ return self.setAttribute(.wrap(local_name), .wrap(value), page);
}
-pub fn setAttributeSafe(self: *Element, name: []const u8, value: []const u8, page: *Page) !void {
+pub fn setAttributeSafe(self: *Element, name: String, value: String, page: *Page) !void {
const attributes = try self.getOrCreateAttributeList(page);
_ = try attributes.putSafe(name, value, self, page);
}
@@ -607,19 +607,19 @@ pub fn setAttributeNode(self: *Element, attr: *Attribute, page: *Page) !?*Attrib
return attributes.putAttribute(attr, self, page);
}
-pub fn removeAttribute(self: *Element, name: []const u8, page: *Page) !void {
+pub fn removeAttribute(self: *Element, name: String, page: *Page) !void {
const attributes = self._attributes orelse return;
return attributes.delete(name, self, page);
}
-pub fn toggleAttribute(self: *Element, name: []const u8, force: ?bool, page: *Page) !bool {
+pub fn toggleAttribute(self: *Element, name: String, force: ?bool, page: *Page) !bool {
try Attribute.validateAttributeName(name);
const has = try self.hasAttribute(name, page);
const should_add = force orelse !has;
if (should_add and !has) {
- try self.setAttribute(name, "", page);
+ try self.setAttribute(name, String.empty, page);
return true;
} else if (!should_add and has) {
try self.removeAttribute(name, page);
@@ -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 = comptime .literal("class"),
+ ._attribute_name = comptime .wrap("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 = comptime .literal("rel"),
+ ._attribute_name = comptime .wrap("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(comptime .literal("width"))) |w| {
+ if (self.getAttributeSafe(comptime .wrap("width"))) |w| {
width = std.fmt.parseFloat(f64, w) catch width;
}
- if (self.getAttributeSafe(comptime .literal("height"))) |h| {
+ if (self.getAttributeSafe(comptime .wrap("height"))) |h| {
height = std.fmt.parseFloat(f64, h) catch height;
}
}
diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig
index 20a1ea7d..8d01cb42 100644
--- a/src/browser/webapi/MutationObserver.zig
+++ b/src/browser/webapi/MutationObserver.zig
@@ -17,6 +17,8 @@
// along with this program. If not, see .
const std = @import("std");
+const String = @import("../../string.zig").String;
+
const js = @import("../js/js.zig");
const Page = @import("../Page.zig");
const Node = @import("Node.zig");
@@ -109,8 +111,8 @@ pub fn takeRecords(self: *MutationObserver, page: *Page) ![]*MutationRecord {
pub fn notifyAttributeChange(
self: *MutationObserver,
target: *Element,
- attribute_name: []const u8,
- old_value: ?[]const u8,
+ attribute_name: String,
+ old_value: ?String,
page: *Page,
) !void {
const target_node = target.asNode();
@@ -129,7 +131,7 @@ pub fn notifyAttributeChange(
}
if (obs.options.attributeFilter) |filter| {
for (filter) |name| {
- if (std.mem.eql(u8, name, attribute_name)) {
+ if (attribute_name.eqlSlice(name)) {
break;
}
} else {
@@ -140,9 +142,9 @@ pub fn notifyAttributeChange(
const record = try page._factory.create(MutationRecord{
._type = .attributes,
._target = target_node,
- ._attribute_name = try page.arena.dupe(u8, attribute_name),
+ ._attribute_name = try page.arena.dupe(u8, attribute_name.str()),
._old_value = if (obs.options.attributeOldValue and old_value != null)
- try page.arena.dupe(u8, old_value.?)
+ try page.arena.dupe(u8, old_value.?.str())
else
null,
._added_nodes = &.{},
diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig
index 295d957b..8948c999 100644
--- a/src/browser/webapi/Node.zig
+++ b/src/browser/webapi/Node.zig
@@ -17,8 +17,9 @@
// along with this program. If not, see .
const std = @import("std");
-
const log = @import("../../log.zig");
+const String = @import("../../string.zig").String;
+
const js = @import("../js/js.zig");
const Page = @import("../Page.zig");
const reflect = @import("../reflect.zig");
@@ -268,7 +269,7 @@ pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!vo
.document => {},
.document_type => {},
.document_fragment => {},
- .attribute => |attr| try writer.writeAll(attr._value),
+ .attribute => |attr| try writer.writeAll(attr._value.str()),
}
}
@@ -297,7 +298,7 @@ pub fn setTextContent(self: *Node, data: []const u8, page: *Page) !void {
}
return frag.replaceChildren(&.{.{ .text = data }}, page);
},
- .attribute => |attr| return attr.setValue(data, page),
+ .attribute => |attr| return attr.setValue(.wrap(data), page),
}
}
@@ -313,7 +314,7 @@ pub fn getNodeName(self: *const Node, buf: []u8) []const u8 {
.document => "#document",
.document_type => |dt| dt.getName(),
.document_fragment => "#document-fragment",
- .attribute => |attr| attr._name,
+ .attribute => |attr| attr._name.str(),
};
}
@@ -559,7 +560,7 @@ pub fn replaceChild(self: *Node, new_child: *Node, old_child: *Node, page: *Page
pub fn getNodeValue(self: *const Node) ?[]const u8 {
return switch (self._type) {
.cdata => |c| c.getData(),
- .attribute => |attr| attr._value,
+ .attribute => |attr| attr._value.str(),
.element => null,
.document => null,
.document_type => null,
@@ -567,9 +568,9 @@ pub fn getNodeValue(self: *const Node) ?[]const u8 {
};
}
-pub fn setNodeValue(self: *const Node, value: ?[]const u8, page: *Page) !void {
+pub fn setNodeValue(self: *const Node, value: ?String, page: *Page) !void {
switch (self._type) {
- .cdata => |c| try c.setData(value, page),
+ .cdata => |c| try c.setData(if (value) |v| v.str() else null, page),
.attribute => |attr| try attr.setValue(value, page),
.element => {},
.document => {},
@@ -910,7 +911,7 @@ pub const JsApi = struct {
return buf.written();
},
.cdata => |cdata| return cdata.getData(),
- .attribute => |attr| return attr._value,
+ .attribute => |attr| return attr._value.str(),
.document => return null,
.document_type => return null,
.document_fragment => return null,
diff --git a/src/browser/webapi/ShadowRoot.zig b/src/browser/webapi/ShadowRoot.zig
index 25048913..09bfa2d9 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(comptime .literal("id")) orelse continue;
+ const element_id = el.getAttributeSafe(comptime .wrap("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 8506175a..a9f31812 100644
--- a/src/browser/webapi/collections/DOMTokenList.zig
+++ b/src/browser/webapi/collections/DOMTokenList.zig
@@ -160,8 +160,8 @@ pub fn getValue(self: *const DOMTokenList) []const u8 {
return self._element.getAttributeSafe(self._attribute_name) orelse "";
}
-pub fn setValue(self: *DOMTokenList, value: []const u8, page: *Page) !void {
- try self._element.setAttribute(self._attribute_name.str(), value, page);
+pub fn setValue(self: *DOMTokenList, value: String, page: *Page) !void {
+ try self._element.setAttribute(self._attribute_name, value, page);
}
pub fn keys(self: *DOMTokenList, page: *Page) !*KeyIterator {
@@ -227,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.str(), joined, page);
+ try self._element.setAttribute(self._attribute_name, .wrap(joined), page);
}
const Iterator = struct {
diff --git a/src/browser/webapi/collections/HTMLAllCollection.zig b/src/browser/webapi/collections/HTMLAllCollection.zig
index facb856a..32fb3056 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(comptime .literal("name"))) |attr_name| {
+ if (el.getAttributeSafe(comptime .wrap("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 e7f06a14..20f9210b 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(comptime .literal("id"))) |id| {
+ if (element.getAttributeSafe(comptime .wrap("id"))) |id| {
if (std.mem.eql(u8, id, name)) {
break :blk true;
}
}
- if (element.getAttributeSafe(comptime .literal("name"))) |elem_name| {
+ if (element.getAttributeSafe(comptime .wrap("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 fc5347c3..f236f3a7 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(comptime .literal("value")) orelse "on";
+ return element.getAttributeSafe(comptime .wrap("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(comptime .literal("value"));
+ const input_value = element.getAttributeSafe(comptime .wrap("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(comptime .literal("id"))) |id| {
+ if (element.getAttributeSafe(comptime .wrap("id"))) |id| {
if (std.mem.eql(u8, id, self._name)) {
return true;
}
}
- if (element.getAttributeSafe(comptime .literal("name"))) |elem_name| {
+ if (element.getAttributeSafe(comptime .wrap("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 3d28ad07..afe7a3f4 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(comptime .literal("name")) orelse continue;
+ const element_name = element.getAttributeSafe(comptime .wrap("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(comptime .literal("class")) orelse return false;
+ const class_attr = el.getAttributeSafe(comptime .wrap("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(comptime .literal("name")) orelse return false;
+ const name_attr = el.getAttributeSafe(comptime .wrap("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(comptime .literal("href"));
+ return el.hasAttributeSafe(comptime .wrap("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(comptime .literal("name"));
+ return el.hasAttributeSafe(comptime .wrap("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(comptime .literal("form"))) |form_attr| {
- const form_id = self._filter.asElement().getAttributeSafe(comptime .literal("id")) orelse return false;
+ if (el.getAttributeSafe(comptime .wrap("form"))) |form_attr| {
+ const form_id = self._filter.asElement().getAttributeSafe(comptime .wrap("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 a913457a..5a86454d 100644
--- a/src/browser/webapi/element/Attribute.zig
+++ b/src/browser/webapi/element/Attribute.zig
@@ -39,33 +39,33 @@ pub fn registerTypes() []const type {
pub const Attribute = @This();
_proto: *Node,
-_name: []const u8,
-_value: []const u8,
+_name: String,
+_value: String,
_element: ?*Element,
pub fn format(self: *const Attribute, writer: *std.Io.Writer) !void {
return formatAttribute(self._name, self._value, writer);
}
-pub fn getName(self: *const Attribute) []const u8 {
+pub fn getName(self: *const Attribute) String {
return self._name;
}
-pub fn getValue(self: *const Attribute) []const u8 {
+pub fn getValue(self: *const Attribute) String {
return self._value;
}
-pub fn setValue(self: *Attribute, data_: ?[]const u8, page: *Page) !void {
- const data = data_ orelse "";
+pub fn setValue(self: *Attribute, data_: ?String, page: *Page) !void {
+ const data = data_ orelse String.empty;
const el = self._element orelse {
- self._value = try page.dupeString(data);
+ self._value = try data.dupe(page.arena);
return;
};
// this takes ownership of the data
try el.setAttribute(self._name, data, page);
// not the most efficient, but we don't expect this to be called often
- self._value = (try el.getAttribute(self._name, page)) orelse "";
+ self._value = (try el.getAttribute(self._name, page)) orelse String.empty;
}
pub fn getNamespaceURI(_: *const Attribute) ?[]const u8 {
@@ -77,7 +77,7 @@ pub fn getOwnerElement(self: *const Attribute) ?*Element {
}
pub fn isEqualNode(self: *const Attribute, other: *const Attribute) bool {
- return std.mem.eql(u8, self.getName(), other.getName()) and std.mem.eql(u8, self.getValue(), other.getValue());
+ return self.getName().eql(other.getName()) and self.getValue().eql(other.getValue());
}
pub fn clone(self: *const Attribute, page: *Page) !*Attribute {
@@ -139,9 +139,9 @@ pub const List = struct {
return self._list.first == null;
}
- pub fn get(self: *const List, name: []const u8, page: *Page) !?[]const u8 {
+ pub fn get(self: *const List, name: String, page: *Page) !?String {
const entry = (try self.getEntry(name, page)) orelse return null;
- return entry._value.str();
+ return entry._value;
}
pub inline fn length(self: *const List) usize {
@@ -180,7 +180,7 @@ pub const List = struct {
return self.getEntryWithNormalizedName(name) != null;
}
- pub fn getAttribute(self: *const List, name: []const u8, element: ?*Element, page: *Page) !?*Attribute {
+ pub fn getAttribute(self: *const List, name: String, element: ?*Element, page: *Page) !?*Attribute {
const entry = (try self.getEntry(name, page)) orelse return null;
const gop = try page._attribute_lookup.getOrPut(page.arena, @intFromPtr(entry));
if (gop.found_existing) {
@@ -191,33 +191,33 @@ pub const List = struct {
return attribute;
}
- pub fn put(self: *List, name: []const u8, value: []const u8, element: *Element, page: *Page) !*Entry {
+ pub fn put(self: *List, name: String, value: String, element: *Element, page: *Page) !*Entry {
const result = try self.getEntryAndNormalizedName(name, page);
return self._put(result, value, element, page);
}
- pub fn putSafe(self: *List, name: []const u8, value: []const u8, element: *Element, page: *Page) !*Entry {
- const entry = self.getEntryWithNormalizedNameOld(name);
+ pub fn putSafe(self: *List, name: String, value: String, element: *Element, page: *Page) !*Entry {
+ const entry = self.getEntryWithNormalizedName(name);
return self._put(.{ .entry = entry, .normalized = name }, value, element, page);
}
- fn _put(self: *List, result: NormalizeAndEntry, value: []const u8, element: *Element, page: *Page) !*Entry {
+ fn _put(self: *List, result: NormalizeAndEntry, value: String, element: *Element, page: *Page) !*Entry {
const is_id = shouldAddToIdMap(result.normalized, element);
var entry: *Entry = undefined;
- var old_value: ?[]const u8 = null;
+ var old_value: ?String = null;
if (result.entry) |e| {
- old_value = try page.call_arena.dupe(u8, e._value.str());
+ old_value = try e._value.dupe(page.call_arena);
if (is_id) {
page.removeElementId(element, e._value.str());
}
- e._value = try String.init(page.arena, value, .{});
+ e._value = try value.dupe(page.arena);
entry = e;
} else {
entry = try page._factory.create(Entry{
._node = .{},
- ._name = try String.init(page.arena, result.normalized, .{}),
- ._value = try String.init(page.arena, value, .{}),
+ ._name = try result.normalized.dupe(page.arena),
+ ._value = try value.dupe(page.arena),
});
self._list.append(&entry._node);
self._len += 1;
@@ -230,7 +230,7 @@ pub const List = struct {
try page.addElementId(parent, element, entry._value.str());
}
page.domChanged();
- page.attributeChange(element, result.normalized, entry._value.str(), old_value);
+ page.attributeChange(element, result.normalized, entry._value, old_value);
return entry;
}
@@ -266,7 +266,7 @@ pub const List = struct {
// called form our parser, names already lower-cased
pub fn putNew(self: *List, name: []const u8, value: []const u8, page: *Page) !void {
- if (try self.getEntry(name, page) != null) {
+ if (try self.getEntry(.wrap(name), page) != null) {
// When parsing, if there are dupicate names, it isn't valid, and
// the first is kept
return;
@@ -281,12 +281,12 @@ pub const List = struct {
self._len += 1;
}
- pub fn delete(self: *List, name: []const u8, element: *Element, page: *Page) !void {
+ pub fn delete(self: *List, name: String, element: *Element, page: *Page) !void {
const result = try self.getEntryAndNormalizedName(name, page);
const entry = result.entry orelse return;
const is_id = shouldAddToIdMap(result.normalized, element);
- const old_value = entry._value.str();
+ const old_value = entry._value;
if (is_id) {
page.removeElementId(element, entry._value.str());
@@ -314,7 +314,7 @@ pub const List = struct {
return .{ ._node = self._list.first };
}
- fn getEntry(self: *const List, name: []const u8, page: *Page) !?*Entry {
+ fn getEntry(self: *const List, name: String, page: *Page) !?*Entry {
const result = try self.getEntryAndNormalizedName(name, page);
return result.entry;
}
@@ -322,16 +322,16 @@ pub const List = struct {
// Dangerous, the returned normalized name is only valid until someone
// else uses pages.buf.
const NormalizeAndEntry = struct {
- normalized: []const u8,
entry: ?*Entry,
+ normalized: String,
};
- fn getEntryAndNormalizedName(self: *const List, name: []const u8, page: *Page) !NormalizeAndEntry {
+ fn getEntryAndNormalizedName(self: *const List, name: String, page: *Page) !NormalizeAndEntry {
const normalized =
if (self.normalize) try normalizeNameForLookup(name, page) else name;
return .{
.normalized = normalized,
- .entry = self.getEntryWithNormalizedNameOld(normalized),
+ .entry = self.getEntryWithNormalizedName(normalized),
};
}
@@ -347,19 +347,6 @@ pub const List = struct {
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);
- if (e._name.eqlSlice(name)) {
- return e;
- }
- node = n.next;
- }
- return null;
- }
-
pub const Entry = struct {
_name: String,
_value: String,
@@ -376,7 +363,7 @@ pub const List = struct {
}
pub fn format(self: *const Entry, writer: *std.Io.Writer) !void {
- return formatAttribute(self._name.str(), self._value.str(), writer);
+ return formatAttribute(self._name, self._value, writer);
}
pub fn toAttribute(self: *const Entry, element: ?*Element, page: *Page) !*Attribute {
@@ -386,15 +373,15 @@ pub const List = struct {
// Cannot directly reference self._name.str() and self._value.str()
// This attribute can outlive the list entry (the node can be
// removed from the element's attribute, but still exist in the DOM)
- ._name = try page.dupeString(self._name.str()),
- ._value = try page.dupeString(self._value.str()),
+ ._name = try self._name.dupe(page.arena),
+ ._value = try self._value.dupe(page.arena),
});
}
};
};
-fn shouldAddToIdMap(normalized_name: []const u8, element: *Element) bool {
- if (!std.mem.eql(u8, normalized_name, "id")) {
+fn shouldAddToIdMap(normalized_name: String, element: *Element) bool {
+ if (!normalized_name.eql(comptime .wrap("id"))) {
return false;
}
@@ -407,17 +394,19 @@ fn shouldAddToIdMap(normalized_name: []const u8, element: *Element) bool {
return node.isConnected();
}
-pub fn validateAttributeName(name: []const u8) !void {
- if (name.len == 0) {
+pub fn validateAttributeName(name: String) !void {
+ const name_str = name.str();
+
+ if (name_str.len == 0) {
return error.InvalidCharacterError;
}
- const first = name[0];
+ const first = name_str[0];
if ((first >= '0' and first <= '9') or first == '-' or first == '.') {
return error.InvalidCharacterError;
}
- for (name) |c| {
+ for (name_str) |c| {
if (c == 0 or c == '/' or c == '=' or c == '>' or std.ascii.isWhitespace(c)) {
return error.InvalidCharacterError;
}
@@ -433,14 +422,16 @@ pub fn validateAttributeName(name: []const u8) !void {
}
}
-pub fn normalizeNameForLookup(name: []const u8, page: *Page) ![]const u8 {
- if (!needsLowerCasing(name)) {
+pub fn normalizeNameForLookup(name: String, page: *Page) !String {
+ if (!needsLowerCasing(name.str())) {
return name;
}
- if (name.len < page.buf.len) {
- return std.ascii.lowerString(&page.buf, name);
- }
- return std.ascii.allocLowerString(page.call_arena, name);
+ const normalized = if (name.len < page.buf.len)
+ std.ascii.lowerString(&page.buf, name.str())
+ else
+ try std.ascii.allocLowerString(page.call_arena, name.str());
+
+ return .wrap(normalized);
}
fn needsLowerCasing(name: []const u8) bool {
@@ -494,7 +485,7 @@ pub const NamedNodeMap = struct {
return null;
}
- pub fn getByName(self: *const NamedNodeMap, name: []const u8, page: *Page) !?*Attribute {
+ pub fn getByName(self: *const NamedNodeMap, name: String, page: *Page) !?*Attribute {
return self._list.getAttribute(name, self._element, page);
}
@@ -503,7 +494,7 @@ pub const NamedNodeMap = struct {
return self._list.putAttribute(attribute, self._element, page);
}
- pub fn removeByName(self: *const NamedNodeMap, name: []const u8, page: *Page) !?*Attribute {
+ pub fn removeByName(self: *const NamedNodeMap, name: String, page: *Page) !?*Attribute {
// this 2-step process (get then delete) isn't efficient. But we don't
// expect this to be called often, and this lets us keep delete straightforward.
const attr = (try self.getByName(name, page)) orelse return null;
@@ -569,11 +560,13 @@ pub const InnerIterator = struct {
}
};
-fn formatAttribute(name: []const u8, value: []const u8, writer: *std.Io.Writer) !void {
- try writer.writeAll(name);
+fn formatAttribute(name: String, value_: String, writer: *std.Io.Writer) !void {
+ try writer.writeAll(name.str());
// Boolean attributes with empty values are serialized without a value
- if (value.len == 0 and boolean_attributes_lookup.has(name)) {
+
+ const value = value_.str();
+ if (value.len == 0 and boolean_attributes_lookup.has(name.str())) {
return;
}
diff --git a/src/browser/webapi/element/DOMStringMap.zig b/src/browser/webapi/element/DOMStringMap.zig
index 4d1d2307..5592a193 100644
--- a/src/browser/webapi/element/DOMStringMap.zig
+++ b/src/browser/webapi/element/DOMStringMap.zig
@@ -21,6 +21,7 @@ const js = @import("../../js/js.zig");
const Element = @import("../Element.zig");
const Page = @import("../../Page.zig");
+const String = @import("../../../string.zig").String;
const Allocator = std.mem.Allocator;
@@ -28,28 +29,60 @@ const DOMStringMap = @This();
_element: *Element,
-fn getProperty(self: *DOMStringMap, name: []const u8, page: *Page) !?[]const u8 {
+fn getProperty(self: *DOMStringMap, name: String, page: *Page) !?String {
const attr_name = try camelToKebab(page.call_arena, name);
return try self._element.getAttribute(attr_name, page);
}
-fn setProperty(self: *DOMStringMap, name: []const u8, value: []const u8, page: *Page) !void {
+fn setProperty(self: *DOMStringMap, name: String, value: String, page: *Page) !void {
const attr_name = try camelToKebab(page.call_arena, name);
return self._element.setAttributeSafe(attr_name, value, page);
}
-fn deleteProperty(self: *DOMStringMap, name: []const u8, page: *Page) !void {
+fn deleteProperty(self: *DOMStringMap, name: String, page: *Page) !void {
const attr_name = try camelToKebab(page.call_arena, name);
try self._element.removeAttribute(attr_name, page);
}
-// fooBar -> foo-bar
-fn camelToKebab(arena: Allocator, camel: []const u8) ![]const u8 {
+// fooBar -> data-foo-bar (with SSO optimization for short strings)
+fn camelToKebab(arena: Allocator, camel: String) !String {
+ const camel_str = camel.str();
+
+ // Calculate output length
+ var output_len: usize = 5; // "data-"
+ for (camel_str, 0..) |c, i| {
+ output_len += 1;
+ if (std.ascii.isUpper(c) and i > 0) output_len += 1; // extra char for '-'
+ }
+
+ if (output_len <= 12) {
+ // SSO path - no allocation!
+ var content: [12]u8 = @splat(0);
+ @memcpy(content[0..5], "data-");
+ var idx: usize = 5;
+
+ for (camel_str, 0..) |c, i| {
+ if (std.ascii.isUpper(c)) {
+ if (i > 0) {
+ content[idx] = '-';
+ idx += 1;
+ }
+ content[idx] = std.ascii.toLower(c);
+ } else {
+ content[idx] = c;
+ }
+ idx += 1;
+ }
+
+ return .{ .len = @intCast(output_len), .payload = .{ .content = content } };
+ }
+
+ // Fallback: allocate for longer strings
var result: std.ArrayList(u8) = .empty;
- try result.ensureTotalCapacity(arena, 5 + camel.len * 2);
+ try result.ensureTotalCapacity(arena, output_len);
result.appendSliceAssumeCapacity("data-");
- for (camel, 0..) |c, i| {
+ for (camel_str, 0..) |c, i| {
if (std.ascii.isUpper(c)) {
if (i > 0) {
result.appendAssumeCapacity('-');
@@ -60,7 +93,7 @@ fn camelToKebab(arena: Allocator, camel: []const u8) ![]const u8 {
}
}
- return result.items;
+ return try String.init(arena, result.items, .{});
}
// data-foo-bar -> fooBar
diff --git a/src/browser/webapi/element/Html.zig b/src/browser/webapi/element/Html.zig
index 2411e711..9eb41edd 100644
--- a/src/browser/webapi/element/Html.zig
+++ b/src/browser/webapi/element/Html.zig
@@ -243,7 +243,7 @@ fn _getInnerText(self: *HtmlElement, writer: *std.Io.Writer, state: *innerTextSt
.document => {},
.document_type => {},
.document_fragment => {},
- .attribute => |attr| try writer.writeAll(attr._value),
+ .attribute => |attr| try writer.writeAll(attr._value.str()),
}
}
}
diff --git a/src/browser/webapi/element/html/Anchor.zig b/src/browser/webapi/element/html/Anchor.zig
index ec216c39..3adb3741 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(comptime .literal("href")) orelse return "";
+ const href = element.getAttributeSafe(comptime .wrap("href")) orelse return "";
if (href.len == 0) {
return "";
}
@@ -48,15 +48,15 @@ pub fn getHref(self: *Anchor, page: *Page) ![]const u8 {
}
pub fn setHref(self: *Anchor, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("href", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("href"), .wrap(value), page);
}
pub fn getTarget(self: *Anchor) []const u8 {
- return self.asElement().getAttributeSafe(comptime .literal("target")) orelse "";
+ return self.asElement().getAttributeSafe(comptime .wrap("target")) orelse "";
}
pub fn setTarget(self: *Anchor, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("target", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("target"), .wrap(value), page);
}
pub fn getOrigin(self: *Anchor, page: *Page) ![]const u8 {
@@ -167,19 +167,19 @@ pub fn setProtocol(self: *Anchor, value: []const u8, page: *Page) !void {
}
pub fn getType(self: *Anchor) []const u8 {
- return self.asElement().getAttributeSafe(comptime .literal("type")) orelse "";
+ return self.asElement().getAttributeSafe(comptime .wrap("type")) orelse "";
}
pub fn setType(self: *Anchor, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("type", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(value), page);
}
pub fn getName(self: *const Anchor) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("name")) orelse "";
}
pub fn setName(self: *Anchor, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("name", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("name"), .wrap(value), page);
}
pub fn getText(self: *Anchor, page: *Page) ![:0]const u8 {
@@ -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(comptime .literal("href")) orelse return null;
+ const href = self.asElement().getAttributeSafe(comptime .wrap("href")) orelse return null;
if (href.len == 0) {
return null;
}
diff --git a/src/browser/webapi/element/html/Audio.zig b/src/browser/webapi/element/html/Audio.zig
index a29926aa..4f7e7491 100644
--- a/src/browser/webapi/element/html/Audio.zig
+++ b/src/browser/webapi/element/html/Audio.zig
@@ -16,6 +16,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
+const String = @import("../../../../string.zig").String;
+
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
@@ -27,16 +29,16 @@ const Audio = @This();
_proto: *Media,
-pub fn constructor(maybe_url: ?[]const u8, page: *Page) !*Media {
+pub fn constructor(maybe_url: ?String, page: *Page) !*Media {
const node = try page.createElementNS(.html, "audio", null);
const el = node.as(Element);
const list = try el.getOrCreateAttributeList(page);
// Always set to "auto" initially.
- _ = try list.putSafe("preload", "auto", el, page);
+ _ = try list.putSafe(comptime .wrap("preload"), comptime .wrap("auto"), el, page);
// Set URL if provided.
if (maybe_url) |url| {
- _ = try list.putSafe("src", url, el, page);
+ _ = try list.putSafe(comptime .wrap("src"), url, el, page);
}
return node.as(Media);
diff --git a/src/browser/webapi/element/html/Body.zig b/src/browser/webapi/element/html/Body.zig
index f00b60d1..7b5b530e 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(comptime .literal("onload")) orelse return;
+ const on_load = el.getAttributeSafe(comptime .wrap("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 9c3d342e..89bb3575 100644
--- a/src/browser/webapi/element/html/Button.zig
+++ b/src/browser/webapi/element/html/Button.zig
@@ -39,50 +39,50 @@ pub fn asNode(self: *Button) *Node {
}
pub fn getDisabled(self: *const Button) bool {
- return self.asConstElement().getAttributeSafe(comptime .literal("disabled")) != null;
+ return self.asConstElement().getAttributeSafe(comptime .wrap("disabled")) != null;
}
pub fn setDisabled(self: *Button, disabled: bool, page: *Page) !void {
if (disabled) {
- try self.asElement().setAttributeSafe("disabled", "", page);
+ try self.asElement().setAttributeSafe(comptime .wrap("disabled"), .wrap(""), page);
} else {
- try self.asElement().removeAttribute("disabled", page);
+ try self.asElement().removeAttribute(comptime .wrap("disabled"), page);
}
}
pub fn getName(self: *const Button) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("name")) orelse "";
}
pub fn setName(self: *Button, name: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("name", name, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("name"), .wrap(name), page);
}
pub fn getType(self: *const Button) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("type")) orelse "submit";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("type")) orelse "submit";
}
pub fn setType(self: *Button, typ: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("type", typ, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(typ), page);
}
pub fn getValue(self: *const Button) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("value")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("value")) orelse "";
}
pub fn setValue(self: *Button, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("value", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("value"), .wrap(value), page);
}
pub fn getRequired(self: *const Button) bool {
- return self.asConstElement().getAttributeSafe(comptime .literal("required")) != null;
+ return self.asConstElement().getAttributeSafe(comptime .wrap("required")) != null;
}
pub fn setRequired(self: *Button, required: bool, page: *Page) !void {
if (required) {
- try self.asElement().setAttributeSafe("required", "", page);
+ try self.asElement().setAttributeSafe(comptime .wrap("required"), .wrap(""), page);
} else {
- try self.asElement().removeAttribute("required", page);
+ try self.asElement().removeAttribute(comptime .wrap("required"), page);
}
}
@@ -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(comptime .literal("form"))) |form_id| {
+ if (element.getAttributeSafe(comptime .wrap("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 1021eb35..122552b5 100644
--- a/src/browser/webapi/element/html/Canvas.zig
+++ b/src/browser/webapi/element/html/Canvas.zig
@@ -40,23 +40,23 @@ pub fn asNode(self: *Canvas) *Node {
}
pub fn getWidth(self: *const Canvas) u32 {
- const attr = self.asConstElement().getAttributeSafe(comptime .literal("width")) orelse return 300;
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("width")) orelse return 300;
return std.fmt.parseUnsigned(u32, attr, 10) catch 300;
}
pub fn setWidth(self: *Canvas, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
- try self.asElement().setAttributeSafe("width", str, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(str), page);
}
pub fn getHeight(self: *const Canvas) u32 {
- const attr = self.asConstElement().getAttributeSafe(comptime .literal("height")) orelse return 150;
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("height")) orelse return 150;
return std.fmt.parseUnsigned(u32, attr, 10) catch 150;
}
pub fn setHeight(self: *Canvas, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
- try self.asElement().setAttributeSafe("height", str, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(str), page);
}
/// Since there's no base class rendering contextes inherit from,
diff --git a/src/browser/webapi/element/html/Custom.zig b/src/browser/webapi/element/html/Custom.zig
index 9965be17..832c937b 100644
--- a/src/browser/webapi/element/html/Custom.zig
+++ b/src/browser/webapi/element/html/Custom.zig
@@ -64,7 +64,7 @@ pub fn invokeDisconnectedCallback(self: *Custom, page: *Page) void {
self.invokeCallback("disconnectedCallback", .{}, page);
}
-pub fn invokeAttributeChangedCallback(self: *Custom, name: []const u8, old_value: ?[]const u8, new_value: ?[]const u8, page: *Page) void {
+pub fn invokeAttributeChangedCallback(self: *Custom, name: String, old_value: ?String, new_value: ?String, page: *Page) void {
const definition = self._definition orelse return;
if (!definition.isAttributeObserved(name)) {
return;
@@ -144,7 +144,7 @@ pub fn invokeDisconnectedCallbackOnElement(element: *Element, page: *Page) void
invokeCallbackOnElement(element, definition, "disconnectedCallback", .{}, page);
}
-pub fn invokeAttributeChangedCallbackOnElement(element: *Element, name: []const u8, old_value: ?[]const u8, new_value: ?[]const u8, page: *Page) void {
+pub fn invokeAttributeChangedCallbackOnElement(element: *Element, name: String, old_value: ?String, new_value: ?String, page: *Page) void {
// Autonomous custom element
if (element.is(Custom)) |custom| {
custom.invokeAttributeChangedCallback(name, old_value, new_value, page);
@@ -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(comptime .literal("is")) orelse return;
+ const is_value = element.getAttributeSafe(comptime .wrap("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 818042c2..421709d7 100644
--- a/src/browser/webapi/element/html/Data.zig
+++ b/src/browser/webapi/element/html/Data.zig
@@ -36,11 +36,11 @@ pub fn asNode(self: *Data) *Node {
}
pub fn getValue(self: *Data) []const u8 {
- return self.asElement().getAttributeSafe(comptime .literal("value")) orelse "";
+ return self.asElement().getAttributeSafe(comptime .wrap("value")) orelse "";
}
pub fn setValue(self: *Data, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("value", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("value"), .wrap(value), page);
}
pub const JsApi = struct {
diff --git a/src/browser/webapi/element/html/Dialog.zig b/src/browser/webapi/element/html/Dialog.zig
index 89049961..65ab02ed 100644
--- a/src/browser/webapi/element/html/Dialog.zig
+++ b/src/browser/webapi/element/html/Dialog.zig
@@ -20,23 +20,23 @@ pub fn asNode(self: *Dialog) *Node {
}
pub fn getOpen(self: *const Dialog) bool {
- return self.asConstElement().getAttributeSafe(comptime .literal("open")) != null;
+ return self.asConstElement().getAttributeSafe(comptime .wrap("open")) != null;
}
pub fn setOpen(self: *Dialog, open: bool, page: *Page) !void {
if (open) {
- try self.asElement().setAttributeSafe("open", "", page);
+ try self.asElement().setAttributeSafe(comptime .wrap("open"), .wrap(""), page);
} else {
- try self.asElement().removeAttribute("open", page);
+ try self.asElement().removeAttribute(comptime .wrap("open"), page);
}
}
pub fn getReturnValue(self: *const Dialog) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("returnvalue")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("returnvalue")) orelse "";
}
pub fn setReturnValue(self: *Dialog, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("returnvalue", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("returnvalue"), .wrap(value), page);
}
pub const JsApi = struct {
diff --git a/src/browser/webapi/element/html/Form.zig b/src/browser/webapi/element/html/Form.zig
index 71a98ea4..8a9279ac 100644
--- a/src/browser/webapi/element/html/Form.zig
+++ b/src/browser/webapi/element/html/Form.zig
@@ -43,15 +43,15 @@ pub fn asNode(self: *Form) *Node {
}
pub fn getName(self: *const Form) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("name")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("name")) orelse "";
}
pub fn setName(self: *Form, name: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("name", name, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("name"), .wrap(name), page);
}
pub fn getMethod(self: *const Form) []const u8 {
- const method = self.asConstElement().getAttributeSafe(comptime .literal("method")) orelse return "get";
+ const method = self.asConstElement().getAttributeSafe(comptime .wrap("method")) orelse return "get";
if (std.ascii.eqlIgnoreCase(method, "post")) {
return "post";
@@ -64,11 +64,11 @@ pub fn getMethod(self: *const Form) []const u8 {
}
pub fn setMethod(self: *Form, method: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("method", method, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("method"), .wrap(method), page);
}
pub fn getElements(self: *Form, page: *Page) !*collections.HTMLFormControlsCollection {
- const form_id = self.asElement().getAttributeSafe(comptime .literal("id"));
+ const form_id = self.asElement().getAttributeSafe(comptime .wrap("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 c159fa97..41482ec9 100644
--- a/src/browser/webapi/element/html/Image.zig
+++ b/src/browser/webapi/element/html/Image.zig
@@ -15,11 +15,11 @@ pub fn constructor(w_: ?u32, h_: ?u32, page: *Page) !*Image {
if (w_) |w| blk: {
const w_string = std.fmt.bufPrint(&page.buf, "{d}", .{w}) catch break :blk;
- try el.setAttributeSafe("width", w_string, page);
+ try el.setAttributeSafe(comptime .wrap("width"), .wrap(w_string), page);
}
if (h_) |h| blk: {
const h_string = std.fmt.bufPrint(&page.buf, "{d}", .{h}) catch break :blk;
- try el.setAttributeSafe("height", h_string, page);
+ try el.setAttributeSafe(comptime .wrap("height"), .wrap(h_string), page);
}
return el.as(Image);
}
@@ -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(comptime .literal("src")) orelse return "";
+ const src = element.getAttributeSafe(comptime .wrap("src")) orelse return "";
if (src.len == 0) {
return "";
}
@@ -46,54 +46,54 @@ pub fn getSrc(self: *const Image, page: *Page) ![]const u8 {
}
pub fn setSrc(self: *Image, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("src", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("src"), .wrap(value), page);
}
pub fn getAlt(self: *const Image) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("alt")) orelse "";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("alt")) orelse "";
}
pub fn setAlt(self: *Image, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("alt", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("alt"), .wrap(value), page);
}
pub fn getWidth(self: *const Image) u32 {
- const attr = self.asConstElement().getAttributeSafe(comptime .literal("width")) orelse return 0;
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("width")) orelse return 0;
return std.fmt.parseUnsigned(u32, attr, 10) catch 0;
}
pub fn setWidth(self: *Image, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
- try self.asElement().setAttributeSafe("width", str, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(str), page);
}
pub fn getHeight(self: *const Image) u32 {
- const attr = self.asConstElement().getAttributeSafe(comptime .literal("height")) orelse return 0;
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("height")) orelse return 0;
return std.fmt.parseUnsigned(u32, attr, 10) catch 0;
}
pub fn setHeight(self: *Image, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
- try self.asElement().setAttributeSafe("height", str, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(str), page);
}
pub fn getCrossOrigin(self: *const Image) ?[]const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("crossorigin"));
+ return self.asConstElement().getAttributeSafe(comptime .wrap("crossorigin"));
}
pub fn setCrossOrigin(self: *Image, value: ?[]const u8, page: *Page) !void {
if (value) |v| {
- return self.asElement().setAttributeSafe("crossorigin", v, page);
+ return self.asElement().setAttributeSafe(comptime .wrap("crossorigin"), .wrap(v), page);
}
- return self.asElement().removeAttribute("crossorigin", page);
+ return self.asElement().removeAttribute(comptime .wrap("crossorigin"), page);
}
pub fn getLoading(self: *const Image) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .literal("loading")) orelse "eager";
+ return self.asConstElement().getAttributeSafe(comptime .wrap("loading")) orelse "eager";
}
pub fn setLoading(self: *Image, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("loading", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("loading"), .wrap(value), page);
}
pub const JsApi = struct {
diff --git a/src/browser/webapi/element/html/Input.zig b/src/browser/webapi/element/html/Input.zig
index 15d8c1f8..d6fe71c2 100644
--- a/src/browser/webapi/element/html/Input.zig
+++ b/src/browser/webapi/element/html/Input.zig
@@ -17,6 +17,8 @@
// along with this program. If not, see .
const std = @import("std");
+const String = @import("../../../../string.zig").String;
+
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
@@ -98,7 +100,7 @@ pub fn getType(self: *const Input) []const u8 {
pub fn setType(self: *Input, typ: []const u8, page: *Page) !void {
// Setting the type property should update the attribute, which will trigger attributeChange
const type_enum = Type.fromString(typ);
- try self.asElement().setAttributeSafe("type", type_enum.toString(), page);
+ try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(type_enum.toString()), page);
}
pub fn getValue(self: *const Input) []const u8 {
@@ -116,7 +118,7 @@ pub fn getDefaultValue(self: *const Input) []const u8 {
}
pub fn setDefaultValue(self: *Input, value: []const u8, page: *Page) !void {
- try self.asElement().setAttributeSafe("value", value, page);
+ try self.asElement().setAttributeSafe(comptime .wrap("value"), .wrap(value), page);
}
pub fn getChecked(self: *const Input) bool {
@@ -147,52 +149,52 @@ pub fn getDefaultChecked(self: *const Input) bool {
pub fn setDefaultChecked(self: *Input, checked: bool, page: *Page) !void {
if (checked) {
- try self.asElement().setAttributeSafe("checked", "", page);
+ try self.asElement().setAttributeSafe(comptime .wrap("checked"), .wrap(""), page);
} else {
- try self.asElement().removeAttribute("checked", page);
+ try self.asElement().removeAttribute(comptime .wrap("checked"), page);
}
}
pub fn getDisabled(self: *const Input) bool {
// TODO: Also check for disabled fieldset ancestors
// (but not if we're inside a