1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::sync::LazyLock;
pub static ADBLOCK_PATTERNS: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
vec![
// Advertisement patterns
"-advertisement.",
"-advertisement-icon.",
"-advertisement-management/",
"-advertisement/script.",
"-ads.",
"-ads/script.",
"-ad.",
"ads.js",
"gtm.js?",
"googletagmanager.com",
"ssl.google-analytics.com",
// Tracking patterns
"-tracking.",
"-tracking/script.",
".tracking",
".snowplowanalytics.snowplow",
".mountain.com",
"tracking.js",
"track.js",
"/upi/jslogger",
"otBannerSdk.js",
// Analytics scripts
"analytics.js",
"analytics.min.js",
"ob.cityrobotflower.com",
"siteintercept.qualtrics.com",
"iesnare.com",
"iovation.com",
"googletagmanager.com",
"forter.com",
"/first.iovation.com",
"/simpleads/impression",
// Specific ad and tracking domains
"googlesyndication.com",
".googlesyndication.com/safeframe/",
"adsafeprotected.com",
"cxense.com/",
".sharethis.com",
"amazon-adsystem.com",
"g.doubleclick.net",
// Explicit ignore for common scripts
"privacy-notice.js",
"insight.min.js",
]
});
#[cfg(feature = "adblock")]
pub mod engine {
use std::sync::Arc;
/// Well-known filter list URLs for callers to fetch externally.
pub struct FilterListUrls;
impl FilterListUrls {
/// EasyList — primary ad-blocking filter list.
pub const EASYLIST: &'static str =
"https://easylist.to/easylist/easylist.txt";
/// EasyPrivacy — privacy/tracking filter list.
pub const EASYPRIVACY: &'static str =
"https://easylist.to/easylist/easyprivacy.txt";
}
/// Wrapper around Brave's `adblock::Engine` with an ergonomic API.
///
/// The engine is `Send + Sync` because we exclude the `single-thread`
/// default feature of the `adblock` crate.
pub struct AdblockEngine {
inner: adblock::Engine,
}
impl AdblockEngine {
/// Build an engine from raw ABP/uBO filter rules.
pub fn from_rules<I, S>(rules: I, debug: bool) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut filter_set = adblock::lists::FilterSet::new(debug);
filter_set.add_filters(rules, adblock::lists::ParseOptions::default());
let engine = adblock::Engine::from_filter_set(filter_set, true);
Self { inner: engine }
}
/// Build an engine from the text content of an ABP filter list
/// (e.g. EasyList). Callers are responsible for fetching the content.
pub fn from_filter_list_content(content: &str, debug: bool) -> Self {
let rules: Vec<&str> = content.lines().collect();
Self::from_rules(rules, debug)
}
/// Check whether `url` should be blocked.
///
/// - `source_url`: the page URL that initiated the request.
/// - `request_type`: resource type (`"script"`, `"xhr"`, `"image"`, etc.).
pub fn should_block(&self, url: &str, source_url: &str, request_type: &str) -> bool {
match adblock::request::Request::new(url, source_url, request_type) {
Ok(request) => self.inner.check_network_request(&request).matched,
Err(_) => false,
}
}
/// Full blocker result for advanced use (redirect, exception, etc.).
pub fn check_request(
&self,
url: &str,
source_url: &str,
request_type: &str,
) -> Option<adblock::blocker::BlockerResult> {
adblock::request::Request::new(url, source_url, request_type)
.ok()
.map(|req| self.inner.check_network_request(&req))
}
/// Serialize the engine to bytes for persistence / caching.
pub fn serialize(&self) -> Vec<u8> {
self.inner.serialize()
}
/// Deserialize a previously serialized engine.
pub fn deserialize(data: &[u8]) -> Option<Self> {
let mut engine = adblock::Engine::default();
if engine.deserialize(data).is_ok() {
Some(Self { inner: engine })
} else {
None
}
}
/// Wrap in `Arc` for concurrent sharing across threads.
pub fn into_shared(self) -> Arc<Self> {
Arc::new(self)
}
}
}