fix(html): make redundant popover calls no-ops

This commit is contained in:
ldm0
2026-09-27 19:28:50 +08:00
parent a5c34c18e0
commit f3fa0c8cde
3 changed files with 45 additions and 53 deletions
@@ -439,15 +439,6 @@ fn close_open_auto_popovers(
}
}
fn throw_redundant_popover_state_error(scope: &mut v8::PinScope<'_, '_>) {
throw_dom_exception(
scope,
"InvalidStateError",
11,
"Popover is already in the requested state.",
);
}
fn parse_popover_invocation<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: &v8::FunctionCallbackArguments<'s>,
@@ -551,14 +542,15 @@ pub(in crate::native_bridge) fn node_show_popover_callback<'s>(
return;
};
let runtime = unsafe { &*runtime_ptr };
if !ensure_popover_supported(scope, runtime, handle)
|| !ensure_popover_connected(scope, runtime, handle)
{
if !ensure_popover_supported(scope, runtime, handle) {
rv.set_undefined();
return;
}
if popover_is_open(runtime, handle) {
throw_redundant_popover_state_error(scope);
rv.set_undefined();
return;
}
if !ensure_popover_connected(scope, runtime, handle) {
rv.set_undefined();
return;
}
@@ -577,14 +569,15 @@ pub(in crate::native_bridge) fn node_hide_popover_callback<'s>(
return;
};
let runtime = unsafe { &*runtime_ptr };
if !ensure_popover_supported(scope, runtime, handle)
|| !ensure_popover_connected(scope, runtime, handle)
{
if !ensure_popover_supported(scope, runtime, handle) {
rv.set_undefined();
return;
}
if !popover_is_open(runtime, handle) {
throw_redundant_popover_state_error(scope);
rv.set_undefined();
return;
}
if !ensure_popover_connected(scope, runtime, handle) {
rv.set_undefined();
return;
}
@@ -367,42 +367,7 @@ fn showing_popover_throws_when_force_close_removes_opening_popover() {
assert_eq!(result, "InvalidStateError:true|false|false");
}
#[test]
fn show_and_hide_popover_throw_on_redundant_state_changes() {
let mut vm = new_storage_test_vm("https://popover-redundant-state.test/");
let result = vm
.eval(
r#"
(() => {
const html = document.appendChild(document.createElement("html"));
const body = html.appendChild(document.createElement("body"));
const popover = document.createElement("div");
popover.popover = "auto";
body.append(popover);
const probe = callback => {
try {
callback();
return "ok";
} catch (error) {
return `${error.name}:${error instanceof DOMException}`;
}
};
const hiddenHide = probe(() => popover.hidePopover());
popover.showPopover();
const shownShow = probe(() => popover.showPopover());
popover.hidePopover();
return [hiddenHide, shownShow, popover.matches(":popover-open")].join("|");
})()
"#,
)
.expect("redundant popover state probe should evaluate");
assert_eq!(
result,
"InvalidStateError:true|InvalidStateError:true|false"
);
}
#[test]
fn opening_auto_popovers_preserves_flat_tree_ancestors() {
let mut vm = new_storage_test_vm("https://popover-flat-tree-ancestors.test/");
@@ -1315,3 +1280,37 @@ fn notification_permission_tracks_permission_overrides_and_request_permission()
);
assert_eq!(request_permission_result(&mut insecure_vm), "denied");
}
#[test]
fn show_and_hide_popover_are_no_ops_for_redundant_state_changes() {
let mut vm = new_storage_test_vm("https://popover-redundant-state.test/");
let result = vm
.eval(
r#"
(() => {
const html = document.appendChild(document.createElement("html"));
const body = html.appendChild(document.createElement("body"));
const popover = document.createElement("div");
popover.popover = "auto";
body.append(popover);
const probe = callback => {
try {
callback();
return "ok";
} catch (error) {
return `${error.name}:${error instanceof DOMException}`;
}
};
const hiddenHide = probe(() => popover.hidePopover());
popover.showPopover();
const shownShow = probe(() => popover.showPopover());
popover.hidePopover();
return [hiddenHide, shownShow, popover.matches(":popover-open")].join("|");
})()
"#,
)
.expect("redundant popover state probe should evaluate");
assert_eq!(result, "ok|ok|false");
}
@@ -453,7 +453,7 @@ fn detached_popover_methods_use_html_element_prototype_brand_checks() {
assert_eq!(
result,
r#"{"shapes":"true:function:0:true:true|true:function:0:true:true|true:function:0:true:true","own":"false,false,false","elementOwn":"false,false,false","svgTypes":"undefined,undefined,undefined","direct":"ERR:NotSupportedError:9|ERR:InvalidStateError:11|ERR:InvalidStateError:11|ERR:InvalidStateError:11","prototype":"ERR:NotSupportedError:9|ERR:InvalidStateError:11|ERR:InvalidStateError:11|ERR:InvalidStateError:11"}"#
r#"{"shapes":"true:function:0:true:true|true:function:0:true:true|true:function:0:true:true","own":"false,false,false","elementOwn":"false,false,false","svgTypes":"undefined,undefined,undefined","direct":"ERR:NotSupportedError:9|ERR:InvalidStateError:11|OK:undefined|ERR:InvalidStateError:11","prototype":"ERR:NotSupportedError:9|ERR:InvalidStateError:11|OK:undefined|ERR:InvalidStateError:11"}"#
);
}