Skip to content

Commit 146b56c

Browse files
committed
refactor HTMLAnchorElement
Prefer new URL implementation with separate store for object data.
1 parent 8e7d822 commit 146b56c

File tree

4 files changed

+255
-111
lines changed

4 files changed

+255
-111
lines changed

src/browser/html/elements.zig

Lines changed: 149 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,29 @@ pub const HTMLAnchorElement = struct {
221221
return parser.anchorGetHref(self);
222222
}
223223

224-
pub fn set_href(self: *parser.Anchor, href: []const u8, page: *const Page) !void {
224+
pub fn set_href(self: *parser.Anchor, href: []const u8, page: *Page) !void {
225225
const full = try urlStitch(page.call_arena, href, page.url.getHref(), .{});
226-
return parser.anchorSetHref(self, full);
226+
227+
// Get the stored internal URL if we had one.
228+
if (page.getObjectData(self)) |internal_url| {
229+
const u = NativeURL.fromInternal(internal_url);
230+
// Reparse with the new href.
231+
_ = try u.reparse(full);
232+
errdefer u.deinit();
233+
234+
// TODO: Remove the entry from the map on an error situation.
235+
236+
return parser.anchorSetHref(self, u.getHref());
237+
}
238+
239+
// We don't have internal URL stored in object_data yet.
240+
// Create one for this anchor element.
241+
const u = try NativeURL.parse(full, null);
242+
errdefer u.deinit();
243+
// Save to map.
244+
try page.putObjectData(self, u.internal.?);
245+
246+
return parser.anchorSetHref(self, u.getHref());
227247
}
228248

229249
pub fn get_hreflang(self: *parser.Anchor) ![]const u8 {
@@ -258,170 +278,188 @@ pub const HTMLAnchorElement = struct {
258278
return try parser.nodeSetTextContent(parser.anchorToNode(self), v);
259279
}
260280

261-
fn url(self: *parser.Anchor, page: *Page) !URL {
262-
// Although the URL.constructor union accepts an .{.element = X}, we
263-
// can't use this here because the behavior is different.
264-
// URL.constructor(document.createElement('a')
265-
// should fail (a.href isn't a valid URL)
266-
// But
267-
// document.createElement('a').host
268-
// should not fail, it should return an empty string
269-
if (try parser.elementGetAttribute(@ptrCast(@alignCast(self)), "href")) |href| {
270-
return URL.constructor(.{ .string = href }, null, page); // TODO inject base url
281+
fn getHref(self: *parser.Anchor) !?[]const u8 {
282+
return parser.elementGetAttribute(@ptrCast(@alignCast(self)), "href");
283+
}
284+
285+
/// Returns the URL associated with given anchor element.
286+
/// Creates a new URL object if not created before.
287+
fn getURL(self: *parser.Anchor, page: *Page) !NativeURL {
288+
if (page.getObjectData(self)) |internal_url| {
289+
return NativeURL.fromInternal(internal_url);
271290
}
272-
return .empty;
291+
292+
// Try to get href string.
293+
const maybe_anchor_href = try getHref(self);
294+
if (maybe_anchor_href) |anchor_href| {
295+
// Allocate a URL for this anchor element.
296+
const u = try NativeURL.parse(anchor_href, null);
297+
// Save in map.
298+
try page.putObjectData(self, u.internal.?);
299+
300+
return u;
301+
}
302+
303+
// No anchor href string found; let's just return an error.
304+
return error.HrefAttributeNotGiven;
273305
}
274306

275307
// TODO return a disposable string
276308
pub fn get_origin(self: *parser.Anchor, page: *Page) ![]const u8 {
277-
var u = try url(self, page);
278-
return u.get_origin(page);
309+
const u = getURL(self, page) catch return "";
310+
// Though we store the URL in object data map, we still have to allocate
311+
// for origin string sadly.
312+
return u.getOrigin(page.arena);
279313
}
280314

281315
// TODO return a disposable string
282316
pub fn get_protocol(self: *parser.Anchor, page: *Page) ![]const u8 {
283-
var u = try url(self, page);
284-
return u.get_protocol();
317+
const u = getURL(self, page) catch return "";
318+
return u.getProtocol();
285319
}
286320

287321
pub fn set_protocol(self: *parser.Anchor, v: []const u8, page: *Page) !void {
288-
const arena = page.arena;
289-
_ = arena;
290-
var u = try url(self, page);
291-
292-
u.set_protocol(v);
293-
const href = try u.get_href(page);
294-
try parser.anchorSetHref(self, href);
322+
const u = try getURL(self, page);
323+
try u.setProtocol(v);
324+
return parser.anchorSetHref(self, u.getHref());
295325
}
296326

297-
pub fn get_host(self: *parser.Anchor, page: *Page) ![]const u8 {
298-
var u = try url(self, page);
299-
return u.get_host();
300-
}
301-
302-
pub fn set_host(self: *parser.Anchor, v: []const u8, page: *Page) !void {
303-
// search : separator
304-
var p: ?[]const u8 = null;
305-
var h: []const u8 = undefined;
306-
for (v, 0..) |c, i| {
307-
if (c == ':') {
308-
h = v[0..i];
309-
//p = try std.fmt.parseInt(u16, v[i + 1 ..], 10);
310-
p = v[i + 1 ..];
311-
break;
312-
}
313-
}
314-
315-
var u = try url(self, page);
316-
317-
if (p) |port| {
318-
u.set_host(h);
319-
u.set_port(port);
320-
} else {
321-
u.set_host(v);
322-
}
327+
const NativeURL = @import("../../url.zig").URL;
323328

324-
const href = try u.get_href(page);
325-
try parser.anchorSetHref(self, href);
329+
// TODO: Return a disposable string.
330+
pub fn get_host(self: *parser.Anchor, page: *Page) ![]const u8 {
331+
const u = getURL(self, page) catch return "";
332+
return u.host();
326333
}
327334

328-
pub fn get_hostname(self: *parser.Anchor, page: *Page) ![]const u8 {
329-
var u = try url(self, page);
330-
return u.get_hostname();
331-
}
335+
pub fn set_host(self: *parser.Anchor, host_str: []const u8, page: *Page) !void {
336+
const u = blk: {
337+
if (page.getObjectData(self)) |internal_url| {
338+
break :blk NativeURL.fromInternal(internal_url);
339+
}
332340

333-
pub fn set_hostname(self: *parser.Anchor, v: []const u8, page: *Page) !void {
334-
var u = try url(self, page);
335-
u.set_host(v);
336-
const href = try u.get_href(page);
337-
try parser.anchorSetHref(self, href);
338-
}
341+
const maybe_anchor_href = try getHref(self);
342+
if (maybe_anchor_href) |anchor_href| {
343+
const new_u = try NativeURL.parse(anchor_href, null);
344+
try page.putObjectData(self, new_u.internal.?);
345+
break :blk new_u;
346+
}
347+
348+
// Last resort; try to create URL object out of host_str.
349+
const new_u = try NativeURL.parse(host_str, null);
350+
// We can just return here since host is updated.
351+
return page.putObjectData(self, new_u.internal.?);
352+
};
353+
354+
try u.setHost(host_str);
355+
return parser.anchorSetHref(self, u.getHref());
356+
}
357+
358+
//pub fn get_hostname(self: *parser.Anchor, page: *Page) ![]const u8 {
359+
// const maybe_href_str = try getAnchorHref(self);
360+
// const href_str = maybe_href_str orelse return "";
361+
//
362+
// const u = try NativeURL.parse(href_str, null);
363+
// defer u.deinit();
364+
//
365+
// return page.arena.dupe(u8, u.getHostname());
366+
//}
367+
368+
//pub fn set_hostname(self: *parser.Anchor, v: []const u8) !void {
369+
// const maybe_href_str = try getAnchorHref(self);
370+
//
371+
// if (maybe_href_str) |href_str| {
372+
// const u = try NativeURL.parse(href_str, null);
373+
// defer u.deinit();
374+
//
375+
// try u.setHostname(v);
376+
//
377+
// return parser.anchorSetHref(self, u.getHref());
378+
// }
379+
//
380+
// // No href string there; use the given value as href.
381+
// const u = try NativeURL.parse(v, null);
382+
// defer u.deinit();
383+
//
384+
// return parser.anchorSetHref(self, u.getHref());
385+
//}
339386

340387
// TODO return a disposable string
341388
pub fn get_port(self: *parser.Anchor, page: *Page) ![]const u8 {
342-
var u = try url(self, page);
343-
return u.get_port();
389+
const u = getURL(self, page) catch return "";
390+
return u.getPort();
344391
}
345392

346-
pub fn set_port(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
347-
var u = try url(self, page);
393+
pub fn set_port(self: *parser.Anchor, maybe_port: ?[]const u8, page: *Page) !void {
394+
// TODO: Check for valid port (u16 integer).
395+
if (maybe_port) |port| {
396+
const u = try getURL(self, page);
397+
try u.setPort(port);
348398

349-
if (v != null and v.?.len > 0) {
350-
u.set_host(v.?);
399+
return parser.anchorSetHref(self, u.getHref());
351400
}
352-
353-
const href = try u.get_href(page);
354-
try parser.anchorSetHref(self, href);
355401
}
356402

357403
// TODO return a disposable string
358404
pub fn get_username(self: *parser.Anchor, page: *Page) ![]const u8 {
359-
var u = try url(self, page);
360-
return u.get_username();
405+
const u = try getURL(self, page);
406+
return u.getUsername() orelse "";
361407
}
362408

363-
pub fn set_username(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
364-
if (v) |username| {
365-
var u = try url(self, page);
366-
u.set_username(username);
367-
368-
const href = try u.get_href(page);
369-
try parser.anchorSetHref(self, href);
409+
pub fn set_username(self: *parser.Anchor, maybe_username: ?[]const u8, page: *Page) !void {
410+
if (maybe_username) |username| {
411+
const u = try getURL(self, page);
412+
try u.setUsername(username);
413+
try parser.anchorSetHref(self, u.getHref());
370414
}
371415
}
372416

373417
pub fn get_password(self: *parser.Anchor, page: *Page) ![]const u8 {
374-
var u = try url(self, page);
375-
return u.get_password();
418+
const u = try getURL(self, page);
419+
return u.getPassword() orelse "";
376420
}
377421

378-
pub fn set_password(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
379-
if (v) |password| {
380-
var u = try url(self, page);
381-
u.set_password(password);
382-
383-
const href = try u.get_href(page);
384-
try parser.anchorSetHref(self, href);
422+
pub fn set_password(self: *parser.Anchor, maybe_password: ?[]const u8, page: *Page) !void {
423+
if (maybe_password) |password| {
424+
const u = try getURL(self, page);
425+
try u.setPassword(password);
426+
try parser.anchorSetHref(self, u.getHref());
385427
}
386428
}
387429

388430
// TODO return a disposable string
389431
pub fn get_pathname(self: *parser.Anchor, page: *Page) ![]const u8 {
390-
var u = try url(self, page);
391-
return u.get_pathname();
432+
const u = try getURL(self, page);
433+
return u.getPath();
392434
}
393435

394-
pub fn set_pathname(self: *parser.Anchor, v: []const u8, page: *Page) !void {
395-
var u = try url(self, page);
396-
u.set_pathname(v);
397-
const href = try u.get_href(page);
398-
try parser.anchorSetHref(self, href);
436+
pub fn set_pathname(self: *parser.Anchor, pathname: []const u8, page: *Page) !void {
437+
const u = try getURL(self, page);
438+
try u.setPath(pathname);
439+
return parser.anchorSetHref(self, u.getHref());
399440
}
400441

401442
pub fn get_search(self: *parser.Anchor, page: *Page) ![]const u8 {
402-
var u = try url(self, page);
403-
return u.get_search(page);
443+
const u = try getURL(self, page);
444+
return u.getSearch() orelse "";
404445
}
405446

406-
pub fn set_search(self: *parser.Anchor, v: []const u8, page: *Page) !void {
407-
var u = try url(self, page);
408-
try u.set_search(v, page);
409-
410-
const href = try u.get_href(page);
411-
try parser.anchorSetHref(self, href);
447+
pub fn set_search(self: *parser.Anchor, search: []const u8, page: *Page) !void {
448+
const u = try getURL(self, page);
449+
u.setSearch(search);
450+
return parser.anchorSetHref(self, u.getHref());
412451
}
413452

414453
// TODO return a disposable string
415454
pub fn get_hash(self: *parser.Anchor, page: *Page) ![]const u8 {
416-
var u = try url(self, page);
417-
return u.get_hash();
455+
const u = try getURL(self, page);
456+
return u.getHash() orelse "";
418457
}
419458

420-
pub fn set_hash(self: *parser.Anchor, v: []const u8, page: *Page) !void {
421-
var u = try url(self, page);
422-
u.set_hash(v);
423-
const href = try u.get_href(page);
424-
try parser.anchorSetHref(self, href);
459+
pub fn set_hash(self: *parser.Anchor, hash: []const u8, page: *Page) !void {
460+
const u = try getURL(self, page);
461+
u.setHash(hash);
462+
return parser.anchorSetHref(self, u.getHref());
425463
}
426464
};
427465

0 commit comments

Comments
 (0)