From 3ef3da1a634fcff39d53dc50e13664ea5a494a93 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 30 Sep 2026 02:45:14 +0800 Subject: [PATCH] fix(document): reset child quirks mode when opening a stream --- .../child_documents/live_parser.rs | 4 + .../live_document/document_write.rs | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs b/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs index c644e32ed3..86e96d1a87 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/child_documents/live_parser.rs @@ -1194,6 +1194,10 @@ impl JsContextHost { .current_child_document_task_owner(child_handle) .expect("committed child document-open parser must have a task owner"); assert_eq!(task_owner.document_owner(), owner); + // open() resets the mode before any input is consumed. The new parser + // can select quirks mode later, after a complete doctype token or EOF. + self.dom_host_mut() + .set_html_quirks_mode_for_parser_document(document_handle, QuirksMode::NoQuirks); let parser = DocumentParserSession::start_open_live_document( document_url, document_handle, diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/document_write.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/document_write.rs index 7c6ab68b66..0068b6f10e 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/document_write.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/document_write.rs @@ -704,3 +704,98 @@ fn document_visibility_and_default_view_follow_receiver_across_realms() { assert_eq!(result, "[]"); } + +#[test] +fn child_document_open_resets_compat_mode_before_doctype_or_eof() { + let mut vm = new_parsed_test_vm( + "https://child-document-open-quirks.test/", + "parent", + ); + let result = vm + .eval( + r#" +(() => { + const f = document.body.appendChild(document.createElement('iframe')); + const d = f.contentDocument; + d.open(); + d.write('initial quirks document'); + d.close(); + const rows = []; + for (const chunks of [ + [], + [''], + [''], + ['', 'quirks'], + [''] + ]) { + const modes = [d.compatMode]; + d.open(); + modes.push(d.compatMode); + for (const chunk of chunks) { + d.write(chunk); + modes.push(d.compatMode); + } + d.close(); + modes.push(d.compatMode); + rows.push(modes.join('|')); + } + f.remove(); + return rows.join('\n'); +})() +"#, + ) + .unwrap(); + assert_eq!( + result, + [ + "BackCompat|CSS1Compat|BackCompat", + "BackCompat|CSS1Compat|CSS1Compat|CSS1Compat|BackCompat|BackCompat", + "BackCompat|CSS1Compat|CSS1Compat|CSS1Compat|CSS1Compat", + "CSS1Compat|CSS1Compat|CSS1Compat|BackCompat|BackCompat", + "BackCompat|CSS1Compat|CSS1Compat|CSS1Compat", + ] + .join("\n") + ); + assert_eq!(vm.eval("document.compatMode").unwrap(), "CSS1Compat"); +} + +#[test] +fn child_document_open_initializes_native_no_quirks_mode() { + for open in ["d.open()", "d.write('quirks parent", + ); + vm.eval(&format!( + r#" +const f = document.body.appendChild(document.createElement('iframe')); +f.id = 'mode-frame'; +const d = f.contentDocument; +{open}; +const html = d.appendChild(d.createElement('html')); +html.appendChild(d.createElement('body')); +d.body.innerHTML = '

child

'; +"# + )) + .unwrap(); + assert_eq!( + vm.eval("JSON.stringify([d.compatMode, d.querySelectorAll('.upper').length, document.compatMode])") + .unwrap(), + r#"["CSS1Compat",0,"BackCompat"]"#, + "{open}" + ); + let host = vm._context_host.borrow(); + let frame = host.dom_host().element_handle_by_id("mode-frame").unwrap(); + let document = host.child_browsing_context_document_handle(frame).unwrap(); + assert_eq!( + host.dom_host() + .node(document) + .unwrap() + .as_document() + .unwrap() + .quirks_mode(), + selectors::matching::QuirksMode::NoQuirks, + "{open}: native mode must not be limited-quirks" + ); + } +}