@@ -25,6 +25,7 @@ const Env = @import("../browser/env.zig").Env;
2525const asUint = @import ("../str/parser.zig" ).asUint ;
2626const Browser = @import ("../browser/browser.zig" ).Browser ;
2727const Session = @import ("../browser/browser.zig" ).Session ;
28+ const Inspector = @import ("../browser/env.zig" ).Env .Inspector ;
2829const Incrementing = @import ("../id.zig" ).Incrementing ;
2930const Notification = @import ("../notification.zig" ).Notification ;
3031
@@ -309,40 +310,51 @@ pub fn BrowserContext(comptime CDP_T: type) type {
309310 node_registry : Node.Registry ,
310311 node_search_list : Node.Search.List ,
311312
312- isolated_world : ? IsolatedWorld (Env ),
313+ inspector : Inspector ,
314+ isolated_world : ? IsolatedWorld ,
313315
314316 const Self = @This ();
315317
316318 fn init (self : * Self , id : []const u8 , cdp : * CDP_T ) ! void {
317319 const allocator = cdp .allocator ;
318320
321+ const session = try cdp .browser .newSession (self );
322+ const arena = session .arena .allocator ();
323+
324+ const inspector = try cdp .browser .env .newInspector (arena , self );
325+
319326 var registry = Node .Registry .init (allocator );
320327 errdefer registry .deinit ();
321328
322- const session = try cdp .browser .newSession (self );
323329 self .* = .{
324330 .id = id ,
325331 .cdp = cdp ,
332+ .arena = arena ,
326333 .target_id = null ,
327334 .session_id = null ,
335+ .session = session ,
328336 .security_origin = URL_BASE ,
329337 .secure_context_type = "Secure" , // TODO = enum
330338 .loader_id = LOADER_ID ,
331- .session = session ,
332- .arena = session .arena .allocator (),
333339 .page_life_cycle_events = false , // TODO; Target based value
334340 .node_registry = registry ,
335341 .node_search_list = undefined ,
336342 .isolated_world = null ,
343+ .inspector = inspector ,
337344 };
338345 self .node_search_list = Node .Search .List .init (allocator , & self .node_registry );
339346 }
340347
341348 pub fn deinit (self : * Self ) void {
342- if (self .isolated_world ) | isolated_world | {
343- isolated_world .executor .endScope ();
344- self .cdp .browser .env .stopExecutor (isolated_world .executor );
345- self .isolated_world = null ;
349+ self .inspector .deinit ();
350+
351+ // If the session has a page, we need to clear it first. The page
352+ // context is always nested inside of the isolated world context,
353+ // so we need to shutdown the page one first.
354+ self .cdp .browser .closeSession ();
355+
356+ if (self .isolated_world ) | * world | {
357+ world .deinit ();
346358 }
347359 self .node_registry .deinit ();
348360 self .node_search_list .deinit ();
@@ -353,25 +365,25 @@ pub fn BrowserContext(comptime CDP_T: type) type {
353365 self .node_search_list .reset ();
354366 }
355367
356- pub fn createIsolatedWorld (
357- self : * Self ,
358- world_name : []const u8 ,
359- grant_universal_access : bool ,
360- ) ! void {
361- if (self .isolated_world != null ) return error .CurrentlyOnly1IsolatedWorldSupported ;
362-
363- const executor = try self .cdp .browser .env .startExecutor (@import ("../browser/html/window.zig" ).Window , & self .session .state , self .session , .isolated );
364- errdefer self .cdp .browser .env .stopExecutor (executor );
368+ pub fn createIsolatedWorld (self : * Self ) ! void {
369+ if (self .isolated_world != null ) {
370+ return error .CurrentlyOnly1IsolatedWorldSupported ;
371+ }
365372
366- // TBD should we endScope on removePage and re-startScope on createPage?
367- // Window will be refactored into the executor so we leave it ugly here for now as a reminder.
368- try executor .startScope (@import ("../browser/html/window.zig" ).Window {});
373+ var executor = try self .cdp .browser .env .newExecutor ();
374+ errdefer executor .deinit ();
369375
370376 self .isolated_world = .{
371- .name = try self .arena .dupe (u8 , world_name ),
372- .grant_universal_access = grant_universal_access ,
377+ .name = "" ,
378+ .global = .{},
379+ .scope = undefined ,
373380 .executor = executor ,
381+ .grant_universal_access = false ,
374382 };
383+ var world = & self .isolated_world .? ;
384+
385+ // TODO: can we do something better than passing `undefined` for the state?
386+ world .scope = try world .executor .startScope (& world .global , undefined , {});
375387 }
376388
377389 pub fn nodeWriter (self : * Self , node : * const Node , opts : Node.Writer.Opts ) Node.Writer {
@@ -384,18 +396,35 @@ pub fn BrowserContext(comptime CDP_T: type) type {
384396
385397 pub fn getURL (self : * const Self ) ? []const u8 {
386398 const page = self .session .currentPage () orelse return null ;
387- return if (page .url ) | * url | url .raw else null ;
399+ const raw_url = page .url .raw ;
400+ return if (raw_url .len == 0 ) null else raw_url ;
388401 }
389402
390403 pub fn notify (ctx : * anyopaque , notification : * const Notification ) ! void {
391404 const self : * Self = @alignCast (@ptrCast (ctx ));
392405
393406 switch (notification .* ) {
407+ .context_created = > | cc | {
408+ const aux_data = try std .fmt .allocPrint (self .arena , "{{\" isDefault\" :true,\" type\" :\" default\" ,\" frameId\" :\" {s}\" }}" , .{self .target_id .? });
409+ self .inspector .contextCreated (
410+ self .session .page .? .scope ,
411+ "" ,
412+ cc .origin ,
413+ aux_data ,
414+ true ,
415+ );
416+ },
394417 .page_navigate = > | * pn | return @import ("domains/page.zig" ).pageNavigate (self , pn ),
395418 .page_navigated = > | * pn | return @import ("domains/page.zig" ).pageNavigated (self , pn ),
396419 }
397420 }
398421
422+ pub fn callInspector (self : * const Self , msg : []const u8 ) void {
423+ self .inspector .send (msg );
424+ // force running micro tasks after send input to the inspector.
425+ self .cdp .browser .runMicrotasks ();
426+ }
427+
399428 pub fn onInspectorResponse (ctx : * anyopaque , _ : u32 , msg : []const u8 ) void {
400429 if (std .log .defaultLogEnabled (.debug )) {
401430 // msg should be {"id":<id>,...
@@ -481,13 +510,17 @@ pub fn BrowserContext(comptime CDP_T: type) type {
481510/// An isolated world has it's own instance of globals like Window.
482511/// Generally the client needs to resolve a node into the isolated world to be able to work with it.
483512/// An object id is unique across all contexts, different object ids can refer to the same Node in different contexts.
484- pub fn IsolatedWorld (comptime E : type ) type {
485- return struct {
486- name : []const u8 ,
487- grant_universal_access : bool ,
488- executor : * E.Executor ,
489- };
490- }
513+ const IsolatedWorld = struct {
514+ name : []const u8 ,
515+ scope : * Env.Scope ,
516+ executor : Env.Executor ,
517+ grant_universal_access : bool ,
518+ global : @import ("../browser/html/window.zig" ).Window ,
519+
520+ pub fn deinit (self : * IsolatedWorld ) void {
521+ self .executor .deinit ();
522+ }
523+ };
491524
492525// This is a generic because when we send a result we have two different
493526// behaviors. Normally, we're sending the result to the client. But in some cases
0 commit comments