mirror of
https://github.com/lexmount/moli.git
synced 2026-11-02 00:06:25 +00:00
fix(dom): preserve event listener type identity
This commit is contained in:
@@ -4,12 +4,13 @@ use super::{
|
||||
clear_event_composed_path, event_initialized, event_internal_bool_flag, event_is_dispatching,
|
||||
new_dom_exception_value, set_event_composed_path, set_event_internal_flag, set_event_trusted,
|
||||
};
|
||||
use crate::event_type::EventType;
|
||||
use crate::util::{throw_type_error, v8str};
|
||||
|
||||
pub(crate) fn prepare_script_dispatch<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
) -> Option<(v8::Local<'s, v8::Object>, String)> {
|
||||
) -> Option<(v8::Local<'s, v8::Object>, EventType)> {
|
||||
let event = v8::Local::<v8::Object>::try_from(value).ok();
|
||||
let Some((event, initialized)) =
|
||||
event.and_then(|event| event_initialized(scope, event).map(|flag| (event, flag)))
|
||||
@@ -31,8 +32,8 @@ pub(crate) fn prepare_script_dispatch<'s>(
|
||||
set_event_trusted(scope, event, false);
|
||||
let event_type = crate::context_bootstrap::event_backing(scope, event)
|
||||
.get(scope, v8str(scope, "type").into())?
|
||||
.to_string(scope)?
|
||||
.to_rust_string_lossy(scope);
|
||||
.to_string(scope)?;
|
||||
let event_type = EventType::from_v8(scope, event_type);
|
||||
Some((event, event_type))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
use crate::context_bootstrap::{
|
||||
EventHandlerType, event_target_dispatch, events, invoke_simple_event_target_listeners,
|
||||
};
|
||||
use crate::event_type::EventTypeKey;
|
||||
use crate::util::get_private_object;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -61,7 +62,7 @@ pub(in crate::context_bootstrap::indexed_db) fn dispatch_idb_event_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> IdbEventDispatchResult {
|
||||
let mut result = IdbEventDispatchResult::default();
|
||||
let mut path = vec![target];
|
||||
|
||||
@@ -29,6 +29,5 @@ pub(super) use self::events::{
|
||||
|
||||
use super::{
|
||||
MEDIA_QUERY_LIST_LISTENERS_SLOT, SIMPLE_EVENT_TARGET_ORDERED_HANDLERS_SLOT,
|
||||
SIMPLE_EVENT_TARGET_SLOT, object_property_as_array, object_string_property_defined, v8_string,
|
||||
v8str,
|
||||
SIMPLE_EVENT_TARGET_SLOT, object_string_property_defined, v8str,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::{
|
||||
MEDIA_QUERY_LIST_LISTENERS_SLOT, SIMPLE_EVENT_TARGET_ORDERED_HANDLERS_SLOT,
|
||||
SIMPLE_EVENT_TARGET_SLOT, object_property_as_array, object_string_property_defined, v8_string,
|
||||
v8str,
|
||||
SIMPLE_EVENT_TARGET_SLOT, object_string_property_defined, v8str,
|
||||
};
|
||||
const SIMPLE_EVENT_TARGET_HANDLER_SLOT_FIELD: &str = "handlerSlot";
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ pub(in crate::context_bootstrap) fn media_query_list_add_listener_callback<'s>(
|
||||
scope,
|
||||
args.this(),
|
||||
MEDIA_QUERY_LIST_LISTENERS_SLOT,
|
||||
"change".to_owned(),
|
||||
"change".into(),
|
||||
listener,
|
||||
webidl::EventListenerOptions::default(),
|
||||
None,
|
||||
|
||||
+13
-11
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::event_type::EventTypeKey;
|
||||
use crate::{
|
||||
callback_invocation::{CallbackInvocation, CallbackInvocationOutcome, CallbackInvoker},
|
||||
context_bootstrap::events::{
|
||||
@@ -46,7 +47,7 @@ pub(crate) fn dispatch_simple_event_target_event<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
dispatch_simple_event_target_event_collecting_errors(
|
||||
@@ -64,7 +65,7 @@ pub(crate) fn dispatch_simple_event_target_event_collecting_errors<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
callback_errors: Option<&mut Vec<crate::exception_reporting::V8ExceptionReport>>,
|
||||
) -> SimpleEventDispatchResult {
|
||||
@@ -84,7 +85,7 @@ pub(crate) fn dispatch_simple_event_target_event_with_original_target<'s>(
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
original_target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
dispatch_simple_event_target_event_with_original_target_collecting_errors(
|
||||
@@ -104,7 +105,7 @@ fn dispatch_simple_event_target_event_with_original_target_collecting_errors<'s>
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
original_target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
mut callback_errors: Option<&mut Vec<crate::exception_reporting::V8ExceptionReport>>,
|
||||
) -> SimpleEventDispatchResult {
|
||||
@@ -117,7 +118,7 @@ fn dispatch_simple_event_target_event_with_original_target_collecting_errors<'s>
|
||||
event,
|
||||
);
|
||||
|
||||
let error_arguments = if event_type == "error"
|
||||
let error_arguments = if event_type.is_type("error")
|
||||
&& (crate::web_api_interfaces::WorkerGlobalScope::is_instance(scope, target)
|
||||
|| crate::web_api_interfaces::Window::is_instance(scope, target))
|
||||
{
|
||||
@@ -136,7 +137,8 @@ fn dispatch_simple_event_target_event_with_original_target_collecting_errors<'s>
|
||||
};
|
||||
if can_invoke && !simple_event_target_uses_ordered_handlers(scope, target) {
|
||||
let handler_name = format!("on{event_type}");
|
||||
if let Some(handler_key) = v8_string(scope, &handler_name)
|
||||
if let Some(event_key) = event_type.to_v8(scope)
|
||||
&& let Some(handler_key) = v8::String::concat(scope, v8str(scope, "on"), event_key)
|
||||
&& let Some(handler_value) = target.get(scope, handler_key.into())
|
||||
&& let Ok(handler) = v8::Local::<v8::Object>::try_from(handler_value)
|
||||
&& handler.is_callable()
|
||||
@@ -223,7 +225,7 @@ pub(crate) fn invoke_simple_event_target_listeners<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
capture_phase: bool,
|
||||
handler_arguments: &[v8::Local<'s, v8::Value>],
|
||||
@@ -303,7 +305,7 @@ pub(crate) fn invoke_simple_event_target_listeners<'s>(
|
||||
|
||||
pub(crate) fn invoke_simple_event_listener<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
listener: &SimpleObjectEventListenerSnapshot<'s>,
|
||||
callback_this: v8::Local<'s, v8::Value>,
|
||||
@@ -332,7 +334,7 @@ struct SimpleEventCallbackResult {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn invoke_simple_event_listener_collecting_errors<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
listener: &SimpleObjectEventListenerSnapshot<'s>,
|
||||
callback_this: v8::Local<'s, v8::Value>,
|
||||
@@ -365,7 +367,7 @@ fn invoke_simple_event_listener_collecting_errors<'s>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn invoke_simple_event_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
callback: v8::Local<'s, v8::Object>,
|
||||
relevant_context: v8::Local<'s, v8::Context>,
|
||||
@@ -417,7 +419,7 @@ fn simple_event_target_interface_name<'s>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn invoke_simple_event_callback_with_invocation<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
callback_target: v8::Local<'s, v8::Value>,
|
||||
relevant_context: v8::Local<'s, v8::Context>,
|
||||
|
||||
+42
-34
@@ -4,6 +4,7 @@ use crate::callback_invocation::CallbackInvocation;
|
||||
use crate::event_listener_args::{
|
||||
AddEventListenerArgs, RemoveEventListenerArgs, parse_listener_args,
|
||||
};
|
||||
use crate::event_type::{EventType, EventTypeKey};
|
||||
use crate::native_bridge::{WindowExecutionContextIdentity, lightweight_popup_id_from_window};
|
||||
use crate::util::{
|
||||
context_host_ptr_from_global_bridge, get_private_object, get_private_value,
|
||||
@@ -128,7 +129,7 @@ impl<'s> SimpleObjectEventListenerSnapshot<'s> {
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<Self> {
|
||||
let listener = self.resolve_callback(scope, target, slot_name, event_type)?;
|
||||
if listener.once {
|
||||
@@ -149,7 +150,7 @@ impl<'s> SimpleObjectEventListenerSnapshot<'s> {
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<Self> {
|
||||
// Removing and re-adding the same callback creates a different entry.
|
||||
// The removed entry must stay inactive in an existing dispatch snapshot.
|
||||
@@ -262,7 +263,7 @@ pub(crate) fn simple_object_event_target_register_webidl_listener<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: String,
|
||||
event_type: EventType,
|
||||
listener: webidl::WebIdlCallbackInterface,
|
||||
options: webidl::EventListenerOptions,
|
||||
signal: Option<ResolvedAbortSignal<'s>>,
|
||||
@@ -278,7 +279,7 @@ fn simple_object_event_target_register_resolved_listener<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: String,
|
||||
event_type: EventType,
|
||||
listener: SimpleObjectResolvedEventListener<'s>,
|
||||
options: webidl::EventListenerOptions,
|
||||
signal: Option<ResolvedAbortSignal<'s>>,
|
||||
@@ -339,7 +340,7 @@ fn simple_object_event_target_register_resolved_listener<'s>(
|
||||
options.capture,
|
||||
);
|
||||
}
|
||||
if event_type == "message" {
|
||||
if event_type.is_type("message") {
|
||||
crate::context_bootstrap::flush_pending_worker_messages_for_listener(scope, target);
|
||||
}
|
||||
}
|
||||
@@ -393,7 +394,7 @@ fn simple_object_abort_remove_listener_callback<'s>(
|
||||
let Some(event_type) = data
|
||||
.get(scope, v8str(scope, "type").into())
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.map(|value| EventType::from_v8(scope, value))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -429,14 +430,14 @@ fn register_simple_object_abort_listener<'s>(
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
entry: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
listener: v8::Local<'s, v8::Value>,
|
||||
capture: bool,
|
||||
) -> bool {
|
||||
let Some(slot_value) = v8_string(scope, slot_name) else {
|
||||
return false;
|
||||
};
|
||||
let Some(event_type_value) = v8_string(scope, event_type) else {
|
||||
let Some(event_type_value) = event_type.to_v8(scope) else {
|
||||
return false;
|
||||
};
|
||||
let data = SimpleObjectAbortListenerDataDeclaration::new(
|
||||
@@ -499,22 +500,21 @@ pub(in crate::context_bootstrap::media_queries::events::simple_event_target) fn
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
create: bool,
|
||||
) -> Option<v8::Local<'s, v8::Array>> {
|
||||
let registry = simple_object_event_listener_registry(scope, target, slot_name, create)?;
|
||||
if let Some(listeners) = object_property_as_array(scope, registry, event_type) {
|
||||
if let Some(listeners) = registry
|
||||
.get(scope, event_type.to_v8(scope)?.into())
|
||||
.and_then(|value| v8::Local::<v8::Array>::try_from(value).ok())
|
||||
{
|
||||
return Some(listeners);
|
||||
}
|
||||
if !create {
|
||||
return None;
|
||||
}
|
||||
let listeners = v8::Array::new(scope, 0);
|
||||
let _ = registry.set(
|
||||
scope,
|
||||
v8_string(scope, event_type)?.into(),
|
||||
listeners.into(),
|
||||
);
|
||||
let _ = registry.set(scope, event_type.to_v8(scope)?.into(), listeners.into());
|
||||
Some(listeners)
|
||||
}
|
||||
|
||||
@@ -522,7 +522,7 @@ pub(crate) fn simple_object_event_listeners_snapshot<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Vec<SimpleObjectEventListenerSnapshot<'s>> {
|
||||
let Some(listeners) =
|
||||
simple_object_event_listener_array(scope, target, slot_name, event_type, false)
|
||||
@@ -558,7 +558,7 @@ pub(crate) fn simple_object_has_event_listeners<'s>(
|
||||
let Some(event_type) = event_types
|
||||
.get_index(scope, index)
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.map(|value| EventType::from_v8(scope, value))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -589,7 +589,7 @@ pub(crate) fn simple_event_target_inspector_listener_snapshots<'s>(
|
||||
let Some(event_type) = event_types
|
||||
.get_index(scope, index)
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.map(|value| EventType::from_v8(scope, value))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -609,7 +609,7 @@ pub(crate) fn simple_event_target_inspector_listener_snapshots<'s>(
|
||||
listener.original
|
||||
};
|
||||
snapshots.push(SimpleObjectEventListenerInspectorSnapshot {
|
||||
event_type: event_type.clone(),
|
||||
event_type: event_type.as_str_lossy().to_owned(),
|
||||
original,
|
||||
callback,
|
||||
relevant_context: listener.relevant_context,
|
||||
@@ -627,7 +627,7 @@ pub(crate) fn simple_object_event_set_ordered_handler<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler_slot_name: &'static str,
|
||||
active: bool,
|
||||
) {
|
||||
@@ -670,7 +670,7 @@ pub(crate) fn simple_object_event_set_ordered_handler<'s>(
|
||||
let _ = next.set_index(scope, next.length(), candidate);
|
||||
}
|
||||
if let Some(registry) = simple_object_event_listener_registry(scope, target, slot_name, false)
|
||||
&& let Some(key) = v8_string(scope, event_type)
|
||||
&& let Some(key) = event_type.to_v8(scope)
|
||||
{
|
||||
let _ = registry.set(scope, key.into(), next.into());
|
||||
if next.length() == 0 {
|
||||
@@ -696,7 +696,7 @@ pub(crate) fn simple_object_event_activate_uncompiled_handler<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler_slot_name: &'static str,
|
||||
) {
|
||||
set_simple_object_event_ordered_handler_callback(
|
||||
@@ -713,7 +713,7 @@ fn set_simple_object_event_ordered_handler_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler_slot_name: &'static str,
|
||||
callback: Option<v8::Local<'s, v8::Object>>,
|
||||
) {
|
||||
@@ -800,7 +800,7 @@ pub(crate) fn simple_object_event_remove_listener_value_for_type<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
listener: v8::Local<'s, v8::Value>,
|
||||
capture: bool,
|
||||
) {
|
||||
@@ -808,7 +808,11 @@ pub(crate) fn simple_object_event_remove_listener_value_for_type<'s>(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(current) = object_property_as_array(scope, registry, event_type) else {
|
||||
let Some(current) = event_type
|
||||
.to_v8(scope)
|
||||
.and_then(|key| registry.get(scope, key.into()))
|
||||
.and_then(|value| v8::Local::<v8::Array>::try_from(value).ok())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let next = v8::Array::new(scope, 0);
|
||||
@@ -825,7 +829,7 @@ pub(crate) fn simple_object_event_remove_listener_value_for_type<'s>(
|
||||
}
|
||||
let _ = next.set_index(scope, next.length(), candidate);
|
||||
}
|
||||
if let Some(key) = v8_string(scope, event_type) {
|
||||
if let Some(key) = event_type.to_v8(scope) {
|
||||
let _ = registry.set(scope, key.into(), next.into());
|
||||
if next.length() == 0 {
|
||||
remove_simple_object_event_type_order(scope, registry, event_type);
|
||||
@@ -899,7 +903,7 @@ fn ensure_simple_object_event_type_order<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) {
|
||||
let Some(registry) = simple_object_event_listener_registry(scope, target, slot_name, false)
|
||||
else {
|
||||
@@ -908,36 +912,40 @@ fn ensure_simple_object_event_type_order<'s>(
|
||||
let Some(order) = simple_object_event_type_order(scope, registry, true) else {
|
||||
return;
|
||||
};
|
||||
let Some(key) = event_type.to_v8(scope) else {
|
||||
return;
|
||||
};
|
||||
for index in 0..order.length() {
|
||||
if order
|
||||
.get_index(scope, index)
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.is_some_and(|value| value.to_rust_string_lossy(scope) == event_type)
|
||||
.is_some_and(|value| value.strict_equals(key.into()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(event_type) = v8_string(scope, event_type) {
|
||||
let _ = order.set_index(scope, order.length(), event_type.into());
|
||||
}
|
||||
let _ = order.set_index(scope, order.length(), key.into());
|
||||
}
|
||||
|
||||
fn remove_simple_object_event_type_order<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
registry: v8::Local<'s, v8::Object>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) {
|
||||
let Some(order) = simple_object_event_type_order(scope, registry, false) else {
|
||||
return;
|
||||
};
|
||||
let next = v8::Array::new(scope, 0);
|
||||
let Some(key) = event_type.to_v8(scope) else {
|
||||
return;
|
||||
};
|
||||
for index in 0..order.length() {
|
||||
let Some(candidate) = order.get_index(scope, index) else {
|
||||
continue;
|
||||
};
|
||||
if candidate
|
||||
.to_string(scope)
|
||||
.is_some_and(|value| value.to_rust_string_lossy(scope) == event_type)
|
||||
.is_some_and(|value| value.strict_equals(key.into()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1129,7 +1137,7 @@ fn simple_object_event_listener_entry_registered<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
slot_name: &str,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
entry: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
let Some(listeners) =
|
||||
|
||||
@@ -3,8 +3,8 @@ pub(in crate::context_bootstrap) use crate::util::{
|
||||
array_contains_strict, array_push_value, get_own_static_property, get_private_value,
|
||||
global_constructor_object, global_constructor_prototype, object_bool_property,
|
||||
object_defined_string_property as object_string_property_defined,
|
||||
object_own_static_bool_property, object_property_as_array, object_property_as_object,
|
||||
object_string_property, set_private_value,
|
||||
object_own_static_bool_property, object_property_as_object, object_string_property,
|
||||
set_private_value,
|
||||
};
|
||||
|
||||
pub(in crate::context_bootstrap) fn global_queue_array<'s>(
|
||||
|
||||
@@ -152,6 +152,11 @@ fn handler_setter<'s>(
|
||||
return;
|
||||
};
|
||||
media_queries::simple_object_event_set_ordered_handler(
|
||||
scope, target, &listeners, event_type, slot, active,
|
||||
scope,
|
||||
target,
|
||||
&listeners,
|
||||
*event_type,
|
||||
slot,
|
||||
active,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::event_type::EventTypeKey;
|
||||
use crate::host::HostTimerOwner;
|
||||
use crate::host::PublicEventDispatchResult;
|
||||
use crate::script_provenance::CompiledStringProvenance;
|
||||
@@ -388,7 +389,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn event_listener_callback_ids(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
capture: bool,
|
||||
) -> Vec<crate::native_bridge::EventCallbackId> {
|
||||
self.events
|
||||
@@ -405,7 +406,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn insert_event_listener(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
registration: crate::host::EventListenerRegistration,
|
||||
) {
|
||||
if !self
|
||||
@@ -427,7 +428,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn remove_event_listener_by_id(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: crate::native_bridge::EventCallbackId,
|
||||
capture: bool,
|
||||
) -> bool {
|
||||
@@ -445,7 +446,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn set_event_handler_property(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: Option<crate::native_bridge::EventCallbackId>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
self.events
|
||||
@@ -455,7 +456,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn set_compiled_event_handler_property(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: Option<crate::native_bridge::EventCallbackId>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
self.events
|
||||
@@ -465,7 +466,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn set_event_handler_content_attribute(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
owner: Option<DomHandle>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
self.events
|
||||
@@ -527,7 +528,7 @@ impl DocumentRuntime {
|
||||
pub(crate) fn uncompiled_event_handler_content_attribute_owner(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<DomHandle> {
|
||||
self.events
|
||||
.uncompiled_event_handler_content_attribute_owner(target, event_type)
|
||||
@@ -536,13 +537,17 @@ impl DocumentRuntime {
|
||||
pub(crate) fn event_handler_property_callback_id(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<Option<crate::native_bridge::EventCallbackId>> {
|
||||
self.events
|
||||
.event_handler_property_callback_id(target, event_type)
|
||||
}
|
||||
|
||||
pub(crate) fn has_event_listener(&self, target: EventTargetHandle, event_type: &str) -> bool {
|
||||
pub(crate) fn has_event_listener(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
self.events.has_listener(target, event_type)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! The shared EventTarget argument boundary, before target-specific mutation.
|
||||
|
||||
use crate::abort_signal_route::{ResolvedAbortSignal, event_listener_signal_from_options_value};
|
||||
use crate::event_type::EventType;
|
||||
use crate::webidl;
|
||||
|
||||
pub(crate) fn parse_listener_args<'s, T: webidl::WebIdlArguments<'s>>(
|
||||
@@ -26,8 +27,8 @@ pub(crate) fn parse_listener_args<'s, T: webidl::WebIdlArguments<'s>>(
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "EventTarget.addEventListener")]
|
||||
pub(crate) struct AddEventListenerArgs<'s> {
|
||||
#[webidl(required, name = "type")]
|
||||
pub(crate) event_type: String,
|
||||
#[webidl(required, name = "type", converter = "raw")]
|
||||
pub(crate) event_type: EventType,
|
||||
#[webidl(required, converter = "callback_interface", nullable)]
|
||||
pub(crate) listener: Option<webidl::WebIdlCallbackInterface>,
|
||||
#[webidl(with = add_event_listener_options)]
|
||||
@@ -37,8 +38,8 @@ pub(crate) struct AddEventListenerArgs<'s> {
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "EventTarget.removeEventListener")]
|
||||
pub(crate) struct RemoveEventListenerArgs {
|
||||
#[webidl(required, name = "type")]
|
||||
pub(crate) event_type: String,
|
||||
#[webidl(required, name = "type", converter = "raw")]
|
||||
pub(crate) event_type: EventType,
|
||||
#[webidl(required, converter = "callback_interface", nullable)]
|
||||
pub(crate) listener: Option<webidl::WebIdlCallbackInterface>,
|
||||
#[webidl(with = webidl::event_listener_options)]
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
//! Event type identity is a DOMString, including unpaired UTF-16 surrogates.
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use crate::{dom::native::DomStringValue, util, webidl};
|
||||
use indexmap::Equivalent;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub(crate) struct EventType(DomStringValue);
|
||||
|
||||
impl EventType {
|
||||
pub(crate) fn from_v8(scope: &v8::PinScope<'_, '_>, value: v8::Local<'_, v8::String>) -> Self {
|
||||
let mut units = vec![0; value.length()];
|
||||
value.write_v2(scope, 0, &mut units, v8::WriteFlags::empty());
|
||||
Self(DomStringValue::from_utf16(&units))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for EventType {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// Scalar strings share str's hash so native names can be queried without
|
||||
// allocating. Surrogate-bearing keys retain their original identity.
|
||||
self.0.as_str_lossy().hash(state);
|
||||
if self.0.as_str().is_none() {
|
||||
self.0.utf16_units().hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EventType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.0.as_str_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for EventType {
|
||||
fn from(value: &str) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> webidl::WebIdlConverter<'s> for EventType {
|
||||
type Options = webidl::StringOptions;
|
||||
|
||||
fn convert(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
value: v8::Local<'s, v8::Value>,
|
||||
context: webidl::Context,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, webidl::WebIdlError> {
|
||||
webidl::DomString16::convert(scope, value, context, options)
|
||||
.map(|value| Self(DomStringValue::from_utf16(&value.0)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed native name or an already-converted JS event type.
|
||||
pub(crate) trait EventTypeKey: Hash + Equivalent<EventType> + fmt::Display {
|
||||
fn to_event_type(&self) -> EventType;
|
||||
fn as_str_lossy(&self) -> &str;
|
||||
fn as_str(&self) -> Option<&str>;
|
||||
fn is_type(&self, name: &str) -> bool;
|
||||
fn to_v8<'s>(&self, scope: &v8::PinScope<'s, '_>) -> Option<v8::Local<'s, v8::String>>;
|
||||
}
|
||||
|
||||
impl EventTypeKey for EventType {
|
||||
fn to_event_type(&self) -> EventType {
|
||||
self.clone()
|
||||
}
|
||||
fn as_str_lossy(&self) -> &str {
|
||||
self.0.as_str_lossy()
|
||||
}
|
||||
fn as_str(&self) -> Option<&str> {
|
||||
self.0.as_str()
|
||||
}
|
||||
fn is_type(&self, name: &str) -> bool {
|
||||
self.0.as_str() == Some(name)
|
||||
}
|
||||
fn to_v8<'s>(&self, scope: &v8::PinScope<'s, '_>) -> Option<v8::Local<'s, v8::String>> {
|
||||
match self.0.as_str() {
|
||||
Some(value) => util::v8_string(scope, value),
|
||||
None => v8::String::new_from_two_byte(
|
||||
scope,
|
||||
&self.0.utf16_units(),
|
||||
v8::NewStringType::Normal,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Equivalent<EventType> for str {
|
||||
fn equivalent(&self, other: &EventType) -> bool {
|
||||
other.is_type(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventTypeKey for str {
|
||||
fn to_event_type(&self) -> EventType {
|
||||
self.into()
|
||||
}
|
||||
fn as_str_lossy(&self) -> &str {
|
||||
self
|
||||
}
|
||||
fn as_str(&self) -> Option<&str> {
|
||||
Some(self)
|
||||
}
|
||||
fn is_type(&self, name: &str) -> bool {
|
||||
self == name
|
||||
}
|
||||
fn to_v8<'s>(&self, scope: &v8::PinScope<'s, '_>) -> Option<v8::Local<'s, v8::String>> {
|
||||
util::v8_string(scope, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Equivalent<EventType> for String {
|
||||
fn equivalent(&self, other: &EventType) -> bool {
|
||||
self.as_str().equivalent(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventTypeKey for String {
|
||||
fn to_event_type(&self) -> EventType {
|
||||
self.as_str().into()
|
||||
}
|
||||
fn as_str_lossy(&self) -> &str {
|
||||
self
|
||||
}
|
||||
fn as_str(&self) -> Option<&str> {
|
||||
Some(self)
|
||||
}
|
||||
fn is_type(&self, name: &str) -> bool {
|
||||
self == name
|
||||
}
|
||||
fn to_v8<'s>(&self, scope: &v8::PinScope<'s, '_>) -> Option<v8::Local<'s, v8::String>> {
|
||||
util::v8_string(scope, self)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::event_type::{EventType, EventTypeKey};
|
||||
use crate::{
|
||||
context_bootstrap::{
|
||||
CHILD_BROWSING_CONTEXT_HANDLE_SLOT, EventHandlerType,
|
||||
@@ -158,8 +159,9 @@ impl EventDispatchEntry {
|
||||
pub(crate) fn event_handler_content_attribute_owner(
|
||||
runtime: &DocumentRuntime,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<DomHandle> {
|
||||
let event_type = event_type.as_str()?;
|
||||
let attribute_name =
|
||||
crate::native_bridge::element::event_handler_content_attribute_name(event_type)?;
|
||||
let handle = match target {
|
||||
@@ -195,9 +197,10 @@ pub(crate) fn event_handler_content_attribute_owner(
|
||||
}
|
||||
|
||||
pub(crate) struct HostEventTargetRegistry {
|
||||
listeners: HashMap<EventTargetHandle, HashMap<String, Vec<EventListenerEntry>>>,
|
||||
handler_properties: HashMap<EventTargetHandle, HashMap<String, EventHandlerPropertyEntry>>,
|
||||
event_type_registration_ids: HashMap<EventTargetHandle, HashMap<String, u64>>,
|
||||
listeners: HashMap<EventTargetHandle, indexmap::IndexMap<EventType, Vec<EventListenerEntry>>>,
|
||||
handler_properties:
|
||||
HashMap<EventTargetHandle, indexmap::IndexMap<EventType, EventHandlerPropertyEntry>>,
|
||||
event_type_registration_ids: HashMap<EventTargetHandle, indexmap::IndexMap<EventType, u64>>,
|
||||
next_listener_id: u64,
|
||||
}
|
||||
|
||||
@@ -235,7 +238,7 @@ pub(crate) fn invoke_prepared_event_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
host_ptr: *mut JsContextHost,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
callback: crate::native_bridge::PreparedEventCallback,
|
||||
target: EventTargetHandle,
|
||||
@@ -261,7 +264,7 @@ pub(crate) fn invoke_prepared_event_callback<'s>(
|
||||
fn invoke_prepared_event_callback_with_receiver<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
host_ptr: *mut JsContextHost,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
callback: crate::native_bridge::PreparedEventCallback,
|
||||
receiver: v8::Local<'s, v8::Value>,
|
||||
@@ -303,7 +306,7 @@ pub(crate) fn invoke_prepared_before_unload_event_handler<'s>(
|
||||
host_ptr: *mut JsContextHost,
|
||||
target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_name: &str,
|
||||
callback: crate::native_bridge::PreparedEventCallback,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
@@ -393,11 +396,12 @@ fn invoke_event_handler_property<'s>(
|
||||
target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
target_object: v8::Local<'s, v8::Object>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
let Some(handler_name) =
|
||||
crate::native_bridge::element::event_handler_content_attribute_name(event_type)
|
||||
let Some(handler_name) = event_type
|
||||
.as_str()
|
||||
.and_then(crate::native_bridge::element::event_handler_content_attribute_name)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -481,7 +485,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
host_ptr: *mut JsContextHost,
|
||||
target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
callback_id: crate::native_bridge::EventCallbackId,
|
||||
) {
|
||||
@@ -492,7 +496,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
|
||||
let timing_started = moli_trace::cdp_nav_timing_enabled().then(Instant::now);
|
||||
if target == EventTargetHandle::Window
|
||||
&& event_type == "error"
|
||||
&& event_type.is_type("error")
|
||||
&& let Some(arguments) = error_event_handler_arguments(scope, event)
|
||||
{
|
||||
let returned = invoke_prepared_event_callback(
|
||||
@@ -519,7 +523,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
tracing::info!(
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_handler_property_invoked",
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
handler_name,
|
||||
?target,
|
||||
elapsed_ms = timing_started.elapsed().as_millis(),
|
||||
@@ -528,7 +532,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
return;
|
||||
}
|
||||
|
||||
if event_type == "beforeunload" {
|
||||
if event_type.is_type("beforeunload") {
|
||||
invoke_prepared_before_unload_event_handler(
|
||||
scope,
|
||||
host_ptr,
|
||||
@@ -543,7 +547,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
tracing::info!(
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_handler_property_invoked",
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
handler_name,
|
||||
?target,
|
||||
elapsed_ms = timing_started.elapsed().as_millis(),
|
||||
@@ -571,7 +575,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
tracing::info!(
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_handler_property_invoked",
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
handler_name,
|
||||
?target,
|
||||
elapsed_ms = timing_started.elapsed().as_millis(),
|
||||
@@ -585,7 +589,7 @@ fn call_event_target_listeners_filtered<'s>(
|
||||
host_ptr: *mut JsContextHost,
|
||||
target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
capture_only: bool,
|
||||
at_target: bool,
|
||||
@@ -641,7 +645,7 @@ fn event_target_is_current(host_ptr: *mut JsContextHost, target: EventTargetHand
|
||||
pub(crate) fn report_event_listener_exception<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
host_ptr: *mut JsContextHost,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler: v8::Local<'s, v8::Function>,
|
||||
report: &V8ExceptionReport,
|
||||
) {
|
||||
@@ -662,7 +666,7 @@ pub(crate) fn report_event_listener_exception<'s>(
|
||||
pub(crate) fn report_event_callback_exception<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
host_ptr: *mut JsContextHost,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
relevant_identity: Option<crate::native_bridge::WindowExecutionContextIdentity>,
|
||||
child_handle: Option<DomHandle>,
|
||||
report: &V8ExceptionReport,
|
||||
@@ -689,7 +693,7 @@ pub(crate) fn report_event_callback_exception<'s>(
|
||||
return;
|
||||
};
|
||||
Some(reporting_scope)
|
||||
} else if event_type == "error" {
|
||||
} else if event_type.is_type("error") {
|
||||
return;
|
||||
} else {
|
||||
None
|
||||
@@ -869,7 +873,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn set_event_handler_property(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: Option<crate::native_bridge::EventCallbackId>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
let existing_listener_id = self
|
||||
@@ -886,7 +890,7 @@ impl HostEventTargetRegistry {
|
||||
.map(EventHandlerPropertyState::Callback)
|
||||
.unwrap_or(EventHandlerPropertyState::Null);
|
||||
let previous = self.handler_properties.entry(target).or_default().insert(
|
||||
event_type.to_owned(),
|
||||
event_type.to_event_type(),
|
||||
EventHandlerPropertyEntry { listener_id, state },
|
||||
);
|
||||
let previous_callback = previous.and_then(event_handler_callback_id);
|
||||
@@ -899,7 +903,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn set_compiled_event_handler_property(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: Option<crate::native_bridge::EventCallbackId>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
let listener_id = self
|
||||
@@ -913,7 +917,7 @@ impl HostEventTargetRegistry {
|
||||
.map(EventHandlerPropertyState::Callback)
|
||||
.unwrap_or(EventHandlerPropertyState::Null);
|
||||
let previous = self.handler_properties.entry(target).or_default().insert(
|
||||
event_type.to_owned(),
|
||||
event_type.to_event_type(),
|
||||
EventHandlerPropertyEntry {
|
||||
listener_id: Some(listener_id),
|
||||
state,
|
||||
@@ -925,18 +929,18 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn set_event_handler_content_attribute(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
owner: Option<DomHandle>,
|
||||
) -> Option<crate::native_bridge::EventCallbackId> {
|
||||
let Some(owner) = owner else {
|
||||
let removed = self
|
||||
.handler_properties
|
||||
.get_mut(&target)
|
||||
.and_then(|handlers| handlers.remove(event_type));
|
||||
.and_then(|handlers| handlers.shift_remove(event_type));
|
||||
let remove_target = self
|
||||
.handler_properties
|
||||
.get(&target)
|
||||
.is_some_and(HashMap::is_empty);
|
||||
.is_some_and(indexmap::IndexMap::is_empty);
|
||||
if remove_target {
|
||||
self.handler_properties.remove(&target);
|
||||
}
|
||||
@@ -952,7 +956,7 @@ impl HostEventTargetRegistry {
|
||||
.unwrap_or_else(|| self.allocate_listener_id());
|
||||
self.ensure_event_type_registration_id(target, event_type, listener_id);
|
||||
let previous = self.handler_properties.entry(target).or_default().insert(
|
||||
event_type.to_owned(),
|
||||
event_type.to_event_type(),
|
||||
EventHandlerPropertyEntry {
|
||||
listener_id: Some(listener_id),
|
||||
state: EventHandlerPropertyState::Uncompiled(owner),
|
||||
@@ -964,7 +968,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn has_event_handler_property_entry(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
self.handler_properties
|
||||
.get(&target)
|
||||
@@ -974,7 +978,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn ensure_event_handler_content_attribute(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
owner: Option<DomHandle>,
|
||||
) {
|
||||
if let Some(owner) = owner
|
||||
@@ -991,7 +995,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn uncompiled_event_handler_content_attribute_owner(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<DomHandle> {
|
||||
match &self
|
||||
.handler_properties
|
||||
@@ -1007,7 +1011,7 @@ impl HostEventTargetRegistry {
|
||||
fn event_handler_property_value(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<EventHandlerPropertyValue> {
|
||||
match &self
|
||||
.handler_properties
|
||||
@@ -1026,7 +1030,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn event_handler_property_callback_id(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<Option<crate::native_bridge::EventCallbackId>> {
|
||||
match &self
|
||||
.handler_properties
|
||||
@@ -1043,7 +1047,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn listener_callback_ids(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
capture: bool,
|
||||
) -> Vec<crate::native_bridge::EventCallbackId> {
|
||||
self.listeners
|
||||
@@ -1071,7 +1075,7 @@ impl HostEventTargetRegistry {
|
||||
event_type_registration_id,
|
||||
EventListenerInspectorSnapshot {
|
||||
registration_id: entry.id,
|
||||
event_type: event_type.clone(),
|
||||
event_type: event_type.as_str_lossy().to_owned(),
|
||||
callback_id: entry.callback_id,
|
||||
capture: entry.capture,
|
||||
once: entry.once,
|
||||
@@ -1094,7 +1098,7 @@ impl HostEventTargetRegistry {
|
||||
.unwrap_or(registration_id),
|
||||
EventListenerInspectorSnapshot {
|
||||
registration_id,
|
||||
event_type: event_type.clone(),
|
||||
event_type: event_type.as_str_lossy().to_owned(),
|
||||
callback_id,
|
||||
capture: false,
|
||||
once: false,
|
||||
@@ -1115,7 +1119,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn insert_listener(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
registration: EventListenerRegistration,
|
||||
) {
|
||||
let id = self.allocate_listener_id();
|
||||
@@ -1124,14 +1128,14 @@ impl HostEventTargetRegistry {
|
||||
.listeners
|
||||
.entry(target)
|
||||
.or_default()
|
||||
.entry(event_type.to_owned())
|
||||
.entry(event_type.to_event_type())
|
||||
.or_default();
|
||||
if moli_trace::cdp_nav_timing_enabled() {
|
||||
tracing::info!(
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_listener_registered",
|
||||
id,
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
?target,
|
||||
capture = registration.capture,
|
||||
passive = registration.passive,
|
||||
@@ -1160,7 +1164,7 @@ impl HostEventTargetRegistry {
|
||||
pub(crate) fn remove_listener_by_id(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: crate::native_bridge::EventCallbackId,
|
||||
capture: bool,
|
||||
) -> bool {
|
||||
@@ -1174,7 +1178,7 @@ impl HostEventTargetRegistry {
|
||||
entries.retain(|entry| entry.callback_id != callback_id || entry.capture != capture);
|
||||
let removed = entries.len() != before;
|
||||
if entries.is_empty() {
|
||||
target_listeners.remove(event_type);
|
||||
target_listeners.shift_remove(event_type);
|
||||
}
|
||||
if target_listeners.is_empty() {
|
||||
self.listeners.remove(&target);
|
||||
@@ -1212,7 +1216,11 @@ impl HostEventTargetRegistry {
|
||||
self.retain_active_event_type_registration_ids();
|
||||
}
|
||||
|
||||
pub(crate) fn has_listener(&self, target: EventTargetHandle, event_type: &str) -> bool {
|
||||
pub(crate) fn has_listener(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
self.handler_properties
|
||||
.get(&target)
|
||||
.and_then(|handlers| handlers.get(event_type))
|
||||
@@ -1236,20 +1244,20 @@ impl HostEventTargetRegistry {
|
||||
fn ensure_event_type_registration_id(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
registration_id: u64,
|
||||
) {
|
||||
self.event_type_registration_ids
|
||||
.entry(target)
|
||||
.or_default()
|
||||
.entry(event_type.to_owned())
|
||||
.entry(event_type.to_event_type())
|
||||
.or_insert(registration_id);
|
||||
}
|
||||
|
||||
fn event_type_registration_id(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<u64> {
|
||||
self.event_type_registration_ids
|
||||
.get(&target)
|
||||
@@ -1260,7 +1268,7 @@ impl HostEventTargetRegistry {
|
||||
fn event_type_has_live_registration(
|
||||
&self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
self.listeners
|
||||
.get(&target)
|
||||
@@ -1276,7 +1284,7 @@ impl HostEventTargetRegistry {
|
||||
fn remove_event_type_registration_id_if_unused(
|
||||
&mut self,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) {
|
||||
if self.event_type_has_live_registration(target, event_type) {
|
||||
return;
|
||||
@@ -1284,7 +1292,7 @@ impl HostEventTargetRegistry {
|
||||
let Some(event_types) = self.event_type_registration_ids.get_mut(&target) else {
|
||||
return;
|
||||
};
|
||||
event_types.remove(event_type);
|
||||
event_types.shift_remove(event_type);
|
||||
if event_types.is_empty() {
|
||||
self.event_type_registration_ids.remove(&target);
|
||||
}
|
||||
@@ -1315,7 +1323,7 @@ impl HostEventTargetRegistry {
|
||||
host_ptr: *mut JsContextHost,
|
||||
target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
capture_only: bool,
|
||||
at_target: bool,
|
||||
@@ -1381,7 +1389,7 @@ impl HostEventTargetRegistry {
|
||||
tracing::info!(
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_listener_batch_start",
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
?target,
|
||||
capture_only,
|
||||
at_target,
|
||||
@@ -1443,7 +1451,7 @@ impl HostEventTargetRegistry {
|
||||
tracing::debug!(
|
||||
listener_id = listener.id,
|
||||
?target,
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
"skipped event listener owned by a retired execution context"
|
||||
);
|
||||
continue;
|
||||
@@ -1490,7 +1498,7 @@ impl HostEventTargetRegistry {
|
||||
target: "moli_cdp_nav_timing",
|
||||
stage = "event_listener_invoked",
|
||||
listener_id = listener.id,
|
||||
event_type,
|
||||
event_type = %event_type,
|
||||
?target,
|
||||
capture_only,
|
||||
at_target,
|
||||
@@ -1878,7 +1886,7 @@ pub(crate) fn dispatch_public_event_with_original_target<'s, 'i>(
|
||||
post_dispatch_related_target,
|
||||
)?;
|
||||
set_event_post_dispatch_source(scope, host_ptr, event, post_dispatch_source_target)?;
|
||||
if event_type == "mouseover"
|
||||
if event_type.is_type("mouseover")
|
||||
&& !event_default_prevented(scope, event)
|
||||
&& let EventTargetHandle::Node(handle) = dispatch_target
|
||||
{
|
||||
@@ -1886,7 +1894,7 @@ pub(crate) fn dispatch_public_event_with_original_target<'s, 'i>(
|
||||
scope, host_ptr, handle,
|
||||
);
|
||||
}
|
||||
if event_type == "keydown"
|
||||
if event_type.is_type("keydown")
|
||||
&& event_bool_property(scope, event, "isTrusted")
|
||||
&& !event_default_prevented(scope, event)
|
||||
{
|
||||
@@ -1918,9 +1926,10 @@ fn stops_submit_or_reset_at_ancestor_form(
|
||||
host_ptr: *mut JsContextHost,
|
||||
original_target: EventTargetHandle,
|
||||
current_target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
if !matches!(event_type, "submit" | "reset") || current_target == original_target {
|
||||
if !matches!(event_type.as_str(), Some("submit" | "reset")) || current_target == original_target
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let EventTargetHandle::Node(handle) = current_target else {
|
||||
@@ -1979,7 +1988,7 @@ fn run_host_event_dispatch<'s>(
|
||||
host_ptr: *mut JsContextHost,
|
||||
dispatch_target: EventTargetHandle,
|
||||
invocation_target_in_shadow_tree: bool,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> std::result::Result<(), String> {
|
||||
let status = call_event_target_listeners_filtered(
|
||||
@@ -2139,7 +2148,7 @@ fn normalize_public_event_record<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
default_target: v8::Local<'s, v8::Value>,
|
||||
) -> std::result::Result<String, String> {
|
||||
) -> std::result::Result<EventType, String> {
|
||||
let type_key = v8str(scope, "type");
|
||||
let target_key = v8str(scope, "target");
|
||||
let current_target_key = v8str(scope, "currentTarget");
|
||||
@@ -2152,7 +2161,7 @@ fn normalize_public_event_record<'s>(
|
||||
}
|
||||
let event_type = event_type_value
|
||||
.to_string(scope)
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.map(|value| EventType::from_v8(scope, value))
|
||||
.unwrap_or_default();
|
||||
|
||||
let target_value =
|
||||
|
||||
@@ -57,6 +57,7 @@ mod document_task_lane;
|
||||
mod dom_parser;
|
||||
mod dynamic_script_owner;
|
||||
mod event_listener_args;
|
||||
mod event_type;
|
||||
mod exception_reporting;
|
||||
mod frame_owner_model;
|
||||
mod host;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::event_type::{EventType, EventTypeKey};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use super::super::document_runtime::EventTargetHandle;
|
||||
@@ -68,7 +69,7 @@ struct AbortSignalState {
|
||||
|
||||
struct AbortLinkedTargetListener {
|
||||
target: EventTargetHandle,
|
||||
event_type: String,
|
||||
event_type: EventType,
|
||||
callback_id: super::EventCallbackId,
|
||||
capture: bool,
|
||||
}
|
||||
@@ -345,7 +346,7 @@ impl AbortStore {
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
signal: v8::Local<'s, v8::Object>,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: super::EventCallbackId,
|
||||
capture: bool,
|
||||
) {
|
||||
@@ -359,7 +360,7 @@ impl AbortStore {
|
||||
.linked_target_listeners
|
||||
.push(AbortLinkedTargetListener {
|
||||
target,
|
||||
event_type: event_type.to_owned(),
|
||||
event_type: event_type.to_event_type(),
|
||||
callback_id,
|
||||
capture,
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::JsContextHost;
|
||||
use crate::event_type::EventTypeKey;
|
||||
use crate::{
|
||||
context_bootstrap::{
|
||||
EVENT_DISPATCHING_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, EVENT_STOP_PROPAGATION_SLOT,
|
||||
@@ -63,7 +64,7 @@ impl JsContextHost {
|
||||
fn child_window_event_requires_runtime_dispatch(
|
||||
&self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
self.child_window_proxy_records.has_live_window(handle)
|
||||
|| self
|
||||
@@ -84,7 +85,7 @@ impl JsContextHost {
|
||||
pub(crate) fn child_window_event_listener_callback_ids(
|
||||
&self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
capture: bool,
|
||||
) -> Vec<EventCallbackId> {
|
||||
self.child_window_event_listeners
|
||||
@@ -118,7 +119,7 @@ impl JsContextHost {
|
||||
.filter_map(|entry| {
|
||||
Some(crate::host::EventListenerInspectorSnapshot {
|
||||
registration_id: entry.registration_id.0,
|
||||
event_type: event_type.clone(),
|
||||
event_type: event_type.as_str_lossy().to_owned(),
|
||||
callback_id: entry.callback_state.callback_id()?,
|
||||
capture: entry.capture,
|
||||
once: entry.once,
|
||||
@@ -132,7 +133,7 @@ impl JsContextHost {
|
||||
pub(crate) fn insert_child_window_event_listener(
|
||||
&mut self,
|
||||
target: ChildWindowEventTarget,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: EventCallbackId,
|
||||
capture: bool,
|
||||
once: bool,
|
||||
@@ -141,7 +142,7 @@ impl JsContextHost {
|
||||
self.child_window_event_listeners
|
||||
.entry(target.child_handle())
|
||||
.or_default()
|
||||
.entry(event_type.to_owned())
|
||||
.entry(event_type.to_event_type())
|
||||
.or_default()
|
||||
.push(ChildWindowEventListenerEntry {
|
||||
registration_id,
|
||||
@@ -193,7 +194,7 @@ impl JsContextHost {
|
||||
pub(crate) fn set_child_window_event_handler_content_attribute(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
owner: Option<DomHandle>,
|
||||
) -> Option<EventCallbackId> {
|
||||
match owner {
|
||||
@@ -209,7 +210,7 @@ impl JsContextHost {
|
||||
fn set_child_window_event_handler_state(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
state: EventHandlerPropertyState,
|
||||
) -> Option<EventCallbackId> {
|
||||
let local_window_id = self
|
||||
@@ -235,7 +236,7 @@ impl JsContextHost {
|
||||
self.child_window_event_listeners
|
||||
.entry(handle)
|
||||
.or_default()
|
||||
.entry(event_type.to_owned())
|
||||
.entry(event_type.to_event_type())
|
||||
.or_default()
|
||||
.push(ChildWindowEventListenerEntry {
|
||||
registration_id,
|
||||
@@ -332,7 +333,7 @@ impl JsContextHost {
|
||||
fn remove_child_window_event_handler_property(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<EventCallbackId> {
|
||||
let target_map = self.child_window_event_listeners.get_mut(&handle)?;
|
||||
let entries = target_map.get_mut(event_type)?;
|
||||
@@ -449,7 +450,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: ChildWindowEventTarget,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
capture_only: bool,
|
||||
at_target: bool,
|
||||
@@ -527,7 +528,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
self.dispatch_child_window_event_with_target_override(
|
||||
@@ -539,7 +540,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
legacy_target_override: bool,
|
||||
) {
|
||||
@@ -634,7 +635,7 @@ impl JsContextHost {
|
||||
fn child_window_event_dispatch_slots(
|
||||
&self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
registration_kind: Option<ChildWindowEventRegistrationKind>,
|
||||
capture: Option<bool>,
|
||||
) -> Vec<ChildWindowEventDispatchSlot> {
|
||||
@@ -661,7 +662,7 @@ impl JsContextHost {
|
||||
fn child_window_event_listener_snapshot(
|
||||
&self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
slot: ChildWindowEventDispatchSlot,
|
||||
) -> Option<ChildWindowEventListenerSnapshot> {
|
||||
let entry = self
|
||||
@@ -681,7 +682,7 @@ impl JsContextHost {
|
||||
fn retire_child_window_event_registration(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
slot: ChildWindowEventDispatchSlot,
|
||||
) {
|
||||
if let Some(callback_id) = self.remove_child_window_event_registration_by_id(
|
||||
@@ -698,7 +699,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
slot: ChildWindowEventDispatchSlot,
|
||||
) -> Option<ReadyChildWindowEventListenerInvocation> {
|
||||
let mut snapshot = self.child_window_event_listener_snapshot(handle, event_type, slot)?;
|
||||
@@ -742,14 +743,14 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
ready: ReadyChildWindowEventListenerInvocation,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> (bool, Option<v8::Global<v8::Value>>) {
|
||||
if !self.child_window_event_target_is_current(ready.target) {
|
||||
return (false, None);
|
||||
}
|
||||
if ready.registration_kind == ChildWindowEventRegistrationKind::EventHandlerProperty
|
||||
&& event_type == "beforeunload"
|
||||
&& event_type.is_type("beforeunload")
|
||||
{
|
||||
invoke_prepared_before_unload_event_handler(
|
||||
scope,
|
||||
@@ -786,7 +787,7 @@ impl JsContextHost {
|
||||
fn remove_child_window_event_registration_by_id(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
registration_id: ChildWindowEventRegistrationId,
|
||||
) -> Option<EventCallbackId> {
|
||||
let target_map = self.child_window_event_listeners.get_mut(&handle)?;
|
||||
@@ -807,7 +808,7 @@ impl JsContextHost {
|
||||
pub(crate) fn remove_child_window_event_listener_by_id(
|
||||
&mut self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: EventCallbackId,
|
||||
capture: bool,
|
||||
) -> bool {
|
||||
@@ -869,7 +870,7 @@ impl JsContextHost {
|
||||
pub(crate) fn child_window_event_callback_identities_for_test(
|
||||
&self,
|
||||
handle: DomHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Vec<(
|
||||
Option<crate::native_bridge::WindowExecutionContextIdentity>,
|
||||
Option<crate::native_bridge::WindowExecutionContextIdentity>,
|
||||
@@ -893,11 +894,11 @@ fn child_window_event_type_from_handler_name(name: &str) -> Option<&str> {
|
||||
fn child_window_event_callback_arguments<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
registration_kind: ChildWindowEventRegistrationKind,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> Vec<v8::Local<'s, v8::Value>> {
|
||||
if registration_kind == ChildWindowEventRegistrationKind::EventHandlerProperty
|
||||
&& event_type == "error"
|
||||
&& event_type.is_type("error")
|
||||
&& let Some(arguments) = error_event_handler_arguments(scope, event)
|
||||
{
|
||||
arguments.to_vec()
|
||||
@@ -908,7 +909,7 @@ fn child_window_event_callback_arguments<'s>(
|
||||
|
||||
fn apply_child_window_event_handler_return<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
returned: Option<v8::Global<v8::Value>>,
|
||||
) {
|
||||
@@ -916,7 +917,7 @@ fn apply_child_window_event_handler_return<'s>(
|
||||
return;
|
||||
};
|
||||
let returned = v8::Local::new(scope, returned);
|
||||
let handler_type = if event_type == "error" && event_is_error_event(scope, event) {
|
||||
let handler_type = if event_type.is_type("error") && event_is_error_event(scope, event) {
|
||||
EventHandlerType::OnErrorEventHandler
|
||||
} else {
|
||||
EventHandlerType::EventHandler
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::event_type::EventTypeKey;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
use moli_page_types::{
|
||||
@@ -307,7 +308,7 @@ impl JsContextHost {
|
||||
|
||||
pub(crate) fn schedule_dom_debugger_event_listener_pause_for_target(
|
||||
&self,
|
||||
event_name: &str,
|
||||
event_name: &(impl EventTypeKey + ?Sized),
|
||||
target: EventTargetHandle,
|
||||
) -> Option<RendererDomDebuggerScheduledPause> {
|
||||
if self
|
||||
@@ -332,9 +333,10 @@ impl JsContextHost {
|
||||
|
||||
pub(crate) fn schedule_dom_debugger_event_listener_pause_for_interface(
|
||||
&self,
|
||||
event_name: &str,
|
||||
event_name: &(impl EventTypeKey + ?Sized),
|
||||
target_name: &str,
|
||||
) -> Option<RendererDomDebuggerScheduledPause> {
|
||||
let event_name = event_name.as_str()?;
|
||||
self.dom_debugger_state
|
||||
.schedule_event_listener_pause(event_name, target_name)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::{JsContextHost, WindowExecutionContextIdentity, WindowExecutionContextOwner};
|
||||
use crate::event_type::EventTypeKey;
|
||||
use moli_webidl_callback::{PreparedWebIdlCallbackInterface, WebIdlCallbackInterface};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
@@ -142,7 +143,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback: v8::Local<'s, v8::Object>,
|
||||
relevant_context: v8::Local<'s, v8::Context>,
|
||||
incumbent_context: v8::Local<'s, v8::Context>,
|
||||
@@ -207,7 +208,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback: v8::Local<'s, v8::Object>,
|
||||
capture: bool,
|
||||
) -> bool {
|
||||
@@ -255,7 +256,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler: Option<v8::Local<'s, v8::Object>>,
|
||||
) {
|
||||
let relevant_context = handler
|
||||
@@ -278,7 +279,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler: Option<v8::Local<'s, v8::Function>>,
|
||||
target_context: v8::Local<'s, v8::Context>,
|
||||
) {
|
||||
@@ -301,7 +302,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
handler: Option<v8::Local<'s, v8::Object>>,
|
||||
relevant_context: v8::Local<'s, v8::Context>,
|
||||
incumbent_context: v8::Local<'s, v8::Context>,
|
||||
@@ -323,7 +324,7 @@ impl JsContextHost {
|
||||
&self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> Option<v8::Local<'s, v8::Value>> {
|
||||
match self.event_handler_property_callback_id(target, event_type)? {
|
||||
Some(callback_id) => self.event_callback_value(scope, callback_id),
|
||||
|
||||
@@ -1037,8 +1037,10 @@ pub(crate) struct JsContextHost {
|
||||
completed_child_document_networks:
|
||||
Vec<crate::protocol_types::ChildFrameDocumentNetworkActivitySnapshot>,
|
||||
active_child_subresource_request_scopes: Vec<DomHandle>,
|
||||
child_window_event_listeners:
|
||||
HashMap<DomHandle, IndexMap<String, Vec<ChildWindowEventListenerEntry>>>,
|
||||
child_window_event_listeners: HashMap<
|
||||
DomHandle,
|
||||
IndexMap<crate::event_type::EventType, Vec<ChildWindowEventListenerEntry>>,
|
||||
>,
|
||||
next_child_window_event_registration_id: u64,
|
||||
event_callbacks: event_callbacks::EventCallbackRegistry,
|
||||
active_window_error_report_owners: HashSet<WindowExecutionContextOwner>,
|
||||
|
||||
@@ -4,6 +4,7 @@ use super::{
|
||||
JsContextHost, NavigationHistoryEntrySeed, child_frame_runtime::WINDOW_EVENT_HANDLER_PROPERTIES,
|
||||
};
|
||||
use crate::document_response_decoder::decode_document_response;
|
||||
use crate::event_type::EventTypeKey;
|
||||
use crate::web_api_interfaces;
|
||||
use crate::{
|
||||
content_security_policy::content_security_policy_forces_opaque_origin,
|
||||
@@ -3626,7 +3627,7 @@ impl JsContextHost {
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
target: PopupWindowEventTarget,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
capture_only: bool,
|
||||
at_target: bool,
|
||||
@@ -3649,7 +3650,7 @@ impl JsContextHost {
|
||||
let previous_message_source = self.enter_window_message_source_scope(
|
||||
super::PendingWindowMessageEndpoint::LightweightPopup(popup_id),
|
||||
);
|
||||
let error_arguments = (event_type == "error")
|
||||
let error_arguments = (event_type.is_type("error"))
|
||||
.then(|| crate::context_bootstrap::error_event_handler_arguments(scope, event))
|
||||
.flatten();
|
||||
let ordinary_arguments = [event.into()];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::event_type::EventTypeKey;
|
||||
|
||||
impl JsContextHost {
|
||||
pub(crate) fn is_abort_signal<'s>(
|
||||
@@ -30,7 +31,7 @@ impl JsContextHost {
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
signal: v8::Local<'s, v8::Object>,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: EventCallbackId,
|
||||
capture: bool,
|
||||
) {
|
||||
@@ -73,7 +74,7 @@ impl JsContextHost {
|
||||
pub(crate) fn remove_registered_event_listener_by_id(
|
||||
&mut self,
|
||||
target: crate::document_runtime::EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
callback_id: EventCallbackId,
|
||||
capture: bool,
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::event_type::EventType;
|
||||
use crate::{
|
||||
abort_signal_route::ResolvedAbortSignal,
|
||||
event_listener_args::{AddEventListenerArgs, AddEventListenerOptions},
|
||||
@@ -20,8 +21,8 @@ struct ObservableEventListenerOptions {
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "EventTarget.when")]
|
||||
struct WhenArgs {
|
||||
#[webidl(required, name = "type")]
|
||||
event_type: String,
|
||||
#[webidl(required, name = "type", converter = "raw")]
|
||||
event_type: EventType,
|
||||
#[webidl(with = options_arg)]
|
||||
options: ObservableEventListenerOptions,
|
||||
}
|
||||
@@ -43,7 +44,7 @@ fn options_arg<'s>(
|
||||
/// Observable must not keep an otherwise unreachable standalone target alive.
|
||||
pub(super) struct EventSource {
|
||||
target: v8::Weak<v8::Object>,
|
||||
event_type: String,
|
||||
event_type: EventType,
|
||||
options: webidl::EventListenerOptions,
|
||||
// A WindowProxy can survive navigation; retain only the exact identity,
|
||||
// never a strong context or a lookup that follows the replacement Window.
|
||||
@@ -52,7 +53,7 @@ pub(super) struct EventSource {
|
||||
|
||||
pub(super) struct PreparedEventSource<'s> {
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
event_type: String,
|
||||
event_type: EventType,
|
||||
options: webidl::EventListenerOptions,
|
||||
window_identity: Option<WindowExecutionContextIdentity>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
globalThis.__probeEventListenerTypes = function eventListenerTypeRows(targets, EventCtor, OtherEventCtor = EventCtor) {
|
||||
const rows = [];
|
||||
const check = (name, passed) => rows.push({name, passed: !!passed});
|
||||
const pairs = [['\ud800', '\ufffd'], ['\udc00', '\ufffd'], ['\ud800', '\ud801'],
|
||||
['prefix\ud800\0suffix', 'prefix\ufffd\0suffix'], ['\ud800\ud800', '\ufffd\ufffd'],
|
||||
['\udc00\ud800', '\ufffd\ufffd'], ['load', 'LOAD'], ['é', 'e\u0301'],
|
||||
['type\0', 'type'], ['\ud83d\ude80', '\ufffd\ufffd']];
|
||||
for (const [name, target] of targets) {
|
||||
for (const [raw, other] of pairs) {
|
||||
const prefix = name + ' ' + JSON.stringify([raw, other]);
|
||||
const calls = [];
|
||||
const first = event => calls.push('first:' + (event.type === raw));
|
||||
const second = () => calls.push('second');
|
||||
target.addEventListener(raw, first);
|
||||
target.addEventListener(other, second);
|
||||
const event = new EventCtor(raw);
|
||||
target.dispatchEvent(event);
|
||||
check(prefix + ' distinct dispatch', calls.join(',') === 'first:true');
|
||||
calls.length = 0;
|
||||
target.removeEventListener(other, first);
|
||||
target.dispatchEvent(new EventCtor(raw));
|
||||
check(prefix + ' removal preserves other key', calls.join(',') === 'first:true');
|
||||
target.removeEventListener(raw, first);
|
||||
target.removeEventListener(other, second);
|
||||
calls.length = 0;
|
||||
const both = () => calls.push('both');
|
||||
target.addEventListener(raw, both, {once:true});
|
||||
target.addEventListener(other, both, {once:true});
|
||||
target.addEventListener(raw, both, {once:false});
|
||||
target.dispatchEvent(new EventCtor(raw));
|
||||
target.dispatchEvent(new EventCtor(raw));
|
||||
target.dispatchEvent(new EventCtor(other));
|
||||
target.dispatchEvent(new EventCtor(other));
|
||||
check(prefix + ' duplicate and once identity', calls.join(',') === 'both,both');
|
||||
target.removeEventListener(raw, both);
|
||||
target.removeEventListener(other, both);
|
||||
calls.length = 0;
|
||||
const controller = new AbortController();
|
||||
target.addEventListener(raw, both, {signal:controller.signal});
|
||||
target.addEventListener(other, both);
|
||||
controller.abort();
|
||||
target.dispatchEvent(new EventCtor(raw));
|
||||
target.dispatchEvent(new EventCtor(other));
|
||||
check(prefix + ' abort removes exact key', calls.join(',') === 'both');
|
||||
target.removeEventListener(raw, both);
|
||||
target.removeEventListener(other, both);
|
||||
calls.length = 0;
|
||||
let conversions = 0;
|
||||
target.addEventListener({[Symbol.toPrimitive](hint) {
|
||||
conversions++; check(prefix + ' string hint', hint === 'string'); return raw;
|
||||
}}, both);
|
||||
target.dispatchEvent(new OtherEventCtor(raw));
|
||||
check(prefix + ' one conversion and cross realm event', conversions === 1 && calls.join(',') === 'both');
|
||||
target.removeEventListener({toString() { conversions++; return raw; }}, both);
|
||||
target.dispatchEvent(new EventCtor(raw));
|
||||
check(prefix + ' converted removal', conversions === 2 && calls.join(',') === 'both');
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
};
|
||||
(() => {
|
||||
const child = document.querySelector('#child').contentWindow;
|
||||
const channel = new MessageChannel();
|
||||
const popup = open('about:blank');
|
||||
if (!popup) throw new Error('listener identity popup did not open');
|
||||
const detached = document.implementation.createHTMLDocument('listener types');
|
||||
const element = document.createElement('button');
|
||||
document.body.appendChild(element);
|
||||
const targets = [
|
||||
['EventTarget', new EventTarget()], ['Window', window], ['Document', document],
|
||||
['connected Element', element], ['windowless Document', detached],
|
||||
['detached select', detached.createElement('select')], ['child Window', child],
|
||||
['popup Window', popup],
|
||||
['MessagePort', channel.port1], ['AbortSignal', new AbortController().signal],
|
||||
['FileReader', new FileReader()], ['XMLHttpRequest', new XMLHttpRequest()],
|
||||
['XMLHttpRequestUpload', new XMLHttpRequest().upload],
|
||||
['MediaQueryList', matchMedia('all')], ['Performance', performance],
|
||||
['FontFaceSet', document.fonts]
|
||||
];
|
||||
let rows;
|
||||
try {
|
||||
rows = __probeEventListenerTypes(targets, Event, child.Event);
|
||||
} finally {
|
||||
channel.port1.close();
|
||||
channel.port2.close();
|
||||
popup.close();
|
||||
element.remove();
|
||||
}
|
||||
globalThis.__eventListenerTypeResults = {rows, passed: rows.filter(row => row.passed).length,
|
||||
total: rows.length, targets: targets.map(([name]) => name)};
|
||||
return rows.every(row => row.passed) || JSON.stringify(rows.filter(row => !row.passed));
|
||||
})()
|
||||
@@ -0,0 +1,45 @@
|
||||
use super::service_worker_drain::drain_service_worker_test_turn;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn event_listener_types_preserve_utf16_identity_for_native_and_wrapper_local_targets() {
|
||||
let mut vm = new_storage_html_test_vm("https://event-listener-type.test/");
|
||||
vm.eval("document.body.innerHTML = '<iframe id=child></iframe>';")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
vm.eval(include_str!("event_listener_type.js")).unwrap(),
|
||||
"true"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn event_listener_types_preserve_utf16_identity_in_workers_indexed_db_and_observable() {
|
||||
let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).unwrap();
|
||||
let (mut vm, browser_context_runtime) =
|
||||
new_service_worker_page_test_vm_with_loader_and_browser_context_runtime(
|
||||
"https://event-listener-type-runtime.test/",
|
||||
&loader,
|
||||
);
|
||||
vm.eval("document.body.innerHTML = '<iframe id=child></iframe>';")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
vm.eval(include_str!("event_listener_type.js")).unwrap(),
|
||||
"true"
|
||||
);
|
||||
vm.eval(&format!(
|
||||
"({}).then(result => globalThis.__listenerTypeDone = result, error => globalThis.__listenerTypeDone = String(error));",
|
||||
include_str!("event_listener_type_runtime.js")
|
||||
)).unwrap();
|
||||
tokio::time::timeout(std::time::Duration::from_secs(10), async {
|
||||
while vm
|
||||
.eval("globalThis.__listenerTypeDone !== undefined")
|
||||
.unwrap()
|
||||
!= "true"
|
||||
{
|
||||
drain_service_worker_test_turn(&mut vm, &browser_context_runtime, &loader).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("worker and IndexedDB listener type fixture should settle");
|
||||
assert_eq!(vm.eval("globalThis.__listenerTypeDone").unwrap(), "true");
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
(async () => {
|
||||
const rows = [];
|
||||
const source = `const probe = ${__probeEventListenerTypes.toString()};
|
||||
const channel = new MessageChannel();
|
||||
try {
|
||||
postMessage(probe([['WorkerGlobalScope', self], ['worker EventTarget', new EventTarget()],
|
||||
['worker AbortSignal', new AbortController().signal], ['worker MessagePort', channel.port1]], Event));
|
||||
} finally { channel.port1.close(); channel.port2.close(); }`;
|
||||
const url = URL.createObjectURL(new Blob([source], {type:'text/javascript'}));
|
||||
const worker = new Worker(url);
|
||||
try {
|
||||
rows.push(...await new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error('worker listener identity timeout')), 5000);
|
||||
worker.onmessage = event => { clearTimeout(timer); resolve(event.data); };
|
||||
worker.onerror = event => { clearTimeout(timer); event.preventDefault(); reject(new Error(event.message)); };
|
||||
}));
|
||||
} finally { worker.terminate(); URL.revokeObjectURL(url); }
|
||||
const name = 'event-listener-type-' + crypto.randomUUID();
|
||||
let database;
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(name, 1);
|
||||
request.onupgradeneeded = () => {
|
||||
database = request.result;
|
||||
const transaction = request.transaction;
|
||||
const store = database.createObjectStore('store');
|
||||
rows.push(...__probeEventListenerTypes([
|
||||
['IDBDatabase', database], ['IDBOpenDBRequest', request],
|
||||
['IDBTransaction', transaction], ['IDBRequest', store.get('key')]], Event));
|
||||
};
|
||||
request.onsuccess = () => { database = request.result; resolve(); };
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
} finally {
|
||||
database?.close();
|
||||
await new Promise((resolve, reject) => {
|
||||
const request = indexedDB.deleteDatabase(name);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onblocked = () => reject(new Error('listener identity database remained open'));
|
||||
});
|
||||
}
|
||||
const channel = new MessageChannel();
|
||||
try {
|
||||
for (const target of [new EventTarget(), channel.port1]) {
|
||||
const raw = '\ud800', replacement = '\ufffd', calls = [];
|
||||
const controller = new AbortController();
|
||||
target.when(raw).subscribe(() => calls.push('raw'), {signal:controller.signal});
|
||||
target.when(replacement).subscribe(() => calls.push('replacement'), {signal:controller.signal});
|
||||
target.dispatchEvent(new Event(raw));
|
||||
rows.push({name:'Observable exact event type', passed:calls.join(',') === 'raw'});
|
||||
controller.abort();
|
||||
target.dispatchEvent(new Event(raw));
|
||||
rows.push({name:'Observable abort removes exact type', passed:calls.join(',') === 'raw'});
|
||||
}
|
||||
} finally { channel.port1.close(); channel.port2.close(); }
|
||||
globalThis.__eventListenerRuntimeResults = {rows, passed:rows.filter(row => row.passed).length, total:rows.length};
|
||||
const combined = [...__eventListenerTypeResults.rows, ...rows];
|
||||
globalThis.__eventListenerTypeResults = {rows:combined, passed:combined.filter(row => row.passed).length, total:combined.length};
|
||||
return combined.every(row => row.passed) || JSON.stringify(combined.filter(row => !row.passed));
|
||||
})()
|
||||
@@ -28,6 +28,7 @@ mod document_open_origin;
|
||||
mod event_constructor_type;
|
||||
mod event_handlers;
|
||||
mod event_listener_options;
|
||||
mod event_listener_type;
|
||||
mod events_selection_storage;
|
||||
mod fontface_descriptors;
|
||||
mod fullscreen;
|
||||
|
||||
@@ -10,7 +10,7 @@ pub use moli_v8_util::{
|
||||
initialize_ecmascript_intrinsic_registry, new_null_prototype_object, object_bool_property,
|
||||
object_chain_contains, object_defined_string_property, object_non_empty_string_property,
|
||||
object_number_property, object_own_static_bool_property,
|
||||
object_own_static_string_property, object_property_as_array, object_property_as_object,
|
||||
object_own_static_string_property, object_property_as_object,
|
||||
object_string_property, private_key, register_ecmascript_intrinsic,
|
||||
registered_ecmascript_constructor, registered_ecmascript_prototype, set_null_prototype,
|
||||
set_private_value, throw_range_error, throw_type_error, v8_json_parse, v8_string, v8str,
|
||||
|
||||
@@ -50,6 +50,7 @@ use super::{
|
||||
use crate::event_listener_args::{
|
||||
AddEventListenerArgs, RemoveEventListenerArgs, parse_listener_args,
|
||||
};
|
||||
use crate::event_type::{EventType, EventTypeKey};
|
||||
use crate::web_api_interfaces;
|
||||
use moli_webapi_declare::{WebApiFunctionTemplate, WebApiObject};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
@@ -280,7 +281,7 @@ fn register_dom_event_target_listener<'s>(
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if call.event_type == "selectionchange"
|
||||
if call.event_type.is_type("selectionchange")
|
||||
&& target == EventTargetHandle::Node(host.document_handle())
|
||||
{
|
||||
define_non_enumerable_static_bool_property(
|
||||
@@ -300,7 +301,9 @@ fn register_dom_event_target_listener<'s>(
|
||||
capture,
|
||||
);
|
||||
}
|
||||
if !matches!(target, EventTargetHandle::ChildWindow(_)) && call.event_type == "animationstart" {
|
||||
if !matches!(target, EventTargetHandle::ChildWindow(_))
|
||||
&& call.event_type.is_type("animationstart")
|
||||
{
|
||||
queue_animation_start_for_listener_target(scope, host_ptr, target);
|
||||
}
|
||||
}
|
||||
@@ -308,11 +311,11 @@ fn register_dom_event_target_listener<'s>(
|
||||
fn default_passive_value(
|
||||
host: &JsContextHost,
|
||||
target: EventTargetHandle,
|
||||
event_type: &str,
|
||||
event_type: &(impl EventTypeKey + ?Sized),
|
||||
) -> bool {
|
||||
if !matches!(
|
||||
event_type,
|
||||
"touchstart" | "touchmove" | "wheel" | "mousewheel"
|
||||
event_type.as_str(),
|
||||
Some("touchstart" | "touchmove" | "wheel" | "mousewheel")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -459,7 +462,9 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
}
|
||||
|
||||
set_event_trusted(scope, event, false);
|
||||
let event_type = event_type_string(scope, event);
|
||||
let Some(event_type) = event_type_value(scope, event) else {
|
||||
return;
|
||||
};
|
||||
if let Some(receiver) = window_receiver {
|
||||
let Some(binding) = receiver.resolve_live_binding(host) else {
|
||||
rv.set_bool(true);
|
||||
@@ -470,12 +475,7 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
let event = v8::Local::new(scope, &event);
|
||||
let host = unsafe { &mut *host_ptr };
|
||||
if let OwnerDispatchScope::Child(handle) = dispatch_scope {
|
||||
host.dispatch_child_window_event(
|
||||
scope,
|
||||
handle,
|
||||
event_type.as_deref().unwrap_or_default(),
|
||||
event,
|
||||
);
|
||||
host.dispatch_child_window_event(scope, handle, &event_type, event);
|
||||
Ok(!crate::context_bootstrap::event_bool_attribute(
|
||||
scope,
|
||||
event,
|
||||
@@ -484,7 +484,7 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
} else {
|
||||
host.dispatch_public_event(scope, host_ptr, EventTargetHandle::Window, event)
|
||||
.map(|dispatch| {
|
||||
if let Some(event_type) = event_type.as_deref() {
|
||||
if let Some(event_type) = event_type.as_str() {
|
||||
increment_performance_event_count(scope, event_type);
|
||||
}
|
||||
dispatch.dispatch_event_return_value()
|
||||
@@ -502,7 +502,7 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let click_activation_target = if event_type.as_deref() == Some("click")
|
||||
let click_activation_target = if event_type.is_type("click")
|
||||
&& event_is_mouse_event(scope, event)
|
||||
&& let EventTargetHandle::Node(handle) = target
|
||||
{
|
||||
@@ -520,7 +520,7 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
match host.dispatch_public_event(scope, host_ptr, target, event) {
|
||||
Ok(dispatch) => {
|
||||
let return_value = dispatch.dispatch_event_return_value();
|
||||
if let Some(event_type) = event_type.as_deref() {
|
||||
if let Some(event_type) = event_type.as_str() {
|
||||
increment_performance_event_count(scope, event_type);
|
||||
}
|
||||
if let Some(activation) = legacy_click_activation {
|
||||
@@ -540,14 +540,14 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
|
||||
}
|
||||
}
|
||||
|
||||
fn event_type_string(
|
||||
fn event_type_value(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
event: v8::Local<'_, v8::Object>,
|
||||
) -> Option<String> {
|
||||
) -> Option<EventType> {
|
||||
crate::context_bootstrap::event_backing(scope, event)
|
||||
.get(scope, v8str(scope, "type").into())
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
.map(|value| EventType::from_v8(scope, value))
|
||||
}
|
||||
|
||||
pub(super) fn window_set_timeout_callback<'s>(
|
||||
|
||||
Reference in New Issue
Block a user