1#:
4
5> The Microsoft Edge WebView2 control enables you to embed web technologies
6(HTML, CSS, and JavaScript) in your native applications. The WebView2 control
7uses Microsoft Edge (Chromium) as the rendering engine to display the web
8content in native applications. With WebView2, you may embed web code in
9different parts of your native application, or build the entire native
10application within a single WebView. For information on how to start building a
11WebView2 application, see [Get
12Started](https://docs.microsoft.com/en-us/microsoft-edge/webview2/#getting-started).
13
14# API
15
16The `webview2` crate contains high-level, idiomatic wrappers for the raw COM
17APIs, which can be found in the `webview2-sys` crate.
18
19The API mapping should be quite straightforward.
20
21The `CreateCoreWebView2EnvironmentWithDetails` function does not have a direct
22equivalent. It is replaced with a nicer `EnvironmentBuilder` API. The
23`GetAvailableCoreWebView2BrowserVersionString` and `CompareBrowserVersions`
24functions are also exposed through the builder.
25
26# Runtime
27
28The Edge browser from beta, dev or canary channels (>= 86.0.622.0) or the
29[Evergreen WebView2
30Runtime](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#understand-the-webview2-runtime-and-installer-preview)
31need to be installed for this to actually work. Or the
32[`build`](struct.EnvironmentBuilder.html#method.build) method will return an
33error.
34
35# WebView2Loader
36
37A binary library `WebView2Loader` from the WebView2 SDK need to be used, either
38the DLL `WebView2Loader.dll` or the static library `WebView2LoaderStatic.lib`.
39This brings some complexities:
40
41* When using the **gnu** toolchain, the static library does not seem to work so
42 the `WebView2Loader.dll` DLL is used. You need to make sure that the DLL can
43 be loaded at runtime, e.g. by **putting it alongside the built exe files**.
44
45* When using the **msvc** toolchain, the static library is used. Make sure you
46 have the **v142** toolset (or **visual studio 2019**), because the static
47 library seem to be built with visual studio 2019 and could not be correctly
48 linked by earlier versions of the visual studio. See [C++ binary compatibility
49 between Visual Studio 2015, 2017, and
50 2019](https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2019).
51
52# Examples
53
54See the `examples` directory, especially the heavily commented `win32` example.
55"###]
56#![cfg(windows)]
57#![allow(clippy::cmp_null)]
59#![allow(clippy::type_complexity)]
60#![allow(clippy::upper_case_acronyms)]
61#![allow(non_camel_case_types)]
62
63use com::{interfaces::IUnknown, ComInterface, ComPtr, ComRc};
64use std::cell::{Cell, RefCell};
65use std::fmt;
66use std::io;
67use std::mem::{self, MaybeUninit};
68use std::path::Path;
69use std::ptr;
70use webview2_sys::*;
71use widestring::{NulError, WideCStr, WideCString};
72use winapi::shared::minwindef::*;
73use winapi::shared::ntdef::*;
74use winapi::shared::windef::*;
75use winapi::shared::winerror::{
76 E_FAIL, E_INVALIDARG, E_NOINTERFACE, FACILITY_WIN32, HRESULT_CODE, HRESULT_FROM_WIN32,
77 MAKE_HRESULT, SEVERITY_ERROR, SUCCEEDED, S_OK,
78};
79use winapi::um::combaseapi::{CoTaskMemAlloc, CoTaskMemFree};
80
81static DEFAULT_TARGET_COMPATIBLE_BROWSER_VERSION: &str = "89.0.765";
82
83#[macro_export]
86macro_rules! callback {
87 ($name:ident, move | $($arg:ident : $arg_type:ty),* $(,)?| -> $ret_type:ty { $($body:tt)* }) => {{
88 #[com::co_class(implements($name))]
89 struct Impl {
90 cb: Box<dyn Fn($($arg_type),*) -> $ret_type>,
91 }
92
93 impl $name for Impl {
94 unsafe fn invoke(&self, $($arg : $arg_type),*) -> $ret_type {
95 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
96 (self.cb)($($arg),*)
97 }));
98 match r {
99 Ok(r) => r,
100 Err(_) => {
101 eprintln!("webview2: panic in callback function. Aborting because it's UB to unwind across FFI boundaries.");
102 std::process::abort()
103 }
104 }
105 }
106 }
107
108 impl Impl {
109 pub fn new() -> Box<Self> {
111 unreachable!()
112 }
113 pub fn new_ptr(cb: impl Fn($($arg_type),*) -> $ret_type + 'static) -> com::ComPtr<dyn $name> {
116 let e = Self::allocate(Box::new(cb));
117 unsafe {
118 use com::interfaces::IUnknown;
119 e.add_ref();
120 com::ComPtr::<dyn $name>::new(Box::into_raw(e) as _)
121 }
122 }
123 }
124
125 Impl::new_ptr(move |$($arg : $arg_type),*| -> $ret_type { $($body)* })
126 }}
127}
128
129unsafe fn add_ref_to_rc<T: ComInterface + ?Sized>(
131 ptr: *mut *mut <T as ComInterface>::VTable,
132) -> ComRc<T> {
133 let ptr = ComPtr::new(ptr);
134 ptr.add_ref();
135 ptr.upgrade()
136}
137
138include!("interfaces.rs");
139
140mod environment_options {
143 use super::*;
144
145 #[com::co_class(implements(ICoreWebView2EnvironmentOptions))]
146 pub struct EnvironmentOptionsImpl {
147 additional_browser_arguments: RefCell<Option<WideCString>>,
148 language: RefCell<Option<WideCString>>,
149 target_compatible_browser_version: RefCell<Option<WideCString>>,
150 allow_single_sign_on_using_osprimary_account: Cell<bool>,
151 }
152
153 impl EnvironmentOptionsImpl {
154 fn new() -> Box<Self> {
155 unreachable!()
156 }
157
158 pub fn from_builder(
159 builder: &EnvironmentBuilder,
160 ) -> Result<*mut *mut ICoreWebView2EnvironmentOptionsVTable> {
161 let additional_browser_arguments = if let Some(v) = builder.additional_browser_arguments
162 {
163 Some(WideCString::from_str(v)?)
164 } else {
165 None
166 };
167 let language = if let Some(v) = builder.language {
168 Some(WideCString::from_str(v)?)
169 } else {
170 None
171 };
172 let version = builder
176 .target_compatible_browser_version
177 .unwrap_or(DEFAULT_TARGET_COMPATIBLE_BROWSER_VERSION);
178 let version = Some(WideCString::from_str(version)?);
179
180 let allow_single_sign_on_using_osprimary_account =
181 builder.allow_single_sign_on_using_osprimary_account;
182
183 let instance = Self::allocate(
184 additional_browser_arguments.into(),
185 language.into(),
186 version.into(),
187 allow_single_sign_on_using_osprimary_account.into(),
188 );
189 unsafe {
190 instance.add_ref();
191 }
192 Ok(Box::into_raw(instance) as _)
193 }
194 }
195
196 fn clone_wide_cstr_with_co_task_mem_alloc(s: &WideCStr) -> LPWSTR {
197 let len = s.len() + 1;
198 unsafe {
199 let s1 = CoTaskMemAlloc(len * 2) as *mut u16;
200 assert!(!s1.is_null());
201 ptr::copy_nonoverlapping(s.as_ptr(), s1, len);
202 s1
203 }
204 }
205
206 impl ICoreWebView2EnvironmentOptions for EnvironmentOptionsImpl {
207 unsafe fn get_additional_browser_arguments(
208 &self,
209 value: *mut LPWSTR,
210 ) -> HRESULT {
211 if let Some(v) = self.additional_browser_arguments.borrow().as_ref() {
212 value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
213 } else {
214 value.write(ptr::null_mut());
215 }
216 S_OK
217 }
218
219 unsafe fn put_additional_browser_arguments(&self, value: LPCWSTR) -> HRESULT {
220 *self.additional_browser_arguments.borrow_mut() =
221 Some(WideCString::from_ptr_str(value));
222 S_OK
223 }
224
225 unsafe fn get_language(&self, value: *mut LPWSTR) -> HRESULT {
226 if let Some(v) = self.language.borrow().as_ref() {
227 value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
228 } else {
229 value.write(ptr::null_mut());
230 }
231 S_OK
232 }
233
234 unsafe fn put_language(&self, value: LPCWSTR) -> HRESULT {
235 *self.language.borrow_mut() = Some(WideCString::from_ptr_str(value));
236 S_OK
237 }
238
239 unsafe fn get_target_compatible_browser_version(
240 &self,
241 value: *mut LPWSTR,
242 ) -> HRESULT {
243 if let Some(v) = self.target_compatible_browser_version.borrow().as_ref() {
244 value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
245 } else {
246 value.write(ptr::null_mut());
247 }
248 S_OK
249 }
250
251 unsafe fn put_target_compatible_browser_version(
252 &self,
253 value: LPCWSTR,
254 ) -> HRESULT {
255 *self.target_compatible_browser_version.borrow_mut() =
256 Some(WideCString::from_ptr_str(value));
257 S_OK
258 }
259
260 unsafe fn get_allow_single_sign_on_using_osprimary_account(&self, value: *mut i32) -> i32 {
261 value.write(if self.allow_single_sign_on_using_osprimary_account.get() {
262 1
263 } else {
264 0
265 });
266 S_OK
267 }
268
269 unsafe fn put_allow_single_sign_on_using_osprimary_account(&self, value: i32) -> i32 {
270 self.allow_single_sign_on_using_osprimary_account
271 .set(value != 0);
272 S_OK
273 }
274 }
275}
276
277pub fn get_available_browser_version_string(
278 browser_executable_folder: Option<&Path>,
279) -> Result<String> {
280 let browser_executable_folder = if let Some(p) = browser_executable_folder {
281 Some(WideCString::from_os_str(p)?)
282 } else {
283 None
284 };
285
286 let mut result = MaybeUninit::<LPWSTR>::uninit();
287
288 check_hresult(unsafe {
289 GetAvailableCoreWebView2BrowserVersionString(
290 browser_executable_folder
291 .as_ref()
292 .map_or(ptr::null(), |x| x.as_ptr()),
293 result.as_mut_ptr(),
294 )
295 })?;
296 let result = unsafe { result.assume_init() };
297 let result1 = unsafe { WideCStr::from_ptr_str(result) }
298 .to_string()
299 .map_err(|_| Error::new(E_FAIL));
300 unsafe { CoTaskMemFree(result as _) };
301 result1
302}
303
304pub fn compare_browser_versions(version1: &str, version2: &str) -> Result<std::cmp::Ordering> {
305 let version1 = WideCString::from_str(version1)?;
306 let version2 = WideCString::from_str(version2)?;
307 let mut result = MaybeUninit::<i32>::uninit();
308
309 check_hresult(unsafe {
310 CompareBrowserVersions(version1.as_ptr(), version2.as_ptr(), result.as_mut_ptr())
311 })?;
312 let result = unsafe { result.assume_init() };
313
314 Ok(result.cmp(&0))
315}
316
317#[derive(Default)]
322pub struct EnvironmentBuilder<'a> {
323 browser_executable_folder: Option<&'a Path>,
324 user_data_folder: Option<&'a Path>,
325 additional_browser_arguments: Option<&'a str>,
326 language: Option<&'a str>,
327 target_compatible_browser_version: Option<&'a str>,
328 allow_single_sign_on_using_osprimary_account: bool,
329}
330
331impl<'a> EnvironmentBuilder<'a> {
332 #[doc(hidden)]
334 #[inline]
335 pub fn new() -> Self {
336 Self::default()
337 }
338
339 #[inline]
340 pub fn with_browser_executable_folder(self, browser_executable_folder: &'a Path) -> Self {
341 Self {
342 browser_executable_folder: Some(browser_executable_folder),
343 ..self
344 }
345 }
346
347 #[inline]
348 pub fn with_user_data_folder(self, user_data_folder: &'a Path) -> Self {
349 Self {
350 user_data_folder: Some(user_data_folder),
351 ..self
352 }
353 }
354
355 #[inline]
356 pub fn with_additional_browser_arguments(self, additional_browser_arguments: &'a str) -> Self {
357 Self {
358 additional_browser_arguments: Some(additional_browser_arguments),
359 ..self
360 }
361 }
362
363 #[inline]
364 pub fn with_language(mut self, language: &'a str) -> Self {
365 self.language = Some(language);
366 self
367 }
368
369 #[inline]
370 pub fn with_target_compatible_browser_version(mut self, version: &'a str) -> Self {
371 self.target_compatible_browser_version = Some(version);
372 self
373 }
374
375 #[inline]
376 pub fn with_allow_single_sign_on_using_osprimary_account(mut self, allow: bool) -> Self {
377 self.allow_single_sign_on_using_osprimary_account = allow;
378 self
379 }
380
381 #[inline]
384 pub fn get_available_browser_version_string(&self) -> Result<String> {
385 get_available_browser_version_string(self.browser_executable_folder)
386 }
387
388 #[deprecated = "use webview2::compare_browser_versions instead"]
389 #[inline]
390 pub fn compare_browser_versions(
391 &self,
392 version1: &str,
393 version2: &str,
394 ) -> Result<std::cmp::Ordering> {
395 compare_browser_versions(version1, version2)
396 }
397
398 #[inline]
399 pub fn build(
400 &self,
401 completed: impl FnOnce(Result<Environment>) -> Result<()> + 'static,
402 ) -> Result<()> {
403 let browser_executable_folder = if let Some(p) = self.browser_executable_folder {
404 Some(WideCString::from_os_str(p)?)
405 } else {
406 None
407 };
408 let user_data_folder = if let Some(p) = self.user_data_folder {
409 Some(WideCString::from_os_str(p)?)
410 } else {
411 None
412 };
413 let options = environment_options::EnvironmentOptionsImpl::from_builder(&self)?;
414
415 let completed = Cell::new(Some(completed));
416 let completed = callback!(
417 ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
418 move |result: HRESULT,
419 created_environment: *mut *mut ICoreWebView2EnvironmentVTable|
420 -> HRESULT {
421 let result = check_hresult(result).map(move |_| Environment {
422 inner: unsafe { add_ref_to_rc(created_environment) },
423 });
424 if let Some(completed) = completed.take() {
425 to_hresult(completed(result))
426 } else {
427 S_OK
428 }
429 }
430 );
431
432 check_hresult(unsafe {
433 CreateCoreWebView2EnvironmentWithOptions(
434 browser_executable_folder
435 .as_ref()
436 .map(|p| p.as_ptr())
437 .unwrap_or(ptr::null()),
438 user_data_folder
439 .as_ref()
440 .map(|p| p.as_ptr())
441 .unwrap_or(ptr::null()),
442 options,
443 completed.as_raw(),
444 )
445 })
446 }
447}
448
449macro_rules! get {
450 ($get_method:ident, $T: ident) => {
451 pub fn $get_method(&self) -> Result<$T> {
452 let mut value = MaybeUninit::<$T>::uninit();
453 check_hresult(unsafe { self.inner.$get_method(value.as_mut_ptr()) })?;
454 Ok(unsafe { value.assume_init() })
455 }
456 };
457}
458
459macro_rules! put {
460 ($put_method:ident, $arg_name:ident : $T:ident) => {
461 pub fn $put_method(&self, $arg_name: $T) -> Result<()> {
462 check_hresult(unsafe { self.inner.$put_method($arg_name) })
463 }
464 };
465}
466
467macro_rules! get_interface {
468 ($get_method:ident, $T: ident) => {
469 pub fn $get_method(&self) -> Result<$T> {
470 let mut ppv = ptr::null_mut();
471 check_hresult(unsafe { self.inner.$get_method(&mut ppv) })?;
472 if ppv.is_null() {
473 Err(Error::new(E_FAIL))
474 } else {
475 Ok(unsafe {
476 $T {
477 inner: add_ref_to_rc(ppv),
478 }
479 })
480 }
481 }
482 };
483}
484
485macro_rules! put_interface {
486 ($put_method:ident, $T: ident) => {
487 pub fn $put_method(&self, i: $T) -> Result<()> {
488 check_hresult(unsafe {
489 self.inner.$put_method(ComPtr::from(i.inner).as_raw())
491 })
492 }
493 };
494}
495
496macro_rules! get_bool {
497 ($get_method:ident) => {
498 pub fn $get_method(&self) -> Result<bool> {
499 let mut enabled = MaybeUninit::<BOOL>::uninit();
500 check_hresult(unsafe { self.inner.$get_method(enabled.as_mut_ptr()) })?;
501 Ok(unsafe { enabled.assume_init() } != 0)
502 }
503 };
504}
505
506macro_rules! put_bool {
507 ($put_method:ident) => {
508 pub fn $put_method(&self, enabled: bool) -> Result<()> {
509 let enabled = if enabled { 1 } else { 0 };
510 check_hresult(unsafe { self.inner.$put_method(enabled) })
511 }
512 };
513}
514
515macro_rules! get_string {
516 ($get_string_method:ident) => {
517 pub fn $get_string_method(&self) -> Result<String> {
518 let mut result: LPWSTR = ptr::null_mut();
519 check_hresult(unsafe { self.inner.$get_string_method(&mut result) })?;
520 let result1 = unsafe { WideCStr::from_ptr_str(result) };
521 let result1 = result1.to_string().map_err(|_| Error { hresult: E_FAIL });
522 unsafe {
523 CoTaskMemFree(result as _);
524 }
525 result1
526 }
527 };
528}
529
530macro_rules! put_string {
531 ($put_string_method:ident) => {
532 pub fn $put_string_method(&self, message_string: &str) -> Result<()> {
533 let message = WideCString::from_str(message_string)?;
534 check_hresult(unsafe { self.inner.$put_string_method(message.as_ptr()) })
535 }
536 };
537}
538
539macro_rules! call {
540 ($method:ident) => {
541 pub fn $method(&self) -> Result<()> {
542 check_hresult(unsafe { self.inner.$method() })
543 }
544 };
545}
546
547macro_rules! add_event_handler_controller {
548 ($method:ident, $arg_type:ident) => {
549 pub fn $method(
550 &self,
551 event_handler: impl Fn(Controller) -> Result<()> + 'static,
552 ) -> Result<EventRegistrationToken> {
553 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
554
555 let event_handler = callback!(
556 $arg_type,
557 move |sender: *mut *mut ICoreWebView2ControllerVTable,
558 _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
559 -> HRESULT {
560 let sender = Controller {
561 inner: unsafe { add_ref_to_rc(sender) },
562 };
563 to_hresult(event_handler(sender))
564 }
565 );
566
567 check_hresult(unsafe {
568 self.inner
569 .$method(event_handler.as_raw(), token.as_mut_ptr())
570 })?;
571 Ok(unsafe { token.assume_init() })
572 }
573 };
574}
575
576macro_rules! add_event_handler_view {
577 ($method:ident, $arg_type:ident) => {
578 pub fn $method(
579 &self,
580 event_handler: impl Fn(WebView) -> Result<()> + 'static,
581 ) -> Result<EventRegistrationToken> {
582 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
583
584 let event_handler = callback!(
585 $arg_type,
586 move |sender: *mut *mut ICoreWebView2VTable,
587 _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
588 -> HRESULT {
589 let sender = WebView {
590 inner: unsafe { add_ref_to_rc(sender) },
591 };
592 to_hresult(event_handler(sender))
593 }
594 );
595
596 check_hresult(unsafe {
597 self.inner
598 .$method(event_handler.as_raw(), token.as_mut_ptr())
599 })?;
600 Ok(unsafe { token.assume_init() })
601 }
602 };
603}
604
605macro_rules! add_event_handler {
606 ($method:ident, $arg_type:ident, $arg_args:ident, $arg_args_type:ident) => {
607 pub fn $method(
608 &self,
609 handler: impl Fn(WebView, $arg_args) -> Result<()> + 'static,
610 ) -> Result<EventRegistrationToken> {
611 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
612
613 let handler = callback!($arg_type, move |sender: *mut *mut ICoreWebView2VTable,
614 args: *mut *mut $arg_args_type|
615 -> HRESULT {
616 let sender = WebView {
617 inner: unsafe { add_ref_to_rc(sender) },
618 };
619 let args = $arg_args {
620 inner: unsafe { add_ref_to_rc(args) },
621 };
622 to_hresult(handler(sender, args))
623 });
624
625 check_hresult(unsafe { self.inner.$method(handler.as_raw(), token.as_mut_ptr()) })?;
626 Ok(unsafe { token.assume_init() })
627 }
628 };
629}
630
631macro_rules! remove_event_handler {
632 ($method:ident) => {
633 pub fn $method(&self, token: EventRegistrationToken) -> Result<()> {
634 check_hresult(unsafe { self.inner.$method(token) })
635 }
636 };
637}
638
639impl Environment {
640 pub fn builder<'a>() -> EnvironmentBuilder<'a> {
641 EnvironmentBuilder::new()
642 }
643
644 pub fn create_controller(
645 &self,
646 parent_window: HWND,
647 completed: impl FnOnce(Result<Controller>) -> Result<()> + 'static,
648 ) -> Result<()> {
649 let completed = Cell::new(Some(completed));
650 let completed = callback!(
651 ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
652 move |result: HRESULT,
653 created_host: *mut *mut ICoreWebView2ControllerVTable|
654 -> HRESULT {
655 let result = check_hresult(result).map(|_| Controller {
656 inner: unsafe { add_ref_to_rc(created_host) },
657 });
658 if let Some(completed) = completed.take() {
659 to_hresult(completed(result))
660 } else {
661 S_OK
662 }
663 }
664 );
665 check_hresult(unsafe {
666 self.inner
667 .create_core_web_view2_controller(parent_window, completed.as_raw())
668 })
669 }
670 pub fn create_web_resource_response(
671 &self,
672 content: Stream,
673 status_code: i32,
674 reason_phrase: &str,
675 headers: &str,
676 ) -> Result<WebResourceResponse> {
677 let content = ComPtr::from(content.into_inner());
678 let reason_phrase = WideCString::from_str(reason_phrase)?;
679 let headers = WideCString::from_str(headers)?;
680 let mut response =
681 MaybeUninit::<*mut *mut ICoreWebView2WebResourceResponseVTable>::uninit();
682 check_hresult(unsafe {
683 self.inner.create_web_resource_response(
684 content.as_raw(),
685 status_code,
686 reason_phrase.as_ptr(),
687 headers.as_ptr(),
688 response.as_mut_ptr(),
689 )
690 })?;
691 Ok(WebResourceResponse::from(unsafe {
692 ComRc::from_raw(response.assume_init())
693 }))
694 }
695 get_string!(get_browser_version_string);
696 pub fn add_new_browser_version_available(
697 &self,
698 event_handler: impl Fn(Environment) -> Result<()> + 'static,
699 ) -> Result<EventRegistrationToken> {
700 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
701
702 let event_handler = callback!(
703 ICoreWebView2NewBrowserVersionAvailableEventHandler,
704 move |sender: *mut *mut ICoreWebView2EnvironmentVTable,
705 _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
706 -> HRESULT {
707 let sender = Environment {
708 inner: unsafe { add_ref_to_rc(sender) },
709 };
710 to_hresult(event_handler(sender))
711 }
712 );
713
714 check_hresult(unsafe {
715 self.inner
716 .add_new_browser_version_available(event_handler.as_raw(), token.as_mut_ptr())
717 })?;
718 Ok(unsafe { token.assume_init() })
719 }
720 remove_event_handler!(remove_new_browser_version_available);
721}
722
723impl Controller {
724 get_bool!(get_is_visible);
725 put_bool!(put_is_visible);
726 get!(get_bounds, RECT);
727 put!(put_bounds, bounds: RECT);
728 get!(get_zoom_factor, f64);
729 put!(put_zoom_factor, zoom_factor: f64);
730 add_event_handler_controller!(
731 add_zoom_factor_changed,
732 ICoreWebView2ZoomFactorChangedEventHandler
733 );
734 remove_event_handler!(remove_zoom_factor_changed);
735 pub fn set_bounds_and_zoom_factor(&self, bounds: RECT, zoom_factor: f64) -> Result<()> {
736 check_hresult(unsafe { self.inner.set_bounds_and_zoom_factor(bounds, zoom_factor) })
737 }
738 pub fn move_focus(&self, reason: MoveFocusReason) -> Result<()> {
739 check_hresult(unsafe { self.inner.move_focus(reason) })
740 }
741 pub fn add_move_focus_requested(
742 &self,
743 handler: impl Fn(Controller, MoveFocusRequestedEventArgs) -> Result<()> + 'static,
744 ) -> Result<EventRegistrationToken> {
745 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
746
747 let handler = callback!(
748 ICoreWebView2MoveFocusRequestedEventHandler,
749 move |sender: *mut *mut ICoreWebView2ControllerVTable,
750 args: *mut *mut ICoreWebView2MoveFocusRequestedEventArgsVTable|
751 -> HRESULT {
752 let sender = Controller {
753 inner: unsafe { add_ref_to_rc(sender) },
754 };
755 let args = MoveFocusRequestedEventArgs {
756 inner: unsafe { add_ref_to_rc(args) },
757 };
758 to_hresult(handler(sender, args))
759 }
760 );
761
762 check_hresult(unsafe {
763 self.inner
764 .add_move_focus_requested(handler.as_raw(), token.as_mut_ptr())
765 })?;
766 Ok(unsafe { token.assume_init() })
767 }
768 remove_event_handler!(remove_move_focus_requested);
769 add_event_handler_controller!(add_got_focus, ICoreWebView2FocusChangedEventHandler);
770 remove_event_handler!(remove_got_focus);
771 add_event_handler_controller!(add_lost_focus, ICoreWebView2FocusChangedEventHandler);
772 remove_event_handler!(remove_lost_focus);
773 pub fn add_accelerator_key_pressed(
774 &self,
775 handler: impl Fn(Controller, AcceleratorKeyPressedEventArgs) -> Result<()> + 'static,
776 ) -> Result<EventRegistrationToken> {
777 let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
778
779 let handler = callback!(
780 ICoreWebView2AcceleratorKeyPressedEventHandler,
781 move |sender: *mut *mut ICoreWebView2ControllerVTable,
782 args: *mut *mut ICoreWebView2AcceleratorKeyPressedEventArgsVTable|
783 -> HRESULT {
784 let sender = Controller {
785 inner: unsafe { add_ref_to_rc(sender) },
786 };
787 let args = AcceleratorKeyPressedEventArgs {
788 inner: unsafe { add_ref_to_rc(args) },
789 };
790 to_hresult(handler(sender, args))
791 }
792 );
793
794 check_hresult(unsafe {
795 self.inner
796 .add_accelerator_key_pressed(handler.as_raw(), token.as_mut_ptr())
797 })?;
798 Ok(unsafe { token.assume_init() })
799 }
800 remove_event_handler!(remove_accelerator_key_pressed);
801 get!(get_parent_window, HWND);
802 put!(put_parent_window, top_level_window: HWND);
803 call!(notify_parent_window_position_changed);
804 call!(close);
805 pub fn get_webview(&self) -> Result<WebView> {
806 let mut ppv: *mut *mut ICoreWebView2VTable = ptr::null_mut();
807 check_hresult(unsafe { self.inner.get_core_web_view2(&mut ppv) })?;
808 Ok(WebView {
809 inner: unsafe { add_ref_to_rc(ppv) },
810 })
811 }
812 pub fn get_controller2(&self) -> Result<Controller2> {
813 let inner = self
814 .inner
815 .get_interface::<dyn ICoreWebView2Controller2>()
816 .ok_or_else(|| Error::new(E_NOINTERFACE))?;
817 Ok(Controller2 { inner })
818 }
819}
820
821impl Controller2 {
822 get!(get_default_background_color, Color);
823 put!(put_default_background_color, color: Color);
824}
825
826impl WebView {
827 pub fn get_settings(&self) -> Result<Settings> {
828 let mut ppv: *mut *mut ICoreWebView2SettingsVTable = ptr::null_mut();
829 check_hresult(unsafe { self.inner.get_settings(&mut ppv) })?;
830 Ok(Settings {
831 inner: unsafe { add_ref_to_rc(ppv) },
832 })
833 }
834 get_string!(get_source);
835 put_string!(navigate);
836 put_string!(navigate_to_string);
837 add_event_handler!(
838 add_navigation_starting,
839 ICoreWebView2NavigationStartingEventHandler,
840 NavigationStartingEventArgs,
841 ICoreWebView2NavigationStartingEventArgsVTable
842 );
843 remove_event_handler!(remove_navigation_starting);
844 add_event_handler!(
845 add_content_loading,
846 ICoreWebView2ContentLoadingEventHandler,
847 ContentLoadingEventArgs,
848 ICoreWebView2ContentLoadingEventArgsVTable
849 );
850 remove_event_handler!(remove_content_loading);
851 add_event_handler!(
852 add_source_changed,
853 ICoreWebView2SourceChangedEventHandler,
854 SourceChangedEventArgs,
855 ICoreWebView2SourceChangedEventArgsVTable
856 );
857 remove_event_handler!(remove_source_changed);
858 add_event_handler_view!(add_history_changed, ICoreWebView2HistoryChangedEventHandler);
859 remove_event_handler!(remove_history_changed);
860 add_event_handler!(
861 add_navigation_completed,
862 ICoreWebView2NavigationCompletedEventHandler,
863 NavigationCompletedEventArgs,
864 ICoreWebView2NavigationCompletedEventArgsVTable
865 );
866 remove_event_handler!(remove_navigation_completed);
867 add_event_handler!(
868 add_frame_navigation_starting,
869 ICoreWebView2NavigationStartingEventHandler,
870 NavigationStartingEventArgs,
871 ICoreWebView2NavigationStartingEventArgsVTable
872 );
873 remove_event_handler!(remove_frame_navigation_starting);
874 add_event_handler!(
875 add_script_dialog_opening,
876 ICoreWebView2ScriptDialogOpeningEventHandler,
877 ScriptDialogOpeningEventArgs,
878 ICoreWebView2ScriptDialogOpeningEventArgsVTable
879 );
880 remove_event_handler!(remove_script_dialog_opening);
881 add_event_handler!(
882 add_permission_requested,
883 ICoreWebView2PermissionRequestedEventHandler,
884 PermissionRequestedEventArgs,
885 ICoreWebView2PermissionRequestedEventArgsVTable
886 );
887 remove_event_handler!(remove_permission_requested);
888 add_event_handler!(
889 add_process_failed,
890 ICoreWebView2ProcessFailedEventHandler,
891 ProcessFailedEventArgs,
892 ICoreWebView2ProcessFailedEventArgsVTable
893 );
894 remove_event_handler!(remove_process_failed);
895 pub fn add_script_to_execute_on_document_created(
898 &self,
899 script: &str,
900 callback: impl FnOnce(String) -> Result<()> + 'static,
901 ) -> Result<()> {
902 let script = WideCString::from_str(script)?;
903 let callback = Cell::new(Some(callback));
904 let callback = callback!(
905 ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler,
906 move |error_code: HRESULT, id: LPCWSTR| -> HRESULT {
907 to_hresult(check_hresult(error_code).and_then(|_| {
908 let id = unsafe { WideCStr::from_ptr_str(id) }
909 .to_string()
910 .map_err(|_| Error::new(E_FAIL))?;
911 if let Some(callback) = callback.take() {
912 callback(id)
913 } else {
914 Ok(())
915 }
916 }))
917 }
918 );
919 check_hresult(unsafe {
920 self.inner
921 .add_script_to_execute_on_document_created(script.as_ptr(), callback.as_raw())
922 })
923 }
924 pub fn remove_script_to_execute_on_document_created(&self, id: &str) -> Result<()> {
925 let id = WideCString::from_str(id)?;
926 check_hresult(unsafe {
927 self.inner
928 .remove_script_to_execute_on_document_created(id.as_ptr())
929 })
930 }
931 pub fn execute_script(
932 &self,
933 script: &str,
934 callback: impl FnOnce(String) -> Result<()> + 'static,
935 ) -> Result<()> {
936 let script = WideCString::from_str(script)?;
937 let callback = Cell::new(Some(callback));
938 let callback = callback!(
939 ICoreWebView2ExecuteScriptCompletedHandler,
940 move |error_code: HRESULT, result_object_as_json: LPCWSTR| -> HRESULT {
941 to_hresult(check_hresult(error_code).and_then(|_| {
942 let result_object_as_json_string =
943 unsafe { WideCStr::from_ptr_str(result_object_as_json) }
944 .to_string()
945 .map_err(|_| Error::new(E_FAIL))?;
946 if let Some(callback) = callback.take() {
947 callback(result_object_as_json_string)
948 } else {
949 Ok(())
950 }
951 }))
952 }
953 );
954 check_hresult(unsafe {
955 self.inner
956 .execute_script(script.as_ptr(), callback.as_raw())
957 })
958 }
959 add_event_handler_view!(
960 add_document_title_changed,
961 ICoreWebView2DocumentTitleChangedEventHandler
962 );
963 remove_event_handler!(remove_document_title_changed);
964 pub fn capture_preview(
965 &self,
966 image_format: CapturePreviewImageFormat,
967 image_stream: Stream,
968 handler: impl FnOnce(Result<()>) -> Result<()> + 'static,
969 ) -> Result<()> {
970 let handler = Cell::new(Some(handler));
971 let handler = callback!(
972 ICoreWebView2CapturePreviewCompletedHandler,
973 move |result: HRESULT| -> HRESULT {
974 if let Some(handler) = handler.take() {
975 to_hresult(handler(check_hresult(result)))
976 } else {
977 S_OK
978 }
979 }
980 );
981 let image_stream = ComPtr::from(image_stream.inner);
982
983 check_hresult(unsafe {
984 self.inner
985 .capture_preview(image_format, image_stream.as_raw(), handler.as_raw())
986 })
987 }
988 call!(reload);
989 put_string!(post_web_message_as_json);
990 put_string!(post_web_message_as_string);
991 add_event_handler!(
992 add_web_message_received,
993 ICoreWebView2WebMessageReceivedEventHandler,
994 WebMessageReceivedEventArgs,
995 ICoreWebView2WebMessageReceivedEventArgsVTable
996 );
997 remove_event_handler!(remove_web_message_received);
998 get!(get_browser_process_id, u32);
1000 get_bool!(get_can_go_back);
1001 get_bool!(get_can_go_forward);
1002 call!(go_back);
1003 call!(go_forward);
1004 call!(stop);
1006 add_event_handler!(
1007 add_new_window_requested,
1008 ICoreWebView2NewWindowRequestedEventHandler,
1009 NewWindowRequestedEventArgs,
1010 ICoreWebView2NewWindowRequestedEventArgsVTable
1011 );
1012 remove_event_handler!(remove_new_window_requested);
1013 get_string!(get_document_title);
1014 call!(open_dev_tools_window);
1017 add_event_handler_view!(
1018 add_contains_full_screen_element_changed,
1019 ICoreWebView2ContainsFullScreenElementChangedEventHandler
1020 );
1021 remove_event_handler!(remove_contains_full_screen_element_changed);
1022 get_bool!(get_contains_full_screen_element);
1023 add_event_handler!(
1024 add_web_resource_requested,
1025 ICoreWebView2WebResourceRequestedEventHandler,
1026 WebResourceRequestedEventArgs,
1027 ICoreWebView2WebResourceRequestedEventArgsVTable
1028 );
1029 remove_event_handler!(remove_web_resource_requested);
1030 pub fn add_web_resource_requested_filter(
1031 &self,
1032 uri: &str,
1033 resource_context: WebResourceContext,
1034 ) -> Result<()> {
1035 let uri = WideCString::from_str(uri)?;
1036 check_hresult(unsafe {
1037 self.inner
1038 .add_web_resource_requested_filter(uri.as_ptr(), resource_context)
1039 })
1040 }
1041 pub fn remove_web_resource_requested_filter(
1042 &self,
1043 uri: &str,
1044 resource_context: WebResourceContext,
1045 ) -> Result<()> {
1046 let uri = WideCString::from_str(uri)?;
1047 check_hresult(unsafe {
1048 self.inner
1049 .remove_web_resource_requested_filter(uri.as_ptr(), resource_context)
1050 })
1051 }
1052 add_event_handler_view!(
1053 add_window_close_requested,
1054 ICoreWebView2WindowCloseRequestedEventHandler
1055 );
1056 remove_event_handler!(remove_window_close_requested);
1057}
1058
1059impl Settings {
1060 get_bool!(get_is_script_enabled);
1061 put_bool!(put_is_script_enabled);
1062
1063 get_bool!(get_is_web_message_enabled);
1064 put_bool!(put_is_web_message_enabled);
1065
1066 get_bool!(get_are_default_script_dialogs_enabled);
1067 put_bool!(put_are_default_script_dialogs_enabled);
1068
1069 get_bool!(get_is_status_bar_enabled);
1070 put_bool!(put_is_status_bar_enabled);
1071
1072 get_bool!(get_are_dev_tools_enabled);
1073 put_bool!(put_are_dev_tools_enabled);
1074
1075 get_bool!(get_are_default_context_menus_enabled);
1076 put_bool!(put_are_default_context_menus_enabled);
1077
1078 get_bool!(get_are_host_objects_allowed);
1079 put_bool!(put_are_host_objects_allowed);
1080
1081 get_bool!(get_is_zoom_control_enabled);
1082 put_bool!(put_is_zoom_control_enabled);
1083
1084 get_bool!(get_is_built_in_error_page_enabled);
1085 put_bool!(put_is_built_in_error_page_enabled);
1086
1087 pub fn get_settings2(&self) -> Result<Settings2> {
1088 let inner = self
1089 .inner
1090 .get_interface::<dyn ICoreWebView2Settings2>()
1091 .ok_or_else(|| Error::new(E_NOINTERFACE))?;
1092 Ok(Settings2 { inner })
1093 }
1094}
1095
1096impl Settings2 {
1097 get_string!(get_user_agent);
1098 put_string!(put_user_agent);
1099}
1100
1101impl ContentLoadingEventArgs {
1102 get_bool!(get_is_error_page);
1103 get!(get_navigation_id, u64);
1104}
1105
1106impl WebMessageReceivedEventArgs {
1107 get_string!(get_source);
1108 get_string!(try_get_web_message_as_string);
1109 get_string!(get_web_message_as_json);
1110}
1111
1112impl HttpHeadersCollectionIterator {
1113 pub fn get_current_header(&self) -> Result<(String, String)> {
1114 let mut name = MaybeUninit::<LPWSTR>::uninit();
1115 let mut value = MaybeUninit::<LPWSTR>::uninit();
1116 unsafe {
1117 check_hresult(
1118 self.inner
1119 .get_current_header(name.as_mut_ptr(), value.as_mut_ptr()),
1120 )?;
1121 let name = name.assume_init();
1122 let value = value.assume_init();
1123 let name1 = WideCStr::from_ptr_str(name)
1124 .to_string()
1125 .map_err(|_| Error::new(E_FAIL));
1126 let value1 = WideCStr::from_ptr_str(value)
1127 .to_string()
1128 .map_err(|_| Error::new(E_FAIL));
1129
1130 CoTaskMemFree(name as _);
1131 CoTaskMemFree(value as _);
1132
1133 Ok((name1?, value1?))
1134 }
1135 }
1136 get_bool!(get_has_current_header);
1137 get_bool!(move_next);
1138}
1139
1140impl Iterator for HttpHeadersCollectionIterator {
1141 type Item = (String, String);
1142
1143 fn next(&mut self) -> Option<(String, String)> {
1144 if self.get_has_current_header() != Ok(true) {
1145 return None;
1146 }
1147 let v = self.get_current_header().ok();
1148 let _ = self.move_next();
1149 v
1150 }
1151}
1152
1153impl HttpRequestHeaders {
1154 pub fn get_header(&self, name: &str) -> Result<String> {
1155 let name = WideCString::from_str(name)?;
1156 let mut value = MaybeUninit::<LPWSTR>::uninit();
1157 unsafe {
1158 check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1159 let value = value.assume_init();
1160 let value1 = WideCStr::from_ptr_str(value)
1161 .to_string()
1162 .map_err(|_| Error::new(E_FAIL));
1163
1164 CoTaskMemFree(value as _);
1165
1166 value1
1167 }
1168 }
1169 pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1170 let name = WideCString::from_str(name)?;
1171 let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1172 ptr::null_mut();
1173 check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1174 Ok(HttpHeadersCollectionIterator {
1175 inner: unsafe { add_ref_to_rc(iterator) },
1176 })
1177 }
1178 pub fn contains(&self, name: &str) -> Result<bool> {
1179 let name = WideCString::from_str(name)?;
1180 let mut result = MaybeUninit::<BOOL>::uninit();
1181 check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1182 Ok(unsafe { result.assume_init() } != 0)
1183 }
1184 pub fn set_header(&self, name: &str, value: &str) -> Result<()> {
1185 let name = WideCString::from_str(name)?;
1186 let value = WideCString::from_str(value)?;
1187 check_hresult(unsafe { self.inner.set_header(name.as_ptr(), value.as_ptr()) })
1188 }
1189 put_string!(remove_header);
1190 get_interface!(get_iterator, HttpHeadersCollectionIterator);
1191}
1192
1193impl HttpResponseHeaders {
1194 pub fn get_header(&self, name: &str) -> Result<String> {
1195 let name = WideCString::from_str(name)?;
1196 let mut value = MaybeUninit::<LPWSTR>::uninit();
1197 unsafe {
1198 check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1199 let value = value.assume_init();
1200 let value1 = WideCStr::from_ptr_str(value)
1201 .to_string()
1202 .map_err(|_| Error::new(E_FAIL));
1203
1204 CoTaskMemFree(value as _);
1205
1206 value1
1207 }
1208 }
1209 pub fn contains(&self, name: &str) -> Result<bool> {
1210 let name = WideCString::from_str(name)?;
1211 let mut result = MaybeUninit::<BOOL>::uninit();
1212 check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1213 Ok(unsafe { result.assume_init() } != 0)
1214 }
1215 pub fn append_header(&self, name: &str, value: &str) -> Result<()> {
1216 let name = WideCString::from_str(name)?;
1217 let value = WideCString::from_str(value)?;
1218 check_hresult(unsafe { self.inner.append_header(name.as_ptr(), value.as_ptr()) })
1219 }
1220 pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1221 let name = WideCString::from_str(name)?;
1222 let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1223 ptr::null_mut();
1224 check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1225 Ok(HttpHeadersCollectionIterator {
1226 inner: unsafe { add_ref_to_rc(iterator) },
1227 })
1228 }
1229 get_interface!(get_iterator, HttpHeadersCollectionIterator);
1230}
1231
1232impl Deferral {
1233 call!(complete);
1234}
1235
1236impl WebResourceRequest {
1237 get_string!(get_uri);
1238 put_string!(put_uri);
1239 get_string!(get_method);
1240 put_string!(put_method);
1241 get_interface!(get_content, Stream);
1242 put_interface!(put_content, Stream);
1243 get_interface!(get_headers, HttpRequestHeaders);
1244}
1245
1246impl WebResourceResponse {
1247 get_interface!(get_content, Stream);
1248 put_interface!(put_content, Stream);
1249 get_interface!(get_headers, HttpResponseHeaders);
1250 get!(get_status_code, i32);
1251 put!(put_status_code, status_code: i32);
1252 get_string!(get_reason_phrase);
1253 put_string!(put_reason_phrase);
1254}
1255
1256impl WebResourceRequestedEventArgs {
1257 get_interface!(get_request, WebResourceRequest);
1258 get_interface!(get_response, WebResourceResponse);
1259 put_interface!(put_response, WebResourceResponse);
1260 get_interface!(get_deferral, Deferral);
1261 get!(get_resource_context, WebResourceContext);
1262}
1263
1264impl NavigationCompletedEventArgs {
1265 get_bool!(get_is_success);
1266 get!(get_web_error_status, WebErrorStatus);
1267 get!(get_navigation_id, u64);
1268}
1269
1270impl NavigationStartingEventArgs {
1271 get_string!(get_uri);
1272 get_bool!(get_is_user_initiated);
1273 get_bool!(get_is_redirected);
1274 get_interface!(get_request_headers, HttpRequestHeaders);
1275 get_bool!(get_cancel);
1276 put_bool!(put_cancel);
1277 get!(get_navigation_id, u64);
1278}
1279
1280impl SourceChangedEventArgs {
1281 get_bool!(get_is_new_document);
1282}
1283
1284impl ScriptDialogOpeningEventArgs {
1285 get_string!(get_uri);
1286 get!(get_kind, ScriptDialogKind);
1287 get_string!(get_message);
1288 call!(accept);
1289 get_string!(get_default_text);
1290 get_string!(get_result_text);
1291 put_string!(put_result_text);
1292 get_interface!(get_deferral, Deferral);
1293}
1294
1295impl PermissionRequestedEventArgs {
1296 get_string!(get_uri);
1297 get!(get_permission_kind, PermissionKind);
1298 get_bool!(get_is_user_initiated);
1299 get!(get_state, PermissionState);
1300 put!(put_state, state: PermissionState);
1301 get_interface!(get_deferral, Deferral);
1302}
1303
1304impl ProcessFailedEventArgs {
1305 get!(get_process_failed_kind, ProcessFailedKind);
1306}
1307
1308impl NewWindowRequestedEventArgs {
1309 get_string!(get_uri);
1310 put_interface!(put_new_window, WebView);
1311 get_interface!(get_new_window, WebView);
1312 put_bool!(put_handled);
1313 get_bool!(get_handled);
1314 get_bool!(get_is_user_initiated);
1315 get_interface!(get_deferral, Deferral);
1316 get_interface!(get_window_features, WindowFeatures);
1317}
1318
1319impl WindowFeatures {
1320 get_bool!(get_has_position);
1321 get_bool!(get_has_size);
1322 get!(get_left, u32);
1323 get!(get_top, u32);
1324 get!(get_height, u32);
1325 get!(get_width, u32);
1326 get_bool!(get_should_display_menu_bar);
1327 get_bool!(get_should_display_status);
1328 get_bool!(get_should_display_toolbar);
1329 get_bool!(get_should_display_scroll_bars);
1330}
1331
1332impl MoveFocusRequestedEventArgs {
1333 get!(get_reason, MoveFocusReason);
1334 get_bool!(get_handled);
1335 put_bool!(put_handled);
1336}
1337
1338impl AcceleratorKeyPressedEventArgs {
1339 get!(get_key_event_kind, KeyEventKind);
1340 get!(get_virtual_key, u32);
1341 get!(get_key_event_lparam, i32);
1342 get!(get_physical_key_status, PhysicalKeyStatus);
1343 get_bool!(get_handled);
1344 put_bool!(put_handled);
1345}
1346
1347extern "stdcall" {
1349 fn SHCreateMemStream(p_init: *const u8, cb_init: UINT) -> *mut *mut IStreamVTable;
1350}
1351
1352impl Stream {
1353 pub fn from_bytes(buf: &[u8]) -> Self {
1355 let ppv = unsafe { SHCreateMemStream(buf.as_ptr(), buf.len() as _) };
1356 assert!(!ppv.is_null());
1357 Self {
1358 inner: unsafe { ComRc::from_raw(ppv) },
1360 }
1361 }
1362
1363 pub unsafe fn from_raw(ppv: *mut *mut IStreamVTable) -> Self {
1369 Self {
1370 inner: ComRc::from_raw(ppv),
1371 }
1372 }
1373}
1374
1375impl io::Read for Stream {
1376 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1377 let mut read_bytes = MaybeUninit::uninit();
1378 check_hresult(unsafe {
1379 self.inner.read(
1380 buf.as_mut_ptr() as *mut _,
1381 buf.len() as _,
1382 read_bytes.as_mut_ptr(),
1383 )
1384 })
1385 .map_err(|e| e.into_io_error())?;
1386 Ok(unsafe { read_bytes.assume_init() } as _)
1387 }
1388}
1389
1390impl io::Write for Stream {
1391 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1392 let mut written_bytes = MaybeUninit::uninit();
1393 check_hresult(unsafe {
1394 self.inner.write(
1395 buf.as_ptr() as *mut _,
1396 buf.len() as _,
1397 written_bytes.as_mut_ptr(),
1398 )
1399 })
1400 .map_err(|e| e.into_io_error())?;
1401 Ok(unsafe { written_bytes.assume_init() } as _)
1402 }
1403
1404 fn flush(&mut self) -> io::Result<()> {
1405 Ok(())
1406 }
1407}
1408
1409impl io::Seek for Stream {
1410 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
1411 use std::convert::TryInto;
1412
1413 let (origin, amount) = match pos {
1414 io::SeekFrom::Start(x) => (0, x.try_into().unwrap()),
1415 io::SeekFrom::Current(x) => (1, x),
1416 io::SeekFrom::End(x) => (2, x),
1417 };
1418
1419 let mut new_pos = MaybeUninit::<u64>::uninit();
1420
1421 check_hresult(unsafe {
1422 self.inner.seek(
1423 mem::transmute(amount),
1424 origin,
1425 new_pos.as_mut_ptr() as *mut _,
1426 )
1427 })
1428 .map_err(|e| e.into_io_error())?;
1429 Ok(unsafe { new_pos.assume_init() })
1430 }
1431}
1432
1433#[doc(inline)]
1434pub use webview2_sys::{
1435 CapturePreviewImageFormat, EventRegistrationToken, KeyEventKind, MoveFocusReason,
1436 PermissionKind, PermissionState, PhysicalKeyStatus, ProcessFailedKind, ScriptDialogKind,
1437 WebErrorStatus, WebResourceContext,
1438};
1439
1440#[derive(Eq, PartialEq)]
1444pub struct Error {
1445 hresult: HRESULT,
1446}
1447
1448pub type Result<T> = std::result::Result<T, Error>;
1449
1450impl fmt::Display for Error {
1451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1452 write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1453 }
1454}
1455
1456impl fmt::Debug for Error {
1457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1458 write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1459 }
1460}
1461
1462impl std::error::Error for Error {}
1463
1464impl From<NulError<u16>> for Error {
1465 fn from(_: NulError<u16>) -> Error {
1466 Error {
1467 hresult: E_INVALIDARG,
1468 }
1469 }
1470}
1471
1472impl From<io::Error> for Error {
1473 fn from(e: io::Error) -> Error {
1474 match e.raw_os_error() {
1475 Some(e) => Error::new(HRESULT_FROM_WIN32(e as u32)),
1476 _ => Error::new(E_FAIL),
1477 }
1478 }
1479}
1480
1481impl Error {
1482 pub fn new(hresult: HRESULT) -> Self {
1483 Self { hresult }
1484 }
1485
1486 fn into_io_error(self) -> io::Error {
1487 if (self.hresult & (0xffff_0000_u32 as i32))
1488 == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0)
1489 {
1490 io::Error::from_raw_os_error(HRESULT_CODE(self.hresult))
1491 } else {
1492 io::Error::new(io::ErrorKind::Other, self)
1493 }
1494 }
1495
1496 pub fn hresult(&self) -> HRESULT {
1497 self.hresult
1498 }
1499}
1500
1501pub fn check_hresult(hresult: HRESULT) -> Result<()> {
1504 if SUCCEEDED(hresult) {
1505 Ok(())
1506 } else {
1507 Err(Error { hresult })
1508 }
1509}
1510
1511fn to_hresult<T>(r: Result<T>) -> HRESULT {
1512 match r {
1513 Ok(_) => S_OK,
1514 Err(Error { hresult }) => hresult,
1515 }
1516}
1517
1518#[cfg(test)]
1519mod tests {
1520 use super::*;
1521 use std::io::{Read, Seek, Write};
1522
1523 #[test]
1524 fn test_stream() {
1525 let mut stream = Stream::from_bytes(b"hello,");
1526 stream.seek(io::SeekFrom::End(0)).unwrap();
1527 stream.write_all(b" world").unwrap();
1528
1529 let mut buf = Vec::new();
1530 stream.seek(io::SeekFrom::Start(0)).unwrap();
1531 stream.read_to_end(&mut buf).unwrap();
1532 assert_eq!(buf, b"hello, world");
1533 }
1534
1535 #[test]
1536 fn test_cmp_version() {
1537 assert_eq!(
1538 compare_browser_versions("84.0.498.0 canary", "84.0.498.0 canary").unwrap(),
1539 std::cmp::Ordering::Equal,
1540 );
1541 assert_eq!(
1542 compare_browser_versions("84.0.430.0 canary", "84.0.498.0 canary").unwrap(),
1543 std::cmp::Ordering::Less,
1544 );
1545 assert_eq!(
1546 compare_browser_versions("84.0.498.0", "84.0.440.0").unwrap(),
1547 std::cmp::Ordering::Greater,
1548 );
1549 }
1550}