Skip to content

Commit 4165f47

Browse files
committed
merge all states
1 parent 19df737 commit 4165f47

File tree

10 files changed

+98
-97
lines changed

10 files changed

+98
-97
lines changed

src/browser/State.zig

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
2+
//
3+
// Francis Bouvier <[email protected]>
4+
// Pierre Tachoire <[email protected]>
5+
//
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU Affero General Public License as
8+
// published by the Free Software Foundation, either version 3 of the
9+
// License, or (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU Affero General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Affero General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
19+
// Sometimes we need to extend libdom. For example, its HTMLDocument doesn't
20+
// have a readyState. We have a couple different options, such as making the
21+
// correction in libdom directly. Another option stems from the fact that every
22+
// libdom node has an opaque embedder_data field. This is the struct that we
23+
// lazily load into that field.
24+
//
25+
// It didn't originally start off as a collection of every single extension, but
26+
// this quickly proved necessary, since different fields are needed on the same
27+
// data at different levels of the prototype chain. This isn't memory efficient.
28+
29+
const Env = @import("env.zig").Env;
30+
const parser = @import("netsurf.zig");
31+
const CSSStyleDeclaration = @import("cssom/css_style_declaration.zig").CSSStyleDeclaration;
32+
33+
// for HTMLScript (but probably needs to be added to more)
34+
onload: ?Env.Function = null,
35+
onerror: ?Env.Function = null,
36+
37+
// for HTMLElement
38+
style: CSSStyleDeclaration = .empty,
39+
40+
// for html/document
41+
ready_state: ReadyState = .loading,
42+
43+
// for dom/document
44+
active_element: ?*parser.Element = null,
45+
46+
// for HTMLSelectElement
47+
// By default, if no option is explicitly selected, the first option should
48+
// be selected. However, libdom doesn't do this, and it sets the
49+
// selectedIndex to -1, which is a valid value for "nothing selected".
50+
// Therefore, when libdom says the selectedIndex == -1, we don't know if
51+
// it means that nothing is selected, or if the first option is selected by
52+
// default.
53+
// There are cases where this won't work, but when selectedIndex is
54+
// explicitly set, we set this boolean flag. Then, when we're getting then
55+
// selectedIndex, if this flag is == false, which is to say that if
56+
// selectedIndex hasn't been explicitly set AND if we have at least 1 option
57+
// AND if it isn't a multi select, we can make the 1st item selected by
58+
// default (by returning selectedIndex == 0).
59+
explicit_index_set: bool = false,
60+
61+
const ReadyState = enum {
62+
loading,
63+
interactive,
64+
complete,
65+
};

src/browser/browser.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const std = @import("std");
2121
const Allocator = std.mem.Allocator;
2222
const ArenaAllocator = std.heap.ArenaAllocator;
2323

24+
const State = @import("State.zig");
2425
const Env = @import("env.zig").Env;
2526
const App = @import("../app.zig").App;
2627
const Session = @import("session.zig").Session;
@@ -41,6 +42,7 @@ pub const Browser = struct {
4142
session_arena: ArenaAllocator,
4243
transfer_arena: ArenaAllocator,
4344
notification: *Notification,
45+
state_pool: std.heap.MemoryPool(State),
4446

4547
pub fn init(app: *App) !Browser {
4648
const allocator = app.allocator;
@@ -61,6 +63,7 @@ pub const Browser = struct {
6163
.page_arena = ArenaAllocator.init(allocator),
6264
.session_arena = ArenaAllocator.init(allocator),
6365
.transfer_arena = ArenaAllocator.init(allocator),
66+
.state_pool = std.heap.MemoryPool(State).init(allocator),
6467
};
6568
}
6669

@@ -71,6 +74,7 @@ pub const Browser = struct {
7174
self.session_arena.deinit();
7275
self.transfer_arena.deinit();
7376
self.notification.deinit();
77+
self.state_pool.deinit();
7478
}
7579

7680
pub fn newSession(self: *Browser) !*Session {

src/browser/dom/document.zig

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ const NodeUnion = @import("node.zig").Union;
2828
const collection = @import("html_collection.zig");
2929
const css = @import("css.zig");
3030

31-
const State = @import("../state/Document.zig");
3231
const Element = @import("element.zig").Element;
3332
const ElementUnion = @import("element.zig").Union;
3433
const TreeWalker = @import("tree_walker.zig").TreeWalker;
@@ -245,7 +244,7 @@ pub const Document = struct {
245244
}
246245

247246
pub fn get_activeElement(self: *parser.Document, page: *Page) !?ElementUnion {
248-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
247+
const state = try page.getOrCreateNodeState(@ptrCast(self));
249248
if (state.active_element) |ae| {
250249
return try Element.toInterface(ae);
251250
}
@@ -262,7 +261,7 @@ pub const Document = struct {
262261
// we could look for the "disabled" attribute, but that's only meaningful
263262
// on certain types, and libdom's vtable doesn't seem to expose this.
264263
pub fn setFocus(self: *parser.Document, e: *parser.ElementHTML, page: *Page) !void {
265-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
264+
const state = try page.getOrCreateNodeState(@ptrCast(self));
266265
state.active_element = @ptrCast(e);
267266
}
268267
};

src/browser/html/document.zig

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const NodeList = @import("../dom/nodelist.zig").NodeList;
3030
const Location = @import("location.zig").Location;
3131

3232
const collection = @import("../dom/html_collection.zig");
33-
const State = @import("../state/Document.zig");
3433
const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
3534
const Cookie = @import("../storage/cookie.zig").Cookie;
3635

@@ -185,7 +184,7 @@ pub const HTMLDocument = struct {
185184
}
186185

187186
pub fn get_readyState(self: *parser.DocumentHTML, page: *Page) ![]const u8 {
188-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
187+
const state = try page.getOrCreateNodeState(@ptrCast(self));
189188
return @tagName(state.ready_state);
190189
}
191190

@@ -264,7 +263,7 @@ pub const HTMLDocument = struct {
264263
}
265264

266265
pub fn documentIsLoaded(self: *parser.DocumentHTML, page: *Page) !void {
267-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
266+
const state = try page.getOrCreateNodeState(@ptrCast(self));
268267
state.ready_state = .interactive;
269268

270269
const evt = try parser.eventCreate();
@@ -279,7 +278,7 @@ pub const HTMLDocument = struct {
279278
}
280279

281280
pub fn documentIsComplete(self: *parser.DocumentHTML, page: *Page) !void {
282-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
281+
const state = try page.getOrCreateNodeState(@ptrCast(self));
283282
state.ready_state = .complete;
284283
}
285284
};

src/browser/html/elements.zig

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const Page = @import("../page.zig").Page;
2525
const urlStitch = @import("../../url.zig").URL.stitch;
2626
const URL = @import("../url/url.zig").URL;
2727
const Node = @import("../dom/node.zig").Node;
28-
const State = @import("../state/HTMLElement.zig");
2928
const Element = @import("../dom/element.zig").Element;
3029

3130
const CSSStyleDeclaration = @import("../cssom/css_style_declaration.zig").CSSStyleDeclaration;
@@ -114,7 +113,7 @@ pub const HTMLElement = struct {
114113
pub const subtype = .node;
115114

116115
pub fn get_style(e: *parser.ElementHTML, page: *Page) !*CSSStyleDeclaration {
117-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(e));
116+
const state = try page.getOrCreateNodeState(@ptrCast(e));
118117
return &state.style;
119118
}
120119

@@ -954,22 +953,22 @@ pub const HTMLScriptElement = struct {
954953
}
955954

956955
pub fn get_onload(self: *parser.Script, page: *Page) !?Env.Function {
957-
const state = page.getNodeWrapper(State, @ptrCast(self)) orelse return null;
956+
const state = page.getNodeState(@ptrCast(self)) orelse return null;
958957
return state.onload;
959958
}
960959

961960
pub fn set_onload(self: *parser.Script, function: ?Env.Function, page: *Page) !void {
962-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
961+
const state = try page.getOrCreateNodeState(@ptrCast(self));
963962
state.onload = function;
964963
}
965964

966965
pub fn get_onerror(self: *parser.Script, page: *Page) !?Env.Function {
967-
const state = page.getNodeWrapper(State, @ptrCast(self)) orelse return null;
966+
const state = page.getNodeState(@ptrCast(self)) orelse return null;
968967
return state.onerror;
969968
}
970969

971970
pub fn set_onerror(self: *parser.Script, function: ?Env.Function, page: *Page) !void {
972-
const state = try page.getOrCreateNodeWrapper(State, @ptrCast(self));
971+
const state = try page.getOrCreateNodeState(@ptrCast(self));
973972
state.onerror = function;
974973
}
975974
};

src/browser/html/select.zig

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,6 @@ pub const HTMLSelectElement = struct {
2626
pub const prototype = *HTMLElement;
2727
pub const subtype = .node;
2828

29-
// By default, if no option is explicitly selected, the first option should
30-
// be selected. However, libdom doesn't do this, and it sets the
31-
// selectedIndex to -1, which is a valid value for "nothing selected".
32-
// Therefore, when libdom says the selectedIndex == -1, we don't know if
33-
// it means that nothing is selected, or if the first option is selected by
34-
// default.
35-
// There are cases where this won't work, but when selectedIndex is
36-
// explicitly set, we set this boolean flag. Then, when we're getting then
37-
// selectedIndex, if this flag is == false, which is to say that if
38-
// selectedIndex hasn't been explicitly set AND if we have at least 1 option
39-
// AND if it isn't a multi select, we can make the 1st item selected by
40-
// default (by returning selectedIndex == 0).
41-
explicit_index_set: bool = false,
42-
4329
pub fn get_length(select: *parser.Select) !u32 {
4430
return parser.selectGetLength(select);
4531
}
@@ -70,11 +56,11 @@ pub const HTMLSelectElement = struct {
7056
}
7157

7258
pub fn get_selectedIndex(select: *parser.Select, page: *Page) !i32 {
73-
const self = try page.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
59+
const state = try page.getOrCreateNodeState(@ptrCast(select));
7460
const selected_index = try parser.selectGetSelectedIndex(select);
7561

7662
// See the explicit_index_set field documentation
77-
if (!self.explicit_index_set) {
63+
if (!state.explicit_index_set) {
7864
if (selected_index == -1) {
7965
if (try parser.selectGetMultiple(select) == false) {
8066
if (try get_length(select) > 0) {
@@ -89,8 +75,8 @@ pub const HTMLSelectElement = struct {
8975
// Libdom's dom_html_select_select_set_selected_index will crash if index
9076
// is out of range, and it doesn't properly unset options
9177
pub fn set_selectedIndex(select: *parser.Select, index: i32, page: *Page) !void {
92-
var self = try page.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
93-
self.explicit_index_set = true;
78+
var state = try page.getOrCreateNodeState(@ptrCast(select));
79+
state.explicit_index_set = true;
9480

9581
const options = try parser.selectGetOptions(select);
9682
const len = try parser.optionCollectionGetLength(options);

src/browser/page.zig

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const builtin = @import("builtin");
2222
const Allocator = std.mem.Allocator;
2323

2424
const Dump = @import("dump.zig");
25+
const State = @import("State.zig");
2526
const Env = @import("env.zig").Env;
2627
const Mime = @import("mime.zig").Mime;
2728
const DataURI = @import("datauri.zig").DataURI;
@@ -95,6 +96,8 @@ pub const Page = struct {
9596
// indicates intention to navigate to another page on the next loop execution.
9697
delayed_navigation: bool = false,
9798

99+
state_pool: *std.heap.MemoryPool(State),
100+
98101
pub fn init(self: *Page, arena: Allocator, session: *Session) !void {
99102
const browser = session.browser;
100103
self.* = .{
@@ -106,6 +109,7 @@ pub const Page = struct {
106109
.call_arena = undefined,
107110
.loop = browser.app.loop,
108111
.renderer = Renderer.init(arena),
112+
.state_pool = &browser.state_pool,
109113
.cookie_jar = &session.cookie_jar,
110114
.microtask_node = .{ .func = microtaskCallback },
111115
.window_clicked_event_node = .{ .func = windowClicked },
@@ -597,21 +601,21 @@ pub const Page = struct {
597601
_ = try self.loop.timeout(0, &navi.navigate_node);
598602
}
599603

600-
pub fn getOrCreateNodeWrapper(self: *Page, comptime T: type, node: *parser.Node) !*T {
601-
if (self.getNodeWrapper(T, node)) |wrap| {
604+
pub fn getOrCreateNodeState(self: *Page, node: *parser.Node) !*State {
605+
if (self.getNodeState(node)) |wrap| {
602606
return wrap;
603607
}
604608

605-
const wrap = try self.arena.create(T);
606-
wrap.* = T{};
609+
const state = try self.state_pool.create();
610+
state.* = .{};
607611

608-
parser.nodeSetEmbedderData(node, wrap);
609-
return wrap;
612+
parser.nodeSetEmbedderData(node, state);
613+
return state;
610614
}
611615

612-
pub fn getNodeWrapper(_: *const Page, comptime T: type, node: *parser.Node) ?*T {
613-
if (parser.nodeGetEmbedderData(node)) |wrap| {
614-
return @alignCast(@ptrCast(wrap));
616+
pub fn getNodeState(_: *const Page, node: *parser.Node) ?*State {
617+
if (parser.nodeGetEmbedderData(node)) |state| {
618+
return @alignCast(@ptrCast(state));
615619
}
616620
return null;
617621
}
@@ -743,8 +747,7 @@ const Script = struct {
743747
// attached to it. But this seems quite unlikely and it does help
744748
// optimize loading scripts, of which there can be hundreds for a
745749
// page.
746-
const State = @import("state/HTMLElement.zig");
747-
if (page.getNodeWrapper(State, @ptrCast(e))) |se| {
750+
if (page.getNodeState(@ptrCast(e))) |se| {
748751
if (se.onload) |function| {
749752
onload = .{ .function = function };
750753
}

src/browser/session.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub const Session = struct {
9090

9191
const page_arena = &self.browser.page_arena;
9292
_ = page_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
93+
_ = self.browser.state_pool.reset(.{ .retain_with_limit = 4 * 1024 });
9394

9495
self.page = @as(Page, undefined);
9596
const page = &self.page.?;

src/browser/state/Document.zig

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/browser/state/HTMLElement.zig

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)