mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 08:00:59 +00:00
fix(events): preserve native dispatch state and listener lifetime
Set and clear child Window event dispatch fields, and clear Worker propagation flags after native delivery. Remove once listeners before invocation and skip listeners removed by an earlier callback, including during synchronous nested dispatch. Add Window, child Window, and Worker regression probes for active event state, redispatch after stopImmediatePropagation, and once/listener removal during nested dispatch.
This commit is contained in:
@@ -10,6 +10,9 @@ use moli_fetch::FetchConfig;
|
||||
use support::FixtureServer;
|
||||
use tokio::time::Duration;
|
||||
|
||||
#[path = "web_apis/event_dispatch.rs"]
|
||||
mod event_dispatch;
|
||||
|
||||
fn diagnostic_global<'a>(
|
||||
page: &'a moli_core::page::Page,
|
||||
name: &str,
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
use super::*;
|
||||
|
||||
fn markup_url(server: &FixtureServer, markup: &str) -> String {
|
||||
let mut url = url::Url::parse(&server.url("/compat/child-dynamic-markup-document")).unwrap();
|
||||
url.query_pairs_mut().append_pair("markup", markup);
|
||||
url.into()
|
||||
}
|
||||
|
||||
pub(super) async fn run_probe(
|
||||
browser: &Browser,
|
||||
server: &FixtureServer,
|
||||
target: &str,
|
||||
source: &str,
|
||||
) -> Result<serde_json::Value> {
|
||||
let markup = if target == "worker" {
|
||||
let source = serde_json::to_string(&format!(
|
||||
"self.finish = value => postMessage(value);\n{source}"
|
||||
))?;
|
||||
format!(
|
||||
r#"<!doctype html><script>
|
||||
const worker = new Worker(URL.createObjectURL(new Blob([{source}], {{type: 'text/javascript'}})));
|
||||
window.done = new Promise(resolve => {{
|
||||
worker.onmessage = event => {{ resolve(event.data); worker.terminate(); }};
|
||||
}});
|
||||
worker.postMessage('go');
|
||||
</script>"#
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"<!doctype html><script>self.done = new Promise(resolve => self.finish = resolve);\n{source}</script>"
|
||||
)
|
||||
};
|
||||
let url = markup_url(server, &markup);
|
||||
let url = if target == "child" {
|
||||
markup_url(
|
||||
server,
|
||||
&format!(
|
||||
"<!doctype html><iframe id=target src=\"{}\"></iframe>",
|
||||
url.replace('&', "&")
|
||||
),
|
||||
)
|
||||
} else {
|
||||
url
|
||||
};
|
||||
let mut page = browser.fetch(&url).await?;
|
||||
let expression = if target == "child" {
|
||||
"document.getElementById('target').contentWindow.done.then(JSON.stringify)"
|
||||
} else {
|
||||
"done.then(JSON.stringify)"
|
||||
};
|
||||
let value = page
|
||||
.evaluate_runtime_expression_with_await_async(expression, true)
|
||||
.await?;
|
||||
Ok(serde_json::from_str(
|
||||
value["value"].as_str().expect("callback probe result"),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn native_message_events_keep_dispatch_state_and_clear_propagation_flags() -> Result<()> {
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
let source = r#"
|
||||
const log = [];
|
||||
addEventListener('message', event => {
|
||||
log.push(['during', event.currentTarget === self, event.eventPhase === 2,
|
||||
typeof document === 'undefined' || window.event === event]);
|
||||
event.initEvent('mutated', true, true);
|
||||
log.push(event.type);
|
||||
event.stopImmediatePropagation();
|
||||
setTimeout(() => {
|
||||
log.push(['after', event.currentTarget === null, event.eventPhase === 0,
|
||||
typeof document === 'undefined' || window.event === undefined]);
|
||||
dispatchEvent(event);
|
||||
finish(log);
|
||||
}, 0);
|
||||
}, {once: true});
|
||||
addEventListener('message', () => log.push('second'));
|
||||
if (typeof document !== 'undefined') postMessage('go', '*');
|
||||
"#;
|
||||
for target in ["window", "child", "worker"] {
|
||||
let observed = run_probe(&browser, &server, target, source).await?;
|
||||
assert_eq!(
|
||||
observed,
|
||||
serde_json::json!([
|
||||
["during", true, true, true],
|
||||
"message",
|
||||
["after", true, true, true],
|
||||
"second"
|
||||
]),
|
||||
"target={target}"
|
||||
);
|
||||
}
|
||||
server.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn native_message_listeners_observe_once_and_removal_during_nested_dispatch() -> Result<()> {
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
let source = r#"
|
||||
const log = [];
|
||||
let calls = 0;
|
||||
const removed = () => log.push('unexpected removed listener');
|
||||
addEventListener('message', () => {
|
||||
log.push('once');
|
||||
if (++calls > 1) return;
|
||||
removeEventListener('message', removed);
|
||||
dispatchEvent(new Event('message'));
|
||||
}, {once: true});
|
||||
addEventListener('message', removed);
|
||||
addEventListener('message', () => {
|
||||
log.push('retained');
|
||||
setTimeout(() => finish(log), 0);
|
||||
});
|
||||
if (typeof document !== 'undefined') postMessage('go', '*');
|
||||
"#;
|
||||
for target in ["window", "child", "worker"] {
|
||||
let observed = run_probe(&browser, &server, target, source).await?;
|
||||
assert_eq!(
|
||||
observed,
|
||||
serde_json::json!(["once", "retained", "retained"]),
|
||||
"target={target}"
|
||||
);
|
||||
}
|
||||
server.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -311,9 +311,9 @@ pub(crate) use self::media_queries::{
|
||||
mark_simple_event_target_slot, simple_event_target_add_event_listener_callback,
|
||||
simple_event_target_dispatch_event_callback, simple_event_target_inspector_listener_snapshots,
|
||||
simple_event_target_remove_event_listener_callback, simple_event_target_slot_name,
|
||||
simple_object_event_listeners_snapshot, simple_object_event_remove_listener_value_for_type,
|
||||
simple_object_event_set_ordered_handler, simple_object_event_target_add_listener,
|
||||
simple_object_event_target_remove_listener,
|
||||
simple_object_event_listener_is_registered, simple_object_event_listeners_snapshot,
|
||||
simple_object_event_remove_listener_value_for_type, simple_object_event_set_ordered_handler,
|
||||
simple_object_event_target_add_listener, simple_object_event_target_remove_listener,
|
||||
};
|
||||
pub(crate) use self::message_ports::{
|
||||
MessagePortDeliveryRunResult, MessagePortEventListenerId, MessagePortEventListenerSnapshot,
|
||||
|
||||
@@ -14,9 +14,9 @@ pub(crate) use self::events::{
|
||||
mark_simple_event_target_slot, simple_event_target_add_event_listener_callback,
|
||||
simple_event_target_dispatch_event_callback, simple_event_target_inspector_listener_snapshots,
|
||||
simple_event_target_remove_event_listener_callback, simple_event_target_slot_name,
|
||||
simple_object_event_listeners_snapshot, simple_object_event_remove_listener_value_for_type,
|
||||
simple_object_event_set_ordered_handler, simple_object_event_target_add_listener,
|
||||
simple_object_event_target_remove_listener,
|
||||
simple_object_event_listener_is_registered, simple_object_event_listeners_snapshot,
|
||||
simple_object_event_remove_listener_value_for_type, simple_object_event_set_ordered_handler,
|
||||
simple_object_event_target_add_listener, simple_object_event_target_remove_listener,
|
||||
};
|
||||
pub(super) use self::events::{
|
||||
media_query_list_add_event_listener_callback, media_query_list_add_listener_callback,
|
||||
|
||||
@@ -20,8 +20,8 @@ pub(crate) use simple_event_target::{
|
||||
mark_simple_event_target_slot, simple_event_target_add_event_listener_callback,
|
||||
simple_event_target_dispatch_event_callback, simple_event_target_inspector_listener_snapshots,
|
||||
simple_event_target_remove_event_listener_callback, simple_event_target_slot_name,
|
||||
simple_object_event_listeners_snapshot, simple_object_event_remove_listener_value_for_type,
|
||||
simple_object_event_set_ordered_handler, simple_object_event_target_add_listener,
|
||||
simple_object_event_target_register_webidl_listener,
|
||||
simple_object_event_listener_is_registered, simple_object_event_listeners_snapshot,
|
||||
simple_object_event_remove_listener_value_for_type, simple_object_event_set_ordered_handler,
|
||||
simple_object_event_target_add_listener, simple_object_event_target_register_webidl_listener,
|
||||
simple_object_event_target_remove_listener,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,11 @@ use super::{
|
||||
JsContextHost, OwnerDispatchScope, child_frame_runtime::WINDOW_EVENT_HANDLER_PROPERTIES,
|
||||
};
|
||||
use crate::{
|
||||
context_bootstrap::{EventHandlerType, apply_event_handler_return_value, event_is_error_event},
|
||||
context_bootstrap::{
|
||||
EVENT_DISPATCHING_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, EVENT_STOP_PROPAGATION_SLOT,
|
||||
EventHandlerType, apply_event_handler_return_value, event_is_error_event,
|
||||
set_event_internal_flag,
|
||||
},
|
||||
document_runtime::DomHandle,
|
||||
document_runtime::EventTargetHandle,
|
||||
exception_reporting::invoke_event_handler,
|
||||
@@ -497,6 +501,12 @@ impl JsContextHost {
|
||||
};
|
||||
let _ = event.set(scope, v8str(scope, "target").into(), target);
|
||||
let _ = event.set(scope, v8str(scope, "currentTarget").into(), window.into());
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "eventPhase").into(),
|
||||
v8::Integer::new(scope, 2).into(),
|
||||
);
|
||||
set_event_internal_flag(scope, event, EVENT_DISPATCHING_SLOT, true);
|
||||
|
||||
if event_type == "load" {
|
||||
install_child_body_load_attribute_handler_if_needed(scope, self, handle);
|
||||
@@ -545,6 +555,19 @@ impl JsContextHost {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "eventPhase").into(),
|
||||
v8::Integer::new(scope, 0).into(),
|
||||
);
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "currentTarget").into(),
|
||||
v8::null(scope).into(),
|
||||
);
|
||||
set_event_internal_flag(scope, event, EVENT_DISPATCHING_SLOT, false);
|
||||
set_event_internal_flag(scope, event, EVENT_STOP_PROPAGATION_SLOT, false);
|
||||
set_event_internal_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, false);
|
||||
self.pop_child_subresource_request_scope();
|
||||
restore_child_window_event_dispatch(scope, previous_active_child_window);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ use crate::context_bootstrap::{
|
||||
dispatch_message_port_events_for_port_collecting_errors,
|
||||
dispatch_service_worker_controller_change, ensure_message_port_wrapper_for_id,
|
||||
event_internal_bool_flag, mark_event_trusted, runtime_message_allowed_for_current_target,
|
||||
set_event_internal_flag, simple_object_event_listeners_snapshot,
|
||||
simple_object_event_remove_listener_value_for_type,
|
||||
set_event_internal_flag, simple_object_event_listener_is_registered,
|
||||
simple_object_event_listeners_snapshot, simple_object_event_remove_listener_value_for_type,
|
||||
structured_deserialize_value_for_message_event,
|
||||
};
|
||||
use crate::exception_reporting::{
|
||||
@@ -695,6 +695,8 @@ fn clear_event_dispatch_fields(scope: &mut v8::PinScope<'_, '_>, event: v8::Loca
|
||||
EVENT_DISPATCHING_SLOT,
|
||||
v8::Boolean::new(scope, false).into(),
|
||||
);
|
||||
set_event_internal_flag(scope, event, EVENT_STOP_PROPAGATION_SLOT, false);
|
||||
set_event_internal_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, false);
|
||||
}
|
||||
|
||||
fn worker_event_prevent_default_callback(
|
||||
@@ -859,6 +861,7 @@ fn dispatch_worker_promise_rejection_event<'s>(
|
||||
listener,
|
||||
global,
|
||||
event,
|
||||
event_type,
|
||||
"WorkerGlobalScope promise rejection listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -873,16 +876,6 @@ fn dispatch_worker_promise_rejection_event<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let allows_default = !event_bool_property(scope, event, "defaultPrevented");
|
||||
@@ -997,8 +990,31 @@ fn invoke_worker_listener<'s>(
|
||||
listener: &SimpleObjectEventListenerSnapshot<'s>,
|
||||
target: v8::Local<'s, v8::Object>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
event_type: &str,
|
||||
callback_name: &str,
|
||||
) -> Result<(), WorkerExceptionError> {
|
||||
if event_stop_immediate_propagation(scope, event)
|
||||
|| !simple_object_event_listener_is_registered(
|
||||
scope,
|
||||
target,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
target,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
let arguments = [event.into()];
|
||||
let invocation = listener.invocation(target.into(), &arguments, Some(event));
|
||||
match CallbackInvoker::invoke(
|
||||
@@ -1088,6 +1104,7 @@ pub(super) fn dispatch_worker_error_event<'s>(
|
||||
listener,
|
||||
global,
|
||||
event,
|
||||
"error",
|
||||
"WorkerGlobalScope error listener",
|
||||
) {
|
||||
let (nested_report, nested_exception) = *nested_report;
|
||||
@@ -1099,16 +1116,6 @@ pub(super) fn dispatch_worker_error_event<'s>(
|
||||
parent_tx,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"error",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let handled = event_bool_property(scope, event, "defaultPrevented");
|
||||
@@ -1332,6 +1339,7 @@ fn dispatch_service_worker_lifecycle_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
event_type,
|
||||
"ServiceWorkerGlobalScope lifecycle listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1346,16 +1354,6 @@ fn dispatch_service_worker_lifecycle_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -1458,6 +1456,7 @@ fn dispatch_service_worker_fetch_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
"fetch",
|
||||
"ServiceWorkerGlobalScope fetch listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1472,16 +1471,6 @@ fn dispatch_service_worker_fetch_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"fetch",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
if event_stop_immediate_propagation(scope, event_object) {
|
||||
break;
|
||||
}
|
||||
@@ -1549,6 +1538,7 @@ fn dispatch_service_worker_message_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
event_type,
|
||||
"ServiceWorkerGlobalScope message listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1563,16 +1553,6 @@ fn dispatch_service_worker_message_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -1636,6 +1616,7 @@ fn dispatch_service_worker_notification_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
event_type,
|
||||
"ServiceWorkerGlobalScope notification listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1650,16 +1631,6 @@ fn dispatch_service_worker_notification_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -1711,6 +1682,7 @@ fn dispatch_service_worker_push_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
"push",
|
||||
"ServiceWorkerGlobalScope push listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1725,16 +1697,6 @@ fn dispatch_service_worker_push_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"push",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -1790,6 +1752,7 @@ fn dispatch_service_worker_sync_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
"sync",
|
||||
"ServiceWorkerGlobalScope sync listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1804,16 +1767,6 @@ fn dispatch_service_worker_sync_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"sync",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -1873,6 +1826,7 @@ fn dispatch_service_worker_periodic_sync_event_in_context<'s>(
|
||||
listener,
|
||||
global,
|
||||
event_object,
|
||||
"periodicsync",
|
||||
"ServiceWorkerGlobalScope periodicsync listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -1887,16 +1841,6 @@ fn dispatch_service_worker_periodic_sync_event_in_context<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"periodicsync",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
perform_worker_microtask_checkpoint_and_report_pending_promise_rejections(scope);
|
||||
clear_event_dispatch_fields(scope, event_object);
|
||||
@@ -4403,6 +4347,7 @@ fn dispatch_worker_global_message_event<'s>(
|
||||
listener,
|
||||
global,
|
||||
event,
|
||||
event_type,
|
||||
"WorkerGlobalScope message listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -4417,16 +4362,6 @@ fn dispatch_worker_global_message_event<'s>(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
event_type,
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clear_event_dispatch_fields(scope, event);
|
||||
@@ -4520,6 +4455,7 @@ pub(super) fn dispatch_shared_worker_connect_event(
|
||||
listener,
|
||||
global,
|
||||
event,
|
||||
"connect",
|
||||
"SharedWorkerGlobalScope connect listener",
|
||||
) {
|
||||
let (report, exception) = *error;
|
||||
@@ -4534,16 +4470,6 @@ pub(super) fn dispatch_shared_worker_connect_event(
|
||||
script_url,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
simple_object_event_remove_listener_value_for_type(
|
||||
scope,
|
||||
global,
|
||||
WORKER_GLOBAL_LISTENERS_SLOT,
|
||||
"connect",
|
||||
listener.original,
|
||||
listener.capture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clear_event_dispatch_fields(scope, event);
|
||||
|
||||
Reference in New Issue
Block a user