tauri-apps / wry

Cross-platform WebView library in Rust for Tauri.
Apache License 2.0
3.28k stars 243 forks source link

feat(macos): add option to set a DataStoreIdentifier for WKWebView #1226

Closed santikid closed 1 month ago

santikid commented 1 month ago

Both WebView2 for Windows and WebkitGTK support setting a custom data directory path to store cookies, IndexedDB and other data (set in src/web_context.rs -> WebContextData). On macOS, WKWebView does not support this so setting data_directory in wry or tauri has no effect.

This PR adds the ability to specify a UUID that is used to call dataStoreForIdentifier, which creates a data directory in ~/Library/WebKit/[application]/WebsiteDataStore/[UUID] instead of using the default ~/Library/WebKit/[application]/WebsiteData .

Discussion: #1198

tauri-apps/tauri#8637

santikid commented 1 month ago

converted to draft because this needs to be added to/tested on iOS as well

santikid commented 1 month ago

just tried on iOS with the newest tauri beta, seems to work well

santikid commented 1 month ago

It may be a little confusing if a data_directory is specified and there is nothing at the given path, but since as you said this is only an issue on macOS that's probably a good trade-off.

Do you think this can be done without adding any external crates for hashing? The built-in std::hash::DefaultHasher is not guaranteed to stay consistent over releases.

I guess some non-cryptographic hash implementation could be vendored?

amrbashir commented 1 month ago

I see, maybe it is fine to just add a separate option as you proposed and downstream tauri could choose to hash the data_directory and reuse it.

pewsheen commented 1 month ago

datastoreforidentifier seems support macOS 14+ and iOS 17+ only. Should we do a version check or add the version limitation to the document?

cc @FabianLars

FabianLars commented 1 month ago

if it crashes on earlier macos version then imo ideally both, a version check and documentation.

santikid commented 1 month ago

It does crash on macOS < 14. For macOS, a version check could be done with NSAppKitVersionNumber in cocoa::appkit, but I couldn't figure out how to do it on iOS.

Alternatively, we could just check for the initWithIdentifier method in _WKWebsiteDataStoreConfiguration.h. I don't fully get why it doesn't exist on WKWebsiteDataStore though.

let class = class!(_WKWebsiteDataStoreConfiguration);
println!("{:?}", class.instance_methods().iter().map(|x| x.name()).collect::<Vec<_>>());
let method = class.instance_method(Sel::register("initWithIdentifier:"));
println!("initWithIdentifier available: {}", method.is_some());

Would appreciate some input on this.

EDIT: Turns out the above snippet works on macOS 13.0, returning false for initWithIdentifier.is_some, but is broken on 13.6, where it prints true even though the API isn't there yet.

pewsheen commented 1 month ago

It does crash on macOS < 14. For macOS, a version check could be done with NSAppKitVersionNumber in cocoa::appkit, but I couldn't figure out how to do it on iOS.

Alternatively, we could just check for the initWithIdentifier method in _WKWebsiteDataStoreConfiguration.h. I don't fully get why it doesn't exist on WKWebsiteDataStore though.

let class = class!(_WKWebsiteDataStoreConfiguration);
println!("{:?}", class.instance_methods().iter().map(|x| x.name()).collect::<Vec<_>>());
let method = class.instance_method(Sel::register("initWithIdentifier:"));
println!("initWithIdentifier available: {}", method.is_some());

Would appreciate some input on this.

EDIT: Turns out the above snippet works on macOS 13.0, returning false for initWithIdentifier.is_some, but is broken on 13.6, where it prints true even though the API isn't there yet.

Thanks for the testing! It's so confusing that the API existed, but you can't use it :\

I think we could use NSOperatingSystemVersion and combine #[cfg(target_os = "ios")], #[cfg(target_os = "macos")] to check iOS and macOS version

This is a code snippet from another PR:

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,
     )
   }
 }

#[cfg(target_os = "macos")]
if operating_system_version().0 >= 14 {
  todo!();
}
#[cfg(target_os = "ios")]
if operating_system_version().0 >= 17 {
  todo!();
}
santikid commented 1 month ago

Thanks @pewsheen !

It's so confusing that the API existed, but you can't use it :\

My understanding of "darwin" APIs / objc is pretty limited, but this does seem very strange. 😅