diff --git a/README.md b/README.md index f716ada..d0552e9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/cli.rs b/src/cli.rs index b23d3d7..8859fa1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -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 { diff --git a/src/webview.rs b/src/webview.rs index 5ce19a0..8e3b383 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -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, host_mode: WebViewHostMode, + mut web_context: Option<&mut WebContext>, #[cfg(target_os = "linux")] gtk_chrome_container: Option<>k::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,24 +1770,37 @@ 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) { - Ok(webview) => Ok((webview, WebViewHostMode::Child)), - Err(err) => { - let err_text = err.to_string(); - if is_child_webview_unsupported_error(&err_text) { - eprintln!( - "child chrome webview unsupported on this backend; falling back to window-hosted chrome" - ); - #[cfg(target_os = "linux")] - { - if let Some(container) = gtk_chrome_container { - return builder() + 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(); + if is_child_webview_unsupported_error(&err_text) { + eprintln!( + "child chrome webview unsupported on this backend; falling back to window-hosted chrome" + ); + #[cfg(target_os = "linux")] + { + if let Some(container) = gtk_chrome_container { + return chrome_webview_builder( + proxy, + web_context, + &chrome_html, + chrome_rect, + ) .build_gtk(container) .map(|webview| (webview, WebViewHostMode::Window)) .map_err(|gtk_err| { @@ -1793,15 +1808,20 @@ fn build_chrome( "Failed to create chrome webview: child failed ({err_text}); GTK fallback failed ({gtk_err})" ) }); + } + return Err(anyhow!( + "Failed to create chrome webview: child failed ({err_text}); missing Linux GTK chrome container" + )); } - return Err(anyhow!( - "Failed to create chrome webview: child failed ({err_text}); missing Linux GTK chrome container" - )); - } - #[cfg(not(target_os = "linux"))] - { - return builder() + #[cfg(not(target_os = "linux"))] + { + return chrome_webview_builder( + proxy, + web_context, + &chrome_html, + chrome_rect, + ) .build(window) .map(|webview| (webview, WebViewHostMode::Window)) .map_err(|fallback_err| { @@ -1809,14 +1829,35 @@ fn build_chrome( "Failed to create chrome webview: child failed ({err_text}); window fallback failed ({fallback_err})" ) }); + } } + Err(anyhow!("Failed to create chrome webview: {err_text}")) } - Err(anyhow!("Failed to create chrome webview: {err_text}")) } - }, + } } } +fn chrome_webview_builder<'a>( + proxy: &EventLoopProxy, + 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 { if private { return None;