diff --git a/moli-renderer-v8/src/runtime/page_vm/tests/rendering_update/realtime_resize_observer.rs b/moli-renderer-v8/src/runtime/page_vm/tests/rendering_update/realtime_resize_observer.rs
index 6fdf6bdb6e..9c3ea48ec6 100644
--- a/moli-renderer-v8/src/runtime/page_vm/tests/rendering_update/realtime_resize_observer.rs
+++ b/moli-renderer-v8/src/runtime/page_vm/tests/rendering_update/realtime_resize_observer.rs
@@ -67,3 +67,84 @@ fn realtime_ro_initial_delivery_follows_raf_and_becomes_idle_without_capture() {
);
});
}
+
+#[test]
+fn realtime_frames_admit_cssom_fonts_after_observer_callbacks_without_capture() {
+ assert_frame_cssom_font_admission(true);
+}
+
+#[test]
+fn realtime_frame_cssom_fonts_preserve_the_disabled_resource_policy() {
+ assert_frame_cssom_font_admission(false);
+}
+
+fn assert_frame_cssom_font_admission(fonts_enabled: bool) {
+ run_page_vm_large_stack_async_test("realtime-cssom-font-admission", move || async move {
+ use base64::Engine;
+
+ let loader = crate::network::ResourceRequestClient::new(&FetchConfig::default()).unwrap();
+ loader.set_optional_resource_fetch_mask(if fonts_enabled {
+ crate::protocol_types::OptionalResourceFetchMask::FONT
+ } else {
+ crate::protocol_types::OptionalResourceFetchMask::NONE
+ });
+ let mut page = realtime_page(&loader);
+ let font = base64::engine::general_purpose::STANDARD.encode(include_bytes!(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/../moli-layout/tests/fixtures/moli-ahem.woff2"
+ )));
+ let markup = "iiii";
+ page.vm_mut().eval(&format!(r#"
+ document.head.innerHTML='';
+ document.body.innerHTML="iiii";
+ const child=frame.contentDocument;
+ child.open();child.write({markup});child.close();
+ window.samples=[[],[]];window.observers=[];
+ [document,child].forEach((doc,index)=>{{
+ const ro=new doc.defaultView.ResizeObserver(entries=>{{
+ samples[index].push(entries[0].contentRect.width);
+ if(samples[index].length===1){{
+ doc.styleSheets[0].cssRules[0].style.src='url(data:font/woff2;base64,{font})';
+ }}
+ }});
+ observers.push(ro);ro.observe(doc.getElementById('target'));
+ }});
+ "#,markup=serde_json::to_string(markup).unwrap())).unwrap();
+ // Advance bounded native frame opportunities, without screenshots,
+ // geometry queries or a manual font-reconciliation helper.
+ for _ in 0..4 {
+ if page
+ .next_rendering_opportunity_deadline_for_test()
+ .is_none()
+ {
+ break;
+ }
+ run_opportunity(&mut page, &loader).await.unwrap();
+ }
+ let samples: Vec> =
+ serde_json::from_str(&page.vm_mut().eval("JSON.stringify(samples)").unwrap()).unwrap();
+ for sample in &samples {
+ assert_eq!(
+ sample.len(),
+ if fonts_enabled { 2 } else { 1 },
+ "font policy {fonts_enabled}: {samples:?}"
+ );
+ assert_ne!(sample[0], 48.0);
+ if fonts_enabled {
+ assert_eq!(sample[1], 48.0);
+ }
+ }
+ assert!(
+ page.next_rendering_opportunity_deadline_for_test()
+ .is_none()
+ );
+ assert_eq!(
+ page.vm()
+ .layout_pass_observability_for_test()
+ .3
+ .unwrap()
+ .paint_operation_count,
+ 0
+ );
+ });
+}
diff --git a/moli-renderer-v8/src/script_vm/rendering_update/realtime.rs b/moli-renderer-v8/src/script_vm/rendering_update/realtime.rs
index 4a047b10e9..7968dd0948 100644
--- a/moli-renderer-v8/src/script_vm/rendering_update/realtime.rs
+++ b/moli-renderer-v8/src/script_vm/rendering_update/realtime.rs
@@ -127,6 +127,16 @@ impl ScriptVm {
if unsafe { &*host_ptr }.realtime_rendering_opportunity_is_current(id, root) {
crate::observer_runtime::sample_realtime_intersections(scope, host_ptr)?;
}
+ if unsafe { &*host_ptr }.realtime_rendering_opportunity_is_current(id, root) {
+ // Layout has published the actual child viewports, and rAF/RO may
+ // have changed CSS font sources. Admit those resources through the
+ // existing owner-checked loader after sampling this frame. A ready
+ // font invalidates geometry and requests its own next opportunity;
+ // synchronous getters never start resource or callback work.
+ crate::document_runtime::DocumentRuntime::reconcile_document_web_fonts_in_current_scope(
+ scope, host_ptr,
+ );
+ }
Ok(invoked)
}
}