From 1502a51b380145a5b218ef8c1998174033bdfdf6 Mon Sep 17 00:00:00 2001 From: Pham Quang Nghi <133509274+nghiphaam@users.noreply.github.com> Date: Sun, 19 Apr 2026 19:16:22 +0700 Subject: [PATCH 1/4] normalize $all to $doc (uBO parity) --- src/filters/abstract_network.rs | 4 +-- src/filters/network.rs | 1 - tests/unit/filters/network.rs | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/filters/abstract_network.rs b/src/filters/abstract_network.rs index b97c9b33..83a629c2 100644 --- a/src/filters/abstract_network.rs +++ b/src/filters/abstract_network.rs @@ -59,7 +59,6 @@ pub(crate) enum NetworkFilterOption { XmlHttpRequest(bool), Websocket(bool), Font(bool), - All, } impl NetworkFilterOption { @@ -78,7 +77,6 @@ impl NetworkFilterOption { | Self::XmlHttpRequest(..) | Self::Websocket(..) | Self::Font(..) - | Self::All ) } @@ -252,7 +250,7 @@ fn parse_filter_options(raw_options: &str) -> Result, N ("websocket", negated) => NetworkFilterOption::Websocket(!negated), ("font", negated) => NetworkFilterOption::Font(!negated), ("all", true) => return Err(NetworkFilterError::NegatedAll), - ("all", false) => NetworkFilterOption::All, + ("all", false) => NetworkFilterOption::Document, (_, _) => return Err(NetworkFilterError::UnrecognisedOption), }); } diff --git a/src/filters/network.rs b/src/filters/network.rs index 4e23ca58..bf6a5cc9 100644 --- a/src/filters/network.rs +++ b/src/filters/network.rs @@ -568,7 +568,6 @@ impl NetworkFilter { apply_content_type!(FROM_WEBSOCKET, enabled) } NetworkFilterOption::Font(enabled) => apply_content_type!(FROM_FONT, enabled), - NetworkFilterOption::All => apply_content_type!(FROM_ALL_TYPES, true), } }); } diff --git a/tests/unit/filters/network.rs b/tests/unit/filters/network.rs index af9a5e35..98745f2e 100644 --- a/tests/unit/filters/network.rs +++ b/tests/unit/filters/network.rs @@ -1080,6 +1080,60 @@ mod parse_tests { } } + #[test] + fn handles_all_option_as_document() { + fn doc_only_defaults() -> NetworkFilterBreakdown { + let mut d = default_network_filter_breakdown(); + d.hostname = Some(String::from("foo.com")); + d.is_hostname_anchor = true; + d.is_plain = true; + d.from_document = true; + d.from_network_types = false; + d.from_font = false; + d.from_image = false; + d.from_media = false; + d.from_object = false; + d.from_other = false; + d.from_ping = false; + d.from_script = false; + d.from_stylesheet = false; + d.from_subdocument = false; + d.from_websocket = false; + d.from_xml_http_request = false; + d + } + + { + let filter = + NetworkFilter::parse("||foo.com$all", true, Default::default()).unwrap(); + assert_eq!(doc_only_defaults(), NetworkFilterBreakdown::from(&filter)); + } + + for opt in &["document", "doc"] { + let filter = NetworkFilter::parse( + &format!("||foo.com${opt}"), + true, + Default::default(), + ) + .unwrap(); + assert_eq!(doc_only_defaults(), NetworkFilterBreakdown::from(&filter)); + } + + { + let filter = NetworkFilter::parse( + "||foo.com$all,domain=example.com", + true, + Default::default(), + ) + .unwrap(); + let mut expected = doc_only_defaults(); + expected.opt_domains = Some(vec![crate::utils::fast_hash("example.com")]); + assert_eq!(expected, NetworkFilterBreakdown::from(&filter)); + } + + assert!(NetworkFilter::parse("||foo.com$~all", true, Default::default()).is_err()); + } + #[test] fn handles_content_type_options() { let options = vec![ From 63a2df9c5737edba380263a803badb3a9f3368bf Mon Sep 17 00:00:00 2001 From: Pham Quang Nghi <133509274+nghiphaam@users.noreply.github.com> Date: Sun, 19 Apr 2026 19:28:10 +0700 Subject: [PATCH 2/4] fully normalize $all to $doc and add tests --- src/filters/abstract_network.rs | 7 +++--- src/filters/network.rs | 2 -- tests/unit/filters/network_matchers.rs | 32 ++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/filters/abstract_network.rs b/src/filters/abstract_network.rs index 83a629c2..be326df0 100644 --- a/src/filters/abstract_network.rs +++ b/src/filters/abstract_network.rs @@ -161,11 +161,14 @@ fn parse_filter_options(raw_options: &str) -> Result, N // Check for options: option=value1|value2 let mut option_and_values = maybe_negated_option.splitn(2, '='); - let (option, value) = ( + let (raw_option_name, value) = ( option_and_values.next().unwrap(), option_and_values.next().unwrap_or_default(), ); + // $all is a uBO alias for $document. + let option = if raw_option_name == "all" { "doc" } else { raw_option_name }; + result.push(match (option, negation) { ("domain", _) | ("from", _) => { let domains: Vec<(bool, String)> = value @@ -249,8 +252,6 @@ fn parse_filter_options(raw_options: &str) -> Result, N } ("websocket", negated) => NetworkFilterOption::Websocket(!negated), ("font", negated) => NetworkFilterOption::Font(!negated), - ("all", true) => return Err(NetworkFilterError::NegatedAll), - ("all", false) => NetworkFilterOption::Document, (_, _) => return Err(NetworkFilterError::UnrecognisedOption), }); } diff --git a/src/filters/network.rs b/src/filters/network.rs index bf6a5cc9..9758293d 100644 --- a/src/filters/network.rs +++ b/src/filters/network.rs @@ -41,8 +41,6 @@ pub enum NetworkFilterError { NegatedGenericHide, #[error("negated document")] NegatedDocument, - #[error("negated all")] - NegatedAll, #[error("generichide without exception")] GenericHideWithoutException, #[error("empty redirection")] diff --git a/tests/unit/filters/network_matchers.rs b/tests/unit/filters/network_matchers.rs index 2a000f46..9a59dcb9 100644 --- a/tests/unit/filters/network_matchers.rs +++ b/tests/unit/filters/network_matchers.rs @@ -804,4 +804,36 @@ mod match_tests { "" ); } + + #[test] + fn all_option_matches_document_only() { + let filter = NetworkFilter::parse("||example.com^$all", true, Default::default()).unwrap(); + + // must match a main document request + let doc_request = request::Request::new( + "https://example.com/", + "https://example.com/", + "document", + ) + .unwrap(); + assert!(filter.matches_test(&doc_request)); + + // must NOT match an image sub-resource on the same host + let img_request = request::Request::new( + "https://example.com/image.png", + "https://example.com/", + "image", + ) + .unwrap(); + assert!(!filter.matches_test(&img_request)); + + // must NOT match a script sub-resource + let script_request = request::Request::new( + "https://example.com/app.js", + "https://example.com/", + "script", + ) + .unwrap(); + assert!(!filter.matches_test(&script_request)); + } } From 8de498aadf9396f0753b66896d76ea987b3de63f Mon Sep 17 00:00:00 2001 From: Pham Quang Nghi <133509274+nghiphaam@users.noreply.github.com> Date: Sun, 19 Apr 2026 20:24:22 +0700 Subject: [PATCH 3/4] add case for $all,image behavior --- tests/unit/filters/network_matchers.rs | 78 +++++++++++++++++--------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/tests/unit/filters/network_matchers.rs b/tests/unit/filters/network_matchers.rs index 9a59dcb9..1fee8d28 100644 --- a/tests/unit/filters/network_matchers.rs +++ b/tests/unit/filters/network_matchers.rs @@ -807,33 +807,55 @@ mod match_tests { #[test] fn all_option_matches_document_only() { - let filter = NetworkFilter::parse("||example.com^$all", true, Default::default()).unwrap(); - - // must match a main document request - let doc_request = request::Request::new( - "https://example.com/", - "https://example.com/", - "document", - ) - .unwrap(); - assert!(filter.matches_test(&doc_request)); - - // must NOT match an image sub-resource on the same host - let img_request = request::Request::new( - "https://example.com/image.png", - "https://example.com/", - "image", - ) - .unwrap(); - assert!(!filter.matches_test(&img_request)); - - // must NOT match a script sub-resource - let script_request = request::Request::new( - "https://example.com/app.js", - "https://example.com/", - "script", - ) - .unwrap(); - assert!(!filter.matches_test(&script_request)); + { + let filter = + NetworkFilter::parse("||example.com^$all", true, Default::default()).unwrap(); + + let doc_request = request::Request::new( + "https://example.com/", + "https://example.com/", + "document", + ) + .unwrap(); + assert!(filter.matches_test(&doc_request)); + + let img_request = request::Request::new( + "https://example.com/image.png", + "https://example.com/", + "image", + ) + .unwrap(); + assert!(!filter.matches_test(&img_request)); + + let script_request = request::Request::new( + "https://example.com/app.js", + "https://example.com/", + "script", + ) + .unwrap(); + assert!(!filter.matches_test(&script_request)); + } + + { + let filter = + NetworkFilter::parse("||example.com^$all,image", true, Default::default()) + .unwrap(); + + let doc_request = request::Request::new( + "https://example.com/", + "https://example.com/", + "document", + ) + .unwrap(); + assert!(filter.matches_test(&doc_request)); + + let img_request = request::Request::new( + "https://example.com/image.png", + "https://example.com/", + "image", + ) + .unwrap(); + assert!(!filter.matches_test(&img_request)); + } } } From 75ce7aa5850241832bf27b4025b657d32064a2bb Mon Sep 17 00:00:00 2001 From: Pham Quang Nghi <133509274+nghiphaam@users.noreply.github.com> Date: Sun, 19 Apr 2026 21:13:33 +0700 Subject: [PATCH 4/4] assert ~all returns NegatedDocument --- tests/unit/filters/network.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/filters/network.rs b/tests/unit/filters/network.rs index 98745f2e..184a0904 100644 --- a/tests/unit/filters/network.rs +++ b/tests/unit/filters/network.rs @@ -1131,7 +1131,7 @@ mod parse_tests { assert_eq!(expected, NetworkFilterBreakdown::from(&filter)); } - assert!(NetworkFilter::parse("||foo.com$~all", true, Default::default()).is_err()); + assert_eq!(NetworkFilter::parse("||foo.com$~all", true, Default::default()).err(), Some(NetworkFilterError::NegatedDocument)); } #[test]