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,208 +36,111 @@ 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- }
8565
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- }
90-
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- };
100- };
101-
102- return init (arena , uri );
103- }
104-
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- ),
66+ break :blk ada .parse (url_str );
11267 };
113- }
11468
115- pub fn get_origin (self : * URL , page : * Page ) ! []const u8 {
116- var aw = std .Io .Writer .Allocating .init (page .arena );
117- try self .uri .writeToStream (& aw .writer , .{
118- .scheme = true ,
119- .authentication = false ,
120- .authority = true ,
121- .path = false ,
122- .query = false ,
123- .fragment = false ,
124- });
125- return aw .written ();
69+ return .{ .url = u };
12670 }
12771
128- // get_href returns the URL by writing all its components.
129- pub fn get_href (self : * URL , page : * Page ) ! []const u8 {
130- return self .toString (page .arena );
72+ pub fn destructor (self : * const URL ) void {
73+ ada .free (self .internal );
13174 }
13275
133- pub fn _toString (self : * URL , page : * Page ) ! []const u8 {
134- return self .toString (page .arena );
76+ // Alias to get_href.
77+ pub fn _toString (self : * const URL ) []const u8 {
78+ return ada .getHref (self .internal );
13579 }
13680
137- // format the url with all its components.
138- pub fn toString (self : * const URL , arena : Allocator ) ! []const u8 {
139- var aw = std .Io .Writer .Allocating .init (arena );
140- try self .uri .writeToStream (& aw .writer , .{
141- .scheme = true ,
142- .authentication = true ,
143- .authority = true ,
144- .path = uriComponentNullStr (self .uri .path ).len > 0 ,
145- });
146-
147- if (self .search_params .get_size () > 0 ) {
148- try aw .writer .writeByte ('?' );
149- try self .search_params .write (& aw .writer );
150- }
81+ // Getters.
15182
152- {
153- const fragment = uriComponentNullStr (self .uri .fragment );
154- if (fragment .len > 0 ) {
155- try aw .writer .writeByte ('#' );
156- try aw .writer .writeAll (fragment );
157- }
158- }
83+ pub fn get_origin (self : * const URL , page : * Page ) ! []const u8 {
84+ const arena = page .arena ;
85+ // `ada.getOrigin` allocates memory in order to find the `origin`.
86+ // We'd like to use our arena allocator for such case;
87+ // so here we allocate the `origin` in page arena and free the original.
88+ const origin = ada .getOrigin (self .internal );
89+ // `OwnedString` itself is not heap allocated so this is safe.
90+ defer ada .freeOwnedString (.{ .data = origin .ptr , .length = origin .len });
15991
160- return aw . written ( );
92+ return arena . dupe ( u8 , origin );
16193 }
16294
163- pub fn get_protocol (self : * URL , page : * Page ) ! []const u8 {
164- return try std . mem . concat ( page . arena , u8 , &[ _ ][] const u8 { self .uri . scheme , ":" } );
95+ pub fn get_href (self : * const URL ) []const u8 {
96+ return ada . getHref ( self .internal );
16597 }
16698
167- pub fn get_username (self : * URL ) []const u8 {
168- return uriComponentNullStr (self .uri . user );
99+ pub fn get_username (self : * const URL ) []const u8 {
100+ return ada . getUsername (self .internal );
169101 }
170102
171- pub fn get_password (self : * URL ) []const u8 {
172- return uriComponentNullStr (self .uri . password );
103+ pub fn get_password (self : * const URL ) []const u8 {
104+ return ada . getPassword (self .internal );
173105 }
174106
175- pub fn get_host (self : * URL , page : * Page ) ! []const u8 {
176- var aw = std .Io .Writer .Allocating .init (page .arena );
177- try self .uri .writeToStream (& aw .writer , .{
178- .scheme = false ,
179- .authentication = false ,
180- .authority = true ,
181- .path = false ,
182- .query = false ,
183- .fragment = false ,
184- });
185- return aw .written ();
107+ pub fn get_port (self : * const URL ) []const u8 {
108+ return ada .getPort (self .internal );
186109 }
187110
188- pub fn get_hostname (self : * URL ) []const u8 {
189- return uriComponentNullStr (self .uri . host );
111+ pub fn get_hash (self : * const URL ) []const u8 {
112+ return ada . getHash (self .internal );
190113 }
191114
192- pub fn get_port (self : * URL , page : * Page ) ! []const u8 {
193- const arena = page .arena ;
194- if (self .uri .port == null ) return try arena .dupe (u8 , "" );
195-
196- var aw = std .Io .Writer .Allocating .init (arena );
197- try aw .writer .printInt (self .uri .port .? , 10 , .lower , .{});
198- return aw .written ();
115+ pub fn get_host (self : * const URL ) []const u8 {
116+ return ada .getHost (self .internal );
199117 }
200118
201- pub fn get_pathname (self : * URL ) []const u8 {
202- if (uriComponentStr (self .uri .path ).len == 0 ) return "/" ;
203- return uriComponentStr (self .uri .path );
119+ pub fn get_hostname (self : * const URL ) []const u8 {
120+ return ada .getHostname (self .internal );
204121 }
205122
206- pub fn get_search (self : * URL , page : * Page ) ! []const u8 {
207- const arena = page .arena ;
208-
209- if (self .search_params .get_size () == 0 ) {
210- return "" ;
211- }
212-
213- var buf : std .ArrayListUnmanaged (u8 ) = .{};
214- try buf .append (arena , '?' );
215- try self .search_params .encode (buf .writer (arena ));
216- return buf .items ;
123+ pub fn get_pathname (self : * const URL ) []const u8 {
124+ return ada .getPathname (self .internal );
217125 }
218126
219- pub fn set_search (self : * URL , qs_ : ? []const u8 , page : * Page ) ! void {
220- self .search_params = .{};
221- if (qs_ ) | qs | {
222- self .search_params = try URLSearchParams .init (page .arena , qs );
223- }
127+ pub fn get_search (self : * const URL ) []const u8 {
128+ return ada .getSearch (self .internal );
224129 }
225130
226- pub fn get_hash (self : * URL , page : * Page ) ! []const u8 {
227- const arena = page .arena ;
228- if (self .uri .fragment == null ) return try arena .dupe (u8 , "" );
229-
230- return try std .mem .concat (arena , u8 , &[_ ][]const u8 { "#" , uriComponentNullStr (self .uri .fragment ) });
131+ pub fn get_protocol (self : * const URL ) []const u8 {
132+ return ada .getProtocol (self .internal );
231133 }
134+ };
232135
233- pub fn get_searchParams (self : * URL ) * URLSearchParams {
234- return & self .search_params ;
235- }
136+ pub const URLSearchParams = struct {
137+ internal : ada.URLSearchParams ,
236138
237- pub fn _toJSON (self : * URL , page : * Page ) ! []const u8 {
238- return self .get_href (page );
239- }
139+ pub const ConstructorOptions = union (enum ) {
140+ string : []const u8 ,
141+ form_data : * const FormData ,
142+ object : js.JsObject ,
143+ };
240144};
241145
242146// uriComponentNullStr converts an optional std.Uri.Component to string value.
@@ -254,112 +158,6 @@ fn uriComponentStr(c: std.Uri.Component) []const u8 {
254158 };
255159}
256160
257- // https://url.spec.whatwg.org/#interface-urlsearchparams
258- pub const URLSearchParams = struct {
259- entries : kv.List = .{},
260-
261- const URLSearchParamsOpts = union (enum ) {
262- qs : []const u8 ,
263- form_data : * const FormData ,
264- js_obj : js.JsObject ,
265- };
266- pub fn constructor (opts_ : ? URLSearchParamsOpts , page : * Page ) ! URLSearchParams {
267- const opts = opts_ orelse return .{ .entries = .{} };
268- return switch (opts ) {
269- .qs = > | qs | init (page .arena , qs ),
270- .form_data = > | fd | .{ .entries = try fd .entries .clone (page .arena ) },
271- .js_obj = > | js_obj | {
272- const arena = page .arena ;
273- var it = js_obj .nameIterator ();
274-
275- var entries : kv.List = .{};
276- try entries .ensureTotalCapacity (arena , it .count );
277-
278- while (try it .next ()) | js_name | {
279- const name = try js_name .toString (arena );
280- const js_val = try js_obj .get (name );
281- entries .appendOwnedAssumeCapacity (
282- name ,
283- try js_val .toString (arena ),
284- );
285- }
286-
287- return .{ .entries = entries };
288- },
289- };
290- }
291-
292- pub fn init (arena : Allocator , qs_ : ? []const u8 ) ! URLSearchParams {
293- return .{
294- .entries = if (qs_ ) | qs | try parseQuery (arena , qs ) else .{},
295- };
296- }
297-
298- pub fn get_size (self : * const URLSearchParams ) u32 {
299- return @intCast (self .entries .count ());
300- }
301-
302- pub fn _append (self : * URLSearchParams , name : []const u8 , value : []const u8 , page : * Page ) ! void {
303- return self .entries .append (page .arena , name , value );
304- }
305-
306- pub fn _set (self : * URLSearchParams , name : []const u8 , value : []const u8 , page : * Page ) ! void {
307- return self .entries .set (page .arena , name , value );
308- }
309-
310- pub fn _delete (self : * URLSearchParams , name : []const u8 , value_ : ? []const u8 ) void {
311- if (value_ ) | value | {
312- return self .entries .deleteKeyValue (name , value );
313- }
314- return self .entries .delete (name );
315- }
316-
317- pub fn _get (self : * const URLSearchParams , name : []const u8 ) ? []const u8 {
318- return self .entries .get (name );
319- }
320-
321- pub fn _getAll (self : * const URLSearchParams , name : []const u8 , page : * Page ) ! []const []const u8 {
322- return self .entries .getAll (page .call_arena , name );
323- }
324-
325- pub fn _has (self : * const URLSearchParams , name : []const u8 ) bool {
326- return self .entries .has (name );
327- }
328-
329- pub fn _keys (self : * const URLSearchParams ) KeyIterable {
330- return .{ .inner = self .entries .keyIterator () };
331- }
332-
333- pub fn _values (self : * const URLSearchParams ) ValueIterable {
334- return .{ .inner = self .entries .valueIterator () };
335- }
336-
337- pub fn _entries (self : * const URLSearchParams ) EntryIterable {
338- return .{ .inner = self .entries .entryIterator () };
339- }
340-
341- pub fn _symbol_iterator (self : * const URLSearchParams ) EntryIterable {
342- return self ._entries ();
343- }
344-
345- pub fn _toString (self : * const URLSearchParams , page : * Page ) ! []const u8 {
346- var arr : std .ArrayListUnmanaged (u8 ) = .empty ;
347- try self .write (arr .writer (page .call_arena ));
348- return arr .items ;
349- }
350-
351- fn write (self : * const URLSearchParams , writer : anytype ) ! void {
352- return kv .urlEncode (self .entries , .query , writer );
353- }
354-
355- // TODO
356- pub fn _sort (_ : * URLSearchParams ) void {}
357-
358- fn encode (self : * const URLSearchParams , writer : anytype ) ! void {
359- return kv .urlEncode (self .entries , .query , writer );
360- }
361- };
362-
363161// Parse the given query.
364162fn parseQuery (arena : Allocator , s : []const u8 ) ! kv.List {
365163 var list = kv.List {};
0 commit comments