Fix autofill event downcast - ignore events of the wrong type (#5379)

* fix autofill events

* ignore the wrong type of event like we do in desktop

* check the event type before dispatch instead of panicing on downcast

* clean up conversion

* remove example

* pull out shared event list

* pull in deserialize_raw

* unified event list

* prettier comment

* restore test

* restore alias

* normal doc comments

* move over comments

* minimize diff

* remove alias, was moved into the macro
This commit is contained in:
Evan Almloff authored and GitHub committed 2026-04-07 13:18:24 -07:00
1 parent 610ee8107f
commit d8026f8824
27 files changed
+596 -960

No files matched your search

+11 -1
View File
@@ -15,7 +15,9 @@ use rustc_hash::FxHashMap;
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{Document, Event, Node};
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
use crate::{
event_type_matches, load_document, virtual_event_from_websys_event, Config, WebEventConverter,
};
pub struct WebsysDom {
#[allow(dead_code)]
@@ -94,6 +96,14 @@ impl WebsysDom {
return;
};
// Some browser features (e.g. datalist autocomplete) dispatch
// a plain Event with a typed name like "keydown" that isn't
// actually a KeyboardEvent. Drop events whose JS type doesn't
// match what the converters will unchecked-cast to.
if !event_type_matches(name.as_str(), web_sys_event) {
return;
}
let data = virtual_event_from_websys_event(web_sys_event.clone(), target);
let event = dioxus_core::Event::new(Rc::new(data) as Rc<dyn Any>, bubbles);
-6
View File
@@ -3,12 +3,6 @@ use web_sys::Event;
use super::{Synthetic, WebEventExt};
impl From<&Event> for Synthetic<Event> {
fn from(e: &Event) -> Self {
Synthetic::new(e.clone())
}
}
impl HasClipboardData for Synthetic<Event> {
fn as_any(&self) -> &dyn std::any::Any {
&self.event
+127 -199
View File
@@ -1,6 +1,4 @@
use dioxus_html::{
DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData,
};
use dioxus_html::{DragData, FormData, HtmlEventConverter, ImageData, PlatformEventData};
use form::WebFormData;
use load::WebImageEvent;
use wasm_bindgen::JsCast;
@@ -52,170 +50,135 @@ fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent
.expect("event should be a GenericWebSysEvent")
}
impl HtmlEventConverter for WebEventConverter {
#[inline(always)]
fn convert_animation_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::AnimationData {
Synthetic::<web_sys::AnimationEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_cancel_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CancelData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_clipboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ClipboardData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_composition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CompositionData {
Synthetic::<web_sys::CompositionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
let event = downcast_event(event);
DragData::new(Synthetic::new(
event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
))
}
#[inline(always)]
fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
Synthetic::<web_sys::FocusEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
}
#[inline(always)]
fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
let event = downcast_event(event);
let error = event.raw.type_() == "error";
ImageData::new(WebImageEvent::new(event.raw.clone(), error))
}
#[inline(always)]
fn convert_keyboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::KeyboardData {
Synthetic::<web_sys::KeyboardEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[allow(unused_variables)]
#[inline(always)]
fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
#[cfg(feature = "mounted")]
{
Synthetic::new(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element")
.clone(),
)
.into()
macro_rules! with_web_event_converters {
($macro:ident) => {
$macro! {
convert_animation_data(AnimationData) => web_sys::AnimationEvent;
convert_cancel_data(CancelData) => web_sys::Event;
convert_clipboard_data(ClipboardData) => web_sys::Event;
convert_composition_data(CompositionData) => web_sys::CompositionEvent;
convert_drag_data(DragData) => web_sys::DragEvent => |event| {
let event = downcast_event(event);
DragData::new(Synthetic::new(
event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
))
};
convert_focus_data(FocusData) => web_sys::FocusEvent;
convert_form_data(FormData) => web_sys::Event => |event| {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
};
convert_image_data(ImageData) => web_sys::Event => |event| {
let event = downcast_event(event);
ImageData::new(WebImageEvent::new(
event.raw.clone(),
event.raw.type_() == "error",
))
};
convert_keyboard_data(KeyboardData) => web_sys::KeyboardEvent;
convert_media_data(MediaData) => web_sys::Event;
convert_mounted_data(MountedData) => web_sys::Element => |event| {
#[cfg(feature = "mounted")]
{
Synthetic::new(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element")
.clone(),
)
.into()
}
#[cfg(not(feature = "mounted"))]
{
let _ = event;
panic!("mounted events require the `mounted` feature on dioxus-web")
}
};
convert_mouse_data(MouseData) => web_sys::MouseEvent;
convert_pointer_data(PointerData) => web_sys::PointerEvent;
convert_resize_data(ResizeData) => web_sys::ResizeObserverEntry;
convert_scroll_data(ScrollData) => web_sys::Event;
convert_selection_data(SelectionData) => web_sys::Event;
convert_toggle_data(ToggleData) => web_sys::Event;
convert_touch_data(TouchData) => web_sys::TouchEvent;
convert_transition_data(TransitionData) => web_sys::TransitionEvent;
convert_visible_data(VisibleData) => web_sys::IntersectionObserverEntry;
convert_wheel_data(WheelData) => web_sys::WheelEvent;
}
#[cfg(not(feature = "mounted"))]
{
panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled")
}
}
#[inline(always)]
fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
Synthetic::<web_sys::MouseEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_pointer_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::PointerData {
Synthetic::<web_sys::PointerEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_resize_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ResizeData {
Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_scroll_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ScrollData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_selection_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::SelectionData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_toggle_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ToggleData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
Synthetic::<web_sys::TouchEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_transition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::TransitionData {
Synthetic::<web_sys::TransitionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_visible_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::VisibleData {
Synthetic::<web_sys::IntersectionObserverEntry>::from(downcast_event(event).raw.clone())
.into()
}
#[inline(always)]
fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
Synthetic::<web_sys::WheelEvent>::from(downcast_event(event).raw.clone()).into()
}
};
}
macro_rules! expand_web_event_converter {
(
$(
$converter:ident($data:ident) => $web_ty:ty $(=> |$event:ident| $body:block)?;
)*
) => {
macro_rules! web_event_type_matches {
$(
($event_name:ident, $converter) => {
$event_name.is_instance_of::<$web_ty>()
};
)*
}
impl HtmlEventConverter for WebEventConverter {
$(
expand_web_event_converter!(@method $converter, $data, $web_ty $(, $event, $body)?);
)*
}
};
(@method $converter:ident, $data:ident, $web_ty:ty) => {
#[inline(always)]
fn $converter(&self, event: &PlatformEventData) -> dioxus_html::$data {
Synthetic::new(downcast_event(event).raw.clone().unchecked_into::<$web_ty>()).into()
}
};
(@method $converter:ident, $data:ident, $web_ty:ty, $event:ident, $body:block) => {
#[inline(always)]
fn $converter(&self, $event: &PlatformEventData) -> dioxus_html::$data $body
};
}
with_web_event_converters!(expand_web_event_converter);
macro_rules! expand_web_event_changes {
(
enum Event {
$(
#[convert = $converter:ident]
#[events = [
$(
$( #[$attr:meta] )*
$name:ident => $raw:ident,
)*
]]
$(#[raw = [$($raw_only:ident),* $(,)?]])?
$group:ident($data:ident),
)*
}
) => {
pub(crate) fn event_type_matches(name: &str, event: &web_sys::Event) -> bool {
let m = match name {
$(
$( stringify!($raw) )|* $($(| stringify!($raw_only))*)? => {
web_event_type_matches!(event, $converter)
}
)*
_ => true,
};
if !m {
tracing::warn!("Ignoring \"{name}\": not the expected type: {event:?}");
}
m
}
};
}
dioxus_html::with_html_event_groups!(expand_web_event_changes);
/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
pub trait WebEventExt {
/// The web specific event type
@@ -262,38 +225,3 @@ pub(crate) fn load_document() -> Document {
.document()
.expect("should have access to the Document")
}
macro_rules! uncheck_convert {
($t:ty) => {
impl From<Event> for Synthetic<$t> {
#[inline]
fn from(e: Event) -> Self {
let e: $t = e.unchecked_into();
Self::new(e)
}
}
impl From<&Event> for Synthetic<$t> {
#[inline]
fn from(e: &Event) -> Self {
let e: &$t = e.unchecked_ref();
Self::new(e.clone())
}
}
};
($($t:ty),+ $(,)?) => {
$(uncheck_convert!($t);)+
};
}
uncheck_convert![
web_sys::CompositionEvent,
web_sys::KeyboardEvent,
web_sys::TouchEvent,
web_sys::PointerEvent,
web_sys::WheelEvent,
web_sys::AnimationEvent,
web_sys::TransitionEvent,
web_sys::MouseEvent,
web_sys::FocusEvent,
];