Skip to main content

windows_webview/
environment.rs

1use super::*;
2
3/// The WebView2 environment. Owns the user data folder and browser process and
4/// creates [`Controller`] instances that host the browser in a window.
5pub struct Environment(pub(crate) ICoreWebView2Environment);
6
7impl Environment {
8    /// Creates the default WebView2 environment, pumping the UI thread until it is ready.
9    ///
10    /// The calling thread is initialized as a COM STA if needed.
11    pub fn new() -> Result<Self> {
12        init_com()?;
13        let slot = pump::slot();
14        create_environment(pump::slot_handler(&slot))?;
15        pump::wait(&slot)
16    }
17
18    /// Creates a WebView2 environment configured by `options`, pumping the UI thread.
19    pub fn with_options(options: &EnvironmentOptions) -> Result<Self> {
20        init_com()?;
21        let slot = pump::slot();
22        options.create_environment(pump::slot_handler(&slot))?;
23        pump::wait(&slot)
24    }
25
26    /// Creates a [`Controller`] hosted in the given window, pumping the UI thread.
27    pub fn create_controller(&self, window: &windows_window::Window) -> Result<Controller> {
28        // SAFETY: `window` owns a live window handle for as long as the borrow
29        // lasts.
30        unsafe { self.create_controller_for_hwnd(window.hwnd()) }
31    }
32
33    /// Creates a [`Controller`] hosted in a raw window handle, pumping the UI thread.
34    ///
35    /// # Safety
36    ///
37    /// `parent` must be a valid window handle that outlives the controller.
38    pub unsafe fn create_controller_for_hwnd(
39        &self,
40        parent: *mut core::ffi::c_void,
41    ) -> Result<Controller> {
42        let slot = pump::slot();
43        unsafe { self.create_controller_async(parent, pump::slot_handler(&slot))? };
44        pump::wait(&slot)
45    }
46
47    unsafe fn create_controller_async<F: FnOnce(Result<Controller>) + 'static>(
48        &self,
49        parent: HWND,
50        handler: F,
51    ) -> Result<()> {
52        let handler = handler::ControllerCompleted::create(handler);
53        unsafe { self.0.CreateCoreWebView2Controller(parent, &handler) }.ok()
54    }
55
56    /// Creates an option-configured [`Controller`] hosted in the given window.
57    pub fn create_controller_with_options(
58        &self,
59        window: &windows_window::Window,
60        options: &ControllerOptions,
61    ) -> Result<Controller> {
62        // SAFETY: `window` owns a live window handle for as long as the borrow
63        // lasts.
64        unsafe { self.create_controller_with_options_for_hwnd(window.hwnd(), options) }
65    }
66
67    /// Creates an option-configured [`Controller`] hosted in a raw window handle.
68    ///
69    /// # Safety
70    ///
71    /// `parent` must be a valid window handle that outlives the controller.
72    pub unsafe fn create_controller_with_options_for_hwnd(
73        &self,
74        parent: *mut core::ffi::c_void,
75        options: &ControllerOptions,
76    ) -> Result<Controller> {
77        let slot = pump::slot();
78        options.create_controller(&self.0, parent, pump::slot_handler(&slot))?;
79        pump::wait(&slot)
80    }
81}
82
83fn create_environment<F: FnOnce(Result<Environment>) + 'static>(handler: F) -> Result<()> {
84    let handler = handler::EnvironmentCompleted::create(handler);
85    unsafe { CreateCoreWebView2Environment(Interface::as_raw(&handler)).ok() }
86}
87
88fn init_com() -> Result<()> {
89    let hr = unsafe { CoInitializeEx(std::ptr::null(), COINIT_APARTMENTTHREADED as u32) };
90    if hr == RPC_E_CHANGED_MODE {
91        return Err(Error::new(
92            RPC_E_CHANGED_MODE,
93            "windows_webview::Environment requires the calling thread to be a COM \
94             single-threaded apartment (STA), but it is already initialized as a \
95             multi-threaded apartment (MTA). Create the environment from a UI thread \
96             that has not called CoInitializeEx(COINIT_MULTITHREADED).",
97        ));
98    }
99    hr.ok()
100}