2024-09-04 23:52:21 +01:00
/*
* Copyright ( c ) 2024 , Jamie Mansfield < jmansfield @ cadixdev . org >
2025-02-12 17:29:29 +01:00
* Copyright ( c ) 2025 , Psychpsyo < psychpsyo @ gmail . com >
2024-09-04 23:52:21 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2025-02-12 17:29:29 +01:00
# include <LibJS/Runtime/BooleanObject.h>
2024-09-04 23:52:21 +01:00
# include <LibWeb/Bindings/Intrinsics.h>
2026-04-18 10:54:06 +02:00
# include <LibWeb/Bindings/MediaCapabilities.h>
2025-02-12 17:29:29 +01:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2024-09-04 23:52:21 +01:00
# include <LibWeb/HTML/Window.h>
# include <LibWeb/MediaCapabilitiesAPI/MediaCapabilities.h>
2025-11-26 13:32:39 -05:00
# include <LibWeb/MimeSniff/MimeType.h>
2025-02-12 17:29:29 +01:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2024-09-04 23:52:21 +01:00
namespace Web : : MediaCapabilitiesAPI {
2025-02-12 17:29:29 +01:00
// https://w3c.github.io/media-capabilities/#valid-mediaconfiguration
2026-05-01 19:01:23 +02:00
bool is_valid_media_configuration ( Bindings : : MediaConfiguration const & configuration )
2025-02-12 17:29:29 +01:00
{
// For a MediaConfiguration to be a valid MediaConfiguration, all of the following conditions MUST be true:
// 1. audio and/or video MUST exist.
2026-05-01 19:01:23 +02:00
if ( ! configuration . audio . has_value ( ) & & ! configuration . video . has_value ( ) )
2025-02-12 17:29:29 +01:00
return false ;
// 2. audio MUST be a valid audio configuration if it exists.
2026-05-01 19:01:23 +02:00
if ( configuration . audio . has_value ( ) & & ! is_valid_audio_configuration ( configuration . audio . value ( ) ) )
2025-02-12 17:29:29 +01:00
return false ;
// 3. video MUST be a valid video configuration if it exists.
2026-05-01 19:01:23 +02:00
if ( configuration . video . has_value ( ) & & ! is_valid_video_configuration ( configuration . video . value ( ) ) )
2025-02-12 17:29:29 +01:00
return false ;
return true ;
}
// https://w3c.github.io/media-capabilities/#valid-mediadecodingconfiguration
2026-05-01 19:01:23 +02:00
bool is_valid_media_decoding_configuration ( Bindings : : MediaDecodingConfiguration const & configuration )
2025-02-12 17:29:29 +01:00
{
// For a MediaDecodingConfiguration to be a valid MediaDecodingConfiguration, all of the following
// conditions MUST be true:
// 1. It MUST be a valid MediaConfiguration.
2026-05-01 19:01:23 +02:00
if ( ! is_valid_media_configuration ( configuration ) )
2025-02-12 17:29:29 +01:00
return false ;
// 2. If keySystemConfiguration exists:
// FIXME: Implement this.
return true ;
}
// https://w3c.github.io/media-capabilities/#valid-audio-mime-type
bool is_valid_audio_mime_type ( StringView string )
{
// A valid audio MIME type is a string that is a valid media MIME type and for which the type per [RFC9110] is
// either audio or application.
auto mime_type = MimeSniff : : MimeType : : parse ( string ) ;
if ( ! mime_type . has_value ( ) )
return false ;
return mime_type - > type ( ) = = " audio " sv | | mime_type - > type ( ) = = " application " sv ;
}
// https://w3c.github.io/media-capabilities/#valid-video-mime-type
bool is_valid_video_mime_type ( StringView string )
{
// A valid video MIME type is a string that is a valid media MIME type and for which the type per [RFC9110] is
// either video or application.
auto mime_type = MimeSniff : : MimeType : : parse ( string ) ;
if ( ! mime_type . has_value ( ) )
return false ;
return mime_type - > type ( ) = = " video " sv | | mime_type - > type ( ) = = " application " sv ;
}
// https://w3c.github.io/media-capabilities/#valid-video-configuration
2026-05-01 19:01:23 +02:00
bool is_valid_video_configuration ( Bindings : : VideoConfiguration const & configuration )
2025-02-12 17:29:29 +01:00
{
// To check if a VideoConfiguration configuration is a valid video configuration, the following steps MUST be
// run:
// 1. If configuration’ s contentType is not a valid video MIME type, return false and abort these steps.
2026-05-01 19:01:23 +02:00
if ( ! is_valid_video_mime_type ( configuration . content_type ) )
2025-02-12 17:29:29 +01:00
return false ;
// 2. If framerate is not finite or is not greater than 0, return false and abort these steps.
2026-05-01 19:01:23 +02:00
if ( ! isfinite ( configuration . framerate ) | | configuration . framerate < = 0 )
2025-02-12 17:29:29 +01:00
return false ;
// 3. If an optional member is specified for a MediaDecodingType or MediaEncodingType to which it’ s not
// applicable, return false and abort these steps. See applicability rules in the member definitions below.
// FIXME: Implement this.
// 4. Return true.
return true ;
}
// https://w3c.github.io/media-capabilities/#valid-video-configuration
2026-05-01 19:01:23 +02:00
bool is_valid_audio_configuration ( Bindings : : AudioConfiguration const & configuration )
2025-02-12 17:29:29 +01:00
{
// To check if a AudioConfiguration configuration is a valid audio configuration, the following steps MUST be
// run:
// 1. If configuration’ s contentType is not a valid audio MIME type, return false and abort these steps.
2026-05-01 19:01:23 +02:00
if ( ! is_valid_audio_mime_type ( configuration . content_type ) )
2025-02-12 17:29:29 +01:00
return false ;
// 2. Return true.
return true ;
}
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( MediaCapabilities ) ;
2024-09-04 23:52:21 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < MediaCapabilities > MediaCapabilities : : create ( JS : : Realm & realm )
2024-09-04 23:52:21 +01:00
{
2024-11-14 05:50:17 +13:00
return realm . create < MediaCapabilities > ( realm ) ;
2024-09-04 23:52:21 +01:00
}
MediaCapabilities : : MediaCapabilities ( JS : : Realm & realm )
: PlatformObject ( realm )
{
}
void MediaCapabilities : : initialize ( JS : : Realm & realm )
{
WEB_SET_PROTOTYPE_FOR_INTERFACE ( MediaCapabilities ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2024-09-04 23:52:21 +01:00
}
2025-02-12 17:29:29 +01:00
// https://w3c.github.io/media-capabilities/#queue-a-media-capabilities-task
void queue_a_media_capabilities_task ( JS : : VM & vm , Function < void ( ) > steps )
{
// When an algorithm queues a Media Capabilities task T, the user agent MUST queue a global task T on the
// media capabilities task source using the global object of the the current realm record.
queue_global_task ( HTML : : Task : : Source : : MediaCapabilities , vm . current_realm ( ) - > global_object ( ) , GC : : create_function ( vm . current_realm ( ) - > heap ( ) , move ( steps ) ) ) ;
}
// https://w3c.github.io/media-capabilities/#dom-mediacapabilities-decodinginfo
2026-05-01 19:01:23 +02:00
GC : : Ref < WebIDL : : Promise > MediaCapabilities : : decoding_info ( Bindings : : MediaDecodingConfiguration const & configuration )
2025-02-12 17:29:29 +01:00
{
auto & realm = this - > realm ( ) ;
// The decodingInfo() method MUST run the following steps:
// 1. If configuration is not a valid MediaDecodingConfiguration, return a Promise rejected with a newly created
// TypeError.
2026-05-01 19:01:23 +02:00
if ( ! is_valid_media_decoding_configuration ( configuration ) ) {
2025-02-12 17:29:29 +01:00
return WebIDL : : create_rejected_promise_from_exception ( realm , vm ( ) . throw_completion < JS : : TypeError > ( " The given configuration is not a valid MediaDecodingConfiguration " sv ) ) ;
}
// 2. If configuration.keySystemConfiguration exists, run the following substeps:
// FIXME: Implement this.
// 3. Let p be a new Promise.
auto p = WebIDL : : create_promise ( realm ) ;
// 4. Run the following steps in parallel:
auto & vm = this - > vm ( ) ;
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( GC : : create_function ( realm . heap ( ) , [ & vm , & realm , p , configuration ] ( ) mutable {
HTML : : TemporaryExecutionContext context ( realm ) ;
// 1. Run the Create a MediaCapabilitiesDecodingInfo algorithm with configuration.
2026-05-01 19:01:23 +02:00
auto result = to_object ( realm , create_a_media_capabilities_decoding_info ( configuration ) ) ;
2025-02-12 17:29:29 +01:00
// Queue a Media Capabilities task to resolve p with its result.
queue_a_media_capabilities_task ( vm , [ & realm , p , result ] {
HTML : : TemporaryExecutionContext context ( realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes ) ;
WebIDL : : resolve_promise ( realm , p , JS : : Value ( result ) ) ;
} ) ;
} ) ) ;
// 5. Return p.
return p ;
}
// https://w3c.github.io/media-capabilities/#create-a-mediacapabilitiesdecodinginfo
2026-05-01 19:01:23 +02:00
Bindings : : MediaCapabilitiesDecodingInfo create_a_media_capabilities_decoding_info ( Bindings : : MediaDecodingConfiguration configuration )
2025-02-12 17:29:29 +01:00
{
// 1. Let info be a new MediaCapabilitiesDecodingInfo instance. Unless stated otherwise, reading and
// writing apply to info for the next steps.
2026-05-01 19:01:23 +02:00
Bindings : : MediaCapabilitiesDecodingInfo info = { } ;
2025-02-12 17:29:29 +01:00
// 2. Set configuration to be a new MediaDecodingConfiguration. For every property in configuration create
// a new property with the same name and value in configuration.
2026-05-01 19:01:23 +02:00
Bindings : : MediaDecodingConfiguration info_configuration { } ;
info_configuration . audio = configuration . audio ;
info_configuration . video = configuration . video ;
info_configuration . type = configuration . type ;
info_configuration . key_system_configuration = configuration . key_system_configuration ;
info . configuration . emplace ( move ( info_configuration ) ) ;
2025-02-12 17:29:29 +01:00
// 3. If configuration.keySystemConfiguration exists:
if ( false ) {
// FIXME: Implement this.
}
// 4. Otherwise, run the following steps:
else {
// 1. Set keySystemAccess to null.
// FIXME: Implement this.
// 2. If the user agent is able to decode the media represented by configuration, set supported to true.
// 3. Otherwise, set it to false.
info . supported = is_able_to_decode_media ( configuration ) ;
}
// 5. If the user agent is able to decode the media represented by configuration at the indicated framerate without
// dropping frames, set smooth to true. Otherwise set it to false.
// FIXME: Actually check this.
info . smooth = false ;
// 6. If the user agent is able to decode the media represented by configuration in a power efficient manner, set
// powerEfficient to true. Otherwise set it to false.
// FIXME: Actually check this... somehow.
info . power_efficient = false ;
// 7. Return info.
return info ;
}
2026-05-01 19:01:23 +02:00
bool is_able_to_decode_media ( Bindings : : MediaDecodingConfiguration configuration )
2025-02-12 17:29:29 +01:00
{
if ( configuration . type ! = Bindings : : MediaDecodingType : : MediaSource )
return false ;
if ( configuration . video . has_value ( ) ) {
auto video_mime_type = MimeSniff : : MimeType : : parse ( configuration . video . value ( ) . content_type ) ;
if ( ! video_mime_type . has_value ( ) | | ! Web : : HTML : : HTMLMediaElement : : supported_video_subtypes . contains_slow ( video_mime_type - > subtype ( ) ) )
return false ;
}
if ( configuration . audio . has_value ( ) ) {
auto audio_mime_type = MimeSniff : : MimeType : : parse ( configuration . audio . value ( ) . content_type ) ;
if ( ! audio_mime_type . has_value ( ) | | ! Web : : HTML : : HTMLMediaElement : : supported_audio_subtypes . contains_slow ( audio_mime_type - > subtype ( ) ) )
return false ;
}
return true ;
}
2026-05-01 19:01:23 +02:00
GC : : Ref < JS : : Object > to_object ( JS : : Realm & realm , Bindings : : MediaCapabilitiesDecodingInfo const & info )
2025-02-12 17:29:29 +01:00
{
auto object = JS : : Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
// FIXME: Also include configuration in this object.
2026-05-01 19:01:23 +02:00
MUST ( object - > create_data_property ( " supported " _utf16_fly_string , JS : : BooleanObject : : create ( realm , info . supported ) ) ) ;
MUST ( object - > create_data_property ( " smooth " _utf16_fly_string , JS : : BooleanObject : : create ( realm , info . smooth ) ) ) ;
MUST ( object - > create_data_property ( " powerEfficient " _utf16_fly_string , JS : : BooleanObject : : create ( realm , info . power_efficient ) ) ) ;
2025-02-12 17:29:29 +01:00
return object ;
}
2024-09-04 23:52:21 +01:00
}