mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-08-01 02:06:17 -04:00
spread new implementation (phase 1)
Will have to come up with a solution for search getter/setter and URLSearchParams still; We've also encountered with a bug in URLSearchParams, a fix for it will also be included in the second phase.
This commit is contained in:
@@ -116,7 +116,7 @@ pub fn reload(_: *const Location, frame: *Frame) !void {
|
||||
}
|
||||
|
||||
pub fn toString(self: *const Location, exec: *const js.Execution) ![:0]const u8 {
|
||||
return self._url.toString(exec);
|
||||
return self._url.toString1(exec);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
|
||||
@@ -20,6 +20,7 @@ const std = @import("std");
|
||||
const js = @import("../js/js.zig");
|
||||
|
||||
const U = @import("../URL.zig");
|
||||
const U_ = @import("../../sys/url.zig");
|
||||
const URLSearchParams = @import("net/URLSearchParams.zig");
|
||||
const Blob = @import("Blob.zig");
|
||||
const Execution = js.Execution;
|
||||
@@ -28,7 +29,10 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
const URL = @This();
|
||||
|
||||
_raw: [:0]const u8,
|
||||
_raw: [:0]const u8 = undefined,
|
||||
_url: *U_.Url = undefined,
|
||||
/// Largest port possible is 65535; which require 5 bytes.
|
||||
_port: [5]u8 = undefined,
|
||||
_arena: ?Allocator = null,
|
||||
_search_params: ?*URLSearchParams = null,
|
||||
|
||||
@@ -36,83 +40,143 @@ _search_params: ?*URLSearchParams = null,
|
||||
pub const resolve = @import("../URL.zig").resolve;
|
||||
pub const eqlDocument = @import("../URL.zig").eqlDocument;
|
||||
|
||||
pub fn init(url: [:0]const u8, base_: ?[:0]const u8, exec: *const Execution) !*URL {
|
||||
const arena = exec.arena;
|
||||
const context_url = exec.url.*;
|
||||
pub fn init(url: []const u8, maybe_base: ?[]const u8, exec: *const Execution) !*URL {
|
||||
// NOTE: about:blank address is valid in rust-url.
|
||||
|
||||
if (std.mem.eql(u8, url, "about:blank")) {
|
||||
return exec._factory.create(URL{
|
||||
._raw = "about:blank",
|
||||
._arena = arena,
|
||||
});
|
||||
var err: i32 = 0;
|
||||
if (maybe_base) |base| {
|
||||
const base_url = U_.url_parse(base.ptr, base.len, &err) orelse return error.TypeError;
|
||||
errdefer U_.url_free(base_url);
|
||||
|
||||
const joined_url = U_.url_join(base_url, url.ptr, url.len, &err) orelse return error.TypeError;
|
||||
errdefer U_.url_free(joined_url);
|
||||
// `base_url` has no use now.
|
||||
U_.url_free(base_url);
|
||||
|
||||
return exec._factory.create(URL{ ._url = joined_url });
|
||||
}
|
||||
const url_is_absolute = @import("../URL.zig").isCompleteHTTPUrl(url);
|
||||
|
||||
const base = if (base_) |b| blk: {
|
||||
// If URL is absolute, base is ignored (but we still use context url internally)
|
||||
if (url_is_absolute) {
|
||||
break :blk context_url;
|
||||
}
|
||||
// For relative URLs, base must be a valid absolute URL
|
||||
if (!@import("../URL.zig").isCompleteHTTPUrl(b)) {
|
||||
return error.TypeError;
|
||||
}
|
||||
break :blk b;
|
||||
} else if (!url_is_absolute) {
|
||||
const u = U_.url_parse(url.ptr, url.len, &err) orelse {
|
||||
return error.TypeError;
|
||||
} else context_url;
|
||||
};
|
||||
errdefer U_.url_free(u);
|
||||
|
||||
const raw = try resolve(arena, base, url, .{ .always_dupe = true });
|
||||
|
||||
return exec._factory.create(URL{
|
||||
._raw = raw,
|
||||
._arena = arena,
|
||||
});
|
||||
return exec._factory.create(URL{ ._url = u });
|
||||
}
|
||||
|
||||
pub fn getUsername(self: *const URL) []const u8 {
|
||||
return U.getUsername(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
U_.url_get_username(self._url, &out, &len);
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setUsername(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setUsername(self._raw, value, allocator);
|
||||
const res = U_.url_set_username(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetUsername;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPassword(self: *const URL) []const u8 {
|
||||
return U.getPassword(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
const res = U_.url_get_password(self._url, &out, &len);
|
||||
if (res != 0) {
|
||||
return "";
|
||||
}
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setPassword(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setPassword(self._raw, value, allocator);
|
||||
const res = U_.url_set_password(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetPassword;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPathname(self: *const URL) []const u8 {
|
||||
return U.getPathname(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
U_.url_get_path(self._url, &out, &len);
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setPathname(self: *URL, value: []const u8) !void {
|
||||
const res = U_.url_set_path(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetPathname;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getProtocol(self: *const URL) []const u8 {
|
||||
return U.getProtocol(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
U_.url_get_scheme(self._url, &out, &len);
|
||||
// rust-url's scheme() omits the ':'. The serialization always has it right
|
||||
// after the scheme ("https://..."), so we extend the borrowed slice by one.
|
||||
return out[0 .. len + 1];
|
||||
}
|
||||
|
||||
pub fn setProtocol(self: *URL, value: []const u8) !void {
|
||||
const res = U_.url_set_scheme(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getHostname(self: *const URL) []const u8 {
|
||||
return U.getHostname(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
if (U_.url_get_hostname(self._url, &out, &len) != 0) {
|
||||
return "";
|
||||
}
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn setHostname(self: *URL, value: []const u8) !void {
|
||||
const res = U_.url_set_hostname(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetHostname;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getHost(self: *const URL) []const u8 {
|
||||
return U.getHost(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
if (U_.url_get_host(self._url, &out, &len) != 0) {
|
||||
return "";
|
||||
}
|
||||
return out[0..len];
|
||||
}
|
||||
|
||||
pub fn getPort(self: *const URL) []const u8 {
|
||||
return U.getPort(self._raw);
|
||||
pub fn setHost(self: *URL, value: []const u8) !void {
|
||||
const res = U_.url_set_host(self._url, value.ptr, value.len);
|
||||
if (res != 0) {
|
||||
return error.SetHost;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getOrigin(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
return (try U.getOrigin(exec.call_arena, self._raw)) orelse {
|
||||
// yes, a null string, that's what the spec wants
|
||||
return "null";
|
||||
pub fn getPort(self: *URL) []const u8 {
|
||||
const port = U_.urlGetPort(self._url) orelse return "";
|
||||
return std.fmt.bufPrint(&self._port, "{d}", .{port}) catch unreachable;
|
||||
}
|
||||
|
||||
/// Spec requires us to silently ignore errors of this setter.
|
||||
pub fn setPort(self: *URL, maybe_value: ?[]const u8) void {
|
||||
// A null or empty value clears the port.
|
||||
const value = maybe_value orelse {
|
||||
_ = U_.url_set_port_to_null(self._url);
|
||||
return;
|
||||
};
|
||||
if (value.len == 0) {
|
||||
_ = U_.url_set_port_to_null(self._url);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore invalid port numbers, leaving the port unchanged.
|
||||
const port = std.fmt.parseInt(u16, value, 10) catch return;
|
||||
_ = U_.url_set_port(self._url, port);
|
||||
}
|
||||
|
||||
pub fn getSearch(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
@@ -129,8 +193,43 @@ pub fn getSearch(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
return U.getSearch(self._raw);
|
||||
}
|
||||
|
||||
pub fn setSearch(self: *URL, value: []const u8, exec: *const Execution) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setSearch(self._raw, value, allocator);
|
||||
|
||||
// Update existing searchParams if it exists
|
||||
if (self._search_params) |sp| {
|
||||
const search = U.getSearch(self._raw);
|
||||
const search_value = if (search.len > 0) search[1..] else "";
|
||||
try sp.updateFromString(search_value, exec);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getHash(self: *const URL) []const u8 {
|
||||
return U.getHash(self._raw);
|
||||
var out: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
const res = U_.url_get_fragment(self._url, &out, &len);
|
||||
// WHATWG `hash` is "" for both a null and an empty fragment.
|
||||
if (res != 0 or len == 0) {
|
||||
return "";
|
||||
}
|
||||
// rust-url's fragment() omits the '#', which always precedes the fragment
|
||||
// in the serialization, so step the borrowed slice back one byte for it.
|
||||
return (out - 1)[0 .. len + 1];
|
||||
}
|
||||
|
||||
pub fn setHash(self: *URL, value: []const u8) !void {
|
||||
// An empty value clears the fragment entirely (removes the '#').
|
||||
if (value.len == 0) {
|
||||
U_.url_set_fragment_to_null(self._url);
|
||||
return;
|
||||
}
|
||||
// Strip a single leading '#', then set the fragment.
|
||||
const fragment = if (value[0] == '#') value[1..] else value;
|
||||
const res = U_.url_set_fragment(self._url, fragment.ptr, fragment.len);
|
||||
if (res != 0) {
|
||||
return error.SetHash;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getSearchParams(self: *URL, exec: *const Execution) !*URLSearchParams {
|
||||
@@ -147,62 +246,39 @@ pub fn getSearchParams(self: *URL, exec: *const Execution) !*URLSearchParams {
|
||||
return params;
|
||||
}
|
||||
|
||||
pub fn getOrigin(self: *const URL, exec: *const Execution) ![]const u8 {
|
||||
const origin = U_.url_get_origin(self._url);
|
||||
defer origin.deinit();
|
||||
|
||||
return exec.call_arena.dupe(u8, origin.slice());
|
||||
}
|
||||
|
||||
pub fn setHref(self: *URL, value: []const u8, exec: *const Execution) !void {
|
||||
const base = if (U.isCompleteHTTPUrl(value)) exec.url.* else self._raw;
|
||||
const raw = try U.resolve(self._arena orelse exec.arena, base, value, .{ .always_dupe = true });
|
||||
self._raw = raw;
|
||||
// This behaves the same as initializing a URL.
|
||||
var err: i32 = 0;
|
||||
const url = U_.url_parse(value.ptr, value.len, &err) orelse return error.TypeError;
|
||||
errdefer U_.url_free(url);
|
||||
|
||||
// Update existing searchParams if it exists
|
||||
// Free the current URL.
|
||||
U_.url_free(self._url);
|
||||
self._url = url;
|
||||
|
||||
// Update existing searchParams if it exists.
|
||||
if (self._search_params) |sp| {
|
||||
const search = U.getSearch(raw);
|
||||
const search = U.getSearch(self.toString());
|
||||
const search_value = if (search.len > 0) search[1..] else "";
|
||||
try sp.updateFromString(search_value, exec);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setProtocol(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setProtocol(self._raw, value, allocator);
|
||||
pub fn toString(self: *const URL) []const u8 {
|
||||
var ptr: [*]const u8 = undefined;
|
||||
var len: usize = 0;
|
||||
U_.url_to_string(self._url, &ptr, &len);
|
||||
return ptr[0..len];
|
||||
}
|
||||
|
||||
pub fn setHost(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setHost(self._raw, value, allocator);
|
||||
}
|
||||
|
||||
pub fn setHostname(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setHostname(self._raw, value, allocator);
|
||||
}
|
||||
|
||||
pub fn setPort(self: *URL, value: ?[]const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setPort(self._raw, value, allocator);
|
||||
}
|
||||
|
||||
pub fn setPathname(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setPathname(self._raw, value, allocator);
|
||||
}
|
||||
|
||||
pub fn setSearch(self: *URL, value: []const u8, exec: *const Execution) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setSearch(self._raw, value, allocator);
|
||||
|
||||
// Update existing searchParams if it exists
|
||||
if (self._search_params) |sp| {
|
||||
const search = U.getSearch(self._raw);
|
||||
const search_value = if (search.len > 0) search[1..] else "";
|
||||
try sp.updateFromString(search_value, exec);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setHash(self: *URL, value: []const u8) !void {
|
||||
const allocator = self._arena orelse return error.NoAllocator;
|
||||
self._raw = try U.setHash(self._raw, value, allocator);
|
||||
}
|
||||
|
||||
pub fn toString(self: *const URL, exec: *const Execution) ![:0]const u8 {
|
||||
pub fn toString1(self: *const URL, exec: *const Execution) ![:0]const u8 {
|
||||
const sp = self._search_params orelse {
|
||||
return self._raw;
|
||||
};
|
||||
@@ -240,11 +316,11 @@ pub fn toString(self: *const URL, exec: *const Execution) ![:0]const u8 {
|
||||
return buf.written()[0 .. buf.written().len - 1 :0];
|
||||
}
|
||||
|
||||
pub fn canParse(url: []const u8, base_: ?[]const u8) bool {
|
||||
if (base_) |b| {
|
||||
return U.isCompleteHTTPUrl(b);
|
||||
pub fn canParse(url: []const u8, maybe_base: ?[]const u8) bool {
|
||||
if (maybe_base) |base| {
|
||||
return U_.url_can_parse_with_base(base.ptr, base.len, url.ptr, url.len);
|
||||
}
|
||||
return U.isCompleteHTTPUrl(url);
|
||||
return U_.url_can_parse(url.ptr, url.len);
|
||||
}
|
||||
|
||||
pub fn createObjectURL(blob: *Blob, exec: *const Execution) ![]const u8 {
|
||||
|
||||
Reference in New Issue
Block a user