From 0eec59dc38b7a6e2aacccbbf2d6f4e8cccfac606 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Fri, 25 Sep 2026 20:52:43 +0800 Subject: [PATCH] fix(layout): adapt WPT input to explicit published snapshots --- moli-layout/src/layout_tree/tree.rs | 24 ++++++++++ .../hyperlink_navigation.rs | 1 + .../same_document_history/navigation_focus.rs | 2 + .../range_algorithms/text.rs | 3 +- moli-renderer-v8/src/layout_renderer/mod.rs | 8 +++- .../src/native_bridge/context_host/layout.rs | 7 +-- .../src/native_bridge/document/hit_test.rs | 1 - .../element/activation/default_action.rs | 12 ++--- .../src/native_bridge/element/content.rs | 9 +--- .../element/forms/text_control.rs | 3 +- .../src/native_bridge/element/geometry.rs | 4 +- .../element/geometry/hit_test.rs | 22 ++++----- .../src/script_vm/input_dispatch.rs | 4 +- .../tests/browser_api/clipboard_exec_copy.rs | 1 + .../src/script_vm/tests/close_watchers.rs | 1 + .../extracted/geometry_and_scrolling.rs | 1 + .../tests/dom_elements/viewport_input.rs | 46 ++++++++++++++++++- .../script_vm/tests/dom_xhr/dom/dom_parser.rs | 24 +++++++++- .../src/script_vm/tests/dom_xhr/misc.rs | 2 + .../src/script_vm/tests/element_click.rs | 15 ++++-- 20 files changed, 142 insertions(+), 48 deletions(-) diff --git a/moli-layout/src/layout_tree/tree.rs b/moli-layout/src/layout_tree/tree.rs index 3dfa5e0029..194b191d80 100644 --- a/moli-layout/src/layout_tree/tree.rs +++ b/moli-layout/src/layout_tree/tree.rs @@ -117,6 +117,30 @@ impl FrozenLayoutTree where N: Copy + Debug + Eq + Hash, { + /// Retains a published viewport for an embedded Document without a root + /// element. The document identity is its source, and no box can receive + /// input or provide element geometry until a later layout is published. + pub fn empty_viewport(source_root: N, viewport: LayoutViewport) -> Self { + Self::new( + source_root, + viewport, + LayoutPoint::ZERO, + LayoutSize::new(viewport.css_width as f32, viewport.css_height as f32), + LayoutOutputBoxId::from_index(0), + Vec::new(), + Vec::new(), + Vec::new(), + FrozenCoordinateSpace { + owner: None, + local_to_viewport: super::model::LayoutTransform2D::IDENTITY, + local_to_viewport_ignoring_css_transforms: + super::model::LayoutTransform2D::IDENTITY, + }, + Vec::new(), + Vec::new(), + ) + } + /// Source root whose complete layout projection this member tree owns. pub const fn source_root(&self) -> N { self.source_root diff --git a/moli-protocol/src/domains/page/tests/same_document_history/hyperlink_navigation.rs b/moli-protocol/src/domains/page/tests/same_document_history/hyperlink_navigation.rs index 50229a11c1..3ddbaeb81a 100644 --- a/moli-protocol/src/domains/page/tests/same_document_history/hyperlink_navigation.rs +++ b/moli-protocol/src/domains/page/tests/same_document_history/hyperlink_navigation.rs @@ -14,6 +14,7 @@ async fn activate( )) .await; if matches!(mode, "trusted" | "location" | "nested-click") { + page.command("Page.captureScreenshot", json!({})).await; for (kind, buttons) in [("mousePressed", 1), ("mouseReleased", 0)] { page.command("Input.dispatchMouseEvent", json!({ "type": kind, "x": 20, "y": 20, "button": "left", "buttons": buttons, "clickCount": 1, diff --git a/moli-protocol/src/domains/page/tests/same_document_history/navigation_focus.rs b/moli-protocol/src/domains/page/tests/same_document_history/navigation_focus.rs index 3800e69258..cb28976b2d 100644 --- a/moli-protocol/src/domains/page/tests/same_document_history/navigation_focus.rs +++ b/moli-protocol/src/domains/page/tests/same_document_history/navigation_focus.rs @@ -162,6 +162,7 @@ async fn navigation_focus_reset_respects_document_policy_and_manual_focus() { .await; // Establish the parent's focus with real input even though its // policy also denies script-initiated focus without activation. + page.command("Page.captureScreenshot", json!({})).await; for (kind, buttons) in [("mousePressed", 1), ("mouseReleased", 0)] { page.command("Input.dispatchMouseEvent", json!({ "type": kind, "x": 310, "y": 10, "button": "left", "buttons": buttons, "clickCount": 1, @@ -201,6 +202,7 @@ async fn navigation_focus_reset_preserves_trusted_initiation_after_activation_ex "{PROBE}\nsetupNavigationFocusProbe(true, \"'none'\")" )) .await; + page.command("Page.captureScreenshot", json!({})).await; for (kind, buttons) in [("mousePressed", 1), ("mouseReleased", 0)] { page.command("Input.dispatchMouseEvent", json!({ "type": kind, "x": 20, "y": 20, "button": "left", "buttons": buttons, "clickCount": 1, diff --git a/moli-renderer-v8/src/context_bootstrap/range_algorithms/text.rs b/moli-renderer-v8/src/context_bootstrap/range_algorithms/text.rs index a96555050c..7b49e3ecc3 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_algorithms/text.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_algorithms/text.rs @@ -2,8 +2,7 @@ use super::ordering::point_order_handles; use super::*; use crate::native_bridge::document::XHTML_NS; use crate::native_bridge::element::{ - StyleMode, observable_sources_with_fragments, - style_property_value, + StyleMode, observable_sources_with_fragments, style_property_value, }; use crate::util::string_from_utf16_units_lossy; use std::{cmp::Ordering, collections::HashSet}; diff --git a/moli-renderer-v8/src/layout_renderer/mod.rs b/moli-renderer-v8/src/layout_renderer/mod.rs index 68f6b925a9..d3a52ff234 100644 --- a/moli-renderer-v8/src/layout_renderer/mod.rs +++ b/moli-renderer-v8/src/layout_renderer/mod.rs @@ -108,7 +108,13 @@ impl EmbeddedFrameRenderer for NativeEmbeddedFrameRenderer<'_> { .dom() .document_element_handle_for_document(document) else { - return Ok(None); + return Ok(Some(moli_layout::EmbeddedFrameSnapshot::new( + moli_layout::FrozenLayoutTree::empty_viewport(document, viewport), + self.capture_paint.then(|| { + moli_layout::PaintSnapshot::new(viewport, moli_layout::PaintColor::TRANSPARENT) + }), + Vec::new(), + ))); }; let mut services = self .embedded_document_services diff --git a/moli-renderer-v8/src/native_bridge/context_host/layout.rs b/moli-renderer-v8/src/native_bridge/context_host/layout.rs index 5c9ff1e765..9eed554301 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/layout.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/layout.rs @@ -477,12 +477,13 @@ impl JsContextHost { let root = self .dom_host() .dom() - .document_element_handle_for_document(document); + .document_element_handle_for_document(document) + .unwrap_or(document); let state = self.document_layout_state.borrow(); state .latest_layout(document) - .or_else(|| root.and_then(|root| state.latest_layout_for_root(root))) - .filter(|tree| Some(tree.source_root()) == root) + .or_else(|| state.latest_layout_for_root(root)) + .filter(|tree| tree.source_root() == root) .map(inspect) } diff --git a/moli-renderer-v8/src/native_bridge/document/hit_test.rs b/moli-renderer-v8/src/native_bridge/document/hit_test.rs index 347884e015..5f5ff27681 100644 --- a/moli-renderer-v8/src/native_bridge/document/hit_test.rs +++ b/moli-renderer-v8/src/native_bridge/document/hit_test.rs @@ -558,7 +558,6 @@ fn element_at_point( document, moli_layout::LayoutPoint::new(x as f32, y as f32), false, - moli_layout::LayoutFlushReason::HitTest, )?; if let Some(element) = hits.into_iter().find_map(|hit| { let element = element_for_hit_source(runtime, hit.source)?; diff --git a/moli-renderer-v8/src/native_bridge/element/activation/default_action.rs b/moli-renderer-v8/src/native_bridge/element/activation/default_action.rs index de0e2df3ce..4a794588d1 100644 --- a/moli-renderer-v8/src/native_bridge/element/activation/default_action.rs +++ b/moli-renderer-v8/src/native_bridge/element/activation/default_action.rs @@ -24,13 +24,11 @@ use super::super::forms::{ use super::super::{ NodePublicEventDispatchOutcome, TextEditInputType, cache_input_files_from_selected_files, closed_details_ancestors_to_reveal, construct_click_event_with_detail_and_modifiers, - construct_command_event, - construct_input_event, construct_simple_event, contenteditable_editing_host, - dispatch_beforeinput, dispatch_popover_hide_events, dispatch_popover_show_events, - 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, + construct_command_event, construct_input_event, construct_simple_event, + contenteditable_editing_host, dispatch_beforeinput, dispatch_popover_hide_events, + dispatch_popover_show_events, 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, diff --git a/moli-renderer-v8/src/native_bridge/element/content.rs b/moli-renderer-v8/src/native_bridge/element/content.rs index be90bbb267..6b67446590 100644 --- a/moli-renderer-v8/src/native_bridge/element/content.rs +++ b/moli-renderer-v8/src/native_bridge/element/content.rs @@ -13,7 +13,7 @@ use super::super::{ JsContextHost, document::set_detached_text_replacement_value, node::node_runtime_and_handle_from_object_or_detached, throw_dom_exception, }; -use super::geometry::observable_client_rects; +use super::geometry::read_client_rects; use super::{ html_element_getter_receiver, html_element_setter_receiver, observable_sources_with_fragments, property_string_value, @@ -832,12 +832,7 @@ fn node_inner_text( && rendered_state .target_style() .is_some_and(|style| style.display != ComputedDisplayKind::Contents) - && observable_client_rects( - runtime, - handle, - moli_layout::LayoutFlushReason::SynchronousGeometry, - )? - .is_empty() + && read_client_rects(runtime, handle)?.is_empty() { return Ok(node.text_content(runtime.dom_host().dom())); } diff --git a/moli-renderer-v8/src/native_bridge/element/forms/text_control.rs b/moli-renderer-v8/src/native_bridge/element/forms/text_control.rs index 8117137ffd..b16945bb0f 100644 --- a/moli-renderer-v8/src/native_bridge/element/forms/text_control.rs +++ b/moli-renderer-v8/src/native_bridge/element/forms/text_control.rs @@ -8,8 +8,7 @@ pub(crate) use events::{ dispatch_text_control_event, queue_text_control_document_selection_change_event, }; pub(crate) use selection::{ - replace_text_control_selection, - text_control_set_selection_range_internal, + replace_text_control_selection, text_control_set_selection_range_internal, text_control_set_selection_range_with_direction_internal, }; pub(in crate::native_bridge) use selection::{ diff --git a/moli-renderer-v8/src/native_bridge/element/geometry.rs b/moli-renderer-v8/src/native_bridge/element/geometry.rs index d7ac1cfa2c..9f93c96d66 100644 --- a/moli-renderer-v8/src/native_bridge/element/geometry.rs +++ b/moli-renderer-v8/src/native_bridge/element/geometry.rs @@ -11,8 +11,8 @@ pub use client_rect::ClientRect; #[cfg(test)] pub(crate) use hit_test::observable_scrollbar_hit_test; pub(crate) use hit_test::{ - element_is_inert_for_hit_testing, InputSurfaceHit, input_surface_hit_test, observable_deep_hit_test, observable_hit_test, - observable_input_hit_test, + InputSurfaceHit, element_is_inert_for_hit_testing, input_surface_hit_test, + observable_deep_hit_test, observable_hit_test, observable_input_hit_test, }; pub(in crate::native_bridge) use metrics::{ node_client_height_getter_function, node_client_left_getter_function, diff --git a/moli-renderer-v8/src/native_bridge/element/geometry/hit_test.rs b/moli-renderer-v8/src/native_bridge/element/geometry/hit_test.rs index d80d592c19..736608414b 100644 --- a/moli-renderer-v8/src/native_bridge/element/geometry/hit_test.rs +++ b/moli-renderer-v8/src/native_bridge/element/geometry/hit_test.rs @@ -1,6 +1,7 @@ use moli_layout::{ FrozenLayoutTree, LayoutControlSurfaceHit, LayoutError, LayoutHit, LayoutPaintedSurfaceHit, - LayoutPoint, LayoutQuery, LayoutQueryAnswer, LayoutQueryBatch, LayoutTransform2D, LayoutViewport, + LayoutPoint, LayoutQuery, LayoutQueryAnswer, LayoutQueryBatch, LayoutTransform2D, + LayoutViewport, }; #[cfg(test)] @@ -114,11 +115,9 @@ pub(crate) fn observable_scrollbar_hit_test( document: DomHandle, point: LayoutPoint, ) -> Result>, LayoutError> { - input_surface_hit_test(runtime, document, point, false, true).map(|hit| { - match hit.control { - Some(LayoutControlSurfaceHit::Scrollbar(scrollbar)) => Some(scrollbar), - Some(LayoutControlSurfaceHit::ScrollbarCorner(_)) | None => None, - } + input_surface_hit_test(runtime, document, point, false, true).map(|hit| match hit.control { + Some(LayoutControlSurfaceHit::Scrollbar(scrollbar)) => Some(scrollbar), + Some(LayoutControlSurfaceHit::ScrollbarCorner(_)) | None => None, }) } @@ -165,13 +164,7 @@ pub(crate) fn observable_deep_hit_test( point: LayoutPoint, ignore_pointer_events_none: bool, ) -> Result, LayoutError> { - let hit = input_surface_hit_test( - runtime, - document, - point, - ignore_pointer_events_none, - false, - )?; + let hit = input_surface_hit_test(runtime, document, point, ignore_pointer_events_none, false)?; Ok(hit.document_or_element().map(|input| input.handle)) } @@ -322,7 +315,8 @@ fn input_surface_hit_test_in_tree( .dom_host() .dom() .document_element_handle_for_document(child_document) - != Some(child_tree.source_root()) + .unwrap_or(child_document) + != child_tree.source_root() { return Err(LayoutError::NoLayoutSnapshot); } diff --git a/moli-renderer-v8/src/script_vm/input_dispatch.rs b/moli-renderer-v8/src/script_vm/input_dispatch.rs index 568683f205..b6a57c6ae6 100644 --- a/moli-renderer-v8/src/script_vm/input_dispatch.rs +++ b/moli-renderer-v8/src/script_vm/input_dispatch.rs @@ -25,9 +25,9 @@ use crate::native_bridge::element::{ construct_pointer_event_with_related_target_and_modifiers, construct_simple_event, construct_touch_event, construct_touch_event_with_points, construct_wheel_event, contenteditable_editing_host, dispatch_public_event, input_surface_hit_test, is_text_control, - observable_input_hit_test, perform_auxiliary_link_default_action, perform_drop_default_action, + observable_input_hit_test, perform_auxiliary_link_default_action, + perform_clipboard_key_default_action, perform_drop_default_action, perform_implicit_submission_from_control, perform_mouse_focus_default_action, - perform_clipboard_key_default_action, perform_scrollbar_scroll_default_action, perform_wheel_scroll_default_action, replace_contenteditable_selection, replace_text_control_selection, select_contenteditable_contents, text_control_set_selection_range_internal, diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/clipboard_exec_copy.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/clipboard_exec_copy.rs index 6bdead2439..73788ebba1 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/clipboard_exec_copy.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/clipboard_exec_copy.rs @@ -18,6 +18,7 @@ navigator.clipboard.writeText('seed');'ready' } fn activated_copy_eval(vm: &mut StandaloneScriptVmHarness, script: &str) -> String { + vm.publish_layout_for_test().unwrap(); vm._context_host .borrow_mut() .begin_protocol_user_gesture_activation(); diff --git a/moli-renderer-v8/src/script_vm/tests/close_watchers.rs b/moli-renderer-v8/src/script_vm/tests/close_watchers.rs index 73deaf6f59..34a60a11b3 100644 --- a/moli-renderer-v8/src/script_vm/tests/close_watchers.rs +++ b/moli-renderer-v8/src/script_vm/tests/close_watchers.rs @@ -338,6 +338,7 @@ fn close_watcher_mouse_and_touch_activate_at_their_respective_input_phases() { setup_close_watcher_input_document(&mut vm); vm.eval("record('first').oncancel = e => e.preventDefault()") .unwrap(); + vm.publish_layout_for_test().unwrap(); if touch { vm.dispatch_touch_event_at_point(10.0, 11.0, "touchstart", false) .unwrap(); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/geometry_and_scrolling.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/geometry_and_scrolling.rs index ba5be11274..9b12d9f5a4 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/geometry_and_scrolling.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/geometry_and_scrolling.rs @@ -1425,6 +1425,7 @@ fn single_line_text_input_preserves_programmatic_scroll_across_select() { ) .expect("the previous text input should be replaced"); + refresh_layout_for_test(&mut vm); vm.eval("__scrollInput.scrollLeft = 33") .expect("text input should accept a programmatic scroll"); refresh_layout_for_test(&mut vm); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/viewport_input.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/viewport_input.rs index 9a7c37530c..1edd4a3bf1 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/viewport_input.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/viewport_input.rs @@ -15,7 +15,6 @@ fn viewport_input_vm() -> StandaloneScriptVmHarness { ..Default::default() })) .unwrap(); - vm.force_fresh_layout_reads_for_test(); vm.eval( r#" if (!document.documentElement) document.appendChild(document.createElement('html')); @@ -37,6 +36,7 @@ fn viewport_input_vm() -> StandaloneScriptVmHarness { } fn click_viewport(vm: &mut StandaloneScriptVmHarness, x: f64, y: f64) { + vm.publish_layout_for_test().unwrap(); for (event, button, buttons) in [("mousemove", -1, 0), ("mousedown", 0, 1), ("mouseup", 0, 0)] { vm.dispatch_mouse_event_at_point(x, y, event, button, Some(buttons), 0.0, 0.0) .unwrap(); @@ -240,6 +240,7 @@ fn viewport_root_input_wheel_keeps_child_target_and_chains_at_boundaries() { ) .unwrap(); vm.eval(setup).unwrap(); + vm.publish_layout_for_test().unwrap(); vm.dispatch_mouse_event_at_point(80.0, 80.0, "wheel", -1, Some(0), 0.0, 120.0) .unwrap(); let actual = vm @@ -256,3 +257,46 @@ fn viewport_root_input_wheel_keeps_child_target_and_chains_at_boundaries() { ); } } + +#[test] +fn viewport_root_input_requires_republication_when_an_empty_child_gains_a_root() { + let mut vm = viewport_input_vm(); + vm.eval( + r#" + globalThis.frame = document.createElement('iframe'); + frame.style.cssText = 'position:fixed;left:40px;top:40px;width:200px;height:50px;border:4px solid black'; + document.body.appendChild(frame); + frame.contentDocument.documentElement.remove(); + recordViewportClicks(frame.contentWindow, 'child'); + "#, + ) + .unwrap(); + click_viewport(&mut vm, 80.0, 80.0); + assert_eq!(vm.eval("viewportClicks.length").unwrap(), "0"); + + vm.eval( + r#" + const child = frame.contentDocument; + const root = child.createElement('html'); + root.style.cssText = 'margin:0;padding:0;height:0'; + child.appendChild(root); + "#, + ) + .unwrap(); + let before = vm.layout_pass_observability_for_test().1; + let error = vm + .dispatch_mouse_event_at_point(80.0, 80.0, "mousedown", 0, Some(1), 0.0, 0.0) + .unwrap_err(); + assert_eq!( + error.downcast_ref::(), + Some(&moli_layout::LayoutError::NoLayoutSnapshot) + ); + assert_eq!(vm.layout_pass_observability_for_test().1, before); + assert_eq!(vm.pressed_mouse_buttons, 0); + assert!(vm.pending_mouse_press.is_none()); + click_viewport(&mut vm, 80.0, 80.0); + assert_eq!( + vm.eval("JSON.stringify(viewportClicks)").unwrap(), + r#"[["child","HTML",36,36,true]]"# + ); +} diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom/dom_parser.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom/dom_parser.rs index 4df2c9ab22..26d35fd3ad 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom/dom_parser.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/dom/dom_parser.rs @@ -618,6 +618,7 @@ html, body { margin: 0; }
"#, ); + vm.publish_layout_for_test().unwrap(); let result = vm .eval( r#" @@ -635,13 +636,32 @@ html, body { margin: 0; } container.inert = false; values.push(top() === target); - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + return values.join('|'); +})() +"#, + ) + .expect("dynamic inert point-query probe should evaluate"); + assert_eq!(result, "true|under|under,html|true"); + + vm.eval( + r#" + globalThis.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('style', 'position:fixed;inset:0;width:100px;height:100px'); const rect = document.createElementNS(svg.namespaceURI, 'rect'); rect.setAttribute('width', '100'); rect.setAttribute('height', '100'); svg.appendChild(rect); document.body.appendChild(svg); +"#, + ) + .unwrap(); + vm.publish_layout_for_test().unwrap(); + let result = vm + .eval( + r#"(() => { + const top = () => document.elementFromPoint(1, 1); + const target = document.getElementById('target'); + const values = []; const svgHit = top(); svg.setAttribute('inert', ''); values.push(top() === svgHit); @@ -657,5 +677,5 @@ html, body { margin: 0; } ) .expect("dynamic inert point-query probe should evaluate"); - assert_eq!(result, "true|under|under,html|true|true|true"); + assert_eq!(result, "true|true"); } diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs index 8e774d2554..8d23ea3343 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs @@ -1955,6 +1955,8 @@ fn dom_rect_list_has_readonly_indexed_semantics_and_rejects_structured_clone() { "
text
", ); + vm.publish_layout_for_test().unwrap(); + let result = vm .eval( r#" diff --git a/moli-renderer-v8/src/script_vm/tests/element_click.rs b/moli-renderer-v8/src/script_vm/tests/element_click.rs index ddaa7c7e42..3ee030a954 100644 --- a/moli-renderer-v8/src/script_vm/tests/element_click.rs +++ b/moli-renderer-v8/src/script_vm/tests/element_click.rs @@ -19,7 +19,7 @@ fn native_element_click_initializes_layout_once() { } #[test] -fn native_element_click_refreshes_stale_layout_after_dom_and_style_changes() { +fn native_element_click_reads_explicitly_republished_layout_after_dom_and_style_changes() { for mutation in [ "target.style.left='300px'", "const veil=document.createElement('div');veil.style.cssText='position:fixed;inset:0;z-index:999';document.body.appendChild(veil)", @@ -55,11 +55,18 @@ fn native_element_click_refreshes_stale_layout_after_dom_and_style_changes() { "(()=>{{const target=document.getElementById('target');{mutation}}})()" )) .expect("mutate live DOM/style before preparing another click"); + let _ = vm.prepare_element_click(target); + assert_eq!( + vm.layout_pass_observability_for_test().1, + before + 1, + "click preparation must not publish changed DOM or style" + ); + publish_layout_for_test(&mut vm); let prepared = vm.prepare_element_click(target); let prepared_passes = vm.layout_pass_observability_for_test().1; assert!( prepared_passes > before + 1, - "a changed DOM or style must refresh the shared geometry snapshot: {mutation}" + "explicit publication must refresh the shared geometry snapshot: {mutation}" ); if mutation != "target.style.left='300px'" { assert!( @@ -107,8 +114,8 @@ fn native_element_click_refreshes_stale_layout_after_dom_and_style_changes() { } let passes = vm.layout_pass_observability_for_test().1 - before; assert_eq!( - passes, 1, - "the entire mouse sequence must reuse one snapshot: {mutation}" + passes, 2, + "the entire mouse sequence must reuse the republished snapshot: {mutation}" ); let events = vm.eval("JSON.stringify([clicks,events])").unwrap(); if let Some((existing_passes, existing_events)) = &reference {