#[cfg(feature = "blocker")]
mod engine {
use adblock::lists::{FilterFormat, FilterSet, ParseOptions};
use adblock::request::Request;
use adblock::Engine;
use std::cell::OnceCell;
const BUILTIN_RULES: &str = "
||google-analytics.com^
||googletagmanager.com^
||google-tag-manager.com^
||doubleclick.net^
||googlesyndication.com^
||googleadservices.com^
||adservice.google.com^
||facebook.com/tr^
||connect.facebook.net^
||scorecardresearch.com^
||quantserve.com^
||quantcount.com^
||adsystem.amazon.com^
||amazon-adsystem.com^
||criteo.com^
||criteo.net^
||outbrain.com^
||taboola.com^
||adnxs.com^
||adsrvr.org^
||rubiconproject.com^
||pubmatic.com^
||openx.net^
||casalemedia.com^
||cookielaw.org^
||onetrust.com^
||trustarc.com^
||doubleverify.com^
||moatads.com^
||segment.com/v1^
||segment.io^
||mixpanel.com^
||hotjar.com^
||fullstory.com^
||cdn.permutive.com^
||sentry.io^
||bugsnag.com^
||newrelic.com^
||clarity.ms^
||intercom.io^
||intercomcdn.com^
||snapchat.com/p^
||tiktok.com/api/v1/web/report^
||analytics.tiktok.com^
||hs-analytics.net^
||hs-scripts.com^
||stats.wp.com^
||wordpress.com/_static^
";
thread_local! {
static ENGINE: OnceCell<Option<Engine>> = const { OnceCell::new() };
}
fn build_engine() -> Option<Engine> {
if std::env::var("BROWSER_OXIDE_BLOCKER").is_err() {
return None;
}
let mut rules = String::from(BUILTIN_RULES);
if let Ok(path) = std::env::var("BROWSER_OXIDE_BLOCKER_RULES") {
match std::fs::read_to_string(&path) {
Ok(extra) => {
rules.push('\n');
rules.push_str(&extra);
}
Err(e) => {
eprintln!(
"[blocker] WARN: BROWSER_OXIDE_BLOCKER_RULES={} failed to read: {}",
path, e
);
}
}
}
let mut filter_set = FilterSet::new(false);
let parse_opts = ParseOptions {
format: FilterFormat::Standard,
..Default::default()
};
filter_set.add_filter_list(rules, parse_opts);
Some(Engine::new_with_filter_set(filter_set))
}
fn with_engine<R>(f: impl FnOnce(Option<&Engine>) -> R) -> R {
ENGINE.with(|cell| {
let opt = cell.get_or_init(build_engine);
f(opt.as_ref())
})
}
pub fn should_block(url: &str, source_url: &str, request_type: &str) -> bool {
with_engine(|opt_eng| {
let Some(eng) = opt_eng else { return false };
let req = match Request::new(url, source_url, request_type, "") {
Ok(r) => r,
Err(_) => return false,
};
eng.check_network_request(&req).should_block()
})
}
}
pub fn classify_request_type(url: &str, hint: Option<&str>) -> &'static str {
if let Some(h) = hint {
return match h {
"image" | "img" => "image",
"script" | "js" => "script",
"stylesheet" | "css" => "stylesheet",
"xhr" | "fetch" => "xmlhttprequest",
"document" => "document",
"subdocument" => "subdocument",
"media" => "media",
"font" => "font",
"websocket" => "websocket",
_ => "other",
};
}
let lower = url.to_lowercase();
if lower.ends_with(".js") || lower.contains(".js?") {
"script"
} else if lower.ends_with(".css") || lower.contains(".css?") {
"stylesheet"
} else if lower.ends_with(".png")
|| lower.ends_with(".jpg")
|| lower.ends_with(".jpeg")
|| lower.ends_with(".gif")
|| lower.ends_with(".webp")
|| lower.ends_with(".svg")
{
"image"
} else {
"xmlhttprequest"
}
}
#[cfg(feature = "blocker")]
pub fn should_block(url: &str, source_url: &str, request_type: &str) -> bool {
engine::should_block(url, source_url, request_type)
}
#[cfg(not(feature = "blocker"))]
pub fn should_block(_url: &str, _source_url: &str, _request_type: &str) -> bool {
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_off_means_does_not_block() {
assert!(!should_block(
"https://www.google-analytics.com/analytics.js",
"https://www.example.com/",
"script"
));
}
#[cfg(feature = "blocker")]
#[test]
#[ignore]
fn blocks_known_tracker_when_enabled() {
std::env::set_var("BROWSER_OXIDE_BLOCKER", "1");
assert!(should_block(
"https://www.google-analytics.com/analytics.js",
"https://www.example.com/",
"script"
));
}
#[cfg(feature = "blocker")]
#[test]
#[ignore]
fn allows_legitimate_request_when_enabled() {
std::env::set_var("BROWSER_OXIDE_BLOCKER", "1");
assert!(!should_block(
"https://www.example.com/static/main.js",
"https://www.example.com/",
"script"
));
}
}