Skip to content

Commit 3f978d3

Browse files
authored
refactor: allow new_window_req_handler to create the webview on its own (#1601)
* feat(linux): craete webview on new_window_req follow-up for #1596 ref tauri-apps/tauri#13876 * refactor: allow new_window_req_handler to create the webview on its own * add webview getter * clippy regression * macos fixes * enhance platform support * fix deadlock on windows * fix mac build
1 parent 1456f8e commit 3f978d3

File tree

12 files changed

+475
-165
lines changed

12 files changed

+475
-165
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wry": minor
3+
---
4+
5+
Refactor `WebViewBuilder::with_new_window_req_handler` to allow creating the webview manually.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wry": minor
3+
---
4+
5+
Disable new window creation by default on Windows to match behavior of other platforms. Use `WebViewBuilder::with_new_window_req_handler` to enable.

examples/simple.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ fn main() -> wry::Result<()> {
1515

1616
let builder = WebViewBuilder::new()
1717
.with_url("http://tauri.app")
18-
.with_new_window_req_handler(|url| {
19-
println!("new window req: {url}");
20-
true
18+
.with_new_window_req_handler(|url, features| {
19+
println!("new window req: {url} {features:?}");
20+
wry::NewWindowResponse::Allow
2121
})
2222
.with_drag_drop_handler(|e| {
2323
match e {

examples/streaming.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ fn stream_protocol(
119119
request: http::Request<Vec<u8>>,
120120
) -> Result<http::Response<Vec<u8>>, Box<dyn std::error::Error>> {
121121
// skip leading `/`
122-
let path = percent_encoding::percent_decode(request.uri().path()[1..].as_bytes())
122+
let path = percent_encoding::percent_decode(&request.uri().path().as_bytes()[1..])
123123
.decode_utf8_lossy()
124124
.to_string();
125125

src/lib.rs

Lines changed: 160 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,9 @@ pub use self::webview2::ScrollBarStyle;
395395
#[cfg(target_os = "windows")]
396396
use self::webview2::*;
397397
#[cfg(target_os = "windows")]
398-
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
398+
use webview2_com::Microsoft::Web::WebView2::Win32::{
399+
ICoreWebView2, ICoreWebView2Controller, ICoreWebView2Environment,
400+
};
399401

400402
use std::{borrow::Cow, collections::HashMap, path::PathBuf, rc::Rc};
401403

@@ -450,6 +452,87 @@ impl RequestAsyncResponder {
450452
}
451453
}
452454

455+
/// Response for the new window request handler.
456+
///
457+
/// See [`WebViewBuilder::with_new_window_req_handler`].
458+
pub enum NewWindowResponse {
459+
/// Allow the window to be opened with the default implementation.
460+
Allow,
461+
/// Allow the window to be opened, with the given platform webview instance.
462+
///
463+
/// ## Platform-specific:
464+
///
465+
/// **Linux**: The webview must be related to the caller webview. See [`WebViewBuilderExtUnix::with_related_view`].
466+
/// **Windows**: The webview must use the same environment as the caller webview. See [`WebViewBuilderExtWindows::with_environment`].
467+
/// **macOS**: The webview must use the same configuration as the caller webview. See [`WebViewBuilderExtMacos::with_webview_configuration`].
468+
#[cfg(not(any(target_os = "android", target_os = "ios")))]
469+
Create {
470+
#[cfg(any(
471+
target_os = "linux",
472+
target_os = "dragonfly",
473+
target_os = "freebsd",
474+
target_os = "netbsd",
475+
target_os = "openbsd",
476+
))]
477+
webview: webkit2gtk::WebView,
478+
#[cfg(windows)]
479+
webview: ICoreWebView2,
480+
#[cfg(target_os = "macos")]
481+
webview: Retained<objc2_web_kit::WKWebView>,
482+
},
483+
/// Deny the window from being opened.
484+
Deny,
485+
}
486+
487+
/// Information about the webview that initiated a new window request.
488+
#[derive(Debug)]
489+
pub struct NewWindowOpener {
490+
/// The instance of the webview that initiated the new window request.
491+
///
492+
/// This must be set as the related view of the new webview. See [`WebViewBuilderExtUnix::with_related_view`].
493+
#[cfg(any(
494+
target_os = "linux",
495+
target_os = "dragonfly",
496+
target_os = "freebsd",
497+
target_os = "netbsd",
498+
target_os = "openbsd",
499+
))]
500+
pub webview: webkit2gtk::WebView,
501+
/// The instance of the webview that initiated the new window request.
502+
#[cfg(windows)]
503+
pub webview: ICoreWebView2,
504+
/// The environment of the webview that initiated the new window request.
505+
///
506+
/// The target webview environment **MUST** match the environment of the opener webview. See [`WebViewBuilderExtWindows::with_environment`].
507+
#[cfg(windows)]
508+
pub environment: ICoreWebView2Environment,
509+
/// The instance of the webview that initiated the new window request.
510+
#[cfg(target_os = "macos")]
511+
pub webview: Retained<objc2_web_kit::WKWebView>,
512+
/// Configuration of the target webview.
513+
///
514+
/// This **MUST** be used when creating the target webview. See [`WebViewBuilderExtMacos::with_webview_configuration`].
515+
#[cfg(target_os = "macos")]
516+
pub target_configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
517+
}
518+
519+
unsafe impl Send for NewWindowOpener {}
520+
unsafe impl Sync for NewWindowOpener {}
521+
522+
/// Window features of a window requested to open.
523+
#[non_exhaustive]
524+
#[derive(Debug)]
525+
pub struct NewWindowFeatures {
526+
/// Specifies the size of the content area
527+
/// as defined by the user's operating system where the new window will be generated.
528+
pub size: Option<dpi::LogicalSize<f64>>,
529+
/// Specifies the position of the window relative to the work area
530+
/// as defined by the user's operating system where the new window will be generated.
531+
pub position: Option<dpi::LogicalPosition<f64>>,
532+
/// Information about the webview opener containing data that must be used when creating the new webview.
533+
pub opener: NewWindowOpener,
534+
}
535+
453536
/// An id for a webview
454537
pub type WebViewId<'a> = &'a str;
455538

@@ -604,11 +687,19 @@ pub struct WebViewAttributes<'a> {
604687
/// due to API limitations.
605688
pub download_completed_handler: Option<Rc<dyn Fn(String, Option<PathBuf>, bool) + 'static>>,
606689

607-
/// A new window handler to decide if incoming url is allowed to open in a new window.
690+
/// A new window request handler to decide if incoming url is allowed to be opened.
691+
///
692+
/// A new window is requested to be opened by the [window.open] API.
608693
///
609-
/// The closure take a `String` parameter as url and return `bool` to determine whether the window should open.
610-
/// `true` allows to open and `false` does not.
611-
pub new_window_req_handler: Option<Box<dyn Fn(String) -> bool>>,
694+
/// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
695+
///
696+
/// ## Platform-specific:
697+
///
698+
/// - **Windows**: The closure is executed on a separate thread to prevent a deadlock.
699+
///
700+
/// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
701+
pub new_window_req_handler:
702+
Option<Box<dyn Fn(String, NewWindowFeatures) -> NewWindowResponse + Send + Sync>>,
612703

613704
/// Enables clipboard access for the page rendered on **Linux** and **Windows**.
614705
///
@@ -1203,11 +1294,18 @@ impl<'a> WebViewBuilder<'a> {
12031294

12041295
/// Set a new window request handler to decide if incoming url is allowed to be opened.
12051296
///
1206-
/// The closure take a `String` parameter as url and return `bool` to determine whether the window should open.
1207-
/// `true` allows to open and `false` does not.
1297+
/// A new window is requested to be opened by the [window.open] API.
1298+
///
1299+
/// The closure take the URL to open and the window features object and returns [`NewWindowResponse`] to determine whether the window should open.
1300+
///
1301+
/// ## Platform-specific:
1302+
///
1303+
/// - **Windows**: The closure is executed on a separate thread to prevent a deadlock.
1304+
///
1305+
/// [window.open]: https://developer.mozilla.org/en-US/docs/Web/API/Window/open
12081306
pub fn with_new_window_req_handler(
12091307
mut self,
1210-
callback: impl Fn(String) -> bool + 'static,
1308+
callback: impl Fn(String, NewWindowFeatures) -> NewWindowResponse + Send + Sync + 'static,
12111309
) -> Self {
12121310
self.attrs.new_window_req_handler = Some(Box::new(callback));
12131311
self
@@ -1368,6 +1466,8 @@ pub(crate) struct PlatformSpecificWebViewAttributes {
13681466
input_accessory_view_builder: Option<Box<InputAccessoryViewBuilder>>,
13691467
#[cfg(target_os = "ios")]
13701468
limit_navigations_to_app_bound_domains: bool,
1469+
#[cfg(target_os = "macos")]
1470+
webview_configuration: Option<Retained<objc2_web_kit::WKWebViewConfiguration>>,
13711471
}
13721472

13731473
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -1382,6 +1482,8 @@ impl Default for PlatformSpecificWebViewAttributes {
13821482
input_accessory_view_builder: None,
13831483
#[cfg(target_os = "ios")]
13841484
limit_navigations_to_app_bound_domains: false,
1485+
#[cfg(target_os = "macos")]
1486+
webview_configuration: None,
13851487
}
13861488
}
13871489
}
@@ -1427,6 +1529,29 @@ impl WebViewBuilderExtDarwin for WebViewBuilder<'_> {
14271529
}
14281530
}
14291531

1532+
#[cfg(target_os = "macos")]
1533+
pub trait WebViewBuilderExtMacos {
1534+
/// Set the webview configuration that must be used to create the new webview.
1535+
fn with_webview_configuration(
1536+
self,
1537+
configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
1538+
) -> Self;
1539+
}
1540+
1541+
#[cfg(target_os = "macos")]
1542+
impl WebViewBuilderExtMacos for WebViewBuilder<'_> {
1543+
fn with_webview_configuration(
1544+
mut self,
1545+
configuration: Retained<objc2_web_kit::WKWebViewConfiguration>,
1546+
) -> Self {
1547+
self
1548+
.platform_specific
1549+
.webview_configuration
1550+
.replace(configuration);
1551+
self
1552+
}
1553+
}
1554+
14301555
#[cfg(target_os = "ios")]
14311556
pub trait WebViewBuilderExtIos {
14321557
/// Allows overriding the the keyboard accessory view on iOS.
@@ -1501,6 +1626,7 @@ pub(crate) struct PlatformSpecificWebViewAttributes {
15011626
browser_extensions_enabled: bool,
15021627
extension_path: Option<PathBuf>,
15031628
default_context_menus: bool,
1629+
environment: Option<ICoreWebView2Environment>,
15041630
}
15051631

15061632
#[cfg(windows)]
@@ -1515,6 +1641,7 @@ impl Default for PlatformSpecificWebViewAttributes {
15151641
scroll_bar_style: ScrollBarStyle::default(),
15161642
browser_extensions_enabled: false,
15171643
extension_path: None,
1644+
environment: None,
15181645
}
15191646
}
15201647
}
@@ -1587,6 +1714,10 @@ pub trait WebViewBuilderExtWindows {
15871714
///
15881715
/// Does nothing if browser extensions are disabled. See [`with_browser_extensions_enabled`](Self::with_browser_extensions_enabled)
15891716
fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
1717+
1718+
/// Set the environment for the webview.
1719+
/// Useful if you need to share the same environment, for instance when using the [`WebViewBuilder::with_new_window_req_handler`].
1720+
fn with_environment(self, environment: ICoreWebView2Environment) -> Self;
15901721
}
15911722

15921723
#[cfg(windows)]
@@ -1630,6 +1761,11 @@ impl WebViewBuilderExtWindows for WebViewBuilder<'_> {
16301761
self.platform_specific.extension_path = Some(path.into());
16311762
self
16321763
}
1764+
1765+
fn with_environment(mut self, environment: ICoreWebView2Environment) -> Self {
1766+
self.platform_specific.environment.replace(environment);
1767+
self
1768+
}
16331769
}
16341770

16351771
#[cfg(target_os = "android")]
@@ -1742,6 +1878,7 @@ pub trait WebViewBuilderExtUnix<'a> {
17421878
fn with_extensions_path(self, path: impl Into<PathBuf>) -> Self;
17431879

17441880
/// Creates a new webview sharing the same web process with the provided webview.
1881+
/// Useful if you need to link a webview to another, for instance when using the [`WebViewBuilder::with_new_window_req_handler`].
17451882
fn with_related_view(self, webview: webkit2gtk::WebView) -> Self;
17461883
}
17471884

@@ -2065,9 +2202,15 @@ pub enum MemoryUsageLevel {
20652202
/// Additional methods on `WebView` that are specific to Windows.
20662203
#[cfg(target_os = "windows")]
20672204
pub trait WebViewExtWindows {
2068-
/// Returns WebView2 Controller
2205+
/// Returns the WebView2 controller.
20692206
fn controller(&self) -> ICoreWebView2Controller;
20702207

2208+
/// Webview environment.
2209+
fn environment(&self) -> ICoreWebView2Environment;
2210+
2211+
/// Webview instance.
2212+
fn webview(&self) -> ICoreWebView2;
2213+
20712214
/// Changes the webview2 theme.
20722215
///
20732216
/// Requires WebView2 Runtime version 101.0.1210.39 or higher, returns error on older versions,
@@ -2098,6 +2241,14 @@ impl WebViewExtWindows for WebView {
20982241
self.webview.controller.clone()
20992242
}
21002243

2244+
fn environment(&self) -> ICoreWebView2Environment {
2245+
self.webview.env.clone()
2246+
}
2247+
2248+
fn webview(&self) -> ICoreWebView2 {
2249+
self.webview.webview.clone()
2250+
}
2251+
21012252
fn set_theme(&self, theme: Theme) -> Result<()> {
21022253
self.webview.set_theme(theme)
21032254
}

0 commit comments

Comments
 (0)