Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / Modules / mediasession / MediaSessionManager.cpp
1 /*
2  * Copyright (C) 2015 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 #include "MediaSessionManager.h"
28
29 #if ENABLE(MEDIA_SESSION)
30
31 #include "MediaSession.h"
32 #include "MediaSessionInterruptionProviderMac.h"
33 #include <wtf/NeverDestroyed.h>
34
35 namespace WebCore {
36
37 static const char* contentSessionKind = "content";
38
39 MediaSessionManager& MediaSessionManager::singleton()
40 {
41     static NeverDestroyed<MediaSessionManager> manager;
42     return manager;
43 }
44
45 MediaSessionManager::MediaSessionManager()
46 {
47 #if PLATFORM(MAC)
48     m_interruptionProvider = adoptRef(new MediaSessionInterruptionProviderMac(*this));
49     m_interruptionProvider->beginListeningForInterruptions();
50 #endif
51 }
52
53 bool MediaSessionManager::hasActiveMediaElements() const
54 {
55     for (auto* session : m_sessions) {
56         if (session->hasActiveMediaElements())
57             return true;
58     }
59
60     return false;
61 }
62
63 void MediaSessionManager::addMediaSession(MediaSession& session)
64 {
65     m_sessions.add(&session);
66 }
67
68 void MediaSessionManager::removeMediaSession(MediaSession& session)
69 {
70     m_sessions.remove(&session);
71 }
72
73 void MediaSessionManager::togglePlayback()
74 {
75     for (auto* session : m_sessions) {
76         String sessionKind = session->kind();
77         if (session->currentState() == MediaSession::State::Active && (sessionKind == contentSessionKind || sessionKind == ""))
78             session->togglePlayback();
79     }
80 }
81
82 void MediaSessionManager::skipToNextTrack()
83 {
84     // 5.2.2 When the user presses the MediaTrackNext media key, then for each Content-based media session that is
85     // currently ACTIVE and has a media remote controller with its nextTrackEnabled attribute set to true, queue a task
86     // to fire a simple event named nexttrack at its media remote controller.
87     for (auto* session : m_sessions) {
88         if (session->currentState() == MediaSession::State::Active && session->kind() == contentSessionKind)
89             session->skipToNextTrack();
90     }
91 }
92
93 void MediaSessionManager::skipToPreviousTrack()
94 {
95     // 5.2.2 When the user presses the MediaTrackPrevious media key, then for each Content-based media session that is
96     // currently ACTIVE and has a media remote controller with its previousTrackEnabled attribute set to true, queue a task
97     // to fire a simple event named previoustrack at its media remote controller.
98     for (auto* session : m_sessions) {
99         if (session->currentState() == MediaSession::State::Active && session->kind() == contentSessionKind)
100             session->skipToPreviousTrack();
101     }
102 }
103
104 void MediaSessionManager::didReceiveStartOfInterruptionNotification(MediaSessionInterruptingCategory interruptingCategory)
105 {
106     // 4.5.2 Interrupting a media session
107     // When a start-of-interruption notification event is received from the platform, then the user agent must run the
108     // media session interruption algorithm against all known media sessions, passing in each media session as media session.
109     for (auto* session : m_sessions) {
110         // 1. If media session's current state is not active, then terminate these steps.
111         if (session->currentState() != MediaSession::State::Active)
112             continue;
113
114         // 2. Let interrupting media session category be the media session category that triggered this interruption.
115         //    If this interruption has no known media session category, let interrupting media session category be Default.
116
117         // 3. Run these substeps:
118         if (interruptingCategory == MediaSessionInterruptingCategory::Content) {
119             // -  If interrupting media session category is Content:
120             //    If media session's current media session type is Default or Content then indefinitely pause all of media
121             //    session's active audio-producing participants and set media session's current state to idle.
122             if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
123                 session->handleIndefinitePauseInterruption();
124         } else if (interruptingCategory == MediaSessionInterruptingCategory::Transient) {
125             // - If interrupting media session category is Transient:
126             //   If media session's current media session type is Default or Content then duck all of media session's active
127             //   audio-producing participants and set media session's current state to interrupted.
128             if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
129                 session->handleDuckInterruption();
130         } else if (interruptingCategory == MediaSessionInterruptingCategory::TransientSolo) {
131             // - If interrupting media session category is Transient Solo:
132             //   If media session's current media session type is Default, Content, Transient or Transient Solo then pause
133             //   all of media session's active audio-producing participants and set current media session's current state to interrupted.
134             if (session->kind() != MediaSessionKind::Ambient)
135                 session->handlePauseInterruption();
136         }
137     }
138 }
139
140 void MediaSessionManager::didReceiveEndOfInterruptionNotification(MediaSessionInterruptingCategory interruptingCategory)
141 {
142     // 4.5.2 Interrupting a media session
143     // When an end-of-interruption notification event is received from the platform, then the user agent must run the
144     // media session continuation algorithm against all known media sessions, passing in each media session as media session.
145     for (auto* session : m_sessions) {
146         // 1. If media session's current state is not interrupted, then terminate these steps.
147         if (session->currentState() != MediaSession::State::Interrupted)
148             continue;
149
150         // 2. Let interrupting media session category be the media session category that initially triggered this interruption.
151         //    If this interruption has no known media session category, let interrupting media session category be Default.
152
153         // 3. Run these substeps:
154         if (interruptingCategory == MediaSessionInterruptingCategory::Transient) {
155             // - If interrupting media session category is Transient:
156             //   If media session's current media session type is Default or Content, then unduck all of media session's
157             //   active audio-producing participants and set media session's current state to active.
158             if (session->kind() == MediaSessionKind::Default || session->kind() == MediaSessionKind::Content)
159                 session->handleUnduckInterruption();
160         } else if (interruptingCategory == MediaSessionInterruptingCategory::TransientSolo) {
161             // - If interrupting media session category is Transient Solo:
162             //   If media session's current media session type is Default, Content, Transient, or Transient Solo, then
163             //   unpause the media session's active audio-producing participants and set media session's current state to active.
164             if (session->kind() != MediaSessionKind::Ambient)
165                 session->handleUnpauseInterruption();
166         }
167     }
168 }
169
170 } // namespace WebCore
171
172 #endif /* ENABLE(MEDIA_SESSION) */