diff --git a/src/lib.rs b/src/lib.rs index 139f9d91c..49634791c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1032,6 +1032,38 @@ impl<'a> WebViewBuilder<'a> { } } +#[cfg(any(target_os = "macos", target_os = "ios",))] +#[derive(Clone)] +pub(crate) struct PlatformSpecificWebViewAttributes { + data_store_identifier: Option<[u8; 16]>, +} + +#[cfg(any(target_os = "macos", target_os = "ios",))] +impl Default for PlatformSpecificWebViewAttributes { + fn default() -> Self { + Self { + data_store_identifier: None, + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios",))] +pub trait WebViewBuilderExtDarwin { + /// Initialize the WebView with a custom data store identifier. + /// Can be used as a replacement for data_directory not being available in WKWebView. + /// + /// - **macOS / iOS**: Available on macOS >= 14 and iOS >= 17 + fn with_data_store_identifier(self, identifier: [u8; 16]) -> Self; +} + +#[cfg(any(target_os = "macos", target_os = "ios",))] +impl WebViewBuilderExtDarwin for WebViewBuilder<'_> { + fn with_data_store_identifier(mut self, identifier: [u8; 16]) -> Self { + self.platform_specific.data_store_identifier = Some(identifier); + self + } +} + #[cfg(windows)] #[derive(Clone)] pub(crate) struct PlatformSpecificWebViewAttributes { @@ -1653,8 +1685,6 @@ pub enum PageLoadEvent { target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "ios", - target_os = "macos", ))] #[derive(Default)] pub(crate) struct PlatformSpecificWebViewAttributes; diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 658bbad2c..209c43922 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -135,7 +135,7 @@ impl InnerWebView { fn new_ns_view( ns_view: id, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, is_child: bool, ) -> Result { @@ -332,11 +332,28 @@ impl InnerWebView { let config: id = msg_send![class!(WKWebViewConfiguration), new]; let mut protocol_ptrs = Vec::new(); - // Incognito mode - let data_store: id = if attributes.incognito { - msg_send![class!(WKWebsiteDataStore), nonPersistentDataStore] - } else { - msg_send![class!(WKWebsiteDataStore), defaultDataStore] + let os_version = util::operating_system_version(); + + #[cfg(target_os = "macos")] + let custom_data_store_available = os_version.0 >= 14; + + #[cfg(target_os = "ios")] + let custom_data_store_available = os_version.0 >= 17; + + let data_store: id = match ( + attributes.incognito, + custom_data_store_available, + pl_attrs.data_store_identifier, + ) { + // incognito has priority + (true, _, _) => msg_send![class!(WKWebsiteDataStore), nonPersistentDataStore], + // if data_store_identifier is given and custom data stores are available, use custom store + (false, true, Some(data_store)) => { + let ns_uuid = NSUUID::new(&data_store); + msg_send![class!(WKWebsiteDataStore), dataStoreForIdentifier:ns_uuid.0] + } + // default data store + _ => msg_send![class!(WKWebsiteDataStore), defaultDataStore], }; for (name, function) in attributes.custom_protocols { @@ -1321,6 +1338,21 @@ impl Drop for InnerWebView { const UTF8_ENCODING: usize = 4; +struct NSUUID(id); + +impl NSUUID { + fn new(data: &[u8; 16]) -> Self { + NSUUID(unsafe { + let ns_uuid: id = msg_send![class!(NSUUID), alloc]; + let ns_uuid: id = msg_send![ns_uuid, initWithUUIDBytes:data.as_ptr()]; + + let _: () = msg_send![ns_uuid, autorelease]; + + ns_uuid + }) + } +} + struct NSString(id); impl NSString { diff --git a/src/wkwebview/util.rs b/src/wkwebview/util.rs index bca97787b..8ddfd8a7c 100644 --- a/src/wkwebview/util.rs +++ b/src/wkwebview/util.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use cocoa::{base::id, foundation::NSOperatingSystemVersion}; use std::sync::atomic::{AtomicU32, Ordering}; pub struct Counter(AtomicU32); @@ -16,3 +17,15 @@ impl Counter { self.0.fetch_add(1, Ordering::Relaxed) } } + +pub fn operating_system_version() -> (u64, u64, u64) { + unsafe { + let process_info: id = msg_send![class!(NSProcessInfo), processInfo]; + let version: NSOperatingSystemVersion = msg_send![process_info, operatingSystemVersion]; + ( + version.majorVersion, + version.minorVersion, + version.patchVersion, + ) + } +}