diff --git a/crates/obscura-browser/src/page.rs b/crates/obscura-browser/src/page.rs index ec05bb3..a061f63 100644 --- a/crates/obscura-browser/src/page.rs +++ b/crates/obscura-browser/src/page.rs @@ -197,6 +197,10 @@ pub struct Page { // contract. Includes `Runtime.addBinding` shims so puppeteer's // `exposeFunction` bindings exist before inline ` + "#, + )); + page.init_js(); + page.execute_scripts().await; + + let before = page + .js + .as_mut() + .unwrap() + .evaluate( + r#" + var scriptStateSetup = true; + const dynamic = document.createElement("script"); + dynamic.id = "dynamic"; + dynamic.textContent = + 'document.body.setAttribute("data-dynamic-runs", String(Number(document.body.getAttribute("data-dynamic-runs")) + 1))'; + document.body.appendChild(dynamic); + + const holder = document.createElement("div"); + holder.innerHTML = + '", + )); + page.init_js(); + page.js + .as_mut() + .unwrap() + .evaluate( + "var setup = true; const old = document.getElementById('old'); globalThis.__markParserScripts([old._nid]); return old._nid;", + ) + .unwrap(); + page.suspend_js(); + + page.url = Some(url::Url::parse("http://example.com/new.html").unwrap()); + page.dom = Some(parse_html( + "", + )); + page.init_js(); + let result = page + .js + .as_mut() + .unwrap() + .evaluate( + "var check = true; document.head.appendChild(document.getElementById('fresh')); return document.body.getAttribute('data-fresh-runs');", + ) + .unwrap(); + assert_eq!(result, serde_json::json!("1")); + } + fn import_map_test_page(name: &str, base: &str, html: &str) -> super::Page { let context = std::sync::Arc::new(crate::BrowserContext::with_storage_and_network( name.to_string(), None, false, None, None, true, diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js index d82093c..35cf893 100644 --- a/crates/obscura-js/js/bootstrap.js +++ b/crates/obscura-js/js/bootstrap.js @@ -86,6 +86,7 @@ const _DOM_MUTATION_COMMANDS = new Set([ "append_child", "insert_before", "remove_child", "set_attribute", "remove_attribute", "set_text_content", "set_inner_html", "set_inner_html_context", + "set_fragment_html_executable", "create_element", "create_text_node", "create_comment_node", "create_document_fragment", "clone_node", ]); @@ -239,13 +240,11 @@ function _decodeDataScriptUrl(url) { } return new TextDecoder().decode(new Uint8Array(bytes)); } -// A script element executes at most once. Parser-discovered scripts are marked -// before page execution starts, and dynamically inserted scripts are marked -// when they are queued. Framework hydration may move existing ", + ); + let result = rt + .evaluate( + r#" + var scriptTestSetup = true; + globalThis.__cloneScriptRuns = 0; + const parser = document.getElementById("parser"); + globalThis.__markParserScripts([parser._nid]); + document.head.appendChild(parser); + document.body.appendChild(parser.cloneNode(true)); + + const dynamic = document.createElement("script"); + dynamic.textContent = "globalThis.__cloneScriptRuns++"; + document.body.appendChild(dynamic); + document.body.appendChild(dynamic.cloneNode(true)); + + const holder = document.createElement("div"); + holder.innerHTML = "