mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 23:23:28 +00:00
Change dataset to work directly off DOM element
This commit is contained in:
@@ -16,64 +16,66 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
const std = @import("std");
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Page = @import("../page.zig").Page;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const DataSet = @This();
|
||||
|
||||
attributes: std.StringHashMapUnmanaged([]const u8),
|
||||
|
||||
pub const empty: DataSet = .{
|
||||
.attributes = .empty,
|
||||
};
|
||||
element: *parser.Element,
|
||||
|
||||
const GetResult = union(enum) {
|
||||
value: []const u8,
|
||||
undefined: void,
|
||||
};
|
||||
pub fn named_get(self: *const DataSet, name: []const u8, _: *bool) GetResult {
|
||||
if (self.attributes.get(name)) |value| {
|
||||
pub fn named_get(self: *const DataSet, name: []const u8, _: *bool, page: *Page) !GetResult {
|
||||
const normalized_name = try normalize(page.call_arena, name);
|
||||
if (try parser.elementGetAttribute(self.element, normalized_name)) |value| {
|
||||
return .{ .value = value };
|
||||
}
|
||||
return .{ .undefined = {} };
|
||||
}
|
||||
|
||||
pub fn named_set(self: *DataSet, name: []const u8, value: []const u8, _: *bool, page: *Page) !void {
|
||||
const arena = page.arena;
|
||||
const gop = try self.attributes.getOrPut(arena, name);
|
||||
errdefer _ = self.attributes.remove(name);
|
||||
|
||||
if (!gop.found_existing) {
|
||||
gop.key_ptr.* = try arena.dupe(u8, name);
|
||||
}
|
||||
gop.value_ptr.* = try arena.dupe(u8, value);
|
||||
const normalized_name = try normalize(page.call_arena, name);
|
||||
try parser.elementSetAttribute(self.element, normalized_name, value);
|
||||
}
|
||||
|
||||
pub fn named_delete(self: *DataSet, name: []const u8, _: *bool) void {
|
||||
_ = self.attributes.remove(name);
|
||||
pub fn named_delete(self: *DataSet, name: []const u8, _: *bool, page: *Page) !void {
|
||||
const normalized_name = try normalize(page.call_arena, name);
|
||||
try parser.elementRemoveAttribute(self.element, normalized_name);
|
||||
}
|
||||
|
||||
pub fn normalizeName(allocator: Allocator, name: []const u8) ![]const u8 {
|
||||
std.debug.assert(std.mem.startsWith(u8, name, "data-"));
|
||||
var owned = try allocator.alloc(u8, name.len - 5);
|
||||
fn normalize(allocator: Allocator, name: []const u8) ![]const u8 {
|
||||
var upper_count: usize = 0;
|
||||
for (name) |c| {
|
||||
if (std.ascii.isUpper(c)) {
|
||||
upper_count += 1;
|
||||
}
|
||||
}
|
||||
// for every upper-case letter, we'll probably need a dash before it
|
||||
// and we need the 'data-' prefix
|
||||
var normalized = try allocator.alloc(u8, name.len + upper_count + 5);
|
||||
|
||||
var pos: usize = 0;
|
||||
var capitalize = false;
|
||||
for (name[5..]) |c| {
|
||||
if (c == '-') {
|
||||
capitalize = true;
|
||||
continue;
|
||||
@memcpy(normalized[0..5], "data-");
|
||||
if (upper_count == 0) {
|
||||
@memcpy(normalized[5..], name);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
if (capitalize) {
|
||||
capitalize = false;
|
||||
owned[pos] = std.ascii.toUpper(c);
|
||||
var pos: usize = 5;
|
||||
for (name) |c| {
|
||||
if (std.ascii.isUpper(c)) {
|
||||
normalized[pos] = '-';
|
||||
pos += 1;
|
||||
normalized[pos] = c + 32;
|
||||
} else {
|
||||
owned[pos] = c;
|
||||
normalized[pos] = c;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return owned[0..pos];
|
||||
return normalized;
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
@@ -88,5 +90,11 @@ test "Browser.HTML.DataSet" {
|
||||
.{ "delete el1.dataset.x", "true" },
|
||||
.{ "el1.dataset.x", "undefined" },
|
||||
.{ "delete el1.dataset.other", "true" }, // yes, this is right
|
||||
|
||||
.{ "let ds1 = el1.dataset", null },
|
||||
.{ "ds1.helloWorld = 'yes'", null },
|
||||
.{ "el1.getAttribute('data-hello-world')", "yes" },
|
||||
.{ "el1.setAttribute('data-this-will-work', 'positive')", null },
|
||||
.{ "ds1.thisWillWork", "positive" },
|
||||
}, .{});
|
||||
}
|
||||
|
||||
@@ -128,28 +128,7 @@ pub const HTMLElement = struct {
|
||||
if (state.dataset) |*ds| {
|
||||
return ds;
|
||||
}
|
||||
|
||||
// The first time this is called, load the data attributes from the DOM
|
||||
var ds: DataSet = .empty;
|
||||
|
||||
if (try parser.nodeGetAttributes(@ptrCast(e))) |map| {
|
||||
const arena = page.arena;
|
||||
const count = try parser.namedNodeMapGetLength(map);
|
||||
for (0..count) |i| {
|
||||
const attr = try parser.namedNodeMapItem(map, @intCast(i)) orelse continue;
|
||||
const name = try parser.attributeGetName(attr);
|
||||
if (!std.mem.startsWith(u8, name, "data-")) {
|
||||
continue;
|
||||
}
|
||||
const normalized_name = try DataSet.normalizeName(arena, name);
|
||||
const value = try parser.attributeGetValue(attr) orelse "";
|
||||
// I don't think we need to dupe value, It'll live in libdom for
|
||||
// as long as the page due to the fact that we're using an arena.
|
||||
try ds.attributes.put(arena, normalized_name, value);
|
||||
}
|
||||
}
|
||||
|
||||
state.dataset = ds;
|
||||
state.dataset = DataSet{ .element = @ptrCast(e) };
|
||||
return &state.dataset.?;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user