2026-02-23 23:21:16 -03:00
/*
* Copyright ( c ) 2026 , The Ladybird developers
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibWeb/Bindings/Intrinsics.h>
2026-04-21 19:03:28 -03:00
# include <LibWeb/Bindings/MediaStream.h>
# include <LibWeb/Bindings/MediaStreamTrack.h>
2026-05-01 14:01:23 -03:00
# include <LibWeb/Bindings/MediaStreamTrackEvent.h>
2026-02-23 23:21:16 -03:00
# include <LibWeb/Crypto/Crypto.h>
# include <LibWeb/HTML/EventNames.h>
# include <LibWeb/MediaCapture/MediaStream.h>
# include <LibWeb/MediaCapture/MediaStreamTrackEvent.h>
namespace Web : : MediaCapture {
GC_DEFINE_ALLOCATOR ( MediaStream ) ;
MediaStream : : MediaStream ( JS : : Realm & realm )
: DOM : : EventTarget ( realm )
{
}
GC : : Ref < MediaStream > MediaStream : : create ( JS : : Realm & realm )
{
auto stream = realm . create < MediaStream > ( realm ) ;
// https://w3c.github.io/mediacapture-main/#dom-mediastream-id
stream - > m_id = Crypto : : generate_random_uuid ( ) ;
return stream ;
}
// https://w3c.github.io/mediacapture-main/#mediastream
2026-05-20 15:58:46 -03:00
GC : : Ref < MediaStream > MediaStream : : construct_impl ( JS : : Realm & realm , ReadonlySpan < GC : : Ref < MediaStreamTrack > > const & tracks )
2026-02-23 23:21:16 -03:00
{
// 1. Let stream be a newly constructed MediaStream object.
// 2. Initialize stream.id attribute to a newly generated value.
auto stream = create ( realm ) ;
// 3. If the constructor's argument is present, run the following steps:
// 3.1. Construct a set of tracks tracks based on the type of argument.
// 3.2. For each MediaStreamTrack, track, in tracks:
for ( auto const & track : tracks ) {
// 3.2.1. If track is already in stream's track set, skip track.
// 3.2.2. Otherwise, add track to stream's track set.
stream - > add_track ( * track ) ;
}
// 4. Return stream.
return stream ;
}
void MediaStream : : initialize ( JS : : Realm & realm )
{
WEB_SET_PROTOTYPE_FOR_INTERFACE ( MediaStream ) ;
Base : : initialize ( realm ) ;
}
void MediaStream : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
for ( auto & track : m_tracks )
visitor . visit ( track ) ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-getaudiotracks
Vector < GC : : Ref < MediaStreamTrack > > MediaStream : : get_audio_tracks ( ) const
{
// The getAudioTracks method MUST return a sequence that represents a snapshot of all the MediaStreamTrack objects in this stream's track set whose [[Kind]] is equal to "audio".
Vector < GC : : Ref < MediaStreamTrack > > result ;
for ( auto const & track : m_tracks ) {
if ( track - > is_audio ( ) )
result . append ( track ) ;
}
return result ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-getvideotracks
Vector < GC : : Ref < MediaStreamTrack > > MediaStream : : get_video_tracks ( ) const
{
// The getVideoTracks method MUST return a sequence that represents a snapshot of all the MediaStreamTrack objects in this stream's track set whose [[Kind]] is equal to "video".
Vector < GC : : Ref < MediaStreamTrack > > result ;
for ( auto const & track : m_tracks ) {
if ( track - > is_video ( ) )
result . append ( track ) ;
}
return result ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-gettracks
Vector < GC : : Ref < MediaStreamTrack > > MediaStream : : get_tracks ( ) const
{
// The getTracks method MUST return a sequence that represents a snapshot of all the MediaStreamTrack objects in this stream's track set, regardless of [[Kind]].
return m_tracks ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-gettrackbyid
GC : : Ptr < MediaStreamTrack > MediaStream : : get_track_by_id ( String const & track_id ) const
{
// The getTrackById method MUST return either a MediaStreamTrack object from this stream's track set whose [[Id]] is equal to trackId, or null.
for ( auto const & track : m_tracks ) {
if ( track - > id ( ) = = track_id )
return track ;
}
return nullptr ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-addtrack
void MediaStream : : add_track ( GC : : Ref < MediaStreamTrack > track )
{
// 1. Let track be the methods argument and stream the MediaStream object on which the method was called.
for ( auto const & existing_track : m_tracks ) {
// 2. If track is already in stream's track set, then abort these steps.
if ( existing_track . ptr ( ) = = track . ptr ( ) )
return ;
}
// 3. Add track to stream's track set.
m_tracks . append ( track ) ;
// 4. Fire a track event named addtrack with track at stream.
2026-05-20 15:58:46 -03:00
Bindings : : MediaStreamTrackEventInit event_init { Bindings : : EventInit { } , track } ;
2026-02-23 23:21:16 -03:00
auto event = MediaStreamTrackEvent : : create ( realm ( ) , HTML : : EventNames : : addtrack , event_init ) ;
dispatch_event ( event ) ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-removetrack
void MediaStream : : remove_track ( GC : : Ref < MediaStreamTrack > track )
{
// 1. Let track be the methods argument and stream the MediaStream object on which the method was called.
// 2. If track is not in stream's track set, then abort these steps.
// 3. Remove track from stream's track set.
auto removed = m_tracks . remove_first_matching ( [ & ] ( auto const & existing_track ) {
return existing_track . ptr ( ) = = track . ptr ( ) ;
} ) ;
if ( ! removed )
return ;
// 4. Fire a track event named removetrack with track at stream.
2026-05-20 15:58:46 -03:00
Bindings : : MediaStreamTrackEventInit event_init { Bindings : : EventInit { } , track } ;
2026-02-23 23:21:16 -03:00
auto event = MediaStreamTrackEvent : : create ( realm ( ) , HTML : : EventNames : : removetrack , event_init ) ;
dispatch_event ( event ) ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-clone
GC : : Ref < MediaStream > MediaStream : : clone ( ) const
{
// 1. Let streamClone be a newly constructed MediaStream object.
auto stream_clone = create ( realm ( ) ) ;
// 3. Clone each track in this MediaStream object and add the result to streamClone's track set.
for ( auto const & track : m_tracks )
stream_clone - > add_track ( track - > clone ( ) ) ;
// 4. Return streamClone.
return stream_clone ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-active
bool MediaStream : : active ( ) const
{
// The active attribute MUST return true if this MediaStream is active and false otherwise.
for ( auto const & track : m_tracks ) {
if ( track - > ready_state ( ) ! = Bindings : : MediaStreamTrackState : : Ended )
return true ;
}
return false ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-onaddtrack
WebIDL : : CallbackType * MediaStream : : onaddtrack ( )
{
return event_handler_attribute ( HTML : : EventNames : : addtrack ) ;
}
void MediaStream : : set_onaddtrack ( WebIDL : : CallbackType * event_handler )
{
// The event type of this event handler is addtrack.
set_event_handler_attribute ( HTML : : EventNames : : addtrack , event_handler ) ;
}
// https://w3c.github.io/mediacapture-main/#dom-mediastream-onremovetrack
WebIDL : : CallbackType * MediaStream : : onremovetrack ( )
{
return event_handler_attribute ( HTML : : EventNames : : removetrack ) ;
}
void MediaStream : : set_onremovetrack ( WebIDL : : CallbackType * event_handler )
{
// The event type of this event handler is removetrack.
set_event_handler_attribute ( HTML : : EventNames : : removetrack , event_handler ) ;
}
}