Merge pull request #764 from lightpanda-io/url_search_parmas_from_object

URLSearchParam constructor support for object initialization
This commit is contained in:
Karl Seguin
2025-06-06 13:22:17 +08:00
committed by GitHub
3 changed files with 71 additions and 0 deletions

View File

@@ -102,6 +102,13 @@ pub const List = struct {
});
}
pub fn appendOwnedAssumeCapacity(self: *List, key: []const u8, value: []const u8) void {
self.entries.appendAssumeCapacity(.{
.key = key,
.value = value,
});
}
pub fn delete(self: *List, key: []const u8) void {
var i: usize = 0;
while (i < self.entries.items.len) {

View File

@@ -20,6 +20,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const parser = @import("../netsurf.zig");
const Env = @import("../env.zig").Env;
const Page = @import("../page.zig").Page;
const FormData = @import("../xhr/form_data.zig").FormData;
const HTMLElement = @import("../html/elements.zig").HTMLElement;
@@ -247,12 +248,31 @@ pub const URLSearchParams = struct {
const URLSearchParamsOpts = union(enum) {
qs: []const u8,
form_data: *const FormData,
js_obj: Env.JsObject,
};
pub fn constructor(opts_: ?URLSearchParamsOpts, page: *Page) !URLSearchParams {
const opts = opts_ orelse return .{ .entries = .{} };
return switch (opts) {
.qs => |qs| init(page.arena, qs),
.form_data => |fd| .{ .entries = try fd.entries.clone(page.arena) },
.js_obj => |js_obj| {
const arena = page.arena;
var it = js_obj.nameIterator();
var entries: kv.List = .{};
try entries.ensureTotalCapacity(arena, it.count);
while (try it.next()) |js_name| {
const name = try js_name.toString(arena);
const js_val = try js_obj.get(name);
entries.appendOwnedAssumeCapacity(
name,
try js_val.toString(arena),
);
}
return .{ .entries = entries };
},
};
}
@@ -613,5 +633,9 @@ test "Browser.URLSearchParams" {
.{ "ups.getAll('b')", "3" },
.{ "fd.delete('a')", null }, // the two aren't linked, it created a copy
.{ "ups.size", "3" },
.{ "ups = new URLSearchParams({over: 9000, spice: 'flow'})", null },
.{ "ups.size", "2" },
.{ "ups.getAll('over')", "9000" },
.{ "ups.getAll('spice')", "flow" },
}, .{});
}

View File

@@ -1366,6 +1366,13 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
}
}
pub fn get(self: JsObject, key: []const u8) !Value {
const scope = self.scope;
const js_key = v8.String.initUtf8(scope.isolate, key);
const js_val = try self.js_obj.getValue(scope.context, js_key);
return scope.createValue(js_val);
}
pub fn isTruthy(self: JsObject) bool {
const js_value = self.js_obj.toValue();
return js_value.toBool(self.scope.isolate);
@@ -1421,6 +1428,20 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
pub fn isNullOrUndefined(self: JsObject) bool {
return self.js_obj.toValue().isNullOrUndefined();
}
pub fn nameIterator(self: JsObject) ValueIterator {
const scope = self.scope;
const js_obj = self.js_obj;
const array = js_obj.getPropertyNames(scope.context);
const count = array.length();
return .{
.count = count,
.scope = scope,
.js_obj = array.castTo(v8.Object),
};
}
};
// This only exists so that we know whether a function wants the opaque
@@ -1626,6 +1647,25 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
}
};
pub const ValueIterator = struct {
count: u32,
idx: u32 = 0,
js_obj: v8.Object,
scope: *const Scope,
pub fn next(self: *ValueIterator) !?Value {
const idx = self.idx;
if (idx == self.count) {
return null;
}
self.idx += 1;
const scope = self.scope;
const js_val = try self.js_obj.getAtIndex(scope.context, idx);
return scope.createValue(js_val);
}
};
fn compileModule(isolate: v8.Isolate, src: []const u8, name: []const u8) !v8.Module {
// compile
const script_name = v8.String.initUtf8(isolate, name);