1/*
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(VIDEO_TRACK)
29
30#include "TrackListBase.h"
31
32#include "EventNames.h"
33#include "HTMLMediaElement.h"
34#include "ScriptExecutionContext.h"
35#include "TrackEvent.h"
36#include <wtf/IsoMallocInlines.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(TrackListBase);
41
42TrackListBase::TrackListBase(HTMLMediaElement* element, ScriptExecutionContext* context)
43 : ActiveDOMObject(context)
44 , m_element(element)
45 , m_asyncEventQueue(*this)
46{
47 ASSERT(!context || is<Document>(context));
48 suspendIfNeeded();
49}
50
51TrackListBase::~TrackListBase()
52{
53 clearElement();
54}
55
56void TrackListBase::clearElement()
57{
58 m_element = nullptr;
59 for (auto& track : m_inbandTracks) {
60 track->setMediaElement(nullptr);
61 track->clearClient();
62 }
63}
64
65Element* TrackListBase::element() const
66{
67 return m_element;
68}
69
70unsigned TrackListBase::length() const
71{
72 return m_inbandTracks.size();
73}
74
75void TrackListBase::remove(TrackBase& track, bool scheduleEvent)
76{
77 size_t index = m_inbandTracks.find(&track);
78 if (index == notFound)
79 return;
80
81 if (track.mediaElement()) {
82 ASSERT(track.mediaElement() == m_element);
83 track.setMediaElement(nullptr);
84 }
85
86 Ref<TrackBase> trackRef = *m_inbandTracks[index];
87
88 m_inbandTracks.remove(index);
89
90 if (scheduleEvent)
91 scheduleRemoveTrackEvent(WTFMove(trackRef));
92}
93
94bool TrackListBase::contains(TrackBase& track) const
95{
96 return m_inbandTracks.find(&track) != notFound;
97}
98
99void TrackListBase::scheduleTrackEvent(const AtomicString& eventName, Ref<TrackBase>&& track)
100{
101 m_asyncEventQueue.enqueueEvent(TrackEvent::create(eventName, Event::CanBubble::No, Event::IsCancelable::No, WTFMove(track)));
102}
103
104void TrackListBase::scheduleAddTrackEvent(Ref<TrackBase>&& track)
105{
106 // 4.8.10.5 Loading the media resource
107 // ...
108 // Fire a trusted event with the name addtrack, that does not bubble and is
109 // not cancelable, and that uses the TrackEvent interface, with the track
110 // attribute initialized to the new AudioTrack object, at this
111 // AudioTrackList object.
112 // ...
113 // Fire a trusted event with the name addtrack, that does not bubble and is
114 // not cancelable, and that uses the TrackEvent interface, with the track
115 // attribute initialized to the new VideoTrack object, at this
116 // VideoTrackList object.
117
118 // 4.8.10.12.3 Sourcing out-of-band text tracks
119 // 4.8.10.12.4 Text track API
120 // ... then queue a task to fire an event with the name addtrack, that does not
121 // bubble and is not cancelable, and that uses the TrackEvent interface, with
122 // the track attribute initialized to the text track's TextTrack object, at
123 // the media element's textTracks attribute's TextTrackList object.
124 scheduleTrackEvent(eventNames().addtrackEvent, WTFMove(track));
125}
126
127void TrackListBase::scheduleRemoveTrackEvent(Ref<TrackBase>&& track)
128{
129 // 4.8.10.6 Offsets into the media resource
130 // If at any time the user agent learns that an audio or video track has
131 // ended and all media data relating to that track corresponds to parts of
132 // the media timeline that are before the earliest possible position, the
133 // user agent may queue a task to remove the track from the audioTracks
134 // attribute's AudioTrackList object or the videoTracks attribute's
135 // VideoTrackList object as appropriate and then fire a trusted event
136 // with the name removetrack, that does not bubble and is not cancelable,
137 // and that uses the TrackEvent interface, with the track attribute
138 // initialized to the AudioTrack or VideoTrack object representing the
139 // track, at the media element's aforementioned AudioTrackList or
140 // VideoTrackList object.
141
142 // 4.8.10.12.3 Sourcing out-of-band text tracks
143 // When a track element's parent element changes and the old parent was a
144 // media element, then the user agent must remove the track element's
145 // corresponding text track from the media element's list of text tracks,
146 // and then queue a task to fire a trusted event with the name removetrack,
147 // that does not bubble and is not cancelable, and that uses the TrackEvent
148 // interface, with the track attribute initialized to the text track's
149 // TextTrack object, at the media element's textTracks attribute's
150 // TextTrackList object.
151 scheduleTrackEvent(eventNames().removetrackEvent, WTFMove(track));
152}
153
154void TrackListBase::scheduleChangeEvent()
155{
156 // 4.8.10.6 Offsets into the media resource
157 // Whenever an audio track in an AudioTrackList is enabled or disabled, the
158 // user agent must queue a task to fire a simple event named change at the
159 // AudioTrackList object.
160 // ...
161 // Whenever a track in a VideoTrackList that was previously not selected is
162 // selected, the user agent must queue a task to fire a simple event named
163 // change at the VideoTrackList object.
164 m_asyncEventQueue.enqueueEvent(Event::create(eventNames().changeEvent, Event::CanBubble::No, Event::IsCancelable::No));
165}
166
167bool TrackListBase::isChangeEventScheduled() const
168{
169 return m_asyncEventQueue.hasPendingEventsOfType(eventNames().changeEvent);
170}
171
172bool TrackListBase::isAnyTrackEnabled() const
173{
174 for (auto& track : m_inbandTracks) {
175 if (track->enabled())
176 return true;
177 }
178 return false;
179}
180
181bool TrackListBase::canSuspendForDocumentSuspension() const
182{
183 return !m_asyncEventQueue.hasPendingEvents();
184}
185
186void TrackListBase::suspend(ReasonForSuspension reason)
187{
188 switch (reason) {
189 case ReasonForSuspension::PageCache:
190 case ReasonForSuspension::PageWillBeSuspended:
191 m_asyncEventQueue.suspend();
192 break;
193 case ReasonForSuspension::JavaScriptDebuggerPaused:
194 case ReasonForSuspension::WillDeferLoading:
195 // Do nothing, we don't pause media playback in these cases.
196 break;
197 }
198}
199
200void TrackListBase::resume()
201{
202 m_asyncEventQueue.resume();
203}
204
205void TrackListBase::stop()
206{
207 m_asyncEventQueue.close();
208}
209
210} // namespace WebCore
211
212#endif
213