Skip to content

cdp: make Network.enable idempotent#3038

Closed
Ppsoft1991 wants to merge 1 commit into
lightpanda-io:mainfrom
Ppsoft1991:fix/cdp-network-enable-idempotent
Closed

cdp: make Network.enable idempotent#3038
Ppsoft1991 wants to merge 1 commit into
lightpanda-io:mainfrom
Ppsoft1991:fix/cdp-network-enable-idempotent

Conversation

@Ppsoft1991

Copy link
Copy Markdown
Contributor

Summary

  • make Network.enable idempotent per browser context
  • clean up partially registered network listeners if enabling fails
  • reset the registration state when Network.disable runs
  • add a regression test that verifies a response body is captured exactly once after two enable calls

Root cause

Notification.register appends a listener on every call. Calling Network.enable twice therefore registers the same http_response_data callback twice, and onHttpResponseData appends every chunk twice to captured_responses.

This is independent of #3037, which covers Network event payload completeness and worker request propagation.

Reproduction

Without this fix, the new test receives:

<p>1</p> <p>2</p>
<p>1</p> <p>2</p>

instead of the fixture body once.

Testing

  • regression test: 1/1 passed after failing on unpatched main
  • TEST_FILTER='cdp.Network' ... zig build ... test: 9/9 passed
  • full test suite: 1062/1062 passed
  • tracked Zig files: zig fmt --check
  • git diff --check

Avoid registering the same BrowserContext notification callbacks more than once. Duplicate registrations emit duplicate Network events and append every response body chunk repeatedly.
@karlseguin

Copy link
Copy Markdown
Collaborator

I think I'd rather dedupe in notification. We currently have no case where a receiver should add multiple listeners to an event (and I can't imagine ever needing it):

pub fn register(self: *Notification, comptime event: EventType, receiver: anytype, func: EventFunc(event)) !void {
    const allocator = self.allocator;
    const gop = try self.listeners.getOrPut(allocator, @intFromPtr(receiver));
    if (gop.found_existing) {
        for (gop.value_ptr.items) |existing| {
            if (existing.event == event) {
                lp.assert(@as(*const anyopaque, @ptrCast(func)) == existing.func, "different notification callbacks per receiver", .{.event = event});
                return;
            }
        }
    } else {
        gop.value_ptr.* = .empty;
    }

    var list = &@field(self.event_listeners, @tagName(event));
    var listener = try self.mem_pool.create();
    errdefer self.mem_pool.destroy(listener);

    listener.* = .{
        .node = .{},
        .list = list,
        .receiver = receiver,
        .event = event,
        .func = @ptrCast(func),
        .struct_name = @typeName(@typeInfo(@TypeOf(receiver)).pointer.child),
    };
    try gop.value_ptr.append(allocator, listener);

    // we don't add this until we've successfully added the entry to
    // self.listeners
    list.append(&listener.node);
}

(would be nice to add a test for this too).

Can remove the existing download_events_registered.

If you agree and prefer, you can close this and I'll make the change.

@Ppsoft1991

Copy link
Copy Markdown
Contributor Author

I agree with your approach. Deduplicating in Notification.register is cleaner and more appropriate. Please feel free to make the change; I’ll close this PR.

@Ppsoft1991 Ppsoft1991 closed this Jul 23, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants