share one web context across chrome and tabs; add --clear-cache

- the chrome webview and tab webviews used separate WebKit data managers
  pointing at the same profile directory, so both raced on the same
  hsts-storage.sqlite ('database is locked' / 'table soup_hsts_policies
  already exists' on every launch). one WebContext is now created and
  handed to every webview
- add --clear-cache: deletes the WebKit HTTP cache and service workers
  (cookies, local storage and logins are kept)
- verified live on sway/wayland: youtube.com loads without the consent
  wall and watch pages play video with hardware GL
- README: document the new flag
This commit is contained in:
Martin Dvořák 2026-10-11 19:14:28 +02:00
commit 38a7b3a626
3 changed files with 104 additions and 35 deletions

View file

@ -46,6 +46,7 @@ cargo run -- --dry-run
# compatibility
cargo run -- --ua-chrome # Chrome UA for sites that block WebKit
cargo run -- --clear-cache # drop HTTP cache + service workers (keeps logins)
# Linux backend controls
cargo run -- --linux-backend x11

View file

@ -50,6 +50,12 @@ pub struct Cli {
help = "Send a Chrome user agent instead of the native WebKit one (for sites that block WebKit)"
)]
pub ua_chrome: bool,
#[arg(
long,
help = "Delete the WebKit HTTP cache and service workers before launching (cookies/logins are kept)"
)]
pub clear_cache: bool,
}
impl Cli {

View file

@ -1097,6 +1097,11 @@ const ADBLOCK_JS: &str = r#"
pub fn launch_webkit(args: &Cli) -> Result<()> {
apply_linux_runtime_overrides(args);
if args.clear_cache {
clear_browser_cache();
}
let mut host_mode = resolve_webview_host_mode(args);
let start_url = args
@ -1118,8 +1123,8 @@ pub fn launch_webkit(args: &Cli) -> Result<()> {
"native".to_string()
};
println!(
"engine=webkit url={start_url} private={} ua={ua} linux_backend={:?} linux_disable_dmabuf={} effective_webkit_disable_dmabuf={effective_dmabuf}",
args.private, args.linux_backend, args.linux_disable_dmabuf
"engine=webkit url={start_url} private={} ua={ua} clear_cache={} linux_backend={:?} linux_disable_dmabuf={} effective_webkit_disable_dmabuf={effective_dmabuf}",
args.private, args.clear_cache, args.linux_backend, args.linux_disable_dmabuf
);
return Ok(());
}
@ -1147,10 +1152,15 @@ pub fn launch_webkit(args: &Cli) -> Result<()> {
(None, None)
};
// One context for the chrome and every tab so they share a single cookie,
// cache and HSTS store instead of racing each other on the same files.
let mut web_context = persistent_web_context(args.private);
let (chrome, effective_host_mode) = build_chrome(
&window,
&proxy,
host_mode,
web_context.as_mut(),
#[cfg(target_os = "linux")]
gtk_chrome_container.as_ref(),
)?;
@ -1178,7 +1188,7 @@ pub fn launch_webkit(args: &Cli) -> Result<()> {
} else {
None
},
web_context: persistent_web_context(args.private),
web_context,
};
open_tab(&mut state, &window, &proxy, start_url)?;
@ -1730,6 +1740,7 @@ fn build_chrome(
window: &Window,
proxy: &EventLoopProxy<UserEvent>,
host_mode: WebViewHostMode,
mut web_context: Option<&mut WebContext>,
#[cfg(target_os = "linux")] gtk_chrome_container: Option<&gtk::Box>,
) -> Result<(WebView, WebViewHostMode)> {
let chrome_rect = chrome_bounds(window, CHROME_HEIGHT_BASE, host_mode);
@ -1741,15 +1752,6 @@ fn build_chrome(
"false"
},
);
let builder = || {
let chrome_proxy = proxy.clone();
WebViewBuilder::new()
.with_html(chrome_html.clone())
.with_bounds(chrome_rect)
.with_ipc_handler(move |req| {
let _ = chrome_proxy.send_event(UserEvent::ChromeIpc(req.body().to_string()));
})
};
match host_mode {
WebViewHostMode::Window => {
@ -1758,7 +1760,7 @@ fn build_chrome(
let container = gtk_chrome_container.ok_or_else(|| {
anyhow!("Failed to create chrome webview: missing Linux GTK chrome container")
})?;
builder()
chrome_webview_builder(proxy, web_context, &chrome_html, chrome_rect)
.build_gtk(container)
.map(|webview| (webview, WebViewHostMode::Window))
.map_err(|err| {
@ -1768,13 +1770,21 @@ fn build_chrome(
#[cfg(not(target_os = "linux"))]
{
builder()
chrome_webview_builder(proxy, web_context, &chrome_html, chrome_rect)
.build(window)
.map(|webview| (webview, WebViewHostMode::Window))
.map_err(|err| anyhow!("Failed to create chrome webview: {err}"))
}
}
WebViewHostMode::Child => match builder().build_as_child(window) {
WebViewHostMode::Child => {
let attempt = chrome_webview_builder(
proxy,
web_context.as_deref_mut(),
&chrome_html,
chrome_rect,
)
.build_as_child(window);
match attempt {
Ok(webview) => Ok((webview, WebViewHostMode::Child)),
Err(err) => {
let err_text = err.to_string();
@ -1785,7 +1795,12 @@ fn build_chrome(
#[cfg(target_os = "linux")]
{
if let Some(container) = gtk_chrome_container {
return builder()
return chrome_webview_builder(
proxy,
web_context,
&chrome_html,
chrome_rect,
)
.build_gtk(container)
.map(|webview| (webview, WebViewHostMode::Window))
.map_err(|gtk_err| {
@ -1801,7 +1816,12 @@ fn build_chrome(
#[cfg(not(target_os = "linux"))]
{
return builder()
return chrome_webview_builder(
proxy,
web_context,
&chrome_html,
chrome_rect,
)
.build(window)
.map(|webview| (webview, WebViewHostMode::Window))
.map_err(|fallback_err| {
@ -1813,9 +1833,30 @@ fn build_chrome(
}
Err(anyhow!("Failed to create chrome webview: {err_text}"))
}
},
}
}
}
}
fn chrome_webview_builder<'a>(
proxy: &EventLoopProxy<UserEvent>,
web_context: Option<&'a mut WebContext>,
chrome_html: &str,
chrome_rect: Rect,
) -> WebViewBuilder<'a> {
let chrome_proxy = proxy.clone();
let mut builder = match web_context {
Some(web_context) => WebViewBuilder::with_web_context(web_context),
None => WebViewBuilder::new(),
};
builder = builder
.with_html(chrome_html)
.with_bounds(chrome_rect)
.with_ipc_handler(move |req| {
let _ = chrome_proxy.send_event(UserEvent::ChromeIpc(req.body().to_string()));
});
builder
}
fn is_child_webview_unsupported_error(err_text: &str) -> bool {
#[cfg(target_os = "linux")]
@ -2029,6 +2070,27 @@ fn tab_webview_builder<'a>(
})
}
fn clear_browser_cache() {
let mut targets = Vec::new();
if let Some(dir) = dirs::cache_dir() {
targets.push(dir.join("RustyWolf"));
}
if let Some(dir) = dirs::data_dir() {
targets.push(dir.join("RustyWolf").join("serviceworkers"));
}
let mut cleared = 0;
for dir in targets {
if dir.exists() && fs::remove_dir_all(&dir).is_ok() {
println!("cleared {}", dir.display());
cleared += 1;
}
}
if cleared == 0 {
println!("cache already empty");
}
}
fn persistent_web_context(private: bool) -> Option<WebContext> {
if private {
return None;