@@ -20,6 +20,8 @@ const std = @import("std");
2020const URL = @import ("../../url.zig" ).URL ;
2121const Page = @import ("../../browser/page.zig" ).Page ;
2222const Notification = @import ("../../notification.zig" ).Notification ;
23+ const log = @import ("../../log.zig" );
24+ const parser = @import ("../../browser/netsurf.zig" );
2325
2426const Allocator = std .mem .Allocator ;
2527
@@ -354,9 +356,20 @@ pub fn pageNavigated(bc: anytype, event: *const Notification.PageNavigated) !voi
354356 }
355357
356358 // frameStoppedLoading
357- return cdp .sendEvent ("Page.frameStoppedLoading" , .{
359+ try cdp .sendEvent ("Page.frameStoppedLoading" , .{
358360 .frameId = target_id ,
359361 }, .{ .session_id = session_id });
362+
363+ // Auto-enable DOM monitoring after page navigation is complete
364+ const page = bc .session .currentPage () orelse return ;
365+ if (page .auto_enable_dom_monitoring ) {
366+ std .debug .print ("dom monitoring enabled\n " , .{});
367+ autoEnableDOMMonitoring (bc , page ) catch | err | {
368+ log .warn (.cdp , "autoenable DOM monitor fail" , .{.err = err });
369+ };
370+ } else {
371+ std .debug .print ("nalok\n " , .{});
372+ }
360373}
361374
362375pub fn pageNetworkIdle (bc : anytype , event : * const Notification.PageNetworkIdle ) ! void {
@@ -389,6 +402,72 @@ const LifecycleEvent = struct {
389402 timestamp : u32 ,
390403};
391404
405+ // Auto-enable DOM monitoring when pages are created/navigated
406+ fn autoEnableDOMMonitoring (bc : anytype , page : anytype ) ! void {
407+ const BC = @TypeOf (bc .* );
408+ const CDP = @TypeOf (bc .cdp .* );
409+
410+ // Check if we have a session (required for sending events)
411+ const session_id = bc .session_id orelse return ;
412+
413+ // Create a CDP-specific mutation observer that sends DOM.attributeModified events
414+ const arena = page .arena ;
415+ const doc = parser .documentHTMLToDocument (page .window .document );
416+ const Observer = struct {
417+ bc : * BC ,
418+ cdp : * CDP ,
419+ session_id : []const u8 ,
420+ event_node : parser.EventNode ,
421+
422+ const Self = @This ();
423+
424+ fn handle (en : * parser.EventNode , event : * parser.Event ) void {
425+ const self : * Self = @fieldParentPtr ("event_node" , en );
426+ self ._handle (event ) catch | err | {
427+ log .err (.cdp , "DOM Observer handle error" , .{.err = err });
428+ };
429+ }
430+
431+ fn _handle (self : * Self , event : * parser.Event ) ! void {
432+ const mutation_event = parser .eventToMutationEvent (event );
433+ const attribute_name = parser .mutationEventAttributeName (mutation_event ) catch return ;
434+ const new_value = parser .mutationEventNewValue (mutation_event ) catch null ;
435+
436+ // Get the target node and register it to get a CDP node ID
437+ const event_target = parser .eventTarget (event ) orelse return ;
438+ const target_node = parser .eventTargetToNode (event_target );
439+ const node = try self .bc .node_registry .register (target_node );
440+
441+ // Send CDP DOM.attributeModified event
442+ try self .cdp .sendEvent ("DOM.attributeModified" , .{
443+ .nodeId = node .id ,
444+ .name = attribute_name ,
445+ .value = new_value ,
446+ }, .{
447+ .session_id = self .session_id ,
448+ });
449+ }
450+ };
451+
452+ const cdp_observer = try arena .create (Observer );
453+ cdp_observer .* = .{
454+ .bc = bc ,
455+ .cdp = bc .cdp ,
456+ .session_id = session_id ,
457+ .event_node = .{ .id = @intFromPtr (cdp_observer ), .func = Observer .handle },
458+ };
459+
460+ // Add event listener to document element to catch all attribute changes
461+ const document_element = (try parser .documentGetDocumentElement (doc )) orelse return ;
462+
463+ _ = try parser .eventTargetAddEventListener (
464+ parser .toEventTarget (parser .Element , document_element ),
465+ "DOMAttrModified" ,
466+ & cdp_observer .event_node ,
467+ true , // use capture to catch all events
468+ );
469+ }
470+
392471const testing = @import ("../testing.zig" );
393472test "cdp.page: getFrameTree" {
394473 var ctx = testing .context ();
0 commit comments