Repository: tauri-apps/wry (language: rust)

Issue:
[feat] API to clear webview-managed data
> This issue has been upstreamed from https://github.com/tauri-apps/tauri/issues/6567 

### Describe the problem

My application uses cookies/localStorage on the javascript side for various purposes.

I don't see a clear way to clear all the stuff stored by the Webview engine. I could have the javascript side clear these, but there are things that this side can't clear like disk cache, etc



### Describe the solution you'd like

I want to put a native button like 'Logout' that clears all this data. 

An API on app/app_handle that clears this data might make sense. Maybe it needs to be part of exit or relaunch?

### Alternatives considered

I considered manually deleting some of the path_resolver() directories, but directories like `/Users/pttamkin/Library/HTTPStorage/{pkg}` and `/Users/pttamkin/Library/WebKit/{pkg}` aren't captured here. Also not sure I can delete these while my app is running?

### Additional context

_No response_


Discussion / hints:

Fix patch:
diff --git a/.changes/clear-browsing-data-api.md b/.changes/clear-browsing-data-api.md
new file mode 100644
index 000000000..4d48e8bbc
--- /dev/null
+++ b/.changes/clear-browsing-data-api.md
@@ -0,0 +1,5 @@
+---
+"wry": "patch"
+---
+
+Add `Webview::clear_browsing_data` method.
diff --git a/src/webview/android/kotlin/RustWebView.kt b/src/webview/android/kotlin/RustWebView.kt
index 574e79c6f..7b0de021a 100644
--- a/src/webview/android/kotlin/RustWebView.kt
+++ b/src/webview/android/kotlin/RustWebView.kt
@@ -33,6 +33,18 @@ class RustWebView(context: Context): WebView(context) {
         }
     }
 
+    fun clearAllBrowsingData() {
+        try {
+            super.context.deleteDatabase("webviewCache.db");
+            super.context.deleteDatabase("webview.db");
+            super.clearCache(true);
+            super.clearHistory();
+            super.clearFormData();
+        } catch (ignore: Exception) {
+            Logger.error("Unable to create temporary media capture file: " + ex.message)
+        }
+    }
+
     fun setAutoPlay(enable: Boolean) {
         post {
           val settings = super.getSettings()
diff --git a/src/webview/android/main_pipe.rs b/src/webview/android/main_pipe.rs
index bfd37879b..af572953e 100644
--- a/src/webview/android/main_pipe.rs
+++ b/src/webview/android/main_pipe.rs
@@ -212,6 +212,11 @@ impl MainPipe<'_> {
             load_url(env, webview.as_obj(), url, headers, false)?;
           }
         }
+        WebViewMessage::ClearAllBrowsingData => {
+          if let Some(webview) = &self.webview {
+            env.call_method(webview, "clearAllBrowsingData", "()V", &[])?;
+          }
+        }
       }
     }
     Ok(())
@@ -273,6 +278,7 @@ pub(crate) enum WebViewMessage {
   GetUrl(Sender<String>),
   Jni(Box<dyn FnOnce(JNIEnv, JObject, JObject) + Send>),
   LoadUrl(String, Option<http::HeaderMap>),
+  ClearAllBrowsingData,
 }
 
 pub(crate) struct CreateWebViewAttributes {
diff --git a/src/webview/android/mod.rs b/src/webview/android/mod.rs
index 4d4062346..bc8281c76 100644
--- a/src/webview/android/mod.rs
+++ b/src/webview/android/mod.rs
@@ -340,6 +340,11 @@ impl InnerWebView {
   pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
     MainPipe::send(WebViewMessage::LoadUrl(url.to_string(), Some(headers)));
   }
+
+  pub fn clear_all_browsing_data(&self) -> Result<()> {
+    MainPipe::send(WebViewMessage::ClearAllBrowsingData);
+    Ok(())
+  }
 }
 
 #[derive(Clone, Copy)]
diff --git a/src/webview/mod.rs b/src/webview/mod.rs
index 495e55d2d..05e244a5b 100644
--- a/src/webview/mod.rs
+++ b/src/webview/mod.rs
@@ -926,13 +926,20 @@ impl WebView {
     self.webview.set_background_color(background_color)
   }
 
+  /// Navigate to the specified url
   pub fn load_url(&self, url: &str) {
     self.webview.load_url(url)
   }
 
+  /// Navigate to the specified url using the specified headers
   pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) {
     self.webview.load_url_with_headers(url, headers)
   }
+
+  /// Clear all browsing data
+  pub fn clear_all_browsing_data(&self) -> Result<()> {
+    self.webview.clear_all_browsing_data()
+  }
 }
 
 /// An event enumeration sent to [`FileDropHandler`].
diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs
index 1f07333ed..6065e4138 100644
--- a/src/webview/webkitgtk/mod.rs
+++ b/src/webview/webkitgtk/mod.rs
@@ -522,6 +522,23 @@ impl InnerWebView {
 
     self.webview.load_request(&req);
   }
+
+  pub fn clear_all_browsing_data(&self) -> Result<()> {
+    if let Some(context) = WebViewExt::context(&*self.webview) {
+      use webkit2gtk::WebContextExt;
+      if let Some(data_manger) = context.website_data_manager() {
+        webkit2gtk::WebsiteDataManagerExtManual::clear(
+          &data_manger,
+          webkit2gtk::WebsiteDataTypes::ALL,
+          glib::TimeSpan::from_seconds(0),
+          None::<&Cancellable>,
+          |_| {},
+        );
+      }
+    }
+
+    Ok(())
+  }
 }
 
 pub fn platform_webview_version() -> Result<String> {
diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs
index 0f76ae486..8ac073aba 100644
--- a/src/webview/webview2/mod.rs
+++ b/src/webview/webview2/mod.rs
@@ -882,6 +882,22 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_
     load_url_with_headers(&self.webview, &self.env, url, headers);
   }
 
+  pub fn clear_all_browsing_data(&self) -> Result<()> {
+    let handler = ClearBrowsingDataCompletedHandler::create(Box::new(move |_| Ok(())));
+    unsafe {
+      self
+        .webview
+        .cast::<ICoreWebView2_13>()
+        .map_err(|e| Error::WebView2Error(webview2_com::Error::WindowsError(e)))?
+        .Profile()
+        .map_err(|e| Error::WebView2Error(webview2_com::Error::WindowsError(e)))?
+        .cast::<ICoreWebView2Profile2>()
+        .map_err(|e| Error::WebView2Error(webview2_com::Error::WindowsError(e)))?
+        .ClearBrowsingDataAll(&handler)
+        .map_err(|e| Error::WebView2Error(webview2_com::Error::WindowsError(e)))
+    }
+  }
+
   pub fn set_theme(&self, theme: Theme) {
     set_theme(&self.webview, theme);
   }
diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs
index 4bebab431..cd4e0c354 100644
--- a/src/webview/wkwebview/mod.rs
+++ b/src/webview/wkwebview/mod.rs
@@ -918,6 +918,18 @@ r#"Object.defineProperty(window, 'ipc', {
     self.navigate_to_url(url, Some(headers))
   }
 
+  pub fn clear_all_browsing_data(&self) -> Result<()> {
+    unsafe {
+      let config: id = msg_send![self.webview, configuration];
+      let store: id = msg_send![config, websiteDataStore];
+      let all_data_types: id = msg_send![class!(WKWebsiteDataStore), allWebsiteDataTypes];
+      let date: id = msg_send![class!(NSDate), dateWithTimeIntervalSince1970: 0.0];
+      let handler = block::ConcreteBlock::new(|| {});
+      let _: () = msg_send![store, removeDataOfTypes:all_data_types modifiedSince:date completionHandler:handler];
+    }
+    Ok(())
+  }
+
   fn navigate_to_url(&self, url: &str, headers: Option<http::HeaderMap>) {
     // Safety: objc runtime calls are unsafe
     unsafe {


Test patch: