refactor(events): inherit canonical EventTarget operations across interfaces

This commit is contained in:
ldm0
2026-10-23 20:55:15 +08:00
committed by Donough Liu
parent 4b3bd91ace
commit cfe83bf566
47 changed files with 257 additions and 516 deletions
+3 -4
View File
@@ -55,6 +55,7 @@ pub(crate) use exposed_interfaces::{
ready_interface_template_names as lazy_ready_constructor_template_names,
storage_interface_materialization_count as lazy_storage_constructor_materialization_count,
};
pub(crate) use indexed_db::dispatch_indexed_db_event_callback;
mod location_history_storage;
mod location_navigation;
mod location_runtime;
@@ -347,14 +348,12 @@ pub(crate) use self::media_queries::{
dispatch_media_query_list_change_events, dispatch_simple_event_target_event,
dispatch_simple_event_target_event_collecting_errors,
evaluate_match_media_query_list_with_viewport, initialize_simple_event_target,
install_simple_event_target_handlers, install_simple_event_target_methods,
invoke_simple_event_listener, is_simple_event_target,
install_simple_event_target_handlers, invoke_simple_event_listener, is_simple_event_target,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_handler_getter_callback, simple_event_target_handler_setter_callback,
simple_event_target_inspector_listener_snapshots,
simple_event_target_remove_event_listener_callback, simple_object_event_listeners_snapshot,
simple_object_event_set_handler, simple_object_event_target_add_listener,
simple_object_event_target_remove_listener, simple_object_has_event_listeners,
simple_object_event_set_handler, simple_object_has_event_listeners,
};
pub(crate) use self::message_ports::{
MessagePortDeliveryRunResult, MessagePortRealmBinding, detach_message_port_owner_for_transfer,
@@ -45,27 +45,6 @@ struct BroadcastChannelObjectDeclaration<'scope> {
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_add_event_listener_callback
)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_dispatch_event_callback
)]
dispatch_event: (),
}
#[derive(WebApiFunctionTemplate)]
@@ -19,6 +19,10 @@ pub(in crate::context_bootstrap) const STORAGE_INTERFACE_NAMES: &[&str] = &[
];
pub(in crate::context_bootstrap) const WORKER_SHARED_INTERFACE_NAMES: &[&str] = &[
"NetworkInformation",
"ServiceWorkerContainer",
"ServiceWorkerRegistration",
"ServiceWorker",
"DOMRectReadOnly",
"DOMRect",
"DOMPointReadOnly",
@@ -117,6 +121,10 @@ pub(in crate::context_bootstrap) const WORKER_SHARED_INTERFACE_NAMES: &[&str] =
];
const SECURE_CONTEXT_ONLY_INTERFACE_NAMES: &[&str] = &[
"BatteryManager",
"ServiceWorkerContainer",
"ServiceWorkerRegistration",
"ServiceWorker",
"PaymentRequestUpdateEvent",
"PaymentMethodChangeEvent",
"AuthenticatorAssertionResponse",
@@ -5,5 +5,5 @@ mod target;
mod version_change;
pub(super) use self::dispatch::*;
pub(super) use self::target::*;
pub(crate) use self::target::dispatch_indexed_db_event_callback;
pub(in crate::context_bootstrap) use self::version_change::*;
@@ -1,33 +1,6 @@
use super::*;
use crate::context_bootstrap::{
simple_object_event_target_add_listener, simple_object_event_target_remove_listener,
};
pub(in crate::context_bootstrap::indexed_db) fn idb_event_target_add_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
_rv: v8::ReturnValue<'_, v8::Value>,
) {
if !require_idb_event_target(scope, args.this()) {
return;
}
simple_object_event_target_add_listener(scope, &args);
}
pub(in crate::context_bootstrap::indexed_db) fn idb_event_target_remove_event_listener_callback<
's,
>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
_rv: v8::ReturnValue<'_, v8::Value>,
) {
if !require_idb_event_target(scope, args.this()) {
return;
}
simple_object_event_target_remove_listener(scope, &args);
}
pub(in crate::context_bootstrap::indexed_db) fn idb_event_target_dispatch_event_callback<'s>(
pub(crate) fn dispatch_indexed_db_event_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'s, v8::Value>,
@@ -3,10 +3,8 @@ use super::{
idb_cursor_continue_primary_key_callback, idb_cursor_delete_callback,
idb_cursor_update_callback, idb_database_close_callback,
idb_database_create_object_store_callback, idb_database_delete_object_store_callback,
idb_database_transaction_callback, idb_event_target_add_event_listener_callback,
idb_event_target_dispatch_event_callback, idb_event_target_remove_event_listener_callback,
idb_factory_cmp_callback, idb_factory_databases_callback, idb_factory_delete_database_callback,
idb_factory_open_callback,
idb_database_transaction_callback, idb_factory_cmp_callback, idb_factory_databases_callback,
idb_factory_delete_database_callback, idb_factory_open_callback,
idb_index_count_callback as idb_index_count_callback_in_current_context,
idb_index_get_all_callback as idb_index_get_all_callback_in_current_context,
idb_index_get_all_keys_callback as idb_index_get_all_keys_callback_in_current_context,
@@ -144,10 +142,8 @@ indexed_db_receiver_realm_callback!(
);
mod constructors;
mod helpers;
mod state;
pub(in crate::context_bootstrap) use self::constructors::install_indexed_db_template_bindings;
use self::helpers::*;
pub(in crate::context_bootstrap) use self::state::ensure_indexed_db_runtime_state;
pub(crate) use self::state::{install_worker_indexed_db_runtime_state, window_indexed_db_getter};
@@ -33,11 +33,9 @@ pub(super) fn install_database_and_transaction_template_bindings<'s>(
) {
match interface_name {
"IDBDatabase" => {
install_idb_event_target_methods(scope, prototype);
IdbDatabasePrototypeDeclaration::initialize_prototype_template(scope, prototype);
}
"IDBTransaction" => {
install_idb_event_target_methods(scope, prototype);
IdbTransactionPrototypeDeclaration::initialize_prototype_template(scope, prototype);
}
_ => {}
@@ -20,11 +20,7 @@ pub(super) fn install_factory_and_request_template_bindings<'s>(
prototype: v8::Local<'s, v8::ObjectTemplate>,
interface_name: &str,
) {
match interface_name {
"IDBFactory" => {
IdbFactoryPrototypeDeclaration::initialize_prototype_template(scope, prototype);
}
"IDBRequest" => install_idb_event_target_methods(scope, prototype),
_ => {}
if interface_name == "IDBFactory" {
IdbFactoryPrototypeDeclaration::initialize_prototype_template(scope, prototype);
}
}
@@ -1,24 +0,0 @@
use super::*;
use moli_webapi_declare::WebApiFunctionTemplate;
#[derive(WebApiFunctionTemplate)]
#[webapi(name = "IDBEventTarget", enumerable)]
struct IdbEventTargetPrototypeDeclaration {
#[webapi(method, length = 2, callback = idb_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
length = 2,
callback = idb_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, length = 1, callback = idb_event_target_dispatch_event_callback)]
dispatch_event: (),
}
pub(super) fn install_idb_event_target_methods<'s>(
scope: &mut v8::PinScope<'s, '_, ()>,
prototype: v8::Local<'s, v8::ObjectTemplate>,
) {
IdbEventTargetPrototypeDeclaration::initialize_prototype_template(scope, prototype);
}
@@ -28,6 +28,7 @@ use self::backend::*;
use self::core::*;
use self::cursor::*;
use self::database::*;
pub(crate) use self::event_target::dispatch_indexed_db_event_callback;
use self::event_target::*;
use self::operation_state::*;
use self::runtime::*;
@@ -13,13 +13,12 @@ pub(crate) use self::events::{
SimpleObjectEventListenerInspectorSnapshot, SimpleObjectEventListenerSnapshot,
dispatch_simple_event_target_event, dispatch_simple_event_target_event_collecting_errors,
initialize_simple_event_target, install_simple_event_target_handlers,
install_simple_event_target_methods, invoke_simple_event_listener, is_simple_event_target,
invoke_simple_event_listener, is_simple_event_target,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_handler_getter_callback, simple_event_target_handler_setter_callback,
simple_event_target_inspector_listener_snapshots,
simple_event_target_remove_event_listener_callback, simple_object_event_listeners_snapshot,
simple_object_event_set_handler, simple_object_event_target_add_listener,
simple_object_event_target_remove_listener, simple_object_has_event_listeners,
simple_object_event_set_handler, simple_object_has_event_listeners,
};
pub(super) use self::events::{
media_query_list_add_listener_callback, media_query_list_remove_listener_callback,
@@ -15,12 +15,11 @@ pub(crate) use simple_event_target::{
SimpleObjectEventListenerInspectorSnapshot, SimpleObjectEventListenerSnapshot,
dispatch_simple_event_target_event, dispatch_simple_event_target_event_collecting_errors,
initialize_simple_event_target, install_simple_event_target_handlers,
install_simple_event_target_methods, invoke_simple_event_listener, is_simple_event_target,
invoke_simple_event_listener, is_simple_event_target,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_handler_getter_callback, simple_event_target_handler_setter_callback,
simple_event_target_inspector_listener_snapshots,
simple_event_target_remove_event_listener_callback, simple_object_event_listeners_snapshot,
simple_object_event_remove_listener_value_for_type, simple_object_event_set_handler,
simple_object_event_target_add_listener, simple_object_event_target_register_webidl_listener,
simple_object_event_target_remove_listener, simple_object_has_event_listeners,
simple_object_event_target_register_webidl_listener, simple_object_has_event_listeners,
};
@@ -3,7 +3,6 @@ use super::*;
mod callbacks;
mod dispatch;
mod handlers;
mod install;
mod listeners;
mod state;
@@ -20,7 +19,7 @@ pub(crate) use handlers::{
install_simple_event_target_handlers, simple_event_target_handler_getter_callback,
simple_event_target_handler_setter_callback,
};
pub(crate) use install::install_simple_event_target_methods;
pub(crate) use listeners::simple_object_event_target_register_webidl_listener;
pub(crate) use listeners::{
SimpleObjectEventListenerInspectorSnapshot, SimpleObjectEventListenerSnapshot,
simple_event_target_inspector_listener_snapshots, simple_object_event_listeners_snapshot,
@@ -33,10 +32,6 @@ use listeners::{
simple_object_event_target_add_listener_for_target,
simple_object_event_target_remove_listener_for_target,
};
pub(crate) use listeners::{
simple_object_event_target_add_listener, simple_object_event_target_register_webidl_listener,
simple_object_event_target_remove_listener,
};
use state::{
ResolvedSimpleEventTarget, SimpleEventListenerRegistry, resolve_simple_event_target,
simple_object_event_listener_registry,
@@ -1,33 +0,0 @@
use super::*;
use crate::web_api_interfaces;
use moli_webapi_declare::WebApiObject;
#[derive(WebApiObject)]
#[webapi(
interface = web_api_interfaces::EventTarget,
prototype = "Object",
enumerable,
receiver
)]
struct SimpleEventTargetMethodsDeclaration {
#[webapi(method, length = 2, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
length = 2,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, length = 1, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
}
pub(crate) fn install_simple_event_target_methods<'s>(
scope: &mut v8::PinScope<'s, '_>,
target: v8::Local<'s, v8::Object>,
) {
initialize_simple_event_target(scope, target);
SimpleEventTargetMethodsDeclaration::new()
.initialize(scope, target)
.expect("simple event target declaration should initialize");
}
@@ -186,13 +186,6 @@ impl<'s> SimpleObjectEventListenerSnapshot<'s> {
}
}
pub(crate) fn simple_object_event_target_add_listener<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: &v8::FunctionCallbackArguments<'s>,
) {
simple_object_event_target_add_listener_for_target(scope, args, args.this(), None);
}
pub(super) fn simple_object_event_target_add_listener_for_target<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: &v8::FunctionCallbackArguments<'s>,
@@ -309,13 +302,6 @@ fn simple_object_event_target_register_resolved_listener<'s>(
}
}
pub(crate) fn simple_object_event_target_remove_listener<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: &v8::FunctionCallbackArguments<'s>,
) {
simple_object_event_target_remove_listener_for_target(scope, args, args.this(), None);
}
pub(super) fn simple_object_event_target_remove_listener_for_target<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: &v8::FunctionCallbackArguments<'s>,
@@ -119,7 +119,7 @@ fn allocate_in_current_realm<'s>(
.bind(scope)
.expect("NavigationHistoryEntry declaration should bind");
native::bind_entry(scope, wrapper, record);
media_queries::install_simple_event_target_methods(scope, wrapper);
media_queries::initialize_simple_event_target(scope, wrapper);
shared_event_targets::install_handlers(scope, wrapper, true);
set_runtime_window_owner(scope, wrapper, owner);
wrapper
@@ -84,7 +84,7 @@ pub(in crate::context_bootstrap) fn build_navigation_runtime_state<'s>(
let navigation = NavigationRuntimeObjectDeclaration::new(current_entry)
.bind(scope)
.map_err(anyhow::Error::from)?;
super::media_queries::install_simple_event_target_methods(scope, navigation);
super::media_queries::initialize_simple_event_target(scope, navigation);
super::super::shared_event_targets::install_handlers(scope, navigation, false);
let owner = history_window_owner(scope, window);
if window_has_shared_history(scope, window)
@@ -290,7 +290,6 @@ struct PermissionStatusPrototypeDeclaration {
#[derive(Default, WebApiObject)]
#[webapi(
interface = web_api_interfaces::NetworkInformation,
prototype = "Object",
receiver
)]
struct NavigatorConnectionDeclaration {
@@ -312,24 +311,11 @@ struct NavigatorConnectionDeclaration {
#[webapi(accessor_property, getter = navigator_connection_save_data_getter_callback, enumerable)]
save_data: (),
#[webapi(data_property, enumerable, init = "null")]
#[webapi(accessor_property, enumerable, getter = simple_event_target_handler_getter_callback, setter = simple_event_target_handler_setter_callback, data = v8str(scope, "onchange"), setter_data = v8str(scope, "onchange"))]
onchange: (),
#[webapi(
method,
enumerable,
length = 2,
callback = navigator_connection_event_target_noop_callback
)]
add_event_listener: (),
#[webapi(
method,
enumerable,
length = 2,
callback = navigator_connection_event_target_noop_callback
)]
remove_event_listener: (),
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
}
#[derive(WebApiObject)]
@@ -401,7 +387,7 @@ struct NavigatorUaDataPrototypeMethodsDeclaration {
}
#[derive(Default, WebApiObject)]
#[webapi(interface = web_api_interfaces::ServiceWorkerContainer, prototype = "Object")]
#[webapi(interface = web_api_interfaces::ServiceWorkerContainer)]
struct ServiceWorkerContainerDeclaration {
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
@@ -451,15 +437,6 @@ struct ServiceWorkerContainerDeclaration {
)]
get_registrations: (),
#[webapi(method, enumerable, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_remove_event_listener_callback)]
remove_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
#[webapi(
accessor_property,
enumerable,
@@ -807,13 +784,6 @@ fn navigator_connection_save_data_getter_callback<'s>(
);
}
fn navigator_connection_event_target_noop_callback<'s>(
_scope: &mut v8::PinScope<'s, '_>,
_args: v8::FunctionCallbackArguments<'s>,
_rv: v8::ReturnValue<'_, v8::Value>,
) {
}
fn navigator_get_autoplay_policy_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
@@ -1,7 +1,5 @@
use super::super::media_queries::{
dispatch_simple_event_target_event, initialize_simple_event_target,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_remove_event_listener_callback,
};
use super::super::*;
use crate::util::{
@@ -121,44 +119,6 @@ struct ScreenOrientationPrototypeMethodsDeclaration {
unlock: (),
}
#[derive(Default, WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::Screen, enumerable)]
struct ScreenEventTargetMethodsDeclaration {
#[webapi(method, length = 2, callback = screen_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
length = 2,
callback = screen_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, length = 1, callback = screen_event_target_dispatch_event_callback)]
dispatch_event: (),
}
#[derive(Default, WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::ScreenOrientation, enumerable)]
struct ScreenOrientationEventTargetMethodsDeclaration {
#[webapi(
method,
length = 2,
callback = screen_orientation_event_target_add_event_listener_callback
)]
add_event_listener: (),
#[webapi(
method,
length = 2,
callback = screen_orientation_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(
method,
length = 1,
callback = screen_orientation_event_target_dispatch_event_callback
)]
dispatch_event: (),
}
fn new_not_supported_dom_exception<'s>(
scope: &mut v8::PinScope<'s, '_>,
message: &str,
@@ -228,78 +188,6 @@ fn screen_orientation_unlock_callback<'s>(
rv.set(v8::undefined(scope).into());
}
fn screen_event_target_add_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::Screen::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_add_event_listener_callback(scope, args, rv);
}
fn screen_event_target_remove_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::Screen::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_remove_event_listener_callback(scope, args, rv);
}
fn screen_event_target_dispatch_event_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::Screen::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_dispatch_event_callback(scope, args, rv);
}
fn screen_orientation_event_target_add_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::ScreenOrientation::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_add_event_listener_callback(scope, args, rv);
}
fn screen_orientation_event_target_remove_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::ScreenOrientation::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_remove_event_listener_callback(scope, args, rv);
}
fn screen_orientation_event_target_dispatch_event_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
if !web_api_interfaces::ScreenOrientation::is_instance(scope, args.this()) {
throw_type_error(scope, "Illegal invocation");
return;
}
simple_event_target_dispatch_event_callback(scope, args, rv);
}
pub(in crate::context_bootstrap) fn install_screen_template_bindings<'s>(
scope: &mut v8::PinScope<'s, '_, ()>,
template: v8::Local<'s, v8::FunctionTemplate>,
@@ -309,15 +197,11 @@ pub(in crate::context_bootstrap) fn install_screen_template_bindings<'s>(
match interface_name {
"Screen" => {
ScreenPrototypeAccessorsDeclaration::initialize_prototype_template(scope, prototype);
ScreenEventTargetMethodsDeclaration::initialize_prototype_template(scope, prototype);
}
"ScreenOrientation" => {
ScreenOrientationPrototypeAccessorsDeclaration::initialize_prototype_template(
scope, prototype,
);
ScreenOrientationEventTargetMethodsDeclaration::initialize_prototype_template(
scope, prototype,
);
ScreenOrientationPrototypeMethodsDeclaration::initialize_prototype_template(
scope, prototype,
);
@@ -1,8 +1,7 @@
use super::{
SIMPLE_EVENT_TARGET_LISTENERS_SLOT, global_constructor_object,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_handler_getter_callback, simple_event_target_handler_setter_callback,
simple_event_target_remove_event_listener_callback, structured_clone_value_for_storage,
structured_clone_value_for_storage,
};
use crate::web_api_interfaces;
use crate::{
@@ -98,27 +97,6 @@ struct NotificationObjectDeclaration<'scope> {
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_add_event_listener_callback
)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_dispatch_event_callback
)]
dispatch_event: (),
#[webapi(method, enumerable, callback = notification_close_callback)]
close: (),
}
@@ -563,10 +541,6 @@ fn initialize_notification_object<'s>(
require_interaction: metadata.require_interaction,
actions: action_array,
event_listeners: (),
add_event_listener: (),
remove_event_listener: (),
dispatch_event: (),
close: (),
}
.initialize(scope, notification)
@@ -376,7 +376,7 @@ pub(in crate::context_bootstrap) fn install_worker_performance_runtime_state<'s>
)
.bind(scope)
.map_err(|error| anyhow!("failed to create worker performance: {error}"))?;
install_simple_event_target_methods(scope, performance);
initialize_simple_event_target(scope, performance);
set_private_value(scope, global, WINDOW_PERFORMANCE_SLOT, performance.into());
Ok(())
@@ -431,7 +431,7 @@ pub(super) fn create_performance_object<'s>(
PERFORMANCE_NAVIGATION_NAME_SEED_SLOT,
navigation_entry_name.into(),
);
install_simple_event_target_methods(scope, performance);
initialize_simple_event_target(scope, performance);
performance
}
@@ -11,9 +11,7 @@ use super::{
document_referrer_policy_for_native_document,
},
shared::SIMPLE_EVENT_TARGET_LISTENERS_SLOT,
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_remove_event_listener_callback, simple_object_event_listeners_snapshot,
simple_object_event_set_handler,
simple_object_event_listeners_snapshot, simple_object_event_set_handler,
worker_host::{
document_query_encoding_override, is_cross_origin_http_worker_script,
materialize_worker_script_source, resolve_worker_script_url, throw_worker_dom_exception,
@@ -61,19 +59,6 @@ struct SharedWorkerObjectDeclaration<'scope> {
#[webapi(slot = SHARED_WORKER_ONERROR_SLOT, init = "null")]
onerror_slot: (),
#[webapi(method, enumerable, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
#[webapi(
accessor_property,
enumerable,
@@ -4,6 +4,26 @@ use crate::web_api_interfaces;
use std::collections::HashSet;
const CONSTRUCTOR_SPECS_BEFORE_STREAMS: &[ConstructorSpec] = &[
ConstructorSpec {
interface: web_api_interfaces::BatteryManager::DESCRIPTOR,
kind: ConstructorKind::Illegal,
},
ConstructorSpec {
interface: web_api_interfaces::NetworkInformation::DESCRIPTOR,
kind: ConstructorKind::Illegal,
},
ConstructorSpec {
interface: web_api_interfaces::ServiceWorkerContainer::DESCRIPTOR,
kind: ConstructorKind::Illegal,
},
ConstructorSpec {
interface: web_api_interfaces::ServiceWorkerRegistration::DESCRIPTOR,
kind: ConstructorKind::Illegal,
},
ConstructorSpec {
interface: web_api_interfaces::ServiceWorker::DESCRIPTOR,
kind: ConstructorKind::Illegal,
},
ConstructorSpec {
interface: web_api_interfaces::PaymentRequestUpdateEvent::DESCRIPTOR,
kind: ConstructorKind::PaymentRequestUpdateEvent,
@@ -1,8 +1,4 @@
use super::media_queries::{
dispatch_simple_event_target_event, simple_event_target_add_event_listener_callback,
simple_event_target_dispatch_event_callback,
simple_event_target_remove_event_listener_callback,
};
use super::media_queries::dispatch_simple_event_target_event;
use super::*;
use crate::native_bridge::throw_dom_exception;
use crate::util::{
@@ -145,16 +141,6 @@ struct OfflineAudioContextObjectDeclaration<'scope> {
state: &'static str,
#[webapi(data_property)]
destination: v8::Local<'scope, v8::Object>,
#[webapi(method, enumerable, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
}
#[derive(WebApiObject)]
@@ -1,8 +1,4 @@
use super::media_queries::{
dispatch_simple_event_target_event, simple_event_target_add_event_listener_callback,
simple_event_target_dispatch_event_callback,
simple_event_target_remove_event_listener_callback, simple_object_event_set_handler,
};
use super::media_queries::{dispatch_simple_event_target_event, simple_object_event_set_handler};
use super::shared::{
READABLE_STREAM_HWM_SLOT, READABLE_STREAM_PENDING_READS_SLOT,
SIMPLE_EVENT_TARGET_LISTENERS_SLOT, WRITABLE_STREAM_SINK_SLOT,
@@ -50,19 +50,6 @@ struct WebSocketObjectDeclaration {
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
#[webapi(method, enumerable, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
}
#[derive(WebApiObject)]
@@ -286,7 +286,7 @@ struct StorageUsageDetailsObjectDeclaration {
}
#[derive(WebApiObject)]
#[webapi(prototype = "Object", interface = web_api_interfaces::BatteryManager, data_properties, receiver)]
#[webapi(interface = web_api_interfaces::BatteryManager, data_properties, receiver)]
struct NavigatorBatteryStatusObjectDeclaration {
#[webapi(data_property, enumerable)]
charging: bool,
@@ -297,26 +297,20 @@ struct NavigatorBatteryStatusObjectDeclaration {
#[webapi(data_property, enumerable)]
level: f64,
#[webapi(data_property, enumerable, init = "null")]
#[webapi(accessor_property, enumerable, getter = simple_event_target_handler_getter_callback, setter = simple_event_target_handler_setter_callback, data = v8str(scope, "onchargingchange"), setter_data = v8str(scope, "onchargingchange"))]
onchargingchange: (),
#[webapi(data_property, enumerable, init = "null")]
#[webapi(accessor_property, enumerable, getter = simple_event_target_handler_getter_callback, setter = simple_event_target_handler_setter_callback, data = v8str(scope, "onchargingtimechange"), setter_data = v8str(scope, "onchargingtimechange"))]
onchargingtimechange: (),
#[webapi(data_property, enumerable, init = "null")]
#[webapi(accessor_property, enumerable, getter = simple_event_target_handler_getter_callback, setter = simple_event_target_handler_setter_callback, data = v8str(scope, "ondischargingtimechange"), setter_data = v8str(scope, "ondischargingtimechange"))]
ondischargingtimechange: (),
#[webapi(data_property, enumerable, init = "null")]
#[webapi(accessor_property, enumerable, getter = simple_event_target_handler_getter_callback, setter = simple_event_target_handler_setter_callback, data = v8str(scope, "onlevelchange"), setter_data = v8str(scope, "onlevelchange"))]
onlevelchange: (),
#[webapi(method, length = 2, callback = battery_event_target_noop_callback)]
add_event_listener: (),
#[webapi(method, length = 2, callback = battery_event_target_noop_callback)]
remove_event_listener: (),
#[webapi(method, length = 1, callback = battery_dispatch_event_callback)]
dispatch_event: (),
#[webapi(slot = SIMPLE_EVENT_TARGET_LISTENERS_SLOT, init = "null_object")]
event_listeners: (),
}
#[derive(WebApiObject)]
@@ -610,21 +604,6 @@ pub(in crate::context_bootstrap) fn navigator_get_battery_callback<'s>(
rv.set(resolver.get_promise(scope).into());
}
fn battery_event_target_noop_callback<'s>(
_scope: &mut v8::PinScope<'s, '_>,
_args: v8::FunctionCallbackArguments<'s>,
_rv: v8::ReturnValue<'_, v8::Value>,
) {
}
fn battery_dispatch_event_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
_args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'_, v8::Value>,
) {
rv.set(v8::Boolean::new(scope, true).into());
}
pub(in crate::context_bootstrap) fn navigator_permissions_query_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
@@ -97,25 +97,6 @@ struct ServiceWorkerRegistrationObjectDeclaration<'scope> {
#[webapi(data_property = "navigationPreload", readonly)]
navigation_preload: Option<v8::Local<'scope, v8::Object>>,
#[webapi(
method = "addEventListener",
callback = service_worker_registration_add_event_listener_callback,
length = 2
)]
add_event_listener: (),
#[webapi(
method = "removeEventListener",
callback = simple_event_target_remove_event_listener_callback,
length = 2
)]
remove_event_listener: (),
#[webapi(
method = "dispatchEvent",
callback = simple_event_target_dispatch_event_callback,
length = 1
)]
dispatch_event: (),
}
#[derive(WebApiObject)]
@@ -293,27 +274,6 @@ struct ServiceWorkerObjectDeclaration {
#[webapi(method = "postMessage", callback = service_worker_post_message_callback, length = 1)]
post_message: (),
#[webapi(
method = "addEventListener",
callback = service_worker_worker_add_event_listener_callback,
length = 2
)]
add_event_listener: (),
#[webapi(
method = "removeEventListener",
callback = simple_event_target_remove_event_listener_callback,
length = 2
)]
remove_event_listener: (),
#[webapi(
method = "dispatchEvent",
callback = simple_event_target_dispatch_event_callback,
length = 1
)]
dispatch_event: (),
}
#[derive(WebApiObject)]
@@ -2847,22 +2807,6 @@ fn mark_service_worker_worker_event_target<'s>(
initialize_simple_event_target(scope, worker);
}
fn service_worker_registration_add_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
simple_event_target_add_event_listener_callback(scope, args, rv);
}
fn service_worker_worker_add_event_listener_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
simple_event_target_add_event_listener_callback(scope, args, rv);
}
fn service_worker_post_message_callback<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
@@ -75,19 +75,6 @@ struct WorkerObjectDeclaration {
#[webapi(slot = WORKER_ONERROR_SLOT, init = "null")]
onerror_slot: (),
#[webapi(method, enumerable, callback = simple_event_target_add_event_listener_callback)]
add_event_listener: (),
#[webapi(
method,
enumerable,
callback = simple_event_target_remove_event_listener_callback
)]
remove_event_listener: (),
#[webapi(method, enumerable, callback = simple_event_target_dispatch_event_callback)]
dispatch_event: (),
#[webapi(
accessor_property,
enumerable,
@@ -1,7 +1,4 @@
use super::media_queries::{
simple_event_target_add_event_listener_callback, simple_event_target_dispatch_event_callback,
simple_event_target_remove_event_listener_callback, simple_object_event_set_handler,
};
use super::media_queries::simple_object_event_set_handler;
use super::shared::SIMPLE_EVENT_TARGET_LISTENERS_SLOT;
use crate::{
util::context_host_ptr_from_global_bridge,
@@ -29,6 +29,12 @@ fn setup_worker_context() -> (v8::OwnedIsolate, v8::Global<v8::Context>) {
let worker_tmpl = v8::FunctionTemplate::builder(worker_constructor_callback)
.length(1)
.build(scope);
let event_target = crate::context_bootstrap::prepare_worker_event_target_template(
scope,
crate::context_bootstrap::exposed_interfaces::RealmKind::DedicatedWorker,
)
.expect("standalone Worker tests need the inherited EventTarget template");
worker_tmpl.inherit(event_target);
let class_name = v8::String::new(scope, "Worker").unwrap();
worker_tmpl.set_class_name(class_name);
@@ -55,6 +61,12 @@ fn setup_worker_context() -> (v8::OwnedIsolate, v8::Global<v8::Context>) {
let context = v8::Context::new(scope, Default::default());
let ctx_scope = &mut v8::ContextScope::new(scope, context);
let global = context.global(ctx_scope);
let event_target_constructor = event_target.get_function(ctx_scope).unwrap();
let _ = global.set(
ctx_scope,
crate::util::v8str(ctx_scope, "EventTarget").into(),
event_target_constructor.into(),
);
crate::context_bootstrap::exposed_interfaces::install_worker_exposed_interfaces(
ctx_scope,
@@ -500,12 +512,15 @@ fn constructor_declared_event_target_slots_ignore_reflection_and_spoofing() {
throw new Error(`Worker internals should not be reflected: ${reflected.join(",")}`);
}
const expectedMethods = {
addEventListener: "true:true:true:true:function:0:addEventListener",
removeEventListener: "true:true:true:true:function:0:removeEventListener",
dispatchEvent: "true:true:true:true:function:0:dispatchEvent"
addEventListener: "true:true:true:true:function:2:addEventListener",
removeEventListener: "true:true:true:true:function:2:removeEventListener",
dispatchEvent: "true:true:true:true:function:1:dispatchEvent"
};
for (const [name, shape] of Object.entries(expectedMethods)) {
const descriptor = Object.getOwnPropertyDescriptor(w, name);
if (Object.hasOwn(w, name) || w[name] !== EventTarget.prototype[name]) {
throw new Error(`${name} must be inherited from EventTarget.prototype`);
}
const descriptor = Object.getOwnPropertyDescriptor(EventTarget.prototype, name);
const actual = [
!!descriptor,
descriptor && descriptor.enumerable,
@@ -9,7 +9,7 @@ use crate::{
SIMPLE_EVENT_TARGET_LISTENERS_SLOT, SharedWebStorageStore, WINDOW_NAME_SLOT,
apply_local_window_location_navigation, clear_simple_window_event_handler_values,
deep_clone_shared_web_storage_store, dispatch_simple_event_target_event,
install_navigation_bootstrap_entry_for_holder, install_simple_event_target_methods,
initialize_simple_event_target, install_navigation_bootstrap_entry_for_holder,
install_simple_window_event_handlers, install_storage_aliases_for_window,
install_window_location_history_navigation_runtime_state, new_shared_web_storage_store,
scoped_indexed_db_factory, sync_document_location_runtime_state_from_window,
@@ -803,6 +803,9 @@ impl JsContextHost {
.bridge
.bindings
.instantiate_window_shell(scope, host_ptr);
crate::web_api_interfaces::Window::DESCRIPTOR
.initialize(scope, window)
.ok()?;
if let Some(dom_exception) = global_constructor_object(scope, "DOMException") {
// Lightweight popups share the opener's V8 realm, so their
// synthetic Window shell must expose the same realm constructor
@@ -866,7 +869,7 @@ impl JsContextHost {
}
}
let _ = install_storage_aliases_for_window(scope, window);
install_simple_event_target_methods(scope, window);
initialize_simple_event_target(scope, window);
install_simple_window_event_handlers(scope, window);
let _ = LightweightPopupWindowMethodsDeclaration::default().initialize(scope, window);
let initial_document_owner = self.allocate_lightweight_popup_document_owner(popup_id);
@@ -989,12 +989,14 @@ async fn shared_worker_declared_surface_ignores_reflection_and_spoofing() {
throw new Error(`SharedWorker internals should not be reflected: ${reflected.join(",")}`);
}
const expectedMethods = {
addEventListener: "true:true:true:true:function:0:addEventListener",
removeEventListener: "true:true:true:true:function:0:removeEventListener",
dispatchEvent: "true:true:true:true:function:0:dispatchEvent"
addEventListener: "true:true:true:true:function:2:addEventListener",
removeEventListener: "true:true:true:true:function:2:removeEventListener",
dispatchEvent: "true:true:true:true:function:1:dispatchEvent"
};
for (const [name, shape] of Object.entries(expectedMethods)) {
const descriptor = Object.getOwnPropertyDescriptor(worker, name);
if (Object.hasOwn(worker, name) || worker[name] !== EventTarget.prototype[name])
throw new Error(`inherited EventTarget method ${name}`);
const descriptor = Object.getOwnPropertyDescriptor(EventTarget.prototype, name);
const actual = [
!!descriptor,
descriptor && descriptor.enumerable,
@@ -735,7 +735,9 @@ fn broadcast_channel_ordered_handler_slot_ignores_reflection_and_spoofing() {
name === "__moliSimpleEventTargetOrderedHandlers"
);
const summarize = name => {
const descriptor = Object.getOwnPropertyDescriptor(channel, name);
if (Object.hasOwn(channel, name) || channel[name] !== EventTarget.prototype[name])
throw new Error(`inherited EventTarget method ${name}`);
const descriptor = Object.getOwnPropertyDescriptor(EventTarget.prototype, name);
return [
!!descriptor,
descriptor && descriptor.enumerable,
@@ -774,7 +776,7 @@ fn broadcast_channel_ordered_handler_slot_ignores_reflection_and_spoofing() {
assert_eq!(
result,
r#"{"ownBefore":[],"publicSpoof":["spoofedListeners",false],"add":"true:true:true:true:function:0:addEventListener","remove":"true:true:true:true:function:0:removeEventListener","dispatch":"true:true:true:true:function:0:dispatchEvent","calls":["listener","handler"]}"#
r#"{"ownBefore":[],"publicSpoof":["spoofedListeners",false],"add":"true:true:true:true:function:2:addEventListener","remove":"true:true:true:true:function:2:removeEventListener","dispatch":"true:true:true:true:function:1:dispatchEvent","calls":["listener","handler"]}"#
);
}
@@ -1,5 +1,24 @@
use super::*;
#[test]
fn event_target_interfaces_inherit_canonical_operations() {
let mut vm = new_storage_html_test_vm("https://event-target-inheritance.test/");
vm.eval(include_str!(
"../../../../tests/fixtures/event-target-inheritance.js"
))
.expect("interface inheritance matrix should run");
assert_eq!(vm.eval("String(!__eventTargetInheritance.pending && __eventTargetInheritance.rows.every(row => row[1]))").unwrap(), "true");
assert_eq!(vm.eval("String(__eventTargetInheritance.rows.some(row => row[0] === 'battery: native inheritance'))").unwrap(), "true");
vm.eval(
r#"(() => {
const popup = open('about:blank', 'inherited-event-target');
try { __eventTargetInheritance.inspect(popup, 'lightweight popup'); }
finally { popup.close(); }
})()"#,
)
.expect("popup should inherit EventTarget operations");
}
#[test]
fn event_target_prototype_methods_preserve_native_state_and_realm_semantics() {
let mut vm = new_parsed_test_vm(
@@ -2567,11 +2567,12 @@ fn web_audio_methods_preserve_own_and_shared_prototype_descriptors() {
return JSON.stringify({
ctxKeys: Object.keys(ctx).join(","),
inheritedEvents: ["addEventListener", "removeEventListener", "dispatchEvent"].every(name => !Object.hasOwn(ctx, name) && ctx[name] === EventTarget.prototype[name]),
oscKeys: Object.keys(osc).join(","),
compKeys: Object.keys(comp).join(","),
analyserKeys: Object.keys(analyser).join(","),
paramKeys: Object.keys(osc.frequency).join(","),
ctx: ["addEventListener", "removeEventListener", "dispatchEvent"].map(name => describe(ctx, name)),
ctx: ["addEventListener", "removeEventListener", "dispatchEvent"].map(name => describe(EventTarget.prototype, name)),
graph: ["connect", "disconnect"].map(name => describe(AudioNode.prototype, name)),
shared: [ctx.destination, osc, comp, analyser].every(node =>
["connect", "disconnect"].every(name =>
@@ -2586,7 +2587,7 @@ fn web_audio_methods_preserve_own_and_shared_prototype_descriptors() {
assert_eq!(
result,
r#"{"ctxKeys":"addEventListener,removeEventListener,dispatchEvent","oscKeys":"","compKeys":"","analyserKeys":"","paramKeys":"","ctx":["addEventListener:true:true:true:function:addEventListener:0","removeEventListener:true:true:true:function:removeEventListener:0","dispatchEvent:true:true:true:function:dispatchEvent:0"],"graph":["connect:true:true:true:function:connect:1","disconnect:true:true:true:function:disconnect:0"],"shared":true,"oscStart":"start:false:true:true:function:start:1","param":"setValueAtTime:false:true:true:function:setValueAtTime:2"}"#
r#"{"ctxKeys":"","inheritedEvents":true,"oscKeys":"","compKeys":"","analyserKeys":"","paramKeys":"","ctx":["addEventListener:true:true:true:function:addEventListener:2","removeEventListener:true:true:true:function:removeEventListener:2","dispatchEvent:true:true:true:function:dispatchEvent:1"],"graph":["connect:true:true:true:function:connect:1","disconnect:true:true:true:function:disconnect:0"],"shared":true,"oscStart":"start:false:true:true:function:start:1","param":"setValueAtTime:false:true:true:function:setValueAtTime:2"}"#
);
}
@@ -705,7 +705,9 @@ fn navigator_battery_status_declares_event_target_methods() {
navigator.getBattery().then((battery) => {
const fakeBattery = Object.create(battery);
const summarize = name => {
const descriptor = Object.getOwnPropertyDescriptor(battery, name);
if (Object.hasOwn(battery, name) || battery[name] !== EventTarget.prototype[name])
throw new Error(`inherited EventTarget method ${name}`);
const descriptor = Object.getOwnPropertyDescriptor(EventTarget.prototype, name);
return [
!!descriptor,
descriptor && descriptor.enumerable,
@@ -728,7 +730,7 @@ fn navigator_battery_status_declares_event_target_methods() {
summarize("addEventListener"),
summarize("removeEventListener"),
summarize("dispatchEvent"),
battery.dispatchEvent({ type: "chargingchange" }),
battery.dispatchEvent(new Event("chargingchange")),
String(battery.addEventListener("chargingchange", () => {})),
String(battery.removeEventListener("chargingchange", () => {})),
methodOutcome("addEventListener", "chargingchange", () => {}),
@@ -747,7 +749,7 @@ fn navigator_battery_status_declares_event_target_methods() {
assert_eq!(
result,
"false|true:false:true:true:function:2:addEventListener|true:false:true:true:function:2:removeEventListener|true:false:true:true:function:1:dispatchEvent|true|undefined|undefined|throw:TypeError|throw:TypeError|throw:TypeError"
"false|true:true:true:true:function:2:addEventListener|true:true:true:true:function:2:removeEventListener|true:true:true:true:function:1:dispatchEvent|true|undefined|undefined|throw:TypeError|throw:TypeError|throw:TypeError"
);
}
#[test]
@@ -865,8 +865,8 @@ fn navigator_runtime_subobjects_keep_declared_brand_and_methods() {
Object.getOwnPropertyNames(connection)
.filter(name => name.startsWith("__moliNavigatorConnection"))
.join(","),
summarizeOwnMethod(connection, "addEventListener"),
summarizeOwnMethod(connection, "removeEventListener"),
summarizeOwnMethod(EventTarget.prototype, "addEventListener"),
summarizeOwnMethod(EventTarget.prototype, "removeEventListener"),
String(connection.addEventListener("change", () => {})),
String(connection.removeEventListener("change", () => {})),
methodOutcome(connection, fakeConnection, "addEventListener", "change", () => {}),
@@ -924,7 +924,7 @@ fn navigator_runtime_subobjects_keep_declared_brand_and_methods() {
assert_eq!(
result,
r#"{"permissions":"true|[object Permissions]|false|function|1","storage":"true|[object StorageManager]|false|false|false|false||true|true|true|true|persisted|0|persist|0|estimate|0|false:undefined:::::|[object Promise]|[object Promise]","connection":"[object Object]|string|unknown|Infinity|string|4g|10|50|false|true|type,downlinkMax,effectiveType,downlink,rtt,saveData,onchange,addEventListener,removeEventListener||true:function:addEventListener:2:true:true:true|true:function:removeEventListener:2:true:true:true|undefined|undefined|throw:TypeError|throw:TypeError","mediaDevices":"true|[object MediaDevices]|false|false||enumerateDevices|0|getUserMedia|1|true:function:enumerateDevices:0:true:true:true|true:function:getUserMedia:1:true:true:true","clipboard":"true|true|[object Clipboard]|false|false||true|true|true|true|readText|0|writeText|1|true:function:readText:0:true:true:true|true:function:writeText:1:true:true:true|[object Promise]|[object Promise]","userActivation":"undefined|[object Object]|boolean|false|false|isActive,hasBeenActive|true|throw:TypeError|throw:TypeError"}"#
r#"{"permissions":"true|[object Permissions]|false|function|1","storage":"true|[object StorageManager]|false|false|false|false||true|true|true|true|persisted|0|persist|0|estimate|0|false:undefined:::::|[object Promise]|[object Promise]","connection":"[object NetworkInformation]|string|unknown|Infinity|string|4g|10|50|false|true|type,downlinkMax,effectiveType,downlink,rtt,saveData,onchange||true:function:addEventListener:2:true:true:true|true:function:removeEventListener:2:true:true:true|undefined|undefined|throw:TypeError|throw:TypeError","mediaDevices":"true|[object MediaDevices]|false|false||enumerateDevices|0|getUserMedia|1|true:function:enumerateDevices:0:true:true:true|true:function:getUserMedia:1:true:true:true","clipboard":"true|true|[object Clipboard]|false|false||true|true|true|true|readText|0|writeText|1|true:function:readText:0:true:true:true|true:function:writeText:1:true:true:true|[object Promise]|[object Promise]","userActivation":"undefined|[object Object]|boolean|false|false|isActive,hasBeenActive|true|throw:TypeError|throw:TypeError"}"#
);
let receiver_errors = vm
.eval(
@@ -1753,7 +1753,10 @@ fn notification_declared_surface_uses_private_slots_and_methods() {
});
const summarize = (target, name) => {
const descriptor = Object.getOwnPropertyDescriptor(target, name);
const inherited = ["addEventListener", "removeEventListener", "dispatchEvent"].includes(name);
if (inherited && (Object.hasOwn(target, name) || target[name] !== EventTarget.prototype[name]))
throw new Error(`${name} should be inherited from EventTarget`);
const descriptor = Object.getOwnPropertyDescriptor(inherited ? EventTarget.prototype : target, name);
return [
!!descriptor,
descriptor && descriptor.enumerable,
@@ -1848,7 +1851,7 @@ fn notification_declared_surface_uses_private_slots_and_methods() {
assert_eq!(
result,
"none|title:function:get title:0:undefined:true:true,data:function:get data:0:undefined:true:true,actions:function:get actions:0:undefined:true:true|real|42|0|throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError|throw:TypeError,throw:TypeError,throw:TypeError|true|true:true:true:true:function:0:requestPermission|true|true:true:true:true:function:0:addEventListener|true:true:true:true:function:0:removeEventListener|true:true:true:true:function:0:dispatchEvent|true|listener"
"none|title:function:get title:0:undefined:true:true,data:function:get data:0:undefined:true:true,actions:function:get actions:0:undefined:true:true|real|42|0|throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError,throw:TypeError|throw:TypeError,throw:TypeError,throw:TypeError|true|true:true:true:true:function:0:requestPermission|false|true:true:true:true:function:2:addEventListener|true:true:true:true:function:2:removeEventListener|true:true:true:true:function:1:dispatchEvent|true|listener"
);
}
@@ -4061,7 +4061,10 @@ fn indexed_db_declared_methods_have_webidl_operation_descriptors() {
[IDBKeyRange, "upperBound", 1],
];
for (const [target, name, length] of methods) {
const descriptor = Object.getOwnPropertyDescriptor(target, name);
const inherited = ["addEventListener", "removeEventListener", "dispatchEvent"].includes(name);
if (inherited && (Object.hasOwn(target, name) || target[name] !== EventTarget.prototype[name]))
throw new Error(`${name} should be inherited from EventTarget`);
const descriptor = Object.getOwnPropertyDescriptor(inherited ? EventTarget.prototype : target, name);
if (!descriptor || typeof descriptor.value !== "function") {
throw new Error(`${name} should be a declared function`);
}
@@ -498,12 +498,14 @@ fn websocket_wpt_target_event_target_listener_options_and_objects() {
throw new Error(`WebSocket simple EventTarget internals should not be reflected: ${simpleOwn.join(',')}`);
}
const expected = {
addEventListener: 'true:true:true:true:function:0:addEventListener',
removeEventListener: 'true:true:true:true:function:0:removeEventListener',
dispatchEvent: 'true:true:true:true:function:0:dispatchEvent'
addEventListener: 'true:true:true:true:function:2:addEventListener',
removeEventListener: 'true:true:true:true:function:2:removeEventListener',
dispatchEvent: 'true:true:true:true:function:1:dispatchEvent'
};
for (const [name, shape] of Object.entries(expected)) {
const descriptor = Object.getOwnPropertyDescriptor(socket, name);
if (Object.hasOwn(socket, name) || socket[name] !== EventTarget.prototype[name])
throw new Error(`inherited EventTarget method ${name}`);
const descriptor = Object.getOwnPropertyDescriptor(EventTarget.prototype, name);
const actual = [
!!descriptor,
descriptor && descriptor.enumerable,
+3 -3
View File
@@ -49,7 +49,7 @@ interfaces! {
AudioWorkletNode: AudioNode;
BaseAudioContext: EventTarget;
BarProp;
BatteryManager;
BatteryManager: EventTarget;
BeforeUnloadEvent: Event;
BiquadFilterNode: AudioNode;
Blob;
@@ -537,12 +537,12 @@ interfaces! {
SVGTransform;
SVGTransformList;
SVGUseElement: SVGGraphicsElement;
Screen;
Screen: EventTarget;
ScreenOrientation: EventTarget;
SecurityPolicyViolationEvent: Event;
Selection;
ServiceWorker: EventTarget;
ServiceWorkerContainer;
ServiceWorkerContainer: EventTarget;
ServiceWorkerGlobalScope: WorkerGlobalScope;
ServiceWorkerRegistration: EventTarget;
ShadowRoot: DocumentFragment;
+9
View File
@@ -307,6 +307,15 @@ pub(super) fn event_target_dispatch_event_callback<'s>(
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'s, v8::Value>,
) {
// IndexedDB retains its transaction-owner dispatch scope while sharing the
// public EventTarget operation with every other interface.
if crate::web_api_interfaces::IDBDatabase::is_instance(scope, args.this())
|| crate::web_api_interfaces::IDBRequest::is_instance(scope, args.this())
|| crate::web_api_interfaces::IDBTransaction::is_instance(scope, args.this())
{
crate::context_bootstrap::dispatch_indexed_db_event_callback(scope, args, rv);
return;
}
if is_simple_event_target(scope, args.this()) {
simple_event_target_dispatch_event_callback(scope, args, rv);
return;
@@ -204,7 +204,7 @@ pub(in crate::worker) fn install_worker_global_scope<'s>(
)?;
crate::context_bootstrap::install_worker_indexed_db_runtime_state(scope, global)?;
crate::context_bootstrap::install_worker_base64_runtime_state(scope, global)?;
install_simple_event_target_methods(scope, global);
initialize_simple_event_target(scope, global);
let script_url = state.borrow().current_script_url.clone();
let origin = script_url
@@ -51,8 +51,8 @@ use super::{
};
use crate::context_bootstrap::WebCryptoTaskResult;
use crate::context_bootstrap::{
WebCryptoRejection, install_simple_event_target_methods,
simple_object_event_listeners_snapshot, simple_object_event_set_handler,
WebCryptoRejection, initialize_simple_event_target, simple_object_event_listeners_snapshot,
simple_object_event_set_handler,
};
use crate::network::loads::{ResourceLoadDisposition, ResourceLoadKind, ResourceLoadLease};
use crate::network_host::xhr_has_upload_listeners;
@@ -195,7 +195,7 @@ pub(in crate::worker) fn build_service_worker_global_service_worker<'s>(
SERVICE_WORKER_VERSION_ID_SLOT,
version_id_value.into(),
);
install_simple_event_target_methods(scope, worker);
initialize_simple_event_target(scope, worker);
Ok(worker)
}
@@ -1,5 +1,28 @@
use super::*;
#[tokio::test]
async fn worker_interfaces_inherit_canonical_event_target_operations() {
ensure_v8();
let probe = include_str!("../../../../tests/fixtures/event-target-inheritance.js");
let mut handle = spawn_worker(
format!(
"{probe}; postMessage({{pending: __eventTargetInheritance.pending, rows: __eventTargetInheritance.rows}}); close();"
),
"https://event-target-inheritance.test/worker.js".into(),
);
let message = timeout(TIMEOUT, handle.recv()).await.unwrap().unwrap();
let result: serde_json::Value = serde_json::from_str(&expect_post_json(message)).unwrap();
assert_eq!(result["pending"], false);
assert!(
result["rows"]
.as_array()
.unwrap()
.iter()
.all(|row| row[1] == true),
"{result}"
);
}
#[tokio::test]
async fn worker_event_target_prototype_methods_preserve_native_state() {
ensure_v8();
@@ -0,0 +1,76 @@
(() => {
const rows = [];
const methods = ['addEventListener', 'removeEventListener', 'dispatchEvent'];
const check = (condition, label) => {
rows.push([label, !!condition]);
if (!condition) throw Error(label);
};
const inspect = (target, label) => {
check(target instanceof EventTarget, label + ': native inheritance');
for (const name of methods) {
check(!Object.hasOwn(target, name), label + ': no own ' + name);
check(target[name] === EventTarget.prototype[name], label + ': inherited ' + name);
}
const calls = [];
function listener(event) {
calls.push(this === target && event.target === target && event.currentTarget === target &&
event.composedPath()[0] === target);
event.preventDefault();
}
target.addEventListener('inheritance-probe', listener, {once: true});
const event = new Event('inheritance-probe', {cancelable: true});
check(!target.dispatchEvent(event) && calls.length === 1 && calls[0], label + ': dispatch identity');
check(target.dispatchEvent(new Event('inheritance-probe')) && calls.length === 1, label + ': once');
check(event.currentTarget === null && event.composedPath().length === 0, label + ': cleanup');
const original = EventTarget.prototype.addEventListener;
let inheritedCalls = 0;
try {
EventTarget.prototype.addEventListener = function(...args) {
inheritedCalls++;
return Reflect.apply(original, this, args);
};
target.addEventListener('inheritance-probe', listener);
check(inheritedCalls === 1, label + ': prototype replacement');
} finally {
EventTarget.prototype.addEventListener = original;
target.removeEventListener('inheritance-probe', listener);
}
};
const prototypes = ['EventTarget', 'Performance', 'BroadcastChannel', 'Worker', 'SharedWorker',
'Notification', 'WebSocket', 'OfflineAudioContext', 'BatteryManager', 'NetworkInformation',
'ServiceWorkerContainer', 'ServiceWorkerRegistration', 'ServiceWorker', 'Screen',
'ScreenOrientation', 'Navigation', 'NavigationHistoryEntry', 'IDBRequest', 'IDBDatabase', 'IDBTransaction'];
for (const name of prototypes) {
if (typeof globalThis[name] !== 'function') continue;
const prototype = globalThis[name].prototype;
for (const method of methods) {
check(prototype[method] === EventTarget.prototype[method], name + ': canonical ' + method);
if (name !== 'EventTarget') check(!Object.hasOwn(prototype, method), name + ': no duplicate ' + method);
}
}
inspect(globalThis, 'global');
inspect(performance, 'performance');
inspect(new EventTarget(), 'EventTarget');
inspect(new FileReader(), 'FileReader');
inspect(new AbortController().signal, 'AbortSignal');
const channel = new MessageChannel();
try { inspect(channel.port1, 'MessagePort'); } finally { channel.port1.close(); channel.port2.close(); }
const broadcast = new BroadcastChannel('inheritance-probe');
try { inspect(broadcast, 'BroadcastChannel'); } finally { broadcast.close(); }
if (typeof window !== 'undefined') {
inspect(navigation, 'navigation');
if (navigation.currentEntry) inspect(navigation.currentEntry, 'NavigationHistoryEntry');
inspect(screen, 'screen');
inspect(screen.orientation, 'screen.orientation');
inspect(navigator.connection, 'navigator.connection');
if (navigator.serviceWorker) inspect(navigator.serviceWorker, 'navigator.serviceWorker');
}
globalThis.__eventTargetInheritance = {rows, pending: false, inspect};
if (typeof navigator.getBattery === 'function') {
__eventTargetInheritance.pending = true;
navigator.getBattery().then(battery => {
try { inspect(battery, 'battery'); } finally { __eventTargetInheritance.pending = false; }
}, () => { rows.push(['battery: resolved', false]); __eventTargetInheritance.pending = false; });
}
return true;
})()