Skip to main content

webview2/native/
mod.rs

1//! https://github.com/jchv/OpenWebView2Loader
2//!
3//! A reimplementation of WebView2Loader in pure Rust.
4
5#![allow(non_snake_case, clippy::missing_safety_doc)]
6
7use windows::Win32::{
8    Foundation::{E_INVALIDARG, E_POINTER},
9    System::Com::CoTaskMemAlloc,
10};
11use windows_core::{Error, HSTRING, PCWSTR, PWSTR, Param, Result};
12
13use crate::*;
14
15mod load;
16mod r#override;
17
18#[inline]
19pub unsafe fn CreateCoreWebView2Environment<P0>(handler: P0) -> Result<()>
20where
21    P0: Param<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>,
22{
23    unsafe { CreateCoreWebView2EnvironmentWithOptions(None, None, None, handler) }
24}
25
26#[inline]
27pub unsafe fn CreateCoreWebView2EnvironmentWithOptions<P0, P1, P2, P3>(
28    browser_executable_folder: P0,
29    user_data_folder: P1,
30    options: P2,
31    handler: P3,
32) -> Result<()>
33where
34    P0: Param<PCWSTR>,
35    P1: Param<PCWSTR>,
36    P2: Param<ICoreWebView2EnvironmentOptions>,
37    P3: Param<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>,
38{
39    unsafe {
40        let handler = handler.param();
41        let handler = handler.borrow();
42        let handler = handler.ok()?;
43
44        let options = options.param();
45        let options = options.borrow();
46        let mut params = WebView2EnvironmentParams {
47            embedded_edge_sub_folder: browser_executable_folder.param().abi().into(),
48            user_data_dir: user_data_folder.param().abi().into(),
49            environment_options: options.as_ref(),
50            release_channel_preference: WebView2ReleaseChannelPreference::Stable,
51        };
52        r#override::update(&mut params);
53        load::create_env_impl(params, handler)
54    }
55}
56
57struct WebView2EnvironmentParams<'a> {
58    embedded_edge_sub_folder: CowPCWSTR,
59    user_data_dir: CowPCWSTR,
60    environment_options: Option<&'a ICoreWebView2EnvironmentOptions>,
61    release_channel_preference: WebView2ReleaseChannelPreference,
62}
63
64enum CowPCWSTR {
65    Pointer(PCWSTR),
66    Owned(HSTRING),
67}
68
69impl CowPCWSTR {
70    pub fn as_ptr(&self) -> PCWSTR {
71        match self {
72            CowPCWSTR::Pointer(ptr) => *ptr,
73            CowPCWSTR::Owned(s) => PCWSTR(s.as_ptr()),
74        }
75    }
76}
77
78impl From<PCWSTR> for CowPCWSTR {
79    fn from(value: PCWSTR) -> Self {
80        Self::Pointer(value)
81    }
82}
83
84impl From<HSTRING> for CowPCWSTR {
85    fn from(value: HSTRING) -> Self {
86        Self::Owned(value)
87    }
88}
89
90#[derive(Clone, Copy, PartialEq, Eq)]
91enum WebView2ReleaseChannelPreference {
92    Stable = 0,
93    Canary = 1,
94}
95
96#[inline]
97pub unsafe fn GetAvailableCoreWebView2BrowserVersionString<P0>(
98    browser_executable_folder: P0,
99    version_info: *mut PWSTR,
100) -> Result<()>
101where
102    P0: Param<PCWSTR>,
103{
104    if version_info.is_null() || !version_info.is_aligned() {
105        return Err(Error::from_hresult(E_POINTER));
106    }
107
108    unsafe {
109        let mut params = WebView2EnvironmentParams {
110            embedded_edge_sub_folder: browser_executable_folder.param().abi().into(),
111            user_data_dir: CowPCWSTR::Pointer(PCWSTR::null()),
112            environment_options: None,
113            release_channel_preference: WebView2ReleaseChannelPreference::Stable,
114        };
115        r#override::update(&mut params);
116        let s = load::get_version_string(params)?;
117        let byte_len = s.len() * 2 + 2;
118        let mem = CoTaskMemAlloc(byte_len).cast::<u16>();
119        if !mem.is_null() {
120            mem.copy_from_nonoverlapping(s.as_ptr(), s.len() + 1);
121        }
122        *version_info = PWSTR(mem);
123        Ok(())
124    }
125}
126
127#[inline]
128pub unsafe fn CompareBrowserVersions<P0, P1>(
129    version1: P0,
130    version2: P1,
131    result: *mut i32,
132) -> Result<()>
133where
134    P0: Param<PCWSTR>,
135    P1: Param<PCWSTR>,
136{
137    if result.is_null() || !result.is_aligned() {
138        return Err(Error::from_hresult(E_POINTER));
139    }
140
141    unsafe {
142        let version1 = version1.param().abi();
143        let version2 = version2.param().abi();
144
145        if version1.is_null() || version2.is_null() {
146            return Err(Error::from_hresult(E_INVALIDARG));
147        }
148
149        let v1 = version1.to_string()?;
150        let v1 = load::parse_version(&v1).ok_or_else(|| Error::from_hresult(E_INVALIDARG))?;
151        let v2 = version2.to_string()?;
152        let v2 = load::parse_version(&v2).ok_or_else(|| Error::from_hresult(E_INVALIDARG))?;
153
154        *result = match v1.cmp(&v2) {
155            std::cmp::Ordering::Less => -1,
156            std::cmp::Ordering::Equal => 0,
157            std::cmp::Ordering::Greater => 1,
158        };
159    }
160
161    Ok(())
162}