add base: browser window, hints, history, cookies, adblock
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
//! C API over adblock-rust's network blocking. Declared in `att_adblock.h`.
|
||||
//!
|
||||
//! Every entry point catches panics: unwinding into C++ is undefined behaviour,
|
||||
//! and a bad filter list or URL should never take the browser down.
|
||||
|
||||
use std::ffi::{c_char, CStr};
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::ptr;
|
||||
|
||||
use adblock::lists::{FilterSet, ParseOptions};
|
||||
use adblock::request::Request;
|
||||
use adblock::Engine;
|
||||
|
||||
pub struct AttAdblockEngine(Engine);
|
||||
|
||||
fn into_raw(engine: Engine) -> *mut AttAdblockEngine {
|
||||
Box::into_raw(Box::new(AttAdblockEngine(engine)))
|
||||
}
|
||||
|
||||
unsafe fn bytes<'a>(data: *const u8, len: usize) -> &'a [u8] {
|
||||
if data.is_null() || len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(data, len)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn str_arg<'a>(s: *const c_char) -> Option<&'a str> {
|
||||
if s.is_null() {
|
||||
None
|
||||
} else {
|
||||
CStr::from_ptr(s).to_str().ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Compiles an engine from filter list text (Adblock Plus / uBlock syntax;
|
||||
/// several lists may simply be concatenated). Returns null on failure.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_engine_from_rules(
|
||||
rules: *const u8,
|
||||
len: usize,
|
||||
) -> *mut AttAdblockEngine {
|
||||
let text = String::from_utf8_lossy(bytes(rules, len)).into_owned();
|
||||
catch_unwind(move || {
|
||||
let mut set = FilterSet::new(false);
|
||||
set.add_filter_list(text, ParseOptions::default());
|
||||
into_raw(Engine::new_with_filter_set(set))
|
||||
})
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Loads an engine saved by `att_adblock_engine_serialize`. Returns null if
|
||||
/// the data is corrupt or was written by an incompatible adblock-rust version.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_engine_deserialize(
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
) -> *mut AttAdblockEngine {
|
||||
let data = bytes(data, len);
|
||||
catch_unwind(|| {
|
||||
let mut engine = Engine::default();
|
||||
match engine.deserialize(data) {
|
||||
Ok(()) => into_raw(engine),
|
||||
Err(_) => ptr::null_mut(),
|
||||
}
|
||||
})
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Serializes the engine. The returned buffer of `*out_len` bytes must be
|
||||
/// released with `att_adblock_buffer_free`. Returns null on failure.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_engine_serialize(
|
||||
engine: *const AttAdblockEngine,
|
||||
out_len: *mut usize,
|
||||
) -> *mut u8 {
|
||||
if engine.is_null() || out_len.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let engine = &(*engine).0;
|
||||
match catch_unwind(AssertUnwindSafe(|| engine.serialize())) {
|
||||
Ok(data) => {
|
||||
let data = data.into_boxed_slice();
|
||||
*out_len = data.len();
|
||||
Box::into_raw(data) as *mut u8
|
||||
}
|
||||
Err(_) => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_buffer_free(data: *mut u8, len: usize) {
|
||||
if !data.is_null() {
|
||||
drop(Box::from_raw(ptr::slice_from_raw_parts_mut(data, len)));
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_engine_free(engine: *mut AttAdblockEngine) {
|
||||
if !engine.is_null() {
|
||||
drop(Box::from_raw(engine));
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a request for `url`, made by a page at `source_url`, should be
|
||||
/// blocked. `request_type` uses the webRequest names ("script", "image",
|
||||
/// "xhr", "sub_frame", ...) and `method` an HTTP method, in any case. Safe to
|
||||
/// call from several threads at once.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn att_adblock_should_block(
|
||||
engine: *const AttAdblockEngine,
|
||||
url: *const c_char,
|
||||
source_url: *const c_char,
|
||||
request_type: *const c_char,
|
||||
method: *const c_char,
|
||||
) -> bool {
|
||||
if engine.is_null() {
|
||||
return false;
|
||||
}
|
||||
let engine = &(*engine).0;
|
||||
let (Some(url), Some(source_url), Some(request_type)) =
|
||||
(str_arg(url), str_arg(source_url), str_arg(request_type))
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let method = str_arg(method).unwrap_or("GET").to_ascii_lowercase();
|
||||
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
match Request::new(url, source_url, request_type, &method) {
|
||||
Ok(request) => engine.check_network_request(&request).should_block(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CString;
|
||||
|
||||
fn check(engine: *const AttAdblockEngine, url: &str, source: &str, ty: &str) -> bool {
|
||||
let (u, s, t, m) = (
|
||||
CString::new(url).unwrap(),
|
||||
CString::new(source).unwrap(),
|
||||
CString::new(ty).unwrap(),
|
||||
CString::new("GET").unwrap(),
|
||||
);
|
||||
unsafe { att_adblock_should_block(engine, u.as_ptr(), s.as_ptr(), t.as_ptr(), m.as_ptr()) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blocks_exceptions_and_round_trips() {
|
||||
let rules = b"||ads.example.com^\n/banner/*$image\n@@||ads.example.com/ok.js\n";
|
||||
unsafe {
|
||||
let engine = att_adblock_engine_from_rules(rules.as_ptr(), rules.len());
|
||||
assert!(!engine.is_null());
|
||||
|
||||
let page = "https://news.example.org/";
|
||||
assert!(check(engine, "https://ads.example.com/x.js", page, "script"));
|
||||
assert!(!check(engine, "https://ads.example.com/ok.js", page, "script"));
|
||||
assert!(check(engine, "https://cdn.example.org/banner/a.png", page, "image"));
|
||||
assert!(!check(engine, "https://cdn.example.org/banner/a.js", page, "script"));
|
||||
assert!(!check(engine, "https://news.example.org/app.js", page, "script"));
|
||||
|
||||
let mut len = 0;
|
||||
let data = att_adblock_engine_serialize(engine, &mut len);
|
||||
assert!(!data.is_null() && len > 0);
|
||||
let restored = att_adblock_engine_deserialize(data, len);
|
||||
att_adblock_buffer_free(data, len);
|
||||
assert!(!restored.is_null());
|
||||
assert!(check(restored, "https://ads.example.com/x.js", page, "script"));
|
||||
assert!(!check(restored, "https://ads.example.com/ok.js", page, "script"));
|
||||
|
||||
let junk = b"not an engine";
|
||||
assert!(att_adblock_engine_deserialize(junk.as_ptr(), junk.len()).is_null());
|
||||
|
||||
att_adblock_engine_free(engine);
|
||||
att_adblock_engine_free(restored);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user