From c06e7fdfe585eb6428f6e8be64d8bd00552e048f Mon Sep 17 00:00:00 2001 From: ldm0 Date: Tue, 15 Sep 2026 02:21:16 +0800 Subject: [PATCH] fix(style): process parser styles after their children finish --- moli-parser/src/stream.rs | 41 ++++++++++++++ moli-parser/src/xml.rs | 18 +++++++ moli-renderer-v8/src/runtime/phase_one/mod.rs | 53 +++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/moli-parser/src/stream.rs b/moli-parser/src/stream.rs index 3433a5ea0..15a136334 100644 --- a/moli-parser/src/stream.rs +++ b/moli-parser/src/stream.rs @@ -2566,6 +2566,47 @@ mod tests { ); } + #[test] + fn parser_style_children_finish_at_close_and_eof() { + for (opening, closing) in [ + (""), + (""), + (""), + ); + let root = document.document_element_node_id().unwrap(); + assert!( + !document + .node(root) + .and_then(Node::as_element) + .unwrap() + .style_children_parsing() + ); + } + } + #[test] fn xml_parser_records_unclosed_and_mismatched_elements() { for (index, source) in [ diff --git a/moli-renderer-v8/src/runtime/phase_one/mod.rs b/moli-renderer-v8/src/runtime/phase_one/mod.rs index 88eea8855..605378027 100644 --- a/moli-renderer-v8/src/runtime/phase_one/mod.rs +++ b/moli-renderer-v8/src/runtime/phase_one/mod.rs @@ -6106,6 +6106,59 @@ globalThis.__outerContinued = true; })); } + #[test] + fn parser_style_csp_checks_complete_contents_once() { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("runtime"); + runtime.block_on(tokio::task::LocalSet::new().run_until(async { + for (markup, policy, report_only, expected_violations, expected_sheet) in [ + ("", "style-src 'none'", false, 1, false), + ("", "style-src 'none'", true, 1, true), + ("", "style-src 'sha256-rB6kiow2O3eFUeTNyyLeK3wV0+l7vNB90J1aqllKvjg='", false, 0, true), + ("", "style-src 'none'", false, 1, false), + ("\")", "style-src 'none'", false, 1, false), + ("", "style-src 'none'", false, 2, false), + ] { + let env = default_test_page_vm_env_config_with(|env| { + let policies = if report_only { + &mut env.document_policy_container.response_content_security_report_only_policies + } else { + &mut env.document_policy_container.response_content_security_policies + }; + *policies = vec![policy.to_owned()]; + }); + let html = Box::leak(format!("{markup}").into_boxed_str()); + let mut page_vm = parse_phase_one_html_into_page_vm_for_test_with_env_and_finish(html, env, true).await; + let result = page_vm.evaluate_expression("Boolean(document.getElementById('sheet').sheet)").expect("stylesheet probe"); + assert_eq!(result.get("value").and_then(serde_json::Value::as_bool), Some(expected_sheet), "{markup}, {policy}, report-only={report_only}"); + + let local_executor = page_vm.local_executor.clone(); + let page_vm_ptr: *mut PageVm = &mut page_vm; + super::access::run_named_owner_local_task( + local_executor, + "parser style CSP task channel closed", + async move { + let page_vm = unsafe { &mut *page_vm_ptr }; + page_vm.page_task_queue.accept_ready_parse_time_wakes(); + page_vm.vm_mut().drain_pre_domcontentloaded_content_security_policy_violation_tasks_for_test(); + while let Some(task) = page_vm.page_task_queue.parse_time_pop_front() { + let work = PostParsePageOwnedWork::lifecycle_work( + crate::page_task_queue::PostParseLifecycleWork::from_parse_time_page_task(task), + ); + execute_page_owned_work_turn_on_local_task(page_vm, work).await?; + } + Ok(()) + }, + ).await.expect("parser style tasks"); + let result = page_vm.evaluate_expression("__parserStyleViolations").expect("violation event count"); + assert_eq!(result.get("value").and_then(serde_json::Value::as_u64), Some(expected_violations), "{markup}, {policy}, report-only={report_only}"); + } + })); + } + #[test] fn buffered_script_preload_cache_reuses_ready_late_parser_blocking_preload() { let runtime = tokio::runtime::Builder::new_current_thread()