Skip to main content

script/dom/audio/
audioscheduledsourcenode.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use servo_media::audio::node::{
10    AudioNodeInit, AudioNodeMessage, AudioScheduledSourceNodeMessage, OnEndedCallback,
11};
12
13use crate::dom::audio::audionode::{AudioNode, UnwrappedAudioNodeOptions};
14use crate::dom::audio::baseaudiocontext::BaseAudioContext;
15use crate::dom::bindings::codegen::Bindings::AudioScheduledSourceNodeBinding::AudioScheduledSourceNodeMethods;
16use crate::dom::bindings::error::{Error, Fallible};
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::num::Finite;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::DomGlobal;
21
22#[dom_struct]
23pub(crate) struct AudioScheduledSourceNode {
24    node: AudioNode,
25    has_start: Cell<bool>,
26    has_stop: Cell<bool>,
27}
28
29impl AudioScheduledSourceNode {
30    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
31    pub(crate) fn new_inherited(
32        cx: &mut JSContext,
33        node_type: AudioNodeInit,
34        context: &BaseAudioContext,
35        options: UnwrappedAudioNodeOptions,
36        number_of_inputs: u32,
37        number_of_outputs: u32,
38    ) -> Fallible<AudioScheduledSourceNode> {
39        Ok(AudioScheduledSourceNode {
40            node: AudioNode::new_inherited(
41                cx,
42                node_type,
43                context,
44                options,
45                number_of_inputs,
46                number_of_outputs,
47            )?,
48            has_start: Cell::new(false),
49            has_stop: Cell::new(false),
50        })
51    }
52
53    pub(crate) fn node(&self) -> &AudioNode {
54        &self.node
55    }
56
57    pub(crate) fn has_start(&self) -> bool {
58        self.has_start.get()
59    }
60}
61
62impl AudioScheduledSourceNodeMethods<crate::DomTypeHolder> for AudioScheduledSourceNode {
63    // https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-onended
64    event_handler!(ended, GetOnended, SetOnended);
65
66    /// <https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-start>
67    fn Start(&self, when: Finite<f64>) -> Fallible<()> {
68        if *when < 0. {
69            return Err(Error::Range(c"'when' must be a positive value".to_owned()));
70        }
71
72        if self.has_start.get() || self.has_stop.get() {
73            return Err(Error::InvalidState(None));
74        }
75
76        let this = Trusted::new(self);
77        let task_source = self
78            .global()
79            .task_manager()
80            .dom_manipulation_task_source()
81            .to_sendable();
82        let callback = OnEndedCallback::new(move || {
83            task_source.queue(task!(ended: move || {
84                let this = this.root();
85                this.global().task_manager().dom_manipulation_task_source().queue_simple_event(
86                    this.upcast(),
87                    atom!("ended"),
88                    );
89            }));
90        });
91
92        self.node()
93            .message(AudioNodeMessage::AudioScheduledSourceNode(
94                AudioScheduledSourceNodeMessage::RegisterOnEndedCallback(callback),
95            ));
96
97        self.has_start.set(true);
98        self.node
99            .message(AudioNodeMessage::AudioScheduledSourceNode(
100                AudioScheduledSourceNodeMessage::Start(*when),
101            ));
102        Ok(())
103    }
104
105    /// <https://webaudio.github.io/web-audio-api/#dom-audioscheduledsourcenode-stop>
106    fn Stop(&self, when: Finite<f64>) -> Fallible<()> {
107        if *when < 0. {
108            return Err(Error::Range(c"'when' must be a positive value".to_owned()));
109        }
110
111        if !self.has_start.get() {
112            return Err(Error::InvalidState(None));
113        }
114        self.has_stop.set(true);
115        self.node
116            .message(AudioNodeMessage::AudioScheduledSourceNode(
117                AudioScheduledSourceNodeMessage::Stop(*when),
118            ));
119        Ok(())
120    }
121}