Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/browser/browser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub const Session = struct {
// can't use the page arena, because we're about to reset it
// and don't want to use the session's arena, because that'll start to
// look like a leak if we navigate from page to page a lot.
var buf: [1024]u8 = undefined;
var buf: [2048]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const url = try self.page.?.url.resolve(fba.allocator(), url_string);

Expand Down Expand Up @@ -706,12 +706,37 @@ pub const Page = struct {
.a => {
const element: *parser.Element = @ptrCast(node);
const href = (try parser.elementGetAttribute(element, "href")) orelse return;
return self.session.pageNavigate(href);

// We cannot navigate immediately as navigating will delete the DOM tree, which holds this event's node.
// As such we schedule the function to be called as soon as possible.
// NOTE Using the page.arena assumes that the scheduling loop does use this object after invoking the callback
// If that changes we may want to consider storing DelayedNavigation in the session instead.
const arena = self.arena;
const navi = try arena.create(DelayedNavigation);
navi.* = .{
.session = self.session,
.href = try arena.dupe(u8, href),
};
_ = try self.state.loop.timeout(0, &navi.navigate_node);
},
else => {},
}
}

const DelayedNavigation = struct {
navigate_node: Loop.CallbackNode = .{ .func = DelayedNavigation.delay_navigate },
session: *Session,
href: []const u8,

fn delay_navigate(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
_ = repeat_delay;
const self: *DelayedNavigation = @fieldParentPtr("navigate_node", node);
self.session.pageNavigate(self.href) catch |err| {
log.err("Delayed navigation error {}", .{err}); // TODO: should we trigger a specific event here?
};
}
};

const Script = struct {
element: *parser.Element,
kind: Kind,
Expand Down Expand Up @@ -818,8 +843,8 @@ const FlatRenderer = struct {
const gop = try self.positions.getOrPut(self.allocator, @intFromPtr(e));
var x: u32 = gop.value_ptr.*;
if (gop.found_existing == false) {
try elements.append(self.allocator, @intFromPtr(e));
x = @intCast(elements.items.len);
try elements.append(self.allocator, @intFromPtr(e));
gop.value_ptr.* = x;
}

Expand All @@ -841,15 +866,15 @@ const FlatRenderer = struct {
}

pub fn width(self: *const FlatRenderer) u32 {
return @intCast(self.elements.items.len + 1); // +1 since x starts at 1 (use len after append)
return @max(@as(u32, @intCast(self.elements.items.len)), 1); // At least 1 pixel even if empty
}

pub fn height(_: *const FlatRenderer) u32 {
return 1;
}

pub fn getElementAtPosition(self: *const FlatRenderer, x: i32, y: i32) ?*parser.Element {
if (y != 1 or x < 0) {
if (y != 0 or x < 0) {
return null;
}

Expand Down
8 changes: 4 additions & 4 deletions src/browser/dom/element.zig
Original file line number Diff line number Diff line change
Expand Up @@ -500,24 +500,24 @@ test "Browser.DOM.Element" {
.{ "document.getElementById('para').clientHeight", "1" },

.{ "let r1 = document.getElementById('para').getBoundingClientRect()", "undefined" },
.{ "r1.x", "1" },
.{ "r1.x", "0" },
.{ "r1.y", "0" },
.{ "r1.width", "1" },
.{ "r1.height", "1" },

.{ "let r2 = document.getElementById('content').getBoundingClientRect()", "undefined" },
.{ "r2.x", "2" },
.{ "r2.x", "1" },
.{ "r2.y", "0" },
.{ "r2.width", "1" },
.{ "r2.height", "1" },

.{ "let r3 = document.getElementById('para').getBoundingClientRect()", "undefined" },
.{ "r3.x", "1" },
.{ "r3.x", "0" },
.{ "r3.y", "0" },
.{ "r3.width", "1" },
.{ "r3.height", "1" },

.{ "document.getElementById('para').clientWidth", "3" },
.{ "document.getElementById('para').clientWidth", "2" },
.{ "document.getElementById('para').clientHeight", "1" },
}, .{});

Expand Down
8 changes: 4 additions & 4 deletions src/browser/dom/intersection_observer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,16 @@ test "Browser.DOM.IntersectionObserver" {
try runner.testCases(&.{
.{ "let entry;", "undefined" },
.{ "new IntersectionObserver(entries => { entry = entries[0]; }).observe(document.createElement('div'));", "undefined" },
.{ "entry.boundingClientRect.x;", "1" },
.{ "entry.boundingClientRect.x;", "0" },
.{ "entry.intersectionRatio;", "1" },
.{ "entry.intersectionRect.x;", "1" },
.{ "entry.intersectionRect.x;", "0" },
.{ "entry.intersectionRect.y;", "0" },
.{ "entry.intersectionRect.width;", "1" },
.{ "entry.intersectionRect.height;", "1" },
.{ "entry.isIntersecting;", "true" },
.{ "entry.rootBounds.x;", "0" },
.{ "entry.rootBounds.y;", "0" },
.{ "entry.rootBounds.width;", "2" },
.{ "entry.rootBounds.width;", "1" },
.{ "entry.rootBounds.height;", "1" },
.{ "entry.target;", "[object HTMLDivElement]" },
}, .{});
Expand All @@ -273,6 +273,6 @@ test "Browser.DOM.IntersectionObserver" {
"undefined",
},
.{ "new_observer.observe(document.createElement('div'));", "undefined" },
.{ "new_entry.rootBounds.x;", "2" },
.{ "new_entry.rootBounds.x;", "1" },
}, .{});
}
16 changes: 16 additions & 0 deletions src/url.zig
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,19 @@ pub const URL = struct {
return WebApiURL.init(allocator, self.uri);
}
};

test "Url resolve size" {
const base = "https://www.lightpande.io";
const url = try URL.parse(base, null);

var url_string: [511]u8 = undefined; // Currently this is the largest url we support, it is however recommmended to at least support 2000 characters
@memset(&url_string, 'a');

var buf: [2048]u8 = undefined; // This is approximately the required size to support the current largest supported URL
var fba = std.heap.FixedBufferAllocator.init(&buf);
const out_url = try url.resolve(fba.allocator(), &url_string);

try std.testing.expectEqualStrings(out_url.raw[0..25], base);
try std.testing.expectEqual(out_url.raw[25], '/');
try std.testing.expectEqualStrings(out_url.raw[26..], &url_string);
}