mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb: Set AudioTrack and VideoTrack fields according to spec
The two classes now inherit from a common base MediaTrackBase, to deduplicate the attributes that are shared between the two. The integer ID from the container is used for each track's id attribute. The kind attribute is set to "main" or "translation" according to: https://dev.w3.org/html5/html-sourcing-inband-tracks/ The label attribute is set to the human-readable name of the track, if one is present. The language attribute is set to a BCP 47 language tag, if one can be parsed successfully.
This commit is contained in:
parent
29ab9c5fd5
commit
3d0b8cc30c
|
|
@ -568,6 +568,7 @@ set(SOURCES
|
|||
HTML/ListOfAvailableImages.cpp
|
||||
HTML/Location.cpp
|
||||
HTML/MediaError.cpp
|
||||
HTML/MediaTrackBase.cpp
|
||||
HTML/MessageChannel.cpp
|
||||
HTML/MessageEvent.cpp
|
||||
HTML/MessagePort.cpp
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWeb/Bindings/AudioTrackPrototype.h>
|
||||
|
|
@ -20,36 +20,22 @@ namespace Web::HTML {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(AudioTrack);
|
||||
|
||||
static IDAllocator s_audio_track_id_allocator;
|
||||
|
||||
AudioTrack::AudioTrack(JS::Realm& realm, GC::Ref<HTMLMediaElement> media_element, Media::Track const& track)
|
||||
: PlatformObject(realm)
|
||||
, m_media_element(media_element)
|
||||
, m_track_in_playback_manager(track)
|
||||
: MediaTrackBase(realm, media_element, track)
|
||||
{
|
||||
}
|
||||
|
||||
AudioTrack::~AudioTrack()
|
||||
{
|
||||
auto id = m_id.to_number<int>();
|
||||
VERIFY(id.has_value());
|
||||
|
||||
s_audio_track_id_allocator.deallocate(id.value());
|
||||
}
|
||||
AudioTrack::~AudioTrack() = default;
|
||||
|
||||
void AudioTrack::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(AudioTrack);
|
||||
Base::initialize(realm);
|
||||
|
||||
auto id = s_audio_track_id_allocator.allocate();
|
||||
m_id = String::number(id);
|
||||
}
|
||||
|
||||
void AudioTrack::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_media_element);
|
||||
visitor.visit(m_audio_track_list);
|
||||
}
|
||||
|
||||
|
|
@ -66,13 +52,13 @@ void AudioTrack::set_enabled(bool enabled)
|
|||
// Whenever an audio track in an AudioTrackList that was disabled is enabled, and whenever one that was enabled
|
||||
// is disabled, the user agent must queue a media element task given the media element to fire an event named
|
||||
// change at the AudioTrackList object.
|
||||
m_media_element->queue_a_media_element_task([this]() {
|
||||
media_element().queue_a_media_element_task([this]() {
|
||||
m_audio_track_list->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::change));
|
||||
});
|
||||
}
|
||||
|
||||
m_enabled = enabled;
|
||||
m_media_element->set_audio_track_enabled({}, this, enabled);
|
||||
media_element().set_audio_track_enabled({}, this, enabled);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibMedia/Audio/Forward.h>
|
||||
#include <LibMedia/Track.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/HTML/MediaTrackBase.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
class AudioTrack final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(AudioTrack, Bindings::PlatformObject);
|
||||
class AudioTrack final : public MediaTrackBase {
|
||||
WEB_PLATFORM_OBJECT(AudioTrack, MediaTrackBase);
|
||||
GC_DECLARE_ALLOCATOR(AudioTrack);
|
||||
|
||||
public:
|
||||
|
|
@ -22,41 +23,19 @@ public:
|
|||
|
||||
void set_audio_track_list(Badge<AudioTrackList>, GC::Ptr<AudioTrackList> audio_track_list) { m_audio_track_list = audio_track_list; }
|
||||
|
||||
String const& id() const { return m_id; }
|
||||
String const& kind() const { return m_kind; }
|
||||
String const& label() const { return m_label; }
|
||||
String const& language() const { return m_language; }
|
||||
|
||||
bool enabled() const { return m_enabled; }
|
||||
void set_enabled(bool enabled);
|
||||
|
||||
Media::Track const& track_in_playback_manager() const { return m_track_in_playback_manager; }
|
||||
|
||||
private:
|
||||
AudioTrack(JS::Realm&, GC::Ref<HTMLMediaElement>, Media::Track const&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-id
|
||||
String m_id;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-kind
|
||||
String m_kind;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-label
|
||||
String m_label;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-language
|
||||
String m_language;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-enabled
|
||||
bool m_enabled { false };
|
||||
|
||||
GC::Ref<HTMLMediaElement> m_media_element;
|
||||
GC::Ptr<AudioTrackList> m_audio_track_list;
|
||||
|
||||
Media::Track m_track_in_playback_manager;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1237,6 +1237,18 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void(Str
|
|||
if (enable == TriState::True)
|
||||
audio_track->set_enabled(true);
|
||||
|
||||
// AD-HOC(ish): According to https://dev.w3.org/html5/html-sourcing-inband-tracks/, kind should be set according to format, and the following criteria within
|
||||
// the specified formats.
|
||||
// WebM:
|
||||
// - "main": the FlagDefault element is set on the track
|
||||
// - "translation": not first audio (video) track
|
||||
// MP4:
|
||||
// - "main": first audio (video) track
|
||||
// - "translation": not first audio (video) track
|
||||
// Though the behavior for WebM is not clear if its first track is not marked with FlagDefault, the idea here seems to be that the preferred
|
||||
// track should be marked as "main", and the rest should be marked as "translation".
|
||||
audio_track->set_kind(enable == TriState::True ? "main"_utf16 : "translation"_utf16);
|
||||
|
||||
// 7. Fire an event named addtrack at this AudioTrackList object, using TrackEvent, with the track attribute initialized to the new AudioTrack object.
|
||||
TrackEventInit event_init {};
|
||||
event_init.track = GC::make_root(audio_track);
|
||||
|
|
@ -1284,6 +1296,9 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void(Str
|
|||
if (enable == TriState::True)
|
||||
video_track->set_selected(true);
|
||||
|
||||
// AD-HOC(ish): See the comment regarding AudioTrack.kind above with regard to https://dev.w3.org/html5/html-sourcing-inband-tracks/.
|
||||
video_track->set_kind(enable == TriState::True ? "main"_utf16 : "translation"_utf16);
|
||||
|
||||
// 7. Fire an event named addtrack at this VideoTrackList object, using TrackEvent, with the track attribute initialized to the new VideoTrack object.
|
||||
TrackEventInit event_init {};
|
||||
event_init.track = GC::make_root(video_track);
|
||||
|
|
|
|||
57
Libraries/LibWeb/HTML/MediaTrackBase.cpp
Normal file
57
Libraries/LibWeb/HTML/MediaTrackBase.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibUnicode/Locale.h>
|
||||
#include <LibWeb/HTML/HTMLMediaElement.h>
|
||||
#include <LibWeb/HTML/MediaTrackBase.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
MediaTrackBase::MediaTrackBase(JS::Realm& realm, GC::Ref<HTMLMediaElement> media_element, Media::Track const& track)
|
||||
: PlatformObject(realm)
|
||||
, m_media_element(media_element)
|
||||
, m_track_in_playback_manager(track)
|
||||
, m_id(Utf16String::number(track.identifier()))
|
||||
, m_label(track.name())
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-language
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-language
|
||||
// The AudioTrack language and VideoTrack language attributes must return the BCP 47 language tag of the language
|
||||
// of the track, if it has one, or the empty string otherwise. If the user agent is not able to express that language
|
||||
// as a BCP 47 language tag (for example because the language information in the media resource's format is a free-form
|
||||
// string without a defined interpretation), then the method must return the empty string, as if the track had no
|
||||
// language.
|
||||
m_language = [&] {
|
||||
auto locale = Unicode::parse_unicode_locale_id(track.language().to_utf8());
|
||||
if (!locale.has_value())
|
||||
return Utf16String();
|
||||
auto language = locale->to_string();
|
||||
// NOTE: We specifically want to exclude "und" here, as RFC 5646 says:
|
||||
//
|
||||
// The 'und' (Undetermined) primary language subtag identifies linguistic content whose language is not
|
||||
// determined. This subtag SHOULD NOT be used unless a language tag is required and language information is
|
||||
// not available or cannot be determined. Omitting the language tag (where permitted) is preferred. The 'und'
|
||||
// subtag might be useful for protocols that require a language tag to be provided or where a primary language
|
||||
// subtag is required (such as in "und-Latn"). The 'und' subtag MAY also be useful when matching language tags
|
||||
// in certain situations.
|
||||
//
|
||||
// Matroska's TrackEntry->Language element is required, and will use "und" as a placeholder as mentioned above. We
|
||||
// don't want to return anything when that placeholder is found:
|
||||
if (language == "und")
|
||||
return Utf16String();
|
||||
return Utf16String::from_utf8_without_validation(language);
|
||||
}();
|
||||
}
|
||||
|
||||
MediaTrackBase::~MediaTrackBase() = default;
|
||||
|
||||
void MediaTrackBase::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_media_element);
|
||||
}
|
||||
|
||||
}
|
||||
57
Libraries/LibWeb/HTML/MediaTrackBase.h
Normal file
57
Libraries/LibWeb/HTML/MediaTrackBase.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibMedia/Track.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
class MediaTrackBase : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(MediaTrackBase, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
virtual ~MediaTrackBase() override;
|
||||
|
||||
HTMLMediaElement& media_element() const { return *m_media_element; }
|
||||
|
||||
Media::Track const& track_in_playback_manager() const { return m_track_in_playback_manager; }
|
||||
|
||||
Utf16String const& id() const { return m_id; }
|
||||
Utf16String const& kind() const { return m_kind; }
|
||||
void set_kind(Utf16String const& kind) { m_kind = kind; }
|
||||
Utf16String const& label() const { return m_label; }
|
||||
Utf16String const& language() const { return m_language; }
|
||||
|
||||
protected:
|
||||
MediaTrackBase(JS::Realm&, GC::Ref<HTMLMediaElement>, Media::Track const&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
GC::Ref<HTMLMediaElement> m_media_element;
|
||||
|
||||
Media::Track m_track_in_playback_manager;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-id
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-id
|
||||
Utf16String m_id;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-kind
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-kind
|
||||
Utf16String m_kind;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-label
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-label
|
||||
Utf16String m_label;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-audiotrack-language
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-language
|
||||
Utf16String m_language;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
|
@ -20,36 +20,22 @@ namespace Web::HTML {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(VideoTrack);
|
||||
|
||||
static IDAllocator s_video_track_id_allocator;
|
||||
|
||||
VideoTrack::VideoTrack(JS::Realm& realm, GC::Ref<HTMLMediaElement> media_element, Media::Track const& track)
|
||||
: PlatformObject(realm)
|
||||
, m_media_element(media_element)
|
||||
, m_track_in_playback_manager(track)
|
||||
: MediaTrackBase(realm, media_element, track)
|
||||
{
|
||||
}
|
||||
|
||||
VideoTrack::~VideoTrack()
|
||||
{
|
||||
auto id = m_id.to_number<int>();
|
||||
VERIFY(id.has_value());
|
||||
|
||||
s_video_track_id_allocator.deallocate(id.value());
|
||||
}
|
||||
VideoTrack::~VideoTrack() = default;
|
||||
|
||||
void VideoTrack::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(VideoTrack);
|
||||
Base::initialize(realm);
|
||||
|
||||
auto id = s_video_track_id_allocator.allocate();
|
||||
m_id = String::number(id);
|
||||
}
|
||||
|
||||
void VideoTrack::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_media_element);
|
||||
visitor.visit(m_video_track_list);
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +63,7 @@ void VideoTrack::set_selected(bool selected)
|
|||
auto selected_track_was_unselected_without_another_selection = m_selected && !selected;
|
||||
|
||||
if (previously_unselected_track_is_selected || selected_track_was_unselected_without_another_selection) {
|
||||
m_media_element->queue_a_media_element_task([this]() {
|
||||
media_element().queue_a_media_element_task([this]() {
|
||||
m_video_track_list->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::change));
|
||||
});
|
||||
}
|
||||
|
|
@ -86,7 +72,7 @@ void VideoTrack::set_selected(bool selected)
|
|||
m_selected = selected;
|
||||
|
||||
// AD-HOC: Inform the element node that we have (un)selected a video track for layout.
|
||||
m_media_element->set_selected_video_track({}, m_selected ? this : nullptr);
|
||||
media_element().set_selected_video_track({}, m_selected ? this : nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2025, Gregory Bertilson <gregory@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
|
@ -10,11 +11,12 @@
|
|||
#include <AK/Time.h>
|
||||
#include <LibMedia/Track.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/HTML/MediaTrackBase.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
class VideoTrack final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(VideoTrack, Bindings::PlatformObject);
|
||||
class VideoTrack final : public MediaTrackBase {
|
||||
WEB_PLATFORM_OBJECT(VideoTrack, MediaTrackBase);
|
||||
GC_DECLARE_ALLOCATOR(VideoTrack);
|
||||
|
||||
public:
|
||||
|
|
@ -22,41 +24,19 @@ public:
|
|||
|
||||
void set_video_track_list(Badge<VideoTrackList>, GC::Ptr<VideoTrackList> video_track_list) { m_video_track_list = video_track_list; }
|
||||
|
||||
String const& id() const { return m_id; }
|
||||
String const& kind() const { return m_kind; }
|
||||
String const& label() const { return m_label; }
|
||||
String const& language() const { return m_language; }
|
||||
|
||||
bool selected() const { return m_selected; }
|
||||
void set_selected(bool selected);
|
||||
|
||||
Media::Track const& track_in_playback_manager() const { return m_track_in_playback_manager; }
|
||||
|
||||
private:
|
||||
VideoTrack(JS::Realm&, GC::Ref<HTMLMediaElement>, Media::Track const& track);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-id
|
||||
String m_id;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-kind
|
||||
String m_kind;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-label
|
||||
String m_label;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-language
|
||||
String m_language;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-selected
|
||||
bool m_selected { false };
|
||||
|
||||
GC::Ref<HTMLMediaElement> m_media_element;
|
||||
GC::Ptr<VideoTrackList> m_video_track_list;
|
||||
|
||||
Media::Track m_track_in_playback_manager;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user