wew/
lib.rs

1//! Wew is a cross-platform WebView rendering library based on Chromium Embedded
2//! Framework (CEF). It supports mouse, keyboard, touch, input methods,
3//! off-screen rendering, and communication with web pages.
4//!
5//! ## Thread Considerations
6//!
7//! In the current project, WebView and Runtime calls are best executed on the
8//! UI thread, which is the main thread of the application process.
9//!
10//! Creating a Runtime must be completed on the UI thread, and all message loop
11//! calls must also be operated on the UI thread.
12//!
13//! Other calls should be executed on the UI thread whenever possible, unless it
14//! is truly unavoidable. Although these calls can run on any thread, there is
15//! currently no guarantee that they will not cause other side effects.
16//!
17//! However, it is important to note that if the WebView manages window events
18//! on its own, such as not using off-screen rendering, then the WebView can be
19//! created on any thread.
20//!
21//!
22//! ## Examples
23//!
24//! ```no_run
25//! use std::{
26//!     sync::mpsc::{Sender, channel},
27//!     thread,
28//! };
29//!
30//! use wew::{
31//!     MainThreadMessageLoop, MessageLoopAbstract, NativeWindowWebView,
32//!     runtime::{LogLevel, RuntimeHandler},
33//!     webview::{WebViewAttributes, WebViewHandler, WebViewState},
34//! };
35//!
36//! struct RuntimeObserver {
37//!     tx: Sender<()>,
38//! }
39//!
40//! impl RuntimeHandler for RuntimeObserver {
41//!     fn on_context_initialized(&self) {
42//!         self.tx.send(()).unwrap();
43//!     }
44//! }
45//!
46//! struct WebViewObserver;
47//!
48//! impl WebViewHandler for WebViewObserver {
49//!     fn on_state_change(&self, state: WebViewState) {
50//!         if state == WebViewState::Close {
51//!             std::process::exit(0);
52//!         }
53//!     }
54//! }
55//!
56//! fn main() {
57//!     if wew::is_subprocess() {
58//!         wew::execute_subprocess();
59//!
60//!         return;
61//!     }
62//!
63//!     #[cfg(target_os = "macos")]
64//!     wew::utils::startup_nsapplication();
65//!
66//!     let message_loop = MainThreadMessageLoop::default();
67//!
68//!     let mut runtime_attributes_builder =
69//!         message_loop.create_runtime_attributes_builder::<NativeWindowWebView>();
70//!
71//!     runtime_attributes_builder = runtime_attributes_builder
72//!         // Set cache path, here we use environment variables passed by the build script.
73//!         .with_root_cache_path(option_env!("CACHE_PATH").unwrap())
74//!         .with_cache_path(option_env!("CACHE_PATH").unwrap())
75//!         .with_log_severity(LogLevel::Info);
76//!
77//!     let (tx, rx) = channel();
78//!
79//!     // Create runtime, wait for the `on_context_initialized` event to be triggered
80//!     // before considering the creation successful.
81//!     let runtime = runtime_attributes_builder
82//!         .build()
83//!         .create_runtime(RuntimeObserver { tx })
84//!         .unwrap();
85//!
86//!     thread::spawn(move || {
87//!         rx.recv().unwrap();
88//!
89//!         let webview = runtime
90//!             .create_webview(
91//!                 "https://www.google.com",
92//!                 WebViewAttributes::default(),
93//!                 WebViewObserver,
94//!             )
95//!             .unwrap();
96//!
97//!         std::mem::forget(webview);
98//!         std::mem::forget(runtime);
99//!     });
100//!
101//!     message_loop.block_run();
102//! }
103//! ```
104
105#![cfg_attr(
106    docsrs,
107    feature(doc_auto_cfg, doc_cfg_hide),
108    doc(cfg_hide(doc, docsrs))
109)]
110#![allow(clippy::needless_doctest_main)]
111
112pub mod events;
113pub mod request;
114pub mod runtime;
115pub mod utils;
116pub mod webview;
117
118use std::sync::atomic::Ordering;
119
120use self::runtime::{RUNTIME_RUNNING, RuntimeAttributesBuilder};
121
122#[cfg(feature = "winit")]
123pub use winit;
124
125pub use raw_window_handle;
126
127#[allow(
128    dead_code,
129    unused_imports,
130    non_snake_case,
131    non_camel_case_types,
132    non_upper_case_globals
133)]
134mod sys {
135    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
136}
137
138#[derive(Debug)]
139pub enum Error {
140    /// The current thread is not the main thread.
141    NonUIThread,
142    FailedToCreateRuntime,
143    /// Only one runtime can be created in a process. Repeated creation will
144    /// trigger this error.
145    RuntimeAlreadyExists,
146    /// If the runtime is not initialized, creating WebView and other operations
147    /// will trigger this error.
148    RuntimeNotInitialization,
149    FailedToCreateWebView,
150}
151
152impl std::error::Error for Error {}
153
154impl std::fmt::Display for Error {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        write!(f, "{:?}", self)
157    }
158}
159
160/// Represents a rectangular area
161#[derive(Debug, Clone, Copy, Default)]
162pub struct Rect {
163    pub x: u32,
164    pub y: u32,
165    pub width: u32,
166    pub height: u32,
167}
168
169/// Message loop abstraction
170///
171/// Message loop abstraction, used to implement different message loop types.
172pub trait MessageLoopAbstract: Default + Clone + Copy {
173    /// Create a runtime attributes builder
174    ///
175    /// This function is used to create a runtime attributes builder.
176    fn create_runtime_attributes_builder<W: Default>(&self) -> RuntimeAttributesBuilder<Self, W> {
177        RuntimeAttributesBuilder::<Self, W>::default()
178    }
179}
180
181/// Multi-threaded message loop
182///
183/// Using multi-threaded message runtime will create a separate thread inside
184/// the runtime to run the message loop.
185///
186/// Note that macOS does not support this type of message loop.
187#[derive(Clone, Copy)]
188pub struct MultiThreadMessageLoop;
189
190impl MessageLoopAbstract for MultiThreadMessageLoop {}
191
192impl Default for MultiThreadMessageLoop {
193    fn default() -> Self {
194        if cfg!(target_os = "macos") {
195            panic!("macOS does not support this type of message loop!");
196        }
197
198        Self
199    }
200}
201
202/// Main thread message loop
203///
204/// You need to manually run the message loop in the main thread of the process.
205#[derive(Default, Clone, Copy)]
206pub struct MainThreadMessageLoop;
207
208impl MessageLoopAbstract for MainThreadMessageLoop {}
209
210impl MainThreadMessageLoop {
211    /// Run the message loop on main thread
212    ///
213    /// This function is used to run the message loop on main thread.
214    ///
215    /// Note that this function will block the current thread until the message
216    /// loop ends.
217    pub fn block_run(&self) {
218        if !utils::is_main_thread() {
219            panic!("this operation is not allowed in non-main threads!");
220        }
221
222        unsafe { sys::run_message_loop() }
223    }
224
225    /// Quit the message loop on main thread
226    ///
227    /// This function is used to quit the message loop on main thread.
228    ///
229    /// Calling this function will cause **`block_run`** to exit and return.
230    pub fn quit(&self) {
231        unsafe {
232            sys::quit_message_loop();
233        }
234    }
235}
236
237/// Message loop pump
238///
239/// If you need to integrate with existing message loops, the message pump
240/// mechanism provides a way for you to drive the message loop yourself.
241#[derive(Default, Clone, Copy)]
242pub struct MessagePumpLoop;
243
244impl MessageLoopAbstract for MessagePumpLoop {}
245
246impl MessagePumpLoop {
247    /// Drive the message loop pump on main thread
248    ///
249    /// This function is used to poll the message loop on main thread.
250    ///
251    /// Note that this function won't block the current thread, external code
252    /// needs to drive the message loop pump.
253    pub fn poll(&self) {
254        if !utils::is_main_thread() {
255            panic!("this operation is not allowed in non-main threads!");
256        }
257
258        if RUNTIME_RUNNING.load(Ordering::Relaxed) {
259            unsafe { sys::poll_message_loop() }
260        }
261    }
262}
263
264/// WebView abstraction
265///
266/// WebView abstraction, used to implement different WebView types.
267pub trait WebViewAbstract: Default {}
268
269/// Off-screen rendering mode
270///
271/// When using off-screen rendering mode, the WebView will not be displayed on
272/// screen, but the rendering results will be pushed through
273/// **`WindowlessRenderWebViewHandler::on_frame`**, and you can handle the video
274/// frames yourself. Also, in this mode, mouse and keyboard events need to be
275/// passed to the WebView by yourself.
276#[derive(Default, Clone, Copy)]
277pub struct WindowlessRenderWebView;
278
279impl WebViewAbstract for WindowlessRenderWebView {}
280
281/// Native window mode
282///
283/// When using native window mode, the WebView will create a native window and
284/// display it on screen.
285#[derive(Default, Clone, Copy)]
286pub struct NativeWindowWebView;
287
288impl WebViewAbstract for NativeWindowWebView {}
289
290/// Execute subprocess
291///
292/// This method is used to start a subprocess in a separate process.
293///
294/// ## Examples
295///
296/// ```no_run
297/// fn main() {
298///     if wew::is_subprocess() {
299///         wew::execute_subprocess();
300///
301///         return;
302///     }
303/// }
304/// ```
305///
306/// #### Please be careful!
307///
308/// Do not call this function in an asynchronous runtime, such as tokio,
309/// which can lead to unexpected crashes!
310pub fn execute_subprocess() -> bool {
311    if !utils::is_main_thread() {
312        panic!("this operation is not allowed in non-main threads!");
313    }
314
315    let args = utils::Args::default();
316    (unsafe { sys::execute_subprocess(args.size() as _, args.as_ptr() as _) }) == 0
317}
318
319/// Check if current process is a subprocess
320///
321/// This function is used to check if the current process is a subprocess.
322///
323/// Note that if the current process is a subprocess, it will block until the
324/// subprocess exits.
325pub fn is_subprocess() -> bool {
326    std::env::args().any(|it| it.contains("--type="))
327}