From d678ba2a43ff837178f5c4fe323d82f5ac337dbb Mon Sep 17 00:00:00 2001 From: ldm0 Date: Fri, 17 Jul 2026 15:10:45 +0800 Subject: [PATCH] fix(fullscreen): expose unsupported request failures --- .../wpt-cross-current/failed-cases.txt | 3 - .../wpt-cross-current/passed-cases.txt | 3 + .../src/context_bootstrap/runtime_state.rs | 6 + moli-renderer-v8/src/native_bridge/element.rs | 26 +- .../native_bridge/element/base_prototypes.rs | 7 + .../element/event_handlers/generic.rs | 99 +++++--- .../element/event_handlers/mod.rs | 3 +- .../src/native_bridge/fullscreen.rs | 231 ++++++++++++++++++ moli-renderer-v8/src/native_bridge/mod.rs | 1 + .../script_vm/tests/browser_api/fullscreen.rs | 128 ++++++++++ .../src/script_vm/tests/browser_api/mod.rs | 1 + 11 files changed, 459 insertions(+), 49 deletions(-) create mode 100644 moli-renderer-v8/src/native_bridge/fullscreen.rs create mode 100644 moli-renderer-v8/src/script_vm/tests/browser_api/fullscreen.rs diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 4183c92521..332581c60b 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -2718,10 +2718,7 @@ focus/iframe-focus-with-same-as-top-intermediate-frame.html font-access/permissions-policy/local-fonts-supported-by-permissions-policy.html fullscreen/api/document-exit-fullscreen-active-document.html fullscreen/api/document-fullscreen-enabled.html -fullscreen/api/document-onfullscreenerror.html fullscreen/api/element-request-fullscreen-active-document.html -fullscreen/api/element-request-fullscreen-not-allowed.html -fullscreen/api/promises-reject.html gyroscope/Gyroscope-supported-by-permissions-policy.html hr-time/test_cross_frame_start.html html/browsers/browsing-the-web/back-forward-cache/eligibility/dedicated-worker.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 939a52c444..669e7ea625 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -5562,7 +5562,10 @@ focus/iframe-focuses-parent-same-site.html focus/scroll-matches-focus.html focus/synthetic-focus-event-crash.html fullscreen/api/document-fullscreen-enabled-active-document.html +fullscreen/api/document-onfullscreenerror.html +fullscreen/api/element-request-fullscreen-not-allowed.html fullscreen/api/historical.html +fullscreen/api/promises-reject.html fullscreen/crashtests/backdrop-list-item.html fullscreen/rendering/fullscreen-pseudo-class-support.html geolocation/non-secure-contexts.http.html diff --git a/moli-renderer-v8/src/context_bootstrap/runtime_state.rs b/moli-renderer-v8/src/context_bootstrap/runtime_state.rs index 2f3c9b13d9..3a7af42df5 100644 --- a/moli-renderer-v8/src/context_bootstrap/runtime_state.rs +++ b/moli-renderer-v8/src/context_bootstrap/runtime_state.rs @@ -120,6 +120,12 @@ struct DocumentPrototypeRuntimeDeclaration { setter = document_fullscreen_enabled_lenient_setter )] fullscreen_enabled: (), + #[webapi( + method = "exitFullscreen", + length = 0, + callback = native_bridge::fullscreen::document_exit_fullscreen_callback + )] + exit_fullscreen: (), #[webapi( accessor_property = "pointerLockElement", getter = native_bridge::pointer_lock::document_pointer_lock_element_getter diff --git a/moli-renderer-v8/src/native_bridge/element.rs b/moli-renderer-v8/src/native_bridge/element.rs index bfbc3f3538..ff7556922a 100644 --- a/moli-renderer-v8/src/native_bridge/element.rs +++ b/moli-renderer-v8/src/native_bridge/element.rs @@ -263,6 +263,7 @@ pub(crate) use details_dialog::{ }; use event_handlers::install_body_or_frameset_window_event_handler_accessors; use event_handlers::install_global_event_handler_template_bindings as install_global_event_handler_templates_for_owner; +use event_handlers::install_node_event_handler_template_bindings; pub(crate) use event_handlers::{ EventAttributeHandlerScope, GlobalEventHandlerOwner, body_or_frameset_reflects_window_event_type, canonical_event_handler_event_type, @@ -1481,15 +1482,22 @@ pub(crate) fn install_element_template_bindings<'s>( } match interface_name { - "Element" => install!( - ElementAriaStringReflectionDeclaration, - ElementAriaElementReflectionDeclaration, - ElementPrototypeReflectionDeclaration, - ElementPrototypeQueryAndAttributeMethodsDeclaration, - ExtendedElementPrototypeMethodsDeclaration, - ElementGeometryPrototypeDeclaration, - ElementStylePrototypeDeclaration, - ), + "Element" => { + install!( + ElementAriaStringReflectionDeclaration, + ElementAriaElementReflectionDeclaration, + ElementPrototypeReflectionDeclaration, + ElementPrototypeQueryAndAttributeMethodsDeclaration, + ExtendedElementPrototypeMethodsDeclaration, + ElementGeometryPrototypeDeclaration, + ElementStylePrototypeDeclaration, + ); + install_node_event_handler_template_bindings( + scope, + prototype, + &["onfullscreenchange", "onfullscreenerror"], + ); + } "Document" => install!(DocumentCustomElementRegistryPrototypeDeclaration), "HTMLElement" => install!( ElementStylePrototypeDeclaration, diff --git a/moli-renderer-v8/src/native_bridge/element/base_prototypes.rs b/moli-renderer-v8/src/native_bridge/element/base_prototypes.rs index 19afbb8ab3..6de063abce 100644 --- a/moli-renderer-v8/src/native_bridge/element/base_prototypes.rs +++ b/moli-renderer-v8/src/native_bridge/element/base_prototypes.rs @@ -159,6 +159,13 @@ pub(super) struct ElementPrototypeReflectionDeclaration { callback = super::super::pointer_lock::element_request_pointer_lock_callback )] request_pointer_lock: (), + #[webapi( + method = "requestFullscreen", + length = 0, + enumerable, + callback = super::fullscreen::element_request_fullscreen_callback + )] + request_fullscreen: (), #[webapi(accessor_property = "shadowRoot", enumerable, getter = element_shadow_root_getter_function)] shadow_root: (), #[webapi( diff --git a/moli-renderer-v8/src/native_bridge/element/event_handlers/generic.rs b/moli-renderer-v8/src/native_bridge/element/event_handlers/generic.rs index c3fd982ac8..c478761f48 100644 --- a/moli-renderer-v8/src/native_bridge/element/event_handlers/generic.rs +++ b/moli-renderer-v8/src/native_bridge/element/event_handlers/generic.rs @@ -120,6 +120,8 @@ pub(crate) const GENERIC_EVENT_HANDLER_PROPERTIES: &[&str] = &[ const DOCUMENT_EVENT_HANDLER_PROPERTIES: &[&str] = &[ "onfreeze", + "onfullscreenchange", + "onfullscreenerror", "onpointerlockchange", "onpointerlockerror", "onreadystatechange", @@ -169,44 +171,68 @@ pub(crate) fn install_global_event_handler_template_bindings<'s>( if matches!(owner, GlobalEventHandlerOwner::Document) && *name == "onstorage" { continue; } - let data = v8str(scope, name).into(); - let (getter, setter) = match owner { - GlobalEventHandlerOwner::Document => ( - v8::FunctionTemplate::builder(document_event_handler_getter_function) - .data(data) - .length(0) - .build(scope), - v8::FunctionTemplate::builder(document_event_handler_setter_function) - .data(data) - .length(1) - .build(scope), - ), - GlobalEventHandlerOwner::Element => ( - v8::FunctionTemplate::builder(node_event_handler_getter_function) - .data(data) - .length(0) - .build(scope), - v8::FunctionTemplate::builder(node_event_handler_setter_function) - .data(data) - .length(1) - .build(scope), - ), - }; - if let Some(function_name) = v8_string(scope, &format!("get {name}")) { - getter.set_class_name(function_name); - } - if let Some(function_name) = v8_string(scope, &format!("set {name}")) { - setter.set_class_name(function_name); - } - prototype.set_accessor_property( - v8str(scope, name).into(), - Some(getter), - Some(setter), - v8::PropertyAttribute::NONE, + install_event_handler_template_binding(scope, prototype, owner, name); + } +} + +pub(crate) fn install_node_event_handler_template_bindings<'s>( + scope: &mut v8::PinScope<'s, '_, ()>, + prototype: v8::Local<'s, v8::ObjectTemplate>, + names: &[&'static str], +) { + for name in names { + install_event_handler_template_binding( + scope, + prototype, + GlobalEventHandlerOwner::Element, + name, ); } } +fn install_event_handler_template_binding<'s>( + scope: &mut v8::PinScope<'s, '_, ()>, + prototype: v8::Local<'s, v8::ObjectTemplate>, + owner: GlobalEventHandlerOwner, + name: &'static str, +) { + let data = v8str(scope, name).into(); + let (getter, setter) = match owner { + GlobalEventHandlerOwner::Document => ( + v8::FunctionTemplate::builder(document_event_handler_getter_function) + .data(data) + .length(0) + .build(scope), + v8::FunctionTemplate::builder(document_event_handler_setter_function) + .data(data) + .length(1) + .build(scope), + ), + GlobalEventHandlerOwner::Element => ( + v8::FunctionTemplate::builder(node_event_handler_getter_function) + .data(data) + .length(0) + .build(scope), + v8::FunctionTemplate::builder(node_event_handler_setter_function) + .data(data) + .length(1) + .build(scope), + ), + }; + if let Some(function_name) = v8_string(scope, &format!("get {name}")) { + getter.set_class_name(function_name); + } + if let Some(function_name) = v8_string(scope, &format!("set {name}")) { + setter.set_class_name(function_name); + } + prototype.set_accessor_property( + v8str(scope, name).into(), + Some(getter), + Some(setter), + v8::PropertyAttribute::NONE, + ); +} + fn event_handler_property_value_for_target<'s>( scope: &mut v8::PinScope<'s, '_>, runtime_ptr: *mut super::super::super::JsContextHost, @@ -328,7 +354,9 @@ pub(crate) fn node_event_handler_getter_function<'s>( rv.set(current); return; } - if !handler_name.starts_with("on") { + if !handler_name.starts_with("on") + || !is_element_event_handler_content_attribute_name(&handler_name) + { rv.set_null(); return; } @@ -562,4 +590,3 @@ fn handle_invalid_event_handler_receiver<'s>( throw_type_error(scope, "Illegal invocation"); } } - diff --git a/moli-renderer-v8/src/native_bridge/element/event_handlers/mod.rs b/moli-renderer-v8/src/native_bridge/element/event_handlers/mod.rs index 19235d9331..c0afbac578 100644 --- a/moli-renderer-v8/src/native_bridge/element/event_handlers/mod.rs +++ b/moli-renderer-v8/src/native_bridge/element/event_handlers/mod.rs @@ -12,7 +12,8 @@ pub(super) use generic::is_element_event_handler_content_attribute_name; pub(crate) use generic::{ GlobalEventHandlerOwner, canonical_event_handler_event_type, event_handler_content_attribute_name, install_global_event_handler_template_bindings, - node_event_handler_getter_function, node_event_handler_setter_function, + install_node_event_handler_template_bindings, node_event_handler_getter_function, + node_event_handler_setter_function, }; pub(crate) use shared::{EventAttributeHandlerScope, compile_event_attribute_handler_for_owner}; diff --git a/moli-renderer-v8/src/native_bridge/fullscreen.rs b/moli-renderer-v8/src/native_bridge/fullscreen.rs new file mode 100644 index 0000000000..7a97dca6ba --- /dev/null +++ b/moli-renderer-v8/src/native_bridge/fullscreen.rs @@ -0,0 +1,231 @@ +use super::{ + JsContextHost, + element::{construct_simple_event, dispatch_public_event}, + node::{ + node_is_document, node_runtime_and_handle_from_args_or_detached, + node_runtime_and_handle_from_object_or_detached, require_element_method_receiver, + throw_incompatible_method_receiver, + }, +}; +use crate::{ + document_runtime::DomHandle, + host::HostTimerOwner, + util::{context_host_ptr_from_global_bridge, v8_string}, + webidl, +}; + +#[derive(Clone, Copy, Default, webidl::WebIdlEnum)] +#[webidl(name = "FullscreenKeyboardLock")] +enum FullscreenKeyboardLock { + #[webidl(token = "browser")] + Browser, + #[default] + #[webidl(token = "none")] + None, +} + +#[derive(Clone, Copy, Default, webidl::WebIdlEnum)] +#[webidl(name = "FullscreenNavigationUI")] +enum FullscreenNavigationUi { + #[default] + #[webidl(token = "auto")] + Auto, + #[webidl(token = "show")] + Show, + #[webidl(token = "hide")] + Hide, +} + +#[derive(Default, webidl::WebIdlDictionary)] +#[webidl(prefix = "FullscreenOptions")] +struct FullscreenOptions { + #[webidl( + name = "keyboardLock", + converter = "enum", + default = FullscreenKeyboardLock::None + )] + keyboard_lock: FullscreenKeyboardLock, + #[webidl( + name = "navigationUI", + converter = "enum", + default = FullscreenNavigationUi::Auto + )] + navigation_ui: FullscreenNavigationUi, +} + +pub(crate) fn element_request_fullscreen_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'s, v8::Value>, +) { + let Ok((runtime_ptr, element)) = node_runtime_and_handle_from_args_or_detached(scope, &args) + else { + throw_incompatible_method_receiver(scope, "Element", "requestFullscreen"); + return; + }; + let runtime = unsafe { &*runtime_ptr }; + if !require_element_method_receiver(scope, runtime, element, "requestFullscreen") { + return; + } + let Some(resolver) = v8::PromiseResolver::new(scope) else { + return; + }; + rv.set(resolver.get_promise(scope).into()); + + let options = match fullscreen_options_for_promise(scope, &args) { + Ok(options) => options, + Err(reason) => { + let _ = resolver.reject(scope, reason); + return; + } + }; + // Dictionary conversion is observable even though this headless platform + // deliberately reports that fullscreen is unsupported. + let _ = (options.keyboard_lock, options.navigation_ui); + + let active_document = runtime.document_handle(); + let Some(owner_document) = runtime.dom_host().owner_document_handle(element) else { + reject_fullscreen_promise(scope, resolver, "Element has no node document."); + return; + }; + if owner_document != active_document { + reject_fullscreen_promise(scope, resolver, "Document is not fully active."); + return; + } + + queue_fullscreen_error_event(scope, runtime_ptr, element, owner_document); + reject_fullscreen_promise(scope, resolver, "Fullscreen is not supported."); +} + +pub(crate) fn document_exit_fullscreen_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'s, v8::Value>, +) { + let Ok((runtime_ptr, document)) = + node_runtime_and_handle_from_object_or_detached(scope, args.this()) + else { + throw_incompatible_method_receiver(scope, "Document", "exitFullscreen"); + return; + }; + if !node_is_document(unsafe { &*runtime_ptr }, document) { + throw_incompatible_method_receiver(scope, "Document", "exitFullscreen"); + return; + } + let Some(resolver) = v8::PromiseResolver::new(scope) else { + return; + }; + rv.set(resolver.get_promise(scope).into()); + reject_fullscreen_promise(scope, resolver, "Document is not in fullscreen."); +} + +fn fullscreen_options<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, +) -> Result { + let context = webidl::Context::argument("Element.requestFullscreen", 1); + webidl::dictionary_arg(args, 0, context)? + .map(|object| webidl::parse_dictionary_object(scope, object)) + .transpose() + .map(|options| options.unwrap_or_default()) +} + +fn fullscreen_options_for_promise<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, +) -> Result> { + let try_catch = std::pin::pin!(v8::TryCatch::new(scope)); + let mut scope = try_catch.init(); + match fullscreen_options(&mut scope, args) { + Ok(options) => Ok(options), + Err(error) if error.is_pending_exception() => Err(scope + .exception() + .unwrap_or_else(|| v8::undefined(&scope).into())), + Err(error) => { + let message = + v8_string(&scope, &error.to_string()).unwrap_or_else(|| v8::String::empty(&scope)); + Err(v8::Exception::type_error(&scope, message)) + } + } +} + +fn queue_fullscreen_error_event( + scope: &mut v8::PinScope<'_, '_>, + runtime_ptr: *mut JsContextHost, + element: DomHandle, + document: DomHandle, +) { + let data = v8::Array::new(scope, 2); + let element = v8::BigInt::new_from_u64(scope, element.index() as u64); + let document = v8::BigInt::new_from_u64(scope, document.index() as u64); + if data.set_index(scope, 0, element.into()) != Some(true) + || data.set_index(scope, 1, document.into()) != Some(true) + { + return; + } + let Some(callback) = v8::Function::builder(queued_fullscreen_error_event_callback) + .data(data.into()) + .build(scope) + else { + return; + }; + let _ = unsafe { &mut *runtime_ptr }.queue_timeout( + scope, + callback, + 0, + HostTimerOwner::Window, + Vec::new(), + ); +} + +fn queued_fullscreen_error_event_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + _rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(runtime_ptr) = context_host_ptr_from_global_bridge(scope) else { + return; + }; + let Some((element, document)) = fullscreen_error_event_task_data(scope, args.data()) else { + return; + }; + let runtime = unsafe { &*runtime_ptr }; + let target = if runtime.dom_host().owner_document_handle(element) == Some(document) + && runtime.dom_host().is_connected_to_document(element) + { + element + } else { + document + }; + if let Some(event) = construct_simple_event(scope, "fullscreenerror", true, false, true) { + let _ = dispatch_public_event(scope, runtime_ptr, target, event); + } +} + +fn fullscreen_error_event_task_data( + scope: &mut v8::PinScope<'_, '_>, + value: v8::Local<'_, v8::Value>, +) -> Option<(DomHandle, DomHandle)> { + let data = v8::Local::::try_from(value).ok()?; + Some(( + dom_handle_from_value(data.get_index(scope, 0)?)?, + dom_handle_from_value(data.get_index(scope, 1)?)?, + )) +} + +fn reject_fullscreen_promise( + scope: &mut v8::PinScope<'_, '_>, + resolver: v8::Local<'_, v8::PromiseResolver>, + message: &str, +) { + let error = v8_string(scope, message) + .map(|message| v8::Exception::type_error(scope, message)) + .unwrap_or_else(|| v8::undefined(scope).into()); + let _ = resolver.reject(scope, error); +} + +fn dom_handle_from_value(value: v8::Local<'_, v8::Value>) -> Option { + let value = v8::Local::::try_from(value).ok()?; + let (index, lossless) = value.u64_value(); + lossless.then(|| DomHandle::new(index as usize)) +} diff --git a/moli-renderer-v8/src/native_bridge/mod.rs b/moli-renderer-v8/src/native_bridge/mod.rs index 7e677c128d..626d7f280d 100644 --- a/moli-renderer-v8/src/native_bridge/mod.rs +++ b/moli-renderer-v8/src/native_bridge/mod.rs @@ -15,6 +15,7 @@ pub(crate) use current_input::{ }; pub(crate) mod document; pub(crate) mod element; +pub(crate) mod fullscreen; mod helpers; mod history_queue; pub(crate) mod history_traversal; diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/fullscreen.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/fullscreen.rs new file mode 100644 index 0000000000..df6451dadd --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/fullscreen.rs @@ -0,0 +1,128 @@ +use super::*; + +#[tokio::test] +async fn unsupported_fullscreen_requests_reject_and_queue_spec_shaped_error_events() { + let loader = ResourceRequestClient::new(&moli_fetch::FetchConfig::default()).expect("loader"); + let mut vm = new_parsed_test_vm( + "https://fullscreen-unsupported.test/", + "
", + ); + + let before = vm + .eval( + r#" +(() => { + globalThis.__fullscreenEvents = []; + globalThis.__fullscreenRejections = []; + globalThis.__fullscreenConversionRejections = []; + globalThis.__fullscreenOptionReads = []; + document.body.setAttribute("onfullscreenerror", "globalThis.__fullscreenAttributeRan = true"); + const contentAttributeIgnored = document.body.onfullscreenerror === null; + document.onfullscreenerror = event => { + __fullscreenEvents.push([ + event.type, + event.target === document.body, + event.bubbles, + event.cancelable, + event.composed + ].join(":")); + }; + const options = { + get keyboardLock() { + __fullscreenOptionReads.push("keyboardLock"); + return "none"; + }, + get navigationUI() { + __fullscreenOptionReads.push("navigationUI"); + return "hide"; + } + }; + const request = document.body.requestFullscreen(options); + const exit = document.exitFullscreen(); + request.catch(error => __fullscreenRejections.push(`request:${error.name}`)); + exit.catch(error => __fullscreenRejections.push(`exit:${error.name}`)); + const getterError = new RangeError("navigationUI getter failed"); + const conversionInputs = [ + ["enum", { navigationUI: "invalid-value" }, null], + ["string", "foo", null], + ["number", 123, null], + ["getter", { + get navigationUI() { + throw getterError; + } + }, getterError] + ]; + const conversionPromiseShapes = conversionInputs.map(([label, input, expected]) => { + try { + const promise = document.body.requestFullscreen(input); + promise.catch(error => __fullscreenConversionRejections.push( + `${label}:${error.name}:${expected === null || error === expected}` + )); + return promise instanceof Promise; + } catch (error) { + return `throw:${error.name}`; + } + }); + const outcome = callback => { + try { + callback(); + return "return"; + } catch (error) { + return error.name; + } + }; + return JSON.stringify({ + requestShape: [ + typeof Element.prototype.requestFullscreen, + Element.prototype.requestFullscreen.length, + Object.hasOwn(Element.prototype, "onfullscreenchange"), + Object.hasOwn(Element.prototype, "onfullscreenerror") + ], + documentShape: [ + typeof Document.prototype.exitFullscreen, + Document.prototype.exitFullscreen.length, + Object.hasOwn(Document.prototype, "onfullscreenchange"), + Object.hasOwn(Document.prototype, "onfullscreenerror"), + document.fullscreenEnabled + ], + promises: request instanceof Promise && exit instanceof Promise, + conversionPromiseShapes, + contentAttributeIgnored, + optionReads: __fullscreenOptionReads, + wrongReceivers: [ + outcome(() => Element.prototype.requestFullscreen.call({})), + outcome(() => Document.prototype.exitFullscreen.call({})) + ], + events: __fullscreenEvents.length + }); +})() +"#, + ) + .expect("fullscreen rejection setup should evaluate"); + + assert_eq!( + before, + r#"{"requestShape":["function",0,true,true],"documentShape":["function",0,true,true,false],"promises":true,"conversionPromiseShapes":[true,true,true,true],"contentAttributeIgnored":true,"optionReads":["keyboardLock","navigationUI"],"wrongReceivers":["TypeError","TypeError"],"events":0}"#, + ); + + vm.advance_timers_until_deadline_for_test(&loader) + .await + .expect("fullscreen error task should drain"); + + let after = vm + .eval( + r#" +JSON.stringify({ + events: __fullscreenEvents, + rejections: __fullscreenRejections.sort(), + conversionRejections: __fullscreenConversionRejections.sort(), + contentAttributeRan: globalThis.__fullscreenAttributeRan === true +}) +"#, + ) + .expect("fullscreen rejection result should evaluate"); + assert_eq!( + after, + r#"{"events":["fullscreenerror:true:true:false:true"],"rejections":["exit:TypeError","request:TypeError"],"conversionRejections":["enum:TypeError:true","getter:RangeError:true","number:TypeError:true","string:TypeError:true"],"contentAttributeRan":false}"#, + ); +} diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/mod.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/mod.rs index 700ba0e2be..152a83aeaa 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/mod.rs @@ -20,6 +20,7 @@ mod event_handlers; mod event_listener_options; mod events_selection_storage; mod fontface_descriptors; +mod fullscreen; mod gamepad; mod history_worlds; mod ice_candidate;