From 5925b9dc768a1eaf58aaf29f37bf45cf17eb3692 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Sun, 22 Oct 2023 20:19:47 +0900 Subject: [PATCH 01/80] Refactor new method entry point to rwh --- Cargo.toml | 1 + examples/custom_protocol.rs | 3 +- examples/custom_titlebar.rs | 9 ++-- examples/detect_js_ecma.rs | 4 +- examples/download_event.rs | 4 +- examples/dragndrop.rs | 6 ++- examples/eval_js.rs | 8 +-- examples/form.rs | 3 +- examples/fullscreen.rs | 4 +- examples/hello_world.rs | 6 ++- examples/multi_window.rs | 9 ++-- examples/navigation_event.rs | 4 +- examples/new_window_req_event.rs | 4 +- examples/proxy.rs | 3 +- examples/stream_range.rs | 3 +- examples/transparent.rs | 4 +- examples/user_agent.rs | 4 +- src/lib.rs | 2 + src/webview/mod.rs | 79 +++++++----------------------- src/webview/wkwebview/file_drop.rs | 39 +++++---------- src/webview/wkwebview/mod.rs | 62 ++++++++++------------- 21 files changed, 114 insertions(+), 147 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 952e9a6da..03099e4b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ thiserror = "1.0" url = "2.4" tao = { version = "0.23", default-features = false, features = [ "serde" ], optional = true } http = "0.2.9" +raw-window-handle = "0.5" [dev-dependencies] http-range = "0.1.5" diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 352a5d945..95296fa26 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -8,6 +8,7 @@ use std::{ }; use http::Request; +use raw_window_handle::HasRawWindowHandle; use wry::{ application::{ event::{Event, StartCause, WindowEvent}, @@ -27,7 +28,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_asynchronous_custom_protocol("wry".into(), move |request, responder| { match get_wry_response(request) { diff --git a/examples/custom_titlebar.rs b/examples/custom_titlebar.rs index 8b07a125d..3b7e60e4b 100644 --- a/examples/custom_titlebar.rs +++ b/examples/custom_titlebar.rs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopBuilder}, - window::{Window, WindowBuilder}, + window::WindowBuilder, }, webview::WebViewBuilder, }; @@ -21,6 +23,7 @@ fn main() -> wry::Result<()> { .with_decorations(false) .build(&event_loop) .unwrap(); + let window_handle = window.raw_window_handle(); const HTML: &str = r#" @@ -116,7 +119,7 @@ fn main() -> wry::Result<()> { let proxy = event_loop.create_proxy(); - let handler = move |window: &Window, req: String| { + let handler = move |req: String| { if req == "minimize" { window.set_minimized(true); } @@ -132,7 +135,7 @@ fn main() -> wry::Result<()> { }; let mut webview = Some( - WebViewBuilder::new(window) + WebViewBuilder::new(window_handle) .unwrap() .with_html(HTML)? .with_ipc_handler(handler) diff --git a/examples/detect_js_ecma.rs b/examples/detect_js_ecma.rs index b5f5f3579..84aa7c063 100644 --- a/examples/detect_js_ecma.rs +++ b/examples/detect_js_ecma.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -120,7 +122,7 @@ fn main() -> wry::Result<()> { .with_title("Detect ECMAScript") .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_html(HTML)? .build()?; diff --git a/examples/download_event.rs b/examples/download_event.rs index e2715d1fc..7ea0fcf3c 100644 --- a/examples/download_event.rs +++ b/examples/download_event.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use std::{env::temp_dir, path::PathBuf}; use wry::{ @@ -34,7 +36,7 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_html(HTML)? .with_download_started_handler({ let proxy = proxy.clone(); diff --git a/examples/dragndrop.rs b/examples/dragndrop.rs index 55d0d8e05..cf88e121f 100644 --- a/examples/dragndrop.rs +++ b/examples/dragndrop.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -20,10 +22,10 @@ Dropping files onto the following form is also possible:

let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_url(HTML)? - .with_file_drop_handler(|_, data| { + .with_file_drop_handler(|data| { println!("Window 1: {:?}", data); false // Returning true will block the OS default behaviour. }) diff --git a/examples/eval_js.rs b/examples/eval_js.rs index 6f4bc2299..5cc6eede6 100644 --- a/examples/eval_js.rs +++ b/examples/eval_js.rs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopBuilder}, - window::{Window, WindowBuilder}, + window::WindowBuilder, }, webview::WebViewBuilder, }; @@ -23,13 +25,13 @@ fn main() -> wry::Result<()> { .with_title("Hello World") .build(&event_loop)?; - let ipc_handler = move |_: &Window, req: String| { + let ipc_handler = move |req: String| { if req == "exec-eval" { let _ = proxy.send_event(UserEvent::ExecEval); } }; - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_html( r#" diff --git a/examples/form.rs b/examples/form.rs index 094336923..4b7bca758 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,6 +7,7 @@ use std::{ fs::{canonicalize, read}, }; +use raw_window_handle::HasRawWindowHandle; use wry::{ application::{ event::{Event, StartCause, WindowEvent}, @@ -24,7 +25,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_custom_protocol("wry".into(), move |request| { if request.method() == Method::POST { diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 48abdf6be..08b1abdc1 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -18,7 +20,7 @@ fn main() -> wry::Result<()> { .with_fullscreen(Some(Fullscreen::Borderless(None))) .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_url("https://browserbench.org/MotionMark1.2/")? .build()?; diff --git a/examples/hello_world.rs b/examples/hello_world.rs index f74d27625..2bb1a1586 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -16,8 +18,8 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? - .with_url("https://www.netflix.com/browse")? + let _webview = WebViewBuilder::new(window.raw_window_handle())? + .with_url("https://tauri.app")? // .with_incognito(true) .build()?; diff --git a/examples/multi_window.rs b/examples/multi_window.rs index b74ed14f1..313163ce0 100644 --- a/examples/multi_window.rs +++ b/examples/multi_window.rs @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use std::collections::HashMap; use wry::{ application::{ event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget}, - window::{Window, WindowBuilder, WindowId}, + window::{WindowBuilder, WindowId}, }, webview::{WebView, WebViewBuilder}, }; @@ -27,8 +29,9 @@ fn main() -> wry::Result<()> { .with_title(title) .build(event_loop) .unwrap(); + let window_handle = window.raw_window_handle(); let window_id = window.id(); - let handler = move |window: &Window, req: String| match req.as_str() { + let handler = move |req: String| match req.as_str() { "new-window" => { let _ = proxy.send_event(UserEvent::NewWindow); } @@ -42,7 +45,7 @@ fn main() -> wry::Result<()> { _ => {} }; - let webview = WebViewBuilder::new(window) + let webview = WebViewBuilder::new(window_handle) .unwrap() .with_html( r#" diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index ddaab184b..740a9af3d 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -21,7 +23,7 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_url("http://neverssl.com")? .with_navigation_handler(move |uri: String| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); diff --git a/examples/new_window_req_event.rs b/examples/new_window_req_event.rs index 272d68d70..4ea52ba56 100644 --- a/examples/new_window_req_event.rs +++ b/examples/new_window_req_event.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -31,7 +33,7 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_html(html)? .with_new_window_req_handler(move |uri: String| { let submitted = proxy.send_event(UserEvent::NewWindow(uri.clone())).is_ok(); diff --git a/examples/proxy.rs b/examples/proxy.rs index f411b0278..c859d3e9b 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; use wry::webview::{ProxyConfig, ProxyEndpoint}; fn main() -> wry::Result<()> { @@ -24,7 +25,7 @@ fn main() -> wry::Result<()> { port: "3128".to_string(), }); - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_proxy_config(http_proxy) .with_url("https://www.myip.com/")? .build()?; diff --git a/examples/stream_range.rs b/examples/stream_range.rs index aa0d5bd75..101e3ef71 100644 --- a/examples/stream_range.rs +++ b/examples/stream_range.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT use http_range::HttpRange; +use raw_window_handle::HasRawWindowHandle; use std::{ borrow::Cow, fs::{canonicalize, File}, @@ -49,7 +50,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window) + let _webview = WebViewBuilder::new(window.raw_window_handle()) .unwrap() .with_custom_protocol("wry".into(), move |request| { get_stream_response(request).unwrap_or_else(|error| { diff --git a/examples/transparent.rs b/examples/transparent.rs index 915afc9f2..c7a84efd2 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -21,7 +23,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? // The second is on webview... .with_transparent(true) // And the last is in html. diff --git a/examples/user_agent.rs b/examples/user_agent.rs index ae45dad2a..a44948cf5 100644 --- a/examples/user_agent.rs +++ b/examples/user_agent.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use raw_window_handle::HasRawWindowHandle; + fn main() -> wry::Result<()> { use wry::{ application::{ @@ -25,7 +27,7 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") .build(&event_loop)?; - let _webview = WebViewBuilder::new(window)? + let _webview = WebViewBuilder::new(window.raw_window_handle())? .with_user_agent(&user_agent_string) .with_url("https://www.whatismybrowser.com/detect/what-is-my-user-agent")? .build()?; diff --git a/src/lib.rs b/src/lib.rs index c51413e89..948604b9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,4 +169,6 @@ pub enum Error { JniError(#[from] tao::platform::android::ndk_glue::jni::errors::Error), #[error("Failed to create proxy endpoint")] ProxyEndpointCreationFailed, + #[error("Failed to get correct raw window handle")] + WindowHandleError, } diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 7fdd1438d..34c441d64 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -7,6 +7,8 @@ mod proxy; mod web_context; +use raw_window_handle::RawWindowHandle; +use tao::dpi::Position; pub use web_context::WebContext; #[cfg(target_os = "android")] @@ -43,7 +45,7 @@ use wkwebview::*; pub(crate) mod webview2; #[cfg(target_os = "windows")] use self::webview2::*; -use crate::{application::dpi::PhysicalPosition, Result}; +use crate::Result; #[cfg(target_os = "windows")] use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; #[cfg(target_os = "windows")] @@ -56,7 +58,6 @@ pub use url::Url; #[cfg(target_os = "windows")] use crate::application::platform::windows::WindowExtWindows; -use crate::application::{dpi::PhysicalSize, window::Window}; use http::{Request, Response as HttpResponse}; @@ -159,7 +160,7 @@ pub struct WebViewAttributes { /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. /// /// Both functions return promises but `notify()` resolves immediately. - pub ipc_handler: Option>, + pub ipc_handler: Option>, /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. /// /// # Blocking OS Default Behavior @@ -168,9 +169,9 @@ pub struct WebViewAttributes { /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. #[cfg(feature = "file-drop")] - pub file_drop_handler: Option bool>>, + pub file_drop_handler: Option bool>>, #[cfg(not(feature = "file-drop"))] - file_drop_handler: Option bool>>, + file_drop_handler: Option bool>>, /// Set a navigation handler to decide if incoming url is allowed to navigate. /// @@ -240,7 +241,7 @@ pub struct WebViewAttributes { pub back_forward_navigation_gestures: bool, /// Set a handler closure to process the change of the webview's document title. - pub document_title_changed_handler: Option>, + pub document_title_changed_handler: Option>, /// Run the WebView with incognito mode. Note that WebContext will be ingored if incognito is /// enabled. @@ -375,12 +376,12 @@ pub struct WebViewBuilder<'a> { pub webview: WebViewAttributes, platform_specific: PlatformSpecificWebViewAttributes, web_context: Option<&'a mut WebContext>, - window: Window, + window: RawWindowHandle, } impl<'a> WebViewBuilder<'a> { /// Create [`WebViewBuilder`] from provided [`Window`]. - pub fn new(window: Window) -> Result { + pub fn new(window: RawWindowHandle) -> Result { let webview = WebViewAttributes::default(); let web_context = None; #[allow(clippy::default_constructed_unit_structs)] @@ -389,7 +390,7 @@ impl<'a> WebViewBuilder<'a> { Ok(Self { webview, web_context, - window, + window: window, platform_specific, }) } @@ -541,7 +542,7 @@ impl<'a> WebViewBuilder<'a> { /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. pub fn with_ipc_handler(mut self, handler: F) -> Self where - F: Fn(&Window, String) + 'static, + F: Fn(String) + 'static, { self.webview.ipc_handler = Some(Box::new(handler)); self @@ -557,7 +558,7 @@ impl<'a> WebViewBuilder<'a> { #[cfg(feature = "file-drop")] pub fn with_file_drop_handler(mut self, handler: F) -> Self where - F: Fn(&Window, FileDropEvent) -> bool + 'static, + F: Fn(FileDropEvent) -> bool + 'static, { self.webview.file_drop_handler = Some(Box::new(handler)); self @@ -711,7 +712,7 @@ impl<'a> WebViewBuilder<'a> { /// Set a handler closure to process the change of the webview's document title. pub fn with_document_title_changed_handler( mut self, - callback: impl Fn(&Window, String) + 'static, + callback: impl Fn(String) + 'static, ) -> Self { self.webview.document_title_changed_handler = Some(Box::new(callback)); self @@ -768,14 +769,13 @@ impl<'a> WebViewBuilder<'a> { /// /// [`EventLoop`]: crate::application::event_loop::EventLoop pub fn build(self) -> Result { - let window = Rc::new(self.window); let webview = InnerWebView::new( - window.clone(), + self.window, self.webview, self.platform_specific, self.web_context, )?; - Ok(WebView { window, webview }) + Ok(WebView { webview }) } } @@ -909,9 +909,8 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and /// scripts for those who prefer to control fine grained window creation and event handling. /// [`WebView`] presents the actual WebView window and let you still able to perform actions -/// during event handling to it. [`WebView`] also contains the associate [`Window`] with it. +/// during event handling to it. pub struct WebView { - window: Rc, webview: InnerWebView, } @@ -954,16 +953,10 @@ impl WebView { /// called in the same thread with the [`EventLoop`] you create. /// /// [`EventLoop`]: crate::application::event_loop::EventLoop - pub fn new(window: Window) -> Result { + pub fn new(window: RawWindowHandle) -> Result { WebViewBuilder::new(window)?.build() } - /// Get the [`Window`] associate with the [`WebView`]. This can let you perform window related - /// actions. - pub fn window(&self) -> &Window { - &self.window - } - /// Get the current url of the webview pub fn url(&self) -> Url { self.webview.url() @@ -1034,40 +1027,6 @@ impl WebView { self.webview.is_devtools_open() } - /// Gets the physical size of the webview’s client area. This is - /// a drop-in replacement for [`Window::inner_size`] because on some platforms - /// (currently, only macOS), it will return an incorrect size. - /// - /// ```no_run - /// use wry::{ - /// application::{ - /// event_loop::EventLoop, - /// window::WindowBuilder - /// }, - /// webview::WebViewBuilder, - /// }; - /// let event_loop = EventLoop::new(); - /// let window = WindowBuilder::new().build(&event_loop).unwrap(); - /// let webview = WebViewBuilder::new(window) - /// .unwrap() - /// .build() - /// .unwrap(); - /// - /// // This returns incorrect window size on macOS. - /// println!("{:?}", webview.window().inner_size()); - /// // Instead, this always returns the correct window size. - /// println!("{:?}", webview.inner_size()); - /// ``` - pub fn inner_size(&self) -> PhysicalSize { - #[cfg(target_os = "macos")] - { - let scale_factor = self.window.scale_factor(); - self.webview.inner_size(scale_factor) - } - #[cfg(not(target_os = "macos"))] - self.window.inner_size() - } - /// Set the webview zoom level /// /// ## Platform-specific: @@ -1117,13 +1076,13 @@ pub enum FileDropEvent { Hovered { paths: Vec, /// The position of the mouse cursor. - position: PhysicalPosition, + position: Position, }, /// The file(s) have been dropped onto the window. Dropped { paths: Vec, /// The position of the mouse cursor. - position: PhysicalPosition, + position: Position, }, /// The file drop was aborted. Cancelled, diff --git a/src/webview/wkwebview/file_drop.rs b/src/webview/wkwebview/file_drop.rs index 36992eb92..cb02e5468 100644 --- a/src/webview/wkwebview/file_drop.rs +++ b/src/webview/wkwebview/file_drop.rs @@ -5,7 +5,6 @@ use std::{ ffi::{c_void, CStr}, path::PathBuf, - rc::Rc, }; use cocoa::{ @@ -18,10 +17,7 @@ use objc::{ }; use once_cell::sync::Lazy; -use crate::{ - application::{dpi::LogicalPosition, platform::macos::WindowExtMacOS, window::Window}, - webview::FileDropEvent, -}; +use crate::{application::dpi::LogicalPosition, webview::FileDropEvent}; pub(crate) type NSDragOperation = cocoa::foundation::NSUInteger; #[allow(non_upper_case_globals)] @@ -61,20 +57,17 @@ static OBJC_DRAGGING_UPDATED: Lazy NSDr // Safety: objc runtime calls are unsafe pub(crate) unsafe fn set_file_drop_handler( webview: *mut Object, - window: Rc, - handler: Box bool>, -) -> *mut (Box bool>, Rc) { - let listener = Box::into_raw(Box::new((handler, window))); + handler: Box bool>, +) -> *mut Box bool> { + let listener = Box::into_raw(Box::new(handler)); (*webview).set_ivar("FileDropHandler", listener as *mut _ as *mut c_void); listener } #[allow(clippy::mut_from_ref)] -unsafe fn get_handler( - this: &Object, -) -> &mut (Box bool>, Rc) { +unsafe fn get_handler(this: &Object) -> &mut Box bool> { let delegate: *mut c_void = *this.get_ivar("FileDropHandler"); - &mut *(delegate as *mut (Box bool>, Rc)) + &mut *(delegate as *mut Box bool>) } unsafe fn collect_paths(drag_info: id) -> Vec { @@ -116,13 +109,10 @@ extern "C" fn dragging_entered(this: &mut Object, sel: Sel, drag_info: id) -> NS let paths = unsafe { collect_paths(drag_info) }; let dl: NSPoint = unsafe { msg_send![drag_info, draggingLocation] }; - let scale_factor = listener.1.scale_factor(); - let ns_window = listener.1.ns_window() as id; - let frame: NSRect = unsafe { msg_send![ns_window, frame] }; - let position = - LogicalPosition::::from((dl.x, frame.size.height - dl.y)).to_physical(scale_factor); + let frame: NSRect = unsafe { msg_send![this, frame] }; + let position = LogicalPosition::::from((dl.x, frame.size.height - dl.y)).into(); - if !listener.0(&listener.1, FileDropEvent::Hovered { paths, position }) { + if !listener(FileDropEvent::Hovered { paths, position }) { // Reject the Wry file drop (invoke the OS default behaviour) OBJC_DRAGGING_ENTERED(this, sel, drag_info) } else { @@ -135,13 +125,10 @@ extern "C" fn perform_drag_operation(this: &mut Object, sel: Sel, drag_info: id) let paths = unsafe { collect_paths(drag_info) }; let dl: NSPoint = unsafe { msg_send![drag_info, draggingLocation] }; - let scale_factor = listener.1.scale_factor(); - let ns_window = listener.1.ns_window() as id; - let frame: NSRect = unsafe { msg_send![ns_window, frame] }; - let position = - LogicalPosition::::from((dl.x, frame.size.height - dl.y)).to_physical(scale_factor); + let frame: NSRect = unsafe { msg_send![this, frame] }; + let position = LogicalPosition::::from((dl.x, frame.size.height - dl.y)).into(); - if !listener.0(&listener.1, FileDropEvent::Dropped { paths, position }) { + if !listener(FileDropEvent::Dropped { paths, position }) { // Reject the Wry file drop (invoke the OS default behaviour) OBJC_PERFORM_DRAG_OPERATION(this, sel, drag_info) } else { @@ -151,7 +138,7 @@ extern "C" fn perform_drag_operation(this: &mut Object, sel: Sel, drag_info: id) extern "C" fn dragging_exited(this: &mut Object, sel: Sel, drag_info: id) { let listener = unsafe { get_handler(this) }; - if !listener.0(&listener.1, FileDropEvent::Cancelled) { + if !listener(FileDropEvent::Cancelled) { // Reject the Wry file drop (invoke the OS default behaviour) OBJC_DRAGGING_EXITED(this, sel, drag_info); } diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index a070a2d36..0ae6312a6 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -11,6 +11,7 @@ mod proxy; #[cfg(target_os = "macos")] mod synthetic_mouse_events; +use raw_window_handle::RawWindowHandle; use url::Url; #[cfg(target_os = "macos")] @@ -25,7 +26,6 @@ use std::{ ffi::{c_void, CStr}, os::raw::c_char, ptr::{null, null_mut}, - rc::Rc, slice, str, sync::{Arc, Mutex}, }; @@ -37,14 +37,9 @@ use objc::{ }; use objc_id::Id; -#[cfg(target_os = "macos")] -use crate::application::platform::macos::WindowExtMacOS; #[cfg(target_os = "macos")] use file_drop::{add_file_drop_methods, set_file_drop_handler}; -#[cfg(target_os = "ios")] -use crate::application::platform::ios::WindowExtIOS; - #[cfg(feature = "mac-proxy")] use crate::webview::{ proxy::ProxyConfig, @@ -54,10 +49,6 @@ use crate::webview::{ }; use crate::{ - application::{ - dpi::{LogicalSize, PhysicalSize}, - window::Window, - }, webview::{ wkwebview::{ download::{ @@ -68,7 +59,7 @@ use crate::{ }, FileDropEvent, PageLoadEvent, RequestAsyncResponder, WebContext, WebViewAttributes, RGBA, }, - Result, + Error, Result, }; use http::{ @@ -91,36 +82,43 @@ pub(crate) struct InnerWebView { pending_scripts: Arc>>>, // Note that if following functions signatures are changed in the future, // all functions pointer declarations in objc callbacks below all need to get updated. - ipc_handler_ptr: *mut (Box, Rc), - document_title_changed_handler: *mut (Box, Rc), + ipc_handler_ptr: *mut Box, + document_title_changed_handler: *mut Box, navigation_decide_policy_ptr: *mut Box bool>, page_load_handler: *mut Box, #[cfg(target_os = "macos")] - file_drop_ptr: *mut (Box bool>, Rc), + file_drop_ptr: *mut Box bool>, download_delegate: id, protocol_ptrs: Vec<*mut Box>, RequestAsyncResponder)>>, } impl InnerWebView { pub fn new( - window: Rc, + window: RawWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { + let window = match window { + #[cfg(target_os = "macos")] + RawWindowHandle::AppKit(w) => w, + #[cfg(target_os = "ios")] + RawWindowHandle::UiKit(w) => w, + _ => return Err(Error::WindowHandleError), + }; + // Function for ipc handler extern "C" fn did_receive(this: &Object, _: Sel, _: id, msg: id) { // Safety: objc runtime calls are unsafe unsafe { let function = this.get_ivar::<*mut c_void>("function"); if !function.is_null() { - let function = - &mut *(*function as *mut (Box Fn(&'r Window, String)>, Rc)); + let function = &mut *(*function as *mut Box); let body: id = msg_send![msg, body]; let utf8: *const c_char = msg_send![body, UTF8String]; let js = CStr::from_ptr(utf8).to_str().expect("Invalid UTF8 string"); - (function.0)(&function.1, js.to_string()); + (function)(js.to_string()); } else { log::warn!("WebView instance is dropped! This handler shouldn't be called."); } @@ -380,7 +378,7 @@ impl InnerWebView { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view() as id; + let ui_view = window.ui_view as id; let frame: CGRect = msg_send![ui_view, frame]; // set all autoresizingmasks let () = msg_send![webview, setAutoresizingMask: 31]; @@ -425,7 +423,7 @@ impl InnerWebView { None => class!(WebViewDelegate), }; let handler: id = msg_send![cls, new]; - let ipc_handler_ptr = Box::into_raw(Box::new((ipc_handler, window.clone()))); + let ipc_handler_ptr = Box::into_raw(Box::new(ipc_handler)); (*handler).set_ivar("function", ipc_handler_ptr as *mut _ as *mut c_void); let ipc = NSString::new(IPC_MESSAGE_HANDLER_NAME); @@ -460,10 +458,9 @@ impl InnerWebView { unsafe { let function = this.get_ivar::<*mut c_void>("function"); if !function.is_null() { - let function = &mut *(*function - as *mut (Box Fn(&'r Window, String)>, Rc)); + let function = &mut *(*function as *mut Box); let title: id = msg_send![of_object, title]; - (function.0)(&function.1, NSString(title).to_str().to_string()); + (function)(NSString(title).to_str().to_string()); } } } @@ -475,7 +472,7 @@ impl InnerWebView { let handler: id = msg_send![cls, new]; let document_title_changed_handler = - Box::into_raw(Box::new((document_title_changed_handler, window.clone()))); + Box::into_raw(Box::new(document_title_changed_handler)); (*handler).set_ivar( "function", @@ -752,17 +749,15 @@ impl InnerWebView { #[cfg(target_os = "macos")] let file_drop_ptr = match attributes.file_drop_handler { // if we have a file_drop_handler defined, use the defined handler - Some(file_drop_handler) => { - set_file_drop_handler(webview, window.clone(), file_drop_handler) - } + Some(file_drop_handler) => set_file_drop_handler(webview, file_drop_handler), // prevent panic by using a blank handler - None => set_file_drop_handler(webview, window.clone(), Box::new(|_, _| false)), + None => set_file_drop_handler(webview, Box::new(|_| false)), }; // ns window is required for the print operation #[cfg(target_os = "macos")] let ns_window = { - let ns_window = window.ns_window() as id; + let ns_window = window.ns_window as id; let can_set_titlebar_style: BOOL = msg_send![ ns_window, @@ -851,7 +846,7 @@ r#"Object.defineProperty(window, 'ipc', { let _: () = msg_send![parent_view, addSubview: webview]; // inject the webview into the window - let ns_window = window.ns_window() as id; + let ns_window = window.ns_window as id; // Tell the webview receive keyboard events in the window. // See https://github.com/tauri-apps/wry/issues/739 let _: () = msg_send![ns_window, setContentView: parent_view]; @@ -1030,13 +1025,6 @@ r#"Object.defineProperty(window, 'ipc', { false } - #[cfg(target_os = "macos")] - pub fn inner_size(&self, scale_factor: f64) -> PhysicalSize { - let view_frame = unsafe { NSView::frame(self.webview) }; - let logical: LogicalSize = (view_frame.size.width, view_frame.size.height).into(); - logical.to_physical(scale_factor) - } - pub fn zoom(&self, scale_factor: f64) { unsafe { let _: () = msg_send![self.webview, setPageZoom: scale_factor]; From 5189de591cfca26792daddfd41ecfc92d552b868 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Sun, 22 Oct 2023 20:52:53 +0900 Subject: [PATCH 02/80] Update doc and add change file --- .changes/rwh.md | 11 +++++++++++ src/lib.rs | 3 ++- src/webview/mod.rs | 9 +++++---- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changes/rwh.md diff --git a/.changes/rwh.md b/.changes/rwh.md new file mode 100644 index 000000000..dc4875caf --- /dev/null +++ b/.changes/rwh.md @@ -0,0 +1,11 @@ +--- +"wry": minor, +--- + +Refactor new method to take raw window handle instead. Following are APIs got affected: + - `WebviewBuilder::new`, `Webview::new` now take `RawWindowHandle` instead. + - Attributes `ipc_handler`, `file_drop_handler`, `document_change_handler` don't have window parameter anymore. + Users should use closure to capture the types they want to use. + - Position field in `FileDrop` event is now `Position` instead of `PhysicalPosition`. Users need to handle scale factor + depend on the situation they have. + - `Webview::inner_size` is removed. diff --git a/src/lib.rs b/src/lib.rs index 948604b9d..520a5858e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,12 +19,13 @@ //! }, //! webview::WebViewBuilder, //! }; +//! use raw_window_handle::HasRawWindowHandle; //! //! let event_loop = EventLoop::new(); //! let window = WindowBuilder::new() //! .with_title("Hello World") //! .build(&event_loop)?; -//! let _webview = WebViewBuilder::new(window)? +//! let _webview = WebViewBuilder::new(window.raw_window_handle())? //! .with_url("https://tauri.studio")? //! .build()?; //! diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 34c441d64..576fe4a83 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -380,7 +380,7 @@ pub struct WebViewBuilder<'a> { } impl<'a> WebViewBuilder<'a> { - /// Create [`WebViewBuilder`] from provided [`Window`]. + /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. pub fn new(window: RawWindowHandle) -> Result { let webview = WebViewAttributes::default(); let web_context = None; @@ -390,7 +390,7 @@ impl<'a> WebViewBuilder<'a> { Ok(Self { webview, web_context, - window: window, + window, platform_specific, }) } @@ -509,12 +509,13 @@ impl<'a> WebViewBuilder<'a> { /// }, /// webview::WebViewBuilder, /// }; + /// use raw_window_handle::HasRawWindowHandle; /// /// let event_loop = EventLoop::new(); /// let window = WindowBuilder::new() /// .build(&event_loop) /// .unwrap(); - /// WebViewBuilder::new(window) + /// WebViewBuilder::new(window.raw_window_handle()) /// .unwrap() /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| { /// // here you can use a tokio task, thread pool or anything @@ -943,7 +944,7 @@ impl Drop for WebView { } impl WebView { - /// Create a [`WebView`] from provided [`Window`]. Note that calling this directly loses + /// Create a [`WebView`] from provided [`RawWindowHandle`]. Note that calling this directly loses /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To /// benefit from above features, create a [`WebViewBuilder`] instead. /// From 4bafb90261c2281665dd697aadd42d4fa440e0d6 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Sun, 22 Oct 2023 20:57:21 +0900 Subject: [PATCH 03/80] Fix mac and ios compile error --- src/webview/mod.rs | 3 +-- src/webview/wkwebview/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 576fe4a83..35b6a0555 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -8,7 +8,6 @@ mod proxy; mod web_context; use raw_window_handle::RawWindowHandle; -use tao::dpi::Position; pub use web_context::WebContext; #[cfg(target_os = "android")] @@ -45,7 +44,7 @@ use wkwebview::*; pub(crate) mod webview2; #[cfg(target_os = "windows")] use self::webview2::*; -use crate::Result; +use crate::{application::dpi::Position, Result}; #[cfg(target_os = "windows")] use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; #[cfg(target_os = "windows")] diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index 0ae6312a6..ea5274ab4 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -860,7 +860,7 @@ r#"Object.defineProperty(window, 'ipc', { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view() as id; + let ui_view = window.ui_view as id; let _: () = msg_send![ui_view, addSubview: webview]; } From dc11115fcb7e18f530fe26fb7ca7e0292741b002 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Sun, 22 Oct 2023 22:02:24 +0900 Subject: [PATCH 04/80] Add new_as_child --- .changes/rwh.md | 2 +- src/webview/mod.rs | 23 +++++++++++++ src/webview/wkwebview/mod.rs | 66 +++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/.changes/rwh.md b/.changes/rwh.md index dc4875caf..54b06d7c0 100644 --- a/.changes/rwh.md +++ b/.changes/rwh.md @@ -1,5 +1,5 @@ --- -"wry": minor, +"wry": minor --- Refactor new method to take raw window handle instead. Following are APIs got affected: diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 35b6a0555..a442b8193 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -268,6 +268,10 @@ pub struct WebViewAttributes { /// /// - **macOS / Android / iOS:** Unsupported. pub focused: bool, + + /// TODO doc + pub position: Option<(isize, isize)>, + pub size: Option<(isize, isize)>, } impl Default for WebViewAttributes { @@ -302,6 +306,8 @@ impl Default for WebViewAttributes { on_page_load_handler: None, proxy_config: None, focused: true, + position: None, + size: None, } } } @@ -394,6 +400,23 @@ impl<'a> WebViewBuilder<'a> { }) } + /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc + pub fn new_as_child(window: RawWindowHandle, position: (isize, isize), size: (isize, isize)) -> Result { + let mut webview = WebViewAttributes::default(); + let web_context = None; + #[allow(clippy::default_constructed_unit_structs)] + let platform_specific = PlatformSpecificWebViewAttributes::default(); + webview.position = Some(position); + webview.size = Some(size); + + Ok(Self { + webview, + web_context, + window, + platform_specific, + }) + } + /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation. /// /// ## Platform-specific: diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index ea5274ab4..004aa66ef 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -370,7 +370,12 @@ impl InnerWebView { #[cfg(target_os = "macos")] { use core_graphics::geometry::{CGPoint, CGSize}; - let frame: CGRect = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(0., 0.)); + let (x, y) = attributes.position.unwrap_or((0, 0)); + let (w, h) = attributes.size.unwrap_or((0, 0)); + let frame: CGRect = CGRect::new( + &CGPoint::new(x as f64, y as f64), + &CGSize::new(w as f64, h as f64), + ); let _: () = msg_send![webview, initWithFrame:frame configuration:config]; // Auto-resize on macOS webview.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); @@ -820,37 +825,42 @@ r#"Object.defineProperty(window, 'ipc', { // Inject the web view into the window as main content #[cfg(target_os = "macos")] { - let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { - Some(mut decl) => { - decl.add_method( - sel!(keyDown:), - key_down as extern "C" fn(&mut Object, Sel, id), - ); + if attributes.position.is_some() || attributes.size.is_some() { + let ns_view = window.ns_view as id; + let _: () = msg_send![ns_view, addSubview: webview]; + } else { + let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { + Some(mut decl) => { + decl.add_method( + sel!(keyDown:), + key_down as extern "C" fn(&mut Object, Sel, id), + ); - extern "C" fn key_down(_this: &mut Object, _sel: Sel, event: id) { - unsafe { - let app = cocoa::appkit::NSApp(); - let menu: id = msg_send![app, mainMenu]; - let () = msg_send![menu, performKeyEquivalent: event]; + extern "C" fn key_down(_this: &mut Object, _sel: Sel, event: id) { + unsafe { + let app = cocoa::appkit::NSApp(); + let menu: id = msg_send![app, mainMenu]; + let () = msg_send![menu, performKeyEquivalent: event]; + } } - } - - decl.register() - } - None => class!(NSView), - }; - let parent_view: id = msg_send![parent_view_cls, alloc]; - let _: () = msg_send![parent_view, init]; - parent_view.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); - let _: () = msg_send![parent_view, addSubview: webview]; + decl.register() + } + None => class!(NSView), + }; - // inject the webview into the window - let ns_window = window.ns_window as id; - // Tell the webview receive keyboard events in the window. - // See https://github.com/tauri-apps/wry/issues/739 - let _: () = msg_send![ns_window, setContentView: parent_view]; - let _: () = msg_send![ns_window, makeFirstResponder: webview]; + let parent_view: id = msg_send![parent_view_cls, alloc]; + let _: () = msg_send![parent_view, init]; + parent_view.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); + let _: () = msg_send![parent_view, addSubview: webview]; + + // inject the webview into the window + let ns_window = window.ns_window as id; + // Tell the webview receive keyboard events in the window. + // See https://github.com/tauri-apps/wry/issues/739 + let _: () = msg_send![ns_window, setContentView: parent_view]; + let _: () = msg_send![ns_window, makeFirstResponder: webview]; + } // make sure the window is always on top when we create a new webview let app_class = class!(NSApplication); From ee0a55aa443c9ce2bfb4bb79edf9b339f4cd5178 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 23 Oct 2023 21:33:49 +0900 Subject: [PATCH 05/80] Add rhw_04, 05 06 flags --- .changes/rwh.md | 1 + Cargo.toml | 6 ++++-- examples/custom_protocol.rs | 2 +- examples/custom_titlebar.rs | 2 +- examples/detect_js_ecma.rs | 2 +- examples/download_event.rs | 2 +- examples/dragndrop.rs | 2 +- examples/eval_js.rs | 2 +- examples/form.rs | 2 +- examples/fullscreen.rs | 2 +- examples/hello_world.rs | 2 +- examples/multi_window.rs | 2 +- examples/navigation_event.rs | 2 +- examples/new_window_req_event.rs | 2 +- examples/proxy.rs | 2 +- examples/stream_range.rs | 2 +- examples/transparent.rs | 2 +- examples/user_agent.rs | 2 +- src/lib.rs | 2 +- src/webview/mod.rs | 9 +++++++-- src/webview/wkwebview/mod.rs | 7 ++++++- 21 files changed, 35 insertions(+), 22 deletions(-) diff --git a/.changes/rwh.md b/.changes/rwh.md index 54b06d7c0..08c7abfed 100644 --- a/.changes/rwh.md +++ b/.changes/rwh.md @@ -9,3 +9,4 @@ Refactor new method to take raw window handle instead. Following are APIs got af - Position field in `FileDrop` event is now `Position` instead of `PhysicalPosition`. Users need to handle scale factor depend on the situation they have. - `Webview::inner_size` is removed. + - Added `rwh_04`, `rwh_05`, `rwh_06` feature flags. diff --git a/Cargo.toml b/Cargo.toml index 03099e4b5..dbc301fd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ targets = [ ] [features] -default = [ "file-drop", "objc-exception", "protocol", "tao" ] +default = [ "file-drop", "objc-exception", "protocol", "tao", "rwh_05" ] objc-exception = [ "objc/exception" ] file-drop = [ ] protocol = [ ] @@ -42,7 +42,9 @@ thiserror = "1.0" url = "2.4" tao = { version = "0.23", default-features = false, features = [ "serde" ], optional = true } http = "0.2.9" -raw-window-handle = "0.5" +rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } +rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true } +rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } [dev-dependencies] http-range = "0.1.5" diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 95296fa26..8cc2e7e36 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -8,7 +8,7 @@ use std::{ }; use http::Request; -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; use wry::{ application::{ event::{Event, StartCause, WindowEvent}, diff --git a/examples/custom_titlebar.rs b/examples/custom_titlebar.rs index 3b7e60e4b..355f1a790 100644 --- a/examples/custom_titlebar.rs +++ b/examples/custom_titlebar.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/detect_js_ecma.rs b/examples/detect_js_ecma.rs index 84aa7c063..a36e2b63f 100644 --- a/examples/detect_js_ecma.rs +++ b/examples/detect_js_ecma.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/download_event.rs b/examples/download_event.rs index 7ea0fcf3c..8ddc71c64 100644 --- a/examples/download_event.rs +++ b/examples/download_event.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use std::{env::temp_dir, path::PathBuf}; diff --git a/examples/dragndrop.rs b/examples/dragndrop.rs index cf88e121f..74c60ff88 100644 --- a/examples/dragndrop.rs +++ b/examples/dragndrop.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/eval_js.rs b/examples/eval_js.rs index 5cc6eede6..946d6826e 100644 --- a/examples/eval_js.rs +++ b/examples/eval_js.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/form.rs b/examples/form.rs index 4b7bca758..f7c3fb14f 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,7 +7,7 @@ use std::{ fs::{canonicalize, read}, }; -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; use wry::{ application::{ event::{Event, StartCause, WindowEvent}, diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 08b1abdc1..6ff03d169 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 2bb1a1586..0c7b1224c 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/multi_window.rs b/examples/multi_window.rs index 313163ce0..3b82f6d33 100644 --- a/examples/multi_window.rs +++ b/examples/multi_window.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use std::collections::HashMap; diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index 740a9af3d..8d80b0140 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/new_window_req_event.rs b/examples/new_window_req_event.rs index 4ea52ba56..201853ecd 100644 --- a/examples/new_window_req_event.rs +++ b/examples/new_window_req_event.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/proxy.rs b/examples/proxy.rs index c859d3e9b..2feb5346d 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; use wry::webview::{ProxyConfig, ProxyEndpoint}; fn main() -> wry::Result<()> { diff --git a/examples/stream_range.rs b/examples/stream_range.rs index 101e3ef71..6618fa5c9 100644 --- a/examples/stream_range.rs +++ b/examples/stream_range.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use http_range::HttpRange; -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; use std::{ borrow::Cow, fs::{canonicalize, File}, diff --git a/examples/transparent.rs b/examples/transparent.rs index c7a84efd2..378761709 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/examples/user_agent.rs b/examples/user_agent.rs index a44948cf5..741219050 100644 --- a/examples/user_agent.rs +++ b/examples/user_agent.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use raw_window_handle::HasRawWindowHandle; +use rwh_05::HasRawWindowHandle; fn main() -> wry::Result<()> { use wry::{ diff --git a/src/lib.rs b/src/lib.rs index 520a5858e..b3c36cc1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ //! }, //! webview::WebViewBuilder, //! }; -//! use raw_window_handle::HasRawWindowHandle; +//! use rwh_05::HasRawWindowHandle; //! //! let event_loop = EventLoop::new(); //! let window = WindowBuilder::new() diff --git a/src/webview/mod.rs b/src/webview/mod.rs index a442b8193..32cc394e5 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -7,7 +7,12 @@ mod proxy; mod web_context; -use raw_window_handle::RawWindowHandle; +#[cfg(feature = "rwh_04")] +use rwh_04::RawWindowHandle; +#[cfg(feature = "rwh_05")] +use rwh_05::RawWindowHandle; +#[cfg(feature = "rwh_06")] +use rwh_06::RawWindowHandle; pub use web_context::WebContext; #[cfg(target_os = "android")] @@ -531,7 +536,7 @@ impl<'a> WebViewBuilder<'a> { /// }, /// webview::WebViewBuilder, /// }; - /// use raw_window_handle::HasRawWindowHandle; + /// use rwh_05::HasRawWindowHandle; /// /// let event_loop = EventLoop::new(); /// let window = WindowBuilder::new() diff --git a/src/webview/wkwebview/mod.rs b/src/webview/wkwebview/mod.rs index 004aa66ef..58c6b9990 100644 --- a/src/webview/wkwebview/mod.rs +++ b/src/webview/wkwebview/mod.rs @@ -11,7 +11,12 @@ mod proxy; #[cfg(target_os = "macos")] mod synthetic_mouse_events; -use raw_window_handle::RawWindowHandle; +#[cfg(feature = "rwh_04")] +use rwh_04::RawWindowHandle; +#[cfg(feature = "rwh_05")] +use rwh_05::RawWindowHandle; +#[cfg(feature = "rwh_06")] +use rwh_06::RawWindowHandle; use url::Url; #[cfg(target_os = "macos")] From 787fb2884a8c65b79036b82e72096cbc452a2c12 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Tue, 24 Oct 2023 11:41:10 +0900 Subject: [PATCH 06/80] Update android port --- src/webview/android/mod.rs | 22 +++++++++++++--------- src/webview/mod.rs | 6 +++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/webview/android/mod.rs b/src/webview/android/mod.rs index bb87d5dbc..fe6ccf7f7 100644 --- a/src/webview/android/mod.rs +++ b/src/webview/android/mod.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use super::{PageLoadEvent, WebContext, WebViewAttributes, RGBA}; -use crate::{application::window::Window, webview::RequestAsyncResponder, Result}; +use crate::{webview::RequestAsyncResponder, Result}; use base64::{engine::general_purpose, Engine}; use crossbeam_channel::*; use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName}; @@ -13,6 +13,12 @@ use http::{ }; use kuchiki::NodeRef; use once_cell::sync::OnceCell; +#[cfg(feature = "rwh_04")] +use rwh_04::RawWindowHandle; +#[cfg(feature = "rwh_05")] +use rwh_05::RawWindowHandle; +#[cfg(feature = "rwh_06")] +use rwh_06::RawWindowHandle; use sha2::{Digest, Sha256}; use std::{borrow::Cow, rc::Rc, sync::mpsc::channel}; use tao::platform::android::ndk_glue::{ @@ -55,9 +61,9 @@ macro_rules! define_static_handlers { } define_static_handlers! { - IPC = UnsafeIpc { handler: Box, window: Rc }; + IPC = UnsafeIpc { handler: Box }; REQUEST_HANDLER = UnsafeRequestHandler { handler: Box>) -> Option>>> }; - TITLE_CHANGE_HANDLER = UnsafeTitleHandler { handler: Box, window: Rc }; + TITLE_CHANGE_HANDLER = UnsafeTitleHandler { handler: Box }; URL_LOADING_OVERRIDE = UnsafeUrlLoadingOverride { handler: Box bool> }; ON_LOAD_HANDLER = UnsafeOnPageLoadHandler { handler: Box }; } @@ -105,12 +111,12 @@ pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef pub(crate) struct InnerWebView { #[allow(unused)] - pub window: Rc, + pub window: RawWindowHandle, } impl InnerWebView { pub fn new( - window: Rc, + window: RawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -247,14 +253,12 @@ impl InnerWebView { })) }); - let w = window.clone(); if let Some(i) = ipc_handler { - IPC.get_or_init(move || UnsafeIpc::new(Box::new(i), w)); + IPC.get_or_init(move || UnsafeIpc::new(Box::new(i))); } - let w = window.clone(); if let Some(i) = attributes.document_title_changed_handler { - TITLE_CHANGE_HANDLER.get_or_init(move || UnsafeTitleHandler::new(i, w)); + TITLE_CHANGE_HANDLER.get_or_init(move || UnsafeTitleHandler::new(i)); } if let Some(i) = attributes.navigation_handler { diff --git a/src/webview/mod.rs b/src/webview/mod.rs index 32cc394e5..12c5e1296 100644 --- a/src/webview/mod.rs +++ b/src/webview/mod.rs @@ -406,7 +406,11 @@ impl<'a> WebViewBuilder<'a> { } /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc - pub fn new_as_child(window: RawWindowHandle, position: (isize, isize), size: (isize, isize)) -> Result { + pub fn new_as_child( + window: RawWindowHandle, + position: (isize, isize), + size: (isize, isize), + ) -> Result { let mut webview = WebViewAttributes::default(); let web_context = None; #[allow(clippy::default_constructed_unit_structs)] From c7c1f7e842dd72e8ac32246522215c250c55d1c1 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Tue, 24 Oct 2023 11:59:48 +0900 Subject: [PATCH 07/80] Remove winit CI --- .github/workflows/build.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 44fe908d7..5a9cb8a4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,9 +55,3 @@ jobs: !contains(matrix.platform.target, 'ios')) run: cargo test --verbose --target ${{ matrix.platform.target }} - - name: build wry with winit - if: ( - contains(matrix.platform.target, 'gnu') || - contains(matrix.platform.target, 'windows') || - contains(matrix.platform.target, 'apple')) - run: cargo build --no-default-features --features winit --target ${{ matrix.platform.target }} From 5d0b24279a9a515ad1452a86332dc7d8f57f4839 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Tue, 24 Oct 2023 12:25:14 +0900 Subject: [PATCH 08/80] Fix android bindings --- src/webview/android/binding.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webview/android/binding.rs b/src/webview/android/binding.rs index 72d09a903..6a5deecb2 100644 --- a/src/webview/android/binding.rs +++ b/src/webview/android/binding.rs @@ -266,7 +266,7 @@ pub unsafe fn ipc(mut env: JNIEnv, _: JClass, arg: JString) { Ok(arg) => { let arg = arg.to_string_lossy().to_string(); if let Some(ipc) = IPC.get() { - (ipc.handler)(&ipc.window, arg) + (ipc.handler)(arg) } } Err(e) => log::warn!("Failed to parse JString: {}", e), @@ -279,7 +279,7 @@ pub unsafe fn handleReceivedTitle(mut env: JNIEnv, _: JClass, _webview: JObject, Ok(title) => { let title = title.to_string_lossy().to_string(); if let Some(title_handler) = TITLE_CHANGE_HANDLER.get() { - (title_handler.handler)(&title_handler.window, title) + (title_handler.handler)(&title_handler, title) } } Err(e) => log::warn!("Failed to parse JString: {}", e), From b5770cd306a88e6533fed543998726b082d47778 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 24 Oct 2023 06:26:01 +0300 Subject: [PATCH 09/80] windows implementation --- Cargo.toml | 24 +- examples/custom_protocol.rs | 29 +- examples/custom_titlebar.rs | 59 +- examples/detect_js_ecma.rs | 34 +- examples/download_event.rs | 24 +- examples/dragndrop.rs | 32 +- examples/eval_js.rs | 22 +- examples/form.rs | 30 +- examples/fullscreen.rs | 32 +- examples/hello_world.rs | 34 +- examples/multi_window.rs | 118 +- examples/navigation_event.rs | 30 +- examples/new_window_req_event.rs | 30 +- examples/proxy.rs | 35 +- examples/stream_range.rs | 29 +- examples/transparent.rs | 50 +- examples/user_agent.rs | 34 +- src/{webview => }/android/binding.rs | 0 src/{webview => }/android/kotlin/Ipc.kt | 0 src/{webview => }/android/kotlin/Logger.kt | 0 .../android/kotlin/PermissionHelper.kt | 0 .../android/kotlin/RustWebChromeClient.kt | 0 .../android/kotlin/RustWebView.kt | 0 .../android/kotlin/RustWebViewClient.kt | 0 .../android/kotlin/WryActivity.kt | 0 .../android/kotlin/proguard-wry.pro | 0 src/{webview => }/android/main_pipe.rs | 0 src/{webview => }/android/mod.rs | 0 src/application.rs | 16 - src/error.rs | 69 + src/lib.rs | 1342 +++++++++++++++-- src/{webview => }/proxy.rs | 0 src/{webview => }/web_context.rs | 0 src/{webview => }/webkitgtk/file_drop.rs | 0 src/{webview => }/webkitgtk/mod.rs | 0 .../webkitgtk/synthetic_mouse_events.rs | 0 .../webkitgtk/undecorated_resizing.rs | 0 src/{webview => }/webkitgtk/web_context.rs | 0 src/webview/mod.rs | 1239 --------------- src/webview/webview2/resize.rs | 155 -- src/{webview => }/webview2/file_drop.rs | 122 +- src/{webview => }/webview2/mod.rs | 254 ++-- src/{webview => }/wkwebview/download.rs | 0 src/{webview => }/wkwebview/file_drop.rs | 0 src/{webview => }/wkwebview/mod.rs | 0 src/{webview => }/wkwebview/navigation.rs | 0 src/{webview => }/wkwebview/proxy.rs | 0 .../wkwebview/synthetic_mouse_events.rs | 0 48 files changed, 1805 insertions(+), 2038 deletions(-) rename src/{webview => }/android/binding.rs (100%) rename src/{webview => }/android/kotlin/Ipc.kt (100%) rename src/{webview => }/android/kotlin/Logger.kt (100%) rename src/{webview => }/android/kotlin/PermissionHelper.kt (100%) rename src/{webview => }/android/kotlin/RustWebChromeClient.kt (100%) rename src/{webview => }/android/kotlin/RustWebView.kt (100%) rename src/{webview => }/android/kotlin/RustWebViewClient.kt (100%) rename src/{webview => }/android/kotlin/WryActivity.kt (100%) rename src/{webview => }/android/kotlin/proguard-wry.pro (100%) rename src/{webview => }/android/main_pipe.rs (100%) rename src/{webview => }/android/mod.rs (100%) delete mode 100644 src/application.rs create mode 100644 src/error.rs rename src/{webview => }/proxy.rs (100%) rename src/{webview => }/web_context.rs (100%) rename src/{webview => }/webkitgtk/file_drop.rs (100%) rename src/{webview => }/webkitgtk/mod.rs (100%) rename src/{webview => }/webkitgtk/synthetic_mouse_events.rs (100%) rename src/{webview => }/webkitgtk/undecorated_resizing.rs (100%) rename src/{webview => }/webkitgtk/web_context.rs (100%) delete mode 100644 src/webview/mod.rs delete mode 100644 src/webview/webview2/resize.rs rename src/{webview => }/webview2/file_drop.rs (63%) rename src/{webview => }/webview2/mod.rs (86%) rename src/{webview => }/wkwebview/download.rs (100%) rename src/{webview => }/wkwebview/file_drop.rs (100%) rename src/{webview => }/wkwebview/mod.rs (100%) rename src/{webview => }/wkwebview/navigation.rs (100%) rename src/{webview => }/wkwebview/proxy.rs (100%) rename src/{webview => }/wkwebview/synthetic_mouse_events.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index dbc301fd2..ed73d2864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ targets = [ ] [features] -default = [ "file-drop", "objc-exception", "protocol", "tao", "rwh_05" ] +default = [ "file-drop", "objc-exception", "protocol" ] objc-exception = [ "objc/exception" ] file-drop = [ ] protocol = [ ] @@ -40,32 +40,20 @@ serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" thiserror = "1.0" url = "2.4" -tao = { version = "0.23", default-features = false, features = [ "serde" ], optional = true } -http = "0.2.9" -rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } -rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true } -rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } - -[dev-dependencies] -http-range = "0.1.5" -base64 = "0.21" +http = "0.2" +raw-window-handle= { version = "0.6", features = ["std"] } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] javascriptcore-rs = { version = "=1.1.1", features = [ "v2_28" ] } webkit2gtk = { version = "=2.0", features = [ "v2_38" ] } webkit2gtk-sys = "=2.0" -gio = "0.18" -glib = "0.18" gtk = "0.18" -gdk = "0.18" soup3 = "0.5" -winit = { version = "0.29", package = "winit-gtk", features = [ "serde" ], optional = true } [target."cfg(target_os = \"windows\")".dependencies] webview2-com = "0.27" windows-implement = "0.51" dunce = "1" -winit = { version = "0.28", features = [ "serde" ], optional = true } [target."cfg(target_os = \"windows\")".dependencies.windows] version = "0.51" @@ -93,7 +81,6 @@ cocoa = "0.25" core-graphics = "0.23" objc = "0.2" objc_id = "0.1" -winit = { version = "0.28", features = [ "serde" ], optional = true } [target."cfg(target_os = \"android\")".dependencies] crossbeam-channel = "0.5" @@ -101,3 +88,8 @@ html5ever = "0.26" kuchiki = { package = "kuchikiki", version = "0.8" } sha2 = "0.10" base64 = "0.21" + +[dev-dependencies] +http-range = "0.1.5" +tao = { git = "https://github.com/tauri-apps/tao", branch = "rwh" } +winit = "0.29" \ No newline at end of file diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 8cc2e7e36..a36e44c21 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -8,15 +8,14 @@ use std::{ }; use http::Request; -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, http::{header::CONTENT_TYPE, Response}, - webview::WebViewBuilder, + WebViewBuilder, }; const PAGE1_HTML: &[u8] = include_bytes!("custom_protocol_page1.html"); @@ -28,8 +27,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() + let _webview = WebViewBuilder::new(&window) .with_asynchronous_custom_protocol("wry".into(), move |request, responder| { match get_wry_response(request) { Ok(http_response) => responder.respond(http_response), @@ -49,13 +47,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/custom_titlebar.rs b/examples/custom_titlebar.rs index 355f1a790..6010ba7db 100644 --- a/examples/custom_titlebar.rs +++ b/examples/custom_titlebar.rs @@ -2,28 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, StartCause, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; + +enum UserEvent { + Minimize, + Maximize, + DragWindow, + CloseWindow, +} fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - - enum UserEvent { - CloseWindow, - } - let event_loop = EventLoopBuilder::::with_user_event().build(); let window = WindowBuilder::new() .with_decorations(false) .build(&event_loop) .unwrap(); - let window_handle = window.raw_window_handle(); const HTML: &str = r#" @@ -104,7 +102,6 @@ fn main() -> wry::Result<()> { e.detail === 2 ? window.ipc.postMessage('maximize') : window.ipc.postMessage('drag_window'); - } }) document.addEventListener('touchstart', (e) => { if (e.target.classList.contains('drag-region')) { @@ -118,25 +115,24 @@ fn main() -> wry::Result<()> { "#; let proxy = event_loop.create_proxy(); - - let handler = move |req: String| { - if req == "minimize" { - window.set_minimized(true); + let handler = move |req: String| match req.as_str() { + "minimize" => { + let _ = proxy.send_event(UserEvent::Minimize); } - if req == "maximize" { - window.set_maximized(!window.is_maximized()); + "maximize" => { + let _ = proxy.send_event(UserEvent::Maximize); } - if req == "close" { - let _ = proxy.send_event(UserEvent::CloseWindow); + "drag_window" => { + let _ = proxy.send_event(UserEvent::DragWindow); } - if req == "drag_window" { - let _ = window.drag_window(); + "close" => { + let _ = proxy.send_event(UserEvent::CloseWindow); } + _ => {} }; let mut webview = Some( - WebViewBuilder::new(window_handle) - .unwrap() + WebViewBuilder::new(&window) .with_html(HTML)? .with_ipc_handler(handler) .with_accept_first_mouse(true) @@ -156,6 +152,13 @@ fn main() -> wry::Result<()> { let _ = webview.take(); *control_flow = ControlFlow::Exit } + + Event::UserEvent(e) => match e { + UserEvent::Minimize => window.set_minimized(true), + UserEvent::Maximize => window.set_maximized(!window.is_maximized()), + UserEvent::DragWindow => window.drag_window().unwrap(), + UserEvent::CloseWindow => { /* handled above */ } + }, _ => (), } }); diff --git a/examples/detect_js_ecma.rs b/examples/detect_js_ecma.rs index a36e2b63f..e94c27668 100644 --- a/examples/detect_js_ecma.rs +++ b/examples/detect_js_ecma.rs @@ -2,18 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - const HTML: &str = r#" @@ -122,21 +118,17 @@ fn main() -> wry::Result<()> { .with_title("Detect ECMAScript") .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() - .with_html(HTML)? - .build()?; + let _webview = WebViewBuilder::new(&window).with_html(HTML)?.build()?; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/download_event.rs b/examples/download_event.rs index 8ddc71c64..e4d94c101 100644 --- a/examples/download_event.rs +++ b/examples/download_event.rs @@ -2,19 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use std::{env::temp_dir, path::PathBuf}; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use std::{env::temp_dir, path::PathBuf}; - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - const HTML: &str = r#"
@@ -35,8 +31,9 @@ fn main() -> wry::Result<()> { let proxy = event_loop.create_proxy(); let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + .build(&event_loop) + .unwrap(); + let _webview = WebViewBuilder::new(&window) .with_html(HTML)? .with_download_started_handler({ let proxy = proxy.clone(); @@ -70,7 +67,6 @@ fn main() -> wry::Result<()> { *control_flow = ControlFlow::Wait; match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, .. diff --git a/examples/dragndrop.rs b/examples/dragndrop.rs index 74c60ff88..b23eb8f5e 100644 --- a/examples/dragndrop.rs +++ b/examples/dragndrop.rs @@ -2,18 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - const HTML: &str = r#"data:text/html, Drop files onto the window and read the console!
Dropping files onto the following form is also possible:

@@ -22,8 +18,7 @@ Dropping files onto the following form is also possible:

let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() + let _webview = WebViewBuilder::new(&window) .with_url(HTML)? .with_file_drop_handler(|data| { println!("Window 1: {:?}", data); @@ -34,13 +29,12 @@ Dropping files onto the following form is also possible:

event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => {} + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/eval_js.rs b/examples/eval_js.rs index 946d6826e..2750e96b6 100644 --- a/examples/eval_js.rs +++ b/examples/eval_js.rs @@ -2,18 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - enum UserEvent { ExecEval, } @@ -23,7 +19,8 @@ fn main() -> wry::Result<()> { let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; + .build(&event_loop) + .unwrap(); let ipc_handler = move |req: String| { if req == "exec-eval" { @@ -31,7 +28,7 @@ fn main() -> wry::Result<()> { } }; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + let _webview = WebViewBuilder::new(&window) .with_html( r#" @@ -80,7 +77,6 @@ fn main() -> wry::Result<()> { }) .unwrap(); } - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, .. diff --git a/examples/form.rs b/examples/form.rs index f7c3fb14f..0891c9f03 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -7,15 +7,15 @@ use std::{ fs::{canonicalize, read}, }; -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, http::{header::CONTENT_TYPE, method::Method, Response}, - webview::WebViewBuilder, + WebViewBuilder, }; fn main() -> wry::Result<()> { @@ -25,8 +25,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() + let _webview = WebViewBuilder::new(&window) .with_custom_protocol("wry".into(), move |request| { if request.method() == Method::POST { let body_string = String::from_utf8_lossy(request.body()); @@ -53,13 +52,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 6ff03d169..33cceffff 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -2,39 +2,33 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::{Fullscreen, WindowBuilder}, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::{Fullscreen, WindowBuilder}, - }, - webview::WebViewBuilder, - }; - let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("3D Render Test") .with_fullscreen(Some(Fullscreen::Borderless(None))) .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() + let _webview = WebViewBuilder::new(&window) .with_url("https://browserbench.org/MotionMark1.2/")? .build()?; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => {} + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 0c7b1224c..ac3767dfb 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -2,23 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + .build(&event_loop) + .unwrap(); + let _webview = WebViewBuilder::new(&window) .with_url("https://tauri.app")? // .with_incognito(true) .build()?; @@ -26,13 +23,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/multi_window.rs b/examples/multi_window.rs index 3b82f6d33..41bdc6214 100644 --- a/examples/multi_window.rs +++ b/examples/multi_window.rs @@ -2,65 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use std::collections::HashMap; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget}, + window::{Window, WindowBuilder, WindowId}, +}; +use wry::{WebView, WebViewBuilder}; -fn main() -> wry::Result<()> { - use std::collections::HashMap; - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget}, - window::{WindowBuilder, WindowId}, - }, - webview::{WebView, WebViewBuilder}, - }; - - enum UserEvent { - CloseWindow(WindowId), - NewWindow, - } - - fn create_new_window( - title: String, - event_loop: &EventLoopWindowTarget, - proxy: EventLoopProxy, - ) -> (WindowId, WebView) { - let window = WindowBuilder::new() - .with_title(title) - .build(event_loop) - .unwrap(); - let window_handle = window.raw_window_handle(); - let window_id = window.id(); - let handler = move |req: String| match req.as_str() { - "new-window" => { - let _ = proxy.send_event(UserEvent::NewWindow); - } - "close" => { - let _ = proxy.send_event(UserEvent::CloseWindow(window.id())); - } - _ if req.starts_with("change-title") => { - let title = req.replace("change-title:", ""); - window.set_title(title.as_str()); - } - _ => {} - }; - - let webview = WebViewBuilder::new(window_handle) - .unwrap() - .with_html( - r#" - - - - "#, - ) - .unwrap() - .with_ipc_handler(handler) - .build() - .unwrap(); - (window_id, webview) - } +enum UserEvent { + CloseWindow(WindowId), + NewTitle(WindowId, String), + NewWindow, +} +fn main() -> wry::Result<()> { let event_loop = EventLoopBuilder::::with_user_event().build(); let mut webviews = HashMap::new(); let proxy = event_loop.create_proxy(); @@ -70,13 +26,12 @@ fn main() -> wry::Result<()> { &event_loop, proxy.clone(), ); - webviews.insert(new_window.0, new_window.1); + webviews.insert(new_window.0.id(), (new_window.0, new_window.1)); event_loop.run(move |event, event_loop, control_flow| { *control_flow = ControlFlow::Wait; match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, @@ -93,7 +48,7 @@ fn main() -> wry::Result<()> { event_loop, proxy.clone(), ); - webviews.insert(new_window.0, new_window.1); + webviews.insert(new_window.0.id(), (new_window.0, new_window.1)); } Event::UserEvent(UserEvent::CloseWindow(id)) => { webviews.remove(&id); @@ -101,7 +56,50 @@ fn main() -> wry::Result<()> { *control_flow = ControlFlow::Exit } } + + Event::UserEvent(UserEvent::NewTitle(id, title)) => { + webviews.get(&id).unwrap().0.set_title(&title); + } _ => (), } }); } + +fn create_new_window( + title: String, + event_loop: &EventLoopWindowTarget, + proxy: EventLoopProxy, +) -> (Window, WebView) { + let window = WindowBuilder::new() + .with_title(title) + .build(event_loop) + .unwrap(); + let window_id = window.id(); + let handler = move |req: String| match req.as_str() { + "new-window" => { + let _ = proxy.send_event(UserEvent::NewWindow); + } + "close" => { + let _ = proxy.send_event(UserEvent::CloseWindow(window_id)); + } + _ if req.starts_with("change-title") => { + let title = req.replace("change-title:", ""); + let _ = proxy.send_event(UserEvent::NewTitle(window_id, title)); + } + _ => {} + }; + + let webview = WebViewBuilder::new(&window) + .with_html( + r#" + + + + "#, + ) + .unwrap() + .with_ipc_handler(handler) + .build() + .unwrap(); + (window, webview) +} diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs index 8d80b0140..71fcb59f7 100644 --- a/examples/navigation_event.rs +++ b/examples/navigation_event.rs @@ -2,28 +2,25 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; -fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - - enum UserEvent { - Navigation(String), - } +enum UserEvent { + Navigation(String), +} +fn main() -> wry::Result<()> { let event_loop = EventLoopBuilder::::with_user_event().build(); let proxy = event_loop.create_proxy(); let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + .build(&event_loop) + .unwrap(); + let _webview = WebViewBuilder::new(&window) .with_url("http://neverssl.com")? .with_navigation_handler(move |uri: String| { let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); @@ -36,7 +33,6 @@ fn main() -> wry::Result<()> { *control_flow = ControlFlow::Wait; match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, .. diff --git a/examples/new_window_req_event.rs b/examples/new_window_req_event.rs index 201853ecd..af7a786ca 100644 --- a/examples/new_window_req_event.rs +++ b/examples/new_window_req_event.rs @@ -2,22 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoopBuilder}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; + +enum UserEvent { + NewWindow(String), +} fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - - enum UserEvent { - NewWindow(String), - } - let html = r#"
@@ -32,8 +28,9 @@ fn main() -> wry::Result<()> { let proxy = event_loop.create_proxy(); let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + .build(&event_loop) + .unwrap(); + let _webview = WebViewBuilder::new(&window) .with_html(html)? .with_new_window_req_handler(move |uri: String| { let submitted = proxy.send_event(UserEvent::NewWindow(uri.clone())).is_ok(); @@ -46,7 +43,6 @@ fn main() -> wry::Result<()> { *control_flow = ControlFlow::Wait; match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), Event::WindowEvent { event: WindowEvent::CloseRequested, .. diff --git a/examples/proxy.rs b/examples/proxy.rs index 2feb5346d..902def74c 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -2,30 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; -use wry::webview::{ProxyConfig, ProxyEndpoint}; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{ProxyConfig, ProxyEndpoint, WebViewBuilder}; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("Proxy Test") - .build(&event_loop)?; + .build(&event_loop) + .unwrap(); let http_proxy = ProxyConfig::Http(ProxyEndpoint { host: "localhost".to_string(), port: "3128".to_string(), }); - let _webview = WebViewBuilder::new(window.raw_window_handle())? + let _webview = WebViewBuilder::new(&window) .with_proxy_config(http_proxy) .with_url("https://www.myip.com/")? .build()?; @@ -33,13 +29,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/stream_range.rs b/examples/stream_range.rs index 6618fa5c9..711f92fcf 100644 --- a/examples/stream_range.rs +++ b/examples/stream_range.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: MIT use http_range::HttpRange; -use rwh_05::HasRawWindowHandle; use std::{ borrow::Cow, fs::{canonicalize, File}, @@ -11,14 +10,14 @@ use std::{ path::PathBuf, process::{Command, Stdio}, }; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, http::{header::CONTENT_TYPE, status::StatusCode, Response}, - webview::WebViewBuilder, + WebViewBuilder, }; fn main() -> wry::Result<()> { @@ -50,8 +49,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(window.raw_window_handle()) - .unwrap() + let _webview = WebViewBuilder::new(&window) .with_custom_protocol("wry".into(), move |request| { get_stream_response(request).unwrap_or_else(|error| { http::Response::builder() @@ -68,13 +66,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry application started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => {} + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/transparent.rs b/examples/transparent.rs index 378761709..6e97d3246 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -2,28 +2,35 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::WebViewBuilder, - }; - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() + #[allow(unused_mut)] + let mut builder = WindowBuilder::new() .with_decorations(false) // There are actually three layer of background color when creating webview window. // The first is window background... - .with_transparent(true) - .build(&event_loop) - .unwrap(); + .with_transparent(true); + #[cfg(target_os = "windows")] + { + use tao::platform::windows::WindowBuilderExtWindows; + builder = builder.with_undecorated_shadow(false); + } + let window = builder.build(&event_loop).unwrap(); + + #[cfg(target_os = "windows")] + { + use tao::platform::windows::WindowExtWindows; + window.set_undecorated_shadow(true); + } - let _webview = WebViewBuilder::new(window.raw_window_handle())? + let _webview = WebViewBuilder::new(&window) // The second is on webview... .with_transparent(true) // And the last is in html. @@ -44,13 +51,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => {} + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/examples/user_agent.rs b/examples/user_agent.rs index 741219050..12ee4eeb9 100644 --- a/examples/user_agent.rs +++ b/examples/user_agent.rs @@ -2,18 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use rwh_05::HasRawWindowHandle; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{webview_version, WebViewBuilder}; fn main() -> wry::Result<()> { - use wry::{ - application::{ - event::{Event, StartCause, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }, - webview::{webview_version, WebViewBuilder}, - }; - let current_version = env!("CARGO_PKG_VERSION"); let current_webview_version = webview_version().unwrap(); let user_agent_string = format!( @@ -26,8 +22,9 @@ fn main() -> wry::Result<()> { let event_loop = EventLoop::new(); let window = WindowBuilder::new() .with_title("Hello World") - .build(&event_loop)?; - let _webview = WebViewBuilder::new(window.raw_window_handle())? + .build(&event_loop) + .unwrap(); + let _webview = WebViewBuilder::new(&window) .with_user_agent(&user_agent_string) .with_url("https://www.whatismybrowser.com/detect/what-is-my-user-agent")? .build()?; @@ -35,13 +32,12 @@ fn main() -> wry::Result<()> { event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; - match event { - Event::NewEvents(StartCause::Init) => println!("Wry has started!"), - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit } }); } diff --git a/src/webview/android/binding.rs b/src/android/binding.rs similarity index 100% rename from src/webview/android/binding.rs rename to src/android/binding.rs diff --git a/src/webview/android/kotlin/Ipc.kt b/src/android/kotlin/Ipc.kt similarity index 100% rename from src/webview/android/kotlin/Ipc.kt rename to src/android/kotlin/Ipc.kt diff --git a/src/webview/android/kotlin/Logger.kt b/src/android/kotlin/Logger.kt similarity index 100% rename from src/webview/android/kotlin/Logger.kt rename to src/android/kotlin/Logger.kt diff --git a/src/webview/android/kotlin/PermissionHelper.kt b/src/android/kotlin/PermissionHelper.kt similarity index 100% rename from src/webview/android/kotlin/PermissionHelper.kt rename to src/android/kotlin/PermissionHelper.kt diff --git a/src/webview/android/kotlin/RustWebChromeClient.kt b/src/android/kotlin/RustWebChromeClient.kt similarity index 100% rename from src/webview/android/kotlin/RustWebChromeClient.kt rename to src/android/kotlin/RustWebChromeClient.kt diff --git a/src/webview/android/kotlin/RustWebView.kt b/src/android/kotlin/RustWebView.kt similarity index 100% rename from src/webview/android/kotlin/RustWebView.kt rename to src/android/kotlin/RustWebView.kt diff --git a/src/webview/android/kotlin/RustWebViewClient.kt b/src/android/kotlin/RustWebViewClient.kt similarity index 100% rename from src/webview/android/kotlin/RustWebViewClient.kt rename to src/android/kotlin/RustWebViewClient.kt diff --git a/src/webview/android/kotlin/WryActivity.kt b/src/android/kotlin/WryActivity.kt similarity index 100% rename from src/webview/android/kotlin/WryActivity.kt rename to src/android/kotlin/WryActivity.kt diff --git a/src/webview/android/kotlin/proguard-wry.pro b/src/android/kotlin/proguard-wry.pro similarity index 100% rename from src/webview/android/kotlin/proguard-wry.pro rename to src/android/kotlin/proguard-wry.pro diff --git a/src/webview/android/main_pipe.rs b/src/android/main_pipe.rs similarity index 100% rename from src/webview/android/main_pipe.rs rename to src/android/main_pipe.rs diff --git a/src/webview/android/mod.rs b/src/android/mod.rs similarity index 100% rename from src/webview/android/mod.rs rename to src/android/mod.rs diff --git a/src/application.rs b/src/application.rs deleted file mode 100644 index 9f9cf7625..000000000 --- a/src/application.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! Re-exported Tao APIs -//! -//! This module re-export [tao] APIs for user to create application windows. To learn more about -//! how to use tao, please see [its documentation](https://crates.io/crates/tao). -//! -//! [tao]: https://crates.io/crates/tao - -#[cfg(feature = "tao")] -pub use tao::*; - -#[cfg(feature = "winit")] -pub use winit::*; diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 000000000..d6aa5b183 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,69 @@ +/// Convenient type alias of Result type for wry. +pub type Result = std::result::Result; + +/// Errors returned by wry. +#[non_exhaustive] +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + #[error(transparent)] + GlibError(#[from] glib::Error), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + #[error(transparent)] + GlibBoolError(#[from] glib::BoolError), + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + #[error("Fail to fetch security manager")] + MissingManager, + #[error("Failed to initialize the script")] + InitScriptError, + #[error("Bad RPC request: {0} ((1))")] + RpcScriptError(String, String), + #[error(transparent)] + NulError(#[from] std::ffi::NulError), + #[error(transparent)] + ReceiverError(#[from] std::sync::mpsc::RecvError), + #[error(transparent)] + SenderError(#[from] std::sync::mpsc::SendError), + #[error("Failed to send the message")] + MessageSender, + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error(transparent)] + UrlError(#[from] url::ParseError), + #[error("IO error: {0}")] + Io(#[from] std::io::Error), + #[cfg(target_os = "windows")] + #[error("WebView2 error: {0}")] + WebView2Error(webview2_com::Error), + #[error("Duplicate custom protocol registered: {0}")] + DuplicateCustomProtocol(String), + #[error(transparent)] + HttpError(#[from] http::Error), + #[error("Infallible error, something went really wrong: {0}")] + Infallible(#[from] std::convert::Infallible), + #[cfg(target_os = "android")] + #[error(transparent)] + JniError(#[from] tao::platform::android::ndk_glue::jni::errors::Error), + #[error("Failed to create proxy endpoint")] + ProxyEndpointCreationFailed, + #[error(transparent)] + WindowHandleError(#[from] raw_window_handle::HandleError), +} diff --git a/src/lib.rs b/src/lib.rs index b3c36cc1c..7b2dad53c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,13 +19,12 @@ //! }, //! webview::WebViewBuilder, //! }; -//! use rwh_05::HasRawWindowHandle; //! //! let event_loop = EventLoop::new(); //! let window = WindowBuilder::new() //! .with_title("Hello World") //! .build(&event_loop)?; -//! let _webview = WebViewBuilder::new(window.raw_window_handle())? +//! let _webview = WebViewBuilder::new(&window)? //! .with_url("https://tauri.studio")? //! .build()?; //! @@ -49,8 +48,6 @@ //! Wry uses a set of feature flags to toggle several advanced features. `file-drop`, `protocol`, //! are enabled by default. //! -//! - `tao`: Default windowing crate used by wry. It is re-exported as `application` module. -//! - `winit`: Replace [tao] with [winit] crate. It only supports Windows and macOS. //! - `file-drop`: Enables [`with_file_drop_handler`] to control the behaviour when there are files //! interacting with the window. Enabled by default. //! - `protocol`: Enables [`with_custom_protocol`] to define custom URL scheme for handling tasks like @@ -66,110 +63,1265 @@ //! Avoid this in release build if your app needs to publish to App Store. //! - `dox`: Enables this in `package.metadata.docs.rs` section to skip linking some **Linux** //! libraries and prevent from building documentation on doc.rs fails. -//! - `linux-headers`: Enables headers support of custom protocol request on Linux. Requires -//! webkit2gtk v2.36 or above. +//! - `linux-body`: Enables body support of custom protocol request on Linux. Requires +//! webkit2gtk v2.40 or above. //! -//! [tao]: https://crates.io/crates/tao -//! [`EventLoop`]: crate::application::event_loop::EventLoop -//! [`Window`]: crate::application::window::Window //! [`WebView`]: crate::webview::WebView //! [`with_file_drop_handler`]: crate::webview::WebView::with_file_drop_handler //! [`with_custom_protocol`]: crate::webview::WebView::with_custom_protocol -#![allow(clippy::new_without_default)] -#![allow(clippy::wrong_self_convention)] -#![allow(clippy::type_complexity)] -#![allow(clippy::unit_cmp)] -#![allow(clippy::upper_case_acronyms)] +pub use http; +pub use raw_window_handle; -#[macro_use] -extern crate serde; -#[macro_use] -extern crate thiserror; #[cfg(any(target_os = "macos", target_os = "ios"))] #[macro_use] extern crate objc; -use std::sync::mpsc::{RecvError, SendError}; +mod error; +mod proxy; +mod web_context; -use crate::application::window::BadIcon; -pub use serde_json::Value; -use url::ParseError; +#[cfg(target_os = "android")] +pub(crate) mod android; +#[cfg(target_os = "android")] +pub mod prelude { + pub use super::android::{binding::*, dispatch, find_class, setup, Context}; +} +#[cfg(target_os = "android")] +pub use android::JniHandle; +#[cfg(target_os = "android")] +use android::*; -pub mod application; -pub use http; -pub mod webview; +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +pub(crate) mod webkitgtk; +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +use webkitgtk::*; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub(crate) mod wkwebview; +#[cfg(any(target_os = "macos", target_os = "ios"))] +use wkwebview::*; + +#[cfg(target_os = "windows")] +pub(crate) mod webview2; +#[cfg(target_os = "windows")] +use self::webview2::*; +#[cfg(target_os = "windows")] +use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; + +use std::{borrow::Cow, path::PathBuf, rc::Rc}; + +pub use error::*; +pub use proxy::{ProxyConfig, ProxyEndpoint}; +pub use url::Url; +pub use web_context::WebContext; + +use http::{Request, Response as HttpResponse}; +use raw_window_handle::HasWindowHandle; + +/// Resolves a custom protocol [`Request`] asynchronously. +/// +/// See [`WebViewBuilder::with_asynchronous_custom_protocol`] for more information. +pub struct RequestAsyncResponder { + pub(crate) responder: Box>)>, +} + +// SAFETY: even though the webview bindings do not indicate the responder is Send, +// it actually is and we need it in order to let the user do the protocol computation +// on a separate thread or async task. +unsafe impl Send for RequestAsyncResponder {} + +impl RequestAsyncResponder { + /// Resolves the request with the given response. + pub fn respond>>(self, response: HttpResponse) { + let (parts, body) = response.into_parts(); + (self.responder)(HttpResponse::from_parts(parts, body.into())) + } +} + +pub struct WebViewAttributes { + /// Whether the WebView should have a custom user-agent. + pub user_agent: Option, + + /// Whether the WebView window should be visible. + pub visible: bool, + + /// Whether the WebView should be transparent. + /// + /// ## Platform-specific: + /// + /// **Windows 7**: Not supported. + pub transparent: bool, + + /// Specify the webview background color. This will be ignored if `transparent` is set to `true`. + /// + /// The color uses the RGBA format. + /// + /// ## Platform-specific: + /// + /// - **macOS / iOS**: Not implemented. + /// - **Windows**: + /// - On Windows 7, transparency is not supported and the alpha value will be ignored. + /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` + pub background_color: Option, + + /// Whether load the provided URL to [`WebView`]. + pub url: Option, + + /// Headers used when loading the requested `url`. + pub headers: Option, + + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific + /// + /// **macOS / Linux / Android / iOS**: Unsupported + pub zoom_hotkeys_enabled: bool, + + /// Whether load the provided html string to [`WebView`]. + /// This will be ignored if the `url` is provided. + /// + /// # Warning + /// + /// The Page loaded from html string will have `null` origin. + /// + /// ## PLatform-specific: + /// + /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size + pub html: Option, + + /// Initialize javascript code when loading new pages. When webview load a new page, this + /// initialization code will be executed. It is guaranteed that code is executed before + /// `window.onload`. + /// + /// ## Platform-specific + /// + /// - **Android:** The Android WebView does not provide an API for initialization scripts, + /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. + pub initialization_scripts: Vec, + + /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// closure. + /// + /// The closure takes a [Request] and returns a [Response]. + /// + /// # Warning + /// + /// Pages loaded from custom protocol will have different Origin on different platforms. And + /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin` + /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the + /// different Origin headers across platforms: + /// + /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. + /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. + /// + /// # Reading assets on mobile + /// + /// - Android: Android has `assets` and `resource` path finder to + /// locate your files in those directories. For more information, see [Loading in-app content](https://developer.android.com/guide/webapps/load-local-content) page. + /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. + /// + /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 + pub custom_protocols: Vec<(String, Box>, RequestAsyncResponder)>)>, + + /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. + /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. + /// + /// Both functions return promises but `notify()` resolves immediately. + pub ipc_handler: Option>, + + /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. + /// + /// # Blocking OS Default Behavior + /// Return `true` in the callback to block the OS' default behavior of handling a file drop. + /// + /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. + /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. + #[cfg(feature = "file-drop")] + pub file_drop_handler: Option bool>>, + #[cfg(not(feature = "file-drop"))] + file_drop_handler: Option bool>>, + + /// Set a navigation handler to decide if incoming url is allowed to navigate. + /// + /// The closure take a `String` parameter as url and return `bool` to determine the url. True is + /// allow to navigate and false is not. + pub navigation_handler: Option bool>>, + + /// Set a download started handler to manage incoming downloads. + /// + /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the + /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter + /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be + /// absolute. The closure returns a `bool` to allow or deny the download. + pub download_started_handler: Option bool>>, + + /// Sets a download completion handler to manage downloads that have finished. + /// + /// The closure is fired when the download completes, whether it was successful or not. + /// The closure takes a `String` representing the URL of the original download request, an `Option` + /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download + /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download + /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// know if the download succeeded. + /// + /// ## Platform-specific: + /// + /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API + /// limitations. + pub download_completed_handler: Option, bool) + 'static>>, + + /// Set a new window handler to decide if incoming url is allowed to open in a new window. + /// + /// The closure take a `String` parameter as url and return `bool` to determine the url. True is + /// allow to navigate and false is not. + pub new_window_req_handler: Option bool>>, + + /// Enables clipboard access for the page rendered on **Linux** and **Windows**. + /// + /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu + /// item accelerators to use shortcuts. + pub clipboard: bool, + + /// Enable web inspector which is usually called dev tool. + /// + /// Note this only enables dev tool to the webview. To open it, you can call + /// [`WebView::open_devtools`], or right click the page and open it from the context menu. + /// + /// ## Platform-specific + /// + /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, + /// but requires `devtools` feature flag to actually enable it in **release** build. + /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. + /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. + pub devtools: bool, + + /// Whether clicking an inactive window also clicks through to the webview. Default is `false`. + /// + /// ## Platform-specific + /// + /// This configuration only impacts macOS. + pub accept_first_mouse: bool, + + /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation. + /// + /// ## Platform-specific: + /// + /// - **Android / iOS:** Unsupported. + pub back_forward_navigation_gestures: bool, + + /// 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, + + /// Set a handler closure to process page load events. + pub on_page_load_handler: Option>, + + /// Set a proxy configuration for the webview. Supports HTTP CONNECT and SOCKSv5 proxies + /// + /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. + /// - **Android / iOS:** Not supported. + pub proxy_config: Option, + + /// Whether the webview should be focused when created. + /// + /// ## Platform-specific: + /// + /// - **macOS / Android / iOS:** Unsupported. + pub focused: bool, + + pub position: Option<(i32, i32)>, + + pub size: Option<(u32, u32)>, +} + +impl Default for WebViewAttributes { + fn default() -> Self { + Self { + user_agent: None, + visible: true, + transparent: false, + background_color: None, + url: None, + headers: None, + html: None, + initialization_scripts: vec![], + custom_protocols: vec![], + ipc_handler: None, + file_drop_handler: None, + navigation_handler: None, + download_started_handler: None, + download_completed_handler: None, + new_window_req_handler: None, + clipboard: false, + #[cfg(debug_assertions)] + devtools: true, + #[cfg(not(debug_assertions))] + devtools: false, + zoom_hotkeys_enabled: false, + accept_first_mouse: false, + back_forward_navigation_gestures: false, + document_title_changed_handler: None, + incognito: false, + autoplay: true, + on_page_load_handler: None, + proxy_config: None, + focused: true, + position: None, + size: None, + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios", +))] +#[derive(Default)] +pub(crate) struct PlatformSpecificWebViewAttributes; + +/// Type alias for a color in the RGBA format. +/// +/// Each value can be 0..255 inclusive. +pub type RGBA = (u8, u8, u8, u8); + +/// Type of of page loading event +pub enum PageLoadEvent { + /// Indicates that the content of the page has started loading + Started, + /// Indicates that the page content has finished loading + Finished, +} + +/// Builder type of [`WebView`]. +/// +/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and +/// scripts for those who prefer to control fine grained window creation and event handling. +/// [`WebViewBuilder`] provides ability to setup initialization before web engine starts. +pub struct WebViewBuilder<'a> { + pub attrs: WebViewAttributes, + as_child: bool, + window: &'a dyn HasWindowHandle, + platform_specific: PlatformSpecificWebViewAttributes, + web_context: Option<&'a mut WebContext>, +} + +impl<'a> WebViewBuilder<'a> { + /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. + pub fn new(window: &'a impl HasWindowHandle) -> Self { + Self { + attrs: WebViewAttributes::default(), + window, + as_child: false, + #[allow(clippy::default_constructed_unit_structs)] + platform_specific: PlatformSpecificWebViewAttributes::default(), + web_context: None, + } + } + + /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc + pub fn new_as_child(parent: &'a impl HasWindowHandle) -> Self { + Self { + attrs: WebViewAttributes::default(), + window: parent, + as_child: true, + #[allow(clippy::default_constructed_unit_structs)] + platform_specific: PlatformSpecificWebViewAttributes::default(), + web_context: None, + } + } + + /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation. + /// + /// ## Platform-specific: + /// + /// - **Android / iOS:** Unsupported. + pub fn with_back_forward_navigation_gestures(mut self, gesture: bool) -> Self { + self.attrs.back_forward_navigation_gestures = gesture; + self + } + + /// Sets whether the WebView should be transparent. + /// + /// ## Platform-specific: + /// + /// **Windows 7**: Not supported. + pub fn with_transparent(mut self, transparent: bool) -> Self { + self.attrs.transparent = transparent; + self + } + + /// Specify the webview background color. This will be ignored if `transparent` is set to `true`. + /// + /// The color uses the RGBA format. + /// + /// ## Platfrom-specific: + /// + /// - **macOS / iOS**: Not implemented. + /// - **Windows**: + /// - on Windows 7, transparency is not supported and the alpha value will be ignored. + /// - on Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` + pub fn with_background_color(mut self, background_color: RGBA) -> Self { + self.attrs.background_color = Some(background_color); + self + } + + /// Sets whether the WebView should be transparent. + pub fn with_visible(mut self, visible: bool) -> Self { + self.attrs.visible = visible; + self + } + + /// Sets whether all media can be played without user interaction. + pub fn with_autoplay(mut self, autoplay: bool) -> Self { + self.attrs.autoplay = autoplay; + self + } + + /// Initialize javascript code when loading new pages. When webview load a new page, this + /// initialization code will be executed. It is guaranteed that code is executed before + /// `window.onload`. + /// + /// ## Platform-specific + /// + /// - **Android:** The Android WebView does not provide an API for initialization scripts, + /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. + pub fn with_initialization_script(mut self, js: &str) -> Self { + if !js.is_empty() { + self.attrs.initialization_scripts.push(js.to_string()); + } + self + } + + /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// closure. + /// + /// The closure takes a [Request] and returns a [Response] + /// + /// # Warning + /// + /// Pages loaded from custom protocol will have different Origin on different platforms. And + /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin` + /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the + /// different Origin headers across platforms: + /// + /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. + /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. + /// + /// # Reading assets on mobile + /// + /// - Android: For loading content from the `assets` folder (which is copied to the Andorid apk) please + /// use the function [`with_asset_loader`] from [`WebViewBuilderExtAndroid`] instead. + /// This function on Android can only be used to serve assets you can embed in the binary or are + /// elsewhere in Android (provided the app has appropriate access), but not from the `assets` + /// folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux. + /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. + /// + /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 + #[cfg(feature = "protocol")] + pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self + where + F: Fn(Request>) -> HttpResponse> + 'static, + { + self.attrs.custom_protocols.push(( + name, + Box::new(move |request, responder| { + let http_response = handler(request); + responder.respond(http_response); + }), + )); + self + } + + /// Same as [`Self::with_custom_protocol`] but with an asynchronous responder. + /// + /// # Examples + /// + /// ```no_run + /// use wry::{ + /// application::{ + /// event_loop::EventLoop, + /// window::WindowBuilder + /// }, + /// webview::WebViewBuilder, + /// }; + /// use rwh_05::HasRawWindowHandle; + /// + /// let event_loop = EventLoop::new(); + /// let window = WindowBuilder::new() + /// .build(&event_loop) + /// .unwrap(); + /// WebViewBuilder::new(window.raw_window_handle()) + /// .unwrap() + /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| { + /// // here you can use a tokio task, thread pool or anything + /// // to do heavy computation to resolve your request + /// // e.g. downloading files, opening the camera... + /// std::thread::spawn(move || { + /// std::thread::sleep(std::time::Duration::from_secs(2)); + /// responder.respond(http::Response::builder().body(Vec::new()).unwrap()); + /// }); + /// }); + /// ``` + #[cfg(feature = "protocol")] + pub fn with_asynchronous_custom_protocol(mut self, name: String, handler: F) -> Self + where + F: Fn(Request>, RequestAsyncResponder) + 'static, + { + self.attrs.custom_protocols.push((name, Box::new(handler))); + self + } + + /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. + /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. + pub fn with_ipc_handler(mut self, handler: F) -> Self + where + F: Fn(String) + 'static, + { + self.attrs.ipc_handler = Some(Box::new(handler)); + self + } + + /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. + /// + /// # Blocking OS Default Behavior + /// Return `true` in the callback to block the OS' default behavior of handling a file drop. + /// + /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. + /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. + #[cfg(feature = "file-drop")] + pub fn with_file_drop_handler(mut self, handler: F) -> Self + where + F: Fn(FileDropEvent) -> bool + 'static, + { + self.attrs.file_drop_handler = Some(Box::new(handler)); + self + } + + /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the + /// [`WebView`]. The provided URL must be valid. + pub fn with_url_and_headers(mut self, url: &str, headers: http::HeaderMap) -> Result { + self.attrs.url = Some(url.parse()?); + self.attrs.headers = Some(headers); + Ok(self) + } + + /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the + /// [`WebView`]. The provided URL must be valid. + pub fn with_url(mut self, url: &str) -> Result { + self.attrs.url = Some(Url::parse(url)?); + self.attrs.headers = None; + Ok(self) + } + + /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the + /// [`WebView`]. This will be ignored if `url` is provided. + /// + /// # Warning + /// + /// The Page loaded from html string will have `null` origin. + /// + /// ## PLatform-specific: + /// + /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size + pub fn with_html(mut self, html: impl Into) -> Result { + self.attrs.html = Some(html.into()); + Ok(self) + } + + /// Set the web context that can share with multiple [`WebView`]s. + pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self { + self.web_context = Some(web_context); + self + } + + /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView. + pub fn with_user_agent(mut self, user_agent: &str) -> Self { + self.attrs.user_agent = Some(user_agent.to_string()); + self + } + + /// Enable or disable web inspector which is usually called dev tool. + /// + /// Note this only enables dev tool to the webview. To open it, you can call + /// [`WebView::open_devtools`], or right click the page and open it from the context menu. + /// + /// ## Platform-specific + /// + /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, + /// but requires `devtools` feature flag to actually enable it in **release** build. + /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. + /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. + pub fn with_devtools(mut self, devtools: bool) -> Self { + self.attrs.devtools = devtools; + self + } + + /// Whether page zooming by hotkeys or gestures is enabled + /// + /// ## Platform-specific + /// + /// **macOS / Linux / Android / iOS**: Unsupported + pub fn with_hotkeys_zoom(mut self, zoom: bool) -> Self { + self.attrs.zoom_hotkeys_enabled = zoom; + self + } + + /// Set a navigation handler to decide if incoming url is allowed to navigate. + /// + /// The closure takes a `String` parameter as url and return `bool` to determine the url. True is + /// allowed to navigate and false is not. + pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self { + self.attrs.navigation_handler = Some(Box::new(callback)); + self + } + + /// Set a download started handler to manage incoming downloads. + /// + /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the + /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter + /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be + /// absolute. The closure returns a `bool` to allow or deny the download. + pub fn with_download_started_handler( + mut self, + started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static, + ) -> Self { + self.attrs.download_started_handler = Some(Box::new(started_handler)); + self + } + + /// Sets a download completion handler to manage downloads that have finished. + /// + /// The closure is fired when the download completes, whether it was successful or not. + /// The closure takes a `String` representing the URL of the original download request, an `Option` + /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download + /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download + /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// know if the download succeeded. + /// + /// ## Platform-specific: + /// + /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API + /// limitations. + pub fn with_download_completed_handler( + mut self, + download_completed_handler: impl Fn(String, Option, bool) + 'static, + ) -> Self { + self.attrs.download_completed_handler = Some(Rc::new(download_completed_handler)); + self + } + + /// Enables clipboard access for the page rendered on **Linux** and **Windows**. + /// + /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu + /// item accelerators to use shortcuts. + pub fn with_clipboard(mut self, clipboard: bool) -> Self { + self.attrs.clipboard = clipboard; + self + } + + /// Set a new window request handler to decide if incoming url is allowed to be opened. + /// + /// The closure takes a `String` parameter as url and return `bool` to determine if the url can be + /// opened in a new window. Returning true will open the url in a new window, whilst returning false + /// will neither open a new window nor allow any navigation. + pub fn with_new_window_req_handler( + mut self, + callback: impl Fn(String) -> bool + 'static, + ) -> Self { + self.attrs.new_window_req_handler = Some(Box::new(callback)); + self + } + + /// Sets whether clicking an inactive window also clicks through to the webview. Default is `false`. + /// + /// ## Platform-specific + /// + /// This configuration only impacts macOS. + pub fn with_accept_first_mouse(mut self, accept_first_mouse: bool) -> Self { + self.attrs.accept_first_mouse = accept_first_mouse; + self + } + + /// Set a handler closure to process the change of the webview's document title. + pub fn with_document_title_changed_handler( + mut self, + callback: impl Fn(String) + 'static, + ) -> Self { + self.attrs.document_title_changed_handler = Some(Box::new(callback)); + 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.attrs.incognito = incognito; + self + } + + /// Set a handler to process page loading events. + /// + /// The handler will be called when the webview begins the indicated loading event. + pub fn with_on_page_load_handler( + mut self, + handler: impl Fn(PageLoadEvent, String) + 'static, + ) -> Self { + self.attrs.on_page_load_handler = Some(Box::new(handler)); + self + } -/// Convenient type alias of Result type for wry. -pub type Result = std::result::Result; + /// Set a proxy configuration for the webview. + /// + /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. Supports HTTP CONNECT and SOCKSv5 proxies. + /// - **Windows / Linux**: Supports HTTP CONNECT and SOCKSv5 proxies. + /// - **Android / iOS:** Not supported. + pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self { + self.attrs.proxy_config = Some(configuration); + self + } -/// Errors returned by wry. + /// Set whether the webview should be focused when created. + /// + /// ## Platform-specific: + /// + /// - **macOS / Android / iOS:** Unsupported. + pub fn with_focused(mut self, focused: bool) -> Self { + self.attrs.focused = focused; + self + } + + pub fn with_position(mut self, position: (i32, i32)) -> Self { + self.attrs.position = Some(position); + self + } + + pub fn with_size(mut self, size: (u32, u32)) -> Self { + self.attrs.size = Some(size); + self + } + + /// Consume the builder and create the [`WebView`]. + /// + /// Platform-specific behavior: + /// + /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be + /// called in the same thread with the [`EventLoop`] you create. + /// + /// [`EventLoop`]: crate::application::event_loop::EventLoop + pub fn build(self) -> Result { + let webview = if self.as_child { + InnerWebView::new_as_child( + &self.window, + self.attrs, + self.platform_specific, + self.web_context, + )? + } else { + InnerWebView::new( + &self.window, + self.attrs, + self.platform_specific, + self.web_context, + )? + }; + Ok(WebView { webview }) + } +} + +#[cfg(windows)] +#[derive(Clone)] +pub(crate) struct PlatformSpecificWebViewAttributes { + additional_browser_args: Option, + browser_accelerator_keys: bool, + theme: Option, + https_scheme: bool, +} + +#[cfg(windows)] +impl Default for PlatformSpecificWebViewAttributes { + fn default() -> Self { + Self { + additional_browser_args: None, + browser_accelerator_keys: true, // This is WebView2's default behavior + theme: None, + https_scheme: false, // To match macOS & Linux behavior in the context of mixed content. + } + } +} + +#[cfg(windows)] +pub trait WebViewBuilderExtWindows { + /// Pass additional args to Webview2 upon creating the webview. + /// + /// ## Warning + /// + /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` + /// `--autoplay-policy=no-user-gesture-required` if autoplay is enabled + /// and `--proxy-server=://:` if a proxy is set. + /// so if you use this method, you have to add these arguments yourself if you want to keep the same behavior. + fn with_additional_browser_args>(self, additional_args: S) -> Self; + + /// Determines whether browser-specific accelerator keys are enabled. When this setting is set to + /// `false`, it disables all accelerator keys that access features specific to a web browser. + /// The default value is `true`. See the following link to know more details. + /// + /// https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings#arebrowseracceleratorkeysenabled + fn with_browser_accelerator_keys(self, enabled: bool) -> Self; + + /// Specifies the theme of webview2. This affects things like `prefers-color-scheme`. + /// + /// Defaults to [`Theme::Auto`] which will follow the OS defaults. + fn with_theme(self, theme: Theme) -> Self; + + /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. + /// + /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints + /// and is therefore less secure but will match the behavior of the `://localhost` protocols used on macOS and Linux. + /// + /// The default value is `false`. + fn with_https_scheme(self, enabled: bool) -> Self; +} + +#[cfg(windows)] +impl WebViewBuilderExtWindows for WebViewBuilder<'_> { + fn with_additional_browser_args>(mut self, additional_args: S) -> Self { + self.platform_specific.additional_browser_args = Some(additional_args.into()); + self + } + + fn with_browser_accelerator_keys(mut self, enabled: bool) -> Self { + self.platform_specific.browser_accelerator_keys = enabled; + self + } + + fn with_theme(mut self, theme: Theme) -> Self { + self.platform_specific.theme = Some(theme); + self + } + + fn with_https_scheme(mut self, enabled: bool) -> Self { + self.platform_specific.https_scheme = enabled; + self + } +} + +#[cfg(target_os = "android")] +#[derive(Default)] +pub(crate) struct PlatformSpecificWebViewAttributes { + on_webview_created: Option< + Box< + dyn Fn( + prelude::Context, + ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> + + Send, + >, + >, + with_asset_loader: bool, + asset_loader_domain: Option, + https_scheme: bool, +} + +#[cfg(target_os = "android")] +pub trait WebViewBuilderExtAndroid { + fn on_webview_created< + F: Fn( + prelude::Context<'_, '_>, + ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> + + Send + + 'static, + >( + self, + f: F, + ) -> Self; + + /// Use [WebviewAssetLoader](https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader) + /// to load assets from Android's `asset` folder when using `with_url` as `://assets/` (e.g.: + /// `wry://assets/index.html`). Note that this registers a custom protocol with the provided + /// String, similar to [`with_custom_protocol`], but also sets the WebViewAssetLoader with the + /// necessary domain (which is fixed as `.assets`). This cannot be used in conjunction + /// to `with_custom_protocol` for Android, as it changes the way in which requests are handled. + #[cfg(feature = "protocol")] + fn with_asset_loader(self, protocol: String) -> Self; + + /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. + /// + /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints + /// and is therefore less secure but will match the behavior of the `://localhost` protocols used on macOS and Linux. + /// + /// The default value is `false`. + fn with_https_scheme(self, enabled: bool) -> Self; +} + +#[cfg(target_os = "android")] +impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { + fn on_webview_created< + F: Fn( + prelude::Context<'_, '_>, + ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> + + Send + + 'static, + >( + mut self, + f: F, + ) -> Self { + self.platform_specific.on_webview_created = Some(Box::new(f)); + self + } + + #[cfg(feature = "protocol")] + fn with_asset_loader(mut self, protocol: String) -> Self { + // register custom protocol with empty Response return, + // this is necessary due to the need of fixing a domain + // in WebViewAssetLoader. + self.webview.custom_protocols.push(( + protocol.clone(), + Box::new(|_, api| { + api.respond(HttpResponse::builder().body(Vec::new()).unwrap()); + }), + )); + self.platform_specific.with_asset_loader = true; + self.platform_specific.asset_loader_domain = Some(format!("{}.assets", protocol)); + self + } + + fn with_https_scheme(mut self, enabled: bool) -> Self { + self.platform_specific.https_scheme = enabled; + self + } +} + +/// The fundamental type to present a [`WebView`]. +/// +/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and +/// scripts for those who prefer to control fine grained window creation and event handling. +/// [`WebView`] presents the actual WebView window and let you still able to perform actions +/// during event handling to it. +pub struct WebView { + webview: InnerWebView, +} + +// Signal the Window to drop on Linux and Windows. On mac, we need to handle several unsafe code +// blocks and raw pointer properly. +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +impl Drop for WebView { + fn drop(&mut self) { + unsafe { + use crate::application::platform::unix::WindowExtUnix; + use gtk::prelude::WidgetExtManual; + self.window().gtk_window().destroy(); + } + } +} + +impl WebView { + /// Create a [`WebView`] from provided [`RawWindowHandle`]. Note that calling this directly loses + /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To + /// benefit from above features, create a [`WebViewBuilder`] instead. + /// + /// Platform-specific behavior: + /// + /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be + /// called in the same thread with the [`EventLoop`] you create. + /// + /// [`EventLoop`]: crate::application::event_loop::EventLoop + pub fn new(window: &impl HasWindowHandle) -> Result { + WebViewBuilder::new(window).build() + } + + pub fn new_as_child(window: &impl HasWindowHandle) -> Result { + WebViewBuilder::new_as_child(window).build() + } + + /// Get the current url of the webview + pub fn url(&self) -> Url { + self.webview.url() + } + + /// Evaluate and run javascript code. Must be called on the same thread who created the + /// [`WebView`]. Use [`EventLoopProxy`] and a custom event to send scripts from other threads. + /// + /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy + /// + pub fn evaluate_script(&self, js: &str) -> Result<()> { + self + .webview + .eval(js, None::>) + } + + /// Evaluate and run javascript code with callback function. The evaluation result will be + /// serialized into a JSON string and passed to the callback function. Must be called on the + /// same thread who created the [`WebView`]. Use [`EventLoopProxy`] and a custom event to + /// send scripts from other threads. + /// + /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy + /// + /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. + /// + /// - ** Android:** Not implemented yet. + pub fn evaluate_script_with_callback( + &self, + js: &str, + callback: impl Fn(String) + Send + 'static, + ) -> Result<()> { + self.webview.eval(js, Some(callback)) + } + + /// Launch print modal for the webview content. + pub fn print(&self) -> Result<()> { + self.webview.print(); + Ok(()) + } + + /// Open the web inspector which is usually called dev tool. + /// + /// ## Platform-specific + /// + /// - **Android / iOS:** Not supported. + #[cfg(any(debug_assertions, feature = "devtools"))] + pub fn open_devtools(&self) { + self.webview.open_devtools(); + } + + /// Close the web inspector which is usually called dev tool. + /// + /// ## Platform-specific + /// + /// - **Windows / Android / iOS:** Not supported. + #[cfg(any(debug_assertions, feature = "devtools"))] + pub fn close_devtools(&self) { + self.webview.close_devtools(); + } + + /// Gets the devtool window's current visibility state. + /// + /// ## Platform-specific + /// + /// - **Windows / Android / iOS:** Not supported. + #[cfg(any(debug_assertions, feature = "devtools"))] + pub fn is_devtools_open(&self) -> bool { + self.webview.is_devtools_open() + } + + /// Set the webview zoom level + /// + /// ## Platform-specific: + /// + /// - **Android**: Not supported. + /// - **macOS**: available on macOS 11+ only. + /// - **iOS**: available on iOS 14+ only. + pub fn zoom(&self, scale_factor: f64) { + self.webview.zoom(scale_factor); + } + + /// Specify the webview background color. + /// + /// The color uses the RGBA format. + /// + /// ## Platfrom-specific: + /// + /// - **macOS / iOS**: Not implemented. + /// - **Windows**: + /// - On Windows 7, transparency is not supported and the alpha value will be ignored. + /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` + pub fn set_background_color(&self, background_color: RGBA) -> Result<()> { + self.webview.set_background_color(background_color) + } + + /// Navigate to the specified url + pub fn load_url(&self, url: &str) { + self.webview.load_url(url) + } + + /// Navigate to the specified url using the specified headers + pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) { + self.webview.load_url_with_headers(url, headers) + } + + /// Clear all browsing data + pub fn clear_all_browsing_data(&self) -> Result<()> { + self.webview.clear_all_browsing_data() + } + + pub fn set_position(&self, position: (i32, i32)) { + self.webview.set_position(position) + } + + pub fn set_size(&self, size: (u32, u32)) { + self.webview.set_size(size) + } +} + +/// An event enumeration sent to [`FileDropHandler`]. #[non_exhaustive] -#[derive(Error, Debug)] -pub enum Error { - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[error(transparent)] - GlibError(#[from] glib::Error), - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[error(transparent)] - GlibBoolError(#[from] glib::BoolError), - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[error("Fail to fetch security manager")] - MissingManager, - #[error("Failed to initialize the script")] - InitScriptError, - #[error("Bad RPC request: {0} ((1))")] - RpcScriptError(String, String), - #[error(transparent)] - NulError(#[from] std::ffi::NulError), - #[error(transparent)] - OsError(#[from] crate::application::error::OsError), - #[error(transparent)] - ReceiverError(#[from] RecvError), - #[error(transparent)] - SenderError(#[from] SendError), - #[error("Failed to send the message")] - MessageSender, - #[error(transparent)] - Json(#[from] serde_json::Error), - #[error(transparent)] - UrlError(#[from] ParseError), - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - #[error("Icon error: {0}")] - Icon(#[from] BadIcon), - #[cfg(target_os = "windows")] - #[error("WebView2 error: {0}")] - WebView2Error(webview2_com::Error), - #[error("Duplicate custom protocol registered: {0}")] - DuplicateCustomProtocol(String), - #[error(transparent)] - HttpError(#[from] http::Error), - #[error("Infallible error, something went really wrong: {0}")] - Infallible(#[from] std::convert::Infallible), - #[cfg(target_os = "android")] - #[error(transparent)] - JniError(#[from] tao::platform::android::ndk_glue::jni::errors::Error), - #[error("Failed to create proxy endpoint")] - ProxyEndpointCreationFailed, - #[error("Failed to get correct raw window handle")] - WindowHandleError, +#[derive(Debug, serde::Serialize, Clone)] +pub enum FileDropEvent { + /// The file(s) have been dragged onto the window, but have not been dropped yet. + Hovered { + paths: Vec, + /// The position of the mouse cursor. + position: (i32, i32), + }, + /// The file(s) have been dropped onto the window. + Dropped { + paths: Vec, + /// The position of the mouse cursor. + position: (i32, i32), + }, + /// The file drop was aborted. + Cancelled, +} + +/// Get Webview/Webkit version on current platform. +pub fn webview_version() -> Result { + platform_webview_version() +} + +/// Additional methods on `WebView` that are specific to Windows. +#[cfg(target_os = "windows")] +pub trait WebviewExtWindows { + /// Returns WebView2 Controller + fn controller(&self) -> ICoreWebView2Controller; + + // Changes the webview2 theme. + fn set_theme(&self, theme: Theme); +} + +#[cfg(target_os = "windows")] +impl WebviewExtWindows for WebView { + fn controller(&self) -> ICoreWebView2Controller { + self.webview.controller.clone() + } + + fn set_theme(&self, theme: Theme) { + self.webview.set_theme(theme) + } +} + +/// Additional methods on `WebView` that are specific to Linux. +#[cfg(target_os = "linux")] +pub trait WebviewExtUnix { + /// Returns Webkit2gtk Webview handle + fn webview(&self) -> Rc; +} + +#[cfg(target_os = "linux")] +impl WebviewExtUnix for WebView { + fn webview(&self) -> Rc { + self.webview.webview.clone() + } +} + +/// Additional methods on `WebView` that are specific to macOS. +#[cfg(target_os = "macos")] +pub trait WebviewExtMacOS { + /// Returns WKWebView handle + fn webview(&self) -> cocoa::base::id; + /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle + fn manager(&self) -> cocoa::base::id; + /// Returns NSWindow associated with the WKWebView webview + fn ns_window(&self) -> cocoa::base::id; +} + +#[cfg(target_os = "macos")] +impl WebviewExtMacOS for WebView { + fn webview(&self) -> cocoa::base::id { + self.webview.webview + } + + fn manager(&self) -> cocoa::base::id { + self.webview.manager + } + + fn ns_window(&self) -> cocoa::base::id { + self.webview.ns_window + } +} + +/// Additional methods on `WebView` that are specific to iOS. +#[cfg(target_os = "ios")] +pub trait WebviewExtIOS { + /// Returns WKWebView handle + fn webview(&self) -> cocoa::base::id; + /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle + fn manager(&self) -> cocoa::base::id; +} + +#[cfg(target_os = "ios")] +impl WebviewExtIOS for WebView { + fn webview(&self) -> cocoa::base::id { + self.webview.webview + } + + fn manager(&self) -> cocoa::base::id { + self.webview.manager + } +} + +#[cfg(target_os = "android")] +/// Additional methods on `WebView` that are specific to Android +pub trait WebviewExtAndroid { + fn handle(&self) -> JniHandle; +} + +#[cfg(target_os = "android")] +impl WebviewExtAndroid for WebView { + fn handle(&self) -> JniHandle { + JniHandle + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Theme { + Dark, + Light, + Auto, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_get_webview_version() { + if let Err(error) = webview_version() { + panic!("{}", error); + } + } } diff --git a/src/webview/proxy.rs b/src/proxy.rs similarity index 100% rename from src/webview/proxy.rs rename to src/proxy.rs diff --git a/src/webview/web_context.rs b/src/web_context.rs similarity index 100% rename from src/webview/web_context.rs rename to src/web_context.rs diff --git a/src/webview/webkitgtk/file_drop.rs b/src/webkitgtk/file_drop.rs similarity index 100% rename from src/webview/webkitgtk/file_drop.rs rename to src/webkitgtk/file_drop.rs diff --git a/src/webview/webkitgtk/mod.rs b/src/webkitgtk/mod.rs similarity index 100% rename from src/webview/webkitgtk/mod.rs rename to src/webkitgtk/mod.rs diff --git a/src/webview/webkitgtk/synthetic_mouse_events.rs b/src/webkitgtk/synthetic_mouse_events.rs similarity index 100% rename from src/webview/webkitgtk/synthetic_mouse_events.rs rename to src/webkitgtk/synthetic_mouse_events.rs diff --git a/src/webview/webkitgtk/undecorated_resizing.rs b/src/webkitgtk/undecorated_resizing.rs similarity index 100% rename from src/webview/webkitgtk/undecorated_resizing.rs rename to src/webkitgtk/undecorated_resizing.rs diff --git a/src/webview/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs similarity index 100% rename from src/webview/webkitgtk/web_context.rs rename to src/webkitgtk/web_context.rs diff --git a/src/webview/mod.rs b/src/webview/mod.rs deleted file mode 100644 index 12c5e1296..000000000 --- a/src/webview/mod.rs +++ /dev/null @@ -1,1239 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -//! [`WebView`] struct and associated types. - -mod proxy; -mod web_context; - -#[cfg(feature = "rwh_04")] -use rwh_04::RawWindowHandle; -#[cfg(feature = "rwh_05")] -use rwh_05::RawWindowHandle; -#[cfg(feature = "rwh_06")] -use rwh_06::RawWindowHandle; -pub use web_context::WebContext; - -#[cfg(target_os = "android")] -pub(crate) mod android; -#[cfg(target_os = "android")] -pub mod prelude { - pub use super::android::{binding::*, dispatch, find_class, setup, Context}; -} -#[cfg(target_os = "android")] -pub use android::JniHandle; -#[cfg(target_os = "android")] -use android::*; -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -pub(crate) mod webkitgtk; -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -use webkitgtk::*; -#[cfg(any(target_os = "macos", target_os = "ios"))] -pub(crate) mod wkwebview; -#[cfg(any(target_os = "macos", target_os = "ios"))] -use wkwebview::*; -#[cfg(target_os = "windows")] -pub(crate) mod webview2; -#[cfg(target_os = "windows")] -use self::webview2::*; -use crate::{application::dpi::Position, Result}; -#[cfg(target_os = "windows")] -use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; -#[cfg(target_os = "windows")] -use windows::{Win32::Foundation::HWND, Win32::UI::WindowsAndMessaging::DestroyWindow}; - -use std::{borrow::Cow, path::PathBuf, rc::Rc}; - -pub use proxy::{ProxyConfig, ProxyEndpoint}; -pub use url::Url; - -#[cfg(target_os = "windows")] -use crate::application::platform::windows::WindowExtWindows; - -use http::{Request, Response as HttpResponse}; - -/// Resolves a custom protocol [`Request`] asynchronously. -/// -/// See [`WebViewBuilder::with_asynchronous_custom_protocol`] for more information. -pub struct RequestAsyncResponder { - pub(crate) responder: Box>)>, -} - -// SAFETY: even though the webview bindings do not indicate the responder is Send, -// it actually is and we need it in order to let the user do the protocol computation -// on a separate thread or async task. -unsafe impl Send for RequestAsyncResponder {} - -impl RequestAsyncResponder { - /// Resolves the request with the given response. - pub fn respond>>(self, response: HttpResponse) { - let (parts, body) = response.into_parts(); - (self.responder)(HttpResponse::from_parts(parts, body.into())) - } -} - -pub struct WebViewAttributes { - /// Whether the WebView should have a custom user-agent. - pub user_agent: Option, - /// Whether the WebView window should be visible. - pub visible: bool, - /// Whether the WebView should be transparent. - /// - /// ## Platform-specific: - /// - /// **Windows 7**: Not supported. - pub transparent: bool, - /// Specify the webview background color. This will be ignored if `transparent` is set to `true`. - /// - /// The color uses the RGBA format. - /// - /// ## Platform-specific: - /// - /// - **macOS / iOS**: Not implemented. - /// - **Windows**: - /// - On Windows 7, transparency is not supported and the alpha value will be ignored. - /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` - pub background_color: Option, - /// Whether load the provided URL to [`WebView`]. - pub url: Option, - /// Headers used when loading the requested `url`. - pub headers: Option, - /// Whether page zooming by hotkeys is enabled - /// - /// ## Platform-specific - /// - /// **macOS / Linux / Android / iOS**: Unsupported - pub zoom_hotkeys_enabled: bool, - /// Whether load the provided html string to [`WebView`]. - /// This will be ignored if the `url` is provided. - /// - /// # Warning - /// - /// The Page loaded from html string will have `null` origin. - /// - /// ## PLatform-specific: - /// - /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size - pub html: Option, - /// Initialize javascript code when loading new pages. When webview load a new page, this - /// initialization code will be executed. It is guaranteed that code is executed before - /// `window.onload`. - /// - /// ## Platform-specific - /// - /// - **Android:** The Android WebView does not provide an API for initialization scripts, - /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. - pub initialization_scripts: Vec, - /// Register custom file loading protocols with pairs of scheme uri string and a handling - /// closure. - /// - /// The closure takes a [Request] and returns a [Response]. - /// - /// # Warning - /// - /// Pages loaded from custom protocol will have different Origin on different platforms. And - /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin` - /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the - /// different Origin headers across platforms: - /// - /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. - /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. - /// - /// # Reading assets on mobile - /// - /// - Android: Android has `assets` and `resource` path finder to - /// locate your files in those directories. For more information, see [Loading in-app content](https://developer.android.com/guide/webapps/load-local-content) page. - /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. - /// - /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 - pub custom_protocols: Vec<(String, Box>, RequestAsyncResponder)>)>, - /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. - /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. - /// - /// Both functions return promises but `notify()` resolves immediately. - pub ipc_handler: Option>, - /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. - /// - /// # Blocking OS Default Behavior - /// Return `true` in the callback to block the OS' default behavior of handling a file drop. - /// - /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. - /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. - #[cfg(feature = "file-drop")] - pub file_drop_handler: Option bool>>, - #[cfg(not(feature = "file-drop"))] - file_drop_handler: Option bool>>, - - /// Set a navigation handler to decide if incoming url is allowed to navigate. - /// - /// The closure take a `String` parameter as url and return `bool` to determine the url. True is - /// allow to navigate and false is not. - pub navigation_handler: Option bool>>, - - /// Set a download started handler to manage incoming downloads. - /// - /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the - /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter - /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be - /// absolute. The closure returns a `bool` to allow or deny the download. - pub download_started_handler: Option bool>>, - - /// Sets a download completion handler to manage downloads that have finished. - /// - /// The closure is fired when the download completes, whether it was successful or not. - /// The closure takes a `String` representing the URL of the original download request, an `Option` - /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download - /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download - /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to - /// know if the download succeeded. - /// - /// ## Platform-specific: - /// - /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API - /// limitations. - pub download_completed_handler: Option, bool) + 'static>>, - - /// Set a new window handler to decide if incoming url is allowed to open in a new window. - /// - /// The closure take a `String` parameter as url and return `bool` to determine the url. True is - /// allow to navigate and false is not. - pub new_window_req_handler: Option bool>>, - - /// Enables clipboard access for the page rendered on **Linux** and **Windows**. - /// - /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu - /// item accelerators to use shortcuts. - pub clipboard: bool, - - /// Enable web inspector which is usually called dev tool. - /// - /// Note this only enables dev tool to the webview. To open it, you can call - /// [`WebView::open_devtools`], or right click the page and open it from the context menu. - /// - /// ## Platform-specific - /// - /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, - /// but requires `devtools` feature flag to actually enable it in **release** build. - /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. - /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. - pub devtools: bool, - /// Whether clicking an inactive window also clicks through to the webview. Default is `false`. - /// - /// ## Platform-specific - /// - /// This configuration only impacts macOS. - pub accept_first_mouse: bool, - - /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation. - /// - /// ## Platform-specific: - /// - /// - **Android / iOS:** Unsupported. - pub back_forward_navigation_gestures: bool, - - /// 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, - - /// Set a handler closure to process page load events. - pub on_page_load_handler: Option>, - - /// Set a proxy configuration for the webview. Supports HTTP CONNECT and SOCKSv5 proxies - /// - /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. - /// - **Android / iOS:** Not supported. - pub proxy_config: Option, - - /// Whether the webview should be focused when created. - /// - /// ## Platform-specific: - /// - /// - **macOS / Android / iOS:** Unsupported. - pub focused: bool, - - /// TODO doc - pub position: Option<(isize, isize)>, - pub size: Option<(isize, isize)>, -} - -impl Default for WebViewAttributes { - fn default() -> Self { - Self { - user_agent: None, - visible: true, - transparent: false, - background_color: None, - url: None, - headers: None, - html: None, - initialization_scripts: vec![], - custom_protocols: vec![], - ipc_handler: None, - file_drop_handler: None, - navigation_handler: None, - download_started_handler: None, - download_completed_handler: None, - new_window_req_handler: None, - clipboard: false, - #[cfg(debug_assertions)] - devtools: true, - #[cfg(not(debug_assertions))] - devtools: false, - zoom_hotkeys_enabled: false, - accept_first_mouse: false, - back_forward_navigation_gestures: false, - document_title_changed_handler: None, - incognito: false, - autoplay: true, - on_page_load_handler: None, - proxy_config: None, - focused: true, - position: None, - size: None, - } - } -} - -#[cfg(windows)] -#[derive(Clone)] -pub(crate) struct PlatformSpecificWebViewAttributes { - additional_browser_args: Option, - browser_accelerator_keys: bool, - theme: Option, - https_scheme: bool, -} -#[cfg(windows)] -impl Default for PlatformSpecificWebViewAttributes { - fn default() -> Self { - Self { - additional_browser_args: None, - browser_accelerator_keys: true, // This is WebView2's default behavior - theme: None, - https_scheme: false, // To match macOS & Linux behavior in the context of mixed content. - } - } -} -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios", -))] -#[derive(Default)] -pub(crate) struct PlatformSpecificWebViewAttributes; - -#[cfg(target_os = "android")] -#[derive(Default)] -pub(crate) struct PlatformSpecificWebViewAttributes { - on_webview_created: Option< - Box< - dyn Fn( - prelude::Context, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send, - >, - >, - with_asset_loader: bool, - asset_loader_domain: Option, - https_scheme: bool, -} - -/// Type alias for a color in the RGBA format. -/// -/// Each value can be 0..255 inclusive. -pub type RGBA = (u8, u8, u8, u8); - -/// Type of of page loading event -pub enum PageLoadEvent { - /// Indicates that the content of the page has started loading - Started, - /// Indicates that the page content has finished loading - Finished, -} - -/// Builder type of [`WebView`]. -/// -/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and -/// scripts for those who prefer to control fine grained window creation and event handling. -/// [`WebViewBuilder`] provides ability to setup initialization before web engine starts. -pub struct WebViewBuilder<'a> { - pub webview: WebViewAttributes, - platform_specific: PlatformSpecificWebViewAttributes, - web_context: Option<&'a mut WebContext>, - window: RawWindowHandle, -} - -impl<'a> WebViewBuilder<'a> { - /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. - pub fn new(window: RawWindowHandle) -> Result { - let webview = WebViewAttributes::default(); - let web_context = None; - #[allow(clippy::default_constructed_unit_structs)] - let platform_specific = PlatformSpecificWebViewAttributes::default(); - - Ok(Self { - webview, - web_context, - window, - platform_specific, - }) - } - - /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc - pub fn new_as_child( - window: RawWindowHandle, - position: (isize, isize), - size: (isize, isize), - ) -> Result { - let mut webview = WebViewAttributes::default(); - let web_context = None; - #[allow(clippy::default_constructed_unit_structs)] - let platform_specific = PlatformSpecificWebViewAttributes::default(); - webview.position = Some(position); - webview.size = Some(size); - - Ok(Self { - webview, - web_context, - window, - platform_specific, - }) - } - - /// Indicates whether horizontal swipe gestures trigger backward and forward page navigation. - /// - /// ## Platform-specific: - /// - /// - **Android / iOS:** Unsupported. - pub fn with_back_forward_navigation_gestures(mut self, gesture: bool) -> Self { - self.webview.back_forward_navigation_gestures = gesture; - self - } - - /// Sets whether the WebView should be transparent. - /// - /// ## Platform-specific: - /// - /// **Windows 7**: Not supported. - pub fn with_transparent(mut self, transparent: bool) -> Self { - self.webview.transparent = transparent; - self - } - - /// Specify the webview background color. This will be ignored if `transparent` is set to `true`. - /// - /// The color uses the RGBA format. - /// - /// ## Platfrom-specific: - /// - /// - **macOS / iOS**: Not implemented. - /// - **Windows**: - /// - on Windows 7, transparency is not supported and the alpha value will be ignored. - /// - on Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` - pub fn with_background_color(mut self, background_color: RGBA) -> Self { - self.webview.background_color = Some(background_color); - self - } - - /// Sets whether the WebView should be transparent. - pub fn with_visible(mut self, visible: bool) -> Self { - self.webview.visible = visible; - self - } - - /// Sets whether all media can be played without user interaction. - pub fn with_autoplay(mut self, autoplay: bool) -> Self { - self.webview.autoplay = autoplay; - self - } - - /// Initialize javascript code when loading new pages. When webview load a new page, this - /// initialization code will be executed. It is guaranteed that code is executed before - /// `window.onload`. - /// - /// ## Platform-specific - /// - /// - **Android:** The Android WebView does not provide an API for initialization scripts, - /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. - pub fn with_initialization_script(mut self, js: &str) -> Self { - if !js.is_empty() { - self.webview.initialization_scripts.push(js.to_string()); - } - self - } - - /// Register custom file loading protocols with pairs of scheme uri string and a handling - /// closure. - /// - /// The closure takes a [Request] and returns a [Response] - /// - /// # Warning - /// - /// Pages loaded from custom protocol will have different Origin on different platforms. And - /// servers which enforce CORS will need to add exact same Origin header in `Access-Control-Allow-Origin` - /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the - /// different Origin headers across platforms: - /// - /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. - /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. - /// - /// # Reading assets on mobile - /// - /// - Android: For loading content from the `assets` folder (which is copied to the Andorid apk) please - /// use the function [`with_asset_loader`] from [`WebViewBuilderExtAndroid`] instead. - /// This function on Android can only be used to serve assets you can embed in the binary or are - /// elsewhere in Android (provided the app has appropriate access), but not from the `assets` - /// folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux. - /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. - /// - /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 - #[cfg(feature = "protocol")] - pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self - where - F: Fn(Request>) -> HttpResponse> + 'static, - { - self.webview.custom_protocols.push(( - name, - Box::new(move |request, responder| { - let http_response = handler(request); - responder.respond(http_response); - }), - )); - self - } - - /// Same as [`Self::with_custom_protocol`] but with an asynchronous responder. - /// - /// # Examples - /// - /// ```no_run - /// use wry::{ - /// application::{ - /// event_loop::EventLoop, - /// window::WindowBuilder - /// }, - /// webview::WebViewBuilder, - /// }; - /// use rwh_05::HasRawWindowHandle; - /// - /// let event_loop = EventLoop::new(); - /// let window = WindowBuilder::new() - /// .build(&event_loop) - /// .unwrap(); - /// WebViewBuilder::new(window.raw_window_handle()) - /// .unwrap() - /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| { - /// // here you can use a tokio task, thread pool or anything - /// // to do heavy computation to resolve your request - /// // e.g. downloading files, opening the camera... - /// std::thread::spawn(move || { - /// std::thread::sleep(std::time::Duration::from_secs(2)); - /// responder.respond(http::Response::builder().body(Vec::new()).unwrap()); - /// }); - /// }); - /// ``` - #[cfg(feature = "protocol")] - pub fn with_asynchronous_custom_protocol(mut self, name: String, handler: F) -> Self - where - F: Fn(Request>, RequestAsyncResponder) + 'static, - { - self - .webview - .custom_protocols - .push((name, Box::new(handler))); - self - } - - /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. - /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. - pub fn with_ipc_handler(mut self, handler: F) -> Self - where - F: Fn(String) + 'static, - { - self.webview.ipc_handler = Some(Box::new(handler)); - self - } - - /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. - /// - /// # Blocking OS Default Behavior - /// Return `true` in the callback to block the OS' default behavior of handling a file drop. - /// - /// Note, that if you do block this behavior, it won't be possible to drop files on `` forms. - /// Also note, that it's not possible to manually set the value of a `` via JavaScript for security reasons. - #[cfg(feature = "file-drop")] - pub fn with_file_drop_handler(mut self, handler: F) -> Self - where - F: Fn(FileDropEvent) -> bool + 'static, - { - self.webview.file_drop_handler = Some(Box::new(handler)); - self - } - - /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. The provided URL must be valid. - pub fn with_url_and_headers(mut self, url: &str, headers: http::HeaderMap) -> Result { - self.webview.url = Some(url.parse()?); - self.webview.headers = Some(headers); - Ok(self) - } - - /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. The provided URL must be valid. - pub fn with_url(mut self, url: &str) -> Result { - self.webview.url = Some(Url::parse(url)?); - self.webview.headers = None; - Ok(self) - } - - /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. This will be ignored if `url` is provided. - /// - /// # Warning - /// - /// The Page loaded from html string will have `null` origin. - /// - /// ## PLatform-specific: - /// - /// - **Windows:** the string can not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size - pub fn with_html(mut self, html: impl Into) -> Result { - self.webview.html = Some(html.into()); - Ok(self) - } - - /// Set the web context that can share with multiple [`WebView`]s. - pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self { - self.web_context = Some(web_context); - self - } - - /// Set a custom [user-agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) for the WebView. - pub fn with_user_agent(mut self, user_agent: &str) -> Self { - self.webview.user_agent = Some(user_agent.to_string()); - self - } - - /// Enable or disable web inspector which is usually called dev tool. - /// - /// Note this only enables dev tool to the webview. To open it, you can call - /// [`WebView::open_devtools`], or right click the page and open it from the context menu. - /// - /// ## Platform-specific - /// - /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, - /// but requires `devtools` feature flag to actually enable it in **release** build. - /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. - /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. - pub fn with_devtools(mut self, devtools: bool) -> Self { - self.webview.devtools = devtools; - self - } - - /// Whether page zooming by hotkeys or gestures is enabled - /// - /// ## Platform-specific - /// - /// **macOS / Linux / Android / iOS**: Unsupported - pub fn with_hotkeys_zoom(mut self, zoom: bool) -> Self { - self.webview.zoom_hotkeys_enabled = zoom; - self - } - - /// Set a navigation handler to decide if incoming url is allowed to navigate. - /// - /// The closure takes a `String` parameter as url and return `bool` to determine the url. True is - /// allowed to navigate and false is not. - pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self { - self.webview.navigation_handler = Some(Box::new(callback)); - self - } - - /// Set a download started handler to manage incoming downloads. - /// - /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the - /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter - /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be - /// absolute. The closure returns a `bool` to allow or deny the download. - pub fn with_download_started_handler( - mut self, - started_handler: impl FnMut(String, &mut PathBuf) -> bool + 'static, - ) -> Self { - self.webview.download_started_handler = Some(Box::new(started_handler)); - self - } - - /// Sets a download completion handler to manage downloads that have finished. - /// - /// The closure is fired when the download completes, whether it was successful or not. - /// The closure takes a `String` representing the URL of the original download request, an `Option` - /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download - /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download - /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to - /// know if the download succeeded. - /// - /// ## Platform-specific: - /// - /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API - /// limitations. - pub fn with_download_completed_handler( - mut self, - download_completed_handler: impl Fn(String, Option, bool) + 'static, - ) -> Self { - self.webview.download_completed_handler = Some(Rc::new(download_completed_handler)); - self - } - - /// Enables clipboard access for the page rendered on **Linux** and **Windows**. - /// - /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu - /// item accelerators to use shortcuts. - pub fn with_clipboard(mut self, clipboard: bool) -> Self { - self.webview.clipboard = clipboard; - self - } - - /// Set a new window request handler to decide if incoming url is allowed to be opened. - /// - /// The closure takes a `String` parameter as url and return `bool` to determine if the url can be - /// opened in a new window. Returning true will open the url in a new window, whilst returning false - /// will neither open a new window nor allow any navigation. - pub fn with_new_window_req_handler( - mut self, - callback: impl Fn(String) -> bool + 'static, - ) -> Self { - self.webview.new_window_req_handler = Some(Box::new(callback)); - self - } - - /// Sets whether clicking an inactive window also clicks through to the webview. Default is `false`. - /// - /// ## Platform-specific - /// - /// This configuration only impacts macOS. - pub fn with_accept_first_mouse(mut self, accept_first_mouse: bool) -> Self { - self.webview.accept_first_mouse = accept_first_mouse; - self - } - - /// Set a handler closure to process the change of the webview's document title. - pub fn with_document_title_changed_handler( - mut self, - callback: impl Fn(String) + 'static, - ) -> Self { - self.webview.document_title_changed_handler = Some(Box::new(callback)); - 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 - } - - /// Set a handler to process page loading events. - /// - /// The handler will be called when the webview begins the indicated loading event. - pub fn with_on_page_load_handler( - mut self, - handler: impl Fn(PageLoadEvent, String) + 'static, - ) -> Self { - self.webview.on_page_load_handler = Some(Box::new(handler)); - self - } - - /// Set a proxy configuration for the webview. - /// - /// - **macOS**: Requires macOS 14.0+ and the `mac-proxy` feature flag to be enabled. Supports HTTP CONNECT and SOCKSv5 proxies. - /// - **Windows / Linux**: Supports HTTP CONNECT and SOCKSv5 proxies. - /// - **Android / iOS:** Not supported. - pub fn with_proxy_config(mut self, configuration: ProxyConfig) -> Self { - self.webview.proxy_config = Some(configuration); - self - } - - /// Set whether the webview should be focused when created. - /// - /// ## Platform-specific: - /// - /// - **macOS / Android / iOS:** Unsupported. - pub fn with_focused(mut self, focused: bool) -> Self { - self.webview.focused = focused; - self - } - - /// Consume the builder and create the [`WebView`]. - /// - /// Platform-specific behavior: - /// - /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be - /// called in the same thread with the [`EventLoop`] you create. - /// - /// [`EventLoop`]: crate::application::event_loop::EventLoop - pub fn build(self) -> Result { - let webview = InnerWebView::new( - self.window, - self.webview, - self.platform_specific, - self.web_context, - )?; - Ok(WebView { webview }) - } -} - -#[cfg(windows)] -pub trait WebViewBuilderExtWindows { - /// Pass additional args to Webview2 upon creating the webview. - /// - /// ## Warning - /// - /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` - /// `--autoplay-policy=no-user-gesture-required` if autoplay is enabled - /// and `--proxy-server=://:` if a proxy is set. - /// so if you use this method, you have to add these arguments yourself if you want to keep the same behavior. - fn with_additional_browser_args>(self, additional_args: S) -> Self; - - /// Determines whether browser-specific accelerator keys are enabled. When this setting is set to - /// `false`, it disables all accelerator keys that access features specific to a web browser. - /// The default value is `true`. See the following link to know more details. - /// - /// https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings#arebrowseracceleratorkeysenabled - fn with_browser_accelerator_keys(self, enabled: bool) -> Self; - - /// Specifies the theme of webview2. This affects things like `prefers-color-scheme`. - /// - /// Defaults to [`Theme::Auto`] which will follow the OS defaults. - fn with_theme(self, theme: Theme) -> Self; - - /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. - /// - /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints - /// and is therefore less secure but will match the behavior of the `://localhost` protocols used on macOS and Linux. - /// - /// The default value is `false`. - fn with_https_scheme(self, enabled: bool) -> Self; -} - -#[cfg(windows)] -impl WebViewBuilderExtWindows for WebViewBuilder<'_> { - fn with_additional_browser_args>(mut self, additional_args: S) -> Self { - self.platform_specific.additional_browser_args = Some(additional_args.into()); - self - } - - fn with_browser_accelerator_keys(mut self, enabled: bool) -> Self { - self.platform_specific.browser_accelerator_keys = enabled; - self - } - - fn with_theme(mut self, theme: Theme) -> Self { - self.platform_specific.theme = Some(theme); - self - } - - fn with_https_scheme(mut self, enabled: bool) -> Self { - self.platform_specific.https_scheme = enabled; - self - } -} - -#[cfg(target_os = "android")] -pub trait WebViewBuilderExtAndroid { - fn on_webview_created< - F: Fn( - prelude::Context<'_, '_>, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send - + 'static, - >( - self, - f: F, - ) -> Self; - - /// Use [WebviewAssetLoader](https://developer.android.com/reference/kotlin/androidx/webkit/WebViewAssetLoader) - /// to load assets from Android's `asset` folder when using `with_url` as `://assets/` (e.g.: - /// `wry://assets/index.html`). Note that this registers a custom protocol with the provided - /// String, similar to [`with_custom_protocol`], but also sets the WebViewAssetLoader with the - /// necessary domain (which is fixed as `.assets`). This cannot be used in conjunction - /// to `with_custom_protocol` for Android, as it changes the way in which requests are handled. - #[cfg(feature = "protocol")] - fn with_asset_loader(self, protocol: String) -> Self; - - /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. - /// - /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints - /// and is therefore less secure but will match the behavior of the `://localhost` protocols used on macOS and Linux. - /// - /// The default value is `false`. - fn with_https_scheme(self, enabled: bool) -> Self; -} - -#[cfg(target_os = "android")] -impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { - fn on_webview_created< - F: Fn( - prelude::Context<'_, '_>, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send - + 'static, - >( - mut self, - f: F, - ) -> Self { - self.platform_specific.on_webview_created = Some(Box::new(f)); - self - } - - #[cfg(feature = "protocol")] - fn with_asset_loader(mut self, protocol: String) -> Self { - // register custom protocol with empty Response return, - // this is necessary due to the need of fixing a domain - // in WebViewAssetLoader. - self.webview.custom_protocols.push(( - protocol.clone(), - Box::new(|_, api| { - api.respond(HttpResponse::builder().body(Vec::new()).unwrap()); - }), - )); - self.platform_specific.with_asset_loader = true; - self.platform_specific.asset_loader_domain = Some(format!("{}.assets", protocol)); - self - } - - fn with_https_scheme(mut self, enabled: bool) -> Self { - self.platform_specific.https_scheme = enabled; - self - } -} - -/// The fundamental type to present a [`WebView`]. -/// -/// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and -/// scripts for those who prefer to control fine grained window creation and event handling. -/// [`WebView`] presents the actual WebView window and let you still able to perform actions -/// during event handling to it. -pub struct WebView { - webview: InnerWebView, -} - -// Signal the Window to drop on Linux and Windows. On mac, we need to handle several unsafe code -// blocks and raw pointer properly. -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -impl Drop for WebView { - fn drop(&mut self) { - unsafe { - use crate::application::platform::unix::WindowExtUnix; - use gtk::prelude::WidgetExtManual; - self.window().gtk_window().destroy(); - } - } -} - -#[cfg(target_os = "windows")] -impl Drop for WebView { - fn drop(&mut self) { - unsafe { - let _ = DestroyWindow(HWND(self.window.hwnd() as _)); - } - } -} - -impl WebView { - /// Create a [`WebView`] from provided [`RawWindowHandle`]. Note that calling this directly loses - /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To - /// benefit from above features, create a [`WebViewBuilder`] instead. - /// - /// Platform-specific behavior: - /// - /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be - /// called in the same thread with the [`EventLoop`] you create. - /// - /// [`EventLoop`]: crate::application::event_loop::EventLoop - pub fn new(window: RawWindowHandle) -> Result { - WebViewBuilder::new(window)?.build() - } - - /// Get the current url of the webview - pub fn url(&self) -> Url { - self.webview.url() - } - - /// Evaluate and run javascript code. Must be called on the same thread who created the - /// [`WebView`]. Use [`EventLoopProxy`] and a custom event to send scripts from other threads. - /// - /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy - /// - pub fn evaluate_script(&self, js: &str) -> Result<()> { - self - .webview - .eval(js, None::>) - } - - /// Evaluate and run javascript code with callback function. The evaluation result will be - /// serialized into a JSON string and passed to the callback function. Must be called on the - /// same thread who created the [`WebView`]. Use [`EventLoopProxy`] and a custom event to - /// send scripts from other threads. - /// - /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy - /// - /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. - /// - /// - ** Android:** Not implemented yet. - pub fn evaluate_script_with_callback( - &self, - js: &str, - callback: impl Fn(String) + Send + 'static, - ) -> Result<()> { - self.webview.eval(js, Some(callback)) - } - - /// Launch print modal for the webview content. - pub fn print(&self) -> Result<()> { - self.webview.print(); - Ok(()) - } - - /// Open the web inspector which is usually called dev tool. - /// - /// ## Platform-specific - /// - /// - **Android / iOS:** Not supported. - #[cfg(any(debug_assertions, feature = "devtools"))] - pub fn open_devtools(&self) { - self.webview.open_devtools(); - } - - /// Close the web inspector which is usually called dev tool. - /// - /// ## Platform-specific - /// - /// - **Windows / Android / iOS:** Not supported. - #[cfg(any(debug_assertions, feature = "devtools"))] - pub fn close_devtools(&self) { - self.webview.close_devtools(); - } - - /// Gets the devtool window's current visibility state. - /// - /// ## Platform-specific - /// - /// - **Windows / Android / iOS:** Not supported. - #[cfg(any(debug_assertions, feature = "devtools"))] - pub fn is_devtools_open(&self) -> bool { - self.webview.is_devtools_open() - } - - /// Set the webview zoom level - /// - /// ## Platform-specific: - /// - /// - **Android**: Not supported. - /// - **macOS**: available on macOS 11+ only. - /// - **iOS**: available on iOS 14+ only. - pub fn zoom(&self, scale_factor: f64) { - self.webview.zoom(scale_factor); - } - - /// Specify the webview background color. - /// - /// The color uses the RGBA format. - /// - /// ## Platfrom-specific: - /// - /// - **macOS / iOS**: Not implemented. - /// - **Windows**: - /// - On Windows 7, transparency is not supported and the alpha value will be ignored. - /// - On Windows higher than 7: translucent colors are not supported so any alpha value other than `0` will be replaced by `255` - pub fn set_background_color(&self, background_color: RGBA) -> Result<()> { - self.webview.set_background_color(background_color) - } - - /// Navigate to the specified url - pub fn load_url(&self, url: &str) { - self.webview.load_url(url) - } - - /// Navigate to the specified url using the specified headers - pub fn load_url_with_headers(&self, url: &str, headers: http::HeaderMap) { - self.webview.load_url_with_headers(url, headers) - } - - /// Clear all browsing data - pub fn clear_all_browsing_data(&self) -> Result<()> { - self.webview.clear_all_browsing_data() - } -} - -/// An event enumeration sent to [`FileDropHandler`]. -#[non_exhaustive] -#[derive(Debug, Serialize, Clone)] -pub enum FileDropEvent { - /// The file(s) have been dragged onto the window, but have not been dropped yet. - Hovered { - paths: Vec, - /// The position of the mouse cursor. - position: Position, - }, - /// The file(s) have been dropped onto the window. - Dropped { - paths: Vec, - /// The position of the mouse cursor. - position: Position, - }, - /// The file drop was aborted. - Cancelled, -} - -/// Get Webview/Webkit version on current platform. -pub fn webview_version() -> Result { - platform_webview_version() -} - -/// Additional methods on `WebView` that are specific to Windows. -#[cfg(target_os = "windows")] -pub trait WebviewExtWindows { - /// Returns WebView2 Controller - fn controller(&self) -> ICoreWebView2Controller; - - // Changes the webview2 theme. - fn set_theme(&self, theme: Theme); -} - -#[cfg(target_os = "windows")] -impl WebviewExtWindows for WebView { - fn controller(&self) -> ICoreWebView2Controller { - self.webview.controller.clone() - } - - fn set_theme(&self, theme: Theme) { - self.webview.set_theme(theme) - } -} - -/// Additional methods on `WebView` that are specific to Linux. -#[cfg(target_os = "linux")] -pub trait WebviewExtUnix { - /// Returns Webkit2gtk Webview handle - fn webview(&self) -> Rc; -} - -#[cfg(target_os = "linux")] -impl WebviewExtUnix for WebView { - fn webview(&self) -> Rc { - self.webview.webview.clone() - } -} - -/// Additional methods on `WebView` that are specific to macOS. -#[cfg(target_os = "macos")] -pub trait WebviewExtMacOS { - /// Returns WKWebView handle - fn webview(&self) -> cocoa::base::id; - /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle - fn manager(&self) -> cocoa::base::id; - /// Returns NSWindow associated with the WKWebView webview - fn ns_window(&self) -> cocoa::base::id; -} - -#[cfg(target_os = "macos")] -impl WebviewExtMacOS for WebView { - fn webview(&self) -> cocoa::base::id { - self.webview.webview - } - - fn manager(&self) -> cocoa::base::id { - self.webview.manager - } - - fn ns_window(&self) -> cocoa::base::id { - self.webview.ns_window - } -} - -/// Additional methods on `WebView` that are specific to iOS. -#[cfg(target_os = "ios")] -pub trait WebviewExtIOS { - /// Returns WKWebView handle - fn webview(&self) -> cocoa::base::id; - /// Returns WKWebView manager [(userContentController)](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller) handle - fn manager(&self) -> cocoa::base::id; -} - -#[cfg(target_os = "ios")] -impl WebviewExtIOS for WebView { - fn webview(&self) -> cocoa::base::id { - self.webview.webview - } - - fn manager(&self) -> cocoa::base::id { - self.webview.manager - } -} - -#[cfg(target_os = "android")] -/// Additional methods on `WebView` that are specific to Android -pub trait WebviewExtAndroid { - fn handle(&self) -> JniHandle; -} - -#[cfg(target_os = "android")] -impl WebviewExtAndroid for WebView { - fn handle(&self) -> JniHandle { - JniHandle - } -} - -#[derive(Debug, Clone, Copy)] -pub enum Theme { - Dark, - Light, - Auto, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_get_webview_version() { - if let Err(error) = webview_version() { - panic!("{}", error); - } - } -} diff --git a/src/webview/webview2/resize.rs b/src/webview/webview2/resize.rs deleted file mode 100644 index 65ddcac86..000000000 --- a/src/webview/webview2/resize.rs +++ /dev/null @@ -1,155 +0,0 @@ -#![allow(non_snake_case)] - -use once_cell::sync::Lazy; -use windows::{ - core::HRESULT, - Win32::{ - Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}, - Graphics::Gdi::{ - GetDC, GetDeviceCaps, MonitorFromWindow, HMONITOR, LOGPIXELSX, MONITOR_DEFAULTTONEAREST, - }, - UI::{ - HiDpi::{MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE}, - Input::KeyboardAndMouse::ReleaseCapture, - WindowsAndMessaging::{ - GetWindowRect, IsProcessDPIAware, PostMessageW, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, - HTCLIENT, HTLEFT, HTNOWHERE, HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, - }, - }, - }, -}; - -use super::get_function; - -#[inline] -pub fn MAKELPARAM(x: i16, y: i16) -> LPARAM { - LPARAM(((x as u16 as u32) | ((y as u16 as u32) << 16)) as usize as _) -} - -#[inline] -pub fn begin_resize_drag( - hwnd: isize, - edge: isize, - button: u32, - x: i32, - y: i32, -) -> windows::core::Result<()> { - unsafe { - let w_param = WPARAM(edge as _); - let l_param = MAKELPARAM(x as i16, y as i16); - - ReleaseCapture()?; - PostMessageW(HWND(hwnd), button, w_param, l_param) - } -} - -type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32; -type GetDpiForMonitor = unsafe extern "system" fn( - hmonitor: HMONITOR, - dpi_type: MONITOR_DPI_TYPE, - dpi_x: *mut u32, - dpi_y: *mut u32, -) -> HRESULT; - -static GET_DPI_FOR_WINDOW: Lazy> = - Lazy::new(|| get_function!("user32.dll", GetDpiForWindow)); -static GET_DPI_FOR_MONITOR: Lazy> = - Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor)); - -const BASE_DPI: u32 = 96; -fn dpi_to_scale_factor(dpi: u32) -> f64 { - dpi as f64 / BASE_DPI as f64 -} - -unsafe fn hwnd_dpi(hwnd: HWND) -> u32 { - let hdc = GetDC(hwnd); - if hdc.is_invalid() { - panic!("[tao] `GetDC` returned null!"); - } - if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW { - // We are on Windows 10 Anniversary Update (1607) or later. - match GetDpiForWindow(hwnd) { - 0 => BASE_DPI, // 0 is returned if hwnd is invalid - dpi => dpi as u32, - } - } else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR { - // We are on Windows 8.1 or later. - let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - if monitor.is_invalid() { - return BASE_DPI; - } - - let mut dpi_x = 0; - let mut dpi_y = 0; - if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y).is_ok() { - dpi_x as u32 - } else { - BASE_DPI - } - } else { - // We are on Vista or later. - if IsProcessDPIAware().as_bool() { - // If the process is DPI aware, then scaling must be handled by the application using - // this DPI value. - GetDeviceCaps(hdc, LOGPIXELSX) as u32 - } else { - // If the process is DPI unaware, then scaling is performed by the OS; we thus return - // 96 (scale factor 1.0) to prevent the window from being re-scaled by both the - // application and the WM. - BASE_DPI - } - } -} - -const BORDERLESS_RESIZE_INSET: i32 = 5; - -pub fn hit_test(hwnd: isize, cx: i32, cy: i32) -> LRESULT { - let hwnd = HWND(hwnd); - let mut window_rect = RECT::default(); - unsafe { - if GetWindowRect(hwnd, &mut window_rect).is_ok() { - const CLIENT: isize = 0b0000; - const LEFT: isize = 0b0001; - const RIGHT: isize = 0b0010; - const TOP: isize = 0b0100; - const BOTTOM: isize = 0b1000; - const TOPLEFT: isize = TOP | LEFT; - const TOPRIGHT: isize = TOP | RIGHT; - const BOTTOMLEFT: isize = BOTTOM | LEFT; - const BOTTOMRIGHT: isize = BOTTOM | RIGHT; - - let RECT { - left, - right, - bottom, - top, - } = window_rect; - - let dpi = hwnd_dpi(hwnd); - let scale_factor = dpi_to_scale_factor(dpi); - let inset = (BORDERLESS_RESIZE_INSET as f64 * scale_factor) as i32; - - #[rustfmt::skip] - let result = - (LEFT * (if cx < (left + inset) { 1 } else { 0 })) - | (RIGHT * (if cx >= (right - inset) { 1 } else { 0 })) - | (TOP * (if cy < (top + inset) { 1 } else { 0 })) - | (BOTTOM * (if cy >= (bottom - inset) { 1 } else { 0 })); - - LRESULT(match result { - CLIENT => HTCLIENT, - LEFT => HTLEFT, - RIGHT => HTRIGHT, - TOP => HTTOP, - BOTTOM => HTBOTTOM, - TOPLEFT => HTTOPLEFT, - TOPRIGHT => HTTOPRIGHT, - BOTTOMLEFT => HTBOTTOMLEFT, - BOTTOMRIGHT => HTBOTTOMRIGHT, - _ => HTNOWHERE, - } as _) - } else { - LRESULT(HTNOWHERE as _) - } - } -} diff --git a/src/webview/webview2/file_drop.rs b/src/webview2/file_drop.rs similarity index 63% rename from src/webview/webview2/file_drop.rs rename to src/webview2/file_drop.rs index 967f4b942..667dc58ca 100644 --- a/src/webview/webview2/file_drop.rs +++ b/src/webview2/file_drop.rs @@ -2,12 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::webview::FileDropEvent; - // A silly implementation of file drop handling for Windows! -// This can be pretty much entirely replaced when WebView2 SDK 1.0.721-prerelease becomes stable. -// https://docs.microsoft.com/en-us/microsoft-edge/webview2/releasenotes#10721-prerelease -// https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2experimentalcompositioncontroller3?view=webview2-1.0.721-prerelease&preserve-view=true + +use crate::FileDropEvent; use std::{ cell::UnsafeCell, @@ -37,90 +34,57 @@ use windows::Win32::{ use windows_implement::implement; -use crate::application::{ - dpi::PhysicalPosition, platform::windows::WindowExtWindows, window::Window, -}; - +#[derive(Default)] pub(crate) struct FileDropController { drop_targets: Vec, } impl FileDropController { - pub(crate) fn new() -> Self { - FileDropController { - drop_targets: Vec::new(), - } - } + pub(crate) fn new(hwnd: HWND, handler: Box bool>) -> Self { + let mut controller = FileDropController::default(); - pub(crate) fn listen( - &mut self, - hwnd: HWND, - window: Rc, - handler: Box bool>, - ) { - let listener = Rc::new(handler); + let handler = Rc::new(handler); + let mut callback = |hwnd| controller.inject_in_hwnd(hwnd, handler.clone()); // Enumerate child windows to find the WebView2 "window" and override! - enumerate_child_windows(hwnd, |hwnd| { - self.inject(hwnd, window.clone(), listener.clone()) - }); - } - - fn inject( - &mut self, - hwnd: HWND, - window: Rc, - listener: Rc bool>, - ) -> bool { - // Safety: WinAPI calls are unsafe - unsafe { - let file_drop_handler: IDropTarget = FileDropHandler::new(window, listener).into(); - - if RevokeDragDrop(hwnd) != Err(DRAGDROP_E_INVALIDHWND.into()) - && RegisterDragDrop(hwnd, &file_drop_handler).is_ok() - { - // Not a great solution. But there is no reliable way to get the window handle of the webview, for whatever reason... - self.drop_targets.push(file_drop_handler); + { + let mut trait_obj: &mut dyn FnMut(HWND) -> bool = &mut callback; + let closure_pointer_pointer: *mut c_void = unsafe { std::mem::transmute(&mut trait_obj) }; + let lparam = LPARAM(closure_pointer_pointer as _); + unsafe extern "system" fn enumerate_callback(hwnd: HWND, lparam: LPARAM) -> BOOL { + let closure = &mut *(lparam.0 as *mut c_void as *mut &mut dyn FnMut(HWND) -> bool); + closure(hwnd).into() } + unsafe { EnumChildWindows(hwnd, Some(enumerate_callback), lparam) }; } - true + controller } -} -// https://gist.github.com/application-developer-DA/5a460d9ca02948f1d2bfa53100c941da -// Safety: WinAPI calls are unsafe - -fn enumerate_child_windows(hwnd: HWND, mut callback: F) -where - F: FnMut(HWND) -> bool, -{ - let mut trait_obj: &mut dyn FnMut(HWND) -> bool = &mut callback; - let closure_pointer_pointer: *mut c_void = unsafe { std::mem::transmute(&mut trait_obj) }; - let lparam = LPARAM(closure_pointer_pointer as _); - unsafe { EnumChildWindows(hwnd, Some(enumerate_callback), lparam) }; -} + fn inject_in_hwnd(&mut self, hwnd: HWND, handler: Rc bool>) -> bool { + let file_drop_handler: IDropTarget = FileDropHandler::new(hwnd, handler).into(); + if unsafe { RevokeDragDrop(hwnd) } != Err(DRAGDROP_E_INVALIDHWND.into()) + && unsafe { RegisterDragDrop(hwnd, &file_drop_handler) }.is_ok() + { + self.drop_targets.push(file_drop_handler); + } -unsafe extern "system" fn enumerate_callback(hwnd: HWND, lparam: LPARAM) -> BOOL { - let closure = &mut *(lparam.0 as *mut c_void as *mut &mut dyn FnMut(HWND) -> bool); - closure(hwnd).into() + true + } } #[implement(IDropTarget)] pub struct FileDropHandler { - window: Rc, - listener: Rc bool>, + hwnd: HWND, + listener: Rc bool>, cursor_effect: UnsafeCell, hovered_is_valid: UnsafeCell, /* If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted */ } impl FileDropHandler { - pub fn new( - window: Rc, - listener: Rc bool>, - ) -> FileDropHandler { + pub fn new(hwnd: HWND, listener: Rc bool>) -> FileDropHandler { Self { - window, + hwnd, listener, cursor_effect: DROPEFFECT_NONE.into(), hovered_is_valid: false.into(), @@ -208,16 +172,13 @@ impl IDropTarget_Impl for FileDropHandler { *self.cursor_effect.get() = cursor_effect; let mut pt = POINT { x: pt.x, y: pt.y }; - ScreenToClient(HWND(self.window.hwnd() as _), &mut pt); + ScreenToClient(self.hwnd, &mut pt); } - (self.listener)( - &self.window, - FileDropEvent::Hovered { - paths, - position: PhysicalPosition::new(pt.x as _, pt.y as _), - }, - ); + (self.listener)(FileDropEvent::Hovered { + paths, + position: (pt.x as _, pt.y as _), + }); Ok(()) } @@ -234,7 +195,7 @@ impl IDropTarget_Impl for FileDropHandler { fn DragLeave(&self) -> windows::core::Result<()> { if unsafe { *self.hovered_is_valid.get() } { - (self.listener)(&self.window, FileDropEvent::Cancelled); + (self.listener)(FileDropEvent::Cancelled); } Ok(()) } @@ -254,16 +215,13 @@ impl IDropTarget_Impl for FileDropHandler { } let mut pt = POINT { x: pt.x, y: pt.y }; - ScreenToClient(HWND(self.window.hwnd() as _), &mut pt); + ScreenToClient(self.hwnd, &mut pt); } - (self.listener)( - &self.window, - FileDropEvent::Dropped { - paths, - position: PhysicalPosition::new(pt.x as _, pt.y as _), - }, - ); + (self.listener)(FileDropEvent::Dropped { + paths, + position: (pt.x as _, pt.y as _), + }); Ok(()) } diff --git a/src/webview/webview2/mod.rs b/src/webview2/mod.rs similarity index 86% rename from src/webview/webview2/mod.rs rename to src/webview2/mod.rs index a141b29bb..58e47b86f 100644 --- a/src/webview/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -3,17 +3,6 @@ // SPDX-License-Identifier: MIT mod file_drop; -mod resize; - -use crate::{ - webview::{ - proxy::ProxyConfig, PageLoadEvent, RequestAsyncResponder, WebContext, WebViewAttributes, RGBA, - }, - Error, Result, -}; - -use file_drop::FileDropController; -use url::Url; use std::{ borrow::Cow, @@ -22,37 +11,44 @@ use std::{ iter::once, os::windows::prelude::OsStrExt, path::PathBuf, - rc::Rc, sync::{mpsc, Arc}, }; -use once_cell::{sync::Lazy, unsync::OnceCell}; - +use http::{Request, Response as HttpResponse, StatusCode}; +use once_cell::sync::Lazy; +use raw_window_handle::HasWindowHandle; +use url::Url; +use webview2_com::{Microsoft::Web::WebView2::Win32::*, *}; use windows::{ core::{s, ComInterface, PCSTR, PCWSTR, PWSTR}, Win32::{ Foundation::*, Globalization::{self, MAX_LOCALE_NAME}, - Graphics::Gdi::{RedrawWindow, HRGN, RDW_INTERNALPAINT}, + Graphics::Gdi::{RedrawWindow, HBRUSH, HRGN, RDW_INTERNALPAINT}, System::{ - Com::IStream, - LibraryLoader::{GetProcAddress, LoadLibraryW}, + Com::{CoInitializeEx, IStream, COINIT_APARTMENTTHREADED}, + LibraryLoader::{GetModuleHandleW, GetProcAddress, LoadLibraryW}, SystemInformation::OSVERSIONINFOW, WinRT::EventRegistrationToken, }, UI::{ Shell::{DefSubclassProc, SHCreateMemStream, SetWindowSubclass}, - WindowsAndMessaging::{self as win32wm, PostMessageW, RegisterWindowMessageA}, + WindowsAndMessaging::{ + self as win32wm, CreateWindowExW, DefWindowProcW, PostMessageW, RegisterClassExW, + RegisterWindowMessageA, SetWindowPos, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, HCURSOR, + HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, + WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, WS_VISIBLE, + }, }, }, }; -use webview2_com::{Microsoft::Web::WebView2::Win32::*, *}; - -use crate::application::{platform::windows::WindowExtWindows, window::Window}; -use http::{Request, Response as HttpResponse, StatusCode}; - +use self::file_drop::FileDropController; use super::Theme; +use crate::{ + proxy::ProxyConfig, Error, PageLoadEvent, RequestAsyncResponder, Result, WebContext, + WebViewAttributes, RGBA, +}; impl From for Error { fn from(err: webview2_com::Error) -> Self { @@ -61,39 +57,123 @@ impl From for Error { } pub(crate) struct InnerWebView { + hwnd: HWND, + is_child: bool, pub controller: ICoreWebView2Controller, webview: ICoreWebView2, env: ICoreWebView2Environment, // Store FileDropController in here to make sure it gets dropped when // the webview gets dropped, otherwise we'll have a memory leak #[allow(dead_code)] - file_drop_controller: Rc>, + file_drop_controller: Option, } impl InnerWebView { pub fn new( - window: Rc, + window: &impl HasWindowHandle, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + match window.window_handle()?.as_raw() { + raw_window_handle::RawWindowHandle::Win32(handle) => { + Self::new_hwnd(HWND(handle.hwnd.get()), attributes, pl_attrs, web_context) + } + _ => unreachable!(), + } + } + + pub fn new_as_child( + window: &impl HasWindowHandle, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + match window.window_handle()?.as_raw() { + raw_window_handle::RawWindowHandle::Win32(parent) => { + let class_name = encode_wide("WRY_WEBVIEW"); + + unsafe extern "system" fn default_window_proc( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, + ) -> LRESULT { + DefWindowProcW(hwnd, msg, wparam, lparam) + } + + let class = WNDCLASSEXW { + cbSize: std::mem::size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(default_window_proc), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: unsafe { HINSTANCE(GetModuleHandleW(PCWSTR::null()).unwrap_or_default().0) }, + hIcon: HICON::default(), + hCursor: HCURSOR::default(), // must be null in order for cursor state to work properly + hbrBackground: HBRUSH::default(), + lpszMenuName: PCWSTR::null(), + lpszClassName: PCWSTR::from_raw(class_name.as_ptr()), + hIconSm: HICON::default(), + }; + + unsafe { RegisterClassExW(&class) }; + + let child = unsafe { + CreateWindowExW( + WINDOW_EX_STYLE::default(), + PCWSTR::from_raw(class_name.as_ptr()), + PCWSTR::null(), + WS_CHILD | WS_VISIBLE, + attributes.position.map(|a| a.0).unwrap_or(CW_USEDEFAULT), + attributes.position.map(|a| a.1).unwrap_or(CW_USEDEFAULT), + attributes.size.map(|a| a.0 as i32).unwrap_or(CW_USEDEFAULT), + attributes.size.map(|a| a.1 as i32).unwrap_or(CW_USEDEFAULT), + HWND(parent.hwnd.get()), + HMENU::default(), + GetModuleHandleW(PCWSTR::null()).unwrap_or_default(), + None, + ) + }; + + Self::new_as_child_hwnd(child, attributes, pl_attrs, web_context) + } + _ => unreachable!(), + } + } + + pub fn new_as_child_hwnd( + hwnd: HWND, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + let mut webview = Self::new_hwnd(hwnd, attributes, pl_attrs, web_context)?; + webview.is_child = true; + Ok(webview) + } + + pub fn new_hwnd( + hwnd: HWND, mut attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { - let hwnd = HWND(window.hwnd() as _); - let file_drop_controller: Rc> = Rc::new(OnceCell::new()); - let file_drop_handler = attributes.file_drop_handler.take(); - let file_drop_window = window.clone(); + let _ = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) }; + + let file_drop_controller = attributes + .file_drop_handler + .take() + .map(|handler| FileDropController::new(hwnd, handler)); let env = Self::create_environment(&web_context, pl_attrs.clone(), &attributes)?; 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 { - let mut controller = FileDropController::new(); - controller.listen(hwnd, file_drop_window, file_drop_handler); - let _ = file_drop_controller.set(controller); - } + let webview = Self::init_webview(hwnd, attributes, &env, &controller, pl_attrs)?; Ok(Self { + hwnd, controller, + is_child: false, webview, env, file_drop_controller, @@ -226,7 +306,6 @@ impl InnerWebView { } fn init_webview( - window: Rc, hwnd: HWND, mut attributes: WebViewAttributes, env: &ICoreWebView2Environment, @@ -311,7 +390,6 @@ impl InnerWebView { // document title changed handler if let Some(document_title_changed_handler) = attributes.document_title_changed_handler { - let window_ = window.clone(); unsafe { webview .add_DocumentTitleChanged( @@ -320,7 +398,7 @@ impl InnerWebView { if let Some(webview) = webview { webview.DocumentTitle(&mut title)?; let title = take_pwstr(title); - document_title_changed_handler(&window_, title); + document_title_changed_handler(title); } Ok(()) })), @@ -367,22 +445,13 @@ impl InnerWebView { Self::add_script_to_execute_on_document_created( &webview, String::from( - r#"Object.defineProperty(window, 'ipc', { - value: Object.freeze({postMessage:s=>window.chrome.webview.postMessage(s)}) -}); - -window.addEventListener('mousedown', (e) => { - if (e.buttons === 1) window.chrome.webview.postMessage('__WEBVIEW_LEFT_MOUSE_DOWN__') -}); -window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('__WEBVIEW_MOUSE_MOVE__'));"#, + r#"Object.defineProperty(window, 'ipc', { value: Object.freeze({ postMessage: s=> window.chrome.webview.postMessage(s) }) });"#, ), )?; for js in attributes.initialization_scripts { Self::add_script_to_execute_on_document_created(&webview, js)?; } - let window_ = window.clone(); - // Message handler let ipc_handler = attributes.ipc_handler.take(); unsafe { @@ -392,49 +461,8 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ let mut js = PWSTR::null(); args.TryGetWebMessageAsString(&mut js)?; let js = take_pwstr(js); - if js == "__WEBVIEW_LEFT_MOUSE_DOWN__" || js == "__WEBVIEW_MOUSE_MOVE__" { - if !window_.is_decorated() && window_.is_resizable() && !window_.is_maximized() { - use crate::application::window::CursorIcon; - - let mut point = POINT::default(); - win32wm::GetCursorPos(&mut point)?; - let result = resize::hit_test(window_.hwnd(), point.x, point.y); - let cursor = match result.0 as u32 { - win32wm::HTLEFT => CursorIcon::WResize, - win32wm::HTTOP => CursorIcon::NResize, - win32wm::HTRIGHT => CursorIcon::EResize, - win32wm::HTBOTTOM => CursorIcon::SResize, - win32wm::HTTOPLEFT => CursorIcon::NwResize, - win32wm::HTTOPRIGHT => CursorIcon::NeResize, - win32wm::HTBOTTOMLEFT => CursorIcon::SwResize, - win32wm::HTBOTTOMRIGHT => CursorIcon::SeResize, - _ => CursorIcon::Arrow, - }; - // don't use `CursorIcon::Arrow` variant or cursor manipulation using css will cause cursor flickering - if cursor != CursorIcon::Arrow { - window_.set_cursor_icon(cursor); - } - - if js == "__WEBVIEW_LEFT_MOUSE_DOWN__" { - // we ignore `HTCLIENT` variant so the webview receives the click correctly if it is not on the edges - // and prevent conflict with `tao::window::drag_window`. - if result.0 as u32 != win32wm::HTCLIENT { - resize::begin_resize_drag( - window_.hwnd(), - result.0, - win32wm::WM_NCLBUTTONDOWN, - point.x, - point.y, - )?; - } - } - } - // these are internal messages, ipc_handlers don't need it so exit early - return Ok(()); - } - if let Some(ipc_handler) = &ipc_handler { - ipc_handler(&window_, js); + ipc_handler(js); } } @@ -588,7 +616,6 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ let env = env.clone(); let main_thread_id = std::thread::current().id(); - let hwnd = window.hwnd(); unsafe { webview .add_WebResourceRequested( @@ -967,6 +994,38 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_ pub fn set_theme(&self, theme: Theme) { set_theme(&self.webview, theme); } + + pub fn set_position(&self, position: (i32, i32)) { + if self.is_child { + unsafe { + let _ = SetWindowPos( + self.hwnd, + HWND::default(), + position.0, + position.1, + 0, + 0, + SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER, + ); + } + } + } + + pub fn set_size(&self, size: (u32, u32)) { + if self.is_child { + unsafe { + let _ = SetWindowPos( + self.hwnd, + HWND::default(), + 0, + 0, + size.0 as _, + size.1 as _, + SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER, + ); + } + } + } } unsafe fn prepare_web_request_response( @@ -1111,11 +1170,8 @@ fn get_function_impl(library: &str, function: &str) -> Option { macro_rules! get_function { ($lib:expr, $func:ident) => { - crate::webview::webview2::get_function_impl( - concat!($lib, '\0'), - concat!(stringify!($func), '\0'), - ) - .map(|f| unsafe { std::mem::transmute::(f) }) + crate::webview2::get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0')) + .map(|f| unsafe { std::mem::transmute::(f) }) }; } @@ -1157,7 +1213,7 @@ fn url_from_webview(webview: &ICoreWebView2) -> String { static EXEC_MSG_ID: Lazy = Lazy::new(|| unsafe { RegisterWindowMessageA(s!("Wry::ExecMsg")) }); -unsafe fn dispatch_handler(hwnd: isize, function: F) +unsafe fn dispatch_handler(hwnd: HWND, function: F) where F: FnMut() + 'static, { @@ -1167,7 +1223,7 @@ where let raw = Box::into_raw(boxed2); - let res = PostMessageW(HWND(hwnd), *EXEC_MSG_ID, WPARAM(raw as _), LPARAM(0)); + let res = PostMessageW(hwnd, *EXEC_MSG_ID, WPARAM(raw as _), LPARAM(0)); assert!( res.is_ok(), "PostMessage failed ; is the messages queue full?" diff --git a/src/webview/wkwebview/download.rs b/src/wkwebview/download.rs similarity index 100% rename from src/webview/wkwebview/download.rs rename to src/wkwebview/download.rs diff --git a/src/webview/wkwebview/file_drop.rs b/src/wkwebview/file_drop.rs similarity index 100% rename from src/webview/wkwebview/file_drop.rs rename to src/wkwebview/file_drop.rs diff --git a/src/webview/wkwebview/mod.rs b/src/wkwebview/mod.rs similarity index 100% rename from src/webview/wkwebview/mod.rs rename to src/wkwebview/mod.rs diff --git a/src/webview/wkwebview/navigation.rs b/src/wkwebview/navigation.rs similarity index 100% rename from src/webview/wkwebview/navigation.rs rename to src/wkwebview/navigation.rs diff --git a/src/webview/wkwebview/proxy.rs b/src/wkwebview/proxy.rs similarity index 100% rename from src/webview/wkwebview/proxy.rs rename to src/wkwebview/proxy.rs diff --git a/src/webview/wkwebview/synthetic_mouse_events.rs b/src/wkwebview/synthetic_mouse_events.rs similarity index 100% rename from src/webview/wkwebview/synthetic_mouse_events.rs rename to src/wkwebview/synthetic_mouse_events.rs From 919ce11cb64b04c78b3e3614064ba55a9b85192f Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 24 Oct 2023 06:33:33 +0300 Subject: [PATCH 10/80] Update Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed73d2864..daa6e65c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,5 +91,5 @@ base64 = "0.21" [dev-dependencies] http-range = "0.1.5" -tao = { git = "https://github.com/tauri-apps/tao", branch = "rwh" } +tao = { git = "https://github.com/tauri-apps/tao" } winit = "0.29" \ No newline at end of file From 0e32a04310d791d47b8d7148a119558f17c31144 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Tue, 24 Oct 2023 13:06:01 +0900 Subject: [PATCH 11/80] Fix macOS and iOS --- .changes/rwh.md | 1 + src/lib.rs | 21 ++++------ src/wkwebview/file_drop.rs | 6 +-- src/wkwebview/mod.rs | 81 +++++++++++++++++++++++++------------ src/wkwebview/navigation.rs | 2 +- 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/.changes/rwh.md b/.changes/rwh.md index 08c7abfed..1af1eaa0c 100644 --- a/.changes/rwh.md +++ b/.changes/rwh.md @@ -3,6 +3,7 @@ --- Refactor new method to take raw window handle instead. Following are APIs got affected: + - `application` module is removed, and `webivew` module is moved to root module. - `WebviewBuilder::new`, `Webview::new` now take `RawWindowHandle` instead. - Attributes `ipc_handler`, `file_drop_handler`, `document_change_handler` don't have window parameter anymore. Users should use closure to capture the types they want to use. diff --git a/src/lib.rs b/src/lib.rs index 7b2dad53c..9112f6e13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,20 +11,18 @@ //! //! ```no_run //! fn main() -> wry::Result<()> { -//! use wry::{ -//! application::{ +//! use tao::{ //! event::{Event, StartCause, WindowEvent}, //! event_loop::{ControlFlow, EventLoop}, //! window::WindowBuilder, -//! }, -//! webview::WebViewBuilder, //! }; +//! use wry::WebViewBuilder; //! //! let event_loop = EventLoop::new(); //! let window = WindowBuilder::new() //! .with_title("Hello World") -//! .build(&event_loop)?; -//! let _webview = WebViewBuilder::new(&window)? +//! .build(&event_loop).unwrap(); +//! let _webview = WebViewBuilder::new(&window) //! .with_url("https://tauri.studio")? //! .build()?; //! @@ -565,21 +563,18 @@ impl<'a> WebViewBuilder<'a> { /// # Examples /// /// ```no_run - /// use wry::{ - /// application::{ + /// use tao::{ /// event_loop::EventLoop, /// window::WindowBuilder - /// }, - /// webview::WebViewBuilder, /// }; - /// use rwh_05::HasRawWindowHandle; + /// use raw_window_handle::HasRawWindowHandle; + /// use wry::WebViewBuilder; /// /// let event_loop = EventLoop::new(); /// let window = WindowBuilder::new() /// .build(&event_loop) /// .unwrap(); - /// WebViewBuilder::new(window.raw_window_handle()) - /// .unwrap() + /// WebViewBuilder::new(&window) /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| { /// // here you can use a tokio task, thread pool or anything /// // to do heavy computation to resolve your request diff --git a/src/wkwebview/file_drop.rs b/src/wkwebview/file_drop.rs index cb02e5468..33e6fc38a 100644 --- a/src/wkwebview/file_drop.rs +++ b/src/wkwebview/file_drop.rs @@ -17,7 +17,7 @@ use objc::{ }; use once_cell::sync::Lazy; -use crate::{application::dpi::LogicalPosition, webview::FileDropEvent}; +use crate::FileDropEvent; pub(crate) type NSDragOperation = cocoa::foundation::NSUInteger; #[allow(non_upper_case_globals)] @@ -110,7 +110,7 @@ extern "C" fn dragging_entered(this: &mut Object, sel: Sel, drag_info: id) -> NS let dl: NSPoint = unsafe { msg_send![drag_info, draggingLocation] }; let frame: NSRect = unsafe { msg_send![this, frame] }; - let position = LogicalPosition::::from((dl.x, frame.size.height - dl.y)).into(); + let position = (dl.x as i32, (frame.size.height - dl.y) as i32); if !listener(FileDropEvent::Hovered { paths, position }) { // Reject the Wry file drop (invoke the OS default behaviour) @@ -126,7 +126,7 @@ extern "C" fn perform_drag_operation(this: &mut Object, sel: Sel, drag_info: id) let dl: NSPoint = unsafe { msg_send![drag_info, draggingLocation] }; let frame: NSRect = unsafe { msg_send![this, frame] }; - let position = LogicalPosition::::from((dl.x, frame.size.height - dl.y)).into(); + let position = (dl.x as i32, (frame.size.height - dl.y) as i32); if !listener(FileDropEvent::Dropped { paths, position }) { // Reject the Wry file drop (invoke the OS default behaviour) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 58c6b9990..71938c431 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -11,12 +11,7 @@ mod proxy; #[cfg(target_os = "macos")] mod synthetic_mouse_events; -#[cfg(feature = "rwh_04")] -use rwh_04::RawWindowHandle; -#[cfg(feature = "rwh_05")] -use rwh_05::RawWindowHandle; -#[cfg(feature = "rwh_06")] -use rwh_06::RawWindowHandle; +use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; use url::Url; #[cfg(target_os = "macos")] @@ -35,7 +30,7 @@ use std::{ sync::{Arc, Mutex}, }; -use core_graphics::geometry::CGRect; +use core_graphics::geometry::{CGPoint, CGRect, CGSize}; use objc::{ declare::ClassDecl, runtime::{Class, Object, Sel, BOOL}, @@ -54,17 +49,15 @@ use crate::webview::{ }; use crate::{ - webview::{ - wkwebview::{ - download::{ - add_download_methods, download_did_fail, download_did_finish, download_policy, - set_download_delegate, - }, - navigation::{add_navigation_mathods, drop_navigation_methods, set_navigation_methods}, + wkwebview::{ + download::{ + add_download_methods, download_did_fail, download_did_finish, download_policy, + set_download_delegate, }, - FileDropEvent, PageLoadEvent, RequestAsyncResponder, WebContext, WebViewAttributes, RGBA, + navigation::{add_navigation_mathods, drop_navigation_methods, set_navigation_methods}, }, - Error, Result, + Error, FileDropEvent, PageLoadEvent, RequestAsyncResponder, Result, WebContext, + WebViewAttributes, RGBA, }; use http::{ @@ -99,17 +92,36 @@ pub(crate) struct InnerWebView { impl InnerWebView { pub fn new( - window: RawWindowHandle, + window: &impl HasWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { - let window = match window { + Self::create(window, attributes, _pl_attrs, _web_context, false) + } + + pub fn new_as_child( + window: &impl HasWindowHandle, + attributes: WebViewAttributes, + _pl_attrs: super::PlatformSpecificWebViewAttributes, + _web_context: Option<&mut WebContext>, + ) -> Result { + Self::create(window, attributes, _pl_attrs, _web_context, false) + } + + fn create( + window: &impl HasWindowHandle, + attributes: WebViewAttributes, + _pl_attrs: super::PlatformSpecificWebViewAttributes, + _web_context: Option<&mut WebContext>, + is_child: bool, + ) -> Result { + let window = match window.window_handle()?.as_raw() { #[cfg(target_os = "macos")] RawWindowHandle::AppKit(w) => w, #[cfg(target_os = "ios")] RawWindowHandle::UiKit(w) => w, - _ => return Err(Error::WindowHandleError), + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; // Function for ipc handler @@ -374,7 +386,6 @@ impl InnerWebView { #[cfg(target_os = "macos")] { - use core_graphics::geometry::{CGPoint, CGSize}; let (x, y) = attributes.position.unwrap_or((0, 0)); let (w, h) = attributes.size.unwrap_or((0, 0)); let frame: CGRect = CGRect::new( @@ -388,7 +399,7 @@ impl InnerWebView { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view as id; + let ui_view = window.ui_view.as_ptr() as id; let frame: CGRect = msg_send![ui_view, frame]; // set all autoresizingmasks let () = msg_send![webview, setAutoresizingMask: 31]; @@ -767,7 +778,8 @@ impl InnerWebView { // ns window is required for the print operation #[cfg(target_os = "macos")] let ns_window = { - let ns_window = window.ns_window as id; + let ns_view = window.ns_view.as_ptr() as id; + let ns_window: id = msg_send![ns_view, window]; let can_set_titlebar_style: BOOL = msg_send![ ns_window, @@ -830,8 +842,8 @@ r#"Object.defineProperty(window, 'ipc', { // Inject the web view into the window as main content #[cfg(target_os = "macos")] { - if attributes.position.is_some() || attributes.size.is_some() { - let ns_view = window.ns_view as id; + if is_child { + let ns_view = window.ns_view.as_ptr() as id; let _: () = msg_send![ns_view, addSubview: webview]; } else { let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { @@ -860,7 +872,8 @@ r#"Object.defineProperty(window, 'ipc', { let _: () = msg_send![parent_view, addSubview: webview]; // inject the webview into the window - let ns_window = window.ns_window as id; + let ns_view = window.ns_view.as_ptr() as id; + let ns_window: id = msg_send![ns_view, window]; // Tell the webview receive keyboard events in the window. // See https://github.com/tauri-apps/wry/issues/739 let _: () = msg_send![ns_window, setContentView: parent_view]; @@ -875,7 +888,7 @@ r#"Object.defineProperty(window, 'ipc', { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view as id; + let ui_view = window.ui_view.as_ptr() as id; let _: () = msg_send![ui_view, addSubview: webview]; } @@ -1049,6 +1062,22 @@ r#"Object.defineProperty(window, 'ipc', { pub fn set_background_color(&self, _background_color: RGBA) -> Result<()> { Ok(()) } + + pub fn set_position(&self, position: (i32, i32)) { + unsafe { + let mut frame: CGRect = msg_send![self.webview, frame]; + frame.origin = CGPoint::new(position.0 as f64, position.1 as f64); + let () = msg_send![self.webview, setFrame: frame]; + } + } + + pub fn set_size(&self, size: (u32, u32)) { + unsafe { + let mut frame: CGRect = msg_send![self.webview, frame]; + frame.size = CGSize::new(size.0 as f64, size.1 as f64); + let () = msg_send![self.webview, setFrame: frame]; + } + } } pub fn url_from_webview(webview: id) -> String { diff --git a/src/wkwebview/navigation.rs b/src/wkwebview/navigation.rs index b66bce591..10579f331 100644 --- a/src/wkwebview/navigation.rs +++ b/src/wkwebview/navigation.rs @@ -11,7 +11,7 @@ use objc::{ }; use super::{url_from_webview, InnerWebView, NSString}; -use crate::webview::PageLoadEvent; +use crate::PageLoadEvent; extern "C" fn did_commit_navigation(this: &Object, _: Sel, webview: id, _navigation: id) { unsafe { From e6b91774b6e3bba94c1248cf1e13924dfa3d84b5 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Tue, 24 Oct 2023 13:28:48 +0900 Subject: [PATCH 12/80] Fix Android --- src/android/mod.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/android/mod.rs b/src/android/mod.rs index fe6ccf7f7..bfa8dd600 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -13,12 +13,7 @@ use http::{ }; use kuchiki::NodeRef; use once_cell::sync::OnceCell; -#[cfg(feature = "rwh_04")] -use rwh_04::RawWindowHandle; -#[cfg(feature = "rwh_05")] -use rwh_05::RawWindowHandle; -#[cfg(feature = "rwh_06")] -use rwh_06::RawWindowHandle; +use raw_window_handle::HasWindowHandle; use sha2::{Digest, Sha256}; use std::{borrow::Cow, rc::Rc, sync::mpsc::channel}; use tao::platform::android::ndk_glue::{ @@ -109,14 +104,11 @@ pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef .unwrap(); } -pub(crate) struct InnerWebView { - #[allow(unused)] - pub window: RawWindowHandle, -} +pub(crate) struct InnerWebView; impl InnerWebView { pub fn new( - window: RawWindowHandle, + _window: & impl HasWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -269,7 +261,7 @@ impl InnerWebView { ON_LOAD_HANDLER.get_or_init(move || UnsafeOnPageLoadHandler::new(h)); } - Ok(Self { window }) + Ok(Self) } pub fn print(&self) {} From 729f14e37b7af236412c69a444ad184dcd03963a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Tue, 24 Oct 2023 15:43:42 +0300 Subject: [PATCH 13/80] fix is_child on WkWebView --- src/wkwebview/mod.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 71938c431..db41d08b5 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -77,6 +77,7 @@ pub(crate) struct InnerWebView { #[cfg(target_os = "macos")] pub ns_window: id, pub manager: id, + is_child: bool, pending_scripts: Arc>>>, // Note that if following functions signatures are changed in the future, // all functions pointer declarations in objc callbacks below all need to get updated. @@ -106,7 +107,7 @@ impl InnerWebView { _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { - Self::create(window, attributes, _pl_attrs, _web_context, false) + Self::create(window, attributes, _pl_attrs, _web_context, true) } fn create( @@ -807,6 +808,7 @@ impl InnerWebView { page_load_handler, download_delegate, protocol_ptrs, + is_child, }; // Initialize scripts @@ -1064,18 +1066,22 @@ r#"Object.defineProperty(window, 'ipc', { } pub fn set_position(&self, position: (i32, i32)) { - unsafe { - let mut frame: CGRect = msg_send![self.webview, frame]; - frame.origin = CGPoint::new(position.0 as f64, position.1 as f64); - let () = msg_send![self.webview, setFrame: frame]; + if self.is_child { + unsafe { + let mut frame: CGRect = msg_send![self.webview, frame]; + frame.origin = CGPoint::new(position.0 as f64, position.1 as f64); + let () = msg_send![self.webview, setFrame: frame]; + } } } pub fn set_size(&self, size: (u32, u32)) { - unsafe { - let mut frame: CGRect = msg_send![self.webview, frame]; - frame.size = CGSize::new(size.0 as f64, size.1 as f64); - let () = msg_send![self.webview, setFrame: frame]; + if self.is_child { + unsafe { + let mut frame: CGRect = msg_send![self.webview, frame]; + frame.size = CGSize::new(size.0 as f64, size.1 as f64); + let () = msg_send![self.webview, setFrame: frame]; + } } } } From 9049943161737bf57b5a2643f5f9829877ed8dff Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 04:28:14 +0300 Subject: [PATCH 14/80] x11 impl --- Cargo.toml | 48 +++--- src/android/mod.rs | 4 +- src/error.rs | 24 ++- src/lib.rs | 31 +--- src/web_context.rs | 2 +- src/webkitgtk/file_drop.rs | 49 ++---- src/webkitgtk/mod.rs | 205 +++++++++++++++++------- src/webkitgtk/synthetic_mouse_events.rs | 4 +- src/webkitgtk/undecorated_resizing.rs | 43 ++++- src/webkitgtk/web_context.rs | 21 ++- src/webview2/mod.rs | 122 +++++++------- src/wkwebview/mod.rs | 53 +++--- 12 files changed, 353 insertions(+), 253 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index daa6e65c5..96bf908d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,63 +1,65 @@ -workspace = { } +workspace = {} [package] name = "wry" version = "0.33.1" -authors = [ "Tauri Programme within The Commons Conservancy" ] +authors = ["Tauri Programme within The Commons Conservancy"] edition = "2021" license = "Apache-2.0 OR MIT" description = "Cross-platform WebView rendering library" readme = "README.md" repository = "https://github.com/tauri-apps/wry" documentation = "https://docs.rs/wry" -categories = [ "gui" ] +categories = ["gui"] [package.metadata.docs.rs] no-default-features = true -features = [ "dox", "file-drop", "protocol" ] +features = ["dox", "file-drop", "protocol"] targets = [ "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", - "x86_64-apple-darwin" + "x86_64-apple-darwin", ] [features] -default = [ "file-drop", "objc-exception", "protocol" ] -objc-exception = [ "objc/exception" ] -file-drop = [ ] -protocol = [ ] -devtools = [ ] -transparent = [ ] -fullscreen = [ ] -linux-body = [ "webkit2gtk/v2_40" ] -mac-proxy = [ ] +default = ["file-drop", "objc-exception", "protocol"] +objc-exception = ["objc/exception"] +file-drop = [] +protocol = [] +devtools = [] +transparent = [] +fullscreen = [] +linux-body = ["webkit2gtk/v2_40"] +mac-proxy = [] [dependencies] libc = "0.2" log = "0.4" once_cell = "1" -serde = { version = "1.0", features = [ "derive" ] } +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" url = "2.4" http = "0.2" -raw-window-handle= { version = "0.6", features = ["std"] } +raw-window-handle = { version = "0.6", features = ["std"] } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -javascriptcore-rs = { version = "=1.1.1", features = [ "v2_28" ] } -webkit2gtk = { version = "=2.0", features = [ "v2_38" ] } +javascriptcore-rs = { version = "=1.1.1", features = ["v2_28"] } +webkit2gtk = { version = "=2.0", features = ["v2_38"] } webkit2gtk-sys = "=2.0" gtk = "0.18" soup3 = "0.5" +x11-dl = "2.9" +gdkx11 = "0.18" [target."cfg(target_os = \"windows\")".dependencies] webview2-com = "0.27" windows-implement = "0.51" dunce = "1" - [target."cfg(target_os = \"windows\")".dependencies.windows] - version = "0.51" - features = [ +[target."cfg(target_os = \"windows\")".dependencies.windows] +version = "0.51" +features = [ "implement", "Win32_Foundation", "Win32_Graphics_Gdi", @@ -72,7 +74,7 @@ dunce = "1" "Win32_Globalization", "Win32_UI_HiDpi", "Win32_UI_Input", - "Win32_UI_Input_KeyboardAndMouse" + "Win32_UI_Input_KeyboardAndMouse", ] [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] @@ -92,4 +94,4 @@ base64 = "0.21" [dev-dependencies] http-range = "0.1.5" tao = { git = "https://github.com/tauri-apps/tao" } -winit = "0.29" \ No newline at end of file +winit = { path = "../winit" } diff --git a/src/android/mod.rs b/src/android/mod.rs index bfa8dd600..fd372d0de 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -107,8 +107,8 @@ pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef pub(crate) struct InnerWebView; impl InnerWebView { - pub fn new( - _window: & impl HasWindowHandle, + pub fn new( + _window: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, diff --git a/src/error.rs b/src/error.rs index d6aa5b183..181254ca6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,7 +13,7 @@ pub enum Error { target_os = "openbsd" ))] #[error(transparent)] - GlibError(#[from] glib::Error), + GlibError(#[from] gtk::glib::Error), #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -22,7 +22,7 @@ pub enum Error { target_os = "openbsd" ))] #[error(transparent)] - GlibBoolError(#[from] glib::BoolError), + GlibBoolError(#[from] gtk::glib::BoolError), #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -32,6 +32,24 @@ pub enum Error { ))] #[error("Fail to fetch security manager")] MissingManager, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + #[error("Couldn't find X11 Display")] + X11DisplayNotFound, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + #[error(transparent)] + XlibError(#[from] x11_dl::error::OpenError), #[error("Failed to initialize the script")] InitScriptError, #[error("Bad RPC request: {0} ((1))")] @@ -66,4 +84,6 @@ pub enum Error { ProxyEndpointCreationFailed, #[error(transparent)] WindowHandleError(#[from] raw_window_handle::HandleError), + #[error(transparent)] + Utf8Error(#[from] std::str::Utf8Error), } diff --git a/src/lib.rs b/src/lib.rs index 9112f6e13..5c460c21c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -433,7 +433,7 @@ pub struct WebViewBuilder<'a> { impl<'a> WebViewBuilder<'a> { /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. - pub fn new(window: &'a impl HasWindowHandle) -> Self { + pub fn new(window: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), window, @@ -445,7 +445,7 @@ impl<'a> WebViewBuilder<'a> { } /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc - pub fn new_as_child(parent: &'a impl HasWindowHandle) -> Self { + pub fn new_as_child(parent: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), window: parent, @@ -1026,25 +1026,6 @@ pub struct WebView { webview: InnerWebView, } -// Signal the Window to drop on Linux and Windows. On mac, we need to handle several unsafe code -// blocks and raw pointer properly. -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -impl Drop for WebView { - fn drop(&mut self) { - unsafe { - use crate::application::platform::unix::WindowExtUnix; - use gtk::prelude::WidgetExtManual; - self.window().gtk_window().destroy(); - } - } -} - impl WebView { /// Create a [`WebView`] from provided [`RawWindowHandle`]. Note that calling this directly loses /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To @@ -1056,11 +1037,11 @@ impl WebView { /// called in the same thread with the [`EventLoop`] you create. /// /// [`EventLoop`]: crate::application::event_loop::EventLoop - pub fn new(window: &impl HasWindowHandle) -> Result { + pub fn new(window: &W) -> Result { WebViewBuilder::new(window).build() } - pub fn new_as_child(window: &impl HasWindowHandle) -> Result { + pub fn new_as_child(window: &W) -> Result { WebViewBuilder::new_as_child(window).build() } @@ -1233,12 +1214,12 @@ impl WebviewExtWindows for WebView { #[cfg(target_os = "linux")] pub trait WebviewExtUnix { /// Returns Webkit2gtk Webview handle - fn webview(&self) -> Rc; + fn webview(&self) -> webkit2gtk::WebView; } #[cfg(target_os = "linux")] impl WebviewExtUnix for WebView { - fn webview(&self) -> Rc { + fn webview(&self) -> webkit2gtk::WebView { self.webview.webview.clone() } } diff --git a/src/web_context.rs b/src/web_context.rs index 216b7b5a2..d66b7f82b 100644 --- a/src/web_context.rs +++ b/src/web_context.rs @@ -9,7 +9,7 @@ target_os = "netbsd", target_os = "openbsd" ))] -use crate::webview::webkitgtk::WebContextImpl; +use crate::webkitgtk::WebContextImpl; use std::path::{Path, PathBuf}; diff --git a/src/webkitgtk/file_drop.rs b/src/webkitgtk/file_drop.rs index 0d7547b70..f05c8c38c 100644 --- a/src/webkitgtk/file_drop.rs +++ b/src/webkitgtk/file_drop.rs @@ -4,23 +4,15 @@ use std::{cell::Cell, path::PathBuf, rc::Rc}; -use gtk::prelude::*; +use gtk::{glib, prelude::*}; use webkit2gtk::WebView; -use crate::{ - application::{dpi::LogicalPosition, window::Window}, - webview::FileDropEvent, -}; +use crate::FileDropEvent; -pub(crate) fn connect_drag_event( - webview: Rc, - window: Rc, - handler: Box bool>, -) { +pub(crate) fn connect_drag_event(webview: WebView, handler: Box bool>) { let listener = Rc::new((handler, Cell::new(None))); let listener_ref = listener.clone(); - let w = window.clone(); webview.connect_drag_data_received(move |_, _, x, y, data, info, _| { if info == 2 { let uris = data @@ -33,47 +25,33 @@ pub(crate) fn connect_drag_event( .collect::>(); listener_ref.1.set(Some(uris.clone())); - let scale_factor = w.scale_factor(); - let position = LogicalPosition::new(x, y).to_physical(scale_factor); - - listener_ref.0( - &w, - FileDropEvent::Hovered { - paths: uris, - position, - }, - ); + listener_ref.0(FileDropEvent::Hovered { + paths: uris, + position: (x, y), + }); } else { // drag_data_received is called twice, so we can ignore this signal } }); let listener_ref = listener.clone(); - let w = window.clone(); webview.connect_drag_drop(move |_, _, x, y, _| { let uris = listener_ref.1.take(); if let Some(uris) = uris { - let scale_factor = w.scale_factor(); - let position = LogicalPosition::new(x, y).to_physical(scale_factor); - - listener_ref.0( - &w, - FileDropEvent::Dropped { - paths: uris, - position, - }, - ) + listener_ref.0(FileDropEvent::Dropped { + paths: uris, + position: (x, y), + }) } else { false } }); let listener_ref = listener.clone(); - let w = window.clone(); webview.connect_drag_leave(move |_, _, time| { if time == 0 { // The user cancelled the drag n drop - listener_ref.0(&w, FileDropEvent::Cancelled); + listener_ref.0(FileDropEvent::Cancelled); } else { // The user dropped the file on the window, but this will be handled in connect_drag_drop instead } @@ -81,9 +59,8 @@ pub(crate) fn connect_drag_event( // Called when a drag "fails" - we'll just emit a Cancelled event. let listener_ref = listener.clone(); - let w = window; webview.connect_drag_failed(move |_, _, _| { - if listener_ref.0(&w, FileDropEvent::Cancelled) { + if listener_ref.0(FileDropEvent::Cancelled) { glib::Propagation::Stop } else { glib::Propagation::Proceed diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index c4e3d60cf..b4355e8bb 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -2,17 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use gdk::EventMask; -use gio::Cancellable; -use gtk::prelude::*; +use gdkx11::X11Window; +use gtk::gdk::{self, EventMask}; +use gtk::gio::Cancellable; +use gtk::{glib, prelude::*}; +use javascriptcore::ValueExt; +use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; #[cfg(any(debug_assertions, feature = "devtools"))] use std::sync::atomic::{AtomicBool, Ordering}; -use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, - rc::Rc, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use url::Url; use webkit2gtk::{ AutoplayPolicy, InputMethodContextExt, LoadEvent, NavigationPolicyDecision, @@ -25,14 +23,14 @@ use webkit2gtk_sys::{ webkit_get_major_version, webkit_get_micro_version, webkit_get_minor_version, webkit_policy_decision_ignore, webkit_policy_decision_use, }; +use x11_dl::xlib::*; use web_context::WebContextExt; pub use web_context::WebContextImpl; use crate::{ - application::{platform::unix::*, window::Window}, - webview::{proxy::ProxyConfig, web_context::WebContext, PageLoadEvent, WebViewAttributes, RGBA}, - Error, Result, + proxy::ProxyConfig, web_context::WebContext, Error, PageLoadEvent, Result, WebViewAttributes, + RGBA, }; mod file_drop; @@ -40,24 +38,120 @@ mod synthetic_mouse_events; mod undecorated_resizing; mod web_context; -use javascriptcore::ValueExt; - pub(crate) struct InnerWebView { - pub webview: Rc, + pub webview: WebView, #[cfg(any(debug_assertions, feature = "devtools"))] is_inspector_open: Arc, pending_scripts: Arc>>>, + + is_child: bool, + display: Option, + xlib: Option, + x11_window: Option, +} + +impl Drop for InnerWebView { + fn drop(&mut self) { + if let Some(xlib) = &self.xlib { + use gdkx11::X11Display; + + let gx11_display: &X11Display = self.display.as_ref().unwrap().downcast_ref().unwrap(); + let raw = glib::translate::ToGlibPtr::to_glib_none(&gx11_display).0; + let display = unsafe { gdkx11::ffi::gdk_x11_display_get_xdisplay(raw) }; + + if self.is_child { + unsafe { (xlib.XDestroyWindow)(display as _, self.x11_window.unwrap()) }; + } + + unsafe { (xlib.XCloseDisplay)(display as _) }; + } + } } impl InnerWebView { - pub fn new( - window: Rc, + pub fn new( + window: &W, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + Self::new_x11(window, attributes, pl_attrs, web_context, false) + } + + pub fn new_as_child( + parent: &W, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + Self::new_x11(parent, attributes, pl_attrs, web_context, true) + } + + fn new_x11( + window: &W, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + is_child: bool, + ) -> Result { + let xlib = Xlib::open()?; + + let window = match window.window_handle()?.as_raw() { + RawWindowHandle::Xlib(w) => w.window, + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + }; + + let gdk_display = gdk::Display::default().ok_or(Error::X11DisplayNotFound)?; + let gx11_display = gdk_display.downcast_ref().unwrap(); + let raw = glib::translate::ToGlibPtr::to_glib_none(&gx11_display).0; + let display = unsafe { gdkx11::ffi::gdk_x11_display_get_xdisplay(raw) }; + + let window = if is_child { + let child = unsafe { + (xlib.XCreateSimpleWindow)( + display as _, + window, + attributes.position.map(|p| p.0).unwrap_or(0), + attributes.position.map(|p| p.1).unwrap_or(0), + attributes.size.map(|s| s.0).unwrap_or(0), + attributes.size.map(|s| s.1).unwrap_or(0), + 0, + 0, + 0, + ) + }; + unsafe { (xlib.XMapRaised)(display as _, child) }; + child + } else { + window + }; + + let gdk_window: gdk::Window = X11Window::foreign_new_for_display(gx11_display, window).upcast(); + let gtk_window = gtk::Window::new(gtk::WindowType::Toplevel); + gtk_window.connect_realize(move |widget| widget.set_window(gdk_window.clone())); + gtk_window.set_has_window(true); + gtk_window.realize(); + + Self::new_gtk(>k_window, attributes, pl_attrs, web_context).map(|mut w| { + w.is_child = is_child; + w.xlib = Some(xlib); + w.display = Some(gdk_display); + w.x11_window = Some(window); + w + }) + } + + pub fn new_gtk( + container: &W, mut attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, - ) -> Result { - let window_rc = Rc::clone(&window); - let window = &window.gtk_window(); + ) -> Result + where + W: IsA, + W: IsA, + { + let window_id = container.as_ptr() as isize; // default_context allows us to create a scoped context on-demand let mut default_context; @@ -110,44 +204,25 @@ impl InnerWebView { web_context.register_automation(webview.clone()); // Message handler - let webview = Rc::new(webview); - 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 = { - let mut hasher = DefaultHasher::new(); - w.id().hash(&mut hasher); - hasher.finish().to_string() - }; // Connect before registering as recommended by the docs manager.connect_script_message_received(None, move |_m, msg| { if let Some(js) = msg.js_value() { if let Some(ipc_handler) = &ipc_handler { - ipc_handler(&w, js.to_string()); + ipc_handler(js.to_string()); } } }); // Register the handler we just connected - manager.register_script_message_handler(&window_hash); - - // Allow the webview to close it's own window - let close_window = window_rc.clone(); - webview.connect_close(move |_| { - close_window.gtk_window().close(); - }); + manager.register_script_message_handler(&window_id.to_string()); // document title changed handler if let Some(document_title_changed_handler) = attributes.document_title_changed_handler { - let w = window_rc.clone(); webview.connect_title_notify(move |webview| { - document_title_changed_handler( - &w, - webview.title().map(|t| t.to_string()).unwrap_or_default(), - ) + document_title_changed_handler(webview.title().map(|t| t.to_string()).unwrap_or_default()) }); } @@ -218,24 +293,19 @@ impl InnerWebView { ) } - // tao adds a default vertical box so we check for that first - if let Some(vbox) = window_rc.default_vbox() { - vbox.pack_start(&*webview, true, true, 0); - } else { - window.add(&*webview); - } + container.add(&webview); if attributes.focused { webview.grab_focus(); } - if let Some(context) = WebViewExt::context(&*webview) { + if let Some(context) = WebViewExt::context(&webview) { use webkit2gtk::WebContextExt; context.set_use_system_appearance_for_scrollbars(false); } // Enable webgl, webaudio, canvas features as default. - if let Some(settings) = WebViewExt::settings(&*webview) { + if let Some(settings) = WebViewExt::settings(&webview) { settings.set_enable_webgl(true); settings.set_enable_webaudio(true); settings @@ -275,17 +345,16 @@ impl InnerWebView { // File drop handling if let Some(file_drop_handler) = attributes.file_drop_handler { - file_drop::connect_drag_event(webview.clone(), window_rc, file_drop_handler); + file_drop::connect_drag_event(webview.clone(), file_drop_handler); } - if window.get_visible() { - window.show_all(); - } + webview.show_all(); + container.show_all(); #[cfg(any(debug_assertions, feature = "devtools"))] let is_inspector_open = { let is_inspector_open = Arc::new(AtomicBool::default()); - if let Some(inspector) = WebViewExt::inspector(&*webview) { + if let Some(inspector) = WebViewExt::inspector(&webview) { let is_inspector_open_ = is_inspector_open.clone(); inspector.connect_bring_to_front(move |_| { is_inspector_open_.store(true, Ordering::Relaxed); @@ -304,12 +373,16 @@ impl InnerWebView { #[cfg(any(debug_assertions, feature = "devtools"))] is_inspector_open, pending_scripts: Arc::new(Mutex::new(Some(Vec::new()))), + is_child: false, + xlib: None, + display: None, + x11_window: None, }; // Initialize message handler let mut init = String::with_capacity(115 + 20 + 22); init.push_str("Object.defineProperty(window, 'ipc', {value: Object.freeze({postMessage:function(x){window.webkit.messageHandlers[\""); - init.push_str(&window_hash); + init.push_str(&window_id.to_string()); init.push_str("\"].postMessage(x)}})})"); w.init(&init)?; @@ -330,7 +403,7 @@ impl InnerWebView { // Navigation if let Some(url) = attributes.url { - web_context.queue_load_uri(Rc::clone(&w.webview), url, attributes.headers); + web_context.queue_load_uri(w.webview.clone(), url, attributes.headers); web_context.flush_queue_loader(); } else if let Some(html) = attributes.html { w.webview.load_html(&html, None); @@ -420,7 +493,7 @@ impl InnerWebView { #[cfg(any(debug_assertions, feature = "devtools"))] pub fn open_devtools(&self) { - if let Some(inspector) = WebViewExt::inspector(&*self.webview) { + if let Some(inspector) = WebViewExt::inspector(&self.webview) { inspector.show(); // `bring-to-front` is not received in this case self.is_inspector_open.store(true, Ordering::Relaxed); @@ -429,7 +502,7 @@ impl InnerWebView { #[cfg(any(debug_assertions, feature = "devtools"))] pub fn close_devtools(&self) { - if let Some(inspector) = WebViewExt::inspector(&*self.webview) { + if let Some(inspector) = WebViewExt::inspector(&self.webview) { inspector.close(); } } @@ -440,7 +513,7 @@ impl InnerWebView { } pub fn zoom(&self, scale_factor: f64) { - WebViewExt::set_zoom_level(&*self.webview, scale_factor); + WebViewExt::set_zoom_level(&self.webview, scale_factor); } pub fn set_background_color(&self, background_color: RGBA) -> Result<()> { @@ -473,8 +546,8 @@ impl InnerWebView { } pub fn clear_all_browsing_data(&self) -> Result<()> { - if let Some(context) = WebViewExt::context(&*self.webview) { - use webkit2gtk::WebContextExt; + use webkit2gtk::WebContextExt; + if let Some(context) = WebViewExt::context(&self.webview) { if let Some(data_manger) = context.website_data_manager() { webkit2gtk::WebsiteDataManagerExtManual::clear( &data_manger, @@ -488,6 +561,14 @@ impl InnerWebView { Ok(()) } + + pub fn set_position(&self, _position: (i32, i32)) { + if self.is_child {} + } + + pub fn set_size(&self, _size: (u32, u32)) { + if self.is_child {} + } } pub fn platform_webview_version() -> Result { diff --git a/src/webkitgtk/synthetic_mouse_events.rs b/src/webkitgtk/synthetic_mouse_events.rs index 2efe58cb4..9a0e6d469 100644 --- a/src/webkitgtk/synthetic_mouse_events.rs +++ b/src/webkitgtk/synthetic_mouse_events.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc}; -use gdk::{EventButton, ModifierType}; -use gtk::prelude::*; +use gtk::gdk::{EventButton, ModifierType}; +use gtk::{gio, glib, prelude::*}; use webkit2gtk::{WebView, WebViewExt}; pub fn setup(webview: &WebView) { diff --git a/src/webkitgtk/undecorated_resizing.rs b/src/webkitgtk/undecorated_resizing.rs index 8e15a4930..aab4ddbac 100644 --- a/src/webkitgtk/undecorated_resizing.rs +++ b/src/webkitgtk/undecorated_resizing.rs @@ -1,6 +1,5 @@ -use crate::application::platform::unix::*; -use gdk::{Cursor, WindowEdge}; -use gtk::prelude::*; +use gtk::gdk::{self, Cursor, WindowEdge}; +use gtk::{glib, prelude::*}; use webkit2gtk::WebView; pub fn setup(webview: &WebView) { @@ -99,3 +98,41 @@ pub fn setup(webview: &WebView) { glib::Propagation::Proceed }); } + +pub fn hit_test(window: &gdk::Window, cx: f64, cy: f64) -> WindowEdge { + let (left, top) = window.position(); + let (w, h) = (window.width(), window.height()); + let (right, bottom) = (left + w, top + h); + let (cx, cy) = (cx as i32, cy as i32); + + const LEFT: i32 = 0b0001; + const RIGHT: i32 = 0b0010; + const TOP: i32 = 0b0100; + const BOTTOM: i32 = 0b1000; + const TOPLEFT: i32 = TOP | LEFT; + const TOPRIGHT: i32 = TOP | RIGHT; + const BOTTOMLEFT: i32 = BOTTOM | LEFT; + const BOTTOMRIGHT: i32 = BOTTOM | RIGHT; + + let inset = 5 * window.scale_factor(); + #[rustfmt::skip] + let result = + (LEFT * (if cx < (left + inset) { 1 } else { 0 })) + | (RIGHT * (if cx >= (right - inset) { 1 } else { 0 })) + | (TOP * (if cy < (top + inset) { 1 } else { 0 })) + | (BOTTOM * (if cy >= (bottom - inset) { 1 } else { 0 })); + + match result { + LEFT => WindowEdge::West, + TOP => WindowEdge::North, + RIGHT => WindowEdge::East, + BOTTOM => WindowEdge::South, + TOPLEFT => WindowEdge::NorthWest, + TOPRIGHT => WindowEdge::NorthEast, + BOTTOMLEFT => WindowEdge::SouthWest, + BOTTOMRIGHT => WindowEdge::SouthEast, + // we return `WindowEdge::__Unknown` to be ignored later. + // we must return 8 or bigger, otherwise it will be the same as one of the other 7 variants of `WindowEdge` enum. + _ => WindowEdge::__Unknown(8), + } +} diff --git a/src/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs index cd0b3c5e0..a215ff8bb 100644 --- a/src/webkitgtk/web_context.rs +++ b/src/webkitgtk/web_context.rs @@ -4,11 +4,8 @@ //! Unix platform extensions for [`WebContext`](super::WebContext). -use crate::{ - webview::{web_context::WebContextData, RequestAsyncResponder}, - Error, -}; -use glib::FileError; +use crate::{web_context::WebContextData, Error, RequestAsyncResponder}; +use gtk::{gio, glib}; use http::{header::CONTENT_TYPE, Request, Response as HttpResponse}; use std::{ borrow::Cow, @@ -136,7 +133,7 @@ pub trait WebContextExt { /// Add a [`WebView`] to the queue waiting to be opened. /// /// See the `WebviewUriLoader` for more information. - fn queue_load_uri(&self, webview: Rc, url: Url, headers: Option); + fn queue_load_uri(&self, webview: WebView, url: Url, headers: Option); /// Flush all queued [`WebView`]s waiting to load a uri. /// @@ -189,7 +186,7 @@ impl WebContextExt for super::WebContext { } } - fn queue_load_uri(&self, webview: Rc, url: Url, headers: Option) { + fn queue_load_uri(&self, webview: WebView, url: Url, headers: Option) { self.os.webview_uri_loader.push(webview, url, headers) } @@ -364,7 +361,7 @@ where Err(_) => { request.finish_error(&mut glib::Error::new( // TODO: use UriError when we can use 2_66 webkit2gtk feature flag - FileError::Exist, + glib::FileError::Exist, "Could not get uri.", )); return; @@ -401,7 +398,7 @@ where handler(http_request, RequestAsyncResponder { responder }); } else { request.finish_error(&mut glib::Error::new( - FileError::Exist, + glib::FileError::Exist, "Could not get uri.", )); } @@ -441,7 +438,7 @@ where #[derive(Debug, Default)] struct WebviewUriLoader { lock: AtomicBool, - queue: Mutex, Url, Option)>>, + queue: Mutex)>>, } impl WebviewUriLoader { @@ -456,13 +453,13 @@ impl WebviewUriLoader { } /// Add a [`WebView`] to the queue. - fn push(&self, webview: Rc, url: Url, headers: Option) { + fn push(&self, webview: WebView, url: Url, headers: Option) { let mut queue = self.queue.lock().expect("poisoned load queue"); queue.push_back((webview, url, headers)) } /// Remove a [`WebView`] from the queue and return it. - fn pop(&self) -> Option<(Rc, Url, Option)> { + fn pop(&self) -> Option<(WebView, Url, Option)> { let mut queue = self.queue.lock().expect("poisoned load queue"); queue.pop_front() } diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 58e47b86f..08395c1b9 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -16,7 +16,7 @@ use std::{ use http::{Request, Response as HttpResponse, StatusCode}; use once_cell::sync::Lazy; -use raw_window_handle::HasWindowHandle; +use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; use url::Url; use webview2_com::{Microsoft::Web::WebView2::Win32::*, *}; use windows::{ @@ -69,77 +69,76 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - pub fn new( - window: &impl HasWindowHandle, + pub fn new( + window: &impl W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { - match window.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(handle) => { - Self::new_hwnd(HWND(handle.hwnd.get()), attributes, pl_attrs, web_context) - } - _ => unreachable!(), - } + let window = match window.window_handle()?.as_raw() { + raw_window_handle::RawWindowHandle::Win32(window) => window.hwnd, + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + }; + Self::new_hwnd(HWND(handle.hwnd.get()), attributes, pl_attrs, web_context) } - pub fn new_as_child( - window: &impl HasWindowHandle, + pub fn new_as_child( + parent: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { - match window.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(parent) => { - let class_name = encode_wide("WRY_WEBVIEW"); - - unsafe extern "system" fn default_window_proc( - hwnd: HWND, - msg: u32, - wparam: WPARAM, - lparam: LPARAM, - ) -> LRESULT { - DefWindowProcW(hwnd, msg, wparam, lparam) - } + let parent = match parent.window_handle()?.as_raw() { + raw_window_handle::RawWindowHandle::Win32(parent) => parent.hwnd, + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + }; - let class = WNDCLASSEXW { - cbSize: std::mem::size_of::() as u32, - style: CS_HREDRAW | CS_VREDRAW, - lpfnWndProc: Some(default_window_proc), - cbClsExtra: 0, - cbWndExtra: 0, - hInstance: unsafe { HINSTANCE(GetModuleHandleW(PCWSTR::null()).unwrap_or_default().0) }, - hIcon: HICON::default(), - hCursor: HCURSOR::default(), // must be null in order for cursor state to work properly - hbrBackground: HBRUSH::default(), - lpszMenuName: PCWSTR::null(), - lpszClassName: PCWSTR::from_raw(class_name.as_ptr()), - hIconSm: HICON::default(), - }; + let class_name = encode_wide("WRY_WEBVIEW"); - unsafe { RegisterClassExW(&class) }; + unsafe extern "system" fn default_window_proc( + hwnd: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, + ) -> LRESULT { + DefWindowProcW(hwnd, msg, wparam, lparam) + } - let child = unsafe { - CreateWindowExW( - WINDOW_EX_STYLE::default(), - PCWSTR::from_raw(class_name.as_ptr()), - PCWSTR::null(), - WS_CHILD | WS_VISIBLE, - attributes.position.map(|a| a.0).unwrap_or(CW_USEDEFAULT), - attributes.position.map(|a| a.1).unwrap_or(CW_USEDEFAULT), - attributes.size.map(|a| a.0 as i32).unwrap_or(CW_USEDEFAULT), - attributes.size.map(|a| a.1 as i32).unwrap_or(CW_USEDEFAULT), - HWND(parent.hwnd.get()), - HMENU::default(), - GetModuleHandleW(PCWSTR::null()).unwrap_or_default(), - None, - ) - }; + let class = WNDCLASSEXW { + cbSize: std::mem::size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(default_window_proc), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: unsafe { HINSTANCE(GetModuleHandleW(PCWSTR::null()).unwrap_or_default().0) }, + hIcon: HICON::default(), + hCursor: HCURSOR::default(), // must be null in order for cursor state to work properly + hbrBackground: HBRUSH::default(), + lpszMenuName: PCWSTR::null(), + lpszClassName: PCWSTR::from_raw(class_name.as_ptr()), + hIconSm: HICON::default(), + }; - Self::new_as_child_hwnd(child, attributes, pl_attrs, web_context) - } - _ => unreachable!(), - } + unsafe { RegisterClassExW(&class) }; + + let child = unsafe { + CreateWindowExW( + WINDOW_EX_STYLE::default(), + PCWSTR::from_raw(class_name.as_ptr()), + PCWSTR::null(), + WS_CHILD | WS_VISIBLE, + attributes.position.map(|a| a.0).unwrap_or(CW_USEDEFAULT), + attributes.position.map(|a| a.1).unwrap_or(CW_USEDEFAULT), + attributes.size.map(|a| a.0 as i32).unwrap_or(CW_USEDEFAULT), + attributes.size.map(|a| a.1 as i32).unwrap_or(CW_USEDEFAULT), + HWND(parent.hwnd.get()), + HMENU::default(), + GetModuleHandleW(PCWSTR::null()).unwrap_or_default(), + None, + ) + }; + + Self::new_as_child_hwnd(child, attributes, pl_attrs, web_context) } pub fn new_as_child_hwnd( @@ -148,9 +147,10 @@ impl InnerWebView { pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { - let mut webview = Self::new_hwnd(hwnd, attributes, pl_attrs, web_context)?; - webview.is_child = true; - Ok(webview) + Self::new_hwnd(hwnd, attributes, pl_attrs, web_context).map(|mut w| { + w.is_child = true; + w + }) } pub fn new_hwnd( diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index db41d08b5..fe22691a2 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -92,39 +92,49 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - pub fn new( - window: &impl HasWindowHandle, + pub fn new( + window: &W, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { - Self::create(window, attributes, _pl_attrs, _web_context, false) + let ns_view = match window.window_handle()?.as_raw() { + #[cfg(target_os = "macos")] + RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), + #[cfg(target_os = "ios")] + RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + }; + + Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context) } - pub fn new_as_child( - window: &impl HasWindowHandle, + pub fn new_as_child( + window: &W, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { - Self::create(window, attributes, _pl_attrs, _web_context, true) + let ns_view = match window.window_handle()?.as_raw() { + #[cfg(target_os = "macos")] + RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), + #[cfg(target_os = "ios")] + RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), + _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + }; + + let mut webview = Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context)?; + webview.is_child = true; + Ok(webview) } - fn create( - window: &impl HasWindowHandle, + pub fn new_ns_view( + ns_view: id, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, is_child: bool, ) -> Result { - let window = match window.window_handle()?.as_raw() { - #[cfg(target_os = "macos")] - RawWindowHandle::AppKit(w) => w, - #[cfg(target_os = "ios")] - RawWindowHandle::UiKit(w) => w, - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), - }; - // Function for ipc handler extern "C" fn did_receive(this: &Object, _: Sel, _: id, msg: id) { // Safety: objc runtime calls are unsafe @@ -400,8 +410,7 @@ impl InnerWebView { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view.as_ptr() as id; - let frame: CGRect = msg_send![ui_view, frame]; + let frame: CGRect = msg_send![ns_view, frame]; // set all autoresizingmasks let () = msg_send![webview, setAutoresizingMask: 31]; let _: () = msg_send![webview, initWithFrame:frame configuration:config]; @@ -779,7 +788,6 @@ impl InnerWebView { // ns window is required for the print operation #[cfg(target_os = "macos")] let ns_window = { - let ns_view = window.ns_view.as_ptr() as id; let ns_window: id = msg_send![ns_view, window]; let can_set_titlebar_style: BOOL = msg_send![ @@ -808,7 +816,7 @@ impl InnerWebView { page_load_handler, download_delegate, protocol_ptrs, - is_child, + is_child: false, }; // Initialize scripts @@ -845,7 +853,6 @@ r#"Object.defineProperty(window, 'ipc', { #[cfg(target_os = "macos")] { if is_child { - let ns_view = window.ns_view.as_ptr() as id; let _: () = msg_send![ns_view, addSubview: webview]; } else { let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { @@ -874,7 +881,6 @@ r#"Object.defineProperty(window, 'ipc', { let _: () = msg_send![parent_view, addSubview: webview]; // inject the webview into the window - let ns_view = window.ns_view.as_ptr() as id; let ns_window: id = msg_send![ns_view, window]; // Tell the webview receive keyboard events in the window. // See https://github.com/tauri-apps/wry/issues/739 @@ -890,8 +896,7 @@ r#"Object.defineProperty(window, 'ipc', { #[cfg(target_os = "ios")] { - let ui_view = window.ui_view.as_ptr() as id; - let _: () = msg_send![ui_view, addSubview: webview]; + let _: () = msg_send![ns_view, addSubview: webview]; } Ok(w) From 36979c91d0b45cb47dff93315ddab67fa1cc220b Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 04:58:51 +0300 Subject: [PATCH 15/80] expose `new_gtk` --- examples/hello_world.rs | 23 ++++++++++- src/lib.rs | 92 ++++++++++++++++++++++++++++++++++------- src/webkitgtk/mod.rs | 10 ++++- 3 files changed, 106 insertions(+), 19 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index ac3767dfb..28b5b247a 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -15,7 +15,28 @@ fn main() -> wry::Result<()> { .with_title("Hello World") .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(&window) + + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + + let _webview = builder .with_url("https://tauri.app")? // .with_incognito(true) .build()?; diff --git a/src/lib.rs b/src/lib.rs index 5c460c21c..97206a15d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -426,9 +426,17 @@ pub enum PageLoadEvent { pub struct WebViewBuilder<'a> { pub attrs: WebViewAttributes, as_child: bool, - window: &'a dyn HasWindowHandle, + window: Option<&'a dyn HasWindowHandle>, platform_specific: PlatformSpecificWebViewAttributes, web_context: Option<&'a mut WebContext>, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + gtk_widget: Option<&'a gtk::Container>, } impl<'a> WebViewBuilder<'a> { @@ -436,11 +444,19 @@ impl<'a> WebViewBuilder<'a> { pub fn new(window: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), - window, + window: Some(window), as_child: false, #[allow(clippy::default_constructed_unit_structs)] platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + gtk_widget: None, } } @@ -448,11 +464,44 @@ impl<'a> WebViewBuilder<'a> { pub fn new_as_child(parent: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), - window: parent, + window: Some(parent), as_child: true, #[allow(clippy::default_constructed_unit_structs)] platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + gtk_widget: None, + } + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + pub fn new_gtk(widget: &'a W) -> Self + where + W: gtk::prelude::IsA, + W: gtk::prelude::IsA, + { + use gdkx11::glib::Cast; + + Self { + attrs: WebViewAttributes::default(), + window: None, + as_child: false, + #[allow(clippy::default_constructed_unit_structs)] + platform_specific: PlatformSpecificWebViewAttributes::default(), + web_context: None, + gtk_widget: Some(widget.dynamic_cast_ref().unwrap()), } } @@ -835,20 +884,16 @@ impl<'a> WebViewBuilder<'a> { /// /// [`EventLoop`]: crate::application::event_loop::EventLoop pub fn build(self) -> Result { - let webview = if self.as_child { - InnerWebView::new_as_child( - &self.window, - self.attrs, - self.platform_specific, - self.web_context, - )? + let webview = if let Some(window) = &self.window { + if self.as_child { + InnerWebView::new_as_child(window, self.attrs, self.platform_specific, self.web_context)? + } else { + InnerWebView::new(window, self.attrs, self.platform_specific, self.web_context)? + } + } else if let Some(widget) = self.gtk_widget { + InnerWebView::new_gtk(widget, self.attrs, self.platform_specific, self.web_context)? } else { - InnerWebView::new( - &self.window, - self.attrs, - self.platform_specific, - self.web_context, - )? + unreachable!() }; Ok(WebView { webview }) } @@ -1045,6 +1090,21 @@ impl WebView { WebViewBuilder::new_as_child(window).build() } + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + pub fn new_gtk(widget: &W) -> Result + where + W: gtk::prelude::IsA, + W: gtk::prelude::IsA, + { + WebViewBuilder::new_gtk(widget).build() + } + /// Get the current url of the webview pub fn url(&self) -> Url { self.webview.url() diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index b4355e8bb..eede795d8 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -293,7 +293,14 @@ impl InnerWebView { ) } - container.add(&webview); + if container.type_().name() == "GtkBox" { + container + .dynamic_cast_ref::() + .unwrap() + .pack_start(&webview, true, true, 0); + } else { + container.add(&webview); + } if attributes.focused { webview.grab_focus(); @@ -349,7 +356,6 @@ impl InnerWebView { } webview.show_all(); - container.show_all(); #[cfg(any(debug_assertions, feature = "devtools"))] let is_inspector_open = { From 7ba09486cf6698421564dd1750d66963f5e18b7e Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:00:12 +0300 Subject: [PATCH 16/80] fix winit-gtk version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 96bf908d7..3c1dfbef1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,4 +94,4 @@ base64 = "0.21" [dev-dependencies] http-range = "0.1.5" tao = { git = "https://github.com/tauri-apps/tao" } -winit = { path = "../winit" } +winit = "0.29" From 101d9c843ea6c724d23473154aef8c2cf88b7b4b Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:05:30 +0300 Subject: [PATCH 17/80] remove undecorated resizing handling --- src/webkitgtk/mod.rs | 2 - src/webkitgtk/synthetic_mouse_events.rs | 2 +- src/webkitgtk/undecorated_resizing.rs | 138 ------------------------ src/webkitgtk/web_context.rs | 2 +- 4 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 src/webkitgtk/undecorated_resizing.rs diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 9f18d6c05..b64066004 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -35,7 +35,6 @@ use crate::{ mod file_drop; mod synthetic_mouse_events; -mod undecorated_resizing; mod web_context; pub(crate) struct InnerWebView { @@ -251,7 +250,6 @@ impl InnerWebView { ); synthetic_mouse_events::setup(&webview); - undecorated_resizing::setup(&webview); if attributes.navigation_handler.is_some() || attributes.new_window_req_handler.is_some() { webview.connect_decide_policy(move |_webview, policy_decision, policy_type| { diff --git a/src/webkitgtk/synthetic_mouse_events.rs b/src/webkitgtk/synthetic_mouse_events.rs index 3aa4d3d73..b1446a9bf 100644 --- a/src/webkitgtk/synthetic_mouse_events.rs +++ b/src/webkitgtk/synthetic_mouse_events.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, rc::Rc}; use gtk::gdk::{EventButton, ModifierType}; -use gtk::{gio, glib, prelude::*}; +use gtk::prelude::*; use webkit2gtk::{WebView, WebViewExt}; pub fn setup(webview: &WebView) { diff --git a/src/webkitgtk/undecorated_resizing.rs b/src/webkitgtk/undecorated_resizing.rs deleted file mode 100644 index 249d2f8db..000000000 --- a/src/webkitgtk/undecorated_resizing.rs +++ /dev/null @@ -1,138 +0,0 @@ -use gtk::gdk::{self, Cursor, WindowEdge}; -use gtk::{glib, prelude::*}; -use webkit2gtk::WebView; - -pub fn setup(webview: &WebView) { - webview.connect_motion_notify_event(|webview, event| { - // This one should be GtkWindow - if let Some(widget) = webview.parent() { - // This one should be GtkWindow - if let Some(window) = widget.parent() { - // Safe to unwrap unless this is not from tao - let window: gtk::Window = window.downcast().unwrap(); - if !window.is_decorated() && window.is_resizable() { - if let Some(window) = window.window() { - let (cx, cy) = event.root(); - let edge = hit_test(&window, cx, cy); - // FIXME: calling `window.begin_resize_drag` seems to revert the cursor back to normal style - window.set_cursor( - Cursor::from_name( - &window.display(), - match edge { - WindowEdge::North => "n-resize", - WindowEdge::South => "s-resize", - WindowEdge::East => "e-resize", - WindowEdge::West => "w-resize", - WindowEdge::NorthWest => "nw-resize", - WindowEdge::NorthEast => "ne-resize", - WindowEdge::SouthEast => "se-resize", - WindowEdge::SouthWest => "sw-resize", - _ => "default", - }, - ) - .as_ref(), - ); - } - } - } - } - gtk::glib::Propagation::Proceed - }); - webview.connect_button_press_event(move |webview, event| { - if event.button() == 1 { - let (cx, cy) = event.root(); - // This one should be GtkBox - if let Some(widget) = webview.parent() { - // This one should be GtkWindow - if let Some(window) = widget.parent() { - // Safe to unwrap unless this is not from tao - let window: gtk::Window = window.downcast().unwrap(); - if !window.is_decorated() && window.is_resizable() { - if let Some(window) = window.window() { - // Safe to unwrap since it's a valid GtkWindow - let result = hit_test(&window, cx, cy); - - // we ignore the `__Unknown` variant so the webview receives the click correctly if it is not on the edges. - match result { - WindowEdge::__Unknown(_) => (), - _ => window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.time()), - } - } - } - } - } - } - gtk::glib::Propagation::Proceed - }); - webview.connect_touch_event(|webview, event| { - // This one should be GtkBox - if let Some(widget) = webview.parent() { - // This one should be GtkWindow - if let Some(window) = widget.parent() { - // Safe to unwrap unless this is not from tao - let window: gtk::Window = window.downcast().unwrap(); - if !window.is_decorated() && window.is_resizable() && !window.is_maximized() { - if let Some(window) = window.window() { - if let Some((cx, cy)) = event.root_coords() { - if let Some(device) = event.device() { - let result = hit_test(&window, cx, cy); - - // we ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges. - match result { - WindowEdge::__Unknown(_) => (), - _ => window.begin_resize_drag_for_device( - result, - &device, - 0, - cx as i32, - cy as i32, - event.time(), - ), - } - } - } - } - } - } - } - gtk::glib::Propagation::Proceed - }); -} - -pub fn hit_test(window: &gdk::Window, cx: f64, cy: f64) -> WindowEdge { - let (left, top) = window.position(); - let (w, h) = (window.width(), window.height()); - let (right, bottom) = (left + w, top + h); - let (cx, cy) = (cx as i32, cy as i32); - - const LEFT: i32 = 0b0001; - const RIGHT: i32 = 0b0010; - const TOP: i32 = 0b0100; - const BOTTOM: i32 = 0b1000; - const TOPLEFT: i32 = TOP | LEFT; - const TOPRIGHT: i32 = TOP | RIGHT; - const BOTTOMLEFT: i32 = BOTTOM | LEFT; - const BOTTOMRIGHT: i32 = BOTTOM | RIGHT; - - let inset = 5 * window.scale_factor(); - #[rustfmt::skip] - let result = - (LEFT * (if cx < (left + inset) { 1 } else { 0 })) - | (RIGHT * (if cx >= (right - inset) { 1 } else { 0 })) - | (TOP * (if cy < (top + inset) { 1 } else { 0 })) - | (BOTTOM * (if cy >= (bottom - inset) { 1 } else { 0 })); - - match result { - LEFT => WindowEdge::West, - TOP => WindowEdge::North, - RIGHT => WindowEdge::East, - BOTTOM => WindowEdge::South, - TOPLEFT => WindowEdge::NorthWest, - TOPRIGHT => WindowEdge::NorthEast, - BOTTOMLEFT => WindowEdge::SouthWest, - BOTTOMRIGHT => WindowEdge::SouthEast, - // we return `WindowEdge::__Unknown` to be ignored later. - // we must return 8 or bigger, otherwise it will be the same as one of the other 7 variants of `WindowEdge` enum. - _ => WindowEdge::__Unknown(8), - } -} diff --git a/src/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs index a2a2cbf75..3c69f48c9 100644 --- a/src/webkitgtk/web_context.rs +++ b/src/webkitgtk/web_context.rs @@ -5,7 +5,7 @@ //! Unix platform extensions for [`WebContext`](super::WebContext). use crate::{web_context::WebContextData, Error, RequestAsyncResponder}; -use gtk::{gio, glib}; +use gtk::glib; use http::{header::CONTENT_TYPE, Request, Response as HttpResponse}; use std::{ borrow::Cow, From 68fd18d496eae245dcb75f2cd5e0ffd49a1c6716 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:31:00 +0300 Subject: [PATCH 18/80] implement set_position and set_size for child gtk in x11 --- src/webkitgtk/mod.rs | 63 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index b64066004..9f4f8216a 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -44,25 +44,21 @@ pub(crate) struct InnerWebView { pending_scripts: Arc>>>, is_child: bool, - display: Option, xlib: Option, + x11_display: Option<*mut std::ffi::c_void>, x11_window: Option, + display: Option, + gtk_window: Option, } impl Drop for InnerWebView { fn drop(&mut self) { if let Some(xlib) = &self.xlib { - use gdkx11::X11Display; - - let gx11_display: &X11Display = self.display.as_ref().unwrap().downcast_ref().unwrap(); - let raw = glib::translate::ToGlibPtr::to_glib_none(&gx11_display).0; - let display = unsafe { gdkx11::ffi::gdk_x11_display_get_xdisplay(raw) }; - if self.is_child { - unsafe { (xlib.XDestroyWindow)(display as _, self.x11_window.unwrap()) }; + unsafe { (xlib.XDestroyWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; } - unsafe { (xlib.XCloseDisplay)(display as _) }; + unsafe { (xlib.XCloseDisplay)(self.x11_display.unwrap() as _) }; } } } @@ -131,11 +127,18 @@ impl InnerWebView { gtk_window.set_has_window(true); gtk_window.realize(); - Self::new_gtk(>k_window, attributes, pl_attrs, web_context).map(|mut w| { + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + gtk_window.add(&vbox); + + Self::new_gtk(&vbox, attributes, pl_attrs, web_context).map(|mut w| { + gtk_window.show_all(); + w.is_child = is_child; w.xlib = Some(xlib); w.display = Some(gdk_display); + w.x11_display = Some(display as _); w.x11_window = Some(window); + w.gtk_window = Some(gtk_window); w }) } @@ -380,7 +383,9 @@ impl InnerWebView { is_child: false, xlib: None, display: None, + x11_display: None, x11_window: None, + gtk_window: None, }; // Initialize message handler @@ -566,12 +571,42 @@ impl InnerWebView { Ok(()) } - pub fn set_position(&self, _position: (i32, i32)) { - if self.is_child {} + pub fn set_position(&self, position: (i32, i32)) { + if self.is_child { + let xlib = self.xlib.as_ref().unwrap(); + let mut changes = XWindowChanges { + x: position.0, + y: position.1, + ..unsafe { std::mem::zeroed() } + }; + unsafe { + (xlib.XConfigureWindow)( + self.x11_display.unwrap() as _, + self.x11_window.unwrap(), + (CWY | CWX) as _, + &mut changes as *mut _, + ); + } + } } - pub fn set_size(&self, _size: (u32, u32)) { - if self.is_child {} + pub fn set_size(&self, size: (u32, u32)) { + if self.is_child { + let xlib = self.xlib.as_ref().unwrap(); + let mut changes = XWindowChanges { + width: size.0 as _, + height: size.1 as _, + ..unsafe { std::mem::zeroed() } + }; + unsafe { + (xlib.XConfigureWindow)( + self.x11_display.unwrap() as _, + self.x11_window.unwrap(), + (CWWidth | CWHeight) as _, + &mut changes as *mut _, + ); + } + } } } From 17e943fe48d2c72242d3c9217312ddbc7b47d35f Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:38:21 +0300 Subject: [PATCH 19/80] fix macos --- src/webview2/mod.rs | 4 ++-- src/wkwebview/mod.rs | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 08395c1b9..dc5b7a335 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -141,7 +141,7 @@ impl InnerWebView { Self::new_as_child_hwnd(child, attributes, pl_attrs, web_context) } - pub fn new_as_child_hwnd( + fn new_as_child_hwnd( hwnd: HWND, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -153,7 +153,7 @@ impl InnerWebView { }) } - pub fn new_hwnd( + fn new_hwnd( hwnd: HWND, mut attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index fe22691a2..59d0df31f 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -106,7 +106,7 @@ impl InnerWebView { _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; - Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context) + Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context, false) } pub fn new_as_child( @@ -123,12 +123,10 @@ impl InnerWebView { _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; - let mut webview = Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context)?; - webview.is_child = true; - Ok(webview) + Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context, true) } - pub fn new_ns_view( + fn new_ns_view( ns_view: id, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -816,7 +814,7 @@ impl InnerWebView { page_load_handler, download_delegate, protocol_ptrs, - is_child: false, + is_child, }; // Initialize scripts From 1352b9784f6b7784f9664e82545d8ee467a88193 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:40:30 +0300 Subject: [PATCH 20/80] more fixes --- src/android/mod.rs | 4 ++-- src/webview2/mod.rs | 8 ++++---- src/wkwebview/mod.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/android/mod.rs b/src/android/mod.rs index fd372d0de..cb662869e 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -107,8 +107,8 @@ pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef pub(crate) struct InnerWebView; impl InnerWebView { - pub fn new( - _window: &W, + pub fn new( + _window: &impl HasWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index dc5b7a335..a043c4dc5 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -69,8 +69,8 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - pub fn new( - window: &impl W, + pub fn new( + window: &impl HasWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, @@ -82,8 +82,8 @@ impl InnerWebView { Self::new_hwnd(HWND(handle.hwnd.get()), attributes, pl_attrs, web_context) } - pub fn new_as_child( - parent: &W, + pub fn new_as_child( + parent: &impl HasWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 59d0df31f..69c844623 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -92,8 +92,8 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - pub fn new( - window: &W, + pub fn new( + window: &impl HasWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -109,8 +109,8 @@ impl InnerWebView { Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context, false) } - pub fn new_as_child( - window: &W, + pub fn new_as_child( + window: &impl HasWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, From dcb01de3c51650f5765b4744508aa008fda6e7f4 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:45:24 +0300 Subject: [PATCH 21/80] one more time --- src/lib.rs | 16 +++++++++++++--- src/wkwebview/mod.rs | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eec1a7d34..f5f3d2592 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -891,11 +891,21 @@ impl<'a> WebViewBuilder<'a> { } else { InnerWebView::new(window, self.attrs, self.platform_specific, self.web_context)? } - } else if let Some(widget) = self.gtk_widget { - InnerWebView::new_gtk(widget, self.attrs, self.platform_specific, self.web_context)? } else { - unreachable!() + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + if let Some(widget) = self.gtk_widget { + InnerWebView::new_gtk(widget, self.attrs, self.platform_specific, self.web_context)? + } else { + unreachable!() + } }; + Ok(WebView { webview }) } } diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 69c844623..c5e307e4c 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -106,7 +106,7 @@ impl InnerWebView { _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; - Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context, false) + Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, false) } pub fn new_as_child( @@ -123,7 +123,7 @@ impl InnerWebView { _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; - Self::new_ns_view(ns_view, attributes, _pl_attrs, _web_context, true) + Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, true) } fn new_ns_view( From 6b601e8205cb7cbd51f707e1b686353184dc7311 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 05:48:12 +0300 Subject: [PATCH 22/80] unreachable --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index f5f3d2592..b4b3707c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -904,6 +904,15 @@ impl<'a> WebViewBuilder<'a> { } else { unreachable!() } + + #[cfg(not(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + )))] + unreachable!() }; Ok(WebView { webview }) From 7283d571aeffeae8f93550089bb644486c99a49a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 06:03:05 +0300 Subject: [PATCH 23/80] update actions --- .github/workflows/audit.yml | 29 ++++++--- .github/workflows/bench.yml | 13 ++-- .github/workflows/build.yml | 11 +--- .github/workflows/covector-status.yml | 4 +- .../workflows/covector-version-or-publish.yml | 18 +++--- .github/workflows/fmt.yml | 61 ++++++++++++------- 6 files changed, 78 insertions(+), 58 deletions(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index c105d2ed6..fd10e86e6 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,20 +1,33 @@ -name: Audit +# Copyright 2022-2022 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: audit on: workflow_dispatch: schedule: - cron: '0 0 * * *' push: + branches: + - dev + paths: + - 'Cargo.lock' + - 'Cargo.toml' + pull_request: paths: - - "Cargo.lock" - - "Cargo.toml" + - 'Cargo.lock' + - 'Cargo.toml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - audit-rust: + audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: rust audit - uses: actions-rs/audit-check@v1 + - uses: actions/checkout@v4 + - uses: rustsec/audit-check@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 713cfa07a..9a0c73ebd 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -27,14 +27,9 @@ jobs: runs-on: ${{ matrix.platform.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: install ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.rust }} - override: true - components: rust-src - target: ${{ matrix.platform.target }} + uses: dtolnay/rust-toolchain@nightly - name: Setup python uses: actions/setup-python@v2 @@ -46,7 +41,7 @@ jobs: run: | python -m pip install --upgrade pip sudo apt-get update - sudo apt-get install -y webkit2gtk-4.1-dev libayatana-appindicator3-dev xvfb + sudo apt-get install -y libwebkit2gtk-4.1-dev xvfb wget https://github.com/sharkdp/hyperfine/releases/download/v1.11.0/hyperfine_1.11.0_amd64.deb sudo dpkg -i hyperfine_1.11.0_amd64.deb pip install memory_profiler @@ -64,7 +59,7 @@ jobs: - name: clone benchmarks_results if: github.repository == 'tauri-apps/wry' && github.ref == 'refs/heads/dev' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: token: ${{ secrets.BENCH_PAT }} path: gh-pages diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5da45a4d..7115ec73c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ jobs: strategy: fail-fast: false matrix: - rust_version: [stable] platform: - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } @@ -28,7 +27,7 @@ jobs: if: contains(matrix.platform.target, 'gnu') run: | sudo apt-get update - sudo apt-get install -y webkit2gtk-4.1 libgtksourceview-3.0-dev libayatana-appindicator3-dev + sudo apt-get install -y libwebkit2gtk-4.1-dev - name: install webview2 (windows only) if: contains(matrix.platform.target, 'windows') @@ -44,14 +43,10 @@ jobs: - name: build tests and examples shell: bash - if: ( - !contains(matrix.platform.target, 'android') && - !contains(matrix.platform.target, 'ios')) + if: (!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'ios')) run: cargo test --no-run --verbose --target ${{ matrix.platform.target }} - name: run tests - if: ( - !contains(matrix.platform.target, 'android') && - !contains(matrix.platform.target, 'ios')) + if: (!contains(matrix.platform.target, 'android') && !contains(matrix.platform.target, 'ios')) run: cargo test --verbose --target ${{ matrix.platform.target }} --features linux-body diff --git a/.github/workflows/covector-status.yml b/.github/workflows/covector-status.yml index 72f9bee13..8cc1b544f 100644 --- a/.github/workflows/covector-status.yml +++ b/.github/workflows/covector-status.yml @@ -6,9 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 + - uses: actions/checkout@v4 - name: covector status uses: jbolda/covector/packages/action@covector-v0 id: covector diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml index 32d2ef3a3..20d6b7c19 100644 --- a/.github/workflows/covector-version-or-publish.yml +++ b/.github/workflows/covector-version-or-publish.yml @@ -1,4 +1,4 @@ -name: version or publish +name: covector version or publish on: push: @@ -15,15 +15,16 @@ jobs: successfulPublish: ${{ steps.covector.outputs.successfulPublish }} steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 + - uses: actions/checkout@v4 + - name: cargo login run: cargo login ${{ secrets.ORG_CRATES_IO_TOKEN }} + - name: git config run: | git config --global user.name "${{ github.event.pusher.name }}" git config --global user.email "${{ github.event.pusher.email }}" + - name: covector version or publish (publish when no change files present) uses: jbolda/covector/packages/action@covector-v0 id: covector @@ -34,14 +35,15 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} command: 'version-or-publish' createRelease: true + - name: Create Pull Request With Versions Bumped id: cpr uses: tauri-apps/create-pull-request@v3 if: steps.covector.outputs.commandRan == 'version' with: token: ${{ secrets.GITHUB_TOKEN }} - title: "Publish New Versions" - commit-message: "publish new versions" - labels: "version updates" - branch: "release" + title: Apply Version Updates From Current Changes + commit-message: 'apply version updates' + labels: 'version updates' + branch: 'release' body: ${{ steps.covector.outputs.change }} diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 01ee2187e..12b55c2b0 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -1,31 +1,48 @@ -name: fmt check +# Copyright 2022-2022 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: clippy & fmt on: + push: + branches: + - dev pull_request: - paths: - - 'src/**' - - 'Cargo.toml' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - clippy_fmt_check: - runs-on: macos-latest + clippy: + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + + runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt,clippy - - uses: Swatinem/rust-cache@v2 - - name: fmt - uses: actions-rs/cargo@v1 + - uses: actions/checkout@v4 + - name: install system deps + if: matrix.platform == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev + + - uses: dtolnay/rust-toolchain@stable with: - command: fmt - args: --all -- --check - - name: clippy - uses: actions-rs/cargo@v1 + components: clippy + + - run: cargo clippy --all-targets --all-features -- -D warnings + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: - command: clippy - args: --all --examples -- -D warnings + components: rustfmt + + - run: cargo fmt --all -- --check \ No newline at end of file From a6c5d27a615bd19a447fe80f753e99ea77ad9d08 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 06:04:24 +0300 Subject: [PATCH 24/80] fix windows --- src/webview2/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index a043c4dc5..ab98e43ca 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -76,10 +76,10 @@ impl InnerWebView { web_context: Option<&mut WebContext>, ) -> Result { let window = match window.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(window) => window.hwnd, + raw_window_handle::RawWindowHandle::Win32(window) => window.hwnd.get(), _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; - Self::new_hwnd(HWND(handle.hwnd.get()), attributes, pl_attrs, web_context) + Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) } pub fn new_as_child( @@ -89,7 +89,7 @@ impl InnerWebView { web_context: Option<&mut WebContext>, ) -> Result { let parent = match parent.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(parent) => parent.hwnd, + raw_window_handle::RawWindowHandle::Win32(parent) => parent.hwnd.get(), _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; @@ -131,7 +131,7 @@ impl InnerWebView { attributes.position.map(|a| a.1).unwrap_or(CW_USEDEFAULT), attributes.size.map(|a| a.0 as i32).unwrap_or(CW_USEDEFAULT), attributes.size.map(|a| a.1 as i32).unwrap_or(CW_USEDEFAULT), - HWND(parent.hwnd.get()), + HWND(parent), HMENU::default(), GetModuleHandleW(PCWSTR::null()).unwrap_or_default(), None, From f09ba8c6eebedef884b10ed8cfda3f05c9a6959b Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 06:15:04 +0300 Subject: [PATCH 25/80] some clippy --- src/webkitgtk/web_context.rs | 2 +- src/webview2/mod.rs | 4 ++-- src/wkwebview/mod.rs | 2 +- src/wkwebview/proxy.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs index 3c69f48c9..a2256065e 100644 --- a/src/webkitgtk/web_context.rs +++ b/src/webkitgtk/web_context.rs @@ -351,7 +351,7 @@ where } result }) - .unwrap_or(Vec::new()); + .unwrap_or_default(); } #[cfg(not(feature = "linux-body"))] { diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index ab98e43ca..848a8ee87 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -76,7 +76,7 @@ impl InnerWebView { web_context: Option<&mut WebContext>, ) -> Result { let window = match window.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(window) => window.hwnd.get(), + RawWindowHandle::Win32(window) => window.hwnd.get(), _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) @@ -89,7 +89,7 @@ impl InnerWebView { web_context: Option<&mut WebContext>, ) -> Result { let parent = match parent.window_handle()?.as_raw() { - raw_window_handle::RawWindowHandle::Win32(parent) => parent.hwnd.get(), + RawWindowHandle::Win32(parent) => parent.hwnd.get(), _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), }; diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index c5e307e4c..e2ec9447b 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -41,7 +41,7 @@ use objc_id::Id; use file_drop::{add_file_drop_methods, set_file_drop_handler}; #[cfg(feature = "mac-proxy")] -use crate::webview::{ +use crate::{ proxy::ProxyConfig, wkwebview::proxy::{ nw_endpoint_t, nw_proxy_config_create_http_connect, nw_proxy_config_create_socksv5, diff --git a/src/wkwebview/proxy.rs b/src/wkwebview/proxy.rs index b2c1f46a2..8a41fd84d 100644 --- a/src/wkwebview/proxy.rs +++ b/src/wkwebview/proxy.rs @@ -5,7 +5,7 @@ use cocoa::base::nil; use libc::c_char; -use crate::{webview::proxy::ProxyEndpoint, Error}; +use crate::{proxy::ProxyEndpoint, Error}; use super::NSString; From 6ada710335bb065b5c735c4ec41f5dd4557504d1 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 13:17:43 +0900 Subject: [PATCH 26/80] Add documentation and fix clippy --- src/lib.rs | 39 ++++++++++++++++++++++++++++++++++----- src/webkitgtk/mod.rs | 1 - 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b4b3707c7..1e66c593b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,13 @@ //! [`with_file_drop_handler`]: crate::webview::WebView::with_file_drop_handler //! [`with_custom_protocol`]: crate::webview::WebView::with_custom_protocol +#![allow(clippy::new_without_default)] +#![allow(clippy::wrong_self_convention)] +#![allow(clippy::type_complexity)] +#![allow(clippy::unit_cmp)] +#![allow(clippy::upper_case_acronyms)] +#![cfg_attr(docsrs, feature(doc_cfg))] + pub use http; pub use raw_window_handle; @@ -351,8 +358,12 @@ pub struct WebViewAttributes { /// - **macOS / Android / iOS:** Unsupported. pub focused: bool, + /// Set the postion if the webview is created by `new_as_child`. If it's `None`, the position + /// will be (0, 0). pub position: Option<(i32, i32)>, + /// Set the size if the webview is created by `new_as_child`. If it's `None`, the position + /// will be (0, 0). pub size: Option<(u32, u32)>, } @@ -442,13 +453,18 @@ pub struct WebViewBuilder<'a> { impl<'a> WebViewBuilder<'a> { /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. + /// + /// ## Platform-specific + /// + /// - **Linux**: This window handle must be created from a GTK window. Only x11 + /// is supported. This method won't work on wayland. pub fn new(window: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), as_child: false, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes::default(), + platform_specific: PlatformSpecificWebViewAttributes, web_context: None, #[cfg(any( target_os = "linux", @@ -461,14 +477,25 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. TODO doc + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. + /// Please read platform specific notes to know how it actually works on different platform. + /// + /// + /// ## Platform-specific + /// + /// - **Windows**: This will create the webview as a child window of the `parent` window. + /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's + /// content view. + /// - **Linux**: This will create the webview as a child window of the `parent` window. Only x11 + /// is supported. This method won't work on wayland. + /// - **Android/iOS:** Unsupported. pub fn new_as_child(parent: &'a W) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(parent), as_child: true, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes::default(), + platform_specific: PlatformSpecificWebViewAttributes, web_context: None, #[cfg(any( target_os = "linux", @@ -488,9 +515,9 @@ impl<'a> WebViewBuilder<'a> { target_os = "netbsd", target_os = "openbsd", ))] + /// Create the webview from a GTK container widget, such as GTK window. pub fn new_gtk(widget: &'a W) -> Self where - W: gtk::prelude::IsA, W: gtk::prelude::IsA, { use gdkx11::glib::Cast; @@ -500,7 +527,7 @@ impl<'a> WebViewBuilder<'a> { window: None, as_child: false, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes::default(), + platform_specific: PlatformSpecificWebViewAttributes, web_context: None, gtk_widget: Some(widget.dynamic_cast_ref().unwrap()), } @@ -1235,10 +1262,12 @@ impl WebView { self.webview.clear_all_browsing_data() } + /// Set the webview position if it's a child window. pub fn set_position(&self, position: (i32, i32)) { self.webview.set_position(position) } + /// Set the webview size if it's a child window. pub fn set_size(&self, size: (u32, u32)) { self.webview.set_size(size) } diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 9f4f8216a..f6b8a2e2f 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -150,7 +150,6 @@ impl InnerWebView { web_context: Option<&mut WebContext>, ) -> Result where - W: IsA, W: IsA, { let window_id = container.as_ptr() as isize; From 8c16f53455d4fbc6c4417fed1cec7dab1776151c Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 13:26:01 +0900 Subject: [PATCH 27/80] Fix windows clippy error --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1e66c593b..d43a0c150 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -415,7 +415,7 @@ impl Default for WebViewAttributes { target_os = "ios", ))] #[derive(Default)] -pub(crate) struct PlatformSpecificWebViewAttributes; +pub(crate) struct PlatformSpecificWebViewAttributes(); /// Type alias for a color in the RGBA format. /// @@ -464,7 +464,7 @@ impl<'a> WebViewBuilder<'a> { window: Some(window), as_child: false, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes, + platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, #[cfg(any( target_os = "linux", @@ -495,7 +495,7 @@ impl<'a> WebViewBuilder<'a> { window: Some(parent), as_child: true, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes, + platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, #[cfg(any( target_os = "linux", @@ -527,7 +527,7 @@ impl<'a> WebViewBuilder<'a> { window: None, as_child: false, #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes, + platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, gtk_widget: Some(widget.dynamic_cast_ref().unwrap()), } From 9de0f239a18b0ed260cf89c08918a85bffbe7da1 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 15:07:36 +0900 Subject: [PATCH 28/80] Fix android biuld --- Cargo.toml | 1 + src/android/binding.rs | 2 +- src/android/mod.rs | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f20399437..8123cf157 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ html5ever = "0.26" kuchiki = { package = "kuchikiki", version = "0.8" } sha2 = "0.10" base64 = "0.21" +tao = { git = "https://github.com/tauri-apps/tao" } [dev-dependencies] http-range = "0.1.5" diff --git a/src/android/binding.rs b/src/android/binding.rs index 6a5deecb2..1bdae9acc 100644 --- a/src/android/binding.rs +++ b/src/android/binding.rs @@ -279,7 +279,7 @@ pub unsafe fn handleReceivedTitle(mut env: JNIEnv, _: JClass, _webview: JObject, Ok(title) => { let title = title.to_string_lossy().to_string(); if let Some(title_handler) = TITLE_CHANGE_HANDLER.get() { - (title_handler.handler)(&title_handler, title) + (title_handler.handler)(title) } } Err(e) => log::warn!("Failed to parse JString: {}", e), diff --git a/src/android/mod.rs b/src/android/mod.rs index cb662869e..e10a4b126 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -308,6 +308,14 @@ impl InnerWebView { MainPipe::send(WebViewMessage::ClearAllBrowsingData); Ok(()) } + + pub fn set_position(&self, _position: (i32, i32)) { + // Unsupported. + } + + pub fn set_size(&self, _size: (u32, u32)) { + // Unsupported. + } } #[derive(Clone, Copy)] From d391bece37a20c019dd5a02e4cd977cf83fd32bd Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 15:30:08 +0900 Subject: [PATCH 29/80] Fix documentation test --- src/android/binding.rs | 4 ++-- src/android/main_pipe.rs | 2 +- src/android/mod.rs | 4 ++-- src/lib.rs | 25 +++++++++++++++++++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/android/binding.rs b/src/android/binding.rs index 1bdae9acc..408416a41 100644 --- a/src/android/binding.rs +++ b/src/android/binding.rs @@ -19,7 +19,7 @@ use super::{ URL_LOADING_OVERRIDE, WITH_ASSET_LOADER, }; -use crate::webview::PageLoadEvent; +use crate::PageLoadEvent; #[macro_export] macro_rules! android_binding { @@ -32,7 +32,7 @@ macro_rules! android_binding { android_binding as tao_android_binding, android_fn, generate_package_name, platform::android::ndk_glue::*, }, - webview::prelude::*, + prelude::*, }; tao_android_binding!($domain, $package, WryActivity, setup, $main); android_fn!( diff --git a/src/android/main_pipe.rs b/src/android/main_pipe.rs index c791f19c8..6c42af257 100644 --- a/src/android/main_pipe.rs +++ b/src/android/main_pipe.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{webview::RGBA, Error}; +use crate::{RGBA, Error}; use crossbeam_channel::*; use once_cell::sync::Lazy; use std::os::unix::prelude::*; diff --git a/src/android/mod.rs b/src/android/mod.rs index e10a4b126..1aa9969c2 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use super::{PageLoadEvent, WebContext, WebViewAttributes, RGBA}; -use crate::{webview::RequestAsyncResponder, Result}; +use crate::{RequestAsyncResponder, Result}; use base64::{engine::general_purpose, Engine}; use crossbeam_channel::*; use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName}; @@ -15,7 +15,7 @@ use kuchiki::NodeRef; use once_cell::sync::OnceCell; use raw_window_handle::HasWindowHandle; use sha2::{Digest, Sha256}; -use std::{borrow::Cow, rc::Rc, sync::mpsc::channel}; +use std::{borrow::Cow, sync::mpsc::channel}; use tao::platform::android::ndk_glue::{ jni::{ errors::Error as JniError, diff --git a/src/lib.rs b/src/lib.rs index d43a0c150..c8f56f9cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,7 +362,7 @@ pub struct WebViewAttributes { /// will be (0, 0). pub position: Option<(i32, i32)>, - /// Set the size if the webview is created by `new_as_child`. If it's `None`, the position + /// Set the size if the webview is created by `new_as_child`. If it's `None`, the size /// will be (0, 0). pub size: Option<(u32, u32)>, } @@ -893,11 +893,15 @@ impl<'a> WebViewBuilder<'a> { self } + /// Set the postion if the webview is created by `new_as_child`. If it's `None`, the position + /// will be (0, 0). pub fn with_position(mut self, position: (i32, i32)) -> Self { self.attrs.position = Some(position); self } + /// Set the size if the webview is created by `new_as_child`. If it's `None`, the size + /// will be (0, 0). pub fn with_size(mut self, size: (u32, u32)) -> Self { self.attrs.size = Some(size); self @@ -1091,7 +1095,7 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { // register custom protocol with empty Response return, // this is necessary due to the need of fixing a domain // in WebViewAssetLoader. - self.webview.custom_protocols.push(( + self.attrs.custom_protocols.push(( protocol.clone(), Box::new(|_, api| { api.respond(HttpResponse::builder().body(Vec::new()).unwrap()); @@ -1133,6 +1137,22 @@ impl WebView { WebViewBuilder::new(window).build() } + #[cfg(not(any( + target_os = "android", + target_os = "ios", + )))] + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. + /// Please read platform specific notes to know how it actually works on different platform. + /// + /// + /// ## Platform-specific + /// + /// - **Windows**: This will create the webview as a child window of the `parent` window. + /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's + /// content view. + /// - **Linux**: This will create the webview as a child window of the `parent` window. Only x11 + /// is supported. This method won't work on wayland. + /// - **Android/iOS:** Unsupported. pub fn new_as_child(window: &W) -> Result { WebViewBuilder::new_as_child(window).build() } @@ -1144,6 +1164,7 @@ impl WebView { target_os = "netbsd", target_os = "openbsd", ))] + /// Create the webview from a GTK container widget, such as GTK window. pub fn new_gtk(widget: &W) -> Result where W: gtk::prelude::IsA, From 263a323e44bfadbea0bf7df07267f09d64a16aba Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 15:46:23 +0900 Subject: [PATCH 30/80] Fix android again --- src/android/main_pipe.rs | 2 +- src/android/mod.rs | 9 +++++++++ src/lib.rs | 4 ---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/android/main_pipe.rs b/src/android/main_pipe.rs index 6c42af257..0db87e076 100644 --- a/src/android/main_pipe.rs +++ b/src/android/main_pipe.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{RGBA, Error}; +use crate::{Error, RGBA}; use crossbeam_channel::*; use once_cell::sync::Lazy; use std::os::unix::prelude::*; diff --git a/src/android/mod.rs b/src/android/mod.rs index 1aa9969c2..3b01c6fef 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -107,6 +107,15 @@ pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef pub(crate) struct InnerWebView; impl InnerWebView { + pub fn new_as_child( + _window: &impl HasWindowHandle, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + _web_context: Option<&mut WebContext>, + ) -> Result { + Self::new(_window, attributes, pl_attrs, _web_context) + } + pub fn new( _window: &impl HasWindowHandle, attributes: WebViewAttributes, diff --git a/src/lib.rs b/src/lib.rs index c8f56f9cc..962aa2020 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1137,10 +1137,6 @@ impl WebView { WebViewBuilder::new(window).build() } - #[cfg(not(any( - target_os = "android", - target_os = "ios", - )))] /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. /// Please read platform specific notes to know how it actually works on different platform. /// From 18cba9100aea27a03e439d79c20a8d537c0f9563 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Wed, 25 Oct 2023 15:53:16 +0900 Subject: [PATCH 31/80] Reduce clippy noise --- .github/workflows/fmt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 12b55c2b0..265ac91d8 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -35,7 +35,7 @@ jobs: with: components: clippy - - run: cargo clippy --all-targets --all-features -- -D warnings + - run: cargo clippy --all-targets -- -D warnings fmt: runs-on: ubuntu-latest @@ -45,4 +45,4 @@ jobs: with: components: rustfmt - - run: cargo fmt --all -- --check \ No newline at end of file + - run: cargo fmt --all -- --check From 59f30890ce744c68e0e92364dbbf472d4182a322 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 15:26:35 +0300 Subject: [PATCH 32/80] use rc? --- src/webview2/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 848a8ee87..6dc685966 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -5,13 +5,8 @@ mod file_drop; use std::{ - borrow::Cow, - collections::HashSet, - fmt::Write, - iter::once, - os::windows::prelude::OsStrExt, - path::PathBuf, - sync::{mpsc, Arc}, + borrow::Cow, collections::HashSet, fmt::Write, iter::once, os::windows::prelude::OsStrExt, + path::PathBuf, rc::Rc, sync::mpsc, }; use http::{Request, Response as HttpResponse, StatusCode}; @@ -409,7 +404,7 @@ impl InnerWebView { } if let Some(on_page_load_handler) = attributes.on_page_load_handler { - let on_page_load_handler = Arc::new(on_page_load_handler); + let on_page_load_handler = Rc::new(on_page_load_handler); let on_page_load_handler_ = on_page_load_handler.clone(); unsafe { From 7300dfae07a86770fda4e0a2d6e7ab8d02353f0a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 19:17:39 +0300 Subject: [PATCH 33/80] refine some documentation, add set_visible --- .github/workflows/fmt.yml | 2 +- examples/custom_protocol.rs | 21 +- src/android/mod.rs | 4 + src/lib.rs | 509 +++++++++++++++++++++-------------- src/web_context.rs | 2 +- src/webkitgtk/mod.rs | 45 +++- src/webkitgtk/web_context.rs | 6 +- src/webview2/mod.rs | 9 +- src/wkwebview/mod.rs | 4 + 9 files changed, 382 insertions(+), 220 deletions(-) diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml index 265ac91d8..cfe706464 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/fmt.yml @@ -35,7 +35,7 @@ jobs: with: components: clippy - - run: cargo clippy --all-targets -- -D warnings + - run: cargo clippy --all-targets --all-features -- -D warnings fmt: runs-on: ubuntu-latest diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index a36e44c21..093548fa1 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -27,7 +27,26 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(&window) + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + let _webview = builder .with_asynchronous_custom_protocol("wry".into(), move |request, responder| { match get_wry_response(request) { Ok(http_response) => responder.respond(http_response), diff --git a/src/android/mod.rs b/src/android/mod.rs index 3b01c6fef..a6a301ed1 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -325,6 +325,10 @@ impl InnerWebView { pub fn set_size(&self, _size: (u32, u32)) { // Unsupported. } + + pub fn set_visible(&self, visible: bool) { + // Unsupported + } } #[derive(Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index 962aa2020..7fd007333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,54 +6,133 @@ //! Wry is a Cross-platform WebView rendering library. //! -//! To build a Window with WebView embedded, we could use [`application`] module to create -//! [`EventLoop`] and the window. It's a module that re-exports APIs from [tao]. Then -//! use [`webview`] module to create the [`WebView`] from the [`Window`]. Here's a minimum example -//! showing how to create a hello world window and load the url to Tauri website. +//! The webview requires a running event loop and a window type that implements [`HasWindowHandle`], +//! or a gtk container widget if you need to support X11 and Wayland. +//! You can use a windowing library like [`tao`] or [`winit`]. +//! +//! ## Examples +//! +//! This example leverages the [`HasWindowHandle`] and supports Windows, macOS, iOS, Android and Linux (X11 Only) +//! +//! ```no_run +//! use wry::WebViewBuilder; +//! +//! # use raw_window_handle as rwh_06; +//! # struct T; +//! # impl rwh_06::HasWindowHandle for T { +//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { +//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( +//! # rwh_06::Win32WindowHandle::new(1), +//! # ))) +//! # } +//! # } +//! # let window = T; +//! let webview = WebViewBuilder::new(&window) +//! .with_url("https://tauri.app") +//! .unwrap() +//! .build() +//! .unwrap(); +//! ``` +//! +//! If you also want to support Wayland too, then we recommend you use [`WebViewBuilder::new_gtk`] on Linux. +//! +//! ```no_run +//! use wry::WebViewBuilder; +//! +//! # use raw_window_handle as rwh_06; +//! # struct T; +//! # impl rwh_06::HasWindowHandle for T { +//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { +//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( +//! # rwh_06::Win32WindowHandle::new(1), +//! # ))) +//! # } +//! # } +//! # let window = T; +//! #[cfg(any( +//! target_os = "windows", +//! target_os = "macos", +//! target_os = "ios", +//! target_os = "android" +//! ))] +//! let builder = WebViewBuilder::new(&window); +//! #[cfg(not(any( +//! target_os = "windows", +//! target_os = "macos", +//! target_os = "ios", +//! target_os = "android" +//! )))] +//! let builder = { +//! use tao::platform::unix::WindowExtUnix; +//! WebViewBuilder::new_gtk(>k_window) +//! }; +//! +//! let webview = builder +//! .with_url("https://tauri.app") +//! .unwrap() +//! .build() +//! .unwrap(); +//! ``` +//! +//! ## Child webviews +//! +//! You can use [`WebView::new_as_child`] to create the webview as a child inside another window. This is supported on +//! macOS, Windows and Linux (X11 Only). //! //! ```no_run -//! fn main() -> wry::Result<()> { -//! use tao::{ -//! event::{Event, StartCause, WindowEvent}, -//! event_loop::{ControlFlow, EventLoop}, -//! window::WindowBuilder, -//! }; -//! use wry::WebViewBuilder; +//! use wry::WebViewBuilder; //! -//! let event_loop = EventLoop::new(); -//! let window = WindowBuilder::new() -//! .with_title("Hello World") -//! .build(&event_loop).unwrap(); -//! let _webview = WebViewBuilder::new(&window) -//! .with_url("https://tauri.studio")? -//! .build()?; +//! # use raw_window_handle as rwh_06; +//! # struct T; +//! # impl rwh_06::HasWindowHandle for T { +//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { +//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( +//! # rwh_06::Win32WindowHandle::new(1), +//! # ))) +//! # } +//! # } +//! # let window = T; +//! let webview = WebViewBuilder::new_as_child(&window) +//! .with_url("https://tauri.app") +//! .unwrap() +//! .build() +//! .unwrap(); +//! ``` +//! +//! ## Platform Considerations //! -//! event_loop.run(move |event, _, control_flow| { -//! *control_flow = ControlFlow::Wait; +//! Note that on Linux, we use webkit2gtk webviews so if the windowing library doesn't support gtk (as in [`winit`]) +//! you'll need to call [`gtk::init`] before creating the webview and then call [`gtk::main_iteration_do`] alongside +//! your windowing library event loop. //! -//! match event { -//! Event::NewEvents(StartCause::Init) => println!("Wry has started!"), -//! Event::WindowEvent { -//! event: WindowEvent::CloseRequested, -//! .. -//! } => *control_flow = ControlFlow::Exit, -//! _ => (), +//! ```no_run,ignore +//! use winit::{event_loop::EventLoop, window::Window}; +//! use wry::WebView; +//! +//! fn main() { +//! let event_loop = EventLoop::new().unwrap(); +//! gtk::init().unwrap(); // <----- IMPORTANT +//! let window = Window::new(&event_loop).unwrap(); +//! let webview = WebView::new(&window); +//! event_loop.run(|_e, _evl|{ +//! // process winit events +//! +//! // then advance gtk event loop <----- IMPORTANT +//! while gtk::events_pending() { +//! gtk::main_iteration_do(false); //! } -//! }); +//! }).unwrap(); //! } //! ``` //! //! ## Feature flags //! -//! Wry uses a set of feature flags to toggle several advanced features. `file-drop`, `protocol`, -//! are enabled by default. +//! Wry uses a set of feature flags to toggle several advanced features. //! -//! - `file-drop`: Enables [`with_file_drop_handler`] to control the behaviour when there are files -//! interacting with the window. Enabled by default. -//! - `protocol`: Enables [`with_custom_protocol`] to define custom URL scheme for handling tasks like -//! loading assets. Enabled by default. -//! This feature requires either `libayatana-appindicator` or `libappindicator` package installed. -//! You can still create those types if you disable it. They just don't create the actual objects. +//! - `protocol` (default): Enables [`WebViewBuilder::with_custom_protocol`] to define custom URL scheme for handling tasks like +//! loading assets. +//! - `file-drop` (default): Enables [`WebViewBuilder::with_file_drop_handler`] to control the behaviour when there are files +//! interacting with the window. //! - `devtools`: Enables devtools on release builds. Devtools are always enabled in debug builds. //! On **macOS**, enabling devtools, requires calling private apis so you should not enable this flag in release //! build if your app needs to publish to App Store. @@ -65,20 +144,14 @@ //! - `linux-body`: Enables body support of custom protocol request on Linux. Requires //! webkit2gtk v2.40 or above. //! -//! [`WebView`]: crate::webview::WebView -//! [`with_file_drop_handler`]: crate::webview::WebView::with_file_drop_handler -//! [`with_custom_protocol`]: crate::webview::WebView::with_custom_protocol +//! [`tao`]: https://docs.rs/tao +//! [`winit`]: https://docs.rs/winit #![allow(clippy::new_without_default)] -#![allow(clippy::wrong_self_convention)] +#![allow(clippy::default_constructed_unit_structs)] #![allow(clippy::type_complexity)] -#![allow(clippy::unit_cmp)] -#![allow(clippy::upper_case_acronyms)] #![cfg_attr(docsrs, feature(doc_cfg))] -pub use http; -pub use raw_window_handle; - #[cfg(any(target_os = "macos", target_os = "ios"))] #[macro_use] extern crate objc; @@ -129,19 +202,21 @@ use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; use std::{borrow::Cow, path::PathBuf, rc::Rc}; +use http::{Request, Response}; +use raw_window_handle::HasWindowHandle; + pub use error::*; +pub use http; pub use proxy::{ProxyConfig, ProxyEndpoint}; +pub use raw_window_handle; pub use url::Url; pub use web_context::WebContext; -use http::{Request, Response as HttpResponse}; -use raw_window_handle::HasWindowHandle; - /// Resolves a custom protocol [`Request`] asynchronously. /// /// See [`WebViewBuilder::with_asynchronous_custom_protocol`] for more information. pub struct RequestAsyncResponder { - pub(crate) responder: Box>)>, + pub(crate) responder: Box>)>, } // SAFETY: even though the webview bindings do not indicate the responder is Send, @@ -151,9 +226,9 @@ unsafe impl Send for RequestAsyncResponder {} impl RequestAsyncResponder { /// Resolves the request with the given response. - pub fn respond>>(self, response: HttpResponse) { + pub fn respond>>(self, response: Response) { let (parts, body) = response.into_parts(); - (self.responder)(HttpResponse::from_parts(parts, body.into())) + (self.responder)(Response::from_parts(parts, body.into())) } } @@ -218,7 +293,7 @@ pub struct WebViewAttributes { /// so we prepend them to each HTML head. They are only implemented on custom protocol URLs. pub initialization_scripts: Vec, - /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// A list of custom loading protocols with pairs of scheme uri string and a handling /// closure. /// /// The closure takes a [Request] and returns a [Response]. @@ -230,25 +305,21 @@ pub struct WebViewAttributes { /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the /// different Origin headers across platforms: /// - /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. - /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. + /// - macOS, iOS and Linux: `://` (so it will be `wry://path/to/page/`). + /// - Windows and Android: `http://.` by default (so it will be `http://wry.path/to/page). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. /// /// # Reading assets on mobile /// /// - Android: Android has `assets` and `resource` path finder to /// locate your files in those directories. For more information, see [Loading in-app content](https://developer.android.com/guide/webapps/load-local-content) page. /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. - /// - /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 pub custom_protocols: Vec<(String, Box>, RequestAsyncResponder)>)>, - /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. - /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. - /// - /// Both functions return promises but `notify()` resolves immediately. + /// The IPC handler to receive the message from Javascript on webview + /// using `window.ipc.postMessage("insert_message_here")` to host Rust code. pub ipc_handler: Option>, - /// Set a handler closure to process incoming [`FileDropEvent`] of the webview. + /// A handler closure to process incoming [`FileDropEvent`] of the webview. /// /// # Blocking OS Default Behavior /// Return `true` in the callback to block the OS' default behavior of handling a file drop. @@ -260,56 +331,56 @@ pub struct WebViewAttributes { #[cfg(not(feature = "file-drop"))] file_drop_handler: Option bool>>, - /// Set a navigation handler to decide if incoming url is allowed to navigate. + /// A navigation handler to decide if incoming url is allowed to navigate. /// - /// The closure take a `String` parameter as url and return `bool` to determine the url. True is - /// allow to navigate and false is not. + /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen. + /// `true` allows to navigate and `false` does not. pub navigation_handler: Option bool>>, - /// Set a download started handler to manage incoming downloads. + /// A download started handler to manage incoming downloads. /// - /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the + /// The closure takes two parameters, the first is a `String` representing the url being downloaded from and and the /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter - /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be + /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be /// absolute. The closure returns a `bool` to allow or deny the download. pub download_started_handler: Option bool>>, - /// Sets a download completion handler to manage downloads that have finished. + /// A download completion handler to manage downloads that have finished. /// /// The closure is fired when the download completes, whether it was successful or not. /// The closure takes a `String` representing the URL of the original download request, an `Option` /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download - /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to /// know if the download succeeded. /// /// ## Platform-specific: /// - /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API - /// limitations. + /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty, + /// due to API limitations. pub download_completed_handler: Option, bool) + 'static>>, - /// Set a new window handler to decide if incoming url is allowed to open in a new window. + /// A new window handler to decide if incoming url is allowed to open in a new window. /// - /// The closure take a `String` parameter as url and return `bool` to determine the url. True is - /// allow to navigate and false is not. + /// The closure take a `String` parameter as url and return `bool` to determine whether the window should open. + /// `true` allows to open and `false` does not. pub new_window_req_handler: Option bool>>, /// Enables clipboard access for the page rendered on **Linux** and **Windows**. /// - /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu - /// item accelerators to use shortcuts. + /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu + /// item accelerators to use the clipboard shortcuts. pub clipboard: bool, - /// Enable web inspector which is usually called dev tool. + /// Enable web inspector which is usually called browser devtools. /// - /// Note this only enables dev tool to the webview. To open it, you can call + /// Note this only enables devtools to the webview. To open it, you can call /// [`WebView::open_devtools`], or right click the page and open it from the context menu. /// /// ## Platform-specific /// - /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, - /// but requires `devtools` feature flag to actually enable it in **release** build. + /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds, + /// but requires `devtools` feature flag to actually enable it in **release** builds. /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. pub devtools: bool, @@ -358,12 +429,14 @@ pub struct WebViewAttributes { /// - **macOS / Android / iOS:** Unsupported. pub focused: bool, - /// Set the postion if the webview is created by `new_as_child`. If it's `None`, the position - /// will be (0, 0). + /// The webview postion. + /// This is effective if the webview was created by [`WebView::new_as_child`]. + /// If it's `None`, the position will be (0, 0). pub position: Option<(i32, i32)>, - /// Set the size if the webview is created by `new_as_child`. If it's `None`, the size - /// will be (0, 0). + /// The webview size. + /// This is effective if the webview was created by [`WebView::new_as_child`]. + /// If it's `None`, the size will be (0, 0). pub size: Option<(u32, u32)>, } @@ -405,31 +478,6 @@ impl Default for WebViewAttributes { } } -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "macos", - target_os = "ios", -))] -#[derive(Default)] -pub(crate) struct PlatformSpecificWebViewAttributes(); - -/// Type alias for a color in the RGBA format. -/// -/// Each value can be 0..255 inclusive. -pub type RGBA = (u8, u8, u8, u8); - -/// Type of of page loading event -pub enum PageLoadEvent { - /// Indicates that the content of the page has started loading - Started, - /// Indicates that the page content has finished loading - Finished, -} - /// Builder type of [`WebView`]. /// /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and @@ -452,13 +500,21 @@ pub struct WebViewBuilder<'a> { } impl<'a> WebViewBuilder<'a> { - /// Create [`WebViewBuilder`] from provided [`RawWindowHandle`]. + /// Create a [`WebViewBuilder`] from a type that implements [`HasWindowHandle`]. /// - /// ## Platform-specific + /// # Platform-specific: + /// + /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilder::new_gtk`]. + /// + /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk + /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. + /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// - /// - **Linux**: This window handle must be created from a GTK window. Only x11 - /// is supported. This method won't work on wayland. - pub fn new(window: &'a W) -> Self { + /// # Panics: + /// + /// - Panics if the provided handle was not supported or invalid. + /// - Panics on Linux, if [`gtk::init`] was not called in this thread. + pub fn new(window: &'a impl HasWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -477,19 +533,26 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. - /// Please read platform specific notes to know how it actually works on different platform. - /// + /// Create [`WebViewBuilder`] as a child window inside the provided [`HasWindowHandle`]. /// /// ## Platform-specific /// /// - **Windows**: This will create the webview as a child window of the `parent` window. /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's /// content view. - /// - **Linux**: This will create the webview as a child window of the `parent` window. Only x11 - /// is supported. This method won't work on wayland. + /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11 + /// is supported. This method won't work on Wayland. + /// + /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk + /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. + /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// - **Android/iOS:** Unsupported. - pub fn new_as_child(parent: &'a W) -> Self { + /// + /// # Panics: + /// + /// - Panics if the provided handle was not support or invalid. + /// - Panics on Linux, if [`gtk::init`] was not called in this thread. + pub fn new_as_child(parent: &'a impl HasWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(parent), @@ -516,6 +579,10 @@ impl<'a> WebViewBuilder<'a> { target_os = "openbsd", ))] /// Create the webview from a GTK container widget, such as GTK window. + /// + /// # Panics: + /// + /// - Panics if [`gtk::init`] was not called in this thread. pub fn new_gtk(widget: &'a W) -> Self where W: gtk::prelude::IsA, @@ -568,7 +635,7 @@ impl<'a> WebViewBuilder<'a> { self } - /// Sets whether the WebView should be transparent. + /// Sets whether the WebView should be visible or not. pub fn with_visible(mut self, visible: bool) -> Self { self.attrs.visible = visible; self @@ -595,7 +662,7 @@ impl<'a> WebViewBuilder<'a> { self } - /// Register custom file loading protocols with pairs of scheme uri string and a handling + /// Register custom loading protocols with pairs of scheme uri string and a handling /// closure. /// /// The closure takes a [Request] and returns a [Response] @@ -607,8 +674,8 @@ impl<'a> WebViewBuilder<'a> { /// if you wish to send requests with native `fetch` and `XmlHttpRequest` APIs. Here are the /// different Origin headers across platforms: /// - /// - macOS, iOS and Linux: `://` (so it will be `wry://examples` in `custom_protocol` example). On Linux, You need to enable `linux-headers` feature flag. - /// - Windows and Android: `http://.` by default (so it will be `http://wry.examples` in `custom_protocol` example). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. + /// - macOS, iOS and Linux: `://` (so it will be `wry://path/to/page). + /// - Windows and Android: `http://.` by default (so it will be `http://wry.path/to/page`). To use `https` instead of `http`, use [`WebViewBuilderExtWindows::with_https_scheme`] and [`WebViewBuilderExtAndroid::with_https_scheme`]. /// /// # Reading assets on mobile /// @@ -618,12 +685,10 @@ impl<'a> WebViewBuilder<'a> { /// elsewhere in Android (provided the app has appropriate access), but not from the `assets` /// folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux. /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. - /// - /// [bug]: https://bugs.webkit.org/show_bug.cgi?id=229034 #[cfg(feature = "protocol")] pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where - F: Fn(Request>) -> HttpResponse> + 'static, + F: Fn(Request>) -> Response> + 'static, { self.attrs.custom_protocols.push(( name, @@ -640,17 +705,18 @@ impl<'a> WebViewBuilder<'a> { /// # Examples /// /// ```no_run - /// use tao::{ - /// event_loop::EventLoop, - /// window::WindowBuilder - /// }; - /// use raw_window_handle::HasRawWindowHandle; /// use wry::WebViewBuilder; /// - /// let event_loop = EventLoop::new(); - /// let window = WindowBuilder::new() - /// .build(&event_loop) - /// .unwrap(); + /// # use raw_window_handle as rwh_06; + /// # struct T; + /// # impl rwh_06::HasWindowHandle for T { + /// # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { + /// # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( + /// # rwh_06::Win32WindowHandle::new(1), + /// # ))) + /// # } + /// # } + /// # let window = T; /// WebViewBuilder::new(&window) /// .with_asynchronous_custom_protocol("wry".into(), |request, responder| { /// // here you can use a tokio task, thread pool or anything @@ -671,8 +737,8 @@ impl<'a> WebViewBuilder<'a> { self } - /// Set the IPC handler to receive the message from Javascript on webview to host Rust code. - /// The message sent from webview should call `window.ipc.postMessage("insert_message_here");`. + /// Set the IPC handler to receive the message from Javascript on webview + /// using `window.ipc.postMessage("insert_message_here")` to host Rust code. pub fn with_ipc_handler(mut self, handler: F) -> Self where F: Fn(String) + 'static, @@ -697,24 +763,24 @@ impl<'a> WebViewBuilder<'a> { self } - /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. The provided URL must be valid. + /// Load the provided URL with given headers when the builder calling [`WebViewBuilder::build`] to create the [`WebView`]. + /// The provided URL must be valid. pub fn with_url_and_headers(mut self, url: &str, headers: http::HeaderMap) -> Result { self.attrs.url = Some(url.parse()?); self.attrs.headers = Some(headers); Ok(self) } - /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. The provided URL must be valid. + /// Load the provided URL when the builder calling [`WebViewBuilder::build`] to create the [`WebView`]. + /// The provided URL must be valid. pub fn with_url(mut self, url: &str) -> Result { self.attrs.url = Some(Url::parse(url)?); self.attrs.headers = None; Ok(self) } - /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the - /// [`WebView`]. This will be ignored if `url` is provided. + /// Load the provided HTML string when the builder calling [`WebViewBuilder::build`] to create the [`WebView`]. + /// This will be ignored if `url` is provided. /// /// # Warning /// @@ -728,7 +794,7 @@ impl<'a> WebViewBuilder<'a> { Ok(self) } - /// Set the web context that can share with multiple [`WebView`]s. + /// Set the web context that can be shared with multiple [`WebView`]s. pub fn with_web_context(mut self, web_context: &'a mut WebContext) -> Self { self.web_context = Some(web_context); self @@ -740,15 +806,15 @@ impl<'a> WebViewBuilder<'a> { self } - /// Enable or disable web inspector which is usually called dev tool. + /// Enable or disable web inspector which is usually called devtools. /// - /// Note this only enables dev tool to the webview. To open it, you can call + /// Note this only enables devtools to the webview. To open it, you can call /// [`WebView::open_devtools`], or right click the page and open it from the context menu. /// /// ## Platform-specific /// - /// - macOS: This will call private functions on **macOS**. It's still enabled if set in **debug** build on mac, - /// but requires `devtools` feature flag to actually enable it in **release** build. + /// - macOS: This will call private functions on **macOS**. It is enabled in **debug** builds, + /// but requires `devtools` feature flag to actually enable it in **release** builds. /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android. /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window. pub fn with_devtools(mut self, devtools: bool) -> Self { @@ -768,8 +834,8 @@ impl<'a> WebViewBuilder<'a> { /// Set a navigation handler to decide if incoming url is allowed to navigate. /// - /// The closure takes a `String` parameter as url and return `bool` to determine the url. True is - /// allowed to navigate and false is not. + /// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen. + /// `true` allows to navigate and `false` does not. pub fn with_navigation_handler(mut self, callback: impl Fn(String) -> bool + 'static) -> Self { self.attrs.navigation_handler = Some(Box::new(callback)); self @@ -777,9 +843,9 @@ impl<'a> WebViewBuilder<'a> { /// Set a download started handler to manage incoming downloads. /// - /// The closure takes two parameters - the first is a `String` representing the url being downloaded from and and the + //// The closure takes two parameters, the first is a `String` representing the url being downloaded from and and the /// second is a mutable `PathBuf` reference that (possibly) represents where the file will be downloaded to. The latter - /// parameter can be used to set the download location by assigning a new path to it - the assigned path _must_ be + /// parameter can be used to set the download location by assigning a new path to it, the assigned path _must_ be /// absolute. The closure returns a `bool` to allow or deny the download. pub fn with_download_started_handler( mut self, @@ -795,13 +861,13 @@ impl<'a> WebViewBuilder<'a> { /// The closure takes a `String` representing the URL of the original download request, an `Option` /// potentially representing the filesystem path the file was downloaded to, and a `bool` indicating if the download /// succeeded. A value of `None` being passed instead of a `PathBuf` does not necessarily indicate that the download - /// did not succeed, and may instead indicate some other failure - always check the third parameter if you need to + /// did not succeed, and may instead indicate some other failure, always check the third parameter if you need to /// know if the download succeeded. /// /// ## Platform-specific: /// - /// - **macOS**: The second parameter indicating the path the file was saved to is always empty, due to API - /// limitations. + /// - **macOS**: The second parameter indicating the path the file was saved to, is always empty, + /// due to API limitations. pub fn with_download_completed_handler( mut self, download_completed_handler: impl Fn(String, Option, bool) + 'static, @@ -812,8 +878,8 @@ impl<'a> WebViewBuilder<'a> { /// Enables clipboard access for the page rendered on **Linux** and **Windows**. /// - /// macOS doesn't provide such method and is always enabled by default. But you still need to add menu - /// item accelerators to use shortcuts. + /// macOS doesn't provide such method and is always enabled by default. But your app will still need to add menu + /// item accelerators to use the clipboard shortcuts. pub fn with_clipboard(mut self, clipboard: bool) -> Self { self.attrs.clipboard = clipboard; self @@ -821,9 +887,8 @@ impl<'a> WebViewBuilder<'a> { /// Set a new window request handler to decide if incoming url is allowed to be opened. /// - /// The closure takes a `String` parameter as url and return `bool` to determine if the url can be - /// opened in a new window. Returning true will open the url in a new window, whilst returning false - /// will neither open a new window nor allow any navigation. + /// The closure take a `String` parameter as url and return `bool` to determine whether the window should open. + /// `true` allows to open and `false` does not. pub fn with_new_window_req_handler( mut self, callback: impl Fn(String) -> bool + 'static, @@ -863,8 +928,6 @@ impl<'a> WebViewBuilder<'a> { } /// Set a handler to process page loading events. - /// - /// The handler will be called when the webview begins the indicated loading event. pub fn with_on_page_load_handler( mut self, handler: impl Fn(PageLoadEvent, String) + 'static, @@ -893,15 +956,13 @@ impl<'a> WebViewBuilder<'a> { self } - /// Set the postion if the webview is created by `new_as_child`. If it's `None`, the position - /// will be (0, 0). + /// Set the webview position relative to its parent if it was created as a child. pub fn with_position(mut self, position: (i32, i32)) -> Self { self.attrs.position = Some(position); self } - /// Set the size if the webview is created by `new_as_child`. If it's `None`, the size - /// will be (0, 0). + /// Set the webview size if it was created as a child. pub fn with_size(mut self, size: (u32, u32)) -> Self { self.attrs.size = Some(size); self @@ -909,12 +970,10 @@ impl<'a> WebViewBuilder<'a> { /// Consume the builder and create the [`WebView`]. /// - /// Platform-specific behavior: + /// # Panics: /// - /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be - /// called in the same thread with the [`EventLoop`] you create. - /// - /// [`EventLoop`]: crate::application::event_loop::EventLoop + /// - Panics if the provided handle was not support or invalid. + /// - Panics on Linux, if [`gtk::init`] was not called in this thread. pub fn build(self) -> Result { let webview = if let Some(window) = &self.window { if self.as_child { @@ -995,10 +1054,10 @@ pub trait WebViewBuilderExtWindows { /// Defaults to [`Theme::Auto`] which will follow the OS defaults. fn with_theme(self, theme: Theme) -> Self; - /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. + /// Determines whether the custom protocols should use `https://.path/to/page` instead of the default `http://.path/to/page`. /// /// Using a `http` scheme will allow mixed content when trying to fetch `http` endpoints - /// and is therefore less secure but will match the behavior of the `://localhost` protocols used on macOS and Linux. + /// and is therefore less secure but will match the behavior of the `://path/to/page` protocols used on macOS and Linux. /// /// The default value is `false`. fn with_https_scheme(self, enabled: bool) -> Self; @@ -1098,7 +1157,7 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { self.attrs.custom_protocols.push(( protocol.clone(), Box::new(|_, api| { - api.respond(HttpResponse::builder().body(Vec::new()).unwrap()); + api.respond(Response::builder().body(Vec::new()).unwrap()); }), )); self.platform_specific.with_asset_loader = true; @@ -1116,41 +1175,54 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { /// /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and /// scripts for those who prefer to control fine grained window creation and event handling. -/// [`WebView`] presents the actual WebView window and let you still able to perform actions -/// during event handling to it. +/// [`WebView`] presents the actual WebView window and let you still able to perform actions on it. pub struct WebView { webview: InnerWebView, } impl WebView { - /// Create a [`WebView`] from provided [`RawWindowHandle`]. Note that calling this directly loses + /// Create a [`WebView`] from from a type that implements [`HasWindowHandle`]. + /// Note that calling this directly loses /// abilities to initialize scripts, add ipc handler, and many more before starting WebView. To /// benefit from above features, create a [`WebViewBuilder`] instead. /// - /// Platform-specific behavior: + /// # Platform-specific: + /// + /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebView::new_gtk`]. /// - /// - **Unix:** This method must be called in a gtk thread. Usually this means it should be - /// called in the same thread with the [`EventLoop`] you create. + /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk + /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. + /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// - /// [`EventLoop`]: crate::application::event_loop::EventLoop - pub fn new(window: &W) -> Result { + /// # Panics: + /// + /// - Panics if the provided handle was not supported or invalid. + /// - Panics on Linux, if [`gtk::init`] was not called in this thread. + pub fn new(window: &impl HasWindowHandle) -> Result { WebViewBuilder::new(window).build() } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. - /// Please read platform specific notes to know how it actually works on different platform. - /// + /// Create [`WebViewBuilder`] as a child window inside the provided [`HasWindowHandle`]. /// /// ## Platform-specific /// /// - **Windows**: This will create the webview as a child window of the `parent` window. /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's /// content view. - /// - **Linux**: This will create the webview as a child window of the `parent` window. Only x11 - /// is supported. This method won't work on wayland. + /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11 + /// is supported. This method won't work on Wayland. + /// + /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk + /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. + /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// - **Android/iOS:** Unsupported. - pub fn new_as_child(window: &W) -> Result { - WebViewBuilder::new_as_child(window).build() + /// + /// # Panics: + /// + /// - Panics if the provided handle was not support or invalid. + /// - Panics on Linux, if [`gtk::init`] was not called in this thread. + pub fn new_as_child(parent: &impl HasWindowHandle) -> Result { + WebViewBuilder::new_as_child(parent).build() } #[cfg(any( @@ -1161,9 +1233,12 @@ impl WebView { target_os = "openbsd", ))] /// Create the webview from a GTK container widget, such as GTK window. + /// + /// # Panics: + /// + /// - Panics if [`gtk::init`] was not called in this thread. pub fn new_gtk(widget: &W) -> Result where - W: gtk::prelude::IsA, W: gtk::prelude::IsA, { WebViewBuilder::new_gtk(widget).build() @@ -1174,11 +1249,7 @@ impl WebView { self.webview.url() } - /// Evaluate and run javascript code. Must be called on the same thread who created the - /// [`WebView`]. Use [`EventLoopProxy`] and a custom event to send scripts from other threads. - /// - /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy - /// + /// Evaluate and run javascript code. pub fn evaluate_script(&self, js: &str) -> Result<()> { self .webview @@ -1186,11 +1257,7 @@ impl WebView { } /// Evaluate and run javascript code with callback function. The evaluation result will be - /// serialized into a JSON string and passed to the callback function. Must be called on the - /// same thread who created the [`WebView`]. Use [`EventLoopProxy`] and a custom event to - /// send scripts from other threads. - /// - /// [`EventLoopProxy`]: crate::application::event_loop::EventLoopProxy + /// serialized into a JSON string and passed to the callback function. /// /// Exception is ignored because of the limitation on windows. You can catch it yourself and return as string as a workaround. /// @@ -1279,18 +1346,23 @@ impl WebView { self.webview.clear_all_browsing_data() } - /// Set the webview position if it's a child window. + /// Set the webview position relative to its parent if it was created as a child. pub fn set_position(&self, position: (i32, i32)) { self.webview.set_position(position) } - /// Set the webview size if it's a child window. + /// Set the webview size if it was created as a child. pub fn set_size(&self, size: (u32, u32)) { self.webview.set_size(size) } + + /// Shows or hides the webview. + pub fn set_visible(&self, visible: bool) { + self.webview.set_visible(visible) + } } -/// An event enumeration sent to [`FileDropHandler`]. +/// An event describing the files drop on the webview. #[non_exhaustive] #[derive(Debug, serde::Serialize, Clone)] pub enum FileDropEvent { @@ -1409,13 +1481,42 @@ impl WebviewExtAndroid for WebView { } } +/// Webview theme. #[derive(Debug, Clone, Copy)] pub enum Theme { + /// Dark Dark, + /// Light Light, + /// System preference Auto, } +/// Type alias for a color in the RGBA format. +/// +/// Each value can be 0..255 inclusive. +pub type RGBA = (u8, u8, u8, u8); + +/// Type of of page loading event +pub enum PageLoadEvent { + /// Indicates that the content of the page has started loading + Started, + /// Indicates that the page content has finished loading + Finished, +} + +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios", +))] +#[derive(Default)] +pub(crate) struct PlatformSpecificWebViewAttributes; + #[cfg(test)] mod tests { use super::*; diff --git a/src/web_context.rs b/src/web_context.rs index d66b7f82b..2266b350a 100644 --- a/src/web_context.rs +++ b/src/web_context.rs @@ -23,7 +23,7 @@ use std::path::{Path, PathBuf}; /// some actions like custom protocol on Mac. Please keep both instances when you still wish to /// interact with them. /// -/// [`WebView`]: crate::webview::WebView +/// [`WebView`]: crate::WebView #[derive(Debug)] pub struct WebContext { data: WebContextData, diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index f6b8a2e2f..7edf09bde 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use gdkx11::X11Window; +use gdkx11::glib::translate::{FromGlibPtrFull, ToGlibPtr}; +use gdkx11::X11Display; use gtk::gdk::{self, EventMask}; use gtk::gio::Cancellable; -use gtk::{glib, prelude::*}; +use gtk::prelude::*; use javascriptcore::ValueExt; use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; #[cfg(any(debug_assertions, feature = "devtools"))] @@ -97,8 +98,8 @@ impl InnerWebView { }; let gdk_display = gdk::Display::default().ok_or(Error::X11DisplayNotFound)?; - let gx11_display = gdk_display.downcast_ref().unwrap(); - let raw = glib::translate::ToGlibPtr::to_glib_none(&gx11_display).0; + let gx11_display: &X11Display = gdk_display.downcast_ref().unwrap(); + let raw = gx11_display.to_glib_none().0; let display = unsafe { gdkx11::ffi::gdk_x11_display_get_xdisplay(raw) }; let window = if is_child { @@ -115,13 +116,18 @@ impl InnerWebView { 0, ) }; + // if attributes.visible { unsafe { (xlib.XMapRaised)(display as _, child) }; + // } child } else { window }; - let gdk_window: gdk::Window = X11Window::foreign_new_for_display(gx11_display, window).upcast(); + let gdk_window = unsafe { + let raw = gdkx11::ffi::gdk_x11_window_foreign_new_for_display(raw, window); + gdk::Window::from_glib_full(raw) + }; let gtk_window = gtk::Window::new(gtk::WindowType::Toplevel); gtk_window.connect_realize(move |widget| widget.set_window(gdk_window.clone())); gtk_window.set_has_window(true); @@ -355,7 +361,9 @@ impl InnerWebView { file_drop::connect_drag_event(webview.clone(), file_drop_handler); } - webview.show_all(); + if attributes.visible { + webview.show_all(); + } #[cfg(any(debug_assertions, feature = "devtools"))] let is_inspector_open = { @@ -607,6 +615,31 @@ impl InnerWebView { } } } + + pub fn set_visible(&self, visible: bool) { + if visible { + if self.is_child { + let xlib = self.xlib.as_ref().unwrap(); + unsafe { + (xlib.XMapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()); + } + self.gtk_window.as_ref().unwrap().show_all() + } + + self.webview.show_all() + } else { + if self.is_child { + let xlib = self.xlib.as_ref().unwrap(); + unsafe { + (xlib.XUnmapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()); + } + + self.gtk_window.as_ref().unwrap().hide() + } + + self.webview.hide() + } + } } pub fn platform_webview_version() -> Result { diff --git a/src/webkitgtk/web_context.rs b/src/webkitgtk/web_context.rs index a2256065e..7b54bbdb9 100644 --- a/src/webkitgtk/web_context.rs +++ b/src/webkitgtk/web_context.rs @@ -325,11 +325,7 @@ where #[cfg(feature = "linux-body")] { - use gtk::{ - gdk::prelude::{InputStreamExt, InputStreamExtManual}, - gio::Cancellable, - glib::Bytes, - }; + use gtk::{gdk::prelude::InputStreamExtManual, gio::Cancellable}; // Set request http body let cancellable: Option<&Cancellable> = None; diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 6dc685966..f7e00aa9e 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -116,12 +116,17 @@ impl InnerWebView { unsafe { RegisterClassExW(&class) }; + let mut flags = WS_CHILD; + if attributes.visible { + flags |= WS_VISIBLE; + } + let child = unsafe { CreateWindowExW( WINDOW_EX_STYLE::default(), PCWSTR::from_raw(class_name.as_ptr()), PCWSTR::null(), - WS_CHILD | WS_VISIBLE, + flags, attributes.position.map(|a| a.0).unwrap_or(CW_USEDEFAULT), attributes.position.map(|a| a.1).unwrap_or(CW_USEDEFAULT), attributes.size.map(|a| a.0 as i32).unwrap_or(CW_USEDEFAULT), @@ -873,7 +878,7 @@ impl InnerWebView { unsafe { controller - .SetIsVisible(true) + .SetIsVisible(attributes.visible) .map_err(webview2_com::Error::WindowsError)?; if attributes.focused { controller diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index e2ec9447b..ded7f9a16 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -1087,6 +1087,10 @@ r#"Object.defineProperty(window, 'ipc', { } } } + + pub fn set_visible(&self, visible: bool) { + // Unimplemented + } } pub fn url_from_webview(webview: id) -> String { From 26074247d51b67ceebd38d08a164e30c40eecc50 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 19:33:54 +0300 Subject: [PATCH 34/80] fix set_visible --- .github/workflows/{fmt.yml => clippy-fmt.yml} | 2 +- src/android/mod.rs | 2 +- src/webkitgtk/mod.rs | 40 +++++++++---------- src/webview2/mod.rs | 4 ++ src/wkwebview/mod.rs | 2 +- 5 files changed, 26 insertions(+), 24 deletions(-) rename .github/workflows/{fmt.yml => clippy-fmt.yml} (93%) diff --git a/.github/workflows/fmt.yml b/.github/workflows/clippy-fmt.yml similarity index 93% rename from .github/workflows/fmt.yml rename to .github/workflows/clippy-fmt.yml index cfe706464..1d297d787 100644 --- a/.github/workflows/fmt.yml +++ b/.github/workflows/clippy-fmt.yml @@ -35,7 +35,7 @@ jobs: with: components: clippy - - run: cargo clippy --all-targets --all-features -- -D warnings + - run: cargo clippy --all-targets --all-features fmt: runs-on: ubuntu-latest diff --git a/src/android/mod.rs b/src/android/mod.rs index a6a301ed1..2bd43af31 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -326,7 +326,7 @@ impl InnerWebView { // Unsupported. } - pub fn set_visible(&self, visible: bool) { + pub fn set_visible(&self, _visible: bool) { // Unsupported } } diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 7edf09bde..951d8ccdf 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -116,9 +116,9 @@ impl InnerWebView { 0, ) }; - // if attributes.visible { - unsafe { (xlib.XMapRaised)(display as _, child) }; - // } + if attributes.visible { + unsafe { (xlib.XMapWindow)(display as _, child) }; + } child } else { window @@ -136,8 +136,12 @@ impl InnerWebView { let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); gtk_window.add(&vbox); + let should_show = attributes.visible; + Self::new_gtk(&vbox, attributes, pl_attrs, web_context).map(|mut w| { - gtk_window.show_all(); + if should_show { + gtk_window.show_all(); + } w.is_child = is_child; w.xlib = Some(xlib); @@ -617,27 +621,21 @@ impl InnerWebView { } pub fn set_visible(&self, visible: bool) { - if visible { - if self.is_child { - let xlib = self.xlib.as_ref().unwrap(); - unsafe { - (xlib.XMapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()); - } + if self.is_child { + let xlib = self.xlib.as_ref().unwrap(); + if visible { + unsafe { (xlib.XMapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; self.gtk_window.as_ref().unwrap().show_all() + } else { + self.gtk_window.as_ref().unwrap().hide(); + unsafe { (xlib.XUnmapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; } + } - self.webview.show_all() + if visible { + self.webview.show_all(); } else { - if self.is_child { - let xlib = self.xlib.as_ref().unwrap(); - unsafe { - (xlib.XUnmapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()); - } - - self.gtk_window.as_ref().unwrap().hide() - } - - self.webview.hide() + self.webview.hide(); } } } diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index f7e00aa9e..126191b51 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1026,6 +1026,10 @@ impl InnerWebView { } } } + + pub fn set_visible(&self, _visible: bool) { + // Unimplemented + } } unsafe fn prepare_web_request_response( diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index ded7f9a16..62ba444e1 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -1088,7 +1088,7 @@ r#"Object.defineProperty(window, 'ipc', { } } - pub fn set_visible(&self, visible: bool) { + pub fn set_visible(&self, _visible: bool) { // Unimplemented } } From 8d1ba861b824a8c2eb521ddb49c0ef00e7b1472d Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 19:38:54 +0300 Subject: [PATCH 35/80] impl set_visible on Windows --- src/webview2/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 126191b51..7fc017c85 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1028,7 +1028,17 @@ impl InnerWebView { } pub fn set_visible(&self, _visible: bool) { - // Unimplemented + if self.is_child { + ShowWindow( + self.hwnd, + match visible { + true => SW_SHOW, + false => SW_HIDE, + }, + ); + } + + let _ = self.controller.SetIsVisible(visible); } } From 91a498b32b90abb32738497b13c1a0adbfd5a9d4 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 19:43:47 +0300 Subject: [PATCH 36/80] fix doctests --- src/lib.rs | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7fd007333..ddee641f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,10 +20,10 @@ //! # use raw_window_handle as rwh_06; //! # struct T; //! # impl rwh_06::HasWindowHandle for T { -//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { -//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( -//! # rwh_06::Win32WindowHandle::new(1), -//! # ))) +//! # fn window_handle(&self) -> Result, rwh_06::HandleError> { +//! # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( +//! # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), +//! # )) }) //! # } //! # } //! # let window = T; @@ -36,19 +36,9 @@ //! //! If you also want to support Wayland too, then we recommend you use [`WebViewBuilder::new_gtk`] on Linux. //! -//! ```no_run +//! ```no_run,ignore //! use wry::WebViewBuilder; //! -//! # use raw_window_handle as rwh_06; -//! # struct T; -//! # impl rwh_06::HasWindowHandle for T { -//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { -//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( -//! # rwh_06::Win32WindowHandle::new(1), -//! # ))) -//! # } -//! # } -//! # let window = T; //! #[cfg(any( //! target_os = "windows", //! target_os = "macos", @@ -64,7 +54,7 @@ //! )))] //! let builder = { //! use tao::platform::unix::WindowExtUnix; -//! WebViewBuilder::new_gtk(>k_window) +//! WebViewBuilder::new_gtk(&window.gtk_window()) //! }; //! //! let webview = builder @@ -85,10 +75,10 @@ //! # use raw_window_handle as rwh_06; //! # struct T; //! # impl rwh_06::HasWindowHandle for T { -//! # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { -//! # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( -//! # rwh_06::Win32WindowHandle::new(1), -//! # ))) +//! # fn window_handle(&self) -> Result, rwh_06::HandleError> { +//! # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( +//! # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), +//! # )) }) //! # } //! # } //! # let window = T; @@ -710,10 +700,10 @@ impl<'a> WebViewBuilder<'a> { /// # use raw_window_handle as rwh_06; /// # struct T; /// # impl rwh_06::HasWindowHandle for T { - /// # fn window_handle(&self) rwh_06::Result, rwh_06::HandleError> { - /// # Ok(rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( - /// # rwh_06::Win32WindowHandle::new(1), - /// # ))) + /// # fn window_handle(&self) -> Result, rwh_06::HandleError> { + /// # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( + /// # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), + /// # )) }) /// # } /// # } /// # let window = T; From 1e595c73155aa3755b7d7dcddd21d938390bfe16 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 20:09:43 +0300 Subject: [PATCH 37/80] more set_visible fixes --- src/webkitgtk/mod.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 951d8ccdf..4dad5c792 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -139,16 +139,21 @@ impl InnerWebView { let should_show = attributes.visible; Self::new_gtk(&vbox, attributes, pl_attrs, web_context).map(|mut w| { - if should_show { - gtk_window.show_all(); - } - w.is_child = is_child; w.xlib = Some(xlib); w.display = Some(gdk_display); w.x11_display = Some(display as _); w.x11_window = Some(window); w.gtk_window = Some(gtk_window); + + // this an ugly workaround, for some reasons, when the webview + // is starting as hidden, we need to call set_visible multiple times + // before it can work correctly + w.set_visible(true); + if !should_show { + w.set_visible(false); + } + w }) } @@ -365,10 +370,6 @@ impl InnerWebView { file_drop::connect_drag_event(webview.clone(), file_drop_handler); } - if attributes.visible { - webview.show_all(); - } - #[cfg(any(debug_assertions, feature = "devtools"))] let is_inspector_open = { let is_inspector_open = Arc::new(AtomicBool::default()); @@ -625,13 +626,19 @@ impl InnerWebView { let xlib = self.xlib.as_ref().unwrap(); if visible { unsafe { (xlib.XMapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; - self.gtk_window.as_ref().unwrap().show_all() } else { - self.gtk_window.as_ref().unwrap().hide(); unsafe { (xlib.XUnmapWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; } } + if let Some(window) = &self.gtk_window { + if visible { + window.show_all(); + } else { + window.hide(); + } + } + if visible { self.webview.show_all(); } else { From 67c0ae8d81862b799dab1a9b9c9c60c5c85fd003 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 20:10:26 +0300 Subject: [PATCH 38/80] fix windows --- src/webview2/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 7fc017c85..82903fafe 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -30,9 +30,9 @@ use windows::{ Shell::{DefSubclassProc, SHCreateMemStream, SetWindowSubclass}, WindowsAndMessaging::{ self as win32wm, CreateWindowExW, DefWindowProcW, PostMessageW, RegisterClassExW, - RegisterWindowMessageA, SetWindowPos, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, HCURSOR, - HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, - WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, WS_VISIBLE, + RegisterWindowMessageA, SetWindowPos, ShowWindow, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, + HCURSOR, HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, + SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, WS_VISIBLE, }, }, }, @@ -1027,7 +1027,7 @@ impl InnerWebView { } } - pub fn set_visible(&self, _visible: bool) { + pub fn set_visible(&self, visible: bool) { if self.is_child { ShowWindow( self.hwnd, From 0f43154d807511dd3e371daf01e05d44249ae87a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 20:15:06 +0300 Subject: [PATCH 39/80] unsafe --- src/webview2/mod.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 82903fafe..185a4b0bc 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1029,13 +1029,15 @@ impl InnerWebView { pub fn set_visible(&self, visible: bool) { if self.is_child { - ShowWindow( - self.hwnd, - match visible { - true => SW_SHOW, - false => SW_HIDE, - }, - ); + unsafe { + ShowWindow( + self.hwnd, + match visible { + true => SW_SHOW, + false => SW_HIDE, + }, + ); + } } let _ = self.controller.SetIsVisible(visible); From 3e38519bb25dd3bf9882257217f85b7a8b1ef028 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Wed, 25 Oct 2023 20:27:25 +0300 Subject: [PATCH 40/80] fix set size for child webviews --- src/webkitgtk/mod.rs | 45 +++++++++++++++++--------------------------- src/webview2/mod.rs | 8 ++++---- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 4dad5c792..7ebb11f62 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -585,39 +585,28 @@ impl InnerWebView { pub fn set_position(&self, position: (i32, i32)) { if self.is_child { - let xlib = self.xlib.as_ref().unwrap(); - let mut changes = XWindowChanges { - x: position.0, - y: position.1, - ..unsafe { std::mem::zeroed() } - }; - unsafe { - (xlib.XConfigureWindow)( - self.x11_display.unwrap() as _, - self.x11_window.unwrap(), - (CWY | CWX) as _, - &mut changes as *mut _, - ); - } + self + .gtk_window + .as_ref() + .unwrap() + .move_(position.0, position.1); } } pub fn set_size(&self, size: (u32, u32)) { if self.is_child { - let xlib = self.xlib.as_ref().unwrap(); - let mut changes = XWindowChanges { - width: size.0 as _, - height: size.1 as _, - ..unsafe { std::mem::zeroed() } - }; - unsafe { - (xlib.XConfigureWindow)( - self.x11_display.unwrap() as _, - self.x11_window.unwrap(), - (CWWidth | CWHeight) as _, - &mut changes as *mut _, - ); - } + self + .gtk_window + .as_ref() + .unwrap() + .window() + .unwrap() + .resize(size.0 as _, size.1 as _); + self + .gtk_window + .as_ref() + .unwrap() + .size_allocate(>k::Allocation::new(200, 200, size.0 as _, size.1 as _)); } } diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 185a4b0bc..7b0680f31 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1028,8 +1028,8 @@ impl InnerWebView { } pub fn set_visible(&self, visible: bool) { - if self.is_child { - unsafe { + unsafe { + if self.is_child { ShowWindow( self.hwnd, match visible { @@ -1038,9 +1038,9 @@ impl InnerWebView { }, ); } - } - let _ = self.controller.SetIsVisible(visible); + let _ = self.controller.SetIsVisible(visible); + } } } From f63ce346ffa6822bc62a7ac371bd6aa01d447220 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 01:33:39 +0300 Subject: [PATCH 41/80] fix initial visibility with new_gtk --- src/webkitgtk/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 7ebb11f62..602c73adb 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -317,6 +317,10 @@ impl InnerWebView { container.add(&webview); } + if attributes.visible { + webview.show_all(); + } + if attributes.focused { webview.grab_focus(); } From 8e0a3d955aa8d038b4091daf69c465a4fed4ef04 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 02:22:19 +0300 Subject: [PATCH 42/80] refine examples --- Cargo.toml | 4 +- examples/async_custom_protocol.rs | 104 +++++++++ examples/custom_protocol.rs | 40 ++-- examples/custom_protocol/index.html | 26 +++ examples/custom_protocol/script.js | 22 ++ .../subpage.html} | 0 .../wasm.wasm} | Bin examples/custom_protocol_page1.html | 28 --- examples/custom_protocol_script.js | 23 -- examples/custom_titlebar.rs | 22 +- examples/detect_js_ecma.rs | 134 ------------ examples/download_event.rs | 95 --------- examples/dragndrop.rs | 40 ---- examples/eval_js.rs | 87 -------- examples/form.html | 31 --- examples/form.rs | 70 ------- examples/fullscreen.rs | 43 ---- examples/multiwebview.rs | 96 +++++++++ examples/{multi_window.rs => multiwindow.rs} | 22 +- examples/navigation_event.rs | 46 ---- examples/new_window_req_event.rs | 56 ----- examples/proxy.rs | 40 ---- examples/{hello_world.rs => simple.rs} | 10 +- examples/stream.html | 31 --- examples/stream_range.rs | 156 -------------- examples/transparent.rs | 22 +- examples/user_agent.rs | 43 ---- examples/wgpu.rs | 197 ++++++++++++++++++ examples/winit.rs | 72 +++++++ src/webkitgtk/mod.rs | 16 +- 30 files changed, 606 insertions(+), 970 deletions(-) create mode 100644 examples/async_custom_protocol.rs create mode 100644 examples/custom_protocol/index.html create mode 100644 examples/custom_protocol/script.js rename examples/{custom_protocol_page2.html => custom_protocol/subpage.html} (100%) rename examples/{custom_protocol_wasm.wasm => custom_protocol/wasm.wasm} (100%) delete mode 100644 examples/custom_protocol_page1.html delete mode 100644 examples/custom_protocol_script.js delete mode 100644 examples/detect_js_ecma.rs delete mode 100644 examples/download_event.rs delete mode 100644 examples/dragndrop.rs delete mode 100644 examples/eval_js.rs delete mode 100644 examples/form.html delete mode 100644 examples/form.rs delete mode 100644 examples/fullscreen.rs create mode 100644 examples/multiwebview.rs rename examples/{multi_window.rs => multiwindow.rs} (86%) delete mode 100644 examples/navigation_event.rs delete mode 100644 examples/new_window_req_event.rs delete mode 100644 examples/proxy.rs rename examples/{hello_world.rs => simple.rs} (83%) delete mode 100644 examples/stream.html delete mode 100644 examples/stream_range.rs delete mode 100644 examples/user_agent.rs create mode 100644 examples/wgpu.rs create mode 100644 examples/winit.rs diff --git a/Cargo.toml b/Cargo.toml index 8123cf157..d645faa55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,5 +96,7 @@ tao = { git = "https://github.com/tauri-apps/tao" } [dev-dependencies] http-range = "0.1.5" +pollster = "0.3" tao = { git = "https://github.com/tauri-apps/tao" } -winit = "0.29" +wgpu = "0.18" +winit = "0.29" \ No newline at end of file diff --git a/examples/async_custom_protocol.rs b/examples/async_custom_protocol.rs new file mode 100644 index 000000000..262561a7a --- /dev/null +++ b/examples/async_custom_protocol.rs @@ -0,0 +1,104 @@ +// Copyright 2020-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::path::PathBuf; + +use http::Request; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{ + http::{header::CONTENT_TYPE, Response}, + WebViewBuilder, +}; + +fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + let _webview = builder + .with_asynchronous_custom_protocol("wry".into(), move |request, responder| { + match get_wry_response(request) { + Ok(http_response) => responder.respond(http_response), + Err(e) => responder.respond( + http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap(), + ), + } + }) + // tell the webview to load the custom protocol + .with_url("wry://localhost")? + .build()?; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} + +fn get_wry_response( + request: Request>, +) -> Result>, Box> { + let path = request.uri().path(); + // Read the file content from file path + let root = PathBuf::from("examples/custom_protocol"); + let path = if path == "/" { + "index.html" + } else { + // removing leading slash + &path[1..] + }; + let content = std::fs::read(std::fs::canonicalize(root.join(path))?)?; + + // Return asset contents and mime types based on file extentions + // If you don't want to do this manually, there are some crates for you. + // Such as `infer` and `mime_guess`. + let mimetype = if path.ends_with(".html") || path == "/" { + "text/html" + } else if path.ends_with(".js") { + "text/javascript" + } else if path.ends_with(".png") { + "image/png" + } else if path.ends_with(".wasm") { + "application/wasm" + } else { + unimplemented!(); + }; + + Response::builder() + .header(CONTENT_TYPE, mimetype) + .body(content) + .map_err(Into::into) +} diff --git a/examples/custom_protocol.rs b/examples/custom_protocol.rs index 093548fa1..58e303245 100644 --- a/examples/custom_protocol.rs +++ b/examples/custom_protocol.rs @@ -2,10 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::{ - fs::{canonicalize, read}, - path::PathBuf, -}; +use std::path::PathBuf; use http::Request; use tao::{ @@ -18,14 +15,9 @@ use wry::{ WebViewBuilder, }; -const PAGE1_HTML: &[u8] = include_bytes!("custom_protocol_page1.html"); - fn main() -> wry::Result<()> { let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Custom Protocol") - .build(&event_loop) - .unwrap(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); #[cfg(any( target_os = "windows", @@ -46,17 +38,17 @@ fn main() -> wry::Result<()> { let vbox = window.default_vbox().unwrap(); WebViewBuilder::new_gtk(vbox) }; + let _webview = builder - .with_asynchronous_custom_protocol("wry".into(), move |request, responder| { + .with_custom_protocol("wry".into(), move |request| { match get_wry_response(request) { - Ok(http_response) => responder.respond(http_response), - Err(e) => responder.respond( - http::Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(500) - .body(e.to_string().as_bytes().to_vec()) - .unwrap(), - ), + Ok(r) => r.map(Into::into), + Err(e) => http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap() + .map(Into::into), } }) // tell the webview to load the custom protocol @@ -81,12 +73,14 @@ fn get_wry_response( ) -> Result>, Box> { let path = request.uri().path(); // Read the file content from file path - let content = if path == "/" { - PAGE1_HTML.into() + let root = PathBuf::from("examples/custom_protocol"); + let path = if path == "/" { + "index.html" } else { - // `1..` for removing leading slash - read(canonicalize(PathBuf::from("examples").join(&path[1..]))?)? + // removing leading slash + &path[1..] }; + let content = std::fs::read(std::fs::canonicalize(root.join(path))?)?; // Return asset contents and mime types based on file extentions // If you don't want to do this manually, there are some crates for you. diff --git a/examples/custom_protocol/index.html b/examples/custom_protocol/index.html new file mode 100644 index 000000000..1d9022ffb --- /dev/null +++ b/examples/custom_protocol/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + +

Welcome to WRY!

+

Page 1

+ +

+ Link + + + + diff --git a/examples/custom_protocol/script.js b/examples/custom_protocol/script.js new file mode 100644 index 000000000..f6f257dcb --- /dev/null +++ b/examples/custom_protocol/script.js @@ -0,0 +1,22 @@ +// Copyright 2020-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT +if (window.location.pathname.startsWith("/page2")) { + console.log("hello from javascript in page2"); +} else { + console.log("hello from javascript in page1"); + + if (typeof WebAssembly.instantiateStreaming !== "undefined") { + WebAssembly.instantiateStreaming(fetch("/wasm.wasm")).then((wasm) => { + console.log(wasm.instance.exports.main()); // should log 42 + }); + } else { + // Older WKWebView may not support `WebAssembly.instantiateStreaming` yet. + fetch("/wasm.wasm") + .then((response) => response.arrayBuffer()) + .then((bytes) => WebAssembly.instantiate(bytes)) + .then((wasm) => { + console.log(wasm.instance.exports.main()); // should log 42 + }); + } +} diff --git a/examples/custom_protocol_page2.html b/examples/custom_protocol/subpage.html similarity index 100% rename from examples/custom_protocol_page2.html rename to examples/custom_protocol/subpage.html diff --git a/examples/custom_protocol_wasm.wasm b/examples/custom_protocol/wasm.wasm similarity index 100% rename from examples/custom_protocol_wasm.wasm rename to examples/custom_protocol/wasm.wasm diff --git a/examples/custom_protocol_page1.html b/examples/custom_protocol_page1.html deleted file mode 100644 index 61a86d419..000000000 --- a/examples/custom_protocol_page1.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - -

Welcome to WRY!

-

Page 1

- -

- Link - - - - - diff --git a/examples/custom_protocol_script.js b/examples/custom_protocol_script.js deleted file mode 100644 index ecfd1c137..000000000 --- a/examples/custom_protocol_script.js +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT -if (window.location.pathname.startsWith('/custom_protocol_page2')) { - console.log("hello from javascript in page2"); -} else { - console.log("hello from javascript in page1"); - - if (typeof WebAssembly.instantiateStreaming !== 'undefined') { - WebAssembly.instantiateStreaming(fetch("/custom_protocol_wasm.wasm")) - .then(wasm => { - console.log(wasm.instance.exports.main()); // should log 42 - }); - } else { - // Older WKWebView may not support `WebAssembly.instantiateStreaming` yet. - fetch("/custom_protocol_wasm.wasm") - .then(response => response.arrayBuffer()) - .then(bytes => WebAssembly.instantiate(bytes)) - .then(wasm => { - console.log(wasm.instance.exports.main()); // should log 42 - }); - } -} diff --git a/examples/custom_titlebar.rs b/examples/custom_titlebar.rs index 6010ba7db..67af0dbde 100644 --- a/examples/custom_titlebar.rs +++ b/examples/custom_titlebar.rs @@ -131,8 +131,28 @@ fn main() -> wry::Result<()> { _ => {} }; + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + let mut webview = Some( - WebViewBuilder::new(&window) + builder .with_html(HTML)? .with_ipc_handler(handler) .with_accept_first_mouse(true) diff --git a/examples/detect_js_ecma.rs b/examples/detect_js_ecma.rs deleted file mode 100644 index e94c27668..000000000 --- a/examples/detect_js_ecma.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -fn main() -> wry::Result<()> { - const HTML: &str = r#" - - - -

ECMAScript support list:

-
    - - -

    Details:

    -

    - -
    - - - - - "#; - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Detect ECMAScript") - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window).with_html(HTML)?.build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} diff --git a/examples/download_event.rs b/examples/download_event.rs deleted file mode 100644 index e4d94c101..000000000 --- a/examples/download_event.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019-2021 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use std::{env::temp_dir, path::PathBuf}; -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -fn main() -> wry::Result<()> { - const HTML: &str = r#" - -
    -

    WRYYYYYYYYYYYYYYYYYYYYYY!

    - Allowed Download - Denied Download -
    - - "#; - - enum UserEvent { - DownloadStarted(String, String), - DownloadComplete(Option, bool), - Rejected(String), - } - - let event_loop = EventLoopBuilder::::with_user_event().build(); - let proxy = event_loop.create_proxy(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_html(HTML)? - .with_download_started_handler({ - let proxy = proxy.clone(); - move |uri: String, default_path: &mut PathBuf| { - if uri.contains("wry-v0.13.3") { - let path = temp_dir().join("example.zip").as_path().to_path_buf(); - - *default_path = path.clone(); - - let submitted = proxy - .send_event(UserEvent::DownloadStarted(uri, path.display().to_string())) - .is_ok(); - - return submitted; - } - - let _ = proxy.send_event(UserEvent::Rejected(uri)); - - false - } - }) - .with_download_completed_handler({ - let proxy = proxy; - move |_uri, path, success| { - let _ = proxy.send_event(UserEvent::DownloadComplete(path, success)); - } - }) - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::UserEvent(UserEvent::DownloadStarted(uri, temp_dir)) => { - println!("Download: {}", uri); - println!("Will write to: {:?}", temp_dir); - } - Event::UserEvent(UserEvent::DownloadComplete(path, success)) => { - let path = path.map(|_| temp_dir().join("example.zip")); - println!("Succeeded: {}", success); - if let Some(path) = path { - println!("Path: {}", path.to_string_lossy()); - let metadata = path.metadata().unwrap(); - println!("Size of {}Mb", (metadata.len() / 1024) / 1024) - } else { - println!("No output path") - } - } - Event::UserEvent(UserEvent::Rejected(uri)) => { - println!("Rejected download from: {}", uri) - } - _ => (), - } - }); -} diff --git a/examples/dragndrop.rs b/examples/dragndrop.rs deleted file mode 100644 index b23eb8f5e..000000000 --- a/examples/dragndrop.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -fn main() -> wry::Result<()> { - const HTML: &str = r#"data:text/html, -Drop files onto the window and read the console!
    -Dropping files onto the following form is also possible:

    - -"#; - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_url(HTML)? - .with_file_drop_handler(|data| { - println!("Window 1: {:?}", data); - false // Returning true will block the OS default behaviour. - }) - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} diff --git a/examples/eval_js.rs b/examples/eval_js.rs deleted file mode 100644 index 2750e96b6..000000000 --- a/examples/eval_js.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2020-2022 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -fn main() -> wry::Result<()> { - enum UserEvent { - ExecEval, - } - - let event_loop = EventLoopBuilder::::with_user_event().build(); - let proxy = event_loop.create_proxy(); - - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - - let ipc_handler = move |req: String| { - if req == "exec-eval" { - let _ = proxy.send_event(UserEvent::ExecEval); - } - }; - - let _webview = WebViewBuilder::new(&window) - .with_html( - r#" - - "#, - )? - .with_ipc_handler(ipc_handler) - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::UserEvent(UserEvent::ExecEval) => { - // String - _webview - .evaluate_script_with_callback( - "if (!foo) { var foo = 'morbin'; } `${foo} time`", - |result| println!("String: {:?}", result), - ) - .unwrap(); - - // Number - _webview - .evaluate_script_with_callback("var num = 9527; num", |result| { - println!("Number: {:?}", result) - }) - .unwrap(); - - // Object - _webview - .evaluate_script_with_callback("var obj = { thank: 'you', '95': 27 }; obj", |result| { - println!("Object: {:?}", result) - }) - .unwrap(); - - // Array - _webview - .evaluate_script_with_callback("var ary = [1,2,3,4,'5']; ary", |result| { - println!("Array: {:?}", result) - }) - .unwrap(); - // Exception thrown - _webview - .evaluate_script_with_callback("throw new Error()", |result| { - println!("Exception Occured: {:?}", result) - }) - .unwrap(); - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - _ => (), - } - }); -} diff --git a/examples/form.html b/examples/form.html deleted file mode 100644 index 332a639bb..000000000 --- a/examples/form.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - -

    Welcome to WRY!

    -
    -
    -
    -
    -
    -
    -

    - -
    -

    - If you click the "Submit" button, the form-data will be sent to the custom - protocol. -

    - - - \ No newline at end of file diff --git a/examples/form.rs b/examples/form.rs deleted file mode 100644 index 0891c9f03..000000000 --- a/examples/form.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use std::{ - borrow::Cow, - fs::{canonicalize, read}, -}; - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -use wry::{ - http::{header::CONTENT_TYPE, method::Method, Response}, - WebViewBuilder, -}; - -fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - - let _webview = WebViewBuilder::new(&window) - .with_custom_protocol("wry".into(), move |request| { - if request.method() == Method::POST { - let body_string = String::from_utf8_lossy(request.body()); - for body in body_string.split('&') { - println!("Value sent; {:?}", body); - } - } - - // remove leading slash - let path = &request.uri().path()[1..]; - - get_response(path).unwrap_or_else(|error| { - http::Response::builder() - .status(http::StatusCode::BAD_REQUEST) - .header(CONTENT_TYPE, "text/plain") - .body(error.to_string().as_bytes().to_vec().into()) - .unwrap() - }) - }) - // tell the webview to load the custom protocol - .with_url("wry://localhost/examples/form.html")? - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} - -fn get_response(path: &str) -> Result>, Box> { - Response::builder() - .header(CONTENT_TYPE, "text/html") - .body(read(canonicalize(path)?)?.into()) - .map_err(Into::into) -} diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs deleted file mode 100644 index 33cceffff..000000000 --- a/examples/fullscreen.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::{Fullscreen, WindowBuilder}, -}; -use wry::WebViewBuilder; - -fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("3D Render Test") - .with_fullscreen(Some(Fullscreen::Borderless(None))) - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_url("https://browserbench.org/MotionMark1.2/")? - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} - -// Test Result: -// CPU: i7 9750H || GPU: Intel(R) UHD Graphics 630 -// Linux kernel 5.8.18-18-ibryza-standard-xin -// Mesa Mesa 20.2.6 -// ================================================ -// Canvas score - Test 1: 542 - Test 2: 368 -// WebGL score - Test 1: 1390 - Test 2: 1342 -// Total score: 3642 diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs new file mode 100644 index 000000000..64bc4ccc2 --- /dev/null +++ b/examples/multiwebview.rs @@ -0,0 +1,96 @@ +// Copyright 2020-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use gtk::prelude::DisplayExtManual; +use winit::{ + dpi::LogicalSize, + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; + +fn main() -> wry::Result<()> { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + { + gtk::init()?; + if gtk::gdk::Display::default().unwrap().backend().is_wayland() { + panic!("This example doesn't support wayland!"); + } + } + + let event_loop = EventLoop::new().unwrap(); + let window = WindowBuilder::new() + .with_inner_size(LogicalSize::new(800, 800)) + .build(&event_loop) + .unwrap(); + + let size = window.inner_size(); + + let webview = WebViewBuilder::new_as_child(&window) + .with_position((0, 0)) + .with_size((size.width / 2, size.height / 2)) + .with_url("https://tauri.app")? + .build()?; + let webview2 = WebViewBuilder::new_as_child(&window) + .with_position(((size.width / 2) as i32, 0)) + .with_size((size.width / 2, size.height / 2)) + .with_url("https://github.com/tauri-apps/wry")? + .build()?; + let webview3 = WebViewBuilder::new_as_child(&window) + .with_position((0, (size.height / 2) as i32)) + .with_size((size.width / 2, size.height / 2)) + .with_url("https://twitter.com/TauriApps")? + .build()?; + let webview4 = WebViewBuilder::new_as_child(&window) + .with_position(((size.width / 2) as i32, (size.height / 2) as i32)) + .with_size((size.width / 2, size.height / 2)) + .with_url("https://google.com")? + .build()?; + + event_loop + .run(move |event, evl| { + evl.set_control_flow(ControlFlow::Poll); + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + while gtk::events_pending() { + gtk::main_iteration_do(false); + } + + match event { + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + webview.set_size((size.width / 2, size.height / 2)); + webview2.set_position(((size.width / 2) as i32, 0)); + webview2.set_size((size.width / 2, size.height / 2)); + webview3.set_position((0, (size.height / 2) as i32)); + webview3.set_size((size.width / 2, size.height / 2)); + webview4.set_position(((size.width / 2) as i32, (size.height / 2) as i32)); + webview4.set_size((size.width / 2, size.height / 2)); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => evl.exit(), + _ => {} + } + }) + .unwrap(); + + Ok(()) +} diff --git a/examples/multi_window.rs b/examples/multiwindow.rs similarity index 86% rename from examples/multi_window.rs rename to examples/multiwindow.rs index 41bdc6214..bb146d0e8 100644 --- a/examples/multi_window.rs +++ b/examples/multiwindow.rs @@ -89,7 +89,27 @@ fn create_new_window( _ => {} }; - let webview = WebViewBuilder::new(&window) + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + + let webview = builder .with_html( r#" diff --git a/examples/navigation_event.rs b/examples/navigation_event.rs deleted file mode 100644 index 71fcb59f7..000000000 --- a/examples/navigation_event.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -enum UserEvent { - Navigation(String), -} - -fn main() -> wry::Result<()> { - let event_loop = EventLoopBuilder::::with_user_event().build(); - let proxy = event_loop.create_proxy(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_url("http://neverssl.com")? - .with_navigation_handler(move |uri: String| { - let submitted = proxy.send_event(UserEvent::Navigation(uri.clone())).is_ok(); - - submitted && uri.contains("neverssl") - }) - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::UserEvent(UserEvent::Navigation(uri)) => { - println!("{}", uri); - } - _ => (), - } - }); -} diff --git a/examples/new_window_req_event.rs b/examples/new_window_req_event.rs deleted file mode 100644 index af7a786ca..000000000 --- a/examples/new_window_req_event.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoopBuilder}, - window::WindowBuilder, -}; -use wry::WebViewBuilder; - -enum UserEvent { - NewWindow(String), -} - -fn main() -> wry::Result<()> { - let html = r#" - -
    -

    WRYYYYYYYYYYYYYYYYYYYYYY!

    - Visit Wikipedia - (Try to) visit GitHub -
    - - "#; - - let event_loop = EventLoopBuilder::::with_user_event().build(); - let proxy = event_loop.create_proxy(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_html(html)? - .with_new_window_req_handler(move |uri: String| { - let submitted = proxy.send_event(UserEvent::NewWindow(uri.clone())).is_ok(); - - submitted && uri.contains("wikipedia") - }) - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::UserEvent(UserEvent::NewWindow(uri)) => { - println!("New Window: {}", uri); - } - _ => (), - } - }); -} diff --git a/examples/proxy.rs b/examples/proxy.rs deleted file mode 100644 index 902def74c..000000000 --- a/examples/proxy.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; -use wry::{ProxyConfig, ProxyEndpoint, WebViewBuilder}; - -fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Proxy Test") - .build(&event_loop) - .unwrap(); - - let http_proxy = ProxyConfig::Http(ProxyEndpoint { - host: "localhost".to_string(), - port: "3128".to_string(), - }); - - let _webview = WebViewBuilder::new(&window) - .with_proxy_config(http_proxy) - .with_url("https://www.myip.com/")? - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} diff --git a/examples/hello_world.rs b/examples/simple.rs similarity index 83% rename from examples/hello_world.rs rename to examples/simple.rs index 28b5b247a..000b0b752 100644 --- a/examples/hello_world.rs +++ b/examples/simple.rs @@ -11,10 +11,7 @@ use wry::WebViewBuilder; fn main() -> wry::Result<()> { let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); #[cfg(any( target_os = "windows", @@ -36,10 +33,7 @@ fn main() -> wry::Result<()> { WebViewBuilder::new_gtk(vbox) }; - let _webview = builder - .with_url("https://tauri.app")? - // .with_incognito(true) - .build()?; + let _webview = builder.with_url("https://tauri.app")?.build()?; event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; diff --git a/examples/stream.html b/examples/stream.html deleted file mode 100644 index f3a7d0478..000000000 --- a/examples/stream.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/stream_range.rs b/examples/stream_range.rs deleted file mode 100644 index 711f92fcf..000000000 --- a/examples/stream_range.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use http_range::HttpRange; -use std::{ - borrow::Cow, - fs::{canonicalize, File}, - io::{Read, Seek, SeekFrom}, - path::PathBuf, - process::{Command, Stdio}, -}; -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; -use wry::{ - http::{header::CONTENT_TYPE, status::StatusCode, Response}, - WebViewBuilder, -}; - -fn main() -> wry::Result<()> { - let video_file = PathBuf::from("examples/test_video.mp4"); - let video_url = - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"; - - if !video_file.exists() { - // Downloading with curl this saves us from adding - // a Rust HTTP client dependency. - println!("Downloading {}", video_url); - let status = Command::new("curl") - .arg("-L") - .arg("-o") - .arg(&video_file) - .arg(video_url) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .unwrap(); - - assert!(status.status.success()); - assert!(video_file.exists()); - } - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - - let _webview = WebViewBuilder::new(&window) - .with_custom_protocol("wry".into(), move |request| { - get_stream_response(request).unwrap_or_else(|error| { - http::Response::builder() - .status(http::StatusCode::BAD_REQUEST) - .header(CONTENT_TYPE, "text/plain") - .body(error.to_string().as_bytes().to_vec().into()) - .unwrap() - }) - }) - // tell the webview to load the custom protocol - .with_url("wry://localhost/examples/stream.html")? - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} - -fn get_stream_response( - request: http::Request>, -) -> Result>, Box> { - // remove leading slash - let path = &request.uri().path()[1..]; - - // Read the file content from file path - let mut content = File::open(canonicalize(path)?)?; - - // Return asset contents and mime types based on file extentions - // If you don't want to do this manually, there are some crates for you. - // Such as `infer` and `mime_guess`. - let mut status_code = StatusCode::OK; - let mut buf = Vec::new(); - - // guess our mimetype from the path - let mimetype = if path.ends_with(".html") { - "text/html" - } else if path.ends_with(".mp4") { - "video/mp4" - } else { - unimplemented!(); - }; - - // prepare our http response - let mut response = Response::builder(); - - // read our range header if it exist, so we can return partial content - if let Some(range) = request.headers().get("range") { - // Get the file size - let file_size = content.metadata().unwrap().len(); - - // we parse the range header - let range = HttpRange::parse(range.to_str().unwrap(), file_size).unwrap(); - - // let support only 1 range for now - let first_range = range.first(); - if let Some(range) = first_range { - let mut real_length = range.length; - - // prevent max_length; - // specially on webview2 - if range.length > file_size / 3 { - // max size sent (400ko / request) - // as it's local file system we can afford to read more often - real_length = 1024 * 400; - } - - // last byte we are reading, the length of the range include the last byte - // who should be skipped on the header - let last_byte = range.start + real_length - 1; - status_code = StatusCode::PARTIAL_CONTENT; - - response = response.header("Connection", "Keep-Alive"); - response = response.header("Accept-Ranges", "bytes"); - // we need to overwrite our content length - response = response.header("Content-Length", real_length); - response = response.header( - "Content-Range", - format!("bytes {}-{}/{}", range.start, last_byte, file_size), - ); - - // seek our file bytes - content.seek(SeekFrom::Start(range.start))?; - content.take(real_length).read_to_end(&mut buf)?; - } else { - content.read_to_end(&mut buf)?; - } - } else { - content.read_to_end(&mut buf)?; - } - - response - .header(CONTENT_TYPE, mimetype) - .status(status_code) - .body(buf.into()) - .map_err(Into::into) -} diff --git a/examples/transparent.rs b/examples/transparent.rs index 6e97d3246..d5b729574 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -30,7 +30,27 @@ fn main() -> wry::Result<()> { window.set_undecorated_shadow(true); } - let _webview = WebViewBuilder::new(&window) + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let builder = WebViewBuilder::new(&window); + + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let builder = { + use tao::platform::unix::WindowExtUnix; + let vbox = window.default_vbox().unwrap(); + WebViewBuilder::new_gtk(vbox) + }; + + let _webview = builder // The second is on webview... .with_transparent(true) // And the last is in html. diff --git a/examples/user_agent.rs b/examples/user_agent.rs deleted file mode 100644 index 12ee4eeb9..000000000 --- a/examples/user_agent.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; -use wry::{webview_version, WebViewBuilder}; - -fn main() -> wry::Result<()> { - let current_version = env!("CARGO_PKG_VERSION"); - let current_webview_version = webview_version().unwrap(); - let user_agent_string = format!( - "wry/{} ({}; {})", - current_version, - std::env::consts::OS, - current_webview_version - ); - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new() - .with_title("Hello World") - .build(&event_loop) - .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_user_agent(&user_agent_string) - .with_url("https://www.whatismybrowser.com/detect/what-is-my-user-agent")? - .build()?; - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); -} diff --git a/examples/wgpu.rs b/examples/wgpu.rs new file mode 100644 index 000000000..343fa588a --- /dev/null +++ b/examples/wgpu.rs @@ -0,0 +1,197 @@ +use gtk::prelude::DisplayExtManual; +use std::borrow::Cow; +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::Window, +}; +use wry::WebViewBuilder; + +async fn run(event_loop: EventLoop<()>, window: Window) { + let size = window.inner_size(); + + let instance = wgpu::Instance::default(); + + let surface = unsafe { instance.create_surface(&window) }.unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, + // Request an adapter which can render to our surface + compatible_surface: Some(&surface), + }) + .await + .expect("Failed to find an appropriate adapter"); + + // Create the logical device and command queue + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. + limits: wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()), + }, + None, + ) + .await + .expect("Failed to create device"); + + // Load the shaders from disk + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed( + r#" +@vertex +fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { + let x = f32(i32(in_vertex_index) - 1); + let y = f32(i32(in_vertex_index & 1u) * 2 - 1); + return vec4(x, y, 0.0, 1.0); +} + +@fragment +fn fs_main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); +} +"#, + )), + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let swapchain_capabilities = surface.get_capabilities(&adapter); + let swapchain_format = swapchain_capabilities.formats[0]; + + let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(swapchain_format.into())], + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let mut config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: swapchain_capabilities.alpha_modes[0], + view_formats: vec![], + }; + + surface.configure(&device, &config); + + let _webview = WebViewBuilder::new_as_child(&window) + .with_position((100, 100)) + .with_size((400, 400)) + .with_url("https://tauri.app") + .unwrap() + .build() + .unwrap(); + + event_loop + .run(move |event, evl| { + evl.set_control_flow(ControlFlow::Poll); + + match event { + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + // Reconfigure the surface with the new size + config.width = size.width; + config.height = size.height; + surface.configure(&device, &config); + // On macos the window needs to be redrawn manually after resizing + window.request_redraw(); + } + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + let frame = surface + .get_current_texture() + .expect("Failed to acquire next swap chain texture"); + let view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&render_pipeline); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + frame.present(); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => evl.exit(), + _ => {} + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + while gtk::events_pending() { + gtk::main_iteration_do(false); + } + }) + .unwrap(); +} + +fn main() { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + { + gtk::init().unwrap(); + if gtk::gdk::Display::default().unwrap().backend().is_wayland() { + panic!("This example doesn't support wayland!"); + } + } + + let event_loop = EventLoop::new().unwrap(); + let window = winit::window::Window::new(&event_loop).unwrap(); + pollster::block_on(run(event_loop, window)); +} diff --git a/examples/winit.rs b/examples/winit.rs new file mode 100644 index 000000000..b49382b53 --- /dev/null +++ b/examples/winit.rs @@ -0,0 +1,72 @@ +// Copyright 2020-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use gtk::prelude::DisplayExtManual; +use winit::{ + dpi::LogicalSize, + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::WebViewBuilder; + +fn main() -> wry::Result<()> { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + { + gtk::init()?; + if gtk::gdk::Display::default().unwrap().backend().is_wayland() { + panic!("This example doesn't support wayland!"); + } + } + + let event_loop = EventLoop::new().unwrap(); + let window = WindowBuilder::new() + .with_inner_size(LogicalSize::new(800, 800)) + .build(&event_loop) + .unwrap(); + + let webview = WebViewBuilder::new_as_child(&window) + .with_size((800, 800)) + .with_url("https://tauri.app")? + .build()?; + + event_loop + .run(move |event, evl| { + evl.set_control_flow(ControlFlow::Poll); + + match event { + Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + webview.set_size(size.into()); + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => evl.exit(), + _ => {} + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + while gtk::events_pending() { + gtk::main_iteration_do(false); + } + }) + .unwrap(); + + Ok(()) +} diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 602c73adb..6c68b4391 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -599,18 +599,10 @@ impl InnerWebView { pub fn set_size(&self, size: (u32, u32)) { if self.is_child { - self - .gtk_window - .as_ref() - .unwrap() - .window() - .unwrap() - .resize(size.0 as _, size.1 as _); - self - .gtk_window - .as_ref() - .unwrap() - .size_allocate(>k::Allocation::new(200, 200, size.0 as _, size.1 as _)); + if let Some(window) = &self.gtk_window { + window.window().unwrap().resize(size.0 as _, size.1 as _); + window.size_allocate(>k::Allocation::new(200, 200, size.0 as _, size.1 as _)); + } } } From f4cd33694957f581ddf19ba569996e3770b3e39a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 02:24:04 +0300 Subject: [PATCH 43/80] fix wpgu example --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d645faa55..9bf025471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ tao = { git = "https://github.com/tauri-apps/tao" } [dev-dependencies] http-range = "0.1.5" -pollster = "0.3" +pollster = "0.3.0" tao = { git = "https://github.com/tauri-apps/tao" } -wgpu = "0.18" -winit = "0.29" \ No newline at end of file +wgpu = "0.18.0" +winit = { version = "0.29", features = ["rwh_05"]} From 5a58f70f821c2d1e863b997d1b49133da7116eef Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 02:34:41 +0300 Subject: [PATCH 44/80] fix examples on windows and macos --- examples/multiwebview.rs | 3 ++- examples/wgpu.rs | 3 ++- examples/winit.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs index 64bc4ccc2..66a379572 100644 --- a/examples/multiwebview.rs +++ b/examples/multiwebview.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use gtk::prelude::DisplayExtManual; use winit::{ dpi::LogicalSize, event::{Event, WindowEvent}, @@ -20,6 +19,8 @@ fn main() -> wry::Result<()> { target_os = "openbsd", ))] { + use gtk::prelude::DisplayExtManual; + gtk::init()?; if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); diff --git a/examples/wgpu.rs b/examples/wgpu.rs index 343fa588a..759b1b4f5 100644 --- a/examples/wgpu.rs +++ b/examples/wgpu.rs @@ -1,4 +1,3 @@ -use gtk::prelude::DisplayExtManual; use std::borrow::Cow; use winit::{ event::{Event, WindowEvent}, @@ -185,6 +184,8 @@ fn main() { target_os = "openbsd", ))] { + use gtk::prelude::DisplayExtManual; + gtk::init().unwrap(); if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); diff --git a/examples/winit.rs b/examples/winit.rs index b49382b53..97582698b 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use gtk::prelude::DisplayExtManual; use winit::{ dpi::LogicalSize, event::{Event, WindowEvent}, @@ -20,6 +19,8 @@ fn main() -> wry::Result<()> { target_os = "openbsd", ))] { + use gtk::prelude::DisplayExtManual; + gtk::init()?; if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); From c7cb2083ad35d3cc4328291a37dcef111ddd4a4f Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 03:55:08 +0300 Subject: [PATCH 45/80] use a better workaround --- src/webkitgtk/mod.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 6c68b4391..0561621fc 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -136,9 +136,19 @@ impl InnerWebView { let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); gtk_window.add(&vbox); - let should_show = attributes.visible; + let hidden = !attributes.visible; Self::new_gtk(&vbox, attributes, pl_attrs, web_context).map(|mut w| { + // for some reason, if the webview starts as hidden, + // we will need about 3 calls to `webview.set_visible` + // with alternating value. + // calling gtk_window.show_all() then hiding it again + // seems to fix the issue. + gtk_window.show_all(); + if hidden { + gtk_window.hide(); + } + w.is_child = is_child; w.xlib = Some(xlib); w.display = Some(gdk_display); @@ -146,14 +156,6 @@ impl InnerWebView { w.x11_window = Some(window); w.gtk_window = Some(gtk_window); - // this an ugly workaround, for some reasons, when the webview - // is starting as hidden, we need to call set_visible multiple times - // before it can work correctly - w.set_visible(true); - if !should_show { - w.set_visible(false); - } - w }) } @@ -589,11 +591,9 @@ impl InnerWebView { pub fn set_position(&self, position: (i32, i32)) { if self.is_child { - self - .gtk_window - .as_ref() - .unwrap() - .move_(position.0, position.1); + if let Some(window) = &self.gtk_window { + window.move_(position.0, position.1); + } } } @@ -616,6 +616,12 @@ impl InnerWebView { } } + if visible { + self.webview.show_all(); + } else { + self.webview.hide(); + } + if let Some(window) = &self.gtk_window { if visible { window.show_all(); @@ -623,12 +629,6 @@ impl InnerWebView { window.hide(); } } - - if visible { - self.webview.show_all(); - } else { - self.webview.hide(); - } } } From 5469e899055db1e5b455e2ee09d40924693d7dcc Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 26 Oct 2023 04:12:33 +0300 Subject: [PATCH 46/80] make set_size work on x11 --- examples/winit.rs | 34 ++++++++++++++++++++-------------- src/lib.rs | 7 ++++++- src/webkitgtk/mod.rs | 6 +++--- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/examples/winit.rs b/examples/winit.rs index 97582698b..d9429ab71 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -21,7 +21,7 @@ fn main() -> wry::Result<()> { { use gtk::prelude::DisplayExtManual; - gtk::init()?; + gtk::init().unwrap(); if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); } @@ -33,8 +33,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let webview = WebViewBuilder::new_as_child(&window) - .with_size((800, 800)) + let webview = WebViewBuilder::new(&window) .with_url("https://tauri.app")? .build()?; @@ -42,7 +41,25 @@ fn main() -> wry::Result<()> { .run(move |event, evl| { evl.set_control_flow(ControlFlow::Poll); + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] + while gtk::events_pending() { + gtk::main_iteration_do(false); + } + match event { + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + ))] Event::WindowEvent { event: WindowEvent::Resized(size), .. @@ -55,17 +72,6 @@ fn main() -> wry::Result<()> { } => evl.exit(), _ => {} } - - #[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - ))] - while gtk::events_pending() { - gtk::main_iteration_do(false); - } }) .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index ddee641f0..49cc71d2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -499,6 +499,8 @@ impl<'a> WebViewBuilder<'a> { /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. + /// - **macOS / Windows**: The webview will auto-resize when the passed handle is resized. + /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually. /// /// # Panics: /// @@ -1183,6 +1185,8 @@ impl WebView { /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. + /// - **macOS / Windows**: The webview will auto-resize when the passed handle is resized. + /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually. /// /// # Panics: /// @@ -1341,7 +1345,8 @@ impl WebView { self.webview.set_position(position) } - /// Set the webview size if it was created as a child. + /// Set the webview size if it was created as a child + /// or if ot was created directly in an X11 Window. pub fn set_size(&self, size: (u32, u32)) { self.webview.set_size(size) } diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 0561621fc..20fd44adf 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -598,11 +598,11 @@ impl InnerWebView { } pub fn set_size(&self, size: (u32, u32)) { - if self.is_child { - if let Some(window) = &self.gtk_window { + if let Some(window) = &self.gtk_window { + if self.is_child { window.window().unwrap().resize(size.0 as _, size.1 as _); - window.size_allocate(>k::Allocation::new(200, 200, size.0 as _, size.1 as _)); } + window.size_allocate(>k::Allocation::new(200, 200, size.0 as _, size.1 as _)); } } From a6f5e0a9c05651dc947bc690f6247b452d07b883 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Thu, 26 Oct 2023 11:39:49 +0900 Subject: [PATCH 47/80] Fix size in multiwebview example --- examples/multiwebview.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs index 66a379572..728587c94 100644 --- a/examples/multiwebview.rs +++ b/examples/multiwebview.rs @@ -33,11 +33,11 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let size = window.inner_size(); + let size = window.inner_size().to_logical::(window.scale_factor()); let webview = WebViewBuilder::new_as_child(&window) .with_position((0, 0)) - .with_size((size.width / 2, size.height / 2)) + .with_size((size.width / 4, size.height / 4)) .with_url("https://tauri.app")? .build()?; let webview2 = WebViewBuilder::new_as_child(&window) @@ -76,6 +76,7 @@ fn main() -> wry::Result<()> { event: WindowEvent::Resized(size), .. } => { + let size = size.to_logical::(window.scale_factor()); webview.set_size((size.width / 2, size.height / 2)); webview2.set_position(((size.width / 2) as i32, 0)); webview2.set_size((size.width / 2, size.height / 2)); From 7c8f13a3908243d63101f18acdc2761bdb052435 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Thu, 26 Oct 2023 13:23:20 +0900 Subject: [PATCH 48/80] Add visible method on macOS and iOS --- src/wkwebview/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 62ba444e1..589999d6c 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -418,6 +418,10 @@ impl InnerWebView { let _: () = msg_send![scroll, setBounces: NO]; } + if !attributes.visible { + let () = msg_send![webview, setHidden: YES]; + } + #[cfg(any(debug_assertions, feature = "devtools"))] if attributes.devtools { let has_inspectable_property: BOOL = @@ -1088,8 +1092,10 @@ r#"Object.defineProperty(window, 'ipc', { } } - pub fn set_visible(&self, _visible: bool) { - // Unimplemented + pub fn set_visible(&self, visible: bool) { + unsafe { + let () = msg_send![self.webview, setHidden: !visible]; + } } } From fe0f345bb104a5f37cbb68160770beca8077f850 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 27 Oct 2023 04:52:29 +0200 Subject: [PATCH 49/80] remvoe `tao` from android backend and update documentation --- .changes/rwh.md | 5 ++++ Cargo.toml | 20 ++++++++------ README.md | 29 +++++++++++++++++++- build.rs | 2 +- examples/winit.rs | 2 +- src/android/binding.rs | 37 +++++++++++-------------- src/android/main_pipe.rs | 29 ++++++++------------ src/android/mod.rs | 29 ++++++++++++-------- src/error.rs | 2 +- src/lib.rs | 59 +++++++++++++++++++++++++++------------- 10 files changed, 133 insertions(+), 81 deletions(-) diff --git a/.changes/rwh.md b/.changes/rwh.md index 1af1eaa0c..54a0bd896 100644 --- a/.changes/rwh.md +++ b/.changes/rwh.md @@ -11,3 +11,8 @@ Refactor new method to take raw window handle instead. Following are APIs got af depend on the situation they have. - `Webview::inner_size` is removed. - Added `rwh_04`, `rwh_05`, `rwh_06` feature flags. + +This also means that we removed `tao` as a dependency completely which required some changes to the Android backend: + - We exposed the `android_setup` function that needs to be called once to setup necessary logic. + - Previously the `android_binding!` had internal call to `tao::android_binding` but now that `tao` has been removed,sa + the macro signature has changed and you now need to call `tao::android_binding` yourself, checkout the crate documentation for more information. \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 3a59b777a..ceb8a05cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ workspace = {} [package] name = "wry" version = "0.34.2" -authors = [ "Tauri Programme within The Commons Conservancy" ] +authors = ["Tauri Programme within The Commons Conservancy"] edition = "2021" license = "Apache-2.0 OR MIT" description = "Cross-platform WebView rendering library" @@ -14,14 +14,14 @@ categories = ["gui"] [package.metadata.docs.rs] no-default-features = true -features = [ "file-drop", "protocol" ] +features = ["file-drop", "protocol"] targets = [ "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", "x86_64-apple-darwin", ] -rustc-args = [ "--cfg", "docsrs" ] -rustdoc-args = [ "--cfg", "docsrs" ] +rustc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "docsrs"] [features] default = ["file-drop", "objc-exception", "protocol"] @@ -46,8 +46,8 @@ http = "0.2" raw-window-handle = { version = "0.6", features = ["std"] } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -javascriptcore-rs = { version = "=1.1", features = [ "v2_28" ] } -webkit2gtk = { version = "=2.0", features = [ "v2_38" ] } +javascriptcore-rs = { version = "=1.1", features = ["v2_28"] } +webkit2gtk = { version = "=2.0", features = ["v2_38"] } webkit2gtk-sys = "=2.0" gtk = "0.18" soup3 = "0.5" @@ -92,11 +92,15 @@ html5ever = "0.26" kuchiki = { package = "kuchikiki", version = "0.8" } sha2 = "0.10" base64 = "0.21" -tao = { git = "https://github.com/tauri-apps/tao" } +jni = "0.21" +ndk = "0.7" +ndk-sys = "0.4" +ndk-context = "0.1" +tao-macros = { version = "0.1.0" } [dev-dependencies] http-range = "0.1.5" pollster = "0.3.0" tao = { git = "https://github.com/tauri-apps/tao" } wgpu = "0.18.0" -winit = { version = "0.29", features = ["rwh_05"]} +winit = { version = "0.29", features = ["rwh_05"] } diff --git a/README.md b/README.md index 838017b01..effe2fa45 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,34 @@ WebView2 provided by Microsoft Edge Chromium is used. So wry supports Windows 7, Wry supports mobile with the help of [`cargo-mobile2`](https://github.com/tauri-apps/cargo-mobile2) CLI to create template project. If you are interested in playing or hacking it, please follow [MOBILE.md](MOBILE.md). -If you wish to create Android project yourself, there are a few kotlin files that are needed to run wry on Android and you have to set the following environment variables: +If you wish to create Android project yourself, there is a few requirements that your application needs to uphold: + +1. You need to set a few environment variables that will be used to generate the necessary kotlin + files that you need to include in your Android application for wry to function properly: + + - `WRY_ANDROID_PACKAGE`: which is the reversed domain name of your android project and the app name in snake_case, for example, `com.wry.example.wry_app` + - `WRY_ANDROID_LIBRARY`: for example, if your cargo project has a lib name `wry_app`, it will generate `libwry_app.so` so you se this env var to `wry_app` + - `WRY_ANDROID_KOTLIN_FILES_OUT_DIR`: for example, `path/to/app/src/main/kotlin/com/wry/example` + +2. Your main Android Activity needs to inherit `AppCompatActivity`, preferably it should use the generated `WryActivity` or inherit it. +3. Your Rust app needs to call `wry::android_setup` function to setup the necessary logic to be able to create webviews later on. +4. Your Rust app needs to call `wry::android_binding!` macro to setup the JNI functions that will be called by `WryActivity` and various other places. + +It is recommended to use [`tao`](https://docs.rs/tao/latest/tao/) crate as it provides maximum compatibility with `wry` + +```rs +#[cfg(target_os = "android")] +{ + tao::android_binding!( + com_example, + wry_app, + WryActivity, + wry::android_setup, // pass the wry::android_setup function to tao which will invoke when the event loop is created + _start_app + ); + wry::android_binding!(com_example, ttt); +} +``` - `WRY_ANDROID_PACKAGE` which is the reversed domain name of your android project and the app name in snake_case for example: `com.wry.example.wry_app` - `WRY_ANDROID_LIBRARY` for example: if your cargo project has a lib name `wry_app`, it will generate `libwry_app.so` so you se this env var to `wry_app` diff --git a/build.rs b/build.rs index 82a448513..dece1aa1d 100644 --- a/build.rs +++ b/build.rs @@ -38,7 +38,7 @@ fn main() { }); let kotlin_files_path = - PathBuf::from(env_var("CARGO_MANIFEST_DIR")).join("src/webview/android/kotlin"); + PathBuf::from(env_var("CARGO_MANIFEST_DIR")).join("src/android/kotlin"); println!("cargo:rerun-if-changed={}", kotlin_files_path.display()); let kotlin_files = fs::read_dir(kotlin_files_path).expect("failed to read kotlin directory"); diff --git a/examples/winit.rs b/examples/winit.rs index d9429ab71..6091ce66b 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -33,7 +33,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let webview = WebViewBuilder::new(&window) + let _webview = WebViewBuilder::new(&window) .with_url("https://tauri.app")? .build()?; diff --git a/src/android/binding.rs b/src/android/binding.rs index 408416a41..064410650 100644 --- a/src/android/binding.rs +++ b/src/android/binding.rs @@ -6,13 +6,14 @@ use http::{ header::{HeaderName, HeaderValue, CONTENT_TYPE}, Request, }; -pub use tao::platform::android::ndk_glue::jni::sys::{jboolean, jstring}; -use tao::platform::android::ndk_glue::jni::{ - errors::Error as JniError, - objects::{JClass, JMap, JObject, JString}, - sys::jobject, +use jni::errors::Result as JniResult; +pub use jni::{ + self, + objects::{GlobalRef, JClass, JMap, JObject, JString}, + sys::{jboolean, jobject, jstring}, JNIEnv, }; +pub use ndk; use super::{ ASSET_LOADER_DOMAIN, IPC, ON_LOAD_HANDLER, REQUEST_HANDLER, TITLE_CHANGE_HANDLER, @@ -23,18 +24,12 @@ use crate::PageLoadEvent; #[macro_export] macro_rules! android_binding { - ($domain:ident, $package:ident, $main:ident) => { - android_binding!($domain, $package, $main, ::wry) + ($domain:ident, $package:ident) => { + ::wry::android_binding!($domain, $package, ::wry) }; - ($domain:ident, $package:ident, $main:ident, $wry:path) => { - use $wry::{ - application::{ - android_binding as tao_android_binding, android_fn, generate_package_name, - platform::android::ndk_glue::*, - }, - prelude::*, - }; - tao_android_binding!($domain, $package, WryActivity, setup, $main); + ($domain:ident, $package:ident, $wry:path) => {{ + use $wry::{prelude::android_fn, prelude::*}; + android_fn!( $domain, $package, @@ -97,10 +92,10 @@ macro_rules! android_binding { handleReceivedTitle, [JObject, JString], ); - }; + }}; } -fn handle_request(env: &mut JNIEnv, request: JObject) -> Result { +fn handle_request(env: &mut JNIEnv, request: JObject) -> JniResult { if let Some(handler) = REQUEST_HANDLER.get() { let mut request_builder = Request::builder(); @@ -113,10 +108,10 @@ fn handle_request(env: &mut JNIEnv, request: JObject) -> Result, Receiver)> = Lazy::new(|| bounded(8)); pub static MAIN_PIPE: Lazy<[RawFd; 2]> = Lazy::new(|| { @@ -39,7 +36,7 @@ impl<'a> MainPipe<'a> { } } - pub fn recv(&mut self) -> Result<(), JniError> { + pub fn recv(&mut self) -> JniResult<()> { let activity = self.activity.as_obj(); if let Ok(message) = CHANNEL.1.recv() { match message { @@ -265,7 +262,7 @@ fn load_url<'a>( url: &JString<'a>, headers: Option, main_thread: bool, -) -> Result<(), JniError> { +) -> JniResult<()> { let function = if main_thread { "loadUrlMainThread" } else { @@ -294,11 +291,7 @@ fn load_url<'a>( Ok(()) } -fn load_html<'a>( - env: &mut JNIEnv<'a>, - webview: &JObject<'a>, - html: &JString<'a>, -) -> Result<(), JniError> { +fn load_html<'a>(env: &mut JNIEnv<'a>, webview: &JObject<'a>, html: &JString<'a>) -> JniResult<()> { env.call_method( webview, "loadHTMLMainThread", @@ -312,7 +305,7 @@ fn set_background_color<'a>( env: &mut JNIEnv<'a>, webview: &JObject<'a>, background_color: RGBA, -) -> Result<(), JniError> { +) -> JniResult<()> { let color_class = env.find_class("android/graphics/Color")?; let color = env.call_static_method( color_class, @@ -349,6 +342,6 @@ pub(crate) struct CreateWebViewAttributes { pub background_color: Option, pub headers: Option, pub autoplay: bool, - pub on_webview_created: Option Result<(), JniError> + Send>>, + pub on_webview_created: Option JniResult<()> + Send>>, pub user_agent: Option, } diff --git a/src/android/mod.rs b/src/android/mod.rs index 2bd43af31..54a5d7871 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -11,20 +11,17 @@ use http::{ header::{HeaderValue, CONTENT_SECURITY_POLICY, CONTENT_TYPE}, Request, Response as HttpResponse, }; +use jni::{ + errors::Result as JniResult, + objects::{GlobalRef, JClass, JObject}, + JNIEnv, +}; use kuchiki::NodeRef; +use ndk::looper::{FdEvent, ForeignLooper}; use once_cell::sync::OnceCell; use raw_window_handle::HasWindowHandle; use sha2::{Digest, Sha256}; use std::{borrow::Cow, sync::mpsc::channel}; -use tao::platform::android::ndk_glue::{ - jni::{ - errors::Error as JniError, - objects::{GlobalRef, JClass, JObject}, - JNIEnv, - }, - ndk::looper::{FdEvent, ForeignLooper}, - PACKAGE, -}; use url::Url; pub(crate) mod binding; @@ -66,7 +63,17 @@ define_static_handlers! { pub static WITH_ASSET_LOADER: OnceCell = OnceCell::new(); pub static ASSET_LOADER_DOMAIN: OnceCell = OnceCell::new(); -pub unsafe fn setup(mut env: JNIEnv, looper: &ForeignLooper, activity: GlobalRef) { +pub(crate) static PACKAGE: OnceCell = OnceCell::new(); + +/// Sets up the necessary logic for wry to be able to create the webviews later. +pub unsafe fn android_setup( + package: &str, + mut env: JNIEnv, + looper: &ForeignLooper, + activity: GlobalRef, +) { + PACKAGE.get_or_init(move || package.to_string()); + // we must create the WebChromeClient here because it calls `registerForActivityResult`, // which gives an `LifecycleOwners must call register before they are STARTED.` error when called outside the onCreate hook let rust_webchrome_client_class = find_class( @@ -376,7 +383,7 @@ pub fn find_class<'a>( env: &mut JNIEnv<'a>, activity: &JObject<'_>, name: String, -) -> std::result::Result, JniError> { +) -> JniResult> { let class_name = env.new_string(name.replace('/', "."))?; let my_class = env .call_method( diff --git a/src/error.rs b/src/error.rs index 181254ca6..e2379bb88 100644 --- a/src/error.rs +++ b/src/error.rs @@ -79,7 +79,7 @@ pub enum Error { Infallible(#[from] std::convert::Infallible), #[cfg(target_os = "android")] #[error(transparent)] - JniError(#[from] tao::platform::android::ndk_glue::jni::errors::Error), + JniError(#[from] jni::errors::Error), #[error("Failed to create proxy endpoint")] ProxyEndpointCreationFailed, #[error(transparent)] diff --git a/src/lib.rs b/src/lib.rs index 49cc71d2a..6ab1c0676 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,6 +115,38 @@ //! } //! ``` //! +//! ## Android +//! +//! In order for `wry` to be able to create webviews on Android, there is a few requirements that your application needs to uphold: +//! 1. You need to set a few environment variables that will be used to generate the necessary kotlin +//! files that you need to include in your Android application for wry to function properly. +//! - `WRY_ANDROID_PACKAGE`: which is the reversed domain name of your android project and the app name in snake_case, for example, `com.wry.example.wry_app` +//! - `WRY_ANDROID_LIBRARY`: for example, if your cargo project has a lib name `wry_app`, it will generate `libwry_app.so` so you se this env var to `wry_app` +//! - `WRY_ANDROID_KOTLIN_FILES_OUT_DIR`: for example, `path/to/app/src/main/kotlin/com/wry/example` +//! 2. Your main Android Activity needs to inherit `AppCompatActivity`, preferably it should use the generated `WryActivity` or inherit it. +//! 3. Your Rust app needs to call `wry::android_setup` function to setup the necessary logic to be able to create webviews later on. +//! 4. Your Rust app needs to call `wry::android_binding!` macro to setup the JNI functions that will be called by `WryActivity` and various other places. +//! +//! It is recommended to use [`tao`](https://docs.rs/tao/latest/tao/) crate as it provides maximum compatibility with `wry` +//! +//! ``` +//! #[cfg(target_os = "android")] +//! { +//! tao::android_binding!( +//! com_example, +//! wry_app, +//! WryActivity, +//! wry::android_setup, // pass the wry::android_setup function to tao which will invoke when the event loop is created +//! _start_app +//! ); +//! wry::android_binding!(com_example, ttt); +//! } +//! ``` +//! +//! If this feels overwhelming, you can just use the preconfigured template from [`cargo-mobile2`](https://github.com/tauri-apps/cargo-mobile2). +//! +//! For more inforamtion, checkout [MOBILE.md](https://github.com/tauri-apps/wry/blob/dev/MOBILE.md). +//! //! ## Feature flags //! //! Wry uses a set of feature flags to toggle several advanced features. @@ -153,8 +185,11 @@ mod web_context; #[cfg(target_os = "android")] pub(crate) mod android; #[cfg(target_os = "android")] +pub use crate::android::android_setup; +#[cfg(target_os = "android")] pub mod prelude { - pub use super::android::{binding::*, dispatch, find_class, setup, Context}; + pub use crate::android::{binding::*, dispatch, find_class, Context}; + pub use tao_macros::{android_fn, generate_package_name}; } #[cfg(target_os = "android")] pub use android::JniHandle; @@ -1081,14 +1116,8 @@ impl WebViewBuilderExtWindows for WebViewBuilder<'_> { #[cfg(target_os = "android")] #[derive(Default)] pub(crate) struct PlatformSpecificWebViewAttributes { - on_webview_created: Option< - Box< - dyn Fn( - prelude::Context, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send, - >, - >, + on_webview_created: + Option std::result::Result<(), jni::errors::Error> + Send>>, with_asset_loader: bool, asset_loader_domain: Option, https_scheme: bool, @@ -1097,11 +1126,7 @@ pub(crate) struct PlatformSpecificWebViewAttributes { #[cfg(target_os = "android")] pub trait WebViewBuilderExtAndroid { fn on_webview_created< - F: Fn( - prelude::Context<'_, '_>, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send - + 'static, + F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error> + Send + 'static, >( self, f: F, @@ -1128,11 +1153,7 @@ pub trait WebViewBuilderExtAndroid { #[cfg(target_os = "android")] impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { fn on_webview_created< - F: Fn( - prelude::Context<'_, '_>, - ) -> std::result::Result<(), tao::platform::android::ndk_glue::jni::errors::Error> - + Send - + 'static, + F: Fn(prelude::Context<'_, '_>) -> std::result::Result<(), jni::errors::Error> + Send + 'static, >( mut self, f: F, From 3a90ae93c1681c6da3458397601b24e3884851b5 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Fri, 27 Oct 2023 05:00:36 +0200 Subject: [PATCH 50/80] fix winit example --- examples/winit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/winit.rs b/examples/winit.rs index 6091ce66b..31bc2831d 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -64,7 +64,7 @@ fn main() -> wry::Result<()> { event: WindowEvent::Resized(size), .. } => { - webview.set_size(size.into()); + _webview.set_size(size.into()); } Event::WindowEvent { event: WindowEvent::CloseRequested, From f41d9c3b5b88b8ecfc3fe0364d4b79126f8b6e16 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Sun, 29 Oct 2023 18:51:13 +0900 Subject: [PATCH 51/80] Add new_as_content_view on macOS --- src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6ab1c0676..69418766c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -534,7 +534,12 @@ impl<'a> WebViewBuilder<'a> { /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. - /// - **macOS / Windows**: The webview will auto-resize when the passed handle is resized. + /// - **macOS**: This method is as same as `new_as_child` which will create the webview as a `NSView` subview of the `parent` window's + /// content view. The webview will auto-resize when the passed handle is resized. If you wan + /// to prevent several bugs menu items, accelerators, IME, cursor icons not working because of + /// existing content view blocks the event chain (like how `winit` does), use `new_as_content_view` instead. + /// content view. + /// - **Windows**: The webview will auto-resize when the passed handle is resized. /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually. /// /// # Panics: @@ -545,7 +550,10 @@ impl<'a> WebViewBuilder<'a> { Self { attrs: WebViewAttributes::default(), window: Some(window), + #[cfg(not(target_os = "macos"))] as_child: false, + #[cfg(target_os = "macos")] + as_child: true, #[allow(clippy::default_constructed_unit_structs)] platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, @@ -598,6 +606,20 @@ impl<'a> WebViewBuilder<'a> { } } + /// Create the webview as the content view instead of subview on macOS to avoid original content + /// view blocks several event delegate methods. + #[cfg(target_os = "macos")] + pub fn new_as_content_view(window: &'a impl HasWindowHandle) -> Self { + Self { + attrs: WebViewAttributes::default(), + window: Some(window), + as_child: false, + #[allow(clippy::default_constructed_unit_structs)] + platform_specific: PlatformSpecificWebViewAttributes::default(), + web_context: None, + } + } + #[cfg(any( target_os = "linux", target_os = "dragonfly", From 94037cf6baf15515ab9d5df6d30812443219d4e0 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 08:02:03 -0300 Subject: [PATCH 52/80] allow using raw-window-handle v0.5 [skip ci] --- Cargo.toml | 9 +++++--- src/error.rs | 4 +++- src/lib.rs | 36 ++++++++++++++++++++--------- src/webkitgtk/mod.rs | 2 +- src/webview2/mod.rs | 4 ++-- src/wkwebview/mod.rs | 54 +++++++++++++++++++++++++++++++++++++------- 6 files changed, 83 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ceb8a05cd..49b113bd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["file-drop", "objc-exception", "protocol"] +default = ["rwh_06", "file-drop", "objc-exception", "protocol"] objc-exception = ["objc/exception"] file-drop = [] protocol = [] @@ -33,6 +33,8 @@ transparent = [] fullscreen = [] linux-body = ["webkit2gtk/v2_40"] mac-proxy = [] +rwh_05 = ["dep:rwh_05", "tao/rwh_05"] +rwh_06 = ["dep:rwh_06", "tao/rwh_06"] [dependencies] libc = "0.2" @@ -43,7 +45,8 @@ serde_json = "1.0" thiserror = "1.0" url = "2.4" http = "0.2" -raw-window-handle = { version = "0.6", features = ["std"] } +rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true } +rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] javascriptcore-rs = { version = "=1.1", features = ["v2_28"] } @@ -101,6 +104,6 @@ tao-macros = { version = "0.1.0" } [dev-dependencies] http-range = "0.1.5" pollster = "0.3.0" -tao = { git = "https://github.com/tauri-apps/tao" } +tao = { git = "https://github.com/tauri-apps/tao", default-features = false } wgpu = "0.18.0" winit = { version = "0.29", features = ["rwh_05"] } diff --git a/src/error.rs b/src/error.rs index e2379bb88..0e96bf623 100644 --- a/src/error.rs +++ b/src/error.rs @@ -83,7 +83,9 @@ pub enum Error { #[error("Failed to create proxy endpoint")] ProxyEndpointCreationFailed, #[error(transparent)] - WindowHandleError(#[from] raw_window_handle::HandleError), + WindowHandleError(#[from] crate::raw_window_handle::HandleError), + #[error("the window handle kind is not supported")] + UnsupportedWindowHandle, #[error(transparent)] Utf8Error(#[from] std::str::Utf8Error), } diff --git a/src/lib.rs b/src/lib.rs index 69418766c..a4b156b57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,17 @@ mod error; mod proxy; mod web_context; +#[cfg(feature = "rwh_05")] +pub use rwh_05 as raw_window_handle; +#[cfg(feature = "rwh_06")] +pub use rwh_06 as raw_window_handle; + +#[cfg(all(not(feature = "rwh_05"), not(feature = "rwh_06")))] +compile_error!("One of the rwh_05 or rwh_06 features must be enabled"); + +#[cfg(all(feature = "rwh_05", feature = "rwh_06"))] +compile_error!("Only one of the rwh_05 or rwh_06 features must be enabled"); + #[cfg(target_os = "android")] pub(crate) mod android; #[cfg(target_os = "android")] @@ -228,12 +239,15 @@ use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller; use std::{borrow::Cow, path::PathBuf, rc::Rc}; use http::{Request, Response}; -use raw_window_handle::HasWindowHandle; + +#[cfg(feature = "rwh_05")] +use raw_window_handle::HasRawWindowHandle as RawWindowHandle; +#[cfg(feature = "rwh_06")] +use raw_window_handle::HasWindowHandle as RawWindowHandle; pub use error::*; pub use http; pub use proxy::{ProxyConfig, ProxyEndpoint}; -pub use raw_window_handle; pub use url::Url; pub use web_context::WebContext; @@ -511,7 +525,7 @@ impl Default for WebViewAttributes { pub struct WebViewBuilder<'a> { pub attrs: WebViewAttributes, as_child: bool, - window: Option<&'a dyn HasWindowHandle>, + window: Option<&'a dyn RawWindowHandle>, platform_specific: PlatformSpecificWebViewAttributes, web_context: Option<&'a mut WebContext>, #[cfg(any( @@ -525,7 +539,7 @@ pub struct WebViewBuilder<'a> { } impl<'a> WebViewBuilder<'a> { - /// Create a [`WebViewBuilder`] from a type that implements [`HasWindowHandle`]. + /// Create a [`WebViewBuilder`] from a type that implements [`RawWindowHandle`]. /// /// # Platform-specific: /// @@ -546,7 +560,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &'a impl HasWindowHandle) -> Self { + pub fn new(window: &'a impl RawWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -568,7 +582,7 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create [`WebViewBuilder`] as a child window inside the provided [`HasWindowHandle`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. /// /// ## Platform-specific /// @@ -587,7 +601,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &'a impl HasWindowHandle) -> Self { + pub fn new_as_child(parent: &'a impl RawWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(parent), @@ -609,7 +623,7 @@ impl<'a> WebViewBuilder<'a> { /// Create the webview as the content view instead of subview on macOS to avoid original content /// view blocks several event delegate methods. #[cfg(target_os = "macos")] - pub fn new_as_content_view(window: &'a impl HasWindowHandle) -> Self { + pub fn new_as_content_view(window: &'a impl RawWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -1235,11 +1249,11 @@ impl WebView { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &impl HasWindowHandle) -> Result { + pub fn new(window: &impl RawWindowHandle) -> Result { WebViewBuilder::new(window).build() } - /// Create [`WebViewBuilder`] as a child window inside the provided [`HasWindowHandle`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. /// /// ## Platform-specific /// @@ -1258,7 +1272,7 @@ impl WebView { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &impl HasWindowHandle) -> Result { + pub fn new_as_child(parent: &impl RawWindowHandle) -> Result { WebViewBuilder::new_as_child(parent).build() } diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 20fd44adf..26efbaf6f 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -94,7 +94,7 @@ impl InnerWebView { let window = match window.window_handle()?.as_raw() { RawWindowHandle::Xlib(w) => w.window, - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + _ => return Err(Error::UnsupportedWindowHandle), }; let gdk_display = gdk::Display::default().ok_or(Error::X11DisplayNotFound)?; diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 7b0680f31..6a01ad093 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -72,7 +72,7 @@ impl InnerWebView { ) -> Result { let window = match window.window_handle()?.as_raw() { RawWindowHandle::Win32(window) => window.hwnd.get(), - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + _ => return Err(Error::UnsupportedWindowHandle), }; Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) } @@ -85,7 +85,7 @@ impl InnerWebView { ) -> Result { let parent = match parent.window_handle()?.as_raw() { RawWindowHandle::Win32(parent) => parent.hwnd.get(), - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + _ => return Err(Error::UnsupportedWindowHandle), }; let class_name = encode_wide("WRY_WEBVIEW"); diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 589999d6c..6b9d3950e 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -11,7 +11,7 @@ mod proxy; #[cfg(target_os = "macos")] mod synthetic_mouse_events; -use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; +use crate::raw_window_handle::RawWindowHandle; use url::Url; #[cfg(target_os = "macos")] @@ -56,8 +56,7 @@ use crate::{ }, navigation::{add_navigation_mathods, drop_navigation_methods, set_navigation_methods}, }, - Error, FileDropEvent, PageLoadEvent, RequestAsyncResponder, Result, WebContext, - WebViewAttributes, RGBA, + Error, PageLoadEvent, RequestAsyncResponder, Result, WebContext, WebViewAttributes, RGBA, }; use http::{ @@ -68,6 +67,7 @@ use http::{ }; const IPC_MESSAGE_HANDLER_NAME: &str = "ipc"; +#[cfg(target_os = "macos")] const ACCEPT_FIRST_MOUSE: &str = "accept_first_mouse"; const NS_JSON_WRITING_FRAGMENTS_ALLOWED: u64 = 4; @@ -86,14 +86,33 @@ pub(crate) struct InnerWebView { navigation_decide_policy_ptr: *mut Box bool>, page_load_handler: *mut Box, #[cfg(target_os = "macos")] - file_drop_ptr: *mut Box bool>, + file_drop_ptr: *mut Box bool>, download_delegate: id, protocol_ptrs: Vec<*mut Box>, RequestAsyncResponder)>>, } impl InnerWebView { + #[cfg(feature = "rwh_05")] + pub fn new( + window: &impl crate::RawWindowHandle, + attributes: WebViewAttributes, + _pl_attrs: super::PlatformSpecificWebViewAttributes, + _web_context: Option<&mut WebContext>, + ) -> Result { + let ns_view = match window.raw_window_handle() { + #[cfg(target_os = "macos")] + RawWindowHandle::AppKit(w) => w.ns_view, + #[cfg(target_os = "ios")] + RawWindowHandle::UiKit(w) => w.ui_view, + _ => return Err(Error::UnsupportedWindowHandle), + }; + + Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, false) + } + + #[cfg(feature = "rwh_06")] pub fn new( - window: &impl HasWindowHandle, + window: &impl crate::RawWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -103,14 +122,33 @@ impl InnerWebView { RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), #[cfg(target_os = "ios")] RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + _ => return Err(Error::UnsupportedWindowHandle), }; Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, false) } + #[cfg(feature = "rwh_05")] + pub fn new_as_child( + window: &impl crate::RawWindowHandle, + attributes: WebViewAttributes, + _pl_attrs: super::PlatformSpecificWebViewAttributes, + _web_context: Option<&mut WebContext>, + ) -> Result { + let ns_view = match window.raw_window_handle() { + #[cfg(target_os = "macos")] + RawWindowHandle::AppKit(w) => w.ns_view, + #[cfg(target_os = "ios")] + RawWindowHandle::UiKit(w) => w.ui_view, + _ => return Err(Error::UnsupportedWindowHandle), + }; + + Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, true) + } + + #[cfg(feature = "rwh_06")] pub fn new_as_child( - window: &impl HasWindowHandle, + window: &impl crate::RawWindowHandle, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -120,7 +158,7 @@ impl InnerWebView { RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), #[cfg(target_os = "ios")] RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), - _ => return Err(Error::WindowHandleError(HandleError::NotSupported)), + _ => return Err(Error::UnsupportedWindowHandle), }; Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, true) From 9d90e235863e4bbcf5e2672df63542f7160b35fc Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 08:40:49 -0300 Subject: [PATCH 53/80] change trait name [skip ci] --- src/lib.rs | 22 +++++++++++----------- src/wkwebview/mod.rs | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a4b156b57..0ef6610ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,9 +241,9 @@ use std::{borrow::Cow, path::PathBuf, rc::Rc}; use http::{Request, Response}; #[cfg(feature = "rwh_05")] -use raw_window_handle::HasRawWindowHandle as RawWindowHandle; +use raw_window_handle::HasRawWindowHandle as RawWindowHandleTrait; #[cfg(feature = "rwh_06")] -use raw_window_handle::HasWindowHandle as RawWindowHandle; +use raw_window_handle::HasWindowHandle as RawWindowHandleTrait; pub use error::*; pub use http; @@ -525,7 +525,7 @@ impl Default for WebViewAttributes { pub struct WebViewBuilder<'a> { pub attrs: WebViewAttributes, as_child: bool, - window: Option<&'a dyn RawWindowHandle>, + window: Option<&'a dyn RawWindowHandleTrait>, platform_specific: PlatformSpecificWebViewAttributes, web_context: Option<&'a mut WebContext>, #[cfg(any( @@ -539,7 +539,7 @@ pub struct WebViewBuilder<'a> { } impl<'a> WebViewBuilder<'a> { - /// Create a [`WebViewBuilder`] from a type that implements [`RawWindowHandle`]. + /// Create a [`WebViewBuilder`] from a type that implements [`RawWindowHandleTrait`]. /// /// # Platform-specific: /// @@ -560,7 +560,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &'a impl RawWindowHandle) -> Self { + pub fn new(window: &'a impl RawWindowHandleTrait) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -582,7 +582,7 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandleTrait`]. /// /// ## Platform-specific /// @@ -601,7 +601,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &'a impl RawWindowHandle) -> Self { + pub fn new_as_child(parent: &'a impl RawWindowHandleTrait) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(parent), @@ -623,7 +623,7 @@ impl<'a> WebViewBuilder<'a> { /// Create the webview as the content view instead of subview on macOS to avoid original content /// view blocks several event delegate methods. #[cfg(target_os = "macos")] - pub fn new_as_content_view(window: &'a impl RawWindowHandle) -> Self { + pub fn new_as_content_view(window: &'a impl RawWindowHandleTrait) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -1249,11 +1249,11 @@ impl WebView { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &impl RawWindowHandle) -> Result { + pub fn new(window: &impl RawWindowHandleTrait) -> Result { WebViewBuilder::new(window).build() } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandle`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandleTrait`]. /// /// ## Platform-specific /// @@ -1272,7 +1272,7 @@ impl WebView { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &impl RawWindowHandle) -> Result { + pub fn new_as_child(parent: &impl RawWindowHandleTrait) -> Result { WebViewBuilder::new_as_child(parent).build() } diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 6b9d3950e..c4166eb41 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -94,7 +94,7 @@ pub(crate) struct InnerWebView { impl InnerWebView { #[cfg(feature = "rwh_05")] pub fn new( - window: &impl crate::RawWindowHandle, + window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -112,7 +112,7 @@ impl InnerWebView { #[cfg(feature = "rwh_06")] pub fn new( - window: &impl crate::RawWindowHandle, + window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -130,7 +130,7 @@ impl InnerWebView { #[cfg(feature = "rwh_05")] pub fn new_as_child( - window: &impl crate::RawWindowHandle, + window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -148,7 +148,7 @@ impl InnerWebView { #[cfg(feature = "rwh_06")] pub fn new_as_child( - window: &impl crate::RawWindowHandle, + window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, From 0b9631a08e1435e7db62f5ae9a0f7d3981f05288 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 08:45:40 -0300 Subject: [PATCH 54/80] fix linux impl [skip ci] --- src/webkitgtk/mod.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 26efbaf6f..b0fd874fc 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -8,7 +8,6 @@ use gtk::gdk::{self, EventMask}; use gtk::gio::Cancellable; use gtk::prelude::*; use javascriptcore::ValueExt; -use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; #[cfg(any(debug_assertions, feature = "devtools"))] use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -34,6 +33,8 @@ use crate::{ RGBA, }; +use crate::{raw_window_handle::RawWindowHandle, RawWindowHandleTrait}; + mod file_drop; mod synthetic_mouse_events; mod web_context; @@ -65,7 +66,7 @@ impl Drop for InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -74,7 +75,7 @@ impl InnerWebView { Self::new_x11(window, attributes, pl_attrs, web_context, false) } - pub fn new_as_child( + pub fn new_as_child( parent: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -83,7 +84,7 @@ impl InnerWebView { Self::new_x11(parent, attributes, pl_attrs, web_context, true) } - fn new_x11( + fn new_x11( window: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -92,7 +93,14 @@ impl InnerWebView { ) -> Result { let xlib = Xlib::open()?; - let window = match window.window_handle()?.as_raw() { + #[cfg(feature = "rwh_05")] + let window_handle = match window.raw_window_handle() { + RawWindowHandle::Xlib(w) => w.window, + _ => return Err(Error::UnsupportedWindowHandle), + }; + + #[cfg(feature = "rwh_06")] + let window_handle = match window.window_handle()?.as_raw() { RawWindowHandle::Xlib(w) => w.window, _ => return Err(Error::UnsupportedWindowHandle), }; @@ -106,7 +114,7 @@ impl InnerWebView { let child = unsafe { (xlib.XCreateSimpleWindow)( display as _, - window, + window_handle, attributes.position.map(|p| p.0).unwrap_or(0), attributes.position.map(|p| p.1).unwrap_or(0), attributes.size.map(|s| s.0).unwrap_or(0), @@ -121,7 +129,7 @@ impl InnerWebView { } child } else { - window + window_handle }; let gdk_window = unsafe { From 8566a88f2aa3accec198e05d03a6b775b05f0f2a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 08:47:27 -0300 Subject: [PATCH 55/80] fix android build [skip ci] --- src/android/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/android/mod.rs b/src/android/mod.rs index 54a5d7871..6d6c6ac76 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT use super::{PageLoadEvent, WebContext, WebViewAttributes, RGBA}; +use crate::RawWindowHandleTrait; use crate::{RequestAsyncResponder, Result}; use base64::{engine::general_purpose, Engine}; use crossbeam_channel::*; @@ -19,7 +20,6 @@ use jni::{ use kuchiki::NodeRef; use ndk::looper::{FdEvent, ForeignLooper}; use once_cell::sync::OnceCell; -use raw_window_handle::HasWindowHandle; use sha2::{Digest, Sha256}; use std::{borrow::Cow, sync::mpsc::channel}; use url::Url; @@ -115,7 +115,7 @@ pub(crate) struct InnerWebView; impl InnerWebView { pub fn new_as_child( - _window: &impl HasWindowHandle, + _window: &impl RawWindowHandleTrait, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -124,7 +124,7 @@ impl InnerWebView { } pub fn new( - _window: &impl HasWindowHandle, + _window: &impl RawWindowHandleTrait, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, From 71638f54592ad5db40fa6192294e19708cb1d496 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 08:53:10 -0300 Subject: [PATCH 56/80] fix windows build --- src/webview2/mod.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 6a01ad093..78d3b68f7 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -11,7 +11,6 @@ use std::{ use http::{Request, Response as HttpResponse, StatusCode}; use once_cell::sync::Lazy; -use raw_window_handle::{HandleError, HasWindowHandle, RawWindowHandle}; use url::Url; use webview2_com::{Microsoft::Web::WebView2::Win32::*, *}; use windows::{ @@ -41,8 +40,8 @@ use windows::{ use self::file_drop::FileDropController; use super::Theme; use crate::{ - proxy::ProxyConfig, Error, PageLoadEvent, RequestAsyncResponder, Result, WebContext, - WebViewAttributes, RGBA, + proxy::ProxyConfig, raw_window_handle::RawWindowHandle, Error, PageLoadEvent, + RawWindowHandleTrait, RequestAsyncResponder, Result, WebContext, WebViewAttributes, RGBA, }; impl From for Error { @@ -64,8 +63,23 @@ pub(crate) struct InnerWebView { } impl InnerWebView { + #[cfg(feature = "rwh_05")] pub fn new( - window: &impl HasWindowHandle, + window: &impl RawWindowHandleTrait, + attributes: WebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, + web_context: Option<&mut WebContext>, + ) -> Result { + let window = match window.raw_window_handle() { + RawWindowHandle::Win32(window) => window.hwnd as _, + _ => return Err(Error::UnsupportedWindowHandle), + }; + Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) + } + + #[cfg(feature = "rwh_06")] + pub fn new( + window: &impl RawWindowHandleTrait, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, @@ -78,11 +92,17 @@ impl InnerWebView { } pub fn new_as_child( - parent: &impl HasWindowHandle, + parent: &impl RawWindowHandleTrait, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { + #[cfg(feature = "rwh_05")] + let parent = match parent.raw_window_handle() { + RawWindowHandle::Win32(parent) => parent.hwnd as _, + _ => return Err(Error::UnsupportedWindowHandle), + }; + #[cfg(feature = "rwh_06")] let parent = match parent.window_handle()?.as_raw() { RawWindowHandle::Win32(parent) => parent.hwnd.get(), _ => return Err(Error::UnsupportedWindowHandle), From 06d7f918e91dcbb79a756ab4f4b78a68f75bf8fa Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 13:23:27 -0300 Subject: [PATCH 57/80] fix(macos): do not autoresize on child webview [skip ci] --- src/wkwebview/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index c4166eb41..6c69c3944 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -441,7 +441,9 @@ impl InnerWebView { ); let _: () = msg_send![webview, initWithFrame:frame configuration:config]; // Auto-resize on macOS - webview.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); + if !is_child { + webview.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); + } } #[cfg(target_os = "ios")] From b3f8bdb1c10a01ecaff24132ee18a5fb961bd397 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 14:13:58 -0300 Subject: [PATCH 58/80] fix macos coordinates [skip ci] --- examples/multiwebview.rs | 2 +- src/wkwebview/mod.rs | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs index 728587c94..88641dea9 100644 --- a/examples/multiwebview.rs +++ b/examples/multiwebview.rs @@ -37,7 +37,7 @@ fn main() -> wry::Result<()> { let webview = WebViewBuilder::new_as_child(&window) .with_position((0, 0)) - .with_size((size.width / 4, size.height / 4)) + .with_size((size.width / 2, size.height / 2)) .with_url("https://tauri.app")? .build()?; let webview2 = WebViewBuilder::new_as_child(&window) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 6c69c3944..9b2231daa 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -17,6 +17,7 @@ use url::Url; #[cfg(target_os = "macos")] use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable}; use cocoa::{ + appkit::NSWindow, base::{id, nil, NO, YES}, foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; @@ -436,7 +437,7 @@ impl InnerWebView { let (x, y) = attributes.position.unwrap_or((0, 0)); let (w, h) = attributes.size.unwrap_or((0, 0)); let frame: CGRect = CGRect::new( - &CGPoint::new(x as f64, y as f64), + &window_position(ns_view, (x, y), (w as f64, h as f64)), &CGSize::new(w as f64, h as f64), ); let _: () = msg_send![webview, initWithFrame:frame configuration:config]; @@ -1116,7 +1117,11 @@ r#"Object.defineProperty(window, 'ipc', { if self.is_child { unsafe { let mut frame: CGRect = msg_send![self.webview, frame]; - frame.origin = CGPoint::new(position.0 as f64, position.1 as f64); + frame.origin = window_position( + msg_send![self.webview, superview], + (position.0, position.1), + (frame.size.width, frame.size.height), + ); let () = msg_send![self.webview, setFrame: frame]; } } @@ -1270,3 +1275,14 @@ impl From for NSString { } struct NSData(id); + +/// Converts from wry screen-coordinates to macOS screen-coordinates. +/// wry: top-left is (0, 0) and y increasing downwards +/// macOS: bottom-left is (0, 0) and y increasing upwards +unsafe fn window_position(ns_view: id, position: (i32, i32), size: (f64, f64)) -> CGPoint { + let frame = NSWindow::frame(ns_view); + CGPoint::new( + position.0 as f64, + frame.size.height - position.1 as f64 - size.1, + ) +} From 12f3c25cb3580aab840c775337483125bb4bb71b Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 14:28:57 -0300 Subject: [PATCH 59/80] fix example [skip ci] --- examples/multiwebview.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs index 88641dea9..e41b52093 100644 --- a/examples/multiwebview.rs +++ b/examples/multiwebview.rs @@ -78,6 +78,7 @@ fn main() -> wry::Result<()> { } => { let size = size.to_logical::(window.scale_factor()); webview.set_size((size.width / 2, size.height / 2)); + webview.set_position((0, 0)); webview2.set_position(((size.width / 2) as i32, 0)); webview2.set_size((size.width / 2, size.height / 2)); webview3.set_position((0, (size.height / 2) as i32)); From c9c3283b484b7d3cae51727ae25aa702003087b7 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 14:46:18 -0300 Subject: [PATCH 60/80] fixed child on macos [skip ci] --- src/wkwebview/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 9b2231daa..658465d80 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -17,7 +17,7 @@ use url::Url; #[cfg(target_os = "macos")] use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable}; use cocoa::{ - appkit::NSWindow, + appkit::{NSViewMinYMargin, NSWindow}, base::{id, nil, NO, YES}, foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; @@ -441,8 +441,11 @@ impl InnerWebView { &CGSize::new(w as f64, h as f64), ); let _: () = msg_send![webview, initWithFrame:frame configuration:config]; - // Auto-resize on macOS - if !is_child { + if is_child { + // fixed element + webview.setAutoresizingMask_(NSViewMinYMargin); + } else { + // Auto-resize webview.setAutoresizingMask_(NSViewHeightSizable | NSViewWidthSizable); } } From 6d5bbffae8af1280cce71f45aef8f17177171291 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 14:54:59 -0300 Subject: [PATCH 61/80] fix docs typos [skip ci] --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0ef6610ee..ef99f500b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -545,12 +545,12 @@ impl<'a> WebViewBuilder<'a> { /// /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilder::new_gtk`]. /// - /// Although this methods only needs an X11 window handle, you use webkit2gtk, so you still need to initialize gtk + /// Although this methods only needs an X11 window handle, we use webkit2gtk, so you still need to initialize gtk /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// - **macOS**: This method is as same as `new_as_child` which will create the webview as a `NSView` subview of the `parent` window's - /// content view. The webview will auto-resize when the passed handle is resized. If you wan - /// to prevent several bugs menu items, accelerators, IME, cursor icons not working because of + /// content view. The webview will auto-resize when the passed handle is resized. If you want + /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of /// existing content view blocks the event chain (like how `winit` does), use `new_as_content_view` instead. /// content view. /// - **Windows**: The webview will auto-resize when the passed handle is resized. From 6fd35b71f04684136c70758cae67e00071ce5c0a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 15:07:50 -0300 Subject: [PATCH 62/80] fix webview position when it's not a child [skip ci] --- src/wkwebview/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 658465d80..b7465bf33 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -437,7 +437,11 @@ impl InnerWebView { let (x, y) = attributes.position.unwrap_or((0, 0)); let (w, h) = attributes.size.unwrap_or((0, 0)); let frame: CGRect = CGRect::new( - &window_position(ns_view, (x, y), (w as f64, h as f64)), + &window_position( + if is_child { ns_view } else { webview }, + (x, y), + (w as f64, h as f64), + ), &CGSize::new(w as f64, h as f64), ); let _: () = msg_send![webview, initWithFrame:frame configuration:config]; From e4b409c2f76549fa54c6af00d303aaae4b6bf051 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 15:30:36 -0300 Subject: [PATCH 63/80] replace new_as_content_view with new_as_subview --- src/lib.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef99f500b..614f35863 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -548,11 +548,6 @@ impl<'a> WebViewBuilder<'a> { /// Although this methods only needs an X11 window handle, we use webkit2gtk, so you still need to initialize gtk /// by callling [`gtk::init`] and advance its loop alongside your event loop using [`gtk::main_iteration_do`]. /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. - /// - **macOS**: This method is as same as `new_as_child` which will create the webview as a `NSView` subview of the `parent` window's - /// content view. The webview will auto-resize when the passed handle is resized. If you want - /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of - /// existing content view blocks the event chain (like how `winit` does), use `new_as_content_view` instead. - /// content view. /// - **Windows**: The webview will auto-resize when the passed handle is resized. /// - **Linux (X11)**: Unlike macOS and Windows, the webview will not auto-resize and you'll need to call [`WebView::set_size`] manually. /// @@ -564,10 +559,7 @@ impl<'a> WebViewBuilder<'a> { Self { attrs: WebViewAttributes::default(), window: Some(window), - #[cfg(not(target_os = "macos"))] as_child: false, - #[cfg(target_os = "macos")] - as_child: true, #[allow(clippy::default_constructed_unit_structs)] platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, @@ -587,8 +579,7 @@ impl<'a> WebViewBuilder<'a> { /// ## Platform-specific /// /// - **Windows**: This will create the webview as a child window of the `parent` window. - /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's - /// content view. + /// - **macOS**: This will create the webview as the contentView of the `parent` window. /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11 /// is supported. This method won't work on Wayland. /// @@ -620,14 +611,16 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create the webview as the content view instead of subview on macOS to avoid original content - /// view blocks several event delegate methods. + /// This method is as same as `new_as_child` which will create the webview as a `NSView` subview of the `parent` window's + /// content view. The webview will auto-resize when the passed handle is resized. If you want + /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of + /// existing content view blocks the event chain (like how `winit` does), use `new` instead. #[cfg(target_os = "macos")] - pub fn new_as_content_view(window: &'a impl RawWindowHandleTrait) -> Self { + pub fn new_as_subview(window: &'a impl RawWindowHandleTrait) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), - as_child: false, + as_child: true, #[allow(clippy::default_constructed_unit_structs)] platform_specific: PlatformSpecificWebViewAttributes::default(), web_context: None, From 7775ec3bf7424e891e197fafed5c8c64bb304176 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 15:42:33 -0300 Subject: [PATCH 64/80] with_as_subview instead of constructor [skip ci] --- examples/winit.rs | 10 +++++++--- src/lib.rs | 40 +++++++++++++++++++++++----------------- src/wkwebview/mod.rs | 20 ++++++++++---------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/examples/winit.rs b/examples/winit.rs index 31bc2831d..f6d5abab1 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -33,9 +33,13 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); - let _webview = WebViewBuilder::new(&window) - .with_url("https://tauri.app")? - .build()?; + let mut builder = WebViewBuilder::new(&window); + #[cfg(target_os = "macos")] + { + use wry::WebViewBuilderExtMacOS; + builder = builder.with_as_subview(true); + } + let _webview = builder.with_url("https://tauri.app")?.build()?; event_loop .run(move |event, evl| { diff --git a/src/lib.rs b/src/lib.rs index 614f35863..35e61df41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -611,22 +611,6 @@ impl<'a> WebViewBuilder<'a> { } } - /// This method is as same as `new_as_child` which will create the webview as a `NSView` subview of the `parent` window's - /// content view. The webview will auto-resize when the passed handle is resized. If you want - /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of - /// existing content view blocks the event chain (like how `winit` does), use `new` instead. - #[cfg(target_os = "macos")] - pub fn new_as_subview(window: &'a impl RawWindowHandleTrait) -> Self { - Self { - attrs: WebViewAttributes::default(), - window: Some(window), - as_child: true, - #[allow(clippy::default_constructed_unit_structs)] - platform_specific: PlatformSpecificWebViewAttributes::default(), - web_context: None, - } - } - #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -1213,6 +1197,29 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { } } +#[cfg(target_os = "macos")] +#[derive(Default)] +pub(crate) struct PlatformSpecificWebViewAttributes { + as_subview: bool, +} + +#[cfg(target_os = "macos")] +pub trait WebViewBuilderExtMacOS { + /// create the webview as a `NSView` subview of the `parent` window's + /// content view. The webview will auto-resize when the passed handle is resized. If you want + /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of + /// existing content view blocks the event chain (like how `winit` does), do not enable this. + fn with_as_subview(self, subview: bool) -> Self; +} + +#[cfg(target_os = "macos")] +impl WebViewBuilderExtMacOS for WebViewBuilder<'_> { + fn with_as_subview(mut self, subview: bool) -> Self { + self.platform_specific.as_subview = subview; + self + } +} + /// The fundamental type to present a [`WebView`]. /// /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and @@ -1556,7 +1563,6 @@ pub enum PageLoadEvent { target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "macos", target_os = "ios", ))] #[derive(Default)] diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index b7465bf33..6a84c5b96 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -97,7 +97,7 @@ impl InnerWebView { pub fn new( window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { let ns_view = match window.raw_window_handle() { @@ -108,14 +108,14 @@ impl InnerWebView { _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, false) + Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, false) } #[cfg(feature = "rwh_06")] pub fn new( window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { let ns_view = match window.window_handle()?.as_raw() { @@ -126,14 +126,14 @@ impl InnerWebView { _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, false) + Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, false) } #[cfg(feature = "rwh_05")] pub fn new_as_child( window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { let ns_view = match window.raw_window_handle() { @@ -144,14 +144,14 @@ impl InnerWebView { _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, true) + Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, true) } #[cfg(feature = "rwh_06")] pub fn new_as_child( window: &impl crate::RawWindowHandleTrait, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, ) -> Result { let ns_view = match window.window_handle()?.as_raw() { @@ -162,13 +162,13 @@ impl InnerWebView { _ => return Err(Error::UnsupportedWindowHandle), }; - Self::new_ns_view(ns_view as _, attributes, _pl_attrs, _web_context, true) + Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, true) } fn new_ns_view( ns_view: id, attributes: WebViewAttributes, - _pl_attrs: super::PlatformSpecificWebViewAttributes, + pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, is_child: bool, ) -> Result { @@ -902,7 +902,7 @@ r#"Object.defineProperty(window, 'ipc', { // Inject the web view into the window as main content #[cfg(target_os = "macos")] { - if is_child { + if is_child || pl_attrs.as_subview { let _: () = msg_send![ns_view, addSubview: webview]; } else { let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { From 4df7a51a8d50146a792d20d251485b90c8be2e61 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 15:58:50 -0300 Subject: [PATCH 65/80] fix position/size when as_subview is true --- src/wkwebview/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 6a84c5b96..eace54470 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -435,10 +435,22 @@ impl InnerWebView { #[cfg(target_os = "macos")] { let (x, y) = attributes.position.unwrap_or((0, 0)); - let (w, h) = attributes.size.unwrap_or((0, 0)); + + let (w, h) = attributes.size.unwrap_or_else(|| { + if pl_attrs.as_subview { + let frame = NSView::frame(ns_view); + (frame.size.width as u32, frame.size.height as u32) + } else { + (0, 0) + } + }); let frame: CGRect = CGRect::new( &window_position( - if is_child { ns_view } else { webview }, + if is_child || pl_attrs.as_subview { + ns_view + } else { + webview + }, (x, y), (w as f64, h as f64), ), @@ -1286,8 +1298,8 @@ struct NSData(id); /// Converts from wry screen-coordinates to macOS screen-coordinates. /// wry: top-left is (0, 0) and y increasing downwards /// macOS: bottom-left is (0, 0) and y increasing upwards -unsafe fn window_position(ns_view: id, position: (i32, i32), size: (f64, f64)) -> CGPoint { - let frame = NSWindow::frame(ns_view); +unsafe fn window_position(view: id, position: (i32, i32), size: (f64, f64)) -> CGPoint { + let frame = NSWindow::frame(view); CGPoint::new( position.0 as f64, frame.size.height - position.1 as f64 - size.1, From 781b86fe95b4a35fa67f40d8b772f16cfbd9b659 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 31 Oct 2023 16:03:37 -0300 Subject: [PATCH 66/80] lint & fmt --- examples/winit.rs | 1 + src/android/binding.rs | 2 +- src/android/mod.rs | 3 +-- src/webkitgtk/mod.rs | 14 +++++++++----- src/webkitgtk/synthetic_mouse_events.rs | 6 ++++-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/examples/winit.rs b/examples/winit.rs index f6d5abab1..cf981158d 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -33,6 +33,7 @@ fn main() -> wry::Result<()> { .build(&event_loop) .unwrap(); + #[allow(unused_mut)] let mut builder = WebViewBuilder::new(&window); #[cfg(target_os = "macos")] { diff --git a/src/android/binding.rs b/src/android/binding.rs index 064410650..15c316387 100644 --- a/src/android/binding.rs +++ b/src/android/binding.rs @@ -28,7 +28,7 @@ macro_rules! android_binding { ::wry::android_binding!($domain, $package, ::wry) }; ($domain:ident, $package:ident, $wry:path) => {{ - use $wry::{prelude::android_fn, prelude::*}; + use $wry::prelude::*; android_fn!( $domain, diff --git a/src/android/mod.rs b/src/android/mod.rs index 6d6c6ac76..e5c442da5 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -3,8 +3,7 @@ // SPDX-License-Identifier: MIT use super::{PageLoadEvent, WebContext, WebViewAttributes, RGBA}; -use crate::RawWindowHandleTrait; -use crate::{RequestAsyncResponder, Result}; +use crate::{RawWindowHandleTrait, RequestAsyncResponder, Result}; use base64::{engine::general_purpose, Engine}; use crossbeam_channel::*; use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName}; diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index b0fd874fc..50ac4732f 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -2,11 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use gdkx11::glib::translate::{FromGlibPtrFull, ToGlibPtr}; -use gdkx11::X11Display; -use gtk::gdk::{self, EventMask}; -use gtk::gio::Cancellable; -use gtk::prelude::*; +use gdkx11::{ + glib::translate::{FromGlibPtrFull, ToGlibPtr}, + X11Display, +}; +use gtk::{ + gdk::{self, EventMask}, + gio::Cancellable, + prelude::*, +}; use javascriptcore::ValueExt; #[cfg(any(debug_assertions, feature = "devtools"))] use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/webkitgtk/synthetic_mouse_events.rs b/src/webkitgtk/synthetic_mouse_events.rs index b1446a9bf..e7253fa5f 100644 --- a/src/webkitgtk/synthetic_mouse_events.rs +++ b/src/webkitgtk/synthetic_mouse_events.rs @@ -1,7 +1,9 @@ use std::{cell::RefCell, rc::Rc}; -use gtk::gdk::{EventButton, ModifierType}; -use gtk::prelude::*; +use gtk::{ + gdk::{EventButton, ModifierType}, + prelude::*, +}; use webkit2gtk::{WebView, WebViewExt}; pub fn setup(webview: &WebView) { From cac3a6d3f1ed25e01210cebb74223ad4fa1c2abc Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 6 Nov 2023 18:58:47 +0900 Subject: [PATCH 67/80] Fix ios build --- .github/workflows/clippy-fmt.yml | 2 +- src/wkwebview/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clippy-fmt.yml b/.github/workflows/clippy-fmt.yml index 1d297d787..216ab24f3 100644 --- a/.github/workflows/clippy-fmt.yml +++ b/.github/workflows/clippy-fmt.yml @@ -35,7 +35,7 @@ jobs: with: components: clippy - - run: cargo clippy --all-targets --all-features + - run: cargo clippy --all-targets fmt: runs-on: ubuntu-latest diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index eace54470..374a6666d 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -15,9 +15,8 @@ use crate::raw_window_handle::RawWindowHandle; use url::Url; #[cfg(target_os = "macos")] -use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable}; use cocoa::{ - appkit::{NSViewMinYMargin, NSWindow}, + appkit::{NSView, NSViewHeightSizable, NSViewMinYMargin, NSViewWidthSizable, NSWindow}, base::{id, nil, NO, YES}, foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; From a79d31a9a7ac9dc8be1543fa7863fd2ee5c241c9 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 6 Nov 2023 19:23:57 +0900 Subject: [PATCH 68/80] Fix cargo test --- examples/wgpu.rs | 2 ++ src/lib.rs | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/wgpu.rs b/examples/wgpu.rs index 759b1b4f5..76038d139 100644 --- a/examples/wgpu.rs +++ b/examples/wgpu.rs @@ -6,6 +6,7 @@ use winit::{ }; use wry::WebViewBuilder; +#[cfg(feature = "rwh_05")] async fn run(event_loop: EventLoop<()>, window: Window) { let size = window.inner_size(); @@ -194,5 +195,6 @@ fn main() { let event_loop = EventLoop::new().unwrap(); let window = winit::window::Window::new(&event_loop).unwrap(); + #[cfg(feature = "rwh_05")] pollster::block_on(run(event_loop, window)); } diff --git a/src/lib.rs b/src/lib.rs index 8e273233d..afffe60a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,6 @@ //! ```no_run //! use wry::WebViewBuilder; //! -//! # use raw_window_handle as rwh_06; //! # struct T; //! # impl rwh_06::HasWindowHandle for T { //! # fn window_handle(&self) -> Result, rwh_06::HandleError> { @@ -72,7 +71,6 @@ //! ```no_run //! use wry::WebViewBuilder; //! -//! # use raw_window_handle as rwh_06; //! # struct T; //! # impl rwh_06::HasWindowHandle for T { //! # fn window_handle(&self) -> Result, rwh_06::HandleError> { @@ -747,7 +745,6 @@ impl<'a> WebViewBuilder<'a> { /// ```no_run /// use wry::WebViewBuilder; /// - /// # use raw_window_handle as rwh_06; /// # struct T; /// # impl rwh_06::HasWindowHandle for T { /// # fn window_handle(&self) -> Result, rwh_06::HandleError> { From 3f28e0aedaf5193fc0d1f21c37cb0ea4df76b39a Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 6 Nov 2023 19:34:30 +0900 Subject: [PATCH 69/80] Fix mac build --- src/wkwebview/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 374a6666d..c2a69b239 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -15,8 +15,8 @@ use crate::raw_window_handle::RawWindowHandle; use url::Url; #[cfg(target_os = "macos")] +use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewMinYMargin, NSViewWidthSizable}; use cocoa::{ - appkit::{NSView, NSViewHeightSizable, NSViewMinYMargin, NSViewWidthSizable, NSWindow}, base::{id, nil, NO, YES}, foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; @@ -1298,7 +1298,7 @@ struct NSData(id); /// wry: top-left is (0, 0) and y increasing downwards /// macOS: bottom-left is (0, 0) and y increasing upwards unsafe fn window_position(view: id, position: (i32, i32), size: (f64, f64)) -> CGPoint { - let frame = NSWindow::frame(view); + let frame: CGRect = msg_send![view, frame]; CGPoint::new( position.0 as f64, frame.size.height - position.1 as f64 - size.1, From b0886f03084ffb2dc1aded8fac1f8cfeabf5c646 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 6 Nov 2023 21:50:19 +0900 Subject: [PATCH 70/80] Update macOS contrusctors --- Cargo.toml | 2 +- examples/winit.rs | 5 ----- src/lib.rs | 27 +++------------------------ src/wkwebview/mod.rs | 8 ++++---- 4 files changed, 8 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 381b4268f..6dea3e90f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,4 +104,4 @@ http-range = "0.1.5" pollster = "0.3.0" tao = { git = "https://github.com/tauri-apps/tao", default-features = false, features = ["rwh_06"] } wgpu = "0.18.0" -winit = { version = "0.29", features = ["rwh_06"] } +winit = { version = "0.29", features = ["rwh_05"] } diff --git a/examples/winit.rs b/examples/winit.rs index cf981158d..17c66785c 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -35,11 +35,6 @@ fn main() -> wry::Result<()> { #[allow(unused_mut)] let mut builder = WebViewBuilder::new(&window); - #[cfg(target_os = "macos")] - { - use wry::WebViewBuilderExtMacOS; - builder = builder.with_as_subview(true); - } let _webview = builder.with_url("https://tauri.app")?.build()?; event_loop diff --git a/src/lib.rs b/src/lib.rs index afffe60a1..1cc192498 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -577,7 +577,8 @@ impl<'a> WebViewBuilder<'a> { /// ## Platform-specific /// /// - **Windows**: This will create the webview as a child window of the `parent` window. - /// - **macOS**: This will create the webview as the contentView of the `parent` window. + /// - **macOS**: This will create the webview as a `NSView` subview of the `parent` window's + /// content view. /// - **Linux**: This will create the webview as a child window of the `parent` window. Only X11 /// is supported. This method won't work on Wayland. /// @@ -1194,29 +1195,6 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { } } -#[cfg(target_os = "macos")] -#[derive(Default)] -pub(crate) struct PlatformSpecificWebViewAttributes { - as_subview: bool, -} - -#[cfg(target_os = "macos")] -pub trait WebViewBuilderExtMacOS { - /// create the webview as a `NSView` subview of the `parent` window's - /// content view. The webview will auto-resize when the passed handle is resized. If you want - /// to prevent several bugs with menu items, accelerators, IME, cursor icons not working because of - /// existing content view blocks the event chain (like how `winit` does), do not enable this. - fn with_as_subview(self, subview: bool) -> Self; -} - -#[cfg(target_os = "macos")] -impl WebViewBuilderExtMacOS for WebViewBuilder<'_> { - fn with_as_subview(mut self, subview: bool) -> Self { - self.platform_specific.as_subview = subview; - self - } -} - /// The fundamental type to present a [`WebView`]. /// /// [`WebViewBuilder`] / [`WebView`] are the basic building blocks to construct WebView contents and @@ -1596,6 +1574,7 @@ pub enum PageLoadEvent { target_os = "netbsd", target_os = "openbsd", target_os = "ios", + target_os = "macos", ))] #[derive(Default)] pub(crate) struct PlatformSpecificWebViewAttributes; diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index c2a69b239..fecd8c3d9 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -167,7 +167,7 @@ impl InnerWebView { fn new_ns_view( ns_view: id, attributes: WebViewAttributes, - pl_attrs: super::PlatformSpecificWebViewAttributes, + _pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, is_child: bool, ) -> Result { @@ -436,7 +436,7 @@ impl InnerWebView { let (x, y) = attributes.position.unwrap_or((0, 0)); let (w, h) = attributes.size.unwrap_or_else(|| { - if pl_attrs.as_subview { + if is_child { let frame = NSView::frame(ns_view); (frame.size.width as u32, frame.size.height as u32) } else { @@ -445,7 +445,7 @@ impl InnerWebView { }); let frame: CGRect = CGRect::new( &window_position( - if is_child || pl_attrs.as_subview { + if is_child { ns_view } else { webview @@ -913,7 +913,7 @@ r#"Object.defineProperty(window, 'ipc', { // Inject the web view into the window as main content #[cfg(target_os = "macos")] { - if is_child || pl_attrs.as_subview { + if is_child { let _: () = msg_send![ns_view, addSubview: webview]; } else { let parent_view_cls = match ClassDecl::new("WryWebViewParent", class!(NSView)) { From bd46e5866b8117557f2106b687e26b724109f829 Mon Sep 17 00:00:00 2001 From: Wu Wayne Date: Mon, 6 Nov 2023 21:51:28 +0900 Subject: [PATCH 71/80] cargo fmt --- src/wkwebview/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index fecd8c3d9..cbf80ac5b 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -445,11 +445,7 @@ impl InnerWebView { }); let frame: CGRect = CGRect::new( &window_position( - if is_child { - ns_view - } else { - webview - }, + if is_child { ns_view } else { webview }, (x, y), (w as f64, h as f64), ), From 46db450e4946c7066262640f1f619023e888247b Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 16:29:19 +0200 Subject: [PATCH 72/80] fix winit usage in the examples, use rwh_05 only for now --- Cargo.toml | 7 +++--- examples/multiwebview.rs | 7 ++++++ examples/wgpu.rs | 23 +++++++++++++++++--- examples/winit.rs | 7 ++++++ src/android/mod.rs | 6 ++--- src/error.rs | 2 +- src/lib.rs | 33 ++++++++-------------------- src/webkitgtk/mod.rs | 16 ++++---------- src/webview2/mod.rs | 30 +++++-------------------- src/wkwebview/mod.rs | 47 ++++------------------------------------ 10 files changed, 63 insertions(+), 115 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6dea3e90f..c9db01a10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ rustc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["rwh_06", "file-drop", "objc-exception", "protocol"] +default = ["file-drop", "objc-exception", "protocol"] objc-exception = ["objc/exception"] file-drop = [] protocol = [] @@ -43,8 +43,7 @@ serde_json = "1.0" thiserror = "1.0" url = "2.4" http = "0.2" -rwh_05 = { package = "raw-window-handle", version = "0.5", features = ["std"], optional = true } -rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } +raw-window-handle = { version = "0.5", features = ["std"] } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] javascriptcore-rs = { version = "=1.1", features = ["v2_28"] } @@ -102,6 +101,6 @@ tao-macros = { version = "0.1.0" } [dev-dependencies] http-range = "0.1.5" pollster = "0.3.0" -tao = { git = "https://github.com/tauri-apps/tao", default-features = false, features = ["rwh_06"] } +tao = { git = "https://github.com/tauri-apps/tao", default-features = false, features = ["rwh_05"] } wgpu = "0.18.0" winit = { version = "0.29", features = ["rwh_05"] } diff --git a/examples/multiwebview.rs b/examples/multiwebview.rs index e41b52093..4c7ed6957 100644 --- a/examples/multiwebview.rs +++ b/examples/multiwebview.rs @@ -25,6 +25,13 @@ fn main() -> wry::Result<()> { if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); } + + // we need to ignore this error here otherwise it will be catched by winit and will be + // make the example crash + winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| { + let error = error as *mut x11_dl::xlib::XErrorEvent; + (unsafe { (*error).error_code }) == 170 + })); } let event_loop = EventLoop::new().unwrap(); diff --git a/examples/wgpu.rs b/examples/wgpu.rs index 76038d139..96d01c90c 100644 --- a/examples/wgpu.rs +++ b/examples/wgpu.rs @@ -6,7 +6,6 @@ use winit::{ }; use wry::WebViewBuilder; -#[cfg(feature = "rwh_05")] async fn run(event_loop: EventLoop<()>, window: Window) { let size = window.inner_size(); @@ -100,7 +99,19 @@ fn fs_main() -> @location(0) vec4 { let _webview = WebViewBuilder::new_as_child(&window) .with_position((100, 100)) .with_size((400, 400)) - .with_url("https://tauri.app") + .with_transparent(true) + .with_html( + r#" + + + hello + + "#, + ) .unwrap() .build() .unwrap(); @@ -191,10 +202,16 @@ fn main() { if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); } + + // we need to ignore this error here otherwise it will be catched by winit and will be + // make the example crash + winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| { + let error = error as *mut x11_dl::xlib::XErrorEvent; + (unsafe { (*error).error_code }) == 170 + })); } let event_loop = EventLoop::new().unwrap(); let window = winit::window::Window::new(&event_loop).unwrap(); - #[cfg(feature = "rwh_05")] pollster::block_on(run(event_loop, window)); } diff --git a/examples/winit.rs b/examples/winit.rs index 17c66785c..4ffa79304 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -25,6 +25,13 @@ fn main() -> wry::Result<()> { if gtk::gdk::Display::default().unwrap().backend().is_wayland() { panic!("This example doesn't support wayland!"); } + + // we need to ignore this error here otherwise it will be catched by winit and will be + // make the example crash + winit::platform::x11::register_xlib_error_hook(Box::new(|_display, error| { + let error = error as *mut x11_dl::xlib::XErrorEvent; + (unsafe { (*error).error_code }) == 170 + })); } let event_loop = EventLoop::new().unwrap(); diff --git a/src/android/mod.rs b/src/android/mod.rs index e5c442da5..21f63dc6b 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT use super::{PageLoadEvent, WebContext, WebViewAttributes, RGBA}; -use crate::{RawWindowHandleTrait, RequestAsyncResponder, Result}; +use crate::{RequestAsyncResponder, Result}; use base64::{engine::general_purpose, Engine}; use crossbeam_channel::*; use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName}; @@ -114,7 +114,7 @@ pub(crate) struct InnerWebView; impl InnerWebView { pub fn new_as_child( - _window: &impl RawWindowHandleTrait, + _window: &impl raw_window_handle::HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -123,7 +123,7 @@ impl InnerWebView { } pub fn new( - _window: &impl RawWindowHandleTrait, + _window: &impl raw_window_handle::HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, diff --git a/src/error.rs b/src/error.rs index 0e96bf623..65abe92a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -83,7 +83,7 @@ pub enum Error { #[error("Failed to create proxy endpoint")] ProxyEndpointCreationFailed, #[error(transparent)] - WindowHandleError(#[from] crate::raw_window_handle::HandleError), + WindowHandleError(#[from] raw_window_handle::HandleError), #[error("the window handle kind is not supported")] UnsupportedWindowHandle, #[error(transparent)] diff --git a/src/lib.rs b/src/lib.rs index 1cc192498..15ce75fac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,17 +180,6 @@ mod error; mod proxy; mod web_context; -#[cfg(feature = "rwh_05")] -pub use rwh_05 as raw_window_handle; -#[cfg(feature = "rwh_06")] -pub use rwh_06 as raw_window_handle; - -#[cfg(all(not(feature = "rwh_05"), not(feature = "rwh_06")))] -compile_error!("One of the rwh_05 or rwh_06 features must be enabled"); - -#[cfg(all(feature = "rwh_05", feature = "rwh_06"))] -compile_error!("Only one of the rwh_05 or rwh_06 features must be enabled"); - #[cfg(target_os = "android")] pub(crate) mod android; #[cfg(target_os = "android")] @@ -213,6 +202,7 @@ use android::*; target_os = "openbsd" ))] pub(crate) mod webkitgtk; +use raw_window_handle::HasRawWindowHandle; #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -238,11 +228,6 @@ use std::{borrow::Cow, path::PathBuf, rc::Rc}; use http::{Request, Response}; -#[cfg(feature = "rwh_05")] -use raw_window_handle::HasRawWindowHandle as RawWindowHandleTrait; -#[cfg(feature = "rwh_06")] -use raw_window_handle::HasWindowHandle as RawWindowHandleTrait; - pub use error::*; pub use http; pub use proxy::{ProxyConfig, ProxyEndpoint}; @@ -523,7 +508,7 @@ impl Default for WebViewAttributes { pub struct WebViewBuilder<'a> { pub attrs: WebViewAttributes, as_child: bool, - window: Option<&'a dyn RawWindowHandleTrait>, + window: Option<&'a dyn HasRawWindowHandle>, platform_specific: PlatformSpecificWebViewAttributes, web_context: Option<&'a mut WebContext>, #[cfg(any( @@ -537,7 +522,7 @@ pub struct WebViewBuilder<'a> { } impl<'a> WebViewBuilder<'a> { - /// Create a [`WebViewBuilder`] from a type that implements [`RawWindowHandleTrait`]. + /// Create a [`WebViewBuilder`] from a type that implements [`HasRawWindowHandle`]. /// /// # Platform-specific: /// @@ -553,7 +538,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &'a impl RawWindowHandleTrait) -> Self { + pub fn new(window: &'a impl HasRawWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(window), @@ -572,7 +557,7 @@ impl<'a> WebViewBuilder<'a> { } } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandleTrait`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`HasRawWindowHandle`]. /// /// ## Platform-specific /// @@ -591,7 +576,7 @@ impl<'a> WebViewBuilder<'a> { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &'a impl RawWindowHandleTrait) -> Self { + pub fn new_as_child(parent: &'a impl HasRawWindowHandle) -> Self { Self { attrs: WebViewAttributes::default(), window: Some(parent), @@ -1224,11 +1209,11 @@ impl WebView { /// /// - Panics if the provided handle was not supported or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new(window: &impl RawWindowHandleTrait) -> Result { + pub fn new(window: &impl HasRawWindowHandle) -> Result { WebViewBuilder::new(window).build() } - /// Create [`WebViewBuilder`] as a child window inside the provided [`RawWindowHandleTrait`]. + /// Create [`WebViewBuilder`] as a child window inside the provided [`HasRawWindowHandle`]. /// /// ## Platform-specific /// @@ -1247,7 +1232,7 @@ impl WebView { /// /// - Panics if the provided handle was not support or invalid. /// - Panics on Linux, if [`gtk::init`] was not called in this thread. - pub fn new_as_child(parent: &impl RawWindowHandleTrait) -> Result { + pub fn new_as_child(parent: &impl HasRawWindowHandle) -> Result { WebViewBuilder::new_as_child(parent).build() } diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 50ac4732f..91d9fd4f1 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -12,6 +12,7 @@ use gtk::{ prelude::*, }; use javascriptcore::ValueExt; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; #[cfg(any(debug_assertions, feature = "devtools"))] use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -37,8 +38,6 @@ use crate::{ RGBA, }; -use crate::{raw_window_handle::RawWindowHandle, RawWindowHandleTrait}; - mod file_drop; mod synthetic_mouse_events; mod web_context; @@ -70,7 +69,7 @@ impl Drop for InnerWebView { } impl InnerWebView { - pub fn new( + pub fn new( window: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -79,7 +78,7 @@ impl InnerWebView { Self::new_x11(window, attributes, pl_attrs, web_context, false) } - pub fn new_as_child( + pub fn new_as_child( parent: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -88,7 +87,7 @@ impl InnerWebView { Self::new_x11(parent, attributes, pl_attrs, web_context, true) } - fn new_x11( + fn new_x11( window: &W, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, @@ -97,18 +96,11 @@ impl InnerWebView { ) -> Result { let xlib = Xlib::open()?; - #[cfg(feature = "rwh_05")] let window_handle = match window.raw_window_handle() { RawWindowHandle::Xlib(w) => w.window, _ => return Err(Error::UnsupportedWindowHandle), }; - #[cfg(feature = "rwh_06")] - let window_handle = match window.window_handle()?.as_raw() { - RawWindowHandle::Xlib(w) => w.window, - _ => return Err(Error::UnsupportedWindowHandle), - }; - let gdk_display = gdk::Display::default().ok_or(Error::X11DisplayNotFound)?; let gx11_display: &X11Display = gdk_display.downcast_ref().unwrap(); let raw = gx11_display.to_glib_none().0; diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 11ad7a059..9a5e4e2bf 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -11,6 +11,7 @@ use std::{ use http::{Request, Response as HttpResponse, StatusCode}; use once_cell::sync::Lazy; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use url::Url; use webview2_com::{Microsoft::Web::WebView2::Win32::*, *}; use windows::{ @@ -40,8 +41,8 @@ use windows::{ use self::file_drop::FileDropController; use super::Theme; use crate::{ - proxy::ProxyConfig, raw_window_handle::RawWindowHandle, Error, MemoryUsageLevel, PageLoadEvent, - RawWindowHandleTrait, RequestAsyncResponder, Result, WebContext, WebViewAttributes, RGBA, + proxy::ProxyConfig, Error, MemoryUsageLevel, PageLoadEvent, RequestAsyncResponder, Result, + WebContext, WebViewAttributes, RGBA, }; impl From for Error { @@ -63,9 +64,8 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - #[cfg(feature = "rwh_05")] pub fn new( - window: &impl RawWindowHandleTrait, + window: &impl HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, @@ -77,36 +77,16 @@ impl InnerWebView { Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) } - #[cfg(feature = "rwh_06")] - pub fn new( - window: &impl RawWindowHandleTrait, - attributes: WebViewAttributes, - pl_attrs: super::PlatformSpecificWebViewAttributes, - web_context: Option<&mut WebContext>, - ) -> Result { - let window = match window.window_handle()?.as_raw() { - RawWindowHandle::Win32(window) => window.hwnd.get(), - _ => return Err(Error::UnsupportedWindowHandle), - }; - Self::new_hwnd(HWND(window), attributes, pl_attrs, web_context) - } - pub fn new_as_child( - parent: &impl RawWindowHandleTrait, + parent: &impl HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, web_context: Option<&mut WebContext>, ) -> Result { - #[cfg(feature = "rwh_05")] let parent = match parent.raw_window_handle() { RawWindowHandle::Win32(parent) => parent.hwnd as _, _ => return Err(Error::UnsupportedWindowHandle), }; - #[cfg(feature = "rwh_06")] - let parent = match parent.window_handle()?.as_raw() { - RawWindowHandle::Win32(parent) => parent.hwnd.get(), - _ => return Err(Error::UnsupportedWindowHandle), - }; let class_name = encode_wide("WRY_WEBVIEW"); diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index cbf80ac5b..7dae6af99 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -11,15 +11,14 @@ mod proxy; #[cfg(target_os = "macos")] mod synthetic_mouse_events; -use crate::raw_window_handle::RawWindowHandle; -use url::Url; - #[cfg(target_os = "macos")] use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewMinYMargin, NSViewWidthSizable}; use cocoa::{ base::{id, nil, NO, YES}, foundation::{NSDictionary, NSFastEnumeration, NSInteger}, }; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use url::Url; use std::{ borrow::Cow, @@ -92,9 +91,8 @@ pub(crate) struct InnerWebView { } impl InnerWebView { - #[cfg(feature = "rwh_05")] pub fn new( - window: &impl crate::RawWindowHandleTrait, + window: &impl HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -110,27 +108,8 @@ impl InnerWebView { Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, false) } - #[cfg(feature = "rwh_06")] - pub fn new( - window: &impl crate::RawWindowHandleTrait, - attributes: WebViewAttributes, - pl_attrs: super::PlatformSpecificWebViewAttributes, - _web_context: Option<&mut WebContext>, - ) -> Result { - let ns_view = match window.window_handle()?.as_raw() { - #[cfg(target_os = "macos")] - RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), - #[cfg(target_os = "ios")] - RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), - _ => return Err(Error::UnsupportedWindowHandle), - }; - - Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, false) - } - - #[cfg(feature = "rwh_05")] pub fn new_as_child( - window: &impl crate::RawWindowHandleTrait, + window: &impl HasRawWindowHandle, attributes: WebViewAttributes, pl_attrs: super::PlatformSpecificWebViewAttributes, _web_context: Option<&mut WebContext>, @@ -146,24 +125,6 @@ impl InnerWebView { Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, true) } - #[cfg(feature = "rwh_06")] - pub fn new_as_child( - window: &impl crate::RawWindowHandleTrait, - attributes: WebViewAttributes, - pl_attrs: super::PlatformSpecificWebViewAttributes, - _web_context: Option<&mut WebContext>, - ) -> Result { - let ns_view = match window.window_handle()?.as_raw() { - #[cfg(target_os = "macos")] - RawWindowHandle::AppKit(w) => w.ns_view.as_ptr(), - #[cfg(target_os = "ios")] - RawWindowHandle::UiKit(w) => w.ui_view.as_ptr(), - _ => return Err(Error::UnsupportedWindowHandle), - }; - - Self::new_ns_view(ns_view as _, attributes, pl_attrs, _web_context, true) - } - fn new_ns_view( ns_view: id, attributes: WebViewAttributes, From 6a6cc9658b227cb4dcda6088b9b16cc186b75c16 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 14:40:04 +0200 Subject: [PATCH 73/80] fix tests --- .changes/rwh.md | 1 - src/lib.rs | 24 +++++++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/.changes/rwh.md b/.changes/rwh.md index 54a0bd896..b234e348b 100644 --- a/.changes/rwh.md +++ b/.changes/rwh.md @@ -10,7 +10,6 @@ Refactor new method to take raw window handle instead. Following are APIs got af - Position field in `FileDrop` event is now `Position` instead of `PhysicalPosition`. Users need to handle scale factor depend on the situation they have. - `Webview::inner_size` is removed. - - Added `rwh_04`, `rwh_05`, `rwh_06` feature flags. This also means that we removed `tao` as a dependency completely which required some changes to the Android backend: - We exposed the `android_setup` function that needs to be called once to setup necessary logic. diff --git a/src/lib.rs b/src/lib.rs index 15ce75fac..743e7fb45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,11 +18,9 @@ //! use wry::WebViewBuilder; //! //! # struct T; -//! # impl rwh_06::HasWindowHandle for T { -//! # fn window_handle(&self) -> Result, rwh_06::HandleError> { -//! # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( -//! # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), -//! # )) }) +//! # unsafe impl raw_window_handle::HasRawWindowHandle for T { +//! # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { +//! # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty()) //! # } //! # } //! # let window = T; @@ -72,11 +70,9 @@ //! use wry::WebViewBuilder; //! //! # struct T; -//! # impl rwh_06::HasWindowHandle for T { -//! # fn window_handle(&self) -> Result, rwh_06::HandleError> { -//! # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( -//! # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), -//! # )) }) +//! # unsafe impl raw_window_handle::HasRawWindowHandle for T { +//! # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { +//! # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty()) //! # } //! # } //! # let window = T; @@ -732,11 +728,9 @@ impl<'a> WebViewBuilder<'a> { /// use wry::WebViewBuilder; /// /// # struct T; - /// # impl rwh_06::HasWindowHandle for T { - /// # fn window_handle(&self) -> Result, rwh_06::HandleError> { - /// # Ok(unsafe { rwh_06::WindowHandle::borrow_raw(rwh_06::RawWindowHandle::Win32( - /// # rwh_06::Win32WindowHandle::new(std::num::NonZeroIsize::new_unchecked(1)), - /// # )) }) + /// # unsafe impl raw_window_handle::HasRawWindowHandle for T { + /// # fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + /// # raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::empty()) /// # } /// # } /// # let window = T; From 71b5663483ddb218621c0dbfffbc5957395e3e5e Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 14:51:49 +0200 Subject: [PATCH 74/80] impl drop on Windows --- src/webview2/mod.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index 9a5e4e2bf..dccd19f7e 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -29,10 +29,11 @@ use windows::{ UI::{ Shell::{DefSubclassProc, SHCreateMemStream, SetWindowSubclass}, WindowsAndMessaging::{ - self as win32wm, CreateWindowExW, DefWindowProcW, PostMessageW, RegisterClassExW, - RegisterWindowMessageA, SetWindowPos, ShowWindow, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, - HCURSOR, HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, - SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, WS_VISIBLE, + self as win32wm, CreateWindowExW, DefWindowProcW, DestroyWindow, PostMessageW, + RegisterClassExW, RegisterWindowMessageA, SetWindowPos, ShowWindow, CS_HREDRAW, CS_VREDRAW, + CW_USEDEFAULT, HCURSOR, HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, + SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, + WS_VISIBLE, }, }, }, @@ -63,6 +64,15 @@ pub(crate) struct InnerWebView { file_drop_controller: Option, } +impl Drop for InnerWebView { + fn drop(&mut self) { + let _ = unsafe { self.controller.Close() }; + if self.is_child { + let _ = unsafe { DestroyWindow(self.hwnd) }; + } + } +} + impl InnerWebView { pub fn new( window: &impl HasRawWindowHandle, From 6eda375540a37a196d946f5f50cb2f190ba85e92 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 16:24:42 +0200 Subject: [PATCH 75/80] fix child transparency on Windows (still needs PRs in tao and winit) --- examples/wgpu.rs | 7 +++++-- src/webview2/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/wgpu.rs b/examples/wgpu.rs index 96d01c90c..33aa53114 100644 --- a/examples/wgpu.rs +++ b/examples/wgpu.rs @@ -151,7 +151,7 @@ fn fs_main() -> @location(0) vec4 { view: &view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), store: wgpu::StoreOp::Store, }, })], @@ -212,6 +212,9 @@ fn main() { } let event_loop = EventLoop::new().unwrap(); - let window = winit::window::Window::new(&event_loop).unwrap(); + let window = winit::window::WindowBuilder::new() + .with_transparent(true) + .build(&event_loop) + .unwrap(); pollster::block_on(run(event_loop, window)); } diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index dccd19f7e..f4da278ba 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -33,7 +33,7 @@ use windows::{ RegisterClassExW, RegisterWindowMessageA, SetWindowPos, ShowWindow, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, HCURSOR, HICON, HMENU, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_EX_STYLE, WNDCLASSEXW, WS_CHILD, - WS_VISIBLE, + WS_CLIPCHILDREN, WS_VISIBLE, }, }, }, @@ -126,7 +126,7 @@ impl InnerWebView { unsafe { RegisterClassExW(&class) }; - let mut flags = WS_CHILD; + let mut flags = WS_CHILD | WS_CLIPCHILDREN; if attributes.visible { flags |= WS_VISIBLE; } From bf3c1ca00855c113b4b9aa1df836cc707956df53 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 16:49:59 +0200 Subject: [PATCH 76/80] add webview.focus --- src/android/mod.rs | 4 ++++ src/lib.rs | 5 +++++ src/webkitgtk/mod.rs | 4 ++++ src/webview2/mod.rs | 8 ++++++++ src/wkwebview/mod.rs | 4 ++++ 5 files changed, 25 insertions(+) diff --git a/src/android/mod.rs b/src/android/mod.rs index 21f63dc6b..1789082c9 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -335,6 +335,10 @@ impl InnerWebView { pub fn set_visible(&self, _visible: bool) { // Unsupported } + + pub fn focus(&self) { + // Unsupported + } } #[derive(Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index 743e7fb45..98dc32c5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1366,6 +1366,11 @@ impl WebView { pub fn set_visible(&self, visible: bool) { self.webview.set_visible(visible) } + + /// Try moving focus to the webview. + pub fn focus(&self) { + self.webview.focus() + } } /// An event describing the files drop on the webview. diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 91d9fd4f1..ad3532b24 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -634,6 +634,10 @@ impl InnerWebView { } } } + + pub fn focus(&self) { + // Unimplemented + } } pub fn platform_webview_version() -> Result { diff --git a/src/webview2/mod.rs b/src/webview2/mod.rs index f4da278ba..8524b063f 100644 --- a/src/webview2/mod.rs +++ b/src/webview2/mod.rs @@ -1053,6 +1053,14 @@ impl InnerWebView { } } + pub fn focus(&self) { + unsafe { + let _ = self + .controller + .MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC); + } + } + pub fn set_memory_usage_level(&self, level: MemoryUsageLevel) { let Ok(webview) = self.webview.cast::() else { return; diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index 7dae6af99..b236686ae 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -1117,6 +1117,10 @@ r#"Object.defineProperty(window, 'ipc', { let () = msg_send![self.webview, setHidden: !visible]; } } + + pub fn focus(&self) { + // Unimplemented + } } pub fn url_from_webview(webview: id) -> String { From 028d20740be93fb7f3e1a85fc9b76222f9f8258d Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 19:16:29 +0200 Subject: [PATCH 77/80] fix dropping on Linux --- src/webkitgtk/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index ad3532b24..38cfc5dae 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -58,12 +58,16 @@ pub(crate) struct InnerWebView { impl Drop for InnerWebView { fn drop(&mut self) { + unsafe { self.webview.destroy() } + if let Some(xlib) = &self.xlib { if self.is_child { unsafe { (xlib.XDestroyWindow)(self.x11_display.unwrap() as _, self.x11_window.unwrap()) }; } + } - unsafe { (xlib.XCloseDisplay)(self.x11_display.unwrap() as _) }; + if let Some(window) = &self.gtk_window { + window.close(); } } } From ea93b36ac175b17083365f37c5922b9730256e3c Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 19:16:56 +0200 Subject: [PATCH 78/80] chore clean examples --- examples/transparent.rs | 18 ++++++++---------- examples/wgpu.rs | 18 ++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/examples/transparent.rs b/examples/transparent.rs index d5b729574..1182f314a 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -55,16 +55,14 @@ fn main() -> wry::Result<()> { .with_transparent(true) // And the last is in html. .with_html( - r#" - - - hello - - "#, + r#" + + + "#, )? .build()?; diff --git a/examples/wgpu.rs b/examples/wgpu.rs index 33aa53114..34a80cf69 100644 --- a/examples/wgpu.rs +++ b/examples/wgpu.rs @@ -101,16 +101,14 @@ fn fs_main() -> @location(0) vec4 { .with_size((400, 400)) .with_transparent(true) .with_html( - r#" - - - hello - - "#, + r#" + + + "#, ) .unwrap() .build() From e947f01a12964e6cb5d19237cd62b3cc89476e17 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Mon, 6 Nov 2023 19:23:32 +0200 Subject: [PATCH 79/80] implement focus on Linux --- src/webkitgtk/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webkitgtk/mod.rs b/src/webkitgtk/mod.rs index 38cfc5dae..ba34662b4 100644 --- a/src/webkitgtk/mod.rs +++ b/src/webkitgtk/mod.rs @@ -640,7 +640,7 @@ impl InnerWebView { } pub fn focus(&self) { - // Unimplemented + self.webview.grab_focus(); } } From cf33d8571be61f9cffea4900796272ac46dab730 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 6 Nov 2023 14:41:13 -0300 Subject: [PATCH 80/80] macos focus --- src/wkwebview/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs index b236686ae..b13291dbc 100644 --- a/src/wkwebview/mod.rs +++ b/src/wkwebview/mod.rs @@ -1119,7 +1119,10 @@ r#"Object.defineProperty(window, 'ipc', { } pub fn focus(&self) { - // Unimplemented + unsafe { + let window: id = msg_send![self.webview, window]; + let _: () = msg_send![window, makeFirstResponder: self.webview]; + } } }