mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 16:01:05 +00:00
fix: construct trusted abort events and reset AbortSignal dispatch state
Worker abort events now use the signal realm's intrinsic Event constructor and carry the native trust flag, including timeout and stream cancellation. Share AbortSignal dispatch setup and cleanup between Window and Worker. Script dispatch validates Event state and clears trust, active redispatch throws, and completion clears currentTarget, eventPhase, the event path, and propagation flags. Pre-stopped events skip listeners and remain reusable. Cover controller, composite, stream and timeout events with overwritten or missing public constructors, plus synthetic dispatch and native event reuse, in Window, child frames and dedicated workers.
This commit is contained in:
@@ -3760,6 +3760,7 @@ dom/abort/abort-signal-any.any.js?moli-wpt-any=window
|
||||
dom/abort/abort-signal-timeout.html
|
||||
dom/abort/crashtests/any-on-abort.html
|
||||
dom/abort/crashtests/timeout-close.html
|
||||
dom/abort/event.any.js?moli-wpt-any=dedicatedworker
|
||||
dom/abort/event.any.js?moli-wpt-any=window
|
||||
dom/abort/reason-constructor.html
|
||||
dom/attributes-are-nodes.html
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
async function runAbortSignalEventProbe(scenario) {
|
||||
if (typeof document !== 'undefined' && document.readyState !== 'complete') {
|
||||
await new Promise(resolve => addEventListener('load', resolve, {once: true}));
|
||||
}
|
||||
// Let document lifecycle events finish before replacing the public constructor.
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
const errors = [];
|
||||
const events = [];
|
||||
let reads = 0;
|
||||
const check = (condition, label) => { if (!condition) errors.push(label); };
|
||||
const during = (event, signal, trusted, label) => {
|
||||
check(event.target === signal && event.srcElement === signal, label + ': target');
|
||||
check(event.currentTarget === signal && event.eventPhase === 2, label + ': dispatch fields');
|
||||
check(event.isTrusted === trusted, label + ': trust');
|
||||
const path = event.composedPath();
|
||||
check(path.length === 1 && path[0] === signal, label + ': path');
|
||||
};
|
||||
const after = (event, signal, label) => {
|
||||
check(event.target === signal, label + ': target retained');
|
||||
check(event.currentTarget === null && event.eventPhase === 0, label + ': dispatch fields cleared');
|
||||
check(event.composedPath().length === 0 && !event.cancelBubble, label + ': propagation cleared');
|
||||
};
|
||||
const throws = (callback, name, label) => {
|
||||
let caught = false;
|
||||
try { callback(); }
|
||||
catch (error) { caught = true; check(error.name === name, label + ': exception type'); }
|
||||
check(caught, label + ': throws');
|
||||
};
|
||||
|
||||
try {
|
||||
if (scenario.startsWith('native-')) {
|
||||
const original = Object.getOwnPropertyDescriptor(globalThis, 'Event');
|
||||
const observed = [];
|
||||
const onerror = event => { errors.push('uncaught: ' + event.message); event.preventDefault(); };
|
||||
addEventListener('error', onerror);
|
||||
let deadline;
|
||||
try {
|
||||
if (scenario === 'native-getter') {
|
||||
Object.defineProperty(globalThis, 'Event', {configurable: true, get() {
|
||||
reads++; throw new Error('public Event getter');
|
||||
}});
|
||||
} else if (scenario === 'native-replacement') {
|
||||
Object.defineProperty(globalThis, 'Event', {configurable: true, value: function () {
|
||||
reads++; throw new Error('public Event constructor');
|
||||
}});
|
||||
} else if (scenario === 'native-missing') {
|
||||
delete globalThis.Event;
|
||||
}
|
||||
const record = (signal, label) => signal.addEventListener('abort', function (event) {
|
||||
events.push(label);
|
||||
observed.push([event, signal, label]);
|
||||
during(event, signal, true, label);
|
||||
check(this === signal && event.type === 'abort', label + ': callback');
|
||||
check(!event.bubbles && !event.cancelable && !event.composed, label + ': defaults');
|
||||
event.preventDefault();
|
||||
check(!event.defaultPrevented, label + ': uncancelable');
|
||||
});
|
||||
const controller = new AbortController();
|
||||
const composite = AbortSignal.any([controller.signal]);
|
||||
record(controller.signal, 'source');
|
||||
record(composite, 'composite');
|
||||
const reason = {cancel: true};
|
||||
controller.abort(reason);
|
||||
check(events.join(',') === 'source,composite', 'controller events are synchronous');
|
||||
check(composite.reason === reason, 'composite reason identity');
|
||||
const writable = new WritableStream({start(controller) { record(controller.signal, 'stream'); }});
|
||||
await writable.abort(reason);
|
||||
const timeout = AbortSignal.timeout(0);
|
||||
record(timeout, 'timeout');
|
||||
await new Promise(resolve => {
|
||||
deadline = setTimeout(() => { errors.push('timeout abort event missing'); resolve(); }, 500);
|
||||
timeout.onabort = () => { clearTimeout(deadline); resolve(); };
|
||||
});
|
||||
check(timeout.reason.name === 'TimeoutError', 'timeout reason');
|
||||
// Promise reactions can run at callback cleanup before native dispatch ends.
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
} finally {
|
||||
clearTimeout(deadline);
|
||||
Object.defineProperty(globalThis, 'Event', original);
|
||||
removeEventListener('error', onerror);
|
||||
}
|
||||
check(reads === 0, 'native event construction does not access public Event');
|
||||
check(new Set(observed.map(([event]) => event)).size === 4, 'each signal gets a distinct event');
|
||||
for (const [event, signal, label] of observed) {
|
||||
check(event instanceof Event, label + ': intrinsic prototype');
|
||||
check(event.isTrusted, label + ': stays trusted after native dispatch');
|
||||
after(event, signal, label);
|
||||
}
|
||||
} else if (scenario === 'script') {
|
||||
const source = new AbortController();
|
||||
const recipient = new AbortController();
|
||||
let nativeEvent;
|
||||
source.signal.addEventListener('abort', event => {
|
||||
nativeEvent = event;
|
||||
events.push('native');
|
||||
during(event, source.signal, true, 'native');
|
||||
throws(() => recipient.signal.dispatchEvent(event), 'InvalidStateError', 'active redispatch');
|
||||
during(event, source.signal, true, 'rejected redispatch preserves active event');
|
||||
event.initEvent('changed', true, true);
|
||||
check(event.type === 'abort' && !event.bubbles && !event.cancelable, 'active initEvent is ignored');
|
||||
event.stopImmediatePropagation();
|
||||
});
|
||||
source.signal.addEventListener('abort', () => errors.push('stopped listener fired'));
|
||||
source.abort('reason');
|
||||
after(nativeEvent, source.signal, 'native');
|
||||
recipient.signal.onabort = event => {
|
||||
events.push('replayed');
|
||||
during(event, recipient.signal, false, 'replayed');
|
||||
check(!recipient.signal.aborted && recipient.signal.reason === undefined, 'script event leaves signal pending');
|
||||
event.stopPropagation();
|
||||
};
|
||||
check(recipient.signal.dispatchEvent(nativeEvent), 'replayed abort is uncancelable');
|
||||
check(!nativeEvent.isTrusted, 'redispatch clears trust');
|
||||
after(nativeEvent, recipient.signal, 'replayed');
|
||||
const cancelable = new Event('probe', {cancelable: true});
|
||||
recipient.signal.addEventListener('probe', event => {
|
||||
events.push('passive');
|
||||
during(event, recipient.signal, false, 'synthetic');
|
||||
const before = event.defaultPrevented;
|
||||
event.preventDefault();
|
||||
check(event.defaultPrevented === before, 'passive cancellation is ignored');
|
||||
}, {passive: true});
|
||||
recipient.signal.addEventListener('probe', event => {
|
||||
events.push('active');
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}, {once: true});
|
||||
recipient.signal.addEventListener('probe', () => events.push('tail'));
|
||||
check(!recipient.signal.dispatchEvent(cancelable), 'canceling a synthetic event returns false');
|
||||
after(cancelable, recipient.signal, 'synthetic');
|
||||
check(!recipient.signal.dispatchEvent(cancelable), 'redispatch preserves cancellation');
|
||||
after(cancelable, recipient.signal, 'synthetic again');
|
||||
const stopped = new Event('pre-stopped');
|
||||
recipient.signal.addEventListener('pre-stopped', () => events.push('pre-stopped'));
|
||||
stopped.stopPropagation();
|
||||
recipient.signal.dispatchEvent(stopped);
|
||||
check(!events.includes('pre-stopped'), 'pre-stopped event skips listeners');
|
||||
after(stopped, recipient.signal, 'pre-stopped');
|
||||
recipient.signal.dispatchEvent(stopped);
|
||||
check(events.includes('pre-stopped'), 'pre-stopped event can be dispatched again');
|
||||
for (const value of [null, undefined, {}, {type: 'abort'}, Object.create(Event.prototype)]) {
|
||||
throws(() => recipient.signal.dispatchEvent(value), 'TypeError', 'non-Event argument');
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
throws(() => recipient.signal.dispatchEvent(document.createEvent('Event')), 'InvalidStateError', 'uninitialized Event');
|
||||
}
|
||||
} else {
|
||||
errors.push('unknown scenario ' + scenario);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push(String(error));
|
||||
}
|
||||
return {errors, events, reads};
|
||||
}
|
||||
@@ -49,3 +49,61 @@ async fn abort_signal_any_runs_listener_removal_before_dispatch_and_snapshots_ea
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn assert_abort_signal_events(scenarios: &[&str], events: &[&str]) -> Result<()> {
|
||||
let server = FixtureServer::spawn().await?;
|
||||
let browser = Browser::new(AppConfig::default())?;
|
||||
for scenario in scenarios {
|
||||
let source = format!(
|
||||
"{}\nrunAbortSignalEventProbe({}).then(finish, error => finish({{error: String(error)}}));",
|
||||
include_str!("../fixtures/runtime/abort_signal_events.js"),
|
||||
serde_json::to_string(scenario)?,
|
||||
);
|
||||
for target in ["window", "child", "worker"] {
|
||||
let observed = tokio::time::timeout(
|
||||
Duration::from_secs(10),
|
||||
super::event_dispatch::run_probe(&browser, &server, target, &source),
|
||||
)
|
||||
.await??;
|
||||
assert_eq!(
|
||||
observed,
|
||||
serde_json::json!({"errors": [], "events": events, "reads": 0}),
|
||||
"{scenario}/{target}"
|
||||
);
|
||||
}
|
||||
}
|
||||
server.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn abort_signal_native_events_use_intrinsic_constructors_and_preserve_trust() -> Result<()> {
|
||||
assert_abort_signal_events(
|
||||
&[
|
||||
"native-original",
|
||||
"native-getter",
|
||||
"native-replacement",
|
||||
"native-missing",
|
||||
],
|
||||
&["source", "composite", "stream", "timeout"],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn abort_signal_script_dispatch_validates_events_resets_trust_and_clears_dispatch_state()
|
||||
-> Result<()> {
|
||||
assert_abort_signal_events(
|
||||
&["script"],
|
||||
&[
|
||||
"native",
|
||||
"replayed",
|
||||
"passive",
|
||||
"active",
|
||||
"passive",
|
||||
"tail",
|
||||
"pre-stopped",
|
||||
],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub(crate) mod abort_signal_events;
|
||||
mod animation_runtime;
|
||||
mod assets;
|
||||
pub(crate) mod bridge_descriptor;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
use super::events::{clear_event_dispatch_fields, set_event_dispatch_fields};
|
||||
use super::{
|
||||
EVENT_PASSIVE_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, EVENT_STOP_PROPAGATION_SLOT,
|
||||
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::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)> {
|
||||
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)))
|
||||
else {
|
||||
throw_type_error(scope, "AbortSignal.dispatchEvent requires an Event.");
|
||||
return None;
|
||||
};
|
||||
if !initialized || event_is_dispatching(scope, event) {
|
||||
let error = new_dom_exception_value(
|
||||
scope,
|
||||
"The event is uninitialized or already being dispatched.",
|
||||
"InvalidStateError",
|
||||
);
|
||||
scope.throw_exception(error);
|
||||
return None;
|
||||
}
|
||||
set_event_trusted(scope, event, false);
|
||||
let event_type = event
|
||||
.get(scope, v8str(scope, "type").into())?
|
||||
.to_string(scope)?
|
||||
.to_rust_string_lossy(scope);
|
||||
Some((event, event_type))
|
||||
}
|
||||
|
||||
// Even a pre-stopped event needs finish_dispatch, but its listeners must not run.
|
||||
pub(crate) fn begin_dispatch<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
signal: v8::Local<'s, v8::Object>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) -> bool {
|
||||
set_event_dispatch_fields(scope, signal, event);
|
||||
let path = v8::Array::new_with_elements(scope, &[signal.into()]);
|
||||
set_event_composed_path(scope, event, path);
|
||||
!event_internal_bool_flag(scope, event, EVENT_STOP_PROPAGATION_SLOT)
|
||||
}
|
||||
|
||||
pub(crate) fn finish_dispatch<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
clear_event_dispatch_fields(scope, event);
|
||||
clear_event_composed_path(scope, event);
|
||||
for flag in [
|
||||
EVENT_STOP_PROPAGATION_SLOT,
|
||||
EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT,
|
||||
EVENT_PASSIVE_SLOT,
|
||||
] {
|
||||
set_event_internal_flag(scope, event, flag, false);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::web_api_interfaces;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use super::super::document_runtime::EventTargetHandle;
|
||||
use super::super::util::{get_private_value, set_private_value, v8_string, v8str};
|
||||
use super::super::util::{get_private_value, set_private_value, v8str};
|
||||
use crate::context_bootstrap::{MessagePortEventListenerId, new_dom_exception_value};
|
||||
use crate::types::MessagePortId;
|
||||
use moli_webapi_declare::WebApiObject;
|
||||
@@ -151,19 +151,6 @@ impl AbortStore {
|
||||
.map(|value| value as u32)
|
||||
}
|
||||
|
||||
fn define_hidden_value(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
object: v8::Local<'_, v8::Object>,
|
||||
key: &str,
|
||||
value: v8::Local<'_, v8::Value>,
|
||||
) {
|
||||
let Some(key) = v8_string(scope, key) else {
|
||||
return;
|
||||
};
|
||||
let _ =
|
||||
object.define_own_property(scope, key.into(), value, v8::PropertyAttribute::DONT_ENUM);
|
||||
}
|
||||
|
||||
fn init_signal(
|
||||
&mut self,
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::{AbortDispatchSnapshot, AbortStore};
|
||||
use super::AbortDispatchSnapshot;
|
||||
use crate::context_bootstrap::abort_signal_events;
|
||||
use crate::context_bootstrap::{
|
||||
EVENT_PASSIVE_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, construct_original_event,
|
||||
event_internal_bool_flag, set_event_internal_flag,
|
||||
@@ -47,9 +48,6 @@ pub(super) fn dispatch_abort<'s>(
|
||||
let Some(event) = construct_original_event(scope, "abort") else {
|
||||
return;
|
||||
};
|
||||
AbortStore::define_hidden_value(scope, event, "target", signal.into());
|
||||
AbortStore::define_hidden_value(scope, event, "currentTarget", signal.into());
|
||||
|
||||
invoke_abort_event_callbacks(
|
||||
scope,
|
||||
host_ptr,
|
||||
@@ -78,6 +76,10 @@ pub(super) fn invoke_abort_event_callbacks<'s>(
|
||||
event_type: &str,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
if !abort_signal_events::begin_dispatch(scope, signal, event) {
|
||||
abort_signal_events::finish_dispatch(scope, event);
|
||||
return;
|
||||
}
|
||||
for listener in dispatch_snapshot.listeners {
|
||||
let Some(listener) = (unsafe { &mut *host_ptr })
|
||||
.claim_abort_signal_event_listener_for_dispatch(
|
||||
@@ -105,10 +107,8 @@ pub(super) fn invoke_abort_event_callbacks<'s>(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if event_internal_bool_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT) {
|
||||
return;
|
||||
}
|
||||
if event_type == "abort"
|
||||
if !event_internal_bool_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT)
|
||||
&& event_type == "abort"
|
||||
&& let Some(onabort) = dispatch_snapshot.onabort
|
||||
{
|
||||
let onabort = v8::Local::new(scope, &onabort);
|
||||
@@ -120,4 +120,5 @@ pub(super) fn invoke_abort_event_callbacks<'s>(
|
||||
&[event.into()],
|
||||
);
|
||||
}
|
||||
abort_signal_events::finish_dispatch(scope, event);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::AbortStore;
|
||||
use super::event::{invoke_abort_event_callbacks, local_object_in_scope};
|
||||
use crate::context_bootstrap::abort_signal_events;
|
||||
use crate::util::{context_host_ptr_from_global_bridge, v8str};
|
||||
use crate::webidl;
|
||||
|
||||
@@ -109,18 +110,12 @@ pub(crate) fn abort_signal_dispatch_event_callback<'s>(
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Ok(event) = v8::Local::<v8::Object>::try_from(parsed.event) else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Some(signal_id) = AbortStore::signal_id_from_object(scope, signal) else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Some(event_type) = event
|
||||
.get(scope, v8str(scope, "type").into())
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|s| s.to_rust_string_lossy(scope))
|
||||
let Some((event, event_type)) =
|
||||
abort_signal_events::prepare_script_dispatch(scope, parsed.event)
|
||||
else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
@@ -132,8 +127,6 @@ pub(crate) fn abort_signal_dispatch_event_callback<'s>(
|
||||
// callback signature that `FunctionTemplate::builder(...)` expects.
|
||||
let signal = local_object_in_scope(scope, signal);
|
||||
let event = local_object_in_scope(scope, event);
|
||||
AbortStore::define_hidden_value(scope, event, "target", signal.into());
|
||||
AbortStore::define_hidden_value(scope, event, "currentTarget", signal.into());
|
||||
let Some(dispatch_snapshot) = unsafe { &mut *host_ptr }
|
||||
.native_bridge_mut()
|
||||
.abort
|
||||
|
||||
@@ -78,19 +78,6 @@ impl WorkerAbortStore {
|
||||
self.next_controller_id
|
||||
}
|
||||
|
||||
fn define_hidden_value(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
object: v8::Local<'_, v8::Object>,
|
||||
key: &str,
|
||||
value: v8::Local<'_, v8::Value>,
|
||||
) {
|
||||
let Some(key) = v8_string(scope, key) else {
|
||||
return;
|
||||
};
|
||||
let _ =
|
||||
object.define_own_property(scope, key.into(), value, v8::PropertyAttribute::DONT_ENUM);
|
||||
}
|
||||
|
||||
pub(super) fn signal_id_from_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
|
||||
@@ -4,12 +4,13 @@ use moli_webidl_callback::{PreparedWebIdlCallbackInterface, WebIdlCallbackInterf
|
||||
|
||||
use super::{WorkerAbortSignalState, WorkerAbortStore, local_object_in_scope, worker_abort_store};
|
||||
use crate::callback_invocation::{CallbackInvocation, CallbackInvocationOutcome, CallbackInvoker};
|
||||
use crate::context_bootstrap::abort_signal_events;
|
||||
use crate::context_bootstrap::{
|
||||
EVENT_PASSIVE_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, event_internal_bool_flag,
|
||||
set_event_internal_flag,
|
||||
EVENT_PASSIVE_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, construct_original_event,
|
||||
event_internal_bool_flag, set_event_internal_flag,
|
||||
};
|
||||
use crate::exception_reporting::{CallbackExceptionLogLevel, invoke_callback};
|
||||
use crate::util::{v8_string, v8str};
|
||||
use crate::util::v8str;
|
||||
use crate::webidl;
|
||||
|
||||
/// Identity of one EventListener registration inside a worker run.
|
||||
@@ -281,18 +282,12 @@ pub(crate) fn worker_abort_signal_dispatch_event_callback<'s>(
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Ok(event) = v8::Local::<v8::Object>::try_from(parsed.event) else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Some(signal_id) = WorkerAbortStore::signal_id_from_object(scope, signal) else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
};
|
||||
let Some(event_type) = event
|
||||
.get(scope, v8str(scope, "type").into())
|
||||
.and_then(|value| value.to_string(scope))
|
||||
.map(|value| value.to_rust_string_lossy(scope))
|
||||
let Some((event, event_type)) =
|
||||
abort_signal_events::prepare_script_dispatch(scope, parsed.event)
|
||||
else {
|
||||
rv.set_bool(false);
|
||||
return;
|
||||
@@ -301,8 +296,6 @@ pub(crate) fn worker_abort_signal_dispatch_event_callback<'s>(
|
||||
let default_prevented_key = v8str(scope, "defaultPrevented");
|
||||
let signal = local_object_in_scope(scope, signal);
|
||||
let event = local_object_in_scope(scope, event);
|
||||
WorkerAbortStore::define_hidden_value(scope, event, "target", signal.into());
|
||||
WorkerAbortStore::define_hidden_value(scope, event, "currentTarget", signal.into());
|
||||
let dispatch_snapshot = store
|
||||
.borrow()
|
||||
.signal_state(signal_id)
|
||||
@@ -331,21 +324,13 @@ pub(super) fn dispatch_abort<'s>(
|
||||
signal_id: u32,
|
||||
dispatch_snapshot: WorkerAbortDispatchSnapshot,
|
||||
) {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let Some(event_ctor) = global
|
||||
.get(scope, v8str(scope, "Event").into())
|
||||
.and_then(|value| v8::Local::<v8::Function>::try_from(value).ok())
|
||||
else {
|
||||
let context = signal
|
||||
.get_creation_context(scope)
|
||||
.unwrap_or_else(|| scope.get_current_context());
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
let Some(event) = construct_original_event(scope, "abort") else {
|
||||
return;
|
||||
};
|
||||
let Some(event_type) = v8_string(scope, "abort") else {
|
||||
return;
|
||||
};
|
||||
let Some(event) = event_ctor.new_instance(scope, &[event_type.into()]) else {
|
||||
return;
|
||||
};
|
||||
WorkerAbortStore::define_hidden_value(scope, event, "target", signal.into());
|
||||
WorkerAbortStore::define_hidden_value(scope, event, "currentTarget", signal.into());
|
||||
dispatch_event_callbacks(
|
||||
store,
|
||||
scope,
|
||||
@@ -367,6 +352,10 @@ fn dispatch_event_callbacks<'s>(
|
||||
dispatch_snapshot: WorkerAbortDispatchSnapshot,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
if !abort_signal_events::begin_dispatch(scope, signal, event) {
|
||||
abort_signal_events::finish_dispatch(scope, event);
|
||||
return;
|
||||
}
|
||||
for listener_id in dispatch_snapshot.listener_ids {
|
||||
let listener =
|
||||
store
|
||||
@@ -389,10 +378,9 @@ fn dispatch_event_callbacks<'s>(
|
||||
}
|
||||
}
|
||||
|
||||
if event_internal_bool_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT) {
|
||||
return;
|
||||
}
|
||||
if let Some(onabort) = dispatch_snapshot.onabort {
|
||||
if !event_internal_bool_flag(scope, event, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT)
|
||||
&& let Some(onabort) = dispatch_snapshot.onabort
|
||||
{
|
||||
let onabort = v8::Local::new(scope, &onabort);
|
||||
let _ = invoke_callback(
|
||||
scope,
|
||||
@@ -402,6 +390,7 @@ fn dispatch_event_callbacks<'s>(
|
||||
&[event.into()],
|
||||
);
|
||||
}
|
||||
abort_signal_events::finish_dispatch(scope, event);
|
||||
}
|
||||
|
||||
fn invoke_worker_abort_event_listener<'s>(
|
||||
|
||||
Reference in New Issue
Block a user