diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index c6a2a7091..c80aa8413 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -103,11 +103,11 @@ fn setCacheDisabled(cmd: *CDP.Command) !void { fn setBlockedURLs(cmd: *CDP.Command) !void { const params = (try cmd.params(struct { - urls: []const []const u8, + urlPatterns: []const HttpClient.BlockPattern, })) orelse return error.InvalidParams; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; - try bc.cdp.browser.http_client.setBlockedUrls(params.urls); + try bc.cdp.browser.http_client.setBlockedUrlPatterns(params.urlPatterns); return cmd.sendResult(null, .{}); } @@ -1018,7 +1018,9 @@ test "cdp.Network: setBlockedURLs blocks requests with inspector reason" { try ctx.processMessage(.{ .id = 2, .method = "Network.setBlockedURLs", - .params = .{ .urls = &[_][]const u8{"*://blocked.test/*"} }, + .params = .{ .urlPatterns = &[_]HttpClient.BlockPattern{ + .{ .urlPattern = "*://blocked.test/*", .block = true }, + } }, }); try ctx.expectSentResult(null, .{ .id = 2 }); diff --git a/src/network/HttpClient.zig b/src/network/HttpClient.zig index a6cbe2a02..15c456fc9 100644 --- a/src/network/HttpClient.zig +++ b/src/network/HttpClient.zig @@ -36,6 +36,7 @@ const Network = @import("Network.zig"); const Cache = @import("cache/Cache.zig"); const RobotsGate = @import("RobotsGate.zig"); const UrlBlocklist = @import("UrlBlocklist.zig"); +pub const BlockPattern = UrlBlocklist.Pattern; const log = lp.log; const Allocator = std.mem.Allocator; @@ -366,6 +367,16 @@ pub fn setBlockedUrls(self: *Client, patterns: []const []const u8) !void { self.url_blocklist = replacement; } +pub fn setBlockedUrlPatterns(self: *Client, patterns: []const BlockPattern) !void { + const replacement: ?UrlBlocklist = if (patterns.len == 0) + null + else + try UrlBlocklist.initPatterns(self.allocator, patterns); + + self.clearUrlBlocklist(); + self.url_blocklist = replacement; +} + fn clearUrlBlocklist(self: *Client) void { if (self.url_blocklist) |*blocklist| { blocklist.deinit(); diff --git a/src/network/UrlBlocklist.zig b/src/network/UrlBlocklist.zig index 4de1dfbef..584f559a2 100644 --- a/src/network/UrlBlocklist.zig +++ b/src/network/UrlBlocklist.zig @@ -19,6 +19,12 @@ const UrlBlocklist = @This(); allocator: std.mem.Allocator, patterns: []const []const u8, +blocks: ?[]const bool = null, + +pub const Pattern = struct { + urlPattern: []const u8, + block: bool, +}; /// Compile and own a set of URL wildcard patterns. Patterns are normalized to /// lowercase once so request matching remains allocation-free. @@ -45,14 +51,28 @@ pub fn init(allocator: std.mem.Allocator, patterns: []const []const u8) !UrlBloc }; } +pub fn initPatterns(allocator: std.mem.Allocator, patterns: []const Pattern) !UrlBlocklist { + const urls = try allocator.alloc([]const u8, patterns.len); + defer allocator.free(urls); + for (patterns, urls) |pattern, *url| url.* = pattern.urlPattern; + + var blocklist = try init(allocator, urls); + errdefer blocklist.deinit(); + const blocks = try allocator.alloc(bool, patterns.len); + for (patterns, blocks) |pattern, *block| block.* = pattern.block; + blocklist.blocks = blocks; + return blocklist; +} + pub fn deinit(self: *UrlBlocklist) void { for (self.patterns) |pattern| self.allocator.free(pattern); self.allocator.free(self.patterns); + if (self.blocks) |blocks| self.allocator.free(blocks); } pub fn isBlocked(self: *const UrlBlocklist, url: []const u8) bool { - for (self.patterns) |pattern| { - if (wildcardMatch(pattern, url)) return true; + for (self.patterns, 0..) |pattern, index| { + if (wildcardMatch(pattern, url)) return if (self.blocks) |blocks| blocks[index] else true; } return false; } @@ -133,9 +153,9 @@ test "UrlBlocklist: wildcard matching backtracks and handles empty patterns" { test "UrlBlocklist: owns compiled patterns" { var pattern = [_]u8{ '*', 'a', 'd', 's', '*' }; - var blocklist = try UrlBlocklist.init(std.testing.allocator, &.{&pattern}); + var blocklist = try UrlBlocklist.initPatterns(std.testing.allocator, &.{ .{ .urlPattern = &pattern, .block = false }, .{ .urlPattern = "*", .block = true } }); defer blocklist.deinit(); @memset(&pattern, 'x'); - try std.testing.expect(blocklist.isBlocked("https://example.com/ads/script.js")); + try std.testing.expect(!blocklist.isBlocked("https://example.com/ads/script.js")); }