1818
1919const std = @import ("std" );
2020const Allocator = std .mem .Allocator ;
21+ const ada = @import ("ada" );
2122
2223const js = @import ("../js/js.zig" );
2324const parser = @import ("../netsurf.zig" );
@@ -35,215 +36,113 @@ pub const Interfaces = .{
3536 EntryIterable ,
3637};
3738
38- // https://url.spec.whatwg.org/#url
39- //
40- // TODO we could avoid many of these getter string allocatoration in two differents
41- // way:
42- //
43- // 1. We can eventually get the slice of scheme *with* the following char in
44- // the underlying string. But I don't know if it's possible and how to do that.
45- // I mean, if the rawuri contains `https://foo.bar`, uri.scheme is a slice
46- // containing only `https`. I want `https:` so, in theory, I don't need to
47- // allocatorate data, I should be able to retrieve the scheme + the following `:`
48- // from rawuri.
49- //
50- // 2. The other way would be to copy the `std.Uri` code to have a dedicated
51- // parser including the characters we want for the web API.
39+ /// https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
5240pub const URL = struct {
53- uri : std.Uri ,
54- search_params : URLSearchParams ,
41+ internal : ada.URL ,
5542
56- pub const empty = URL {
57- .uri = .{ .scheme = "" },
58- .search_params = .{},
59- };
60-
61- const URLArg = union (enum ) {
43+ // You can use an existing URL object for either argument, and it will be
44+ // stringified from the object's href property.
45+ pub const ConstructorArg = union (enum ) {
6246 url : * URL ,
63- element : * parser.ElementHTML ,
47+ element : * parser.Element ,
6448 string : []const u8 ,
6549
66- fn toString (self : URLArg , arena : Allocator ) ! ? []const u8 {
67- switch (self ) {
68- .string = > | s | return s ,
69- .url = > | url | return try url .toString ( arena ),
70- .element = > | e | return try parser .elementGetAttribute (@ptrCast (e ), "href" ),
71- }
50+ fn toString (self : * const ConstructorArg ) error { Invalid } ! []const u8 {
51+ return switch (self ) {
52+ .string = > | s | s ,
53+ .url = > | url | url ._toString ( ),
54+ .element = > | e | parser .elementGetAttribute (@ptrCast (e ), "href" ) orelse error . Invalid ,
55+ };
7256 }
7357 };
7458
75- pub fn constructor (url : URLArg , base : ? URLArg , page : * Page ) ! URL {
76- const arena = page .arena ;
77- const url_str = try url .toString (arena ) orelse return error .InvalidArgument ;
78-
79- var raw : ? []const u8 = null ;
80- if (base ) | b | {
81- if (try b .toString (arena )) | bb | {
82- raw = try @import ("../../url.zig" ).URL .stitch (arena , url_str , bb , .{});
59+ pub fn constructor (url : ConstructorArg , maybe_base : ? ConstructorArg , _ : * Page ) ! URL {
60+ const u = blk : {
61+ const url_str = try url .toString ();
62+ if (maybe_base ) | base | {
63+ break :blk ada .parseWithBase (url_str , try base .toString ());
8364 }
84- }
85-
86- if (raw == null ) {
87- // if it was a URL, then it's already be owned by the arena
88- raw = if (url == .url ) url_str else try arena .dupe (u8 , url_str );
89- }
9065
91- const uri = std .Uri .parse (raw .? ) catch blk : {
92- if (! std .mem .endsWith (u8 , raw .? , "://" )) {
93- return error .TypeError ;
94- }
95- // schema only is valid!
96- break :blk std.Uri {
97- .scheme = raw .? [0 .. raw .? .len - 3 ],
98- .host = .{ .percent_encoded = "" },
99- };
66+ break :blk ada .parse (url_str );
10067 };
10168
102- return init ( arena , uri ) ;
69+ return .{ . url = u } ;
10370 }
10471
105- pub fn init (arena : Allocator , uri : std.Uri ) ! URL {
106- return .{
107- .uri = uri ,
108- .search_params = try URLSearchParams .init (
109- arena ,
110- uriComponentNullStr (uri .query ),
111- ),
112- };
72+ pub fn destructor (self : * const URL ) void {
73+ ada .free (self .internal );
11374 }
11475
11576 pub fn initWithoutSearchParams (uri : std.Uri ) URL {
11677 return .{ .uri = uri , .search_params = .{} };
11778 }
118-
119- pub fn get_origin (self : * URL , page : * Page ) ! []const u8 {
120- var aw = std .Io .Writer .Allocating .init (page .arena );
121- try self .uri .writeToStream (& aw .writer , .{
122- .scheme = true ,
123- .authentication = false ,
124- .authority = true ,
125- .path = false ,
126- .query = false ,
127- .fragment = false ,
128- });
129- return aw .written ();
79+ pub fn _toString (self : * const URL ) []const u8 {
80+ return ada .getHref (self .internal );
13081 }
13182
132- // get_href returns the URL by writing all its components.
133- pub fn get_href (self : * URL , page : * Page ) ! []const u8 {
134- return self .toString (page .arena );
135- }
83+ // Getters.
13684
137- pub fn _toString (self : * URL , page : * Page ) ! []const u8 {
138- return self .toString (page .arena );
139- }
140-
141- // format the url with all its components.
142- pub fn toString (self : * const URL , arena : Allocator ) ! []const u8 {
143- var aw = std .Io .Writer .Allocating .init (arena );
144- try self .uri .writeToStream (& aw .writer , .{
145- .scheme = true ,
146- .authentication = true ,
147- .authority = true ,
148- .path = uriComponentNullStr (self .uri .path ).len > 0 ,
149- });
150-
151- if (self .search_params .get_size () > 0 ) {
152- try aw .writer .writeByte ('?' );
153- try self .search_params .write (& aw .writer );
154- }
155-
156- {
157- const fragment = uriComponentNullStr (self .uri .fragment );
158- if (fragment .len > 0 ) {
159- try aw .writer .writeByte ('#' );
160- try aw .writer .writeAll (fragment );
161- }
162- }
85+ pub fn get_origin (self : * const URL , page : * Page ) ! []const u8 {
86+ const arena = page .arena ;
87+ // `ada.getOrigin` allocates memory in order to find the `origin`.
88+ // We'd like to use our arena allocator for such case;
89+ // so here we allocate the `origin` in page arena and free the original.
90+ const origin = ada .getOrigin (self .internal );
91+ // `OwnedString` itself is not heap allocated so this is safe.
92+ defer ada .freeOwnedString (.{ .data = origin .ptr , .length = origin .len });
16393
164- return aw . written ( );
94+ return arena . dupe ( u8 , origin );
16595 }
16696
167- pub fn get_protocol (self : * const URL ) []const u8 {
168- // std.Uri keeps a pointer to "https", "http" (scheme part) so we know
169- // its followed by ':'.
170- const scheme = self .uri .scheme ;
171- return scheme .ptr [0 .. scheme .len + 1 ];
97+ pub fn get_href (self : * const URL ) []const u8 {
98+ return ada .getHref (self .internal );
17299 }
173100
174- pub fn get_username (self : * URL ) []const u8 {
175- return uriComponentNullStr (self .uri . user );
101+ pub fn get_username (self : * const URL ) []const u8 {
102+ return ada . getUsername (self .internal );
176103 }
177104
178- pub fn get_password (self : * URL ) []const u8 {
179- return uriComponentNullStr (self .uri . password );
105+ pub fn get_password (self : * const URL ) []const u8 {
106+ return ada . getPassword (self .internal );
180107 }
181108
182- pub fn get_host (self : * URL , page : * Page ) ! []const u8 {
183- var aw = std .Io .Writer .Allocating .init (page .arena );
184- try self .uri .writeToStream (& aw .writer , .{
185- .scheme = false ,
186- .authentication = false ,
187- .authority = true ,
188- .path = false ,
189- .query = false ,
190- .fragment = false ,
191- });
192- return aw .written ();
109+ pub fn get_port (self : * const URL ) []const u8 {
110+ return ada .getPort (self .internal );
193111 }
194112
195- pub fn get_hostname (self : * URL ) []const u8 {
196- return uriComponentNullStr (self .uri . host );
113+ pub fn get_hash (self : * const URL ) []const u8 {
114+ return ada . getHash (self .internal );
197115 }
198116
199- pub fn get_port (self : * URL , page : * Page ) ! []const u8 {
200- const arena = page .arena ;
201- if (self .uri .port == null ) return try arena .dupe (u8 , "" );
202-
203- var aw = std .Io .Writer .Allocating .init (arena );
204- try aw .writer .printInt (self .uri .port .? , 10 , .lower , .{});
205- return aw .written ();
117+ pub fn get_host (self : * const URL ) []const u8 {
118+ return ada .getHost (self .internal );
206119 }
207120
208- pub fn get_pathname (self : * URL ) []const u8 {
209- if (uriComponentStr (self .uri .path ).len == 0 ) return "/" ;
210- return uriComponentStr (self .uri .path );
121+ pub fn get_hostname (self : * const URL ) []const u8 {
122+ return ada .getHostname (self .internal );
211123 }
212124
213- pub fn get_search (self : * URL , page : * Page ) ! []const u8 {
214- const arena = page .arena ;
215-
216- if (self .search_params .get_size () == 0 ) {
217- return "" ;
218- }
219-
220- var buf : std .ArrayListUnmanaged (u8 ) = .{};
221- try buf .append (arena , '?' );
222- try self .search_params .encode (buf .writer (arena ));
223- return buf .items ;
125+ pub fn get_pathname (self : * const URL ) []const u8 {
126+ return ada .getPathname (self .internal );
224127 }
225128
226- pub fn set_search (self : * URL , qs_ : ? []const u8 , page : * Page ) ! void {
227- self .search_params = .{};
228- if (qs_ ) | qs | {
229- self .search_params = try URLSearchParams .init (page .arena , qs );
230- }
129+ pub fn get_search (self : * const URL ) []const u8 {
130+ return ada .getSearch (self .internal );
231131 }
232132
233- pub fn get_hash (self : * URL , page : * Page ) ! []const u8 {
234- const arena = page .arena ;
235- if (self .uri .fragment == null ) return try arena .dupe (u8 , "" );
236-
237- return try std .mem .concat (arena , u8 , &[_ ][]const u8 { "#" , uriComponentNullStr (self .uri .fragment ) });
133+ pub fn get_protocol (self : * const URL ) []const u8 {
134+ return ada .getProtocol (self .internal );
238135 }
136+ };
239137
240- pub fn get_searchParams (self : * URL ) * URLSearchParams {
241- return & self .search_params ;
242- }
138+ pub const URLSearchParams = struct {
139+ internal : ada.URLSearchParams ,
243140
244- pub fn _toJSON (self : * URL , page : * Page ) ! []const u8 {
245- return self .get_href (page );
246- }
141+ pub const ConstructorOptions = union (enum ) {
142+ string : []const u8 ,
143+ form_data : * const FormData ,
144+ object : js.JsObject ,
145+ };
247146};
248147
249148// uriComponentNullStr converts an optional std.Uri.Component to string value.
@@ -261,112 +160,6 @@ fn uriComponentStr(c: std.Uri.Component) []const u8 {
261160 };
262161}
263162
264- // https://url.spec.whatwg.org/#interface-urlsearchparams
265- pub const URLSearchParams = struct {
266- entries : kv.List = .{},
267-
268- const URLSearchParamsOpts = union (enum ) {
269- qs : []const u8 ,
270- form_data : * const FormData ,
271- js_obj : js.Object ,
272- };
273- pub fn constructor (opts_ : ? URLSearchParamsOpts , page : * Page ) ! URLSearchParams {
274- const opts = opts_ orelse return .{ .entries = .{} };
275- return switch (opts ) {
276- .qs = > | qs | init (page .arena , qs ),
277- .form_data = > | fd | .{ .entries = try fd .entries .clone (page .arena ) },
278- .js_obj = > | js_obj | {
279- const arena = page .arena ;
280- var it = js_obj .nameIterator ();
281-
282- var entries : kv.List = .{};
283- try entries .ensureTotalCapacity (arena , it .count );
284-
285- while (try it .next ()) | js_name | {
286- const name = try js_name .toString (arena );
287- const js_val = try js_obj .get (name );
288- entries .appendOwnedAssumeCapacity (
289- name ,
290- try js_val .toString (arena ),
291- );
292- }
293-
294- return .{ .entries = entries };
295- },
296- };
297- }
298-
299- pub fn init (arena : Allocator , qs_ : ? []const u8 ) ! URLSearchParams {
300- return .{
301- .entries = if (qs_ ) | qs | try parseQuery (arena , qs ) else .{},
302- };
303- }
304-
305- pub fn get_size (self : * const URLSearchParams ) u32 {
306- return @intCast (self .entries .count ());
307- }
308-
309- pub fn _append (self : * URLSearchParams , name : []const u8 , value : []const u8 , page : * Page ) ! void {
310- return self .entries .append (page .arena , name , value );
311- }
312-
313- pub fn _set (self : * URLSearchParams , name : []const u8 , value : []const u8 , page : * Page ) ! void {
314- return self .entries .set (page .arena , name , value );
315- }
316-
317- pub fn _delete (self : * URLSearchParams , name : []const u8 , value_ : ? []const u8 ) void {
318- if (value_ ) | value | {
319- return self .entries .deleteKeyValue (name , value );
320- }
321- return self .entries .delete (name );
322- }
323-
324- pub fn _get (self : * const URLSearchParams , name : []const u8 ) ? []const u8 {
325- return self .entries .get (name );
326- }
327-
328- pub fn _getAll (self : * const URLSearchParams , name : []const u8 , page : * Page ) ! []const []const u8 {
329- return self .entries .getAll (page .call_arena , name );
330- }
331-
332- pub fn _has (self : * const URLSearchParams , name : []const u8 ) bool {
333- return self .entries .has (name );
334- }
335-
336- pub fn _keys (self : * const URLSearchParams ) KeyIterable {
337- return .{ .inner = self .entries .keyIterator () };
338- }
339-
340- pub fn _values (self : * const URLSearchParams ) ValueIterable {
341- return .{ .inner = self .entries .valueIterator () };
342- }
343-
344- pub fn _entries (self : * const URLSearchParams ) EntryIterable {
345- return .{ .inner = self .entries .entryIterator () };
346- }
347-
348- pub fn _symbol_iterator (self : * const URLSearchParams ) EntryIterable {
349- return self ._entries ();
350- }
351-
352- pub fn _toString (self : * const URLSearchParams , page : * Page ) ! []const u8 {
353- var arr : std .ArrayListUnmanaged (u8 ) = .empty ;
354- try self .write (arr .writer (page .call_arena ));
355- return arr .items ;
356- }
357-
358- fn write (self : * const URLSearchParams , writer : anytype ) ! void {
359- return kv .urlEncode (self .entries , .query , writer );
360- }
361-
362- // TODO
363- pub fn _sort (_ : * URLSearchParams ) void {}
364-
365- fn encode (self : * const URLSearchParams , writer : anytype ) ! void {
366- return kv .urlEncode (self .entries , .query , writer );
367- }
368- };
369-
370163// Parse the given query.
371164fn parseQuery (arena : Allocator , s : []const u8 ) ! kv.List {
372165 var list = kv.List {};
0 commit comments