fix(input): reconcile native drop events and merged keyboard fixtures

This commit is contained in:
ldm0
2026-09-29 16:41:49 +08:00
parent e966a6c424
commit 7fa4476e44
6 changed files with 56 additions and 32 deletions
+1 -1
View File
@@ -3,8 +3,8 @@ use crate::conn::{BrowserContext, CdpCommandTaskStep, CommandDispatchContext};
use crate::testing::{TestContext, wait_until_frame_stopped_loading};
use moli_core::LayoutPolicy;
mod element_click;
mod drop_events;
mod element_click;
mod keyboard_events;
mod mouse_snapshot;
mod text_control_compat;
@@ -12,13 +12,14 @@ async fn keyboard_fixture(control: &str) -> TestContext {
window.__events = [];
window.__keypresses = [];
window.__inputEvents = [];
window.__inputPayloads = [];
window.__cancel = '';
window.__moveFocus = false;
for (const type of ['keydown', 'keypress', 'beforeinput', 'input', 'keyup']) {
document.addEventListener(type, event => {
__events.push(event.type + ':' + event.target.id);
if (type === 'beforeinput' || type === 'input') {
__inputEvents.push({type, data: event.data, inputType: event.inputType,
__inputPayloads.push({type, data: event.data, inputType: event.inputType,
isComposing: event.isComposing, native: event instanceof InputEvent,
ui: event instanceof UIEvent, trusted: event.isTrusted,
bubbles: event.bubbles, cancelable: event.cancelable, composed: event.composed});
@@ -528,7 +529,7 @@ fn expected_input_event(
}
async fn input_events(ctx: &mut TestContext) -> serde_json::Value {
serde_json::from_str(&evaluate_string(ctx, "JSON.stringify(__inputEvents)").await)
serde_json::from_str(&evaluate_string(ctx, "JSON.stringify(__inputPayloads)").await)
.expect("input event log must be JSON")
}
@@ -586,7 +587,7 @@ async fn cdp_input_event_data_preserves_utf16_maxlength_edits() {
])
);
evaluate_string(&mut ctx, "(__inputEvents.length = 0, '')").await;
evaluate_string(&mut ctx, "(__inputPayloads.length = 0, '')").await;
ctx.process_async(json!({
"id": 902, "method": "Input.insertText", "params": {"text": "z"}
}))
@@ -630,7 +631,7 @@ async fn cdp_beforeinput_deletion_preserves_utf16_ranges_and_cancellation() {
json!([expected_input_event("beforeinput", input_type, json!(null))])
);
evaluate_string(&mut ctx, "(__cancel = '', __inputEvents.length = 0, '')").await;
evaluate_string(&mut ctx, "(__cancel = '', __inputPayloads.length = 0, '')").await;
dispatch(&mut ctx, "rawKeyDown", key, key, "").await;
assert_eq!(field_value(&mut ctx).await, "AB");
assert_eq!(
@@ -270,11 +270,11 @@ pub(in crate::native_bridge::element) use events::construct_event;
pub(crate) use events::construct_focus_event;
pub(crate) use events::{
NodePublicEventDispatchOutcome, TextEditInputType, TouchEventPoint, construct_command_event,
construct_drag_event, construct_drop_input_event, construct_input_event, construct_interest_event,
construct_keyboard_event, construct_mouse_event_with_detail_and_modifiers,
construct_mouse_event_with_modifiers, construct_mouse_event_with_related_target_and_modifiers,
construct_pointer_event, construct_pointer_event_with_modifiers,
construct_pointer_event_with_related_target,
construct_drag_event, construct_drop_input_event, construct_input_event,
construct_interest_event, construct_keyboard_event,
construct_mouse_event_with_detail_and_modifiers, construct_mouse_event_with_modifiers,
construct_mouse_event_with_related_target_and_modifiers, construct_pointer_event,
construct_pointer_event_with_modifiers, construct_pointer_event_with_related_target,
construct_pointer_event_with_related_target_and_modifiers, construct_simple_event,
construct_submit_event, construct_toggle_event, construct_touch_event,
construct_touch_event_with_points, construct_wheel_event, dispatch_beforeinput,
@@ -25,14 +25,14 @@ use super::super::forms::{FormAssociatedResetCallbackTiming, reset_form_default_
use super::super::{
NodePublicEventDispatchOutcome, TextEditInputType, cache_input_files_from_selected_files,
construct_click_event_with_detail_and_modifiers, construct_command_event,
construct_input_event, construct_simple_event, contenteditable_editing_host,
dispatch_beforeinput, dispatch_popover_toggle_events, dispatch_public_event, element_attribute,
element_has_attribute, form_associated_form_owner, is_disabled_form_control, is_focusable,
is_valid_submit_button, label_activation_control_handle,
perform_popover_invoker_default_action, perform_summary_click_default_action,
replace_text_control_selection, resolve_url_like_attribute,
resolved_reflected_element_attribute_handle, scroll_node_into_view_at_start,
submit_form_with_submit_event, update_focus,
construct_drop_input_event, construct_input_event, construct_simple_event,
contenteditable_editing_host, dispatch_beforeinput, dispatch_popover_toggle_events,
dispatch_public_event, element_attribute, element_has_attribute, form_associated_form_owner,
is_disabled_form_control, is_focusable, is_valid_submit_button,
label_activation_control_handle, perform_popover_invoker_default_action,
perform_summary_click_default_action, replace_text_control_selection,
resolve_url_like_attribute, resolved_reflected_element_attribute_handle,
scroll_node_into_view_at_start, submit_form_with_submit_event, update_focus,
};
use super::targets::{
SpecialBrowsingContextTarget, named_iframe_target_handle_for_navigation,
@@ -638,7 +638,12 @@ fn perform_contenteditable_drop_default_action<'s>(
return false;
};
let before_input = if plaintext_only {
construct_input_event(scope, "beforeinput", TextEditInputType::InsertFromDrop, Some(&text))
construct_input_event(
scope,
"beforeinput",
TextEditInputType::InsertFromDrop,
Some(&text),
)
} else {
construct_drop_input_event(scope, "beforeinput", input_data_transfer)
};
@@ -5,11 +5,12 @@ mod dispatch;
pub(crate) use self::constructors::construct_focus_event;
pub(crate) use self::constructors::{
TextEditInputType, TouchEventPoint, construct_command_event, construct_drag_event, construct_drop_input_event,
construct_input_event, construct_interest_event, construct_keyboard_event,
construct_mouse_event_with_detail_and_modifiers, construct_mouse_event_with_modifiers,
construct_mouse_event_with_related_target_and_modifiers, construct_pointer_event,
construct_pointer_event_with_modifiers, construct_pointer_event_with_related_target,
TextEditInputType, TouchEventPoint, construct_command_event, construct_drag_event,
construct_drop_input_event, construct_input_event, construct_interest_event,
construct_keyboard_event, construct_mouse_event_with_detail_and_modifiers,
construct_mouse_event_with_modifiers, construct_mouse_event_with_related_target_and_modifiers,
construct_pointer_event, construct_pointer_event_with_modifiers,
construct_pointer_event_with_related_target,
construct_pointer_event_with_related_target_and_modifiers, construct_simple_event,
construct_submit_event, construct_toggle_event, construct_touch_event,
construct_touch_event_with_points, construct_wheel_event,
@@ -1,7 +1,7 @@
use crate::runtime::RendererPointerEventProperties;
use crate::util::{serialize_v8_iter_array, v8_string};
use super::{construct_event, event_constructor};
use super::{construct_event, event_constructor, mark_event_trusted};
use moli_webapi_declare::WebApiObject;
#[derive(WebApiObject)]
@@ -808,7 +808,11 @@ pub(crate) fn construct_drop_input_event<'s>(
data_transfer: v8::Local<'s, v8::Object>,
) -> Option<v8::Local<'s, v8::Object>> {
construct_input_event_with_transfer(
scope, event_type, TextEditInputType::InsertFromDrop, None, Some(data_transfer),
scope,
event_type,
TextEditInputType::InsertFromDrop,
None,
Some(data_transfer),
)
}
@@ -824,13 +828,26 @@ fn construct_input_event_with_transfer<'s>(
Some(text) => v8_string(scope, text)?.into(),
None => v8::null(scope).into(),
};
let data_transfer = data_transfer.map(|value| value.into()).unwrap_or_else(|| v8::null(scope).into());
let init =
InputEventInitDeclaration::new(true, event_type == "beforeinput", true, input_type, data, data_transfer)
.bind(scope)
.ok()?;
let data_transfer = data_transfer
.map(|value| value.into())
.unwrap_or_else(|| v8::null(scope).into());
let init = InputEventInitDeclaration::new(
true,
event_type == "beforeinput",
true,
input_type,
data,
data_transfer,
)
.bind(scope)
.ok()?;
// Native editing must not invoke a page-replaced InputEvent constructor.
let ctor = crate::context_bootstrap::exposed_interfaces::ensure_intrinsic_interface_constructor(scope, "InputEvent").ok()?;
let ctor =
crate::context_bootstrap::exposed_interfaces::ensure_intrinsic_interface_constructor(
scope,
"InputEvent",
)
.ok()?;
let event_type = v8_string(scope, event_type)?;
let event = ctor.new_instance(scope, &[event_type.into(), init.into()])?;
mark_event_trusted(scope, event);