webview2_com/
options.rs

1#![allow(non_snake_case)]
2use std::{cell::UnsafeCell, default::Default, ffi::c_void, mem, ptr};
3
4use windows::{
5    core::{Error, IUnknown, IUnknown_Vtbl, Interface, Result, BOOL, HRESULT, PCWSTR, PWSTR},
6    Win32::{
7        Foundation::{E_POINTER, E_UNEXPECTED, S_OK},
8        System::Com::CoTaskMemAlloc,
9    },
10};
11
12use windows_implement::implement;
13use windows_interface::interface;
14
15use crate::{
16    pwstr::{pwstr_from_str, string_from_pcwstr},
17    Microsoft::Web::WebView2::Win32::{
18        ICoreWebView2CustomSchemeRegistration, ICoreWebView2CustomSchemeRegistration_Impl,
19        ICoreWebView2EnvironmentOptions, ICoreWebView2EnvironmentOptions2,
20        ICoreWebView2EnvironmentOptions2_Impl, ICoreWebView2EnvironmentOptions3,
21        ICoreWebView2EnvironmentOptions3_Impl, ICoreWebView2EnvironmentOptions5,
22        ICoreWebView2EnvironmentOptions5_Impl, ICoreWebView2EnvironmentOptions6,
23        ICoreWebView2EnvironmentOptions6_Impl, ICoreWebView2EnvironmentOptions7,
24        ICoreWebView2EnvironmentOptions7_Impl, ICoreWebView2EnvironmentOptions8,
25        ICoreWebView2EnvironmentOptions8_Impl, ICoreWebView2EnvironmentOptions_Impl,
26        COREWEBVIEW2_CHANNEL_SEARCH_KIND, COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE,
27        COREWEBVIEW2_RELEASE_CHANNELS, COREWEBVIEW2_RELEASE_CHANNELS_BETA,
28        COREWEBVIEW2_RELEASE_CHANNELS_CANARY, COREWEBVIEW2_RELEASE_CHANNELS_DEV,
29        COREWEBVIEW2_RELEASE_CHANNELS_STABLE, COREWEBVIEW2_SCROLLBAR_STYLE,
30        COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT, CORE_WEBVIEW_TARGET_PRODUCT_VERSION,
31    },
32};
33
34#[implement(
35    ICoreWebView2EnvironmentOptions,
36    ICoreWebView2EnvironmentOptions2,
37    ICoreWebView2EnvironmentOptions3,
38    IFixedEnvironmentOptions4,
39    ICoreWebView2EnvironmentOptions5,
40    ICoreWebView2EnvironmentOptions6,
41    ICoreWebView2EnvironmentOptions7,
42    ICoreWebView2EnvironmentOptions8
43)]
44pub struct CoreWebView2EnvironmentOptions {
45    additional_browser_arguments: UnsafeCell<String>,
46    language: UnsafeCell<String>,
47    target_compatible_browser_version: UnsafeCell<String>,
48    allow_single_sign_on_using_os_primary_account: UnsafeCell<bool>,
49    exclusive_user_data_folder_access: UnsafeCell<bool>,
50    is_custom_crash_reporting_enabled: UnsafeCell<bool>,
51    scheme_registrations: UnsafeCell<Vec<Option<ICoreWebView2CustomSchemeRegistration>>>,
52    enable_tracking_prevention: UnsafeCell<bool>,
53    are_browser_extensions_enabled: UnsafeCell<bool>,
54    channel_search_kind: UnsafeCell<COREWEBVIEW2_CHANNEL_SEARCH_KIND>,
55    release_channels: UnsafeCell<COREWEBVIEW2_RELEASE_CHANNELS>,
56    scroll_bar_style: UnsafeCell<COREWEBVIEW2_SCROLLBAR_STYLE>,
57}
58
59impl Default for CoreWebView2EnvironmentOptions {
60    fn default() -> Self {
61        Self {
62            additional_browser_arguments: String::new().into(),
63            language: String::new().into(),
64            target_compatible_browser_version: unsafe {
65                CORE_WEBVIEW_TARGET_PRODUCT_VERSION.to_string()
66            }
67            .unwrap_or_default()
68            .into(),
69            allow_single_sign_on_using_os_primary_account: false.into(),
70            exclusive_user_data_folder_access: false.into(),
71            is_custom_crash_reporting_enabled: false.into(),
72            scheme_registrations: Vec::new().into(),
73            enable_tracking_prevention: true.into(),
74            are_browser_extensions_enabled: false.into(),
75            channel_search_kind: COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE.into(),
76            release_channels: (COREWEBVIEW2_RELEASE_CHANNELS_BETA
77                | COREWEBVIEW2_RELEASE_CHANNELS_CANARY
78                | COREWEBVIEW2_RELEASE_CHANNELS_DEV
79                | COREWEBVIEW2_RELEASE_CHANNELS_STABLE)
80                .into(),
81            scroll_bar_style: COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT.into(),
82        }
83    }
84}
85
86impl CoreWebView2EnvironmentOptions {
87    /// Equivalent to `ICoreWebView2EnvironmentOptions::AdditionalBrowserArguments` without
88    /// extra memory copies or type conversions.
89    ///
90    /// # Safety
91    ///
92    /// This method reads from the same [`UnsafeCell<String>`] as the COM method, but it does not
93    /// write to a mutable pointer for the result.
94    pub unsafe fn additional_browser_arguments(&self) -> &str {
95        (*self.additional_browser_arguments.get()).as_str()
96    }
97
98    /// Equivalent to `ICoreWebView2EnvironmentOptions::SetAdditionalBrowserArguments` without
99    /// extra memory copies or type conversions.
100    ///
101    /// # Safety
102    ///
103    /// This method writes to the same [`UnsafeCell<String>`] as the COM method. It takes an
104    /// immutable reference to `self` so that it can be reused in the COM method.
105    pub unsafe fn set_additional_browser_arguments(&self, value: String) {
106        *self.additional_browser_arguments.get() = value;
107    }
108
109    /// Equivalent to `ICoreWebView2EnvironmentOptions::Language` without extra memory copies or
110    /// type conversions.
111    ///
112    /// # Safety
113    ///
114    /// This method reads from the same [`UnsafeCell<String>`] as the COM method, but it does not
115    /// write to a mutable pointer for the result.
116    pub unsafe fn language(&self) -> &str {
117        (*self.language.get()).as_str()
118    }
119
120    /// Equivalent to `ICoreWebView2EnvironmentOptions::SetLanguage` without extra memory copies or
121    /// type conversions.
122    ///
123    /// # Safety
124    ///
125    /// This method writes to the same [`UnsafeCell<String>`] as the COM method, but it does not
126    /// dereference a pointer for the `value` parameter. It takes an immutable reference to `self`
127    /// so that it can be reused in the COM method.
128    pub unsafe fn set_language(&self, value: String) {
129        *self.language.get() = value;
130    }
131
132    /// Equivalent to `ICoreWebView2EnvironmentOptions::TargetCompatibleBrowserVersion` without
133    /// extra memory copies or type conversions.
134    ///
135    /// # Safety
136    ///
137    /// This method reads from the same [`UnsafeCell<String>`] as the COM method, but it does not
138    /// write to a mutable pointer for the result.
139    pub unsafe fn target_compatible_browser_version(&self) -> &str {
140        (*self.target_compatible_browser_version.get()).as_str()
141    }
142
143    /// Equivalent to `ICoreWebView2EnvironmentOptions::SetTargetCompatibleBrowserVersion` without
144    /// extra memory copies or type conversions.
145    ///
146    /// # Safety
147    ///
148    /// This method writes to the same [`UnsafeCell<String>`] as the COM method, but it does not
149    /// dereference a pointer for the `value` parameter. It takes an immutable reference to `self`
150    /// so that it can be reused in the COM method.
151    pub unsafe fn set_target_compatible_browser_version(&self, value: String) {
152        *self.target_compatible_browser_version.get() = value;
153    }
154
155    /// Equivalent to `ICoreWebView2EnvironmentOptions::AllowSingleSignOnUsingOSPrimaryAccount`
156    /// without extra memory copies or type conversions.
157    ///
158    /// # Safety
159    ///
160    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
161    /// write to a mutable pointer for the result.
162    pub unsafe fn allow_single_sign_on_using_os_primary_account(&self) -> bool {
163        *self.allow_single_sign_on_using_os_primary_account.get()
164    }
165
166    /// Equivalent to `ICoreWebView2EnvironmentOptions::SetAllowSingleSignOnUsingOSPrimaryAccount`
167    /// without extra memory copies or type conversions.
168    ///
169    /// # Safety
170    ///
171    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
172    /// immutable reference to `self` so that it can be reused in the COM method.
173    pub unsafe fn set_allow_single_sign_on_using_os_primary_account(&self, value: bool) {
174        *self.allow_single_sign_on_using_os_primary_account.get() = value;
175    }
176
177    /// Equivalent to `ICoreWebView2EnvironmentOptions2::ExclusiveUserDataFolderAccess` without
178    /// extra memory copies or type conversions.
179    ///
180    /// # Safety
181    ///
182    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
183    /// write to a mutable pointer for the result.
184    pub unsafe fn exclusive_user_data_folder_access(&self) -> bool {
185        *self.exclusive_user_data_folder_access.get()
186    }
187
188    /// Equivalent to `ICoreWebView2EnvironmentOptions2::SetExclusiveUserDataFolderAccess` without
189    /// extra memory copies or type conversions.
190    ///
191    /// # Safety
192    ///
193    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
194    /// immutable reference to `self` so that it can be reused in the COM method.
195    pub unsafe fn set_exclusive_user_data_folder_access(&self, value: bool) {
196        *self.exclusive_user_data_folder_access.get() = value;
197    }
198
199    /// Equivalent to `ICoreWebView2EnvironmentOptions3::IsCustomCrashReportingEnabled` without
200    /// extra memory copies or type conversions.
201    ///
202    /// # Safety
203    ///
204    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
205    /// write to a mutable pointer for the result.
206    pub unsafe fn is_custom_crash_reporting_enabled(&self) -> bool {
207        *self.is_custom_crash_reporting_enabled.get()
208    }
209
210    /// Equivalent to `ICoreWebView2EnvironmentOptions3::SetIsCustomCrashReportingEnabled` without
211    /// extra memory copies or type conversions.
212    ///
213    /// # Safety
214    ///
215    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
216    /// immutable reference to `self` so that it can be reused in the COM method.
217    pub unsafe fn set_is_custom_crash_reporting_enabled(&self, value: bool) {
218        *self.is_custom_crash_reporting_enabled.get() = value;
219    }
220
221    /// Equivalent to [`IFixedEnvironmentOptions4`]`::GetCustomSchemeRegistrations` without extra
222    /// memory copies or type conversions.
223    ///
224    /// # Safety
225    ///
226    /// This method reads from the same [`UnsafeCell<Vec<>>`] as the COM method, but it does not
227    /// allocate or write to a mutable pointer for the result.
228    pub unsafe fn scheme_registrations(
229        &self,
230    ) -> Vec<Option<ICoreWebView2CustomSchemeRegistration>> {
231        (*self.scheme_registrations.get()).clone()
232    }
233
234    /// Equivalent to [`IFixedEnvironmentOptions4`]`::SetCustomSchemeRegistrations` without extra
235    /// memory copies or type conversions.
236    ///
237    /// # Safety
238    ///
239    /// This method writes to the same [`UnsafeCell<Vec<>>`] as the COM method. It takes an
240    /// immutable reference to `self` for consistency with the other public set methods, however
241    /// the COM method implementation does not actually reuse it.
242    pub unsafe fn set_scheme_registrations(
243        &self,
244        value: Vec<Option<ICoreWebView2CustomSchemeRegistration>>,
245    ) {
246        *self.scheme_registrations.get() = value;
247    }
248
249    /// Equivalent to `ICoreWebView2EnvironmentOptions5::EnableTrackingPrevention` without
250    /// extra memory copies or type conversions.
251    ///
252    /// # Safety
253    ///
254    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
255    /// write to a mutable pointer for the result.
256    pub unsafe fn enable_tracking_prevention(&self) -> bool {
257        *self.enable_tracking_prevention.get()
258    }
259
260    /// Equivalent to `ICoreWebView2EnvironmentOptions5::SetEnableTrackingPrevention` without
261    /// extra memory copies or type conversions.
262    ///
263    /// # Safety
264    ///
265    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
266    /// immutable reference to `self` so that it can be reused in the COM method.
267    pub unsafe fn set_enable_tracking_prevention(&self, value: bool) {
268        *self.enable_tracking_prevention.get() = value;
269    }
270
271    /// Equivalent to `ICoreWebView2EnvironmentOptions6::AreBrowserExtensionsEnabled` without
272    /// extra memory copies or type conversions.
273    ///
274    /// # Safety
275    ///
276    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
277    /// write to a mutable pointer for the result.
278    pub unsafe fn are_browser_extensions_enabled(&self) -> bool {
279        *self.are_browser_extensions_enabled.get()
280    }
281
282    /// Equivalent to `ICoreWebView2EnvironmentOptions6::SetAreBrowserExtensionsEnabled` without
283    /// extra memory copies or type conversions.
284    ///
285    /// # Safety
286    ///
287    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
288    /// immutable reference to `self` so that it can be reused in the COM method.
289    pub unsafe fn set_are_browser_extensions_enabled(&self, value: bool) {
290        *self.are_browser_extensions_enabled.get() = value;
291    }
292
293    /// Equivalent to `ICoreWebView2EnvironmentOptions7::ChannelSearchKind` without extra memory
294    /// copies or type conversions.
295    ///
296    /// # Safety
297    ///
298    /// This method reads from the same [`UnsafeCell<COREWEBVIEW2_CHANNEL_SEARCH_KIND>`] as the COM
299    /// method, but it does not write to a mutable pointer for the result.
300    pub unsafe fn channel_search_kind(&self) -> COREWEBVIEW2_CHANNEL_SEARCH_KIND {
301        *self.channel_search_kind.get()
302    }
303
304    /// Equivalent to `ICoreWebView2EnvironmentOptions7::SetChannelSearchKind` without extra memory
305    /// copies or type conversions.
306    ///
307    /// # Safety
308    ///
309    /// This method writes to the same [`UnsafeCell<COREWEBVIEW2_CHANNEL_SEARCH_KIND>`] as the COM
310    /// method. It takes an immutable reference to `self` so that it can be reused in the COM
311    /// method.
312    pub unsafe fn set_channel_search_kind(&self, value: COREWEBVIEW2_CHANNEL_SEARCH_KIND) {
313        *self.channel_search_kind.get() = value;
314    }
315
316    /// Equivalent to `ICoreWebView2EnvironmentOptions7::ReleaseChannels` without extra memory
317    /// copies or type conversions.
318    ///
319    /// # Safety
320    ///
321    /// This method reads from the same [`UnsafeCell<COREWEBVIEW2_RELEASE_CHANNELS>`] as the COM
322    /// method, but it does not write to a mutable pointer for the result.
323    pub unsafe fn release_channels(&self) -> COREWEBVIEW2_RELEASE_CHANNELS {
324        *self.release_channels.get()
325    }
326
327    /// Equivalent to `ICoreWebView2EnvironmentOptions7::SetReleaseChannels` without extra memory
328    /// copies or type conversions.
329    ///
330    /// # Safety
331    ///
332    /// This method writes to the same [`UnsafeCell<COREWEBVIEW2_RELEASE_CHANNELS>`] as the COM
333    /// method. It takes an immutable reference to `self` so that it can be reused in the COM
334    /// method.
335    pub unsafe fn set_release_channels(&self, value: COREWEBVIEW2_RELEASE_CHANNELS) {
336        *self.release_channels.get() = value;
337    }
338
339    /// Equivalent to `ICoreWebView2EnvironmentOptions8::ScrollBarStyle` without extra memory
340    /// copies or type conversions.
341    ///
342    /// # Safety
343    ///
344    /// This method reads from the same [`UnsafeCell<COREWEBVIEW2_SCROLLBAR_STYLE>`] as the COM
345    /// method, but it does not write to a mutable pointer for the result.
346    pub unsafe fn scroll_bar_style(&self) -> COREWEBVIEW2_SCROLLBAR_STYLE {
347        *self.scroll_bar_style.get()
348    }
349
350    /// Equivalent to `ICoreWebView2EnvironmentOptions8::SetScrollBarStyle` without extra memory
351    /// copies or type conversions.
352    ///
353    /// # Safety
354    ///
355    /// This method writes to the same [`UnsafeCell<COREWEBVIEW2_SCROLLBAR_STYLE>`] as the COM
356    /// method. It takes an immutable reference to `self` so that it can be reused in the COM
357    /// method.
358    pub unsafe fn set_scroll_bar_style(&self, value: COREWEBVIEW2_SCROLLBAR_STYLE) {
359        *self.scroll_bar_style.get() = value;
360    }
361}
362
363#[allow(clippy::not_unsafe_ptr_arg_deref)]
364impl ICoreWebView2EnvironmentOptions_Impl for CoreWebView2EnvironmentOptions_Impl {
365    fn AdditionalBrowserArguments(&self, result: *mut PWSTR) -> Result<()> {
366        if result.is_null() {
367            E_POINTER.ok()
368        } else {
369            unsafe { *result = pwstr_from_str(self.additional_browser_arguments()) };
370            Ok(())
371        }
372    }
373
374    fn SetAdditionalBrowserArguments(&self, value: &PCWSTR) -> Result<()> {
375        unsafe { self.set_additional_browser_arguments(string_from_pcwstr(value)) };
376        Ok(())
377    }
378
379    fn Language(&self, result: *mut PWSTR) -> Result<()> {
380        if result.is_null() {
381            E_POINTER.ok()
382        } else {
383            unsafe { *result = pwstr_from_str(self.language()) };
384            Ok(())
385        }
386    }
387
388    fn SetLanguage(&self, value: &PCWSTR) -> Result<()> {
389        unsafe { self.set_language(string_from_pcwstr(value)) };
390        Ok(())
391    }
392
393    fn TargetCompatibleBrowserVersion(&self, result: *mut PWSTR) -> Result<()> {
394        if result.is_null() {
395            E_POINTER.ok()
396        } else {
397            unsafe { *result = pwstr_from_str(self.target_compatible_browser_version()) };
398            Ok(())
399        }
400    }
401
402    fn SetTargetCompatibleBrowserVersion(&self, value: &PCWSTR) -> Result<()> {
403        unsafe { self.set_target_compatible_browser_version(string_from_pcwstr(value)) };
404        Ok(())
405    }
406
407    fn AllowSingleSignOnUsingOSPrimaryAccount(&self, result: *mut BOOL) -> Result<()> {
408        if result.is_null() {
409            E_POINTER.ok()
410        } else {
411            unsafe { *result = self.allow_single_sign_on_using_os_primary_account().into() };
412            Ok(())
413        }
414    }
415
416    fn SetAllowSingleSignOnUsingOSPrimaryAccount(&self, value: BOOL) -> Result<()> {
417        unsafe {
418            self.set_allow_single_sign_on_using_os_primary_account(value.into());
419        }
420        Ok(())
421    }
422}
423
424#[allow(clippy::not_unsafe_ptr_arg_deref)]
425impl ICoreWebView2EnvironmentOptions2_Impl for CoreWebView2EnvironmentOptions_Impl {
426    fn ExclusiveUserDataFolderAccess(&self, result: *mut BOOL) -> Result<()> {
427        if result.is_null() {
428            E_POINTER.ok()
429        } else {
430            unsafe { *result = self.exclusive_user_data_folder_access().into() };
431            Ok(())
432        }
433    }
434
435    fn SetExclusiveUserDataFolderAccess(&self, value: BOOL) -> Result<()> {
436        unsafe {
437            self.set_exclusive_user_data_folder_access(value.into());
438        }
439        Ok(())
440    }
441}
442
443#[allow(clippy::not_unsafe_ptr_arg_deref)]
444impl ICoreWebView2EnvironmentOptions3_Impl for CoreWebView2EnvironmentOptions_Impl {
445    fn IsCustomCrashReportingEnabled(&self, result: *mut BOOL) -> Result<()> {
446        if result.is_null() {
447            E_POINTER.ok()
448        } else {
449            unsafe { *result = self.is_custom_crash_reporting_enabled().into() };
450            Ok(())
451        }
452    }
453
454    fn SetIsCustomCrashReportingEnabled(&self, value: BOOL) -> Result<()> {
455        unsafe {
456            self.set_is_custom_crash_reporting_enabled(value.into());
457        }
458        Ok(())
459    }
460}
461
462/// This is an alternate declaration of the [`crate::Microsoft::Web::WebView2::Win32::ICoreWebView2EnvironmentOptions4`]
463/// interface, which matches the parameters for `SetCustomSchemeRegistrations`. The `windows`
464/// crate mistakenly interprets the array of interface pointers as a pointer to an out-param,
465/// and it converts that into a `Result<Option<ICoreWebView2CustomSchemeRegistration>>`.
466#[interface("AC52D13F-0D38-475A-9DCA-876580D6793E")]
467pub unsafe trait IFixedEnvironmentOptions4: IUnknown {
468    fn GetCustomSchemeRegistrations(
469        &self,
470        count: *mut u32,
471        scheme_registrations: *mut *mut *mut c_void,
472    ) -> HRESULT;
473
474    fn SetCustomSchemeRegistrations(
475        &self,
476        count: u32,
477        scheme_registrations: *const *mut c_void,
478    ) -> HRESULT;
479}
480
481#[allow(clippy::not_unsafe_ptr_arg_deref)]
482impl IFixedEnvironmentOptions4_Impl for CoreWebView2EnvironmentOptions_Impl {
483    #[allow(clippy::crosspointer_transmute)]
484    unsafe fn GetCustomSchemeRegistrations(
485        &self,
486        count: *mut u32,
487        results: *mut *mut *mut c_void,
488    ) -> HRESULT {
489        if count.is_null() || results.is_null() {
490            E_POINTER
491        } else {
492            let scheme_registrations = &*self.scheme_registrations.get();
493            if let Ok(length) = scheme_registrations.len().try_into() {
494                *count = length;
495                if !scheme_registrations.is_empty() {
496                    *results = CoTaskMemAlloc(
497                        mem::size_of::<*mut c_void>() * (*scheme_registrations).len(),
498                    ) as *mut *mut _;
499                    let results =
500                        ptr::slice_from_raw_parts_mut(*results, scheme_registrations.len());
501                    for (i, scheme) in scheme_registrations.iter().enumerate() {
502                        (*results)[i] = scheme
503                            .clone()
504                            .map_or(ptr::null_mut(), |scheme| scheme.into_raw())
505                    }
506                } else {
507                    *results = ptr::null_mut();
508                }
509                S_OK
510            } else {
511                E_UNEXPECTED
512            }
513        }
514    }
515
516    unsafe fn SetCustomSchemeRegistrations(
517        &self,
518        count: u32,
519        values: *const *mut c_void,
520    ) -> HRESULT {
521        if let Ok(count) = count.try_into() {
522            let scheme_registrations = &mut *self.scheme_registrations.get();
523            scheme_registrations.clear();
524            scheme_registrations.reserve_exact(count);
525            let values = &*ptr::slice_from_raw_parts(values, count);
526            for &scheme in values.iter() {
527                scheme_registrations.push(
528                    ICoreWebView2CustomSchemeRegistration::from_raw_borrowed(&scheme).cloned(),
529                );
530            }
531            S_OK
532        } else {
533            E_UNEXPECTED
534        }
535    }
536}
537
538impl ICoreWebView2EnvironmentOptions5_Impl for CoreWebView2EnvironmentOptions_Impl {
539    #[allow(clippy::not_unsafe_ptr_arg_deref)]
540    fn EnableTrackingPrevention(&self, value: *mut BOOL) -> windows_core::Result<()> {
541        unsafe {
542            let value = value.as_mut().ok_or(Error::from(E_POINTER))?;
543            *value = self.enable_tracking_prevention().into()
544        };
545        Ok(())
546    }
547
548    fn SetEnableTrackingPrevention(&self, value: BOOL) -> windows_core::Result<()> {
549        unsafe {
550            self.set_enable_tracking_prevention(value.into());
551        }
552        Ok(())
553    }
554}
555
556impl ICoreWebView2EnvironmentOptions6_Impl for CoreWebView2EnvironmentOptions_Impl {
557    #[allow(clippy::not_unsafe_ptr_arg_deref)]
558    fn AreBrowserExtensionsEnabled(&self, value: *mut BOOL) -> windows_core::Result<()> {
559        unsafe {
560            let value = value.as_mut().ok_or(Error::from(E_POINTER))?;
561            *value = self.are_browser_extensions_enabled().into()
562        };
563        Ok(())
564    }
565
566    fn SetAreBrowserExtensionsEnabled(&self, value: BOOL) -> windows_core::Result<()> {
567        unsafe {
568            self.set_are_browser_extensions_enabled(value.into());
569        }
570        Ok(())
571    }
572}
573
574impl ICoreWebView2EnvironmentOptions7_Impl for CoreWebView2EnvironmentOptions_Impl {
575    #[allow(clippy::not_unsafe_ptr_arg_deref)]
576    fn ChannelSearchKind(
577        &self,
578        value: *mut COREWEBVIEW2_CHANNEL_SEARCH_KIND,
579    ) -> windows_core::Result<()> {
580        unsafe {
581            let value = value.as_mut().ok_or(Error::from(E_POINTER))?;
582            *value = self.channel_search_kind()
583        };
584        Ok(())
585    }
586
587    fn SetChannelSearchKind(
588        &self,
589        value: COREWEBVIEW2_CHANNEL_SEARCH_KIND,
590    ) -> windows_core::Result<()> {
591        unsafe {
592            self.set_channel_search_kind(value);
593        }
594        Ok(())
595    }
596
597    #[allow(clippy::not_unsafe_ptr_arg_deref)]
598    fn ReleaseChannels(
599        &self,
600        value: *mut COREWEBVIEW2_RELEASE_CHANNELS,
601    ) -> windows_core::Result<()> {
602        unsafe {
603            let value = value.as_mut().ok_or(Error::from(E_POINTER))?;
604            *value = self.release_channels()
605        };
606        Ok(())
607    }
608
609    fn SetReleaseChannels(&self, value: COREWEBVIEW2_RELEASE_CHANNELS) -> windows_core::Result<()> {
610        unsafe {
611            self.set_release_channels(value);
612        }
613        Ok(())
614    }
615}
616
617impl ICoreWebView2EnvironmentOptions8_Impl for CoreWebView2EnvironmentOptions_Impl {
618    #[allow(clippy::not_unsafe_ptr_arg_deref)]
619    fn ScrollBarStyle(&self, value: *mut COREWEBVIEW2_SCROLLBAR_STYLE) -> windows_core::Result<()> {
620        unsafe {
621            let value = value.as_mut().ok_or(Error::from(E_POINTER))?;
622            *value = self.scroll_bar_style()
623        };
624        Ok(())
625    }
626
627    fn SetScrollBarStyle(&self, value: COREWEBVIEW2_SCROLLBAR_STYLE) -> windows_core::Result<()> {
628        unsafe {
629            self.set_scroll_bar_style(value);
630        }
631        Ok(())
632    }
633}
634
635#[implement(ICoreWebView2CustomSchemeRegistration)]
636pub struct CoreWebView2CustomSchemeRegistration {
637    scheme_name: String,
638    treat_as_secure: UnsafeCell<bool>,
639    allowed_origins: UnsafeCell<Vec<String>>,
640    has_authority_component: UnsafeCell<bool>,
641}
642
643impl CoreWebView2CustomSchemeRegistration {
644    pub fn new(scheme_name: String) -> Self {
645        Self {
646            scheme_name,
647            treat_as_secure: false.into(),
648            allowed_origins: Vec::new().into(),
649            has_authority_component: false.into(),
650        }
651    }
652
653    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::SchemeName` without extra memory
654    /// copies or type conversions.
655    pub fn scheme_name(&self) -> &str {
656        self.scheme_name.as_str()
657    }
658
659    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::TreatAsSecure` without extra memory
660    /// copies or type conversions.
661    ///
662    /// # Safety
663    ///
664    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
665    /// write to a mutable pointer for the result.
666    pub unsafe fn treat_as_secure(&self) -> bool {
667        *self.treat_as_secure.get()
668    }
669
670    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::SetTreatAsSecure` without extra
671    /// memory copies or type conversions.
672    ///
673    /// # Safety
674    ///
675    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
676    /// immutable reference to `self` so that it can be reused in the COM method.
677    pub unsafe fn set_treat_as_secure(&self, value: bool) {
678        *self.treat_as_secure.get() = value;
679    }
680
681    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::GetAllowedOrigins` without extra
682    /// memory copies or type conversions.
683    ///
684    /// # Safety
685    ///
686    /// This method reads from the same [`UnsafeCell<Vec<>>`] as the COM method, but it does not
687    /// allocate or write to a mutable pointer for the result.
688    pub unsafe fn allowed_origins(&self) -> Vec<String> {
689        (*self.allowed_origins.get()).clone()
690    }
691
692    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::SetAllowedOrigins` without extra
693    /// memory copies or type conversions.
694    ///
695    /// # Safety
696    ///
697    /// This method writes to the same [`UnsafeCell<Vec<>>`] as the COM method. It takes an
698    /// immutable reference to `self` for consistency with the other public set methods, however
699    /// the COM method implementation does not actually reuse it.
700    pub unsafe fn set_allowed_origins(&self, value: Vec<String>) {
701        *self.allowed_origins.get() = value;
702    }
703
704    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::HasAuthorityComponent` without extra
705    /// memory copies or type conversions.
706    ///
707    /// # Safety
708    ///
709    /// This method reads from the same [`UnsafeCell<bool>`] as the COM method, but it does not
710    /// write to a mutable pointer for the result.
711    pub unsafe fn has_authority_component(&self) -> bool {
712        *self.has_authority_component.get()
713    }
714
715    /// Equivalent to `ICoreWebView2CustomSchemeRegistration::SetHasAuthorityComponent` without
716    /// extra memory copies or type conversions.
717    ///
718    /// # Safety
719    ///
720    /// This method writes to the same [`UnsafeCell<bool>`] as the COM method. It takes an
721    /// immutable reference to `self` so that it can be reused in the COM method.
722    pub unsafe fn set_has_authority_component(&self, value: bool) {
723        *self.has_authority_component.get() = value;
724    }
725}
726
727#[allow(clippy::not_unsafe_ptr_arg_deref)]
728impl ICoreWebView2CustomSchemeRegistration_Impl for CoreWebView2CustomSchemeRegistration_Impl {
729    fn SchemeName(&self, result: *mut PWSTR) -> Result<()> {
730        if result.is_null() {
731            E_POINTER.ok()
732        } else {
733            unsafe { *result = pwstr_from_str(&self.scheme_name) };
734            Ok(())
735        }
736    }
737
738    fn TreatAsSecure(&self, result: *mut BOOL) -> Result<()> {
739        if result.is_null() {
740            E_POINTER.ok()
741        } else {
742            unsafe { *result = self.treat_as_secure().into() };
743            Ok(())
744        }
745    }
746
747    fn SetTreatAsSecure(&self, value: BOOL) -> Result<()> {
748        unsafe {
749            self.set_treat_as_secure(value.into());
750        }
751        Ok(())
752    }
753
754    fn GetAllowedOrigins(&self, count: *mut u32, results: *mut *mut PWSTR) -> Result<()> {
755        unsafe {
756            let allowed_origins = &*self.allowed_origins.get();
757            *count = allowed_origins
758                .len()
759                .try_into()
760                .map_err(|_| Error::from(E_UNEXPECTED))?;
761            if !allowed_origins.is_empty() {
762                *results = CoTaskMemAlloc(mem::size_of::<*mut PWSTR>() * (*allowed_origins).len())
763                    as *mut _;
764                let results = ptr::slice_from_raw_parts_mut(*results, allowed_origins.len());
765                for (i, scheme) in allowed_origins.iter().enumerate() {
766                    (*results)[i] = pwstr_from_str(scheme);
767                }
768            } else {
769                *results = ptr::null_mut();
770            }
771        }
772        Ok(())
773    }
774
775    fn SetAllowedOrigins(&self, count: u32, values: *const PCWSTR) -> Result<()> {
776        unsafe {
777            let count = count.try_into().map_err(|_| Error::from(E_UNEXPECTED))?;
778            let allowed_origins = &mut *self.allowed_origins.get();
779            allowed_origins.clear();
780            allowed_origins.reserve_exact(count);
781            let values = &*ptr::slice_from_raw_parts(values, count);
782            for origin in values.iter() {
783                allowed_origins.push(string_from_pcwstr(origin));
784            }
785        }
786        Ok(())
787    }
788
789    fn HasAuthorityComponent(&self, result: *mut BOOL) -> Result<()> {
790        if result.is_null() {
791            E_POINTER.ok()
792        } else {
793            unsafe { *result = self.has_authority_component().into() };
794            Ok(())
795        }
796    }
797
798    fn SetHasAuthorityComponent(&self, value: BOOL) -> Result<()> {
799        unsafe {
800            self.set_has_authority_component(value.into());
801        }
802        Ok(())
803    }
804}
805
806#[cfg(test)]
807mod test {
808    use std::{collections::BTreeSet, ptr};
809
810    use regex::Regex;
811    use windows::Win32::System::Com::CoTaskMemFree;
812    use windows_core::w;
813
814    use webview2_com_sys::declared_interfaces;
815
816    use crate::{
817        pwstr::take_pwstr,
818        Microsoft::Web::WebView2::Win32::{
819            ICoreWebView2EnvironmentOptions, CORE_WEBVIEW_TARGET_PRODUCT_VERSION,
820        },
821    };
822
823    use super::*;
824
825    #[test]
826    fn all_implemented() {
827        let contents = include_str!("options.rs");
828        let pattern =
829            Regex::new(r#"(ICoreWebView2EnvironmentOptions[0-9]*)"#).expect("valid regex");
830        let implemented: BTreeSet<&str> = contents
831            .lines()
832            .filter_map(|line| pattern.captures(line))
833            .filter_map(|captures| captures.get(1))
834            .map(|match_1| match_1.as_str())
835            .collect();
836        let all_declared_options = declared_interfaces::all_declared_options();
837        let missing: Vec<_> = all_declared_options
838            .iter()
839            .filter_map(|name| {
840                if implemented.contains(name) {
841                    None
842                } else {
843                    Some(name.to_string())
844                }
845            })
846            .collect();
847        let extra: Vec<_> = implemented
848            .iter()
849            .filter_map(|name| {
850                if all_declared_options.contains(name) {
851                    None
852                } else {
853                    Some(name.to_string())
854                }
855            })
856            .collect();
857        assert!(
858            missing.is_empty() && extra.is_empty(),
859            "missing: {missing:?}\nextra: {extra:?}"
860        );
861    }
862
863    #[test]
864    fn additional_arguments() {
865        let options: ICoreWebView2EnvironmentOptions =
866            CoreWebView2EnvironmentOptions::default().into();
867        unsafe { options.SetAdditionalBrowserArguments(w!("FakeArguments")) }.unwrap();
868        let mut result = PWSTR(ptr::null_mut());
869        unsafe { options.AdditionalBrowserArguments(&mut result) }.unwrap();
870        let result = take_pwstr(result);
871        assert_eq!(&result, "FakeArguments");
872    }
873
874    #[test]
875    fn override_language() {
876        let options: ICoreWebView2EnvironmentOptions =
877            CoreWebView2EnvironmentOptions::default().into();
878        unsafe { options.SetLanguage(w!("FakeLanguage")) }.unwrap();
879        let mut result = PWSTR(ptr::null_mut::<u16>());
880        unsafe { options.Language(&mut result) }.unwrap();
881        let result = take_pwstr(result);
882        assert_eq!(&result, "FakeLanguage");
883    }
884
885    #[test]
886    fn default_version() {
887        let options: ICoreWebView2EnvironmentOptions =
888            CoreWebView2EnvironmentOptions::default().into();
889        let mut result = PWSTR(ptr::null_mut::<u16>());
890        unsafe { options.TargetCompatibleBrowserVersion(&mut result) }.unwrap();
891        let result = take_pwstr(result);
892        assert_eq!(
893            result,
894            unsafe { CORE_WEBVIEW_TARGET_PRODUCT_VERSION.to_string() }.unwrap()
895        );
896    }
897
898    #[test]
899    fn override_version() {
900        assert_ne!(
901            "FakeVersion",
902            unsafe { CORE_WEBVIEW_TARGET_PRODUCT_VERSION.to_string() }
903                .unwrap()
904                .as_str()
905        );
906        let options: ICoreWebView2EnvironmentOptions =
907            CoreWebView2EnvironmentOptions::default().into();
908        unsafe { options.SetTargetCompatibleBrowserVersion(w!("FakeVersion")) }.unwrap();
909        let mut result = PWSTR(ptr::null_mut::<u16>());
910        unsafe { options.TargetCompatibleBrowserVersion(&mut result) }.unwrap();
911        let result = take_pwstr(result);
912        assert_eq!(&result, "FakeVersion");
913    }
914
915    #[test]
916    fn default_allow_sso() {
917        let options: ICoreWebView2EnvironmentOptions =
918            CoreWebView2EnvironmentOptions::default().into();
919        let mut result = BOOL(1);
920        unsafe { options.AllowSingleSignOnUsingOSPrimaryAccount(&mut result) }.unwrap();
921        assert_eq!(result.0, 0);
922    }
923
924    #[test]
925    fn override_allow_sso() {
926        let options: ICoreWebView2EnvironmentOptions =
927            CoreWebView2EnvironmentOptions::default().into();
928        unsafe { options.SetAllowSingleSignOnUsingOSPrimaryAccount(true) }.unwrap();
929        let mut result = BOOL(0);
930        unsafe { options.AllowSingleSignOnUsingOSPrimaryAccount(&mut result) }.unwrap();
931        assert_eq!(result.0, 1);
932    }
933
934    #[test]
935    fn default_exclusive_data_folder() {
936        let options: ICoreWebView2EnvironmentOptions2 =
937            CoreWebView2EnvironmentOptions::default().into();
938        let mut result = BOOL(1);
939        unsafe { options.ExclusiveUserDataFolderAccess(&mut result) }.unwrap();
940        assert_eq!(result.0, 0);
941    }
942
943    #[test]
944    fn override_exclusive_data_folder() {
945        let options: ICoreWebView2EnvironmentOptions2 =
946            CoreWebView2EnvironmentOptions::default().into();
947        unsafe { options.SetExclusiveUserDataFolderAccess(true) }.unwrap();
948        let mut result = BOOL(0);
949        unsafe { options.ExclusiveUserDataFolderAccess(&mut result) }.unwrap();
950        assert_eq!(result.0, 1);
951    }
952
953    #[test]
954    fn default_custom_crash_reporting() {
955        let options: ICoreWebView2EnvironmentOptions3 =
956            CoreWebView2EnvironmentOptions::default().into();
957        let mut result = BOOL(1);
958        unsafe { options.IsCustomCrashReportingEnabled(&mut result) }.unwrap();
959        assert_eq!(result.0, 0);
960    }
961
962    #[test]
963    fn override_custom_crash_reporting() {
964        let options: ICoreWebView2EnvironmentOptions3 =
965            CoreWebView2EnvironmentOptions::default().into();
966        unsafe { options.SetIsCustomCrashReportingEnabled(true) }.unwrap();
967        let mut result = BOOL(0);
968        unsafe { options.IsCustomCrashReportingEnabled(&mut result) }.unwrap();
969        assert_eq!(result.0, 1);
970    }
971
972    #[test]
973    fn default_scheme_registrations() {
974        let options: IFixedEnvironmentOptions4 = CoreWebView2EnvironmentOptions::default().into();
975        let mut count = 1;
976        let mut scheme_registrations = ptr::null_mut();
977        assert!(unsafe {
978            options.GetCustomSchemeRegistrations(&mut count, &mut scheme_registrations)
979        }
980        .is_ok());
981        assert_eq!(0, count);
982        assert_eq!(ptr::null_mut(), scheme_registrations);
983    }
984
985    #[test]
986    fn override_scheme_registrations() {
987        let options: IFixedEnvironmentOptions4 = CoreWebView2EnvironmentOptions::default().into();
988        let scheme: ICoreWebView2CustomSchemeRegistration =
989            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
990        assert!(
991            unsafe { options.SetCustomSchemeRegistrations(1, &[scheme.as_raw()] as *const _) }
992                .is_ok()
993        );
994        let mut count = 0;
995        let mut scheme_registrations = ptr::null_mut();
996        assert!(unsafe {
997            options.GetCustomSchemeRegistrations(&mut count, &mut scheme_registrations)
998        }
999        .is_ok());
1000        assert_eq!(1, count);
1001        unsafe {
1002            let scheme_registration =
1003                ICoreWebView2CustomSchemeRegistration::from_raw(*scheme_registrations);
1004            assert_eq!(scheme.as_raw(), scheme_registration.as_raw());
1005            CoTaskMemFree(Some(scheme_registrations as *const _));
1006        }
1007    }
1008
1009    #[test]
1010    fn scheme_name() {
1011        const SCHEME_NAME: &str = "custom";
1012        let scheme: ICoreWebView2CustomSchemeRegistration =
1013            CoreWebView2CustomSchemeRegistration::new(SCHEME_NAME.to_string()).into();
1014        let mut result = PWSTR(ptr::null_mut::<u16>());
1015        unsafe { scheme.SchemeName(&mut result) }.unwrap();
1016        let result = take_pwstr(result);
1017        assert_eq!(result, SCHEME_NAME);
1018    }
1019
1020    #[test]
1021    fn default_treat_as_secure() {
1022        let scheme: ICoreWebView2CustomSchemeRegistration =
1023            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1024        let mut result = BOOL(1);
1025        unsafe { scheme.TreatAsSecure(&mut result) }.unwrap();
1026        assert_eq!(result.0, 0);
1027    }
1028
1029    #[test]
1030    fn override_treat_as_secure() {
1031        let scheme: ICoreWebView2CustomSchemeRegistration =
1032            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1033        unsafe { scheme.SetTreatAsSecure(true) }.unwrap();
1034        let mut result = BOOL(0);
1035        unsafe { scheme.TreatAsSecure(&mut result) }.unwrap();
1036        assert_eq!(result.0, 1);
1037    }
1038
1039    #[test]
1040    fn default_allowed_origins() {
1041        let scheme: ICoreWebView2CustomSchemeRegistration =
1042            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1043        let mut count = 1_u32;
1044        let mut origin = pwstr_from_str("origin");
1045        let mut results = &mut origin as *mut _;
1046        let _ = take_pwstr(origin);
1047        unsafe { scheme.GetAllowedOrigins(&mut count, &mut results) }.unwrap();
1048        assert_eq!(count, 0);
1049        assert_eq!(results, ptr::null_mut());
1050    }
1051
1052    #[test]
1053    fn override_allowed_origins() {
1054        const ORIGIN: &str = "origin";
1055        let scheme: ICoreWebView2CustomSchemeRegistration =
1056            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1057        let origin = pwstr_from_str(ORIGIN);
1058        unsafe { scheme.SetAllowedOrigins(1, &PCWSTR(origin.0)) }.unwrap();
1059
1060        let mut count = 0_u32;
1061        let mut results = ptr::null_mut();
1062        unsafe { scheme.GetAllowedOrigins(&mut count, &mut results) }.unwrap();
1063        assert_eq!(count, 1);
1064
1065        assert_ne!(results, ptr::null_mut());
1066        let mut origin = PWSTR(ptr::null_mut());
1067        unsafe { core::ptr::swap(&mut origin, results) };
1068        let origin = take_pwstr(origin);
1069        unsafe { CoTaskMemFree(Some(results as *const _)) };
1070        assert_eq!(origin, ORIGIN);
1071    }
1072
1073    #[test]
1074    fn default_has_authority_component() {
1075        let scheme: ICoreWebView2CustomSchemeRegistration =
1076            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1077        let mut result = BOOL(1);
1078        unsafe { scheme.HasAuthorityComponent(&mut result) }.unwrap();
1079        assert_eq!(result.0, 0);
1080    }
1081
1082    #[test]
1083    fn override_has_authority_component() {
1084        let scheme: ICoreWebView2CustomSchemeRegistration =
1085            CoreWebView2CustomSchemeRegistration::new(String::new()).into();
1086        unsafe { scheme.SetHasAuthorityComponent(true) }.unwrap();
1087        let mut result = BOOL(0);
1088        unsafe { scheme.HasAuthorityComponent(&mut result) }.unwrap();
1089        assert_eq!(result.0, 1);
1090    }
1091}