diff options
author | sreeise <reeisesean@gmail.com> | 2019-01-04 07:55:38 -0500 |
---|---|---|
committer | sreeise <reeisesean@gmail.com> | 2019-03-03 09:04:50 -0500 |
commit | cac4aa56f77c46db75c9ca1bfdf23be39cfd4604 (patch) | |
tree | 5b58d44fef2d68fb6031bf7c9a959d9e2b85eeb4 /components/script/dom | |
parent | 4d8d54fc00644204768886569959429dd67998a0 (diff) | |
download | servo-cac4aa56f77c46db75c9ca1bfdf23be39cfd4604.tar.gz servo-cac4aa56f77c46db75c9ca1bfdf23be39cfd4604.zip |
Added AudioTrack, AudioTrackList, VideoTrack, VideoTrackList, and TrackEvent interfaces
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/audiotrack.rs | 97 | ||||
-rw-r--r-- | components/script/dom/audiotracklist.rs | 126 | ||||
-rw-r--r-- | components/script/dom/htmlmediaelement.rs | 121 | ||||
-rw-r--r-- | components/script/dom/mod.rs | 5 | ||||
-rw-r--r-- | components/script/dom/texttracklist.rs | 43 | ||||
-rw-r--r-- | components/script/dom/trackevent.rs | 114 | ||||
-rw-r--r-- | components/script/dom/videotrack.rs | 97 | ||||
-rw-r--r-- | components/script/dom/videotracklist.rs | 164 | ||||
-rw-r--r-- | components/script/dom/webidls/AudioTrack.webidl | 14 | ||||
-rw-r--r-- | components/script/dom/webidls/AudioTrackList.webidl | 16 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLMediaElement.webidl | 4 | ||||
-rw-r--r-- | components/script/dom/webidls/TrackEvent.webidl | 15 | ||||
-rw-r--r-- | components/script/dom/webidls/VideoTrack.webidl | 14 | ||||
-rw-r--r-- | components/script/dom/webidls/VideoTrackList.webidl | 17 |
14 files changed, 839 insertions, 8 deletions
diff --git a/components/script/dom/audiotrack.rs b/components/script/dom/audiotrack.rs new file mode 100644 index 00000000000..9233b1d9c93 --- /dev/null +++ b/components/script/dom/audiotrack.rs @@ -0,0 +1,97 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::AudioTrackBinding::{self, AudioTrackMethods}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct AudioTrack { + reflector_: Reflector, + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + enabled: Cell<bool>, +} + +impl AudioTrack { + pub fn new_inherited( + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + ) -> AudioTrack { + AudioTrack { + reflector_: Reflector::new(), + id: id.into(), + kind: kind.into(), + label: label.into(), + language: language.into(), + enabled: Cell::new(false), + } + } + + pub fn new( + window: &Window, + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + ) -> DomRoot<AudioTrack> { + reflect_dom_object( + Box::new(AudioTrack::new_inherited(id, kind, label, language)), + window, + AudioTrackBinding::Wrap, + ) + } + + pub fn id(&self) -> DOMString { + self.id.clone() + } + + pub fn enabled(&self) -> bool { + self.enabled.get() + } + + pub fn set_enabled(&self, value: bool) { + self.enabled.set(value); + } +} + +impl AudioTrackMethods for AudioTrack { + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-id + fn Id(&self) -> DOMString { + self.id() + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-kind + fn Kind(&self) -> DOMString { + self.kind.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-label + fn Label(&self) -> DOMString { + self.label.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-language + fn Language(&self) -> DOMString { + self.language.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-enabled + fn Enabled(&self) -> bool { + self.enabled() + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotrack-enabled + fn SetEnabled(&self, value: bool) { + self.set_enabled(value); + } +} diff --git a/components/script/dom/audiotracklist.rs b/components/script/dom/audiotracklist.rs new file mode 100644 index 00000000000..353c97c2598 --- /dev/null +++ b/components/script/dom/audiotracklist.rs @@ -0,0 +1,126 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::audiotrack::AudioTrack; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::AudioTrackListBinding::{self, AudioTrackListMethods}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; +use crate::dom::window::Window; +use crate::task_source::TaskSource; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct AudioTrackList { + eventtarget: EventTarget, + tracks: DomRefCell<Vec<Dom<AudioTrack>>>, +} + +impl AudioTrackList { + pub fn new_inherited(tracks: &[&AudioTrack]) -> AudioTrackList { + AudioTrackList { + eventtarget: EventTarget::new_inherited(), + tracks: DomRefCell::new(tracks.iter().map(|track| Dom::from_ref(&**track)).collect()), + } + } + + pub fn new(window: &Window, tracks: &[&AudioTrack]) -> DomRoot<AudioTrackList> { + reflect_dom_object( + Box::new(AudioTrackList::new_inherited(tracks)), + window, + AudioTrackListBinding::Wrap, + ) + } + + pub fn len(&self) -> usize { + self.tracks.borrow().len() + } + + pub fn item(&self, idx: usize) -> Option<DomRoot<AudioTrack>> { + self.tracks + .borrow() + .get(idx) + .map(|track| DomRoot::from_ref(&**track)) + } + + pub fn enabled_index(&self) -> Option<usize> { + self.tracks + .borrow() + .iter() + .position(|track| track.enabled()) + } + + // TODO(#22799) Integrate DOM Audio and Video track selection with media player. + pub fn set_enabled(&self, idx: usize, value: bool) { + let track = match self.item(idx) { + Some(t) => t, + None => return, + }; + + // If the chosen tracks enabled status is the same as the new status, return early. + if track.enabled() == value { + return; + } + // Set the tracks enabled status. + track.set_enabled(value); + + // Queue a task to fire an event named change. + let global = &self.global(); + let this = Trusted::new(self); + let (source, canceller) = global + .as_window() + .task_manager() + .media_element_task_source_with_canceller(); + + let _ = source.queue_with_canceller( + task!(media_track_change: move || { + let this = this.root(); + this.upcast::<EventTarget>().fire_event(atom!("change")); + }), + &canceller, + ); + } + + pub fn add(&self, track: &AudioTrack) { + self.tracks.borrow_mut().push(Dom::from_ref(track)); + } + + pub fn clear(&self) { + self.tracks.borrow_mut().clear(); + } +} + +impl AudioTrackListMethods for AudioTrackList { + // https://html.spec.whatwg.org/multipage/#dom-audiotracklist-length + fn Length(&self) -> u32 { + self.len() as u32 + } + + // https://html.spec.whatwg.org/multipage/#dom-tracklist-item + fn IndexedGetter(&self, idx: u32) -> Option<DomRoot<AudioTrack>> { + self.item(idx as usize) + } + + // https://html.spec.whatwg.org/multipage/#dom-audiotracklist-gettrackbyid + fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<AudioTrack>> { + self.tracks + .borrow() + .iter() + .find(|track| track.id() == id) + .map(|track| DomRoot::from_ref(&**track)) + } + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onchange + event_handler!(change, GetOnchange, SetOnchange); + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onaddtrack + event_handler!(addtrack, GetOnaddtrack, SetOnaddtrack); + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onremovetrack + event_handler!(removetrack, GetOnremovetrack, SetOnremovetrack); +} diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index c64bd26e57c..703ce9438a7 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -4,6 +4,8 @@ use crate::document_loader::{LoadBlocker, LoadType}; use crate::dom::attr::Attr; +use crate::dom::audiotrack::AudioTrack; +use crate::dom::audiotracklist::AudioTrackList; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::CanPlayTypeResult; @@ -15,6 +17,7 @@ use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethod use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode}; use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId}; use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId}; +use crate::dom::bindings::codegen::UnionTypes::VideoTrackOrAudioTrackOrTextTrack; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; @@ -25,6 +28,7 @@ use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::blob::Blob; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; @@ -37,6 +41,9 @@ use crate::dom::promise::Promise; use crate::dom::texttrack::TextTrack; use crate::dom::texttracklist::TextTrackList; use crate::dom::timeranges::{TimeRanges, TimeRangesContainer}; +use crate::dom::trackevent::TrackEvent; +use crate::dom::videotrack::VideoTrack; +use crate::dom::videotracklist::VideoTrackList; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::FetchCanceller; use crate::microtask::{Microtask, MicrotaskRunnable}; @@ -206,6 +213,10 @@ pub struct HTMLMediaElement { /// https://html.spec.whatwg.org/multipage/#dom-media-played #[ignore_malloc_size_of = "Rc"] played: DomRefCell<TimeRangesContainer>, + // https://html.spec.whatwg.org/multipage/#dom-media-audiotracks + audio_tracks_list: MutNullableDom<AudioTrackList>, + // https://html.spec.whatwg.org/multipage/#dom-media-videotracks + video_tracks_list: MutNullableDom<VideoTrackList>, /// https://html.spec.whatwg.org/multipage/#dom-media-texttracks text_tracks_list: MutNullableDom<TextTrackList>, /// Time of last timeupdate notification. @@ -268,6 +279,8 @@ impl HTMLMediaElement { seeking: Cell::new(false), resource_url: DomRefCell::new(None), played: DomRefCell::new(TimeRangesContainer::new()), + audio_tracks_list: Default::default(), + video_tracks_list: Default::default(), text_tracks_list: Default::default(), next_timeupdate_event: Cell::new(time::get_time() + Duration::milliseconds(250)), current_fetch_context: DomRefCell::new(None), @@ -803,7 +816,8 @@ impl HTMLMediaElement { ))); // Step 2. - // FIXME(nox): Forget the media-resource-specific tracks. + this.AudioTracks().clear(); + this.VideoTracks().clear(); // Step 3. this.network_state.set(NetworkState::NoSource); @@ -904,7 +918,8 @@ impl HTMLMediaElement { // FIXME(nox): Detach MediaSource media provider object. // Step 6.4. - // FIXME(nox): Forget the media-resource-specific tracks. + self.AudioTracks().clear(); + self.VideoTracks().clear(); // Step 6.5. if self.ready_state.get() != ReadyState::HaveNothing { @@ -1227,6 +1242,92 @@ impl HTMLMediaElement { }, PlayerEvent::MetadataUpdated(ref metadata) => { // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list + // => If the media resource is found to have an audio track + if !metadata.audio_tracks.is_empty() { + for (i, _track) in metadata.audio_tracks.iter().enumerate() { + // Step 1. + let kind = match i { + 0 => DOMString::from("main"), + _ => DOMString::new(), + }; + let window = window_from_node(self); + let audio_track = AudioTrack::new( + &window, + DOMString::new(), + kind, + DOMString::new(), + DOMString::new(), + ); + + // Steps 2. & 3. + self.AudioTracks().add(&audio_track); + + // Step 4 + // https://www.w3.org/TR/media-frags/#media-fragment-syntax + // https://github.com/servo/servo/issues/22366 + + // Step 5. & 6, + if self.AudioTracks().enabled_index().is_none() { + self.AudioTracks() + .set_enabled(self.AudioTracks().len() - 1, true); + } + + // Steps 7. + let event = TrackEvent::new( + &self.global(), + atom!("addtrack"), + false, + false, + &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)), + ); + + event.upcast::<Event>().fire(self.upcast::<EventTarget>()); + } + } + + // => If the media resource is found to have a video track + if !metadata.video_tracks.is_empty() { + for (i, _track) in metadata.video_tracks.iter().enumerate() { + // Step 1. + let kind = match i { + 0 => DOMString::from("main"), + _ => DOMString::new(), + }; + let window = window_from_node(self); + let video_track = VideoTrack::new( + &window, + DOMString::new(), + kind, + DOMString::new(), + DOMString::new(), + ); + + // Steps 2. & 3. + self.VideoTracks().add(&video_track); + + // Step 4. + // https://www.w3.org/TR/media-frags/#media-fragment-syntax + // https://github.com/servo/servo/issues/22366 + + // Step 5. & 6. + if self.VideoTracks().selected_index().is_none() { + self.VideoTracks() + .set_selected(self.VideoTracks().len() - 1, true); + } + + // Steps 7. + let event = TrackEvent::new( + &self.global(), + atom!("addtrack"), + false, + false, + &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)), + ); + + event.upcast::<Event>().fire(self.upcast::<EventTarget>()); + } + } + // => "Once enough of the media data has been fetched to determine the duration..." // Step 1. // servo-media owns the media timeline. @@ -1287,7 +1388,7 @@ impl HTMLMediaElement { // https://www.w3.org/TR/media-frags/#media-fragment-syntax // https://github.com/servo/media/issues/156 - // XXX Steps 12 and 13 require audio and video tracks support. + // Step 12 & 13 are already handled by the earlier media track processing. }, PlayerEvent::NeedData => { // The player needs more data. @@ -1702,6 +1803,20 @@ impl HTMLMediaElementMethods for HTMLMediaElement { TimeRanges::new(self.global().as_window(), buffered) } + // https://html.spec.whatwg.org/multipage/#dom-media-audiotracks + fn AudioTracks(&self) -> DomRoot<AudioTrackList> { + let window = window_from_node(self); + self.audio_tracks_list + .or_init(|| AudioTrackList::new(&window, &[])) + } + + // https://html.spec.whatwg.org/multipage/#dom-media-videotracks + fn VideoTracks(&self) -> DomRoot<VideoTrackList> { + let window = window_from_node(self); + self.video_tracks_list + .or_init(|| VideoTrackList::new(&window, &[])) + } + // https://html.spec.whatwg.org/multipage/#dom-media-texttracks fn TextTracks(&self) -> DomRoot<TextTrackList> { let window = window_from_node(self); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index a965b6893bc..b8f288a7e4e 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -225,6 +225,8 @@ pub mod audiolistener; pub mod audionode; pub mod audioparam; pub mod audioscheduledsourcenode; +pub mod audiotrack; +pub mod audiotracklist; pub mod baseaudiocontext; pub mod beforeunloadevent; pub mod bindings; @@ -478,6 +480,7 @@ pub mod timeranges; pub mod touch; pub mod touchevent; pub mod touchlist; +pub mod trackevent; pub mod transitionevent; pub mod treewalker; pub mod uievent; @@ -488,6 +491,8 @@ pub mod userscripts; pub mod validation; pub mod validitystate; pub mod values; +pub mod videotrack; +pub mod videotracklist; pub mod virtualmethods; pub mod vrdisplay; pub mod vrdisplaycapabilities; diff --git a/components/script/dom/texttracklist.rs b/components/script/dom/texttracklist.rs index 220d012cdfd..df5e2d630ac 100644 --- a/components/script/dom/texttracklist.rs +++ b/components/script/dom/texttracklist.rs @@ -4,13 +4,18 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::TextTrackListBinding::{self, TextTrackListMethods}; +use crate::dom::bindings::codegen::UnionTypes::VideoTrackOrAudioTrackOrTextTrack; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; +use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::texttrack::TextTrack; +use crate::dom::trackevent::TrackEvent; use crate::dom::window::Window; +use crate::task_source::TaskSource; use dom_struct::dom_struct; #[dom_struct] @@ -56,8 +61,40 @@ impl TextTrackList { // Only add a track if it does not exist in the list if self.find(track).is_none() { self.dom_tracks.borrow_mut().push(Dom::from_ref(track)); - }; - self.upcast::<EventTarget>().fire_event(atom!("addtrack")); + + let this = Trusted::new(self); + let (source, canceller) = &self + .global() + .as_window() + .task_manager() + .media_element_task_source_with_canceller(); + + let idx = match self.find(&track) { + Some(t) => t, + None => return, + }; + + let _ = source.queue_with_canceller( + task!(track_event_queue: move || { + let this = this.root(); + + if let Some(track) = this.item(idx) { + let event = TrackEvent::new( + &this.global(), + atom!("addtrack"), + false, + false, + &Some(VideoTrackOrAudioTrackOrTextTrack::TextTrack( + DomRoot::from_ref(&track) + )), + ); + + event.upcast::<Event>().fire(this.upcast::<EventTarget>()); + } + }), + &canceller, + ); + } } // FIXME(#22314, dlrobertson) allow TextTracks to be diff --git a/components/script/dom/trackevent.rs b/components/script/dom/trackevent.rs new file mode 100644 index 00000000000..684a0fcde52 --- /dev/null +++ b/components/script/dom/trackevent.rs @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::audiotrack::AudioTrack; +use crate::dom::bindings::codegen::Bindings::EventBinding::EventBinding::EventMethods; +use crate::dom::bindings::codegen::Bindings::TrackEventBinding; +use crate::dom::bindings::codegen::Bindings::TrackEventBinding::TrackEventMethods; +use crate::dom::bindings::codegen::UnionTypes::VideoTrackOrAudioTrackOrTextTrack; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::event::Event; +use crate::dom::globalscope::GlobalScope; +use crate::dom::texttrack::TextTrack; +use crate::dom::videotrack::VideoTrack; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +#[must_root] +#[derive(JSTraceable, MallocSizeOf)] +enum MediaTrack { + Video(Dom<VideoTrack>), + Audio(Dom<AudioTrack>), + Text(Dom<TextTrack>), +} + +#[dom_struct] +pub struct TrackEvent { + event: Event, + track: Option<MediaTrack>, +} + +impl TrackEvent { + #[allow(unrooted_must_root)] + fn new_inherited(track: &Option<VideoTrackOrAudioTrackOrTextTrack>) -> TrackEvent { + let media_track = match track { + Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(VideoTrack)) => { + Some(MediaTrack::Video(Dom::from_ref(VideoTrack))) + }, + Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(AudioTrack)) => { + Some(MediaTrack::Audio(Dom::from_ref(AudioTrack))) + }, + Some(VideoTrackOrAudioTrackOrTextTrack::TextTrack(TextTrack)) => { + Some(MediaTrack::Text(Dom::from_ref(TextTrack))) + }, + None => None, + }; + + TrackEvent { + event: Event::new_inherited(), + track: media_track, + } + } + + pub fn new( + global: &GlobalScope, + type_: Atom, + bubbles: bool, + cancelable: bool, + track: &Option<VideoTrackOrAudioTrackOrTextTrack>, + ) -> DomRoot<TrackEvent> { + let te = reflect_dom_object( + Box::new(TrackEvent::new_inherited(&track)), + global, + TrackEventBinding::Wrap, + ); + { + let event = te.upcast::<Event>(); + event.init_event(type_, bubbles, cancelable); + } + te + } + + pub fn Constructor( + window: &Window, + type_: DOMString, + init: &TrackEventBinding::TrackEventInit, + ) -> Fallible<DomRoot<TrackEvent>> { + Ok(TrackEvent::new( + &window.global(), + Atom::from(type_), + init.parent.bubbles, + init.parent.cancelable, + &init.track, + )) + } +} + +impl TrackEventMethods for TrackEvent { + // https://html.spec.whatwg.org/multipage/#dom-trackevent-track + fn GetTrack(&self) -> Option<VideoTrackOrAudioTrackOrTextTrack> { + match &self.track { + Some(MediaTrack::Video(VideoTrack)) => Some( + VideoTrackOrAudioTrackOrTextTrack::VideoTrack(DomRoot::from_ref(VideoTrack)), + ), + Some(MediaTrack::Audio(AudioTrack)) => Some( + VideoTrackOrAudioTrackOrTextTrack::AudioTrack(DomRoot::from_ref(AudioTrack)), + ), + Some(MediaTrack::Text(TextTrack)) => Some( + VideoTrackOrAudioTrackOrTextTrack::TextTrack(DomRoot::from_ref(TextTrack)), + ), + None => None, + } + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/videotrack.rs b/components/script/dom/videotrack.rs new file mode 100644 index 00000000000..fd337db5e8a --- /dev/null +++ b/components/script/dom/videotrack.rs @@ -0,0 +1,97 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::VideoTrackBinding::{self, VideoTrackMethods}; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct VideoTrack { + reflector_: Reflector, + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + selected: Cell<bool>, +} + +impl VideoTrack { + pub fn new_inherited( + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + ) -> VideoTrack { + VideoTrack { + reflector_: Reflector::new(), + id: id.into(), + kind: kind.into(), + label: label.into(), + language: language.into(), + selected: Cell::new(false), + } + } + + pub fn new( + window: &Window, + id: DOMString, + kind: DOMString, + label: DOMString, + language: DOMString, + ) -> DomRoot<VideoTrack> { + reflect_dom_object( + Box::new(VideoTrack::new_inherited(id, kind, label, language)), + window, + VideoTrackBinding::Wrap, + ) + } + + pub fn id(&self) -> DOMString { + self.id.clone() + } + + pub fn selected(&self) -> bool { + self.selected.get().clone() + } + + pub fn set_selected(&self, value: bool) { + self.selected.set(value); + } +} + +impl VideoTrackMethods for VideoTrack { + // https://html.spec.whatwg.org/multipage/#dom-videotrack-id + fn Id(&self) -> DOMString { + self.id() + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-kind + fn Kind(&self) -> DOMString { + self.kind.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-label + fn Label(&self) -> DOMString { + self.label.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-language + fn Language(&self) -> DOMString { + self.language.clone() + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-selected + fn Selected(&self) -> bool { + self.selected() + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-selected + fn SetSelected(&self, value: bool) { + self.set_selected(value); + } +} diff --git a/components/script/dom/videotracklist.rs b/components/script/dom/videotracklist.rs new file mode 100644 index 00000000000..e495efcccd2 --- /dev/null +++ b/components/script/dom/videotracklist.rs @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::VideoTrackListBinding::{self, VideoTrackListMethods}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::Dom; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; +use crate::dom::videotrack::VideoTrack; +use crate::dom::window::Window; +use crate::task_source::TaskSource; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct VideoTrackList { + eventtarget: EventTarget, + tracks: DomRefCell<Vec<Dom<VideoTrack>>>, +} + +impl VideoTrackList { + pub fn new_inherited(tracks: &[&VideoTrack]) -> VideoTrackList { + VideoTrackList { + eventtarget: EventTarget::new_inherited(), + tracks: DomRefCell::new(tracks.iter().map(|track| Dom::from_ref(&**track)).collect()), + } + } + + pub fn new(window: &Window, tracks: &[&VideoTrack]) -> DomRoot<VideoTrackList> { + reflect_dom_object( + Box::new(VideoTrackList::new_inherited(tracks)), + window, + VideoTrackListBinding::Wrap, + ) + } + + pub fn len(&self) -> usize { + self.tracks.borrow().len() + } + + pub fn item(&self, idx: usize) -> Option<DomRoot<VideoTrack>> { + self.tracks + .borrow() + .get(idx) + .map(|track| DomRoot::from_ref(&**track)) + } + + pub fn selected_index(&self) -> Option<usize> { + self.tracks + .borrow() + .iter() + .position(|track| track.selected()) + } + + // TODO(#22799) Integrate DOM Audio and Video track selection with media player. + pub fn set_selected(&self, idx: usize, value: bool) { + let track = match self.item(idx) { + Some(t) => t, + None => return, + }; + + // If the chosen tracks selected status is the same as the new status, return early. + if track.selected() == value { + return; + } + + let global = &self.global(); + let this = Trusted::new(self); + let (source, canceller) = global + .as_window() + .task_manager() + .media_element_task_source_with_canceller(); + + if let Some(current) = self.selected_index() { + if current != idx { + self.tracks.borrow()[current].set_selected(false); + track.set_selected(true); + + let _ = source.queue_with_canceller( + task!(media_track_change: move || { + let this = this.root(); + this.upcast::<EventTarget>().fire_event(atom!("change")); + }), + &canceller, + ); + } else { + self.tracks.borrow()[current].set_selected(false); + + let _ = source.queue_with_canceller( + task!(media_track_change: move || { + let this = this.root(); + this.upcast::<EventTarget>().fire_event(atom!("change")); + }), + &canceller, + ); + } + } else { + track.set_selected(true); + + let _ = source.queue_with_canceller( + task!(media_track_change: move || { + let this = this.root(); + this.upcast::<EventTarget>().fire_event(atom!("change")); + }), + &canceller, + ); + } + } + + pub fn add(&self, track: &VideoTrack) { + self.tracks.borrow_mut().push(Dom::from_ref(track)); + if track.selected() { + if let Some(idx) = self.selected_index() { + self.set_selected(idx, false); + } + } + } + + pub fn clear(&self) { + self.tracks.borrow_mut().clear(); + } +} + +impl VideoTrackListMethods for VideoTrackList { + // https://html.spec.whatwg.org/multipage/#dom-videotracklist-length + fn Length(&self) -> u32 { + self.len() as u32 + } + + // https://html.spec.whatwg.org/multipage/#dom-tracklist-item + fn IndexedGetter(&self, idx: u32) -> Option<DomRoot<VideoTrack>> { + self.item(idx as usize) + } + + // https://html.spec.whatwg.org/multipage/#dom-videotracklist-gettrackbyid + fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<VideoTrack>> { + self.tracks + .borrow() + .iter() + .find(|track| track.id() == id) + .map(|track| DomRoot::from_ref(&**track)) + } + + // https://html.spec.whatwg.org/multipage/#dom-videotrack-selected + fn SelectedIndex(&self) -> i32 { + if let Some(idx) = self.selected_index() { + return idx as i32; + } + return -1; + } + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onchange + event_handler!(change, GetOnchange, SetOnchange); + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onaddtrack + event_handler!(addtrack, GetOnaddtrack, SetOnaddtrack); + + // https://html.spec.whatwg.org/multipage/#handler-tracklist-onremovetrack + event_handler!(removetrack, GetOnremovetrack, SetOnremovetrack); +} diff --git a/components/script/dom/webidls/AudioTrack.webidl b/components/script/dom/webidls/AudioTrack.webidl new file mode 100644 index 00000000000..2fa2ec9a5fa --- /dev/null +++ b/components/script/dom/webidls/AudioTrack.webidl @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#audiotrack + +[Exposed=Window] +interface AudioTrack { + readonly attribute DOMString id; + readonly attribute DOMString kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + attribute boolean enabled; +}; diff --git a/components/script/dom/webidls/AudioTrackList.webidl b/components/script/dom/webidls/AudioTrackList.webidl new file mode 100644 index 00000000000..4428776972c --- /dev/null +++ b/components/script/dom/webidls/AudioTrackList.webidl @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#audiotracklist + +[Exposed=Window] +interface AudioTrackList : EventTarget { + readonly attribute unsigned long length; + getter AudioTrack (unsigned long index); + AudioTrack? getTrackById(DOMString id); + + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; +}; diff --git a/components/script/dom/webidls/HTMLMediaElement.webidl b/components/script/dom/webidls/HTMLMediaElement.webidl index c932516773c..ba5669315d8 100644 --- a/components/script/dom/webidls/HTMLMediaElement.webidl +++ b/components/script/dom/webidls/HTMLMediaElement.webidl @@ -59,8 +59,8 @@ interface HTMLMediaElement : HTMLElement { [CEReactions] attribute boolean defaultMuted; // tracks - // readonly attribute AudioTrackList audioTracks; - // readonly attribute VideoTrackList videoTracks; + readonly attribute AudioTrackList audioTracks; + readonly attribute VideoTrackList videoTracks; readonly attribute TextTrackList textTracks; TextTrack addTextTrack(TextTrackKind kind, optional DOMString label = "", optional DOMString language = ""); }; diff --git a/components/script/dom/webidls/TrackEvent.webidl b/components/script/dom/webidls/TrackEvent.webidl new file mode 100644 index 00000000000..214583e3245 --- /dev/null +++ b/components/script/dom/webidls/TrackEvent.webidl @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#the-trackevent-interface + +[Exposed=Window, + Constructor(DOMString type, optional TrackEventInit eventInitDict)] +interface TrackEvent : Event { + readonly attribute (VideoTrack or AudioTrack or TextTrack)? track; +}; + +dictionary TrackEventInit : EventInit { + (VideoTrack or AudioTrack or TextTrack)? track = null; +}; diff --git a/components/script/dom/webidls/VideoTrack.webidl b/components/script/dom/webidls/VideoTrack.webidl new file mode 100644 index 00000000000..90d6c487eaa --- /dev/null +++ b/components/script/dom/webidls/VideoTrack.webidl @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#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/components/script/dom/webidls/VideoTrackList.webidl b/components/script/dom/webidls/VideoTrackList.webidl new file mode 100644 index 00000000000..9c880f0d2b7 --- /dev/null +++ b/components/script/dom/webidls/VideoTrackList.webidl @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#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; +}; |