1919const std = @import ("std" );
2020const builtin = @import ("builtin" );
2121
22+ const Allocator = std .mem .Allocator ;
23+
2224const Types = @import ("root" ).Types ;
2325
2426const parser = @import ("netsurf" );
@@ -57,30 +59,44 @@ pub const user_agent = "Lightpanda/1.0";
5759// A browser contains only one session.
5860// TODO allow multiple sessions per browser.
5961pub const Browser = struct {
60- session : Session = undefined ,
61- agent : []const u8 = user_agent ,
62+ loop : * Loop ,
63+ session : ? * Session ,
64+ allocator : Allocator ,
65+ session_pool : SessionPool ,
6266
63- const uri = "about:blank" ;
67+ const SessionPool = std . heap . MemoryPool ( Session ) ;
6468
65- pub fn init (self : * Browser , alloc : std.mem.Allocator , loop : * Loop , vm : jsruntime.VM ) ! void {
66- // We want to ensure the caller initialised a VM, but the browser
67- // doesn't use it directly...
68- _ = vm ;
69+ const uri = "about:blank" ;
6970
70- try Session .init (& self .session , alloc , loop , uri );
71+ pub fn init (allocator : Allocator , loop : * Loop ) Browser {
72+ return .{
73+ .loop = loop ,
74+ .session = null ,
75+ .allocator = allocator ,
76+ .session_pool = SessionPool .init (allocator ),
77+ };
7178 }
7279
7380 pub fn deinit (self : * Browser ) void {
74- self .session .deinit ();
81+ self .closeSession ();
82+ self .session_pool .deinit ();
7583 }
7684
77- pub fn newSession (
78- self : * Browser ,
79- alloc : std.mem.Allocator ,
80- loop : * jsruntime.Loop ,
81- ) ! void {
82- self .session .deinit ();
83- try Session .init (& self .session , alloc , loop , uri );
85+ pub fn newSession (self : * Browser , ctx : anytype ) ! * Session {
86+ self .closeSession ();
87+
88+ const session = try self .session_pool .create ();
89+ try Session .init (session , self .allocator , ctx , self .loop , uri );
90+ self .session = session ;
91+ return session ;
92+ }
93+
94+ fn closeSession (self : * Browser ) void {
95+ if (self .session ) | session | {
96+ session .deinit ();
97+ self .session_pool .destroy (session );
98+ self .session = null ;
99+ }
84100 }
85101
86102 pub fn currentPage (self : * Browser ) ? * Page {
@@ -96,7 +112,7 @@ pub const Browser = struct {
96112// deinit a page before running another one.
97113pub const Session = struct {
98114 // allocator used to init the arena.
99- alloc : std.mem. Allocator ,
115+ allocator : Allocator ,
100116
101117 // The arena is used only to bound the js env init b/c it leaks memory.
102118 // see https://github.com/lightpanda-io/jsruntime-lib/issues/181
@@ -109,8 +125,9 @@ pub const Session = struct {
109125
110126 // TODO handle proxy
111127 loader : Loader ,
112- env : Env = undefined ,
113- inspector : ? jsruntime.Inspector = null ,
128+
129+ env : Env ,
130+ inspector : jsruntime.Inspector ,
114131
115132 window : Window ,
116133
@@ -121,20 +138,54 @@ pub const Session = struct {
121138
122139 jstypes : [Types .len ]usize = undefined ,
123140
124- fn init (self : * Session , alloc : std.mem. Allocator , loop : * Loop , uri : []const u8 ) ! void {
125- self .* = Session {
141+ fn init (self : * Session , allocator : Allocator , ctx : anytype , loop : * Loop , uri : []const u8 ) ! void {
142+ self .* = . {
126143 .uri = uri ,
127- .alloc = alloc ,
128- .arena = std .heap .ArenaAllocator .init (alloc ),
144+ .env = undefined ,
145+ .inspector = undefined ,
146+ .allocator = allocator ,
147+ .loader = Loader .init (allocator ),
148+ .httpClient = .{ .allocator = allocator },
149+ .storageShed = storage .Shed .init (allocator ),
150+ .arena = std .heap .ArenaAllocator .init (allocator ),
129151 .window = Window .create (null , .{ .agent = user_agent }),
130- .loader = Loader .init (alloc ),
131- .storageShed = storage .Shed .init (alloc ),
132- .httpClient = undefined ,
133152 };
134153
135- Env .init (& self .env , self .arena .allocator (), loop , null );
136- self .httpClient = .{ .allocator = alloc };
154+ const arena = self .arena .allocator ();
155+
156+ Env .init (& self .env , arena , loop , null );
157+ errdefer self .env .deinit ();
137158 try self .env .load (& self .jstypes );
159+
160+ const ContextT = @TypeOf (ctx );
161+ const InspectorContainer = switch (@typeInfo (ContextT )) {
162+ .Struct = > ContextT ,
163+ .Pointer = > | ptr | ptr .child ,
164+ .Void = > NoopInspector ,
165+ else = > @compileError ("invalid context type" ),
166+ };
167+
168+ // const ctx_opaque = @as(*anyopaque, @ptrCast(ctx));
169+ self .inspector = try jsruntime .Inspector .init (
170+ arena ,
171+ self .env ,
172+ if (@TypeOf (ctx ) == void ) @constCast (@ptrCast (&{})) else ctx ,
173+ InspectorContainer .onInspectorResponse ,
174+ InspectorContainer .onInspectorEvent ,
175+ );
176+ self .env .setInspector (self .inspector );
177+ }
178+
179+ fn deinit (self : * Session ) void {
180+ if (self .page ) | * p | {
181+ p .deinit ();
182+ }
183+
184+ self .env .deinit ();
185+ self .arena .deinit ();
186+ self .httpClient .deinit ();
187+ self .loader .deinit ();
188+ self .storageShed .deinit ();
138189 }
139190
140191 fn fetchModule (ctx : * anyopaque , referrer : ? jsruntime.Module , specifier : []const u8 ) ! jsruntime.Module {
@@ -152,49 +203,21 @@ pub const Session = struct {
152203 return self .env .compileModule (body , specifier );
153204 }
154205
155- fn deinit (self : * Session ) void {
156- if (self .page ) | * p | p .deinit ();
157-
158- if (self .inspector ) | inspector | {
159- inspector .deinit (self .alloc );
160- }
161-
162- self .env .deinit ();
163- self .arena .deinit ();
164-
165- self .httpClient .deinit ();
166- self .loader .deinit ();
167- self .storageShed .deinit ();
168- }
169-
170- pub fn initInspector (
171- self : * Session ,
172- ctx : anytype ,
173- onResp : jsruntime.InspectorOnResponseFn ,
174- onEvent : jsruntime.InspectorOnEventFn ,
175- ) ! void {
176- const ctx_opaque = @as (* anyopaque , @ptrCast (ctx ));
177- self .inspector = try jsruntime .Inspector .init (self .alloc , self .env , ctx_opaque , onResp , onEvent );
178- self .env .setInspector (self .inspector .? );
179- }
180-
181206 pub fn callInspector (self : * Session , msg : []const u8 ) void {
182- if (self .inspector ) | inspector | {
183- inspector .send (msg , self .env );
184- } else {
185- @panic ("No Inspector" );
186- }
207+ self .inspector .send (self .env , msg );
187208 }
188209
189210 // NOTE: the caller is not the owner of the returned value,
190211 // the pointer on Page is just returned as a convenience
191212 pub fn createPage (self : * Session ) ! * Page {
192213 if (self .page != null ) return error .SessionPageExists ;
193- const p : Page = undefined ;
194- self .page = p ;
195- Page .init (& self .page .? , self .alloc , self );
214+ self .page = Page .init (self .allocator , self );
196215 return & self .page .? ;
197216 }
217+
218+ pub fn currentPage (self : * Session ) ? * Page {
219+ return &(self .page orelse return null );
220+ }
198221};
199222
200223// Page navigates to an url.
@@ -203,8 +226,8 @@ pub const Session = struct {
203226// The page handle all its memory in an arena allocator. The arena is reseted
204227// when end() is called.
205228pub const Page = struct {
206- arena : std.heap.ArenaAllocator ,
207229 session : * Session ,
230+ arena : std.heap.ArenaAllocator ,
208231 doc : ? * parser.Document = null ,
209232
210233 // handle url
@@ -218,17 +241,19 @@ pub const Page = struct {
218241
219242 raw_data : ? []const u8 = null ,
220243
221- fn init (
222- self : * Page ,
223- alloc : std.mem.Allocator ,
224- session : * Session ,
225- ) void {
226- self .* = .{
227- .arena = std .heap .ArenaAllocator .init (alloc ),
244+ fn init (allocator : Allocator , session : * Session ) Page {
245+ return .{
228246 .session = session ,
247+ .arena = std .heap .ArenaAllocator .init (allocator ),
229248 };
230249 }
231250
251+ pub fn deinit (self : * Page ) void {
252+ self .end ();
253+ self .arena .deinit ();
254+ self .session .page = null ;
255+ }
256+
232257 // start js env.
233258 // - auxData: extra data forwarded to the Inspector
234259 // see Inspector.contextCreated
@@ -253,18 +278,15 @@ pub const Page = struct {
253278 try polyfill .load (self .arena .allocator (), self .session .env );
254279
255280 // inspector
256- if (self .session .inspector ) | inspector | {
257- log .debug ("inspector context created" , .{});
258- inspector .contextCreated (self .session .env , "" , self .origin orelse "://" , auxData );
259- }
281+ log .debug ("inspector context created" , .{});
282+ self .session .inspector .contextCreated (self .session .env , "" , self .origin orelse "://" , auxData );
260283 }
261284
262285 // reset js env and mem arena.
263286 pub fn end (self : * Page ) void {
264287 self .session .env .stop ();
265288 // TODO unload document: https://html.spec.whatwg.org/#unloading-documents
266289
267- if (self .url ) | * u | u .deinit (self .arena .allocator ());
268290 self .url = null ;
269291 self .location .url = null ;
270292 self .session .window .replaceLocation (& self .location ) catch | e | {
@@ -278,14 +300,8 @@ pub const Page = struct {
278300 _ = self .arena .reset (.free_all );
279301 }
280302
281- pub fn deinit (self : * Page ) void {
282- self .end ();
283- self .arena .deinit ();
284- self .session .page = null ;
285- }
286-
287303 // dump writes the page content into the given file.
288- pub fn dump (self : * Page , out : std.fs.File ) ! void {
304+ pub fn dump (self : * const Page , out : std.fs.File ) ! void {
289305
290306 // if no HTML document pointer available, dump the data content only.
291307 if (self .doc == null ) {
@@ -333,11 +349,9 @@ pub const Page = struct {
333349 }
334350
335351 // own the url
336- if (self .rawuri ) | prev | alloc .free (prev );
337352 self .rawuri = try alloc .dupe (u8 , uri );
338353 self .uri = std .Uri .parse (self .rawuri .? ) catch try std .Uri .parseAfterScheme ("" , self .rawuri .? );
339354
340- if (self .url ) | * prev | prev .deinit (alloc );
341355 self .url = try URL .constructor (alloc , self .rawuri .? , null );
342356 self .location .url = & self .url .? ;
343357 try self .session .window .replaceLocation (& self .location );
@@ -435,9 +449,7 @@ pub const Page = struct {
435449 // https://html.spec.whatwg.org/#read-html
436450
437451 // inspector
438- if (self .session .inspector ) | inspector | {
439- inspector .contextCreated (self .session .env , "" , self .origin .? , auxData );
440- }
452+ self .session .inspector .contextCreated (self .session .env , "" , self .origin .? , auxData );
441453
442454 // replace the user context document with the new one.
443455 try self .session .env .setUserContext (.{
@@ -596,7 +608,7 @@ pub const Page = struct {
596608 };
597609
598610 // the caller owns the returned string
599- fn fetchData (self : * Page , alloc : std.mem. Allocator , src : []const u8 ) ! []const u8 {
611+ fn fetchData (self : * Page , alloc : Allocator , src : []const u8 ) ! []const u8 {
600612 log .debug ("starting fetch {s}" , .{src });
601613
602614 var buffer : [1024 ]u8 = undefined ;
@@ -671,7 +683,7 @@ pub const Page = struct {
671683 return .unknown ;
672684 }
673685
674- fn eval (self : Script , alloc : std.mem. Allocator , env : Env , body : []const u8 ) ! void {
686+ fn eval (self : Script , alloc : Allocator , env : Env , body : []const u8 ) ! void {
675687 var try_catch : jsruntime.TryCatch = undefined ;
676688 try_catch .init (env );
677689 defer try_catch .deinit ();
@@ -696,3 +708,8 @@ pub const Page = struct {
696708 }
697709 };
698710};
711+
712+ const NoopInspector = struct {
713+ pub fn onInspectorResponse (_ : * anyopaque , _ : u32 , _ : []const u8 ) void {}
714+ pub fn onInspectorEvent (_ : * anyopaque , _ : []const u8 ) void {}
715+ };
0 commit comments