diff --git a/examples/custom_user_data.rs b/examples/custom_user_data.rs new file mode 100644 index 000000000..8355a9bff --- /dev/null +++ b/examples/custom_user_data.rs @@ -0,0 +1,27 @@ +use std::{fs, path::PathBuf}; +use wry::{Application, Attributes, Result}; + +fn main() -> Result<()> { + let mut app = Application::new()?; + + // Use a sample directory at the root of the project + let mut test_path = PathBuf::from("./target/webview_data"); + // The directory need to exist or the Webview will panic + fs::create_dir_all(&test_path)?; + // We need an absoulte path for the webview + test_path = fs::canonicalize(&test_path)?; + // The directory need to exist or the Webview will panic + println!("Webview storage path: {:#?}", &test_path); + + let attributes = Attributes { + url: Some("https://tauri.studio/".to_string()), + title: String::from("Hello World!"), + // Currently supported only on Windows + user_data_path: Some(test_path), + ..Default::default() + }; + + app.add_window(attributes)?; + app.run(); + Ok(()) +} diff --git a/src/application/attributes.rs b/src/application/attributes.rs index 370369272..fec8cd2ec 100644 --- a/src/application/attributes.rs +++ b/src/application/attributes.rs @@ -1,6 +1,9 @@ use crate::{Result, RpcRequest, RpcResponse, WindowProxy}; -use std::{fs::read, path::Path}; +use std::{ + fs::read, + path::{Path, PathBuf}, +}; /// The RPC handler to Communicate between the host Rust code and Javascript on webview. /// @@ -191,6 +194,11 @@ pub struct Attributes { /// /// The default is an empty vector. pub initialization_scripts: Vec, + + /// Webview user data path. + /// + /// The default is `None`. + pub user_data_path: Option, } impl Attributes { @@ -220,6 +228,7 @@ impl Attributes { transparent: self.transparent, url: self.url, initialization_scripts: self.initialization_scripts, + user_data_path: self.user_data_path, }, ) } @@ -249,6 +258,7 @@ impl Default for Attributes { skip_taskbar: false, url: None, initialization_scripts: vec![], + user_data_path: None, } } } @@ -278,4 +288,5 @@ pub(crate) struct InnerWebViewAttributes { pub transparent: bool, pub url: Option, pub initialization_scripts: Vec, + pub user_data_path: Option, } diff --git a/src/application/general.rs b/src/application/general.rs index 5b2f76643..bbb8cc571 100644 --- a/src/application/general.rs +++ b/src/application/general.rs @@ -354,7 +354,10 @@ fn _create_webview( ) -> Result { let window_id = window.id(); - let mut webview = WebViewBuilder::new(window)?.transparent(attributes.transparent); + let mut webview = WebViewBuilder::new(window)? + .transparent(attributes.transparent) + .user_data_path(attributes.user_data_path); + for js in attributes.initialization_scripts { webview = webview.initialize_script(&js); } diff --git a/src/webview/linux/mod.rs b/src/webview/linux/mod.rs index 60b7798ea..55368c6cf 100644 --- a/src/webview/linux/mod.rs +++ b/src/webview/linux/mod.rs @@ -5,7 +5,7 @@ use crate::{ Error, FileDropHandler, Result, RpcHandler, }; -use std::rc::Rc; +use std::{path::PathBuf, rc::Rc}; use gdk::RGBA; use gio::Cancellable; @@ -33,6 +33,7 @@ impl WV for InnerWebView { custom_protocol: Option<(String, F)>, rpc_handler: Option, file_drop_handler: Option, + _user_data_path: Option, ) -> Result { // Webview widget let manager = UserContentManager::new(); diff --git a/src/webview/macos/mod.rs b/src/webview/macos/mod.rs index 62bfb068e..c4cd24668 100644 --- a/src/webview/macos/mod.rs +++ b/src/webview/macos/mod.rs @@ -9,6 +9,7 @@ use file_drop::{add_file_drop_methods, set_file_drop_handler}; use std::{ ffi::{c_void, CStr}, os::raw::c_char, + path::PathBuf, ptr::null, slice, str, }; @@ -42,6 +43,7 @@ impl WV for InnerWebView { custom_protocol: Option<(String, F)>, rpc_handler: Option, file_drop_handler: Option, + _user_data_path: Option, ) -> Result { // Function for rpc handler extern "C" fn did_receive(this: &Object, _: Sel, _: id, msg: id) { diff --git a/src/webview/mod.rs b/src/webview/mod.rs index cecdbbb54..008123fb9 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -17,7 +17,10 @@ use win::*; use crate::{Error, FileDropHandler, Result}; -use std::sync::mpsc::{channel, Receiver, Sender}; +use std::{ + path::PathBuf, + sync::mpsc::{channel, Receiver, Sender}, +}; use serde_json::Value; use url::Url; @@ -85,6 +88,7 @@ pub struct WebViewBuilder { custom_protocol: Option<(String, Box Result>>)>, rpc_handler: Option, file_drop_handler: Option, + user_data_path: Option, } impl WebViewBuilder { @@ -102,6 +106,7 @@ impl WebViewBuilder { custom_protocol: None, rpc_handler: None, file_drop_handler: None, + user_data_path: None, }) } @@ -120,6 +125,13 @@ impl WebViewBuilder { self } + /// Whether the WebView window should have a custom user data path. This is usefull in Windows + /// when a bundled application can't have the webview data inside `Program Files`. + pub fn user_data_path(mut self, user_data_path: Option) -> Self { + self.user_data_path = user_data_path; + self + } + /// Create a [`Dispatcher`] to send evaluation scripts to the WebView. [`WebView`] is not thread /// safe because it must be run on the main thread who creates it. [`Dispatcher`] can let you /// send the scripts from other threads. @@ -213,6 +225,7 @@ impl WebViewBuilder { self.custom_protocol, self.rpc_handler, self.file_drop_handler, + self.user_data_path, )?; Ok(WebView { window: self.window, @@ -251,7 +264,16 @@ impl WebView { pub fn new_with_configs(window: Window, transparent: bool) -> Result { let picky_none: Option<(String, Box Result>>)> = None; - let webview = InnerWebView::new(&window, vec![], None, transparent, picky_none, None, None)?; + let webview = InnerWebView::new( + &window, + vec![], + None, + transparent, + picky_none, + None, + None, + None, + )?; let (tx, rx) = channel(); @@ -327,6 +349,7 @@ pub(crate) trait WV: Sized { custom_protocol: Option<(String, F)>, rpc_handler: Option, file_drop_handler: Option, + user_data_path: Option, ) -> Result; fn eval(&self, js: &str) -> Result<()>; diff --git a/src/webview/win/mod.rs b/src/webview/win/mod.rs index 822bfe4f6..2489e8146 100644 --- a/src/webview/win/mod.rs +++ b/src/webview/win/mod.rs @@ -7,7 +7,7 @@ use crate::{ use file_drop::FileDropController; -use std::{os::raw::c_void, rc::Rc}; +use std::{os::raw::c_void, path::PathBuf, rc::Rc}; use once_cell::unsync::OnceCell; use url::Url; @@ -37,6 +37,7 @@ impl WV for InnerWebView { custom_protocol: Option<(String, F)>, rpc_handler: Option, file_drop_handler: Option, + user_data_path: Option, ) -> Result { let hwnd = window.hwnd() as HWND; @@ -46,8 +47,19 @@ impl WV for InnerWebView { let file_drop_controller: Rc> = Rc::new(OnceCell::new()); let file_drop_controller_clone = file_drop_controller.clone(); + let webview_builder: webview2::EnvironmentBuilder; + let user_data_path_provided: PathBuf; + + if user_data_path.is_some() { + user_data_path_provided = user_data_path.unwrap(); + webview_builder = + webview2::EnvironmentBuilder::new().with_user_data_folder(&user_data_path_provided); + } else { + webview_builder = webview2::EnvironmentBuilder::new(); + } + // Webview controller - webview2::EnvironmentBuilder::new().build(move |env| { + webview_builder.build(move |env| { let env = env?; let env_ = env.clone(); env.create_controller(hwnd, move |controller| {