diff --git a/.changes/as_incognito.md b/.changes/as_incognito.md new file mode 100644 index 000000000..78d827f7a --- /dev/null +++ b/.changes/as_incognito.md @@ -0,0 +1,5 @@ +--- +"wry": patch +--- + +Adds a way to launch a WebView as incognito through a new API at WebViewBuilder named as `with_incognito`. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 06f77738c..4717df7e9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ Cargo.lock gh-pages .DS_Store examples/test_video.mp4 -.vscode/ \ No newline at end of file +.vscode/ diff --git a/examples/hello_world.rs b/examples/hello_world.rs index fa0f21580..f74d27625 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -17,7 +17,8 @@ fn main() -> wry::Result<()> { .with_title("Hello World") .build(&event_loop)?; let _webview = WebViewBuilder::new(window)? - .with_url("https://html5test.com")? + .with_url("https://www.netflix.com/browse")? + // .with_incognito(true) .build()?; event_loop.run(move |event, _, control_flow| { diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 30bf85afb..495e55d2d 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -228,6 +228,14 @@ pub struct WebViewAttributes { /// Set a handler closure to process the change of the webview's document title. pub document_title_changed_handler: Option>, + /// Run the WebView with incognito mode. Note that WebContext will be ingored if incognito is + /// enabled. + /// + /// ## Platform-specific: + /// + /// - **Android:** Unsupported yet. + pub incognito: bool, + /// Whether all media can be played without user interaction. pub autoplay: bool, } @@ -259,6 +267,7 @@ impl Default for WebViewAttributes { accept_first_mouse: false, back_forward_navigation_gestures: false, document_title_changed_handler: None, + incognito: false, autoplay: true, } } @@ -625,6 +634,17 @@ impl<'a> WebViewBuilder<'a> { self } + /// Run the WebView with incognito mode. Note that WebContext will be ingored if incognito is + /// enabled. + /// + /// ## Platform-specific: + /// + /// - **Android:** Unsupported yet. + pub fn with_incognito(mut self, incognito: bool) -> Self { + self.webview.incognito = incognito; + self + } + /// Consume the builder and create the [`WebView`]. /// /// Platform-specific behavior: diff --git a/src/webview/web_context.rs b/src/webview/web_context.rs index e7368517f..714e282c7 100644 --- a/src/webview/web_context.rs +++ b/src/webview/web_context.rs @@ -45,6 +45,19 @@ impl WebContext { Self { data, os } } + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + pub(crate) fn new_ephemeral() -> Self { + let data = WebContextData::default(); + let os = WebContextImpl::new_ephemeral(); + Self { data, os } + } + /// A reference to the data directory the context was created with. pub fn data_directory(&self) -> Option<&Path> { self.data.data_directory() diff --git a/src/webview/webkitgtk/mod.rs b/src/webview/webkitgtk/mod.rs index 75c22178f..1f07333ed 100644 --- a/src/webview/webkitgtk/mod.rs +++ b/src/webview/webkitgtk/mod.rs @@ -16,9 +16,9 @@ use std::{ }; use url::Url; use webkit2gtk::{ - traits::*, AutoplayPolicy, LoadEvent, NavigationPolicyDecision, PolicyDecisionType, URIRequest, - UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, WebViewBuilder, - WebsitePoliciesBuilder, + traits::*, AutoplayPolicy, LoadEvent, NavigationPolicyDecision, PolicyDecisionType, SettingsExt, + URIRequest, UserContentInjectedFrames, UserScript, UserScriptInjectionTime, WebView, + WebViewBuilder, WebsitePoliciesBuilder, }; use webkit2gtk_sys::{ webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, @@ -58,11 +58,16 @@ impl InnerWebView { // default_context allows us to create a scoped context on-demand let mut default_context; - let web_context = match web_context { - Some(w) => w, - None => { - default_context = Default::default(); - &mut default_context + let web_context = if attributes.incognito { + default_context = WebContext::new_ephemeral(); + &mut default_context + } else { + match web_context { + Some(w) => w, + None => { + default_context = Default::default(); + &mut default_context + } } }; @@ -88,7 +93,6 @@ impl InnerWebView { let w = window_rc.clone(); let ipc_handler = attributes.ipc_handler.take(); let manager = web_context.manager(); - // Use the window hash as the script handler name to prevent from conflict when sharing same // web context. let window_hash = { diff --git a/src/webview/webkitgtk/web_context.rs b/src/webview/webkitgtk/web_context.rs index b0cca7aaf..1d6c0acaa 100644 --- a/src/webview/webkitgtk/web_context.rs +++ b/src/webview/webkitgtk/web_context.rs @@ -37,8 +37,6 @@ pub struct WebContextImpl { impl WebContextImpl { pub fn new(data: &WebContextData) -> Self { - use webkit2gtk::traits::*; - let mut context_builder = WebContextBuilder::new(); if let Some(data_directory) = data.data_directory() { let data_manager = WebsiteDataManagerBuilder::new() @@ -58,9 +56,19 @@ impl WebContextImpl { } context_builder = context_builder.website_data_manager(&data_manager); } - let context = context_builder.build(); + Self::create_context(context) + } + + pub fn new_ephemeral() -> Self { + let context = WebContext::new_ephemeral(); + + Self::create_context(context) + } + + pub fn create_context(context: WebContext) -> Self { + use webkit2gtk::traits::*; let automation = false; context.set_automation_allowed(automation); diff --git a/src/webview/webview2/mod.rs b/src/webview/webview2/mod.rs index 773fa7356..0f76ae486 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview/webview2/mod.rs @@ -73,7 +73,7 @@ impl InnerWebView { let file_drop_window = window.clone(); let env = Self::create_environment(&web_context, pl_attrs.clone(), attributes.autoplay)?; - let controller = Self::create_controller(hwnd, &env)?; + let controller = Self::create_controller(hwnd, &env, attributes.incognito)?; let webview = Self::init_webview(window, hwnd, attributes, &env, &controller, pl_attrs)?; if let Some(file_drop_handler) = file_drop_handler { @@ -173,14 +173,18 @@ impl InnerWebView { fn create_controller( hwnd: HWND, env: &ICoreWebView2Environment, + incognito: bool, ) -> webview2_com::Result { let (tx, rx) = mpsc::channel(); - let env = env.clone(); + let env = env.clone().cast::()?; + let controller_opts = unsafe { env.CreateCoreWebView2ControllerOptions()? }; + + unsafe { controller_opts.SetIsInPrivateModeEnabled(incognito)? } CreateCoreWebView2ControllerCompletedHandler::wait_for_async_operation( Box::new(move |handler| unsafe { env - .CreateCoreWebView2Controller(hwnd, &handler) + .CreateCoreWebView2ControllerWithOptions(hwnd, &controller_opts, &handler) .map_err(webview2_com::Error::WindowsError) }), Box::new(move |error_code, controller| { diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index 0bbc010f1..98889c43f 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -239,6 +239,14 @@ impl InnerWebView { // Config and custom protocol 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] + }; + for (name, function) in attributes.custom_protocols { let scheme_name = format!("{}URLSchemeHandler", name); let cls = ClassDecl::new(&scheme_name, class!(NSObject)); @@ -299,6 +307,7 @@ impl InnerWebView { _ => class!(WryWebView), }; let webview: id = msg_send![cls, alloc]; + let () = msg_send![config, setWebsiteDataStore: data_store]; let _preference: id = msg_send![config, preferences]; let _yes: id = msg_send![class!(NSNumber), numberWithBool:1];