fix(page-visibility): hide windowless documents

Source-commit: 194db8dfb4
This commit is contained in:
ldm0
2026-09-11 01:09:18 +08:00
parent 5699c4621b
commit bd58b672a6
3 changed files with 45 additions and 21 deletions
+37 -15
View File
@@ -1108,11 +1108,14 @@ fn document_hidden_getter_function<'s>(
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'s, v8::Value>,
) {
if document_receiver_runtime_and_handle(scope, args.this()).is_none() {
let Some((runtime_ptr, handle)) = document_receiver_runtime_and_handle(scope, args.this())
else {
rv.set_undefined();
return;
}
rv.set_bool(false);
};
rv.set_bool(
document_associated_window_for_object(scope, runtime_ptr, handle, args.this()).is_none(),
);
}
fn document_visibility_state_getter_function<'s>(
@@ -1120,11 +1123,19 @@ fn document_visibility_state_getter_function<'s>(
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'s, v8::Value>,
) {
if document_receiver_runtime_and_handle(scope, args.this()).is_none() {
let Some((runtime_ptr, handle)) = document_receiver_runtime_and_handle(scope, args.this())
else {
rv.set_undefined();
return;
}
set_document_string_return_value(scope, &mut rv, "visible");
};
let state = if document_associated_window_for_object(scope, runtime_ptr, handle, args.this())
.is_some()
{
"visible"
} else {
"hidden"
};
set_document_string_return_value(scope, &mut rv, state);
}
fn document_prerendering_getter_function<'s>(
@@ -1316,22 +1327,33 @@ fn document_default_view_getter_function<'s>(
rv.set_null();
return;
};
match document_associated_window_for_object(scope, runtime_ptr, handle, args.this()) {
Some(window) => rv.set(window.into()),
None => rv.set_null(),
}
}
fn document_associated_window_for_object<'s>(
scope: &mut v8::PinScope<'s, '_>,
runtime_ptr: *mut JsContextHost,
handle: DomHandle,
document: v8::Local<'s, v8::Object>,
) -> Option<v8::Local<'s, v8::Object>> {
let runtime = unsafe { &*runtime_ptr };
if !node_is_document(runtime, handle) {
rv.set_null();
return;
return None;
}
if let Some(window) = get_private_value(scope, args.this(), DOCUMENT_ASSOCIATED_WINDOW_SLOT)
&& !window.is_null_or_undefined()
if let Some(window) = get_private_value(scope, document, DOCUMENT_ASSOCIATED_WINDOW_SLOT)
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
{
rv.set(window);
return;
return Some(window);
}
if runtime.dom_host().document_handle() != handle {
rv.set_null();
return;
return None;
}
rv.set(scope.get_current_context().global(scope).into());
document
.get_creation_context(scope)
.map(|context| context.global(scope))
}
pub(in crate::native_bridge) fn set_document_associated_window<'s>(
@@ -3275,8 +3275,8 @@ fn detached_document_state_and_collections_use_document_prototype_accessors() {
const htmlKeys = Object.keys(html).filter((name) => names.includes(name)).join(",");
assert(html.currentScript === null, "html currentScript");
assert(html.hidden === false, "html hidden");
assert(html.visibilityState === "visible", "html visibility");
assert(html.hidden === true, "html hidden");
assert(html.visibilityState === "hidden", "html visibility");
assert(html.prerendering === false, "html prerendering");
assert(html.scrollingElement === html.documentElement, "html scrollingElement");
assert(html.forms.length === 1, "html forms");
@@ -3288,9 +3288,11 @@ fn detached_document_state_and_collections_use_document_prototype_accessors() {
assert(html.plugins.length === 1, "html plugins");
assert(html.applets.length === 0, "html applets");
assert(parsed.images.length === 1, "parsed images");
assert(parsed.hidden === true, "parsed hidden");
assert(parsed.visibilityState === "hidden", "parsed visibility");
assert(xml.images === undefined, "xml images");
assert(xml.hidden === false, "xml hidden");
assert(xml.visibilityState === "visible", "xml visibility");
assert(xml.hidden === true, "xml hidden");
assert(xml.visibilityState === "hidden", "xml visibility");
for (const name of names) {
html[name];
@@ -908,8 +908,8 @@ fn document_state_and_collection_accessors_live_on_document_prototype() {
const xml = document.implementation.createDocument("urn:test", "root", null);
assert(!own(xml, "images"), "xml images should not be own");
assert(xml.images === undefined, "xml images value");
assert(xml.hidden === false, "xml hidden value");
assert(xml.visibilityState === "visible", "xml visibility value");
assert(xml.hidden === true, "xml hidden value");
assert(xml.visibilityState === "hidden", "xml visibility value");
return [
Object.prototype.toString.call(fonts),