ladybird/Libraries/LibWeb/MediaCapabilitiesAPI/MediaCapabilities.cpp

246 lines
9.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org>
* Copyright (c) 2025, Psychpsyo <psychpsyo@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/BooleanObject.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/MediaCapabilitiesPrototype.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/MediaCapabilitiesAPI/MediaCapabilities.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
namespace Web::MediaCapabilitiesAPI {
// https://w3c.github.io/media-capabilities/#valid-mediaconfiguration
bool MediaConfiguration::is_valid_media_configuration() const
{
// For a MediaConfiguration to be a valid MediaConfiguration, all of the following conditions MUST be true:
// 1. audio and/or video MUST exist.
if (!audio.has_value() && !video.has_value())
return false;
// 2. audio MUST be a valid audio configuration if it exists.
if (audio.has_value() && !audio.value().is_valid_audio_configuration())
return false;
// 3. video MUST be a valid video configuration if it exists.
if (video.has_value() && !video.value().is_valid_video_configuration())
return false;
return true;
}
// https://w3c.github.io/media-capabilities/#valid-mediadecodingconfiguration
bool MediaDecodingConfiguration::is_valid_media_decoding_configuration() const
{
// For a MediaDecodingConfiguration to be a valid MediaDecodingConfiguration, all of the following
// conditions MUST be true:
// 1. It MUST be a valid MediaConfiguration.
if (!is_valid_media_configuration())
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
bool VideoConfiguration::is_valid_video_configuration() const
{
// To check if a VideoConfiguration configuration is a valid video configuration, the following steps MUST be
// run:
// 1. If configurations contentType is not a valid video MIME type, return false and abort these steps.
if (!is_valid_video_mime_type(content_type))
return false;
// 2. If framerate is not finite or is not greater than 0, return false and abort these steps.
if (!isfinite(framerate) || framerate <= 0)
return false;
// 3. If an optional member is specified for a MediaDecodingType or MediaEncodingType to which its 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
bool AudioConfiguration::is_valid_audio_configuration() const
{
// To check if a AudioConfiguration configuration is a valid audio configuration, the following steps MUST be
// run:
// 1. If configurations contentType is not a valid audio MIME type, return false and abort these steps.
if (!is_valid_audio_mime_type(content_type))
return false;
// 2. Return true.
return true;
}
GC_DEFINE_ALLOCATOR(MediaCapabilities);
GC::Ref<MediaCapabilities> MediaCapabilities::create(JS::Realm& realm)
{
return realm.create<MediaCapabilities>(realm);
}
MediaCapabilities::MediaCapabilities(JS::Realm& realm)
: PlatformObject(realm)
{
}
void MediaCapabilities::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(MediaCapabilities);
}
// 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
GC::Ref<WebIDL::Promise> MediaCapabilities::decoding_info(MediaDecodingConfiguration const& configuration)
{
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.
if (!configuration.is_valid_media_decoding_configuration()) {
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.
auto result = create_a_media_capabilities_decoding_info(configuration).to_object(realm);
// 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
MediaCapabilitiesDecodingInfo create_a_media_capabilities_decoding_info(MediaDecodingConfiguration configuration)
{
// 1. Let info be a new MediaCapabilitiesDecodingInfo instance. Unless stated otherwise, reading and
// writing apply to info for the next steps.
MediaCapabilitiesDecodingInfo info = {};
// 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.
info.configuration = { { configuration.video, configuration.audio }, configuration.type, configuration.key_system_configuration };
// 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;
}
bool is_able_to_decode_media(MediaDecodingConfiguration configuration)
{
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;
}
GC::Ref<JS::Object> MediaCapabilitiesDecodingInfo::to_object(JS::Realm& realm)
{
auto object = JS::Object::create(realm, realm.intrinsics().object_prototype());
// FIXME: Also include configuration in this object.
MUST(object->create_data_property("supported", JS::BooleanObject::create(realm, supported)));
MUST(object->create_data_property("smooth", JS::BooleanObject::create(realm, smooth)));
MUST(object->create_data_property("powerEfficent", JS::BooleanObject::create(realm, power_efficient)));
return object;
}
}