Skip to content

Commit c82b476

Browse files
authored
feat(core): expose with_webview API to access the platform webview (#4058)
1 parent 72e577d commit c82b476

File tree

7 files changed

+239
-18
lines changed

7 files changed

+239
-18
lines changed

.changes/webview-getters.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": "patch"
3+
"tauri-runtime": patch
4+
"tauri-runtime-wry": patch
5+
---
6+
7+
Expose methods to access the underlying native handles of the webview.

core/tauri-runtime-wry/Cargo.toml

+8-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
1313
readme = "README.md"
1414

1515
[dependencies]
16-
wry = { version = "0.15", default-features = false, features = [ "file-drop", "protocol" ] }
16+
wry = { version = "0.16", default-features = false, features = [ "file-drop", "protocol" ] }
1717
tauri-runtime = { version = "0.4.0", path = "../tauri-runtime" }
1818
tauri-utils = { version = "1.0.0-rc.5", path = "../tauri-utils" }
1919
uuid = { version = "1", features = [ "v4" ] }
@@ -22,14 +22,18 @@ rand = "0.8"
2222
[target."cfg(windows)".dependencies]
2323
webview2-com = "0.13.0"
2424

25-
[target."cfg(windows)".dependencies.windows]
26-
version = "0.30.0"
27-
features = [ "Win32_Foundation" ]
25+
[target."cfg(windows)".dependencies.windows]
26+
version = "0.30.0"
27+
features = [ "Win32_Foundation" ]
2828

2929
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
3030
gtk = { version = "0.15", features = [ "v3_20" ] }
31+
webkit2gtk = { version = "0.17", features = [ "v2_22" ] }
3132
percent-encoding = "2.1"
3233

34+
[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
35+
cocoa = "0.24"
36+
3337
[features]
3438
dox = [ "wry/dox" ]
3539
devtools = [ "wry/devtools", "tauri-runtime/devtools" ]

core/tauri-runtime-wry/src/lib.rs

+45-2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ use std::{
100100

101101
type WebviewId = u64;
102102

103+
mod webview;
104+
pub use webview::Webview;
105+
103106
#[cfg(feature = "system-tray")]
104107
mod system_tray;
105108
#[cfg(feature = "system-tray")]
@@ -1000,8 +1003,8 @@ pub struct GtkWindow(gtk::ApplicationWindow);
10001003
#[allow(clippy::non_send_fields_in_send_ty)]
10011004
unsafe impl Send for GtkWindow {}
10021005

1003-
#[derive(Debug, Clone)]
10041006
pub enum WindowMessage {
1007+
WithWebview(Box<dyn FnOnce(Webview) + Send>),
10051008
// Devtools
10061009
#[cfg(any(debug_assertions, feature = "devtools"))]
10071010
OpenDevTools,
@@ -1121,7 +1124,6 @@ pub enum Message<T: 'static> {
11211124
impl<T: UserEvent> Clone for Message<T> {
11221125
fn clone(&self) -> Self {
11231126
match self {
1124-
Self::Window(i, m) => Self::Window(*i, m.clone()),
11251127
Self::Webview(i, m) => Self::Webview(*i, m.clone()),
11261128
#[cfg(feature = "system-tray")]
11271129
Self::Tray(m) => Self::Tray(m.clone()),
@@ -1146,6 +1148,15 @@ pub struct WryDispatcher<T: UserEvent> {
11461148
#[allow(clippy::non_send_fields_in_send_ty)]
11471149
unsafe impl<T: UserEvent> Sync for WryDispatcher<T> {}
11481150

1151+
impl<T: UserEvent> WryDispatcher<T> {
1152+
pub fn with_webview<F: FnOnce(Webview) + Send + 'static>(&self, f: F) -> Result<()> {
1153+
send_user_message(
1154+
&self.context,
1155+
Message::Window(self.window_id, WindowMessage::WithWebview(Box::new(f))),
1156+
)
1157+
}
1158+
}
1159+
11491160
impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
11501161
type Runtime = Wry<T>;
11511162
type WindowBuilder = WindowBuilderWrapper;
@@ -2138,6 +2149,38 @@ fn handle_user_message<T: UserEvent>(
21382149
{
21392150
let window = window_handle.window();
21402151
match window_message {
2152+
WindowMessage::WithWebview(f) => {
2153+
if let WindowHandle::Webview(w) = window_handle {
2154+
#[cfg(any(
2155+
target_os = "linux",
2156+
target_os = "dragonfly",
2157+
target_os = "freebsd",
2158+
target_os = "netbsd",
2159+
target_os = "openbsd"
2160+
))]
2161+
{
2162+
use wry::webview::WebviewExtUnix;
2163+
f(w.webview());
2164+
}
2165+
#[cfg(target_os = "macos")]
2166+
{
2167+
use wry::webview::WebviewExtMacOS;
2168+
f(Webview {
2169+
webview: w.webview(),
2170+
manager: w.manager(),
2171+
ns_window: w.ns_window(),
2172+
});
2173+
}
2174+
2175+
#[cfg(windows)]
2176+
{
2177+
f(Webview {
2178+
controller: w.controller(),
2179+
});
2180+
}
2181+
}
2182+
}
2183+
21412184
#[cfg(any(debug_assertions, feature = "devtools"))]
21422185
WindowMessage::OpenDevTools => {
21432186
if let WindowHandle::Webview(w) = &window_handle {

core/tauri-runtime-wry/src/webview.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
#[cfg(any(
6+
target_os = "linux",
7+
target_os = "dragonfly",
8+
target_os = "freebsd",
9+
target_os = "netbsd",
10+
target_os = "openbsd"
11+
))]
12+
mod imp {
13+
use std::rc::Rc;
14+
15+
pub type Webview = Rc<webkit2gtk::WebView>;
16+
}
17+
18+
#[cfg(target_os = "macos")]
19+
mod imp {
20+
use cocoa::base::id;
21+
22+
pub struct Webview {
23+
pub webview: id,
24+
pub manager: id,
25+
pub ns_window: id,
26+
}
27+
}
28+
29+
#[cfg(windows)]
30+
mod imp {
31+
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
32+
pub struct Webview {
33+
pub controller: ICoreWebView2Controller,
34+
}
35+
}
36+
37+
pub use imp::*;

core/tauri-runtime/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,6 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
473473
#[cfg(windows)]
474474
fn hwnd(&self) -> Result<HWND>;
475475

476-
/// Returns the current window theme.
477-
fn theme(&self) -> Result<Theme>;
478-
479476
/// Returns the native handle that is used by this window.
480477
#[cfg(target_os = "macos")]
481478
fn ns_window(&self) -> Result<*mut std::ffi::c_void>;
@@ -490,6 +487,9 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
490487
))]
491488
fn gtk_window(&self) -> Result<gtk::ApplicationWindow>;
492489

490+
/// Returns the current window theme.
491+
fn theme(&self) -> Result<Theme>;
492+
493493
// SETTERS
494494

495495
/// Centers the window.

core/tauri/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ ico = { version = "0.1", optional = true }
9797
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
9898
gtk = { version = "0.15", features = [ "v3_20" ] }
9999
glib = "0.15"
100+
webkit2gtk = { version = "0.17", features = [ "v2_22" ] }
100101

101102
[target."cfg(target_os = \"macos\")".dependencies]
102103
embed_plist = "1.2"
104+
cocoa = "0.24"
105+
objc = "0.2"
106+
107+
[target."cfg(windows)".dependencies]
108+
webview2-com = "0.13.0"
103109

104110
[target."cfg(windows)".dependencies.windows]
105111
version = "0.30.0"

core/tauri/src/window.rs

+133-9
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,130 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
543543
}
544544
}
545545

546+
/// The platform webview handle. Accessed with [`Window#method.with_webview`];
547+
#[cfg(feature = "wry")]
548+
#[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
549+
pub struct PlatformWebview(tauri_runtime_wry::Webview);
550+
551+
#[cfg(feature = "wry")]
552+
impl PlatformWebview {
553+
/// Returns [`webkit2gtk::WebView`] handle.
554+
#[cfg(any(
555+
target_os = "linux",
556+
target_os = "dragonfly",
557+
target_os = "freebsd",
558+
target_os = "netbsd",
559+
target_os = "openbsd"
560+
))]
561+
#[cfg_attr(
562+
doc_cfg,
563+
doc(cfg(any(
564+
target_os = "linux",
565+
target_os = "dragonfly",
566+
target_os = "freebsd",
567+
target_os = "netbsd",
568+
target_os = "openbsd"
569+
)))
570+
)]
571+
pub fn inner(&self) -> std::rc::Rc<webkit2gtk::WebView> {
572+
self.0.clone()
573+
}
574+
575+
/// Returns the WebView2 controller.
576+
#[cfg(windows)]
577+
#[cfg_attr(doc_cfg, doc(cfg(windows)))]
578+
pub fn controller(
579+
&self,
580+
) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller {
581+
self.0.controller.clone()
582+
}
583+
584+
/// Returns the [WKWebView] handle.
585+
///
586+
/// [WKWebView]: https://developer.apple.com/documentation/webkit/wkwebview
587+
#[cfg(target_os = "macos")]
588+
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
589+
pub fn inner(&self) -> cocoa::base::id {
590+
self.0.webview.clone()
591+
}
592+
593+
/// Returns WKWebView [controller] handle.
594+
///
595+
/// [controller]: https://developer.apple.com/documentation/webkit/wkusercontentcontroller
596+
#[cfg(target_os = "macos")]
597+
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
598+
pub fn controller(&self) -> cocoa::base::id {
599+
self.0.manager.clone()
600+
}
601+
602+
/// Returns [NSWindow] associated with the WKWebView webview.
603+
///
604+
/// [NSWindow]: https://developer.apple.com/documentation/appkit/nswindow
605+
#[cfg(target_os = "macos")]
606+
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
607+
pub fn ns_window(&self) -> cocoa::base::id {
608+
self.0.ns_window.clone()
609+
}
610+
}
611+
612+
#[cfg(feature = "wry")]
613+
impl Window<crate::Wry> {
614+
/// Executes the closure accessing the platform's webview handle.
615+
///
616+
/// The closure is executed in the main thread.
617+
///
618+
/// # Examples
619+
///
620+
/// ```rust,no_run
621+
/// #[cfg(target_os = "macos")]
622+
/// #[macro_use]
623+
/// extern crate objc;
624+
/// use tauri::Manager;
625+
///
626+
/// fn main() {
627+
/// tauri::Builder::default()
628+
/// .setup(|app| {
629+
/// let main_window = app.get_window("main").unwrap();
630+
/// main_window.with_webview(|webview| {
631+
/// #[cfg(target_os = "linux")]
632+
/// {
633+
/// // see https://docs.rs/webkit2gtk/latest/webkit2gtk/struct.WebView.html
634+
/// // and https://docs.rs/webkit2gtk/latest/webkit2gtk/trait.WebViewExt.html
635+
/// use webkit2gtk::traits::WebViewExt;
636+
/// webview.inner().set_zoom_level(4.);
637+
/// }
638+
///
639+
/// #[cfg(windows)]
640+
/// unsafe {
641+
/// // see https://docs.rs/webview2-com/latest/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html
642+
/// webview.controller().SetZoomFactor(4.).unwrap();
643+
/// }
644+
///
645+
/// #[cfg(target_os = "macos")]
646+
/// unsafe {
647+
/// let () = msg_send![webview.inner(), setPageZoom: 4.];
648+
/// let () = msg_send![webview.controller(), removeAllUserScripts];
649+
/// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.];
650+
/// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color];
651+
/// }
652+
/// });
653+
/// Ok(())
654+
/// });
655+
/// }
656+
/// ```
657+
#[cfg_attr(doc_cfg, doc(cfg(eature = "wry")))]
658+
pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(
659+
&self,
660+
f: F,
661+
) -> crate::Result<()> {
662+
self
663+
.window
664+
.dispatcher
665+
.with_webview(|w| f(PlatformWebview(w)))
666+
.map_err(Into::into)
667+
}
668+
}
669+
546670
impl<R: Runtime> Window<R> {
547671
/// Create a new window that is attached to the manager.
548672
pub(crate) fn new(
@@ -976,15 +1100,6 @@ impl<R: Runtime> Window<R> {
9761100
self.window.dispatcher.hwnd().map_err(Into::into)
9771101
}
9781102

979-
/// Returns the current window theme.
980-
///
981-
/// ## Platform-specific
982-
///
983-
/// - **macOS / Linux**: Not implemented, always return [`Theme::Light`].
984-
pub fn theme(&self) -> crate::Result<Theme> {
985-
self.window.dispatcher.theme().map_err(Into::into)
986-
}
987-
9881103
/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
9891104
///
9901105
/// Note that this can only be used on the main thread.
@@ -999,6 +1114,15 @@ impl<R: Runtime> Window<R> {
9991114
self.window.dispatcher.gtk_window().map_err(Into::into)
10001115
}
10011116

1117+
/// Returns the current window theme.
1118+
///
1119+
/// ## Platform-specific
1120+
///
1121+
/// - **macOS / Linux**: Not implemented, always return [`Theme::Light`].
1122+
pub fn theme(&self) -> crate::Result<Theme> {
1123+
self.window.dispatcher.theme().map_err(Into::into)
1124+
}
1125+
10021126
// Setters
10031127

10041128
/// Centers the window.

0 commit comments

Comments
 (0)