mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 16:01:05 +00:00
fix(events): apply exact handler return cancellation
This commit is contained in:
@@ -3236,7 +3236,6 @@ html/webappapis/scripting/events/event-handler-attributes-frameset-alt.html
|
||||
html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
|
||||
html/webappapis/scripting/events/event-handler-attributes-window-frameset.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
|
||||
html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html
|
||||
html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-detached-iframe.html
|
||||
html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html
|
||||
|
||||
@@ -7329,6 +7329,7 @@ html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthe
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
|
||||
html/webappapis/scripting/events/event-handler-processing-algorithm.html
|
||||
html/webappapis/scripting/events/event-handler-sourcetext.html
|
||||
html/webappapis/scripting/events/eventhandler-cancellation.html
|
||||
|
||||
@@ -232,10 +232,10 @@ pub(crate) use self::css_stylesheet_runtime::{
|
||||
pub(crate) use self::dom_rect::build_dom_rect_object;
|
||||
pub(crate) use self::events::{
|
||||
EVENT_DISPATCHING_SLOT, 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, event_is_error_event, event_is_mouse_event,
|
||||
initialize_event_object, mark_event_trusted, set_event_composed_path, set_event_internal_flag,
|
||||
set_event_trusted,
|
||||
EVENT_STOP_PROPAGATION_SLOT, EventHandlerType, apply_event_handler_return_value,
|
||||
clear_event_composed_path, event_initialized, event_internal_bool_flag, event_is_dispatching,
|
||||
event_is_error_event, event_is_mouse_event, initialize_event_object, mark_event_trusted,
|
||||
set_event_composed_path, set_event_internal_flag, set_event_trusted,
|
||||
};
|
||||
pub(crate) use self::file_api::{
|
||||
DataTransferStringCallbackTask, DataTransferStringCallbackTaskEffect,
|
||||
|
||||
@@ -340,6 +340,7 @@ pub(super) use base::{
|
||||
set_event_initialized,
|
||||
};
|
||||
pub(super) use kind::EventSubclassKind;
|
||||
pub(crate) use methods::{EventHandlerType, apply_event_handler_return_value};
|
||||
pub(super) use methods::{
|
||||
event_cancel_bubble_getter_function, event_cancel_bubble_setter_function,
|
||||
event_composed_path_callback, event_prevent_default_callback,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum EventHandlerType {
|
||||
EventHandler,
|
||||
OnErrorEventHandler,
|
||||
}
|
||||
|
||||
pub(super) fn set_event_default_prevented(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
event: v8::Local<'_, v8::Object>,
|
||||
@@ -9,19 +15,40 @@ pub(super) fn set_event_default_prevented(
|
||||
let _ = event.define_own_property(scope, key.into(), value, Default::default());
|
||||
}
|
||||
|
||||
fn prevent_event_default_if_allowed<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
) {
|
||||
if !object_bool_property(scope, event, "cancelable").unwrap_or(false)
|
||||
|| event_internal_bool_flag(scope, event, EVENT_PASSIVE_SLOT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
set_event_default_prevented(scope, event);
|
||||
}
|
||||
|
||||
pub(crate) fn apply_event_handler_return_value<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
event: v8::Local<'s, v8::Object>,
|
||||
returned: v8::Local<'s, v8::Value>,
|
||||
handler_type: EventHandlerType,
|
||||
) {
|
||||
let cancels = match handler_type {
|
||||
EventHandlerType::EventHandler => returned.is_boolean() && !returned.boolean_value(scope),
|
||||
EventHandlerType::OnErrorEventHandler => returned.is_true(),
|
||||
};
|
||||
if cancels {
|
||||
prevent_event_default_if_allowed(scope, event);
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn event_prevent_default_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
_rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let event = args.this();
|
||||
if !object_bool_property(scope, event, "cancelable").unwrap_or(false) {
|
||||
return;
|
||||
}
|
||||
if event_internal_bool_flag(scope, event, EVENT_PASSIVE_SLOT) {
|
||||
return;
|
||||
}
|
||||
set_event_default_prevented(scope, event);
|
||||
prevent_event_default_if_allowed(scope, event);
|
||||
}
|
||||
|
||||
pub(in crate::context_bootstrap) fn event_return_value_getter_function<'s>(
|
||||
|
||||
+21
-2
@@ -6,6 +6,7 @@ use crate::{
|
||||
clear_event_dispatch_fields, event_internal_bool_flag, set_event_composed_path,
|
||||
set_event_dispatch_fields, set_event_internal_flag,
|
||||
},
|
||||
context_bootstrap::{EventHandlerType, apply_event_handler_return_value},
|
||||
exception_reporting::CallbackExceptionLogLevel,
|
||||
host::report_event_callback_exception,
|
||||
util::{context_host_ptr_from_global_bridge, serialize_v8_array},
|
||||
@@ -78,7 +79,7 @@ pub(crate) fn dispatch_simple_event_target_event<'s>(
|
||||
.get_creation_context(scope)
|
||||
.unwrap_or(current_context);
|
||||
let incumbent_context = scope.get_incumbent_context().unwrap_or(current_context);
|
||||
let _ = invoke_simple_event_callback(
|
||||
let returned = invoke_simple_event_callback(
|
||||
scope,
|
||||
event_type,
|
||||
&format!("simple event target {handler_name}"),
|
||||
@@ -90,6 +91,14 @@ pub(crate) fn dispatch_simple_event_target_event<'s>(
|
||||
&[event.into()],
|
||||
event,
|
||||
);
|
||||
if let Some(returned) = returned {
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
event,
|
||||
v8::Local::new(scope, &returned),
|
||||
EventHandlerType::EventHandler,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +131,7 @@ pub(crate) fn dispatch_simple_event_target_event<'s>(
|
||||
);
|
||||
}
|
||||
set_event_internal_flag(scope, event, EVENT_PASSIVE_SLOT, listener.passive);
|
||||
let _ = invoke_simple_event_listener(
|
||||
let returned = invoke_simple_event_listener(
|
||||
scope,
|
||||
event_type,
|
||||
&format!("simple event target {event_type} listener"),
|
||||
@@ -131,6 +140,16 @@ pub(crate) fn dispatch_simple_event_target_event<'s>(
|
||||
&[event.into()],
|
||||
event,
|
||||
);
|
||||
if listener.handler_slot.is_some()
|
||||
&& let Some(returned) = returned
|
||||
{
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
event,
|
||||
v8::Local::new(scope, &returned),
|
||||
EventHandlerType::EventHandler,
|
||||
);
|
||||
}
|
||||
set_event_internal_flag(scope, event, EVENT_PASSIVE_SLOT, false);
|
||||
if event_stop_immediate_propagation(scope, event) {
|
||||
break 'phases;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
CHILD_BROWSING_CONTEXT_HANDLE_SLOT, MessagePortRealmBinding,
|
||||
ensure_message_port_wrapper_for_id_in_realm,
|
||||
CHILD_BROWSING_CONTEXT_HANDLE_SLOT, EventHandlerType, MessagePortRealmBinding,
|
||||
apply_event_handler_return_value, ensure_message_port_wrapper_for_id_in_realm,
|
||||
events::{clear_event_dispatch_fields, set_event_dispatch_fields},
|
||||
invoke_simple_event_listener,
|
||||
navigation_serialize::{
|
||||
@@ -817,12 +817,12 @@ fn dispatch_shared_worker_error_event<'s>(
|
||||
);
|
||||
if listener.handler_slot.as_deref() == Some(SHARED_WORKER_ONERROR_SLOT)
|
||||
&& let Some(returned) = callback_result
|
||||
&& v8::Local::new(scope, &returned).boolean_value(scope)
|
||||
{
|
||||
let _ = event.set(
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
v8str(scope, "defaultPrevented").into(),
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
event,
|
||||
v8::Local::new(scope, &returned),
|
||||
EventHandlerType::EventHandler,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
|
||||
@@ -5,7 +5,7 @@ use moli_webapi_declare::WebApiObject;
|
||||
|
||||
use super::{WORKER_LISTENERS_SLOT, WORKER_ONERROR_SLOT};
|
||||
use crate::context_bootstrap::{
|
||||
dispatch_simple_event_target_event,
|
||||
EventHandlerType, apply_event_handler_return_value, dispatch_simple_event_target_event,
|
||||
events::{clear_event_dispatch_fields, set_event_dispatch_fields},
|
||||
invoke_simple_event_listener, simple_object_event_listeners_snapshot,
|
||||
simple_object_event_remove_listener_value_for_type,
|
||||
@@ -313,12 +313,12 @@ pub(crate) fn dispatch_worker_error_event_with_kind<'s>(
|
||||
);
|
||||
if listener.handler_slot.as_deref() == Some(WORKER_ONERROR_SLOT)
|
||||
&& let Some(returned) = callback_result
|
||||
&& v8::Local::new(scope, &returned).boolean_value(scope)
|
||||
{
|
||||
let _ = event.set(
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
v8str(scope, "defaultPrevented").into(),
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
event,
|
||||
v8::Local::new(scope, &returned),
|
||||
EventHandlerType::EventHandler,
|
||||
);
|
||||
}
|
||||
if listener.once {
|
||||
|
||||
@@ -1442,6 +1442,72 @@ async fn constructor_onerror_has_event_fields() {
|
||||
panic!("timed out waiting for error event fields");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn constructor_onerror_return_true_does_not_cancel_syntax_error_event() {
|
||||
ensure_v8();
|
||||
let (mut isolate, ctx) = setup_worker_context();
|
||||
|
||||
eval_ok(
|
||||
&mut isolate,
|
||||
&ctx,
|
||||
r#"
|
||||
var errorReturnObservations = null;
|
||||
var w = new Worker("function {");
|
||||
w.onerror = function(e) {
|
||||
errorReturnObservations = [arguments.length, e.defaultPrevented === true];
|
||||
return true;
|
||||
};
|
||||
w.addEventListener("error", function(e) {
|
||||
errorReturnObservations.push(e.defaultPrevented === true);
|
||||
});
|
||||
"#,
|
||||
);
|
||||
|
||||
for _ in 0..50 {
|
||||
sleep(Duration::from_millis(20)).await;
|
||||
let result = eval_ok(
|
||||
&mut isolate,
|
||||
&ctx,
|
||||
"__drainWorkerMessages(w); JSON.stringify(errorReturnObservations)",
|
||||
);
|
||||
if result != "null" {
|
||||
assert_eq!(result, "[1,false,false]");
|
||||
eval_ok(&mut isolate, &ctx, "w.terminate()");
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic!("timed out waiting for worker syntax error event");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn worker_event_handler_only_boolean_false_cancels_synthetic_event() {
|
||||
ensure_v8();
|
||||
let (mut isolate, ctx) = setup_worker_context();
|
||||
|
||||
let result = eval_ok(
|
||||
&mut isolate,
|
||||
&ctx,
|
||||
r#"
|
||||
(() => {
|
||||
const w = new Worker("");
|
||||
const run = returned => {
|
||||
w.onerror = () => returned;
|
||||
const event = { type: "error", cancelable: true, defaultPrevented: false };
|
||||
return [w.dispatchEvent(event), event.defaultPrevented];
|
||||
};
|
||||
const observations = [run(true), run(false), run(0), run("")];
|
||||
w.terminate();
|
||||
return JSON.stringify(observations);
|
||||
})()
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
"[[true,false],[false,true],[true,false],[true,false]]"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn constructor_error_listener_receives_event() {
|
||||
ensure_v8();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
context_bootstrap::{
|
||||
CHILD_BROWSING_CONTEXT_HANDLE_SLOT, clear_event_composed_path, event_is_error_event,
|
||||
mark_event_trusted, set_event_composed_path,
|
||||
CHILD_BROWSING_CONTEXT_HANDLE_SLOT, EventHandlerType, apply_event_handler_return_value,
|
||||
clear_event_composed_path, event_is_error_event, mark_event_trusted,
|
||||
set_event_composed_path,
|
||||
},
|
||||
document_runtime::DocumentRuntime,
|
||||
dom_parser::DOM_PARSER_FOREIGN_NODE_SLOT,
|
||||
@@ -452,13 +453,12 @@ fn invoke_registered_event_handler<'s>(
|
||||
);
|
||||
if let Some(returned) = returned {
|
||||
let returned = v8::Local::new(scope, returned);
|
||||
if returned.boolean_value(scope) {
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "defaultPrevented").into(),
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
);
|
||||
}
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
event,
|
||||
returned,
|
||||
EventHandlerType::OnErrorEventHandler,
|
||||
);
|
||||
}
|
||||
if let Some(timing_started) = timing_started {
|
||||
tracing::info!(
|
||||
@@ -486,13 +486,7 @@ fn invoke_registered_event_handler<'s>(
|
||||
);
|
||||
if let Some(returned) = returned {
|
||||
let returned = v8::Local::new(scope, returned);
|
||||
if returned.is_boolean() && !returned.boolean_value(scope) {
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "defaultPrevented").into(),
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
);
|
||||
}
|
||||
apply_event_handler_return_value(scope, event, returned, EventHandlerType::EventHandler);
|
||||
}
|
||||
if let Some(timing_started) = timing_started {
|
||||
tracing::info!(
|
||||
|
||||
@@ -6722,7 +6722,7 @@ async fn worker_runtime_error_promise_reaction_can_prevent_window_propagation()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn worker_runtime_error_return_truthy_suppresses_window_onerror() {
|
||||
async fn worker_runtime_error_return_non_boolean_truthy_propagates_to_window_onerror() {
|
||||
run_page_vm_async_test(async move {
|
||||
let (base_url, server) = spawn_single_response_http_server(
|
||||
"HTTP/1.1 200 OK",
|
||||
@@ -6759,8 +6759,8 @@ async fn worker_runtime_error_return_truthy_suppresses_window_onerror() {
|
||||
)?;
|
||||
drive_websocket_until_done(
|
||||
&mut page_vm,
|
||||
"String(globalThis.__workerDone === true)",
|
||||
"truthy worker onerror should suppress window.onerror",
|
||||
"String(globalThis.__workerDone === true && globalThis.__windowErrorCalled === true)",
|
||||
"non-boolean truthy worker onerror should propagate to window.onerror",
|
||||
)
|
||||
.await?;
|
||||
page_vm.vm_mut().eval(
|
||||
@@ -6768,14 +6768,14 @@ async fn worker_runtime_error_return_truthy_suppresses_window_onerror() {
|
||||
)
|
||||
})
|
||||
.await
|
||||
.expect("worker truthy onerror suppression test should run on owner lane");
|
||||
.expect("worker non-boolean truthy onerror propagation test should run on owner lane");
|
||||
|
||||
server
|
||||
.await
|
||||
.expect("worker truthy onerror suppression server should finish");
|
||||
.expect("worker non-boolean truthy onerror propagation server should finish");
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"windowErrorCalled":false,"listenerSawDefaultPrevented":true}"#
|
||||
r#"{"windowErrorCalled":true,"listenerSawDefaultPrevented":false}"#
|
||||
);
|
||||
})
|
||||
.await;
|
||||
|
||||
@@ -1719,6 +1719,31 @@ fn synthetic_error_event_uses_normal_window_event_handler_arguments() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_error_handler_only_boolean_true_cancels() {
|
||||
let mut vm = new_storage_test_vm("https://window-error-return-value.test/");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const run = returned => {
|
||||
window.onerror = () => returned;
|
||||
const event = new ErrorEvent("error", { cancelable: true });
|
||||
return [window.dispatchEvent(event), event.defaultPrevented];
|
||||
};
|
||||
return JSON.stringify([run(true), run(1), run(false), run(0)]);
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("window error handler return-value probe should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
"[[false,true],[true,false],[true,false],[true,false]]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_onerror_null_assignment_supersedes_uncompiled_body_attribute() {
|
||||
let mut vm = new_storage_test_vm("https://window-onerror-null-override.test/");
|
||||
|
||||
@@ -12,7 +12,8 @@ use moli_webapi_declare::WebApiObject;
|
||||
use crate::callback_invocation::{CallbackInvocationOutcome, CallbackInvoker};
|
||||
use crate::context_bootstrap::{
|
||||
EVENT_DISPATCHING_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, EVENT_STOP_PROPAGATION_SLOT,
|
||||
SimpleObjectEventListenerSnapshot, dispatch_message_port_events_for_port_collecting_errors,
|
||||
EventHandlerType, SimpleObjectEventListenerSnapshot, apply_event_handler_return_value,
|
||||
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,
|
||||
@@ -1048,13 +1049,12 @@ pub(super) fn dispatch_worker_error_event<'s>(
|
||||
],
|
||||
) {
|
||||
Ok(returned) => {
|
||||
if v8::Local::new(scope, &returned).is_true() {
|
||||
let _ = event.set(
|
||||
scope,
|
||||
v8str(scope, "defaultPrevented").into(),
|
||||
v8::Boolean::new(scope, true).into(),
|
||||
);
|
||||
}
|
||||
apply_event_handler_return_value(
|
||||
scope,
|
||||
event,
|
||||
v8::Local::new(scope, &returned),
|
||||
EventHandlerType::OnErrorEventHandler,
|
||||
);
|
||||
}
|
||||
Err(nested_report) => {
|
||||
report_exception_to_parent(
|
||||
|
||||
@@ -92,13 +92,16 @@ promise_test(async function () {
|
||||
});
|
||||
|
||||
const defaultPrevented = await wait_for(listenerSeen, "worker error listener");
|
||||
await wait_for(handlerSeen, "worker.onerror truthy handler");
|
||||
await wait_for(handlerSeen, "worker.onerror non-boolean truthy handler");
|
||||
await wait(30);
|
||||
|
||||
assert_true(defaultPrevented, "truthy worker.onerror should mark the error as handled");
|
||||
assert_false(
|
||||
defaultPrevented,
|
||||
"a non-boolean truthy worker.onerror return should not mark the error as handled",
|
||||
);
|
||||
} finally {
|
||||
window.onerror = previousOnerror;
|
||||
worker.terminate();
|
||||
}
|
||||
}, "Truthy worker.onerror marks the worker error as handled for later listeners");
|
||||
}, "A non-boolean truthy Worker.onerror return does not cancel the error event");
|
||||
</script>
|
||||
|
||||
@@ -97,17 +97,20 @@ async function assert_worker_error_prevent_default_suppresses_window() {
|
||||
}
|
||||
}
|
||||
|
||||
async function assert_truthy_worker_onerror_suppresses_window() {
|
||||
async function assert_non_boolean_truthy_worker_onerror_propagates_to_window() {
|
||||
const previousOnerror = window.onerror;
|
||||
let worker = null;
|
||||
let windowErrorCalled = false;
|
||||
let handlerSeen = false;
|
||||
|
||||
try {
|
||||
window.onerror = function () {
|
||||
windowErrorCalled = true;
|
||||
return true;
|
||||
};
|
||||
const windowSeen = new Promise(function (resolve) {
|
||||
window.onerror = function () {
|
||||
windowErrorCalled = true;
|
||||
resolve();
|
||||
return true;
|
||||
};
|
||||
});
|
||||
|
||||
worker = new Worker(worker_error_url());
|
||||
worker.onerror = function () {
|
||||
@@ -125,12 +128,19 @@ async function assert_truthy_worker_onerror_suppresses_window() {
|
||||
{ once: true },
|
||||
);
|
||||
}),
|
||||
"worker error listener after truthy onerror",
|
||||
"worker error listener after non-boolean truthy onerror",
|
||||
);
|
||||
await wait_for(windowSeen, "window.onerror after non-boolean truthy worker.onerror");
|
||||
|
||||
assert_true(handlerSeen, "worker.onerror should run before the later listener");
|
||||
assert_true(listenerDefaultPrevented, "truthy worker.onerror should mark the worker error handled");
|
||||
assert_false(windowErrorCalled, "truthy worker.onerror should suppress window.onerror");
|
||||
assert_false(
|
||||
listenerDefaultPrevented,
|
||||
"a non-boolean truthy worker.onerror return should not mark the worker error handled",
|
||||
);
|
||||
assert_true(
|
||||
windowErrorCalled,
|
||||
"a non-boolean truthy worker.onerror return should propagate to window.onerror",
|
||||
);
|
||||
} finally {
|
||||
window.onerror = previousOnerror;
|
||||
if (worker) {
|
||||
@@ -142,6 +152,6 @@ async function assert_truthy_worker_onerror_suppresses_window() {
|
||||
promise_test(async function () {
|
||||
await assert_unhandled_worker_error_propagates_to_window();
|
||||
await assert_worker_error_prevent_default_suppresses_window();
|
||||
await assert_truthy_worker_onerror_suppresses_window();
|
||||
await assert_non_boolean_truthy_worker_onerror_propagates_to_window();
|
||||
}, "Dedicated worker runtime errors propagate to page window.onerror unless handled");
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user