mirror of
https://github.com/lexmount/moli.git
synced 2026-10-31 00:06:19 +00:00
fix(events): convert inherited mouse dictionaries and validate native getters
This commit is contained in:
@@ -436,6 +436,38 @@ struct KeyboardEventTemplateMethodsDeclaration {
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::MouseEvent, enumerable, receiver)]
|
||||
struct MouseEventTemplateMethodsDeclaration {
|
||||
#[webapi(accessor_property = "screenX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "screenX"))]
|
||||
screen_x: (),
|
||||
#[webapi(accessor_property = "screenY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "screenY"))]
|
||||
screen_y: (),
|
||||
#[webapi(accessor_property = "clientX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "clientX"))]
|
||||
client_x: (),
|
||||
#[webapi(accessor_property = "clientY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "clientY"))]
|
||||
client_y: (),
|
||||
#[webapi(accessor_property = "button", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "button"))]
|
||||
button: (),
|
||||
#[webapi(accessor_property = "buttons", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "buttons"))]
|
||||
buttons: (),
|
||||
#[webapi(accessor_property = "ctrlKey", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "ctrlKey"))]
|
||||
ctrl_key: (),
|
||||
#[webapi(accessor_property = "altKey", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "altKey"))]
|
||||
alt_key: (),
|
||||
#[webapi(accessor_property = "shiftKey", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "shiftKey"))]
|
||||
shift_key: (),
|
||||
#[webapi(accessor_property = "metaKey", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "metaKey"))]
|
||||
meta_key: (),
|
||||
#[webapi(accessor_property = "movementX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "movementX"))]
|
||||
movement_x: (),
|
||||
#[webapi(accessor_property = "movementY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "movementY"))]
|
||||
movement_y: (),
|
||||
#[webapi(accessor_property = "pageX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "pageX"))]
|
||||
page_x: (),
|
||||
#[webapi(accessor_property = "pageY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "pageY"))]
|
||||
page_y: (),
|
||||
#[webapi(accessor_property = "x", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "x"))]
|
||||
x: (),
|
||||
#[webapi(accessor_property = "y", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "y"))]
|
||||
y: (),
|
||||
#[webapi(
|
||||
method = "getModifierState",
|
||||
length = 1,
|
||||
@@ -460,7 +492,7 @@ struct MouseEventTemplateMethodsDeclaration {
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::WheelEvent, enumerable)]
|
||||
#[webapi(interface = web_api_interfaces::WheelEvent, enumerable, receiver)]
|
||||
struct WheelEventTemplateConstantsDeclaration {
|
||||
#[webapi(constant = "DOM_DELTA_PIXEL", value = 0u32)]
|
||||
dom_delta_pixel: (),
|
||||
@@ -470,11 +502,53 @@ struct WheelEventTemplateConstantsDeclaration {
|
||||
|
||||
#[webapi(constant = "DOM_DELTA_PAGE", value = 2u32)]
|
||||
dom_delta_page: (),
|
||||
|
||||
#[webapi(accessor_property = "deltaMode", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "deltaMode"))]
|
||||
delta_mode: (),
|
||||
#[webapi(accessor_property = "deltaX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "deltaX"))]
|
||||
delta_x: (),
|
||||
#[webapi(accessor_property = "deltaY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "deltaY"))]
|
||||
delta_y: (),
|
||||
#[webapi(accessor_property = "deltaZ", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "deltaZ"))]
|
||||
delta_z: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::DragEvent, enumerable, receiver)]
|
||||
struct DragEventTemplateAccessorsDeclaration {
|
||||
#[webapi(accessor_property = "dataTransfer", getter = event_platform_attribute_getter, data = crate::util::v8str(scope, "dataTransfer"))]
|
||||
data_transfer: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::PointerEvent, enumerable, receiver)]
|
||||
struct PointerEventTemplateMethodsDeclaration {
|
||||
#[webapi(accessor_property = "pointerId", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "pointerId"))]
|
||||
pointer_id: (),
|
||||
#[webapi(accessor_property = "width", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "width"))]
|
||||
width: (),
|
||||
#[webapi(accessor_property = "height", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "height"))]
|
||||
height: (),
|
||||
#[webapi(accessor_property = "pressure", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "pressure"))]
|
||||
pressure: (),
|
||||
#[webapi(accessor_property = "tangentialPressure", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "tangentialPressure"))]
|
||||
tangential_pressure: (),
|
||||
#[webapi(accessor_property = "tiltX", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "tiltX"))]
|
||||
tilt_x: (),
|
||||
#[webapi(accessor_property = "tiltY", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "tiltY"))]
|
||||
tilt_y: (),
|
||||
#[webapi(accessor_property = "twist", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "twist"))]
|
||||
twist: (),
|
||||
#[webapi(accessor_property = "altitudeAngle", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "altitudeAngle"))]
|
||||
altitude_angle: (),
|
||||
#[webapi(accessor_property = "azimuthAngle", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "azimuthAngle"))]
|
||||
azimuth_angle: (),
|
||||
#[webapi(accessor_property = "pointerType", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "pointerType"))]
|
||||
pointer_type: (),
|
||||
#[webapi(accessor_property = "isPrimary", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "isPrimary"))]
|
||||
is_primary: (),
|
||||
#[webapi(accessor_property = "persistentDeviceId", getter = event_value_attribute_getter, data = crate::util::v8str(scope, "persistentDeviceId"))]
|
||||
persistent_device_id: (),
|
||||
#[webapi(
|
||||
method = "getPredictedEvents",
|
||||
length = 0,
|
||||
@@ -578,6 +652,7 @@ fn event_template_declaration(interface: &str) -> Option<EventTemplateDeclaratio
|
||||
EventTemplateDeclaration::new::<KeyboardEventTemplateMethodsDeclaration>()
|
||||
}
|
||||
"MouseEvent" => EventTemplateDeclaration::new::<MouseEventTemplateMethodsDeclaration>(),
|
||||
"DragEvent" => EventTemplateDeclaration::new::<DragEventTemplateAccessorsDeclaration>(),
|
||||
"WheelEvent" => EventTemplateDeclaration::new::<WheelEventTemplateConstantsDeclaration>(),
|
||||
"PointerEvent" => EventTemplateDeclaration::new::<PointerEventTemplateMethodsDeclaration>(),
|
||||
"CloseEvent" => EventTemplateDeclaration::new::<CloseEventTemplateAccessorsDeclaration>(),
|
||||
|
||||
@@ -87,7 +87,7 @@ struct StorageEventStateDeclaration<'scope> {
|
||||
}
|
||||
|
||||
#[derive(Default, WebApiObject)]
|
||||
#[webapi(fragment, prototype = "PointerEvent", enumerable)]
|
||||
#[webapi(fragment, prototype = "PointerEvent", enumerable, receiver = crate::web_api_interfaces::PointerEvent::is_instance)]
|
||||
struct SecurePointerEventPrototypeRuntimeDeclaration {
|
||||
#[webapi(
|
||||
method = "getCoalescedEvents",
|
||||
|
||||
@@ -103,27 +103,6 @@ impl EventModifierInitMembers {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn initialize_event_modifiers<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> bool {
|
||||
let modifiers = match init {
|
||||
Some(init) => {
|
||||
match webidl::parse_dictionary_object::<EventModifierInitMembers>(scope, init) {
|
||||
Ok(modifiers) => modifiers,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => EventModifierInitMembers::default(),
|
||||
};
|
||||
modifiers.initialize(scope, event);
|
||||
true
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn initialize_legacy_event_modifiers<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
|
||||
@@ -17,6 +17,10 @@ enum EventSubclassInit<'s> {
|
||||
Clipboard(data::ClipboardEventInitMembers<'s>),
|
||||
ClipboardChange(data::ClipboardChangeEventInitMembers<'s>),
|
||||
Keyboard(keyboard::KeyboardEventInit<'s>),
|
||||
Mouse(pointer::MouseEventInit<'s>),
|
||||
Wheel(pointer::WheelEventInit<'s>),
|
||||
Pointer(pointer::PointerEventInit<'s>),
|
||||
Drag(pointer::DragEventInit<'s>),
|
||||
Message(message::MessageEventInit<'s>),
|
||||
Storage(data::StorageEventInitMembers<'s>),
|
||||
Error(error::ErrorEventInit<'s>),
|
||||
@@ -48,6 +52,18 @@ impl<'s> EventSubclassInit<'s> {
|
||||
EventSubclassKind::KeyboardEvent => {
|
||||
Self::Keyboard(keyboard::parse_keyboard_event_init(scope, args)?)
|
||||
}
|
||||
EventSubclassKind::MouseEvent => {
|
||||
Self::Mouse(ui::parse_ui_dictionary(scope, args, "MouseEvent")?)
|
||||
}
|
||||
EventSubclassKind::WheelEvent => {
|
||||
Self::Wheel(ui::parse_ui_dictionary(scope, args, "WheelEvent")?)
|
||||
}
|
||||
EventSubclassKind::PointerEvent => {
|
||||
Self::Pointer(ui::parse_ui_dictionary(scope, args, "PointerEvent")?)
|
||||
}
|
||||
EventSubclassKind::DragEvent => {
|
||||
Self::Drag(ui::parse_ui_dictionary(scope, args, "DragEvent")?)
|
||||
}
|
||||
EventSubclassKind::MessageEvent => {
|
||||
Self::Message(message::parse_message_event_init(scope, args)?)
|
||||
}
|
||||
@@ -87,6 +103,10 @@ impl<'s> EventSubclassInit<'s> {
|
||||
Self::Clipboard(init) => init.event_flags(),
|
||||
Self::ClipboardChange(init) => init.event_flags(),
|
||||
Self::Keyboard(init) => init.event_flags(),
|
||||
Self::Mouse(init) => init.event_flags(),
|
||||
Self::Wheel(init) => init.event_flags(),
|
||||
Self::Pointer(init) => init.event_flags(),
|
||||
Self::Drag(init) => init.event_flags(),
|
||||
Self::Message(init) => init.event_flags(),
|
||||
Self::Storage(init) => init.event_flags(),
|
||||
Self::Error(init) => init.event_flags(),
|
||||
@@ -114,6 +134,10 @@ impl<'s> EventSubclassInit<'s> {
|
||||
return data::initialize_clipboard_change_event(scope, event, init);
|
||||
}
|
||||
Self::Keyboard(init) => keyboard::initialize_keyboard_event(scope, event, init),
|
||||
Self::Mouse(init) => init.initialize(scope, event),
|
||||
Self::Wheel(init) => init.initialize(scope, event),
|
||||
Self::Pointer(init) => init.initialize(scope, event),
|
||||
Self::Drag(init) => init.initialize(scope, event),
|
||||
Self::Message(init) => message::initialize_message_event(scope, event, init),
|
||||
Self::Storage(init) => data::initialize_storage_event(scope, event, init),
|
||||
Self::Error(init) => init.initialize(scope, event),
|
||||
@@ -224,32 +248,12 @@ fn initialize_legacy_event<'s>(
|
||||
}
|
||||
}
|
||||
EventSubclassKind::CustomEvent => basic::initialize_custom_event(scope, event, init),
|
||||
EventSubclassKind::MouseEvent => {
|
||||
if !pointer::initialize_mouse_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EventSubclassKind::DragEvent => {
|
||||
if !data::initialize_drag_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EventSubclassKind::CapturedMouseEvent => {
|
||||
if !data::initialize_captured_mouse_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EventSubclassKind::InputEvent => data::initialize_input_event(scope, event, init),
|
||||
EventSubclassKind::WheelEvent => {
|
||||
if !pointer::initialize_wheel_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EventSubclassKind::PointerEvent => {
|
||||
if !pointer::initialize_pointer_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EventSubclassKind::TouchEvent => {
|
||||
crate::context_bootstrap::touch_runtime::initialize_touch_event(scope, event, init);
|
||||
}
|
||||
@@ -297,6 +301,10 @@ fn initialize_legacy_event<'s>(
|
||||
| EventSubclassKind::CompositionEvent
|
||||
| EventSubclassKind::ErrorEvent
|
||||
| EventSubclassKind::KeyboardEvent
|
||||
| EventSubclassKind::MouseEvent
|
||||
| EventSubclassKind::WheelEvent
|
||||
| EventSubclassKind::PointerEvent
|
||||
| EventSubclassKind::DragEvent
|
||||
| EventSubclassKind::MessageEvent
|
||||
| EventSubclassKind::NavigateEvent
|
||||
| EventSubclassKind::NavigationCurrentEntryChangeEvent
|
||||
|
||||
@@ -137,12 +137,6 @@ struct NavigateEventMethodsDeclaration {
|
||||
scroll: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain, data_properties, enumerable)]
|
||||
struct DragEventInitDeclaration<'scope> {
|
||||
data_transfer: v8::Local<'scope, v8::Value>,
|
||||
}
|
||||
|
||||
/// Web IDL converts inherited `EventInit` members before the derived members,
|
||||
/// with each dictionary's members in lexicographic order. Keeping them in one
|
||||
/// conversion also ensures each init getter is read only once.
|
||||
@@ -469,53 +463,6 @@ pub(super) fn parse_clipboard_change_event_init<'s>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap::events::subclasses) fn initialize_drag_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> bool {
|
||||
if !pointer::initialize_mouse_event(scope, event, init) {
|
||||
return false;
|
||||
}
|
||||
let data_transfer = match init {
|
||||
None => v8::null(scope).into(),
|
||||
Some(init) => match webidl::property_result(
|
||||
scope,
|
||||
init,
|
||||
"dataTransfer",
|
||||
webidl::Context::member("DragEventInit", "dataTransfer"),
|
||||
) {
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return false;
|
||||
}
|
||||
Ok(None) => v8::null(scope).into(),
|
||||
Ok(Some(value)) if value.is_null_or_undefined() => v8::null(scope).into(),
|
||||
Ok(Some(value)) => {
|
||||
let Ok(object) = v8::Local::<v8::Object>::try_from(value) else {
|
||||
throw_type_error(
|
||||
scope,
|
||||
"Failed to construct 'DragEvent': member dataTransfer is not of type DataTransfer.",
|
||||
);
|
||||
return false;
|
||||
};
|
||||
if !is_branded_data_transfer_object(scope, object) {
|
||||
throw_type_error(
|
||||
scope,
|
||||
"Failed to construct 'DragEvent': member dataTransfer is not of type DataTransfer.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
value
|
||||
}
|
||||
},
|
||||
};
|
||||
DragEventInitDeclaration::new(data_transfer)
|
||||
.initialize(scope, event)
|
||||
.expect("DragEvent init declaration should initialize");
|
||||
true
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap::events::subclasses) fn initialize_clipboard_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
|
||||
@@ -1,32 +1,366 @@
|
||||
use super::*;
|
||||
use moli_webapi_declare::WebApiObject;
|
||||
|
||||
use crate::context_bootstrap::events::{modifiers::EventModifierInitMembers, ui::UiEventInit};
|
||||
use crate::context_bootstrap::file_api::is_branded_data_transfer_object;
|
||||
use crate::web_api_interfaces;
|
||||
use crate::webidl;
|
||||
|
||||
const POINTER_EVENT_COALESCED_EVENTS_SLOT: &str = "__moliPointerEventCoalescedEvents";
|
||||
const POINTER_EVENT_PREDICTED_EVENTS_SLOT: &str = "__moliPointerEventPredictedEvents";
|
||||
|
||||
#[derive(Default, webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "PointerEventInit")]
|
||||
struct PointerEventSequences<'scope> {
|
||||
#[webidl(sequence, interface = crate::web_api_interfaces::PointerEvent, brand_check = is_pointer_event_sequence_item, default = Vec::new())]
|
||||
coalesced_events: Vec<v8::Local<'scope, v8::Object>>,
|
||||
#[webidl(sequence, interface = crate::web_api_interfaces::PointerEvent, brand_check = is_pointer_event_sequence_item, default = Vec::new())]
|
||||
predicted_events: Vec<v8::Local<'scope, v8::Object>>,
|
||||
struct EventTargetReference<'s>(v8::Local<'s, v8::Object>);
|
||||
|
||||
impl<'s> webidl::WebIdlConverter<'s> for EventTargetReference<'s> {
|
||||
type Options = ();
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
context: webidl::Context,
|
||||
_options: &Self::Options,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
let target = webidl::convert::<v8::Local<'s, v8::Object>>(scope, value, context)?;
|
||||
if !web_api_interfaces::EventTarget::is_instance(scope, target) {
|
||||
return Err(webidl::WebIdlError::cannot_convert(context, "EventTarget"));
|
||||
}
|
||||
Ok(Self(target))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pointer_event_sequence_item<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
super::super::event_subclass_kind(scope, event) == Some(EventSubclassKind::PointerEvent)
|
||||
/// MouseEventInit includes the CSSOM View and Pointer Lock double members.
|
||||
/// Each derived dictionary delegates to this base before reading its own members.
|
||||
#[derive(Default, webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "MouseEventInit")]
|
||||
struct MouseEventInitMembers<'s> {
|
||||
#[webidl(default = 0)]
|
||||
button: i16,
|
||||
#[webidl(default = 0)]
|
||||
buttons: u16,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
client_x: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
client_y: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
movement_x: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
movement_y: f64,
|
||||
#[webidl(nullable, converter = "raw")]
|
||||
related_target: Option<EventTargetReference<'s>>,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
screen_x: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
screen_y: f64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct MouseEventInit<'s> {
|
||||
ui: UiEventInit<'s>,
|
||||
modifiers: EventModifierInitMembers,
|
||||
members: MouseEventInitMembers<'s>,
|
||||
}
|
||||
|
||||
impl<'s> webidl::WebIdlDictionary<'s> for MouseEventInit<'s> {
|
||||
fn parse_dictionary(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
Ok(Self {
|
||||
ui: webidl::parse_dictionary_object(scope, object)?,
|
||||
modifiers: webidl::parse_dictionary_object(scope, object)?,
|
||||
members: webidl::parse_dictionary_object(scope, object)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> MouseEventInit<'s> {
|
||||
pub(super) fn event_flags(&self) -> (bool, bool, bool) {
|
||||
self.ui.event_flags()
|
||||
}
|
||||
|
||||
pub(super) fn initialize(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
self.ui.initialize(scope, event);
|
||||
self.modifiers.initialize(scope, event);
|
||||
let members = self.members;
|
||||
MouseEventBaseInitDeclaration::new(
|
||||
members.screen_x,
|
||||
members.screen_y,
|
||||
members.client_x,
|
||||
members.client_y,
|
||||
members.client_x,
|
||||
members.client_y,
|
||||
members.client_x,
|
||||
members.client_y,
|
||||
members.button,
|
||||
members.buttons,
|
||||
members.movement_x,
|
||||
members.movement_y,
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("MouseEvent state should initialize");
|
||||
let related_target = members
|
||||
.related_target
|
||||
.map(|target| target.0.into())
|
||||
.unwrap_or_else(|| v8::null(scope).into());
|
||||
MouseEventRelatedTargetDeclaration::new(related_target)
|
||||
.initialize(scope, event)
|
||||
.expect("MouseEvent relatedTarget should initialize");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "WheelEventInit")]
|
||||
struct WheelEventInitMembers {
|
||||
#[webidl(default = 0)]
|
||||
delta_mode: u32,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
delta_x: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
delta_y: f64,
|
||||
#[webidl(default = 0.0, converter = "double")]
|
||||
delta_z: f64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct WheelEventInit<'s> {
|
||||
mouse: MouseEventInit<'s>,
|
||||
members: WheelEventInitMembers,
|
||||
}
|
||||
|
||||
impl<'s> webidl::WebIdlDictionary<'s> for WheelEventInit<'s> {
|
||||
fn parse_dictionary(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
Ok(Self {
|
||||
mouse: webidl::parse_dictionary_object(scope, object)?,
|
||||
members: webidl::parse_dictionary_object(scope, object)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> WheelEventInit<'s> {
|
||||
pub(super) fn event_flags(&self) -> (bool, bool, bool) {
|
||||
self.mouse.event_flags()
|
||||
}
|
||||
|
||||
pub(super) fn initialize(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
self.mouse.initialize(scope, event);
|
||||
WheelEventDeltaInitDeclaration::new(
|
||||
self.members.delta_x,
|
||||
self.members.delta_y,
|
||||
self.members.delta_z,
|
||||
self.members.delta_mode,
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("WheelEvent state should initialize");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "PointerEventInit")]
|
||||
struct PointerEventInitMembers<'s> {
|
||||
#[webidl(converter = "double")]
|
||||
altitude_angle: Option<f64>,
|
||||
#[webidl(converter = "double")]
|
||||
azimuth_angle: Option<f64>,
|
||||
#[webidl(sequence, interface = web_api_interfaces::PointerEvent, default = Vec::new())]
|
||||
coalesced_events: Vec<v8::Local<'s, v8::Object>>,
|
||||
#[webidl(default = 1.0, converter = "double")]
|
||||
height: f64,
|
||||
#[webidl(default = false)]
|
||||
is_primary: bool,
|
||||
#[webidl(default = 0)]
|
||||
persistent_device_id: i32,
|
||||
#[webidl(default = 0)]
|
||||
pointer_id: i32,
|
||||
#[webidl(default = webidl::DomString16(Vec::new()), converter = "raw")]
|
||||
pointer_type: webidl::DomString16,
|
||||
#[webidl(sequence, interface = web_api_interfaces::PointerEvent, default = Vec::new())]
|
||||
predicted_events: Vec<v8::Local<'s, v8::Object>>,
|
||||
#[webidl(default = 0.0)]
|
||||
pressure: f32,
|
||||
#[webidl(default = 0.0)]
|
||||
tangential_pressure: f32,
|
||||
tilt_x: Option<i32>,
|
||||
tilt_y: Option<i32>,
|
||||
#[webidl(default = 0)]
|
||||
twist: i32,
|
||||
#[webidl(default = 1.0, converter = "double")]
|
||||
width: f64,
|
||||
}
|
||||
|
||||
impl Default for PointerEventInitMembers<'_> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
altitude_angle: None,
|
||||
azimuth_angle: None,
|
||||
coalesced_events: Vec::new(),
|
||||
height: 1.0,
|
||||
is_primary: false,
|
||||
persistent_device_id: 0,
|
||||
pointer_id: 0,
|
||||
pointer_type: webidl::DomString16(Vec::new()),
|
||||
predicted_events: Vec::new(),
|
||||
pressure: 0.0,
|
||||
tangential_pressure: 0.0,
|
||||
tilt_x: None,
|
||||
tilt_y: None,
|
||||
twist: 0,
|
||||
width: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct PointerEventInit<'s> {
|
||||
mouse: MouseEventInit<'s>,
|
||||
members: PointerEventInitMembers<'s>,
|
||||
}
|
||||
|
||||
impl<'s> webidl::WebIdlDictionary<'s> for PointerEventInit<'s> {
|
||||
fn parse_dictionary(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
Ok(Self {
|
||||
mouse: webidl::parse_dictionary_object(scope, object)?,
|
||||
members: webidl::parse_dictionary_object(scope, object)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> PointerEventInit<'s> {
|
||||
pub(super) fn event_flags(&self) -> (bool, bool, bool) {
|
||||
self.mouse.event_flags()
|
||||
}
|
||||
|
||||
pub(super) fn initialize(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
self.mouse.initialize(scope, event);
|
||||
let angles = pointer_event_angle_init(&self.members);
|
||||
let members = self.members;
|
||||
PointerEventNumberInitDeclaration::new(
|
||||
members.pointer_id,
|
||||
members.width,
|
||||
members.height,
|
||||
members.pressure,
|
||||
members.tangential_pressure,
|
||||
angles.tilt_x,
|
||||
angles.tilt_y,
|
||||
angles.azimuth_angle,
|
||||
angles.altitude_angle,
|
||||
members.twist,
|
||||
members.persistent_device_id,
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("PointerEvent state should initialize");
|
||||
let pointer_type = crate::util::v8_string_from_utf16_units(scope, &members.pointer_type.0)
|
||||
.expect("PointerEvent pointerType should fit in a V8 string");
|
||||
PointerEventTailInitDeclaration::new(members.is_primary, pointer_type)
|
||||
.initialize(scope, event)
|
||||
.expect("PointerEvent tail should initialize");
|
||||
store_pointer_event_sequence(
|
||||
scope,
|
||||
event,
|
||||
POINTER_EVENT_COALESCED_EVENTS_SLOT,
|
||||
&members.coalesced_events,
|
||||
);
|
||||
store_pointer_event_sequence(
|
||||
scope,
|
||||
event,
|
||||
POINTER_EVENT_PREDICTED_EVENTS_SLOT,
|
||||
&members.predicted_events,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct DataTransferReference<'s>(v8::Local<'s, v8::Object>);
|
||||
|
||||
impl<'s> webidl::WebIdlConverter<'s> for DataTransferReference<'s> {
|
||||
type Options = ();
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
context: webidl::Context,
|
||||
_options: &Self::Options,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
let object = webidl::convert::<v8::Local<'s, v8::Object>>(scope, value, context)?;
|
||||
if !is_branded_data_transfer_object(scope, object) {
|
||||
return Err(webidl::WebIdlError::cannot_convert(context, "DataTransfer"));
|
||||
}
|
||||
Ok(Self(object))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "DragEventInit")]
|
||||
struct DragEventInitMembers<'s> {
|
||||
#[webidl(nullable, converter = "raw")]
|
||||
data_transfer: Option<DataTransferReference<'s>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct DragEventInit<'s> {
|
||||
mouse: MouseEventInit<'s>,
|
||||
members: DragEventInitMembers<'s>,
|
||||
}
|
||||
|
||||
impl<'s> webidl::WebIdlDictionary<'s> for DragEventInit<'s> {
|
||||
fn parse_dictionary(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
Ok(Self {
|
||||
mouse: webidl::parse_dictionary_object(scope, object)?,
|
||||
members: webidl::parse_dictionary_object(scope, object)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> DragEventInit<'s> {
|
||||
pub(super) fn event_flags(&self) -> (bool, bool, bool) {
|
||||
self.mouse.event_flags()
|
||||
}
|
||||
|
||||
pub(super) fn initialize(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
self.mouse.initialize(scope, event);
|
||||
let data_transfer = self
|
||||
.members
|
||||
.data_transfer
|
||||
.map(|data| data.0.into())
|
||||
.unwrap_or_else(|| v8::null(scope).into());
|
||||
DragEventInitDeclaration::new(data_transfer)
|
||||
.initialize(scope, event)
|
||||
.expect("DragEvent state should initialize");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain, data_properties, enumerable)]
|
||||
struct MouseEventBaseInitDeclaration<'scope> {
|
||||
view: v8::Local<'scope, v8::Value>,
|
||||
detail: f64,
|
||||
struct DragEventInitDeclaration<'s> {
|
||||
data_transfer: v8::Local<'s, v8::Value>,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain, data_properties, enumerable)]
|
||||
struct MouseEventBaseInitDeclaration {
|
||||
screen_x: f64,
|
||||
screen_y: f64,
|
||||
client_x: f64,
|
||||
@@ -35,8 +369,8 @@ struct MouseEventBaseInitDeclaration<'scope> {
|
||||
y: f64,
|
||||
page_x: f64,
|
||||
page_y: f64,
|
||||
button: f64,
|
||||
buttons: f64,
|
||||
button: i16,
|
||||
buttons: u16,
|
||||
movement_x: f64,
|
||||
movement_y: f64,
|
||||
}
|
||||
@@ -53,22 +387,23 @@ struct WheelEventDeltaInitDeclaration {
|
||||
delta_x: f64,
|
||||
delta_y: f64,
|
||||
delta_z: f64,
|
||||
delta_mode: f64,
|
||||
delta_mode: u32,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
#[webapi(plain, data_properties, enumerable)]
|
||||
struct PointerEventNumberInitDeclaration {
|
||||
pointer_id: f64,
|
||||
pointer_id: i32,
|
||||
width: f64,
|
||||
height: f64,
|
||||
pressure: f64,
|
||||
tangential_pressure: f64,
|
||||
tilt_x: f64,
|
||||
tilt_y: f64,
|
||||
pressure: f32,
|
||||
tangential_pressure: f32,
|
||||
tilt_x: i32,
|
||||
tilt_y: i32,
|
||||
azimuth_angle: f64,
|
||||
altitude_angle: f64,
|
||||
twist: f64,
|
||||
twist: i32,
|
||||
persistent_device_id: i32,
|
||||
}
|
||||
|
||||
#[derive(WebApiObject)]
|
||||
@@ -86,17 +421,6 @@ struct PointerEventAngleInit {
|
||||
altitude_angle: f64,
|
||||
}
|
||||
|
||||
impl Default for PointerEventAngleInit {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tilt_x: 0,
|
||||
tilt_y: 0,
|
||||
azimuth_angle: 0.0,
|
||||
altitude_angle: std::f64::consts::FRAC_PI_2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_tilt_degrees(tilt_degrees: i32) -> i32 {
|
||||
if (-90..=90).contains(&tilt_degrees) {
|
||||
return tilt_degrees;
|
||||
@@ -211,41 +535,11 @@ fn tilt_y_from_spherical(azimuth_angle: f64, altitude_angle: f64) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_event_angle_init<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> Result<PointerEventAngleInit, webidl::WebIdlError> {
|
||||
let Some(init) = init else {
|
||||
return Ok(PointerEventAngleInit::default());
|
||||
};
|
||||
let tilt_x = webidl::optional_member::<webidl::Long>(
|
||||
scope,
|
||||
init,
|
||||
"tiltX",
|
||||
webidl::Context::member("PointerEventInit", "tiltX"),
|
||||
)?
|
||||
.map(|value| value.0);
|
||||
let tilt_y = webidl::optional_member::<webidl::Long>(
|
||||
scope,
|
||||
init,
|
||||
"tiltY",
|
||||
webidl::Context::member("PointerEventInit", "tiltY"),
|
||||
)?
|
||||
.map(|value| value.0);
|
||||
let azimuth_angle = webidl::optional_member::<webidl::Double>(
|
||||
scope,
|
||||
init,
|
||||
"azimuthAngle",
|
||||
webidl::Context::member("PointerEventInit", "azimuthAngle"),
|
||||
)?
|
||||
.map(|value| value.0);
|
||||
let altitude_angle = webidl::optional_member::<webidl::Double>(
|
||||
scope,
|
||||
init,
|
||||
"altitudeAngle",
|
||||
webidl::Context::member("PointerEventInit", "altitudeAngle"),
|
||||
)?
|
||||
.map(|value| value.0);
|
||||
fn pointer_event_angle_init(init: &PointerEventInitMembers<'_>) -> PointerEventAngleInit {
|
||||
let tilt_x = init.tilt_x;
|
||||
let tilt_y = init.tilt_y;
|
||||
let azimuth_angle = init.azimuth_angle;
|
||||
let altitude_angle = init.altitude_angle;
|
||||
|
||||
let has_tilt = tilt_x.is_some() || tilt_y.is_some();
|
||||
let has_spherical_angles = azimuth_angle.is_some() || altitude_angle.is_some();
|
||||
@@ -267,7 +561,7 @@ fn pointer_event_angle_init<'s>(
|
||||
angles.tilt_x = tilt_x_from_spherical(normalized_azimuth, normalized_altitude);
|
||||
angles.tilt_y = tilt_y_from_spherical(normalized_azimuth, normalized_altitude);
|
||||
}
|
||||
Ok(angles)
|
||||
angles
|
||||
}
|
||||
|
||||
fn store_pointer_event_sequence<'s>(
|
||||
@@ -305,17 +599,7 @@ fn pointer_event_sequence_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
slot: &'static str,
|
||||
method: &'static str,
|
||||
) {
|
||||
if super::super::event_subclass_kind(scope, args.this())
|
||||
!= Some(EventSubclassKind::PointerEvent)
|
||||
{
|
||||
throw_type_error(
|
||||
scope,
|
||||
&format!("Failed to execute '{method}' on 'PointerEvent': Illegal invocation"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
rv.set(pointer_event_sequence_copy(scope, args.this(), slot).into());
|
||||
}
|
||||
|
||||
@@ -324,13 +608,7 @@ pub(in crate::context_bootstrap) fn pointer_event_get_coalesced_events_callback<
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
pointer_event_sequence_callback(
|
||||
scope,
|
||||
args,
|
||||
rv,
|
||||
POINTER_EVENT_COALESCED_EVENTS_SLOT,
|
||||
"getCoalescedEvents",
|
||||
);
|
||||
pointer_event_sequence_callback(scope, args, rv, POINTER_EVENT_COALESCED_EVENTS_SLOT);
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn pointer_event_get_predicted_events_callback<'s>(
|
||||
@@ -338,158 +616,5 @@ pub(in crate::context_bootstrap) fn pointer_event_get_predicted_events_callback<
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
pointer_event_sequence_callback(
|
||||
scope,
|
||||
args,
|
||||
rv,
|
||||
POINTER_EVENT_PREDICTED_EVENTS_SLOT,
|
||||
"getPredictedEvents",
|
||||
);
|
||||
}
|
||||
|
||||
fn define_mouse_event_base_fields<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
constructor_name: &'static str,
|
||||
) -> Option<v8::Local<'s, v8::Value>> {
|
||||
let Ok(view) = init_window_view_property(scope, init, constructor_name) else {
|
||||
return None;
|
||||
};
|
||||
let detail = init_number_property(scope, init, "detail", 0.0);
|
||||
if !super::super::modifiers::initialize_event_modifiers(scope, event, init) {
|
||||
return None;
|
||||
}
|
||||
let client_x = init_number_property(scope, init, "clientX", 0.0);
|
||||
let client_y = init_number_property(scope, init, "clientY", 0.0);
|
||||
let related_target =
|
||||
init_value_property(scope, init, "relatedTarget").unwrap_or_else(|| v8::null(scope).into());
|
||||
|
||||
MouseEventBaseInitDeclaration::new(
|
||||
view,
|
||||
detail,
|
||||
init_number_property(scope, init, "screenX", 0.0),
|
||||
init_number_property(scope, init, "screenY", 0.0),
|
||||
client_x,
|
||||
client_y,
|
||||
client_x,
|
||||
client_y,
|
||||
client_x,
|
||||
client_y,
|
||||
init_number_property(scope, init, "button", 0.0),
|
||||
init_number_property(scope, init, "buttons", 0.0),
|
||||
init_number_property(scope, init, "movementX", 0.0),
|
||||
init_number_property(scope, init, "movementY", 0.0),
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("MouseEvent base init declaration should initialize");
|
||||
Some(related_target)
|
||||
}
|
||||
|
||||
fn define_mouse_event_related_target<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
related_target: v8::Local<'s, v8::Value>,
|
||||
) {
|
||||
MouseEventRelatedTargetDeclaration::new(related_target)
|
||||
.initialize(scope, event)
|
||||
.expect("MouseEvent relatedTarget declaration should initialize");
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap::events::subclasses) fn initialize_mouse_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> bool {
|
||||
let Some(related_target) = define_mouse_event_base_fields(scope, event, init, "MouseEvent")
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
define_mouse_event_related_target(scope, event, related_target);
|
||||
true
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap::events::subclasses) fn initialize_wheel_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> bool {
|
||||
let Some(related_target) = define_mouse_event_base_fields(scope, event, init, "WheelEvent")
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
WheelEventDeltaInitDeclaration::new(
|
||||
init_number_property(scope, init, "deltaX", 0.0),
|
||||
init_number_property(scope, init, "deltaY", 0.0),
|
||||
init_number_property(scope, init, "deltaZ", 0.0),
|
||||
init_number_property(scope, init, "deltaMode", 0.0),
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("WheelEvent delta init declaration should initialize");
|
||||
define_mouse_event_related_target(scope, event, related_target);
|
||||
true
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap::events::subclasses) fn initialize_pointer_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
init: Option<v8::Local<'s, v8::Object>>,
|
||||
) -> bool {
|
||||
let pointer_type = init_string_property(scope, init, "pointerType", "");
|
||||
let pointer_type = v8_string(scope, &pointer_type).expect("PointerEvent pointerType");
|
||||
let sequences = match init
|
||||
.map(|init| webidl::parse_dictionary_object::<PointerEventSequences>(scope, init))
|
||||
.transpose()
|
||||
{
|
||||
Ok(sequences) => sequences.unwrap_or_default(),
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let Some(related_target) = define_mouse_event_base_fields(scope, event, init, "PointerEvent")
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let angles = match pointer_event_angle_init(scope, init) {
|
||||
Ok(angles) => angles,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
PointerEventNumberInitDeclaration::new(
|
||||
init_number_property(scope, init, "pointerId", 0.0),
|
||||
init_number_property(scope, init, "width", 1.0),
|
||||
init_number_property(scope, init, "height", 1.0),
|
||||
init_number_property(scope, init, "pressure", 0.0),
|
||||
init_number_property(scope, init, "tangentialPressure", 0.0),
|
||||
f64::from(angles.tilt_x),
|
||||
f64::from(angles.tilt_y),
|
||||
angles.azimuth_angle,
|
||||
angles.altitude_angle,
|
||||
init_number_property(scope, init, "twist", 0.0),
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("PointerEvent number init declaration should initialize");
|
||||
PointerEventTailInitDeclaration::new(
|
||||
init_bool_property(scope, init, "isPrimary", false),
|
||||
pointer_type,
|
||||
)
|
||||
.initialize(scope, event)
|
||||
.expect("PointerEvent tail init declaration should initialize");
|
||||
define_mouse_event_related_target(scope, event, related_target);
|
||||
store_pointer_event_sequence(
|
||||
scope,
|
||||
event,
|
||||
POINTER_EVENT_COALESCED_EVENTS_SLOT,
|
||||
&sequences.coalesced_events,
|
||||
);
|
||||
store_pointer_event_sequence(
|
||||
scope,
|
||||
event,
|
||||
POINTER_EVENT_PREDICTED_EVENTS_SLOT,
|
||||
&sequences.predicted_events,
|
||||
);
|
||||
true
|
||||
pointer_event_sequence_callback(scope, args, rv, POINTER_EVENT_PREDICTED_EVENTS_SLOT);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ pub(super) fn parse_composition_event_init<'s>(
|
||||
parse_ui_dictionary(scope, args, "CompositionEvent")
|
||||
}
|
||||
|
||||
fn parse_ui_dictionary<'s, T: Default + webidl::WebIdlDictionary<'s>>(
|
||||
pub(super) fn parse_ui_dictionary<'s, T: Default + webidl::WebIdlDictionary<'s>>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: &v8::FunctionCallbackArguments<'s>,
|
||||
constructor: &'static str,
|
||||
|
||||
@@ -54,6 +54,7 @@ mod message_event_init;
|
||||
mod message_port_events;
|
||||
mod message_port_lifecycle;
|
||||
mod misc;
|
||||
mod mouse_event_init;
|
||||
mod native_event_construction;
|
||||
mod navigation;
|
||||
mod navigation_recursion;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
globalThis.__mousePointerProbe = realms => {
|
||||
const rows = [], errors = [], unexecuted = [];
|
||||
const record = (label, checks, observed = null) => rows.push({label, checks, observed});
|
||||
const run = (label, work) => { try { work(); } catch (error) { errors.push({label, error: String(error.stack || error)}); } };
|
||||
const eq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
||||
const units = s => Array.from({length:s.length}, (_,i)=>s.charCodeAt(i));
|
||||
const integer = (n,bits,signed) => { n=Number(n); if(!Number.isFinite(n)||n===0)return 0; const m=2**bits; n=((Math.trunc(n)%m)+m)%m; return signed && n>=m/2?n-m:n; };
|
||||
const base = ['bubbles','cancelable','composed','detail','view','which'];
|
||||
const modifiers = ['altKey','ctrlKey','metaKey','modifierAltGraph','modifierCapsLock','modifierFn','modifierFnLock','modifierHyper','modifierNumLock','modifierScrollLock','modifierSuper','modifierSymbol','modifierSymbolLock','shiftKey'];
|
||||
const mouse = ['button','buttons','clientX','clientY','movementX','movementY','relatedTarget','screenX','screenY'];
|
||||
const pointer = ['altitudeAngle','azimuthAngle','coalescedEvents','height','isPrimary','persistentDeviceId','pointerId','pointerType','predictedEvents','pressure','tangentialPressure','tiltX','tiltY','twist','width'];
|
||||
const wheel = ['deltaMode','deltaX','deltaY','deltaZ'];
|
||||
const mouseFields = ['screenX','screenY','clientX','clientY','button','buttons','ctrlKey','altKey','shiftKey','metaKey','movementX','movementY','pageX','pageY','x','y','relatedTarget'];
|
||||
const pointerFields = pointer.filter(k=>!k.endsWith('Events'));
|
||||
for (const [r,realm] of realms.entries()) for (const name of ['MouseEvent','WheelEvent','PointerEvent','DragEvent']) {
|
||||
const C=realm[name], prefix=`${r}/${name}`, own=name==='MouseEvent'?mouseFields:name==='WheelEvent'?wheel:name==='PointerEvent'?pointerFields:['dataTransfer'];
|
||||
const keys=[...base,...modifiers,...mouse,...(name==='WheelEvent'?wheel:name==='PointerEvent'?pointer:name==='DragEvent'?['dataTransfer']:[])];
|
||||
run(prefix+'/metadata',()=>{
|
||||
const e=new C('x');
|
||||
record(prefix+'/constructor',{name:C.name===name,length:C.length===1,parent:Object.getPrototypeOf(C.prototype)===(name==='MouseEvent'?realm.UIEvent:realm.MouseEvent).prototype,instance:e instanceof realm.MouseEvent});
|
||||
for(const field of own) {
|
||||
const d=Object.getOwnPropertyDescriptor(C.prototype,field);
|
||||
record(prefix+'/descriptor-'+field,{getter:typeof d?.get==='function',name:d?.get?.name==='get '+field,length:d?.get?.length===0,enumerable:d?.enumerable===true,configurable:d?.configurable===true,setter:d?.set===undefined,own:!Object.hasOwn(e,field)});
|
||||
}
|
||||
});
|
||||
for(const [i,init] of [undefined,null,{},[],function(){},new Date(0)].entries())run(prefix+'/defaults-'+i,()=>{
|
||||
const e=new C('x',init);
|
||||
record(prefix+'/defaults-'+i,{flags:!e.bubbles&&!e.cancelable&&!e.composed,ui:e.view===null&&e.detail===0&&e.which===0,mouse:mouseFields.filter(k=>k!=='relatedTarget').every(k=>e[k]===(k.endsWith('Key')?false:0)),related:e.relatedTarget===null,derived:name==='WheelEvent'?wheel.every(k=>e[k]===0):name==='PointerEvent'?e.pointerId===0&&e.width===1&&e.height===1&&e.pressure===0&&e.tangentialPressure===0&&e.tiltX===0&&e.tiltY===0&&e.twist===0&&e.altitudeAngle===Math.PI/2&&e.azimuthAngle===0&&e.pointerType===''&&!e.isPrimary&&e.persistentDeviceId===0:name==='DragEvent'?e.dataTransfer===null:true});
|
||||
});
|
||||
for(const [i,init] of [0,1,'',true,Symbol('init'),1n].entries())run(prefix+'/dictionary-'+i,()=>{
|
||||
let error;try{new C('x',init);}catch(e){error=e;}record(prefix+'/dictionary-'+i,{typeError:error instanceof realm.TypeError},error?.name||'returned');
|
||||
});
|
||||
run(prefix+'/order',()=>{
|
||||
const trace=[];new C('x',new Proxy({}, {get(_,key){trace.push(key);}}));record(prefix+'/order',{order:eq(trace,keys)},trace);
|
||||
for(const [i,key] of keys.entries()){
|
||||
const sentinel={},reads=[];let error;try{new C('x',new Proxy({}, {get(_,k){reads.push(k);if(k===key)throw sentinel;}}));}catch(e){error=e;}
|
||||
record(prefix+'/throw-get-'+key,{identity:error===sentinel,order:eq(reads,keys.slice(0,i+1))},reads);
|
||||
}
|
||||
const trace2=[];const type={[Symbol.toPrimitive](hint){trace2.push('type:'+hint);return '\uD800';}};
|
||||
const e=new C(type,new Proxy({}, {get(_,key){trace2.push(key);}}));record(prefix+'/type-first',{type:e.type==='\uD800',order:eq(trace2,['type:string',...keys])},trace2);
|
||||
const sentinel={},reads=[];let error;try{new C({[Symbol.toPrimitive](){throw sentinel;}},new Proxy({}, {get(_,k){reads.push(k);}}));}catch(e){error=e;}record(prefix+'/type-throw',{identity:error===sentinel,noReads:reads.length===0});
|
||||
});
|
||||
const numeric={detail:['int',32,true,0],which:['int',32,false,0],button:['int',16,true,0],buttons:['int',16,false,0]};
|
||||
for(const k of ['screenX','screenY','clientX','clientY','movementX','movementY'])numeric[k]=['double',0,false,0];
|
||||
if(name==='WheelEvent'){numeric.deltaMode=['int',32,false,0];for(const k of ['deltaX','deltaY','deltaZ'])numeric[k]=['double',0,false,0];}
|
||||
if(name==='PointerEvent'){
|
||||
for(const k of ['pointerId','persistentDeviceId','tiltX','tiltY','twist'])numeric[k]=['int',32,true,0];
|
||||
for(const k of ['width','height'])numeric[k]=['double',0,false,1];
|
||||
for(const k of ['pressure','tangentialPressure'])numeric[k]=['float',0,false,0];
|
||||
numeric.altitudeAngle=['double',0,false,Math.PI/2];numeric.azimuthAngle=['double',0,false,0];
|
||||
}
|
||||
const values=[undefined,null,0,-0,-1,-3.5,3.5,65535,65536,2147483648,4294967295,4294967297,NaN,Infinity,-Infinity,true,false,'3.5','NaN',1e40];
|
||||
for(const [field,[kind,bits,signed,def]] of Object.entries(numeric)){
|
||||
for(const [i,value] of (['altitudeAngle','azimuthAngle'].includes(field)?[undefined,null,0,-0,-1,-3.5,3.5,NaN,Infinity,-Infinity,true,false,'3.5','NaN',Math.PI/4]:values).entries())run(prefix+'/number-'+field+'-'+i,()=>{
|
||||
const n=value===undefined?def:Number(value),expected=kind==='int'?integer(n,bits,signed):kind==='float'?Math.fround(n):n;
|
||||
const valid=kind==='int'||Number.isFinite(expected);let e,error;try{e=new C('x',{[field]:value});}catch(caught){error=caught;}
|
||||
record(prefix+'/number-'+field+'-'+i,valid?{value:Object.is(e?.[field],expected)}:{typeError:error instanceof realm.TypeError},{actual:e?.[field],expected:String(expected),error:error?.name});
|
||||
});
|
||||
for(const [i,value] of [Symbol('n'),1n].entries())run(prefix+'/invalid-number-'+field+'-'+i,()=>{let error;try{new C('x',{[field]:value});}catch(e){error=e;}record(prefix+'/invalid-number-'+field+'-'+i,{typeError:error instanceof realm.TypeError},error?.name||'returned');});
|
||||
run(prefix+'/number-hint-'+field,()=>{
|
||||
const trace=[],v={[Symbol.toPrimitive](hint){trace.push(hint);return 3.5;}};const e=new C('x',{[field]:v});
|
||||
record(prefix+'/number-hint-'+field,{hint:eq(trace,['number']),value:e[field]===(kind==='int'?3:3.5)},trace);
|
||||
const sentinel={},reads=[];let error;try{new C('x',new Proxy({}, {get(_,key){reads.push(key);return key===field?{[Symbol.toPrimitive](){throw sentinel;}}:undefined;}}));}catch(e){error=e;}
|
||||
record(prefix+'/throw-convert-'+field,{identity:error===sentinel,order:eq(reads,keys.slice(0,keys.indexOf(field)+1))},reads);
|
||||
});
|
||||
}
|
||||
for(const [i,key] of modifiers.entries())run(prefix+'/modifier-'+key,()=>{
|
||||
const e=new C('x',{[key]:{}}),names=['Alt','Control','Meta','AltGraph','CapsLock','Fn','FnLock','Hyper','NumLock','ScrollLock','Super','Symbol','SymbolLock','Shift'];
|
||||
record(prefix+'/modifier-'+key,{state:e.getModifierState(names[i]),others:names.filter((_,j)=>i!==j).every(k=>!e.getModifierState(k))});
|
||||
});
|
||||
for(const [i,value] of [null,undefined,realm.document,realm.document.createElement('div'),new realm.EventTarget(),realms[(r+1)%realms.length].document,realm.document.implementation.createHTMLDocument('').createElement('select')].entries())run(prefix+'/related-valid-'+i,()=>{
|
||||
const e=new C('x',{relatedTarget:value});record(prefix+'/related-valid-'+i,{identity:e.relatedTarget===(value??null)});
|
||||
});
|
||||
const bad=[{},Object.create(realm.EventTarget.prototype),Object.create(realm.document),new Proxy(realm.document,{}),new Proxy(new realm.EventTarget(),{})];
|
||||
const revoked=Proxy.revocable(realm.document,{});revoked.revoke();bad.push(revoked.proxy,1,'x',Symbol('bad'));
|
||||
for(const [i,value] of bad.entries())run(prefix+'/related-invalid-'+i,()=>{let error;try{new C('x',{relatedTarget:value});}catch(e){error=e;}record(prefix+'/related-invalid-'+i,{typeError:error instanceof realm.TypeError},error?.name||'returned');});
|
||||
run(prefix+'/related-no-traps',()=>{let traps=0,error;const p=new Proxy(realm.document,{get(){traps++;},getPrototypeOf(){traps++;}});try{new C('x',{relatedTarget:p});}catch(e){error=e;}record(prefix+'/related-no-traps',{typeError:error instanceof realm.TypeError,noTraps:traps===0},traps);});
|
||||
run(prefix+'/flags-aliases',()=>{
|
||||
for(let n=0;n<8;n++){
|
||||
const e=new C('x',{bubbles:!!(n&1),cancelable:!!(n&2),composed:!!(n&4),clientX:31,clientY:42,view:realm});
|
||||
record(prefix+'/flags-'+n,{bubbles:e.bubbles===!!(n&1),cancelable:e.cancelable===!!(n&2),composed:e.composed===!!(n&4),view:e.view===realm,aliases:e.x===31&&e.pageX===31&&e.y===42&&e.pageY===42&&e.offsetX===31&&e.offsetY===42});
|
||||
}
|
||||
const e=new C('x');realm.document.body.dispatchEvent(e);record(prefix+'/dispatch',{target:e.target===realm.document.body,phase:e.eventPhase===0,untrusted:!e.isTrusted});
|
||||
});
|
||||
for(const field of own)run(prefix+'/receiver-'+field,()=>{
|
||||
const getter=Object.getOwnPropertyDescriptor(C.prototype,field)?.get;
|
||||
if(typeof getter!=='function'){
|
||||
unexecuted.push({label:prefix+'/receiver-'+field,checks:12,reason:'missing prototype getter'});return;
|
||||
}
|
||||
const e=new C('x'),other=new realms[(r+1)%realms.length][name]('x');
|
||||
record(prefix+'/genuine-'+field,{same:Object.is(getter.call(e),e[field]),other:Object.is(getter.call(other),other[field])});
|
||||
const bad=[{},Object.create(C.prototype),Object.create(e),new Proxy(e,{})];let revoked=Proxy.revocable(e,{});revoked.revoke();bad.push(revoked.proxy);
|
||||
for(const [i,receiver] of bad.entries()){let error;try{getter.call(receiver);}catch(e){error=e;}record(prefix+'/receiver-'+field+'-'+i,{typeError:error instanceof realm.TypeError,noWrongRealm:r===0||!(error instanceof realms[0].TypeError)},error?.name||'returned');}
|
||||
});
|
||||
if(name==='PointerEvent')run(prefix+'/pointer',()=>{
|
||||
for(const [i,value] of [undefined,null,'pen','\uD800','\uDC00','x\uD800y','\uD83D\uDE80',1n,true,['a','b']].entries()){
|
||||
const actual=new C('x',{pointerType:value}).pointerType;record(prefix+'/pointerType-'+i,{value:actual===(value===undefined?'':String(value))},units(actual));
|
||||
}
|
||||
const trace=[],input={[Symbol.toPrimitive](hint){trace.push(hint);return '\uD800';}},actual=new C('x',{pointerType:input}).pointerType;record(prefix+'/pointerType-hint',{hint:eq(trace,['string']),value:actual==='\uD800'},units(actual));
|
||||
let error;try{new C('x',{pointerType:Symbol('x')});}catch(e){error=e;}record(prefix+'/pointerType-symbol',{typeError:error instanceof realm.TypeError});
|
||||
for(const key of ['coalescedEvents','predictedEvents']){
|
||||
const a=new C('a'),b=new realms[(r+1)%realms.length].PointerEvent('b'),list=[a,b],e=new C('x',{[key]:list}),method=key==='coalescedEvents'?'getCoalescedEvents':'getPredictedEvents';
|
||||
if(typeof e[method]!=='function' && !realm.isSecureContext && method==='getCoalescedEvents'){record(prefix+'/'+key+'-secure',{absent:true});continue;} if(typeof e[method]!=='function'){unexecuted.push({label:prefix+'/'+key+'-copy',checks:5,reason:'missing method in secure context'});continue;}
|
||||
list.length=0;const first=e[method](),second=e[method]();first.pop();record(prefix+'/'+key+'-copy',{copied:second.length===2,fresh:first!==second,identity:second[0]===a&&second[1]===b,stable:e[method]().length===2,array:second instanceof realm.Array});
|
||||
for(const [i,value] of [null,1,{},[{}],[new realm.MouseEvent('x')],[new Proxy(a,{})],[Object.create(a)],[Object.create(C.prototype)]].entries()){
|
||||
let error;try{new C('x',{[key]:value});}catch(e){error=e;}record(prefix+'/'+key+'-invalid-'+i,{typeError:error instanceof realm.TypeError},error?.name||'returned');
|
||||
}
|
||||
const sentinel={},trace=[];let error;try{new C('x',{[key]:{[Symbol.iterator](){trace.push('iterator');return{next(){trace.push('next');throw sentinel;}};}}});}catch(e){error=e;}record(prefix+'/'+key+'-iterator-throw',{identity:error===sentinel,trace:eq(trace,['iterator','next'])},trace);
|
||||
const fn=C.prototype[method];for(const [i,receiver] of [{},new realm.MouseEvent('x'),new Proxy(e,{})].entries()){let error;try{fn.call(receiver);}catch(e){error=e;}record(prefix+'/'+method+'-receiver-'+i,{typeError:error instanceof realm.TypeError});}
|
||||
}
|
||||
const supplied=new C('x',{tiltX:45,azimuthAngle:Math.PI/4}),derived=new C('x',{tiltY:45});record(prefix+'/angle-presence',{retained:supplied.tiltX===45&&supplied.azimuthAngle===Math.PI/4&&supplied.altitudeAngle===Math.PI/2,derived:derived.tiltX===0&&derived.tiltY===45&&derived.azimuthAngle===Math.PI/2&&Math.abs(derived.altitudeAngle-Math.PI/4)<1e-14});
|
||||
});
|
||||
if(name==='DragEvent')run(prefix+'/drag',()=>{
|
||||
const dt=new realm.DataTransfer(),e=new C('x',{dataTransfer:dt});record(prefix+'/dataTransfer-native',{identity:e.dataTransfer===dt});
|
||||
for(const [i,value] of [{},Object.create(realm.DataTransfer.prototype),Object.create(dt),new Proxy(dt,{})].entries()){let error;try{new C('x',{dataTransfer:value});}catch(e){error=e;}record(prefix+'/dataTransfer-invalid-'+i,{typeError:error instanceof realm.TypeError});}
|
||||
});
|
||||
run(prefix+'/subclass',()=>{class Sub extends C{}const e=new Sub('x',{button:65535,buttons:-1});record(prefix+'/subclass',{identity:e instanceof Sub&&e instanceof C,conversion:e.button===-1&&e.buttons===65535});let missing,call;try{new C();}catch(e){missing=e;}try{C('x');}catch(e){call=e;}record(prefix+'/required',{missing:missing instanceof realm.TypeError,call:call instanceof realm.TypeError});});
|
||||
}
|
||||
const executed=rows.reduce((n,r)=>n+Object.keys(r.checks).length,0),passed=rows.reduce((n,r)=>n+Object.values(r.checks).filter(Boolean).length,0),skipped=unexecuted.reduce((n,r)=>n+r.checks,0);
|
||||
return {total:executed+skipped,executed,passed,failed:executed-passed,unexecutedChecks:skipped,unexecuted,errors,rows,complete:errors.length===0&&skipped===0&&passed===executed};
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn mouse_event_dictionaries_and_prototypes_share_native_conversion_and_brands() {
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
let mut vm = new_storage_page_task_executor_test_vm_with_loader(
|
||||
"https://mouse-event-init.test/",
|
||||
&loader,
|
||||
);
|
||||
vm.eval(
|
||||
r#"
|
||||
if (!document.documentElement) document.appendChild(document.createElement('html'));
|
||||
if (!document.body) document.documentElement.appendChild(document.createElement('body'));
|
||||
const frame = document.body.appendChild(document.createElement('iframe'));
|
||||
frame.id = 'child'; frame.srcdoc = '<head></head><body></body>'; void frame.contentWindow;
|
||||
'ready'
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
vm.run_one_child_frame_task_executor_turn(
|
||||
ChildFrameSemanticTurnKind::RealmMaterialization,
|
||||
&loader
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
);
|
||||
vm.drain_ready_page_task_executor_turns_for_setup(&loader, 128)
|
||||
.await
|
||||
.unwrap();
|
||||
let source = format!(
|
||||
"{}; globalThis.__uiEventResults = __mousePointerProbe([globalThis, document.getElementById('child').contentWindow]); __uiEventResults.complete",
|
||||
include_str!("mouse_event_init.js"),
|
||||
);
|
||||
assert_eq!(vm.eval(&source).unwrap(), "true", "{}",
|
||||
vm.eval("JSON.stringify({errors:__uiEventResults.errors,failures:__uiEventResults.rows.filter(row=>Object.values(row.checks).some(value=>value!==true))})").unwrap());
|
||||
}
|
||||
Reference in New Issue
Block a user