diff --git a/.changes/linux-request-queue-feature.md b/.changes/linux-request-queue-feature.md new file mode 100644 index 000000000..a7b3555f7 --- /dev/null +++ b/.changes/linux-request-queue-feature.md @@ -0,0 +1,7 @@ +--- +wry: minor +--- + +Added `linux-request-queue` feature flag (enabled by default, which matches previous behavior). +The queue prevents an unknown concurrency bug with loading multiple URIs at the same time on webkit2gtk. +But it can introduce a deadlock situation under certain conditions (https://github.com/tauri-apps/tauri/issues/12589) and it prevents parallelization of request loading. diff --git a/Cargo.toml b/Cargo.toml index 4e70dd150..db78bd8c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["/.changes", "/.github", "/audits", "/wry-logo.svg"] [package.metadata.docs.rs] no-default-features = true -features = ["drag-drop", "protocol", "os-webview"] +features = ["drag-drop", "protocol", "os-webview", "linux-request-queue"] targets = [ "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", @@ -26,7 +26,7 @@ rustc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["drag-drop", "protocol", "os-webview", "x11"] +default = ["drag-drop", "protocol", "os-webview", "x11", "linux-request-queue"] serde = ["dpi/serde"] drag-drop = [] protocol = [] @@ -34,6 +34,7 @@ devtools = [] transparent = [] fullscreen = [] linux-body = ["webkit2gtk/v2_40", "os-webview"] +linux-request-queue = [] mac-proxy = [] os-webview = [ "javascriptcore-rs", diff --git a/README.md b/README.md index 66e6bef74..ef8199eb2 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,10 @@ Wry uses a set of feature flags to toggle several advanced features. libraries and prevent from building documentation on doc.rs fails. - `linux-body`: Enables body support of custom protocol request on Linux. Requires webkit2gtk v2.40 or above. +- `linux-request-queue` (default): Enables a request queue for webviews on Linux. + This Prevents an unknown concurrency bug with loading multiple URIs at the same time on webkit2gtk. + You might want to disable it because it can affect request performance, lead to a deadlock under certain conditions + and it might no longer be needed with newer WebKitGTK versions. - `tracing`: enables [`tracing`] for `evaluate_script`, `ipc_handler` and `custom_protocols. ### Partners diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 8232fadec..fccabf5bb 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -362,8 +362,13 @@ impl InnerWebView { // Navigation if let Some(url) = attributes.url { - web_context.queue_load_uri(w.webview.clone(), url, attributes.headers); - web_context.flush_queue_loader(); + #[cfg(feature = "linux-request-queue")] + { + web_context.queue_load_uri(w.webview.clone(), url, attributes.headers); + web_context.flush_queue_loader(); + } + #[cfg(not(feature = "linux-request-queue"))] + web_context.load_uri(w.webview.clone(), url, attributes.headers); } else if let Some(html) = attributes.html { w.webview.load_html(&html, None); } diff --git a/src/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs index 42321ace0..930c9ccdf 100644 --- a/src/webkitgtk/web_context.rs +++ b/src/webkitgtk/web_context.rs @@ -29,6 +29,7 @@ use webkit2gtk::{ #[derive(Debug)] pub struct WebContextImpl { context: WebContext, + #[cfg(feature = "linux-request-queue")] webview_uri_loader: Rc, automation: bool, app_info: Option, @@ -83,6 +84,7 @@ impl WebContextImpl { Self { context, automation, + #[cfg(feature = "linux-request-queue")] webview_uri_loader: Rc::default(), app_info: Some(app_info), } @@ -110,14 +112,20 @@ pub trait WebContextExt { where F: Fn(crate::WebViewId, Request>, RequestAsyncResponder) + 'static; + /// Directly loads a URI for a [`WebView`] bypassing the [`WebViewUriLoader`]. + #[cfg(not(feature = "linux-request-queue"))] + fn load_uri(&self, webview: WebView, url: String, headers: Option); + /// Add a [`WebView`] to the queue waiting to be opened. /// /// See the [`WebViewUriLoader`] for more information. + #[cfg(feature = "linux-request-queue")] fn queue_load_uri(&self, webview: WebView, url: String, headers: Option); /// Flush all queued [`WebView`]s waiting to load a uri. /// /// See the [`WebViewUriLoader`] for more information. + #[cfg(feature = "linux-request-queue")] fn flush_queue_loader(&self); /// If the context allows automation. @@ -275,10 +283,32 @@ impl WebContextExt for super::WebContext { Ok(()) } + #[cfg(not(feature = "linux-request-queue"))] + fn load_uri(&self, webview: WebView, uri: String, headers: Option) { + if let Some(headers) = headers { + let req = URIRequest::builder().uri(&uri).build(); + + if let Some(ref mut req_headers) = req.http_headers() { + for (header, value) in headers.iter() { + req_headers.append( + header.to_string().as_str(), + value.to_str().unwrap_or_default(), + ); + } + } + + webview.load_request(&req); + } else { + webview.load_uri(&uri); + } + } + + #[cfg(feature = "linux-request-queue")] fn queue_load_uri(&self, webview: WebView, url: String, headers: Option) { self.os.webview_uri_loader.push(webview, url, headers) } + #[cfg(feature = "linux-request-queue")] fn flush_queue_loader(&self) { self.os.webview_uri_loader.clone().flush() } @@ -409,12 +439,14 @@ struct WebviewUriRequest { /// FIXME: We think this may be an underlying concurrency bug in webkit2gtk as the usual ways of /// fixing threading issues are not working. Ideally, the locks are not needed if we can understand /// the true cause of the bug. +#[cfg(feature = "linux-request-queue")] #[derive(Debug, Default)] struct WebViewUriLoader { lock: AtomicBool, queue: Mutex>, } +#[cfg(feature = "linux-request-queue")] impl WebViewUriLoader { /// Check if the lock is in use. fn is_locked(&self) -> bool {