diff options
Diffstat (limited to 'Userland/Libraries/LibWeb/HTML')
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrack.cpp | 88 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrack.h | 65 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrack.idl | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrackList.cpp | 130 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrackList.h | 50 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/VideoTrackList.idl | 16 |
6 files changed, 358 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrack.cpp b/Userland/Libraries/LibWeb/HTML/VideoTrack.cpp new file mode 100644 index 0000000000..14983a2284 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrack.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/IDAllocator.h> +#include <LibJS/Runtime/Realm.h> +#include <LibJS/Runtime/VM.h> +#include <LibWeb/Bindings/Intrinsics.h> +#include <LibWeb/Bindings/VideoTrackPrototype.h> +#include <LibWeb/DOM/Event.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/HTMLMediaElement.h> +#include <LibWeb/HTML/VideoTrack.h> +#include <LibWeb/HTML/VideoTrackList.h> + +namespace Web::HTML { + +static IDAllocator s_video_track_id_allocator; + +VideoTrack::VideoTrack(JS::Realm& realm, JS::NonnullGCPtr<HTMLMediaElement> media_element, NonnullOwnPtr<Video::Matroska::MatroskaDemuxer> demuxer, Video::Track track) + : PlatformObject(realm) + , m_media_element(media_element) + , m_demuxer(move(demuxer)) + , m_track(track) +{ +} + +VideoTrack::~VideoTrack() +{ + auto id = m_id.to_number<int>(); + VERIFY(id.has_value()); + + s_video_track_id_allocator.deallocate(id.value()); +} + +JS::ThrowCompletionOr<void> VideoTrack::initialize(JS::Realm& realm) +{ + MUST_OR_THROW_OOM(Base::initialize(realm)); + set_prototype(&Bindings::ensure_web_prototype<Bindings::VideoTrackPrototype>(realm, "VideoTrack")); + + auto id = s_video_track_id_allocator.allocate(); + m_id = TRY_OR_THROW_OOM(realm.vm(), String::number(id)); + + return {}; +} + +void VideoTrack::visit_edges(Cell::Visitor& visitor) +{ + visitor.visit(m_media_element); + visitor.visit(m_video_track_list); +} + +// https://html.spec.whatwg.org/multipage/media.html#dom-videotrack-selected +void VideoTrack::set_selected(bool selected) +{ + // On setting, it must select the track if the new value is true, and unselect it otherwise. + if (m_selected == selected) + return; + + // If the track is in a VideoTrackList, then all the other VideoTrack objects in that list must be unselected. (If the track is + // no longer in a VideoTrackList object, then the track being selected or unselected has no effect beyond changing the value of + // the attribute on the VideoTrack object.) + if (m_video_track_list) { + for (auto video_track : m_video_track_list->video_tracks({})) { + if (video_track.ptr() != this) + video_track->m_selected = false; + } + + // Whenever a track in a VideoTrackList that was previously not selected is selected, and whenever the selected track in a + // VideoTrackList is unselected without a new track being selected in its stead, the user agent must queue a media element + // task given the media element to fire an event named change at the VideoTrackList object. This task must be queued before + // the task that fires the resize event, if any. + auto previously_unselected_track_is_selected = !m_selected && 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]() { + m_video_track_list->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::change.to_deprecated_fly_string()).release_value_but_fixme_should_propagate_errors()); + }); + } + } + + m_selected = selected; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrack.h b/Userland/Libraries/LibWeb/HTML/VideoTrack.h new file mode 100644 index 0000000000..4f2264dba7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrack.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/String.h> +#include <AK/Time.h> +#include <LibVideo/Containers/Matroska/MatroskaDemuxer.h> +#include <LibVideo/Track.h> +#include <LibWeb/Bindings/PlatformObject.h> + +namespace Web::HTML { + +class VideoTrack final : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(VideoTrack, Bindings::PlatformObject); + +public: + virtual ~VideoTrack() override; + + void set_video_track_list(Badge<VideoTrackList>, JS::GCPtr<VideoTrackList> video_track_list) { m_video_track_list = video_track_list; } + + Time duration() const { return m_track.video_data().duration; } + u64 pixel_width() const { return m_track.video_data().pixel_width; } + u64 pixel_height() const { return m_track.video_data().pixel_height; } + + 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); + +private: + explicit VideoTrack(JS::Realm&, JS::NonnullGCPtr<HTMLMediaElement>, NonnullOwnPtr<Video::Matroska::MatroskaDemuxer>, Video::Track); + + virtual JS::ThrowCompletionOr<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 }; + + JS::NonnullGCPtr<HTMLMediaElement> m_media_element; + JS::GCPtr<VideoTrackList> m_video_track_list; + + NonnullOwnPtr<Video::Matroska::MatroskaDemuxer> m_demuxer; + Video::Track m_track; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrack.idl b/Userland/Libraries/LibWeb/HTML/VideoTrack.idl new file mode 100644 index 0000000000..00036a865b --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrack.idl @@ -0,0 +1,9 @@ +// https://html.spec.whatwg.org/multipage/media.html#videotrack +[Exposed=Window] +interface VideoTrack { + readonly attribute DOMString id; + readonly attribute DOMString kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + attribute boolean selected; +}; diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrackList.cpp b/Userland/Libraries/LibWeb/HTML/VideoTrackList.cpp new file mode 100644 index 0000000000..f590c9fedc --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrackList.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/Realm.h> +#include <LibJS/Runtime/VM.h> +#include <LibWeb/Bindings/Intrinsics.h> +#include <LibWeb/Bindings/VideoTrackListPrototype.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/VideoTrackList.h> + +namespace Web::HTML { + +VideoTrackList::VideoTrackList(JS::Realm& realm) + : DOM::EventTarget(realm) + , m_video_tracks(realm.heap()) +{ +} + +JS::ThrowCompletionOr<void> VideoTrackList::initialize(JS::Realm& realm) +{ + MUST_OR_THROW_OOM(Base::initialize(realm)); + set_prototype(&Bindings::ensure_web_prototype<Bindings::VideoTrackListPrototype>(realm, "VideoTrackList")); + + return {}; +} + +// https://html.spec.whatwg.org/multipage/media.html#dom-tracklist-item +JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> VideoTrackList::internal_get_own_property(JS::PropertyKey const& property_name) const +{ + // To determine the value of an indexed property for a given index index in an AudioTrackList or VideoTrackList + // object list, the user agent must return the AudioTrack or VideoTrack object that represents the indexth track + // in list. + if (property_name.is_number()) { + if (auto index = property_name.as_number(); index < m_video_tracks.size()) { + JS::PropertyDescriptor descriptor; + descriptor.value = m_video_tracks.at(index); + + return descriptor; + } + } + + return Base::internal_get_own_property(property_name); +} + +ErrorOr<void> VideoTrackList::add_track(Badge<HTMLMediaElement>, JS::NonnullGCPtr<VideoTrack> video_track) +{ + TRY(m_video_tracks.try_append(video_track)); + video_track->set_video_track_list({}, this); + + return {}; +} + +void VideoTrackList::remove_all_tracks(Badge<HTMLMediaElement>) +{ + m_video_tracks.clear(); +} + +// https://html.spec.whatwg.org/multipage/media.html#dom-videotracklist-gettrackbyid +JS::GCPtr<VideoTrack> VideoTrackList::get_track_by_id(StringView id) const +{ + // The AudioTrackList getTrackById(id) and VideoTrackList getTrackById(id) methods must return the first AudioTrack + // or VideoTrack object (respectively) in the AudioTrackList or VideoTrackList object (respectively) whose identifier + // is equal to the value of the id argument (in the natural order of the list, as defined above). + auto it = m_video_tracks.find_if([&](auto const& video_track) { + return video_track->id() == id; + }); + + // When no tracks match the given argument, the methods must return null. + if (it == m_video_tracks.end()) + return nullptr; + + return *it; +} + +// https://html.spec.whatwg.org/multipage/media.html#dom-videotracklist-selectedindex +i32 VideoTrackList::selected_index() const +{ + // The VideoTrackList selectedIndex attribute must return the index of the currently selected track, if any. + auto it = m_video_tracks.find_if([&](auto const& video_track) { + return video_track->selected(); + }); + + // If the VideoTrackList object does not currently represent any tracks, or if none of the tracks are selected, + // it must instead return −1. + if (it == m_video_tracks.end()) + return -1; + + return static_cast<i32>(it.index()); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onchange +void VideoTrackList::set_onchange(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::change, event_handler); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onchange +WebIDL::CallbackType* VideoTrackList::onchange() +{ + return event_handler_attribute(HTML::EventNames::change); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onaddtrack +void VideoTrackList::set_onaddtrack(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::addtrack, event_handler); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onaddtrack +WebIDL::CallbackType* VideoTrackList::onaddtrack() +{ + return event_handler_attribute(HTML::EventNames::addtrack); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onremovetrack +void VideoTrackList::set_onremovetrack(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::removetrack, event_handler); +} + +// https://html.spec.whatwg.org/multipage/media.html#handler-tracklist-onremovetrack +WebIDL::CallbackType* VideoTrackList::onremovetrack() +{ + return event_handler_attribute(HTML::EventNames::removetrack); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrackList.h b/Userland/Libraries/LibWeb/HTML/VideoTrackList.h new file mode 100644 index 0000000000..d0f9732d12 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrackList.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/Badge.h> +#include <AK/String.h> +#include <LibJS/Heap/MarkedVector.h> +#include <LibWeb/DOM/EventTarget.h> +#include <LibWeb/HTML/VideoTrack.h> + +namespace Web::HTML { + +class VideoTrackList final : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(VideoTrackList, DOM::EventTarget); + +public: + ErrorOr<void> add_track(Badge<HTMLMediaElement>, JS::NonnullGCPtr<VideoTrack>); + void remove_all_tracks(Badge<HTMLMediaElement>); + + Span<JS::NonnullGCPtr<VideoTrack>> video_tracks(Badge<VideoTrack>) { return m_video_tracks; } + + // https://html.spec.whatwg.org/multipage/media.html#dom-videotracklist-length + size_t length() const { return m_video_tracks.size(); } + + JS::GCPtr<VideoTrack> get_track_by_id(StringView id) const; + i32 selected_index() const; + + void set_onchange(WebIDL::CallbackType*); + WebIDL::CallbackType* onchange(); + + void set_onaddtrack(WebIDL::CallbackType*); + WebIDL::CallbackType* onaddtrack(); + + void set_onremovetrack(WebIDL::CallbackType*); + WebIDL::CallbackType* onremovetrack(); + +private: + explicit VideoTrackList(JS::Realm&); + + virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override; + virtual JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> internal_get_own_property(JS::PropertyKey const& property_name) const override; + + JS::MarkedVector<JS::NonnullGCPtr<VideoTrack>> m_video_tracks; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/VideoTrackList.idl b/Userland/Libraries/LibWeb/HTML/VideoTrackList.idl new file mode 100644 index 0000000000..3c54de0c17 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VideoTrackList.idl @@ -0,0 +1,16 @@ +#import <DOM/EventHandler.idl> +#import <DOM/EventTarget.idl> +#import <HTML/VideoTrack.idl> + +// https://html.spec.whatwg.org/multipage/media.html#videotracklist +[Exposed=Window] +interface VideoTrackList : EventTarget { + readonly attribute unsigned long length; + getter VideoTrack (unsigned long index); + VideoTrack? getTrackById(DOMString id); + readonly attribute long selectedIndex; + + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; +}; |