Skip to main content

windows_webview/
event.rs

1use super::*;
2
3/// Details about a completed navigation, delivered to a
4/// [`WebView::on_navigation_completed`] handler.
5pub struct NavigationCompletedArgs(pub(crate) ICoreWebView2NavigationCompletedEventArgs);
6
7impl NavigationCompletedArgs {
8    /// Returns `true` if the navigation succeeded.
9    pub fn is_success(&self) -> bool {
10        unsafe { self.0.IsSuccess() }.is_ok_and(|value| value.as_bool())
11    }
12
13    /// Returns the unique identifier for the navigation.
14    pub fn navigation_id(&self) -> u64 {
15        unsafe { self.0.NavigationId() }.unwrap_or(0)
16    }
17}
18
19/// Details about a navigation that is about to start.
20pub struct NavigationStartingArgs(pub(crate) ICoreWebView2NavigationStartingEventArgs);
21
22impl NavigationStartingArgs {
23    /// Returns the URI the navigation is targeting.
24    pub fn uri(&self) -> String {
25        unsafe { string::take_result(self.0.Uri()) }
26    }
27
28    /// Returns `true` if the navigation was initiated by the user (for example a
29    /// link click) rather than by script.
30    pub fn is_user_initiated(&self) -> bool {
31        unsafe { self.0.IsUserInitiated() }.is_ok_and(|value| value.as_bool())
32    }
33
34    /// Returns `true` if the navigation is a redirect.
35    pub fn is_redirected(&self) -> bool {
36        unsafe { self.0.IsRedirected() }.is_ok_and(|value| value.as_bool())
37    }
38
39    /// Returns the unique identifier for the navigation.
40    pub fn navigation_id(&self) -> u64 {
41        unsafe { self.0.NavigationId() }.unwrap_or(0)
42    }
43
44    /// Returns `true` if the navigation is currently marked to be canceled.
45    pub fn is_cancelled(&self) -> bool {
46        unsafe { self.0.Cancel() }.is_ok_and(|value| value.as_bool())
47    }
48
49    /// Sets whether the navigation is canceled.
50    pub fn set_cancel(&self, cancel: bool) -> Result<()> {
51        unsafe { self.0.SetCancel(cancel) }.ok()
52    }
53}
54
55/// A message posted from the hosted page's JavaScript, delivered to a
56/// [`WebView::on_web_message_received`] handler.
57pub struct WebMessageReceivedArgs(pub(crate) ICoreWebView2WebMessageReceivedEventArgs);
58
59impl WebMessageReceivedArgs {
60    /// Returns the URI of the document that sent the message.
61    pub fn source(&self) -> String {
62        unsafe { string::take_result(self.0.Source()) }
63    }
64
65    /// Returns the message serialized as a JSON string. Messages sent with
66    /// `window.chrome.webview.postMessage` arrive here regardless of type.
67    pub fn web_message_as_json(&self) -> String {
68        unsafe { string::take_result(self.0.WebMessageAsJson()) }
69    }
70
71    /// Returns the message as a string. Fails if the page posted a value that is
72    /// not a JavaScript string.
73    pub fn try_web_message_as_string(&self) -> Result<String> {
74        let value = unsafe { self.0.TryGetWebMessageAsString()? };
75        Ok(unsafe { string::take(value) })
76    }
77}
78
79/// Details about content that is starting to load, delivered to a
80/// [`WebView::on_content_loading`] handler.
81pub struct ContentLoadingArgs(pub(crate) ICoreWebView2ContentLoadingEventArgs);
82
83impl ContentLoadingArgs {
84    /// Returns `true` if the loading content is the error page.
85    pub fn is_error_page(&self) -> bool {
86        unsafe { self.0.IsErrorPage() }.is_ok_and(|value| value.as_bool())
87    }
88
89    /// Returns the unique identifier for the navigation.
90    pub fn navigation_id(&self) -> u64 {
91        unsafe { self.0.NavigationId() }.unwrap_or(0)
92    }
93}
94
95/// Details about a page requesting to open a new window.
96pub struct NewWindowRequestedArgs(pub(crate) ICoreWebView2NewWindowRequestedEventArgs);
97
98impl NewWindowRequestedArgs {
99    /// Returns the URI the new window would navigate to.
100    pub fn uri(&self) -> String {
101        unsafe { string::take_result(self.0.Uri()) }
102    }
103
104    /// Returns `true` if the request was initiated by the user rather than by
105    /// script.
106    pub fn is_user_initiated(&self) -> bool {
107        unsafe { self.0.IsUserInitiated() }.is_ok_and(|value| value.as_bool())
108    }
109
110    /// Returns `true` if the request has been marked as handled.
111    pub fn is_handled(&self) -> bool {
112        unsafe { self.0.Handled() }.is_ok_and(|value| value.as_bool())
113    }
114
115    /// Marks the request as handled, suppressing the creation of a default new
116    /// window. Set this (without providing a [new window](Self::set_new_window))
117    /// to block the popup entirely.
118    pub fn set_handled(&self, handled: bool) -> Result<()> {
119        unsafe { self.0.SetHandled(handled) }.ok()
120    }
121
122    /// Provides an existing [`WebView`] to host the requested content instead of
123    /// creating a new window.
124    pub fn set_new_window(&self, webview: &WebView) -> Result<()> {
125        unsafe { self.0.SetNewWindow(&webview.0) }.ok()
126    }
127
128    /// Takes a [`Deferral`] so the request can be resolved after the handler
129    /// returns, for example once an asynchronously created window is ready.
130    pub fn defer(&self) -> Result<Deferral> {
131        Ok(Deferral::new(unsafe { self.0.GetDeferral()? }))
132    }
133}
134
135/// The kind of capability a page is requesting in a
136/// [`PermissionRequestedArgs`].
137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
138#[non_exhaustive]
139pub enum PermissionKind {
140    /// A permission kind not represented by the other variants.
141    Unknown,
142    /// Access to the microphone.
143    Microphone,
144    /// Access to the camera.
145    Camera,
146    /// Access to the device location.
147    Geolocation,
148    /// Permission to show notifications.
149    Notifications,
150    /// Access to device sensors other than location.
151    OtherSensors,
152    /// Permission to read the clipboard.
153    ClipboardRead,
154    /// Permission to start multiple downloads automatically.
155    MultipleAutomaticDownloads,
156    /// Access to files on the device.
157    FileReadWrite,
158    /// Permission to play media without user interaction.
159    Autoplay,
160    /// Access to locally installed fonts.
161    LocalFonts,
162    /// Access to system-exclusive MIDI messages.
163    MidiSystemExclusiveMessages,
164    /// Permission to inspect and place windows on screens.
165    WindowManagement,
166}
167
168impl PermissionKind {
169    fn from_raw(value: COREWEBVIEW2_PERMISSION_KIND) -> Self {
170        match value {
171            1 => Self::Microphone,
172            2 => Self::Camera,
173            3 => Self::Geolocation,
174            4 => Self::Notifications,
175            5 => Self::OtherSensors,
176            6 => Self::ClipboardRead,
177            7 => Self::MultipleAutomaticDownloads,
178            8 => Self::FileReadWrite,
179            9 => Self::Autoplay,
180            10 => Self::LocalFonts,
181            11 => Self::MidiSystemExclusiveMessages,
182            12 => Self::WindowManagement,
183            _ => Self::Unknown,
184        }
185    }
186}
187
188/// How a [`PermissionRequestedArgs`] should be resolved.
189#[derive(Clone, Copy, Debug, PartialEq, Eq)]
190pub enum PermissionState {
191    /// Defer to the browser default (typically prompting the user).
192    Default,
193    /// Grant the permission.
194    Allow,
195    /// Deny the permission.
196    Deny,
197}
198
199impl PermissionState {
200    fn from_raw(value: COREWEBVIEW2_PERMISSION_STATE) -> Self {
201        match value {
202            1 => Self::Allow,
203            2 => Self::Deny,
204            _ => Self::Default,
205        }
206    }
207
208    fn to_raw(self) -> COREWEBVIEW2_PERMISSION_STATE {
209        match self {
210            Self::Default => 0,
211            Self::Allow => 1,
212            Self::Deny => 2,
213        }
214    }
215}
216
217/// Details about a permission a page is requesting (for example camera or
218/// geolocation access), delivered to a [`WebView::on_permission_requested`]
219/// handler. Decide the outcome with [`set_state`](Self::set_state).
220pub struct PermissionRequestedArgs(pub(crate) ICoreWebView2PermissionRequestedEventArgs);
221
222impl PermissionRequestedArgs {
223    /// Returns the URI of the page requesting the permission.
224    pub fn uri(&self) -> String {
225        unsafe { string::take_result(self.0.Uri()) }
226    }
227
228    /// Returns the kind of permission being requested.
229    pub fn kind(&self) -> PermissionKind {
230        unsafe { self.0.PermissionKind() }.map_or(PermissionKind::Unknown, PermissionKind::from_raw)
231    }
232
233    /// Returns `true` if the request was initiated by the user rather than by
234    /// script.
235    pub fn is_user_initiated(&self) -> bool {
236        unsafe { self.0.IsUserInitiated() }.is_ok_and(|value| value.as_bool())
237    }
238
239    /// Returns the current resolution state of the request.
240    pub fn state(&self) -> PermissionState {
241        unsafe { self.0.State() }.map_or(PermissionState::Default, PermissionState::from_raw)
242    }
243
244    /// Sets how the request should be resolved.
245    pub fn set_state(&self, state: PermissionState) -> Result<()> {
246        unsafe { self.0.SetState(state.to_raw()) }.ok()
247    }
248
249    /// Takes a [`Deferral`] so the request can be resolved after the handler
250    /// returns, for example once the user has answered a prompt.
251    pub fn defer(&self) -> Result<Deferral> {
252        Ok(Deferral::new(unsafe { self.0.GetDeferral()? }))
253    }
254}
255
256/// The kind of process that failed, reported by [`ProcessFailedArgs::kind`].
257#[derive(Clone, Copy, Debug, PartialEq, Eq)]
258#[non_exhaustive]
259pub enum ProcessFailedKind {
260    /// A process failure kind not represented by the other variants.
261    Unknown,
262    /// The browser process ended unexpectedly; the `WebView` is no longer usable.
263    BrowserProcessExited,
264    /// A render process ended unexpectedly (a renderer crash); the page is gone
265    /// but the `WebView` can be reloaded.
266    RenderProcessExited,
267    /// A render process is unresponsive (hung).
268    RenderProcessUnresponsive,
269    /// A frame's render process ended unexpectedly.
270    FrameRenderProcessExited,
271    /// A utility process ended unexpectedly.
272    UtilityProcessExited,
273    /// A sandbox helper process ended unexpectedly.
274    SandboxHelperProcessExited,
275    /// The GPU process ended unexpectedly.
276    GpuProcessExited,
277    /// A PPAPI plugin process ended unexpectedly.
278    PpapiPluginProcessExited,
279    /// A PPAPI broker process ended unexpectedly.
280    PpapiBrokerProcessExited,
281    /// An unrecognized process ended unexpectedly.
282    UnknownProcessExited,
283}
284
285impl ProcessFailedKind {
286    fn from_raw(value: COREWEBVIEW2_PROCESS_FAILED_KIND) -> Self {
287        match value {
288            0 => Self::BrowserProcessExited,
289            1 => Self::RenderProcessExited,
290            2 => Self::RenderProcessUnresponsive,
291            3 => Self::FrameRenderProcessExited,
292            4 => Self::UtilityProcessExited,
293            5 => Self::SandboxHelperProcessExited,
294            6 => Self::GpuProcessExited,
295            7 => Self::PpapiPluginProcessExited,
296            8 => Self::PpapiBrokerProcessExited,
297            9 => Self::UnknownProcessExited,
298            _ => Self::Unknown,
299        }
300    }
301}
302
303/// Details about a failed or unresponsive browser process, delivered to a
304/// [`WebView::on_process_failed`] handler.
305pub struct ProcessFailedArgs(pub(crate) ICoreWebView2ProcessFailedEventArgs);
306
307impl ProcessFailedArgs {
308    /// Returns which kind of process failed.
309    pub fn kind(&self) -> ProcessFailedKind {
310        unsafe { self.0.ProcessFailedKind() }
311            .map_or(ProcessFailedKind::Unknown, ProcessFailedKind::from_raw)
312    }
313}
314
315/// The reason focus is moving, reported by [`MoveFocusRequestedArgs::reason`]
316/// and passed to [`Controller::move_focus`].
317#[derive(Clone, Copy, Debug, PartialEq, Eq)]
318pub enum MoveFocusReason {
319    /// Focus is being set programmatically.
320    Programmatic,
321    /// Focus is moving forward (for example Tab).
322    Next,
323    /// Focus is moving backward (for example Shift+Tab).
324    Previous,
325}
326
327impl MoveFocusReason {
328    fn from_raw(value: COREWEBVIEW2_MOVE_FOCUS_REASON) -> Self {
329        match value {
330            1 => Self::Next,
331            2 => Self::Previous,
332            _ => Self::Programmatic,
333        }
334    }
335
336    pub(crate) fn to_raw(self) -> COREWEBVIEW2_MOVE_FOCUS_REASON {
337        match self {
338            Self::Programmatic => 0,
339            Self::Next => 1,
340            Self::Previous => 2,
341        }
342    }
343}
344
345/// A request to move focus out of the browser.
346pub struct MoveFocusRequestedArgs(pub(crate) ICoreWebView2MoveFocusRequestedEventArgs);
347
348impl MoveFocusRequestedArgs {
349    /// Returns the direction focus is moving.
350    pub fn reason(&self) -> MoveFocusReason {
351        unsafe { self.0.Reason() }.map_or(MoveFocusReason::Programmatic, MoveFocusReason::from_raw)
352    }
353
354    /// Returns `true` if the request has been marked as handled.
355    pub fn is_handled(&self) -> bool {
356        unsafe { self.0.Handled() }.is_ok_and(|value| value.as_bool())
357    }
358
359    /// Marks the request as handled, indicating the host moved focus itself and
360    /// WebView2 should not apply its default focus behavior.
361    pub fn set_handled(&self, handled: bool) -> Result<()> {
362        unsafe { self.0.SetHandled(handled) }.ok()
363    }
364}
365
366/// The kind of key event reported by [`AcceleratorKeyPressedArgs::key_event_kind`].
367#[derive(Clone, Copy, Debug, PartialEq, Eq)]
368pub enum KeyEventKind {
369    /// A key was pressed.
370    KeyDown,
371    /// A key was released.
372    KeyUp,
373    /// A system key was pressed (for example a key combined with Alt).
374    SystemKeyDown,
375    /// A system key was released.
376    SystemKeyUp,
377}
378
379impl KeyEventKind {
380    fn from_raw(value: COREWEBVIEW2_KEY_EVENT_KIND) -> Self {
381        match value {
382            1 => Self::KeyUp,
383            2 => Self::SystemKeyDown,
384            3 => Self::SystemKeyUp,
385            _ => Self::KeyDown,
386        }
387    }
388}
389
390/// A browser-level key press delivered before the page sees it.
391pub struct AcceleratorKeyPressedArgs(pub(crate) ICoreWebView2AcceleratorKeyPressedEventArgs);
392
393impl AcceleratorKeyPressedArgs {
394    /// Returns whether the key was pressed or released, and whether it is a
395    /// system key.
396    pub fn key_event_kind(&self) -> KeyEventKind {
397        unsafe { self.0.KeyEventKind() }.map_or(KeyEventKind::KeyDown, KeyEventKind::from_raw)
398    }
399
400    /// Returns the Win32 virtual-key code of the key.
401    pub fn virtual_key(&self) -> u32 {
402        unsafe { self.0.VirtualKey() }.unwrap_or(0)
403    }
404
405    /// Returns `true` if the key has been marked as handled.
406    pub fn is_handled(&self) -> bool {
407        unsafe { self.0.Handled() }.is_ok_and(|value| value.as_bool())
408    }
409
410    /// Marks the key as handled, preventing WebView2's default processing so the
411    /// host can act on the shortcut itself.
412    pub fn set_handled(&self, handled: bool) -> Result<()> {
413        unsafe { self.0.SetHandled(handled) }.ok()
414    }
415}
416
417/// A Chrome DevTools Protocol event, delivered to a
418/// [`WebView::on_dev_tools_protocol_event`] handler. The event's parameters are
419/// available as a JSON object string.
420pub struct DevToolsProtocolEventReceivedArgs(
421    pub(crate) ICoreWebView2DevToolsProtocolEventReceivedEventArgs,
422);
423
424impl DevToolsProtocolEventReceivedArgs {
425    /// Returns the event's parameter object as a JSON string, matching the
426    /// `params` of the corresponding CDP event.
427    pub fn parameter_object_as_json(&self) -> String {
428        unsafe { string::take_result(self.0.ParameterObjectAsJson()) }
429    }
430}
431
432/// An event subscription that unsubscribes when dropped.
433#[must_use]
434pub struct EventRegistration(Option<Box<dyn FnOnce()>>);
435
436impl EventRegistration {
437    pub(crate) fn new<F: FnOnce() + 'static>(remove: F) -> Self {
438        Self(Some(Box::new(remove)))
439    }
440
441    /// Unsubscribes the handler.
442    pub fn remove(mut self) {
443        if let Some(remove) = self.0.take() {
444            remove();
445        }
446    }
447}
448
449impl Drop for EventRegistration {
450    fn drop(&mut self) {
451        if let Some(remove) = self.0.take() {
452            remove();
453        }
454    }
455}