mirror of
https://github.com/lexmount/moli.git
synced 2026-09-27 16:01:31 +00:00
fix(forms): honor submission entry-list guards
This commit is contained in:
@@ -1671,6 +1671,10 @@ impl JsContextHost {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn is_constructing_form_data_for(&self, form_handle: DomHandle) -> bool {
|
||||
self.constructing_form_data_forms.contains(&form_handle)
|
||||
}
|
||||
|
||||
pub(crate) fn end_form_data_construction(&mut self, form_handle: DomHandle) {
|
||||
if let Some(index) = self
|
||||
.constructing_form_data_forms
|
||||
|
||||
@@ -205,6 +205,7 @@ pub(crate) fn submit_form_with_submit_event(
|
||||
let form_can_dispatch_submit_event = {
|
||||
let runtime = unsafe { &*runtime_ptr };
|
||||
form_is_connected_or_in_detached_document(runtime, form_handle)
|
||||
&& !runtime.is_constructing_form_data_for(form_handle)
|
||||
};
|
||||
if !form_can_dispatch_submit_event {
|
||||
return false;
|
||||
@@ -576,6 +577,15 @@ pub(in crate::native_bridge) fn submit_form_default_action(
|
||||
submitter: Option<DomHandle>,
|
||||
user_initiated: bool,
|
||||
) -> bool {
|
||||
let form_can_submit = {
|
||||
let runtime = unsafe { &*runtime_ptr };
|
||||
form_is_connected_or_in_detached_document(runtime, form_handle)
|
||||
&& !runtime.is_constructing_form_data_for(form_handle)
|
||||
};
|
||||
if !form_can_submit {
|
||||
return false;
|
||||
}
|
||||
|
||||
let method = {
|
||||
let runtime = unsafe { &*runtime_ptr };
|
||||
resolve_form_submission_method(runtime, form_handle, submitter)
|
||||
@@ -604,6 +614,9 @@ pub(in crate::native_bridge) fn submit_form_default_action(
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if !form_is_connected_or_in_detached_document(unsafe { &*runtime_ptr }, form_handle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let special_target = target_name
|
||||
.as_deref()
|
||||
|
||||
@@ -2562,6 +2562,89 @@ fn url_like_href_resolution_does_not_legacy_encode_fragment_question_mark() {
|
||||
"https://href-fragment-gbk.test/search#frag?q=%E5%AE%B6%E5%B1%85"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_submission_returns_while_constructing_entry_list() {
|
||||
let mut vm = new_storage_test_vm("https://form-entry-list-reentrant.test/page.html");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const parent = document.body || document.documentElement || document;
|
||||
const form = document.createElement('form');
|
||||
form.action = '/submitted';
|
||||
form.innerHTML = '<input name=n value=v>';
|
||||
parent.appendChild(form);
|
||||
|
||||
const outcomes = [];
|
||||
let submitEvents = 0;
|
||||
form.addEventListener('submit', event => {
|
||||
submitEvents++;
|
||||
event.preventDefault();
|
||||
});
|
||||
form.addEventListener('formdata', () => {
|
||||
for (const submit of [
|
||||
() => form.submit(),
|
||||
() => form.requestSubmit()
|
||||
]) {
|
||||
try {
|
||||
submit();
|
||||
outcomes.push('returned');
|
||||
} catch (error) {
|
||||
outcomes.push(`threw:${error.name}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.submit();
|
||||
return JSON.stringify({ outcomes, submitEvents });
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("form submission entry-list reentrancy probe should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"outcomes":["returned","returned"],"submitEvents":0}"#
|
||||
);
|
||||
assert_eq!(
|
||||
vm.take_pending_location_navigation_with_seed()
|
||||
.expect("outer form submission should still queue navigation")
|
||||
.url
|
||||
.as_str(),
|
||||
"https://form-entry-list-reentrant.test/submitted?n=v"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_submission_aborts_when_formdata_listener_disconnects_form() {
|
||||
let mut vm = new_storage_test_vm("https://form-disconnected-after-entry-list.test/page.html");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const parent = document.body || document.documentElement || document;
|
||||
const form = document.createElement('form');
|
||||
form.action = '/submitted';
|
||||
form.innerHTML = '<input name=n value=v>';
|
||||
parent.appendChild(form);
|
||||
form.addEventListener('formdata', () => form.remove());
|
||||
form.submit();
|
||||
return String(form.isConnected);
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("formdata disconnect submission probe should evaluate");
|
||||
|
||||
assert_eq!(result, "false");
|
||||
assert!(
|
||||
vm.take_pending_location_navigation_with_seed().is_none(),
|
||||
"a form disconnected while constructing its entry list must not navigate"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn formdata_event_appended_entries_are_submitted_to_named_iframe() {
|
||||
let server = StaticHttpServer::spawn(1).await;
|
||||
|
||||
+1
@@ -31,6 +31,7 @@
|
||||
const form = document.createElement("form");
|
||||
form.target = "frame2";
|
||||
form.action = "resources/a-page.html";
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
|
||||
document.body.setAttribute(
|
||||
|
||||
Reference in New Issue
Block a user